aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--java/res/xml/method.xml128
-rw-r--r--java/src/com/android/inputmethod/keyboard/EmojiKeyboardView.java5
-rw-r--r--java/src/com/android/inputmethod/keyboard/Keyboard.java9
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java54
-rw-r--r--java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java2
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionary.java22
-rw-r--r--java/src/com/android/inputmethod/latin/Constants.java6
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java8
-rw-r--r--java/src/com/android/inputmethod/latin/SubtypeSwitcher.java13
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java11
-rw-r--r--java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java6
-rw-r--r--java/src/com/android/inputmethod/latin/utils/StringUtils.java76
-rw-r--r--native/jni/Android.mk2
-rw-r--r--native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp28
-rw-r--r--native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp12
-rw-r--r--native/jni/src/suggest/core/dictionary/bigram_dictionary.h2
-rw-r--r--native/jni/src/suggest/core/dictionary/dictionary.cpp5
-rw-r--r--native/jni/src/suggest/core/dictionary/dictionary.h2
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.cpp4
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.h2
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.cpp46
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.h8
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/header/header_policy.cpp60
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h28
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.cpp131
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.h (renamed from native/jni/src/suggest/policyimpl/dictionary/header/header_reading_utils.h)25
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/header/header_reading_utils.cpp81
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.h2
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.cpp26
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.h6
-rw-r--r--tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java111
-rw-r--r--tests/src/com/android/inputmethod/latin/utils/StringUtilsTests.java12
32 files changed, 673 insertions, 260 deletions
diff --git a/java/res/xml/method.xml b/java/res/xml/method.xml
index 6014646bb..d7424c0c7 100644
--- a/java/res/xml/method.xml
+++ b/java/res/xml/method.xml
@@ -99,112 +99,112 @@
android:subtypeId="0xc9194f98"
android:imeSubtypeLocale="en_US"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="TrySuppressingImeSwitcher,AsciiCapable,SupportTouchPositionCorrection"
+ android:imeSubtypeExtraValue="TrySuppressingImeSwitcher,AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_en_GB"
android:subtypeId="0xb045e755"
android:imeSubtypeLocale="en_GB"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="TrySuppressingImeSwitcher,AsciiCapable,SupportTouchPositionCorrection"
+ android:imeSubtypeExtraValue="TrySuppressingImeSwitcher,AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0x6f972360"
android:imeSubtypeLocale="af"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0x590dde40"
android:imeSubtypeLocale="ar"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="SupportTouchPositionCorrection"
+ android:imeSubtypeExtraValue="SupportTouchPositionCorrection,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0x70b0f974"
android:imeSubtypeLocale="az"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0x1dc3a859"
android:imeSubtypeLocale="be"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="KeyboardLayoutSet=east_slavic"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=east_slavic,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0x0ba9c0e8"
android:imeSubtypeLocale="bg"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="KeyboardLayoutSet=bulgarian"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=bulgarian,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_bulgarian_bds"
android:subtypeId="0x5f51ba9a"
android:imeSubtypeLocale="bg"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="KeyboardLayoutSet=bulgarian_bds"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=bulgarian_bds,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0xd2e520d5"
android:imeSubtypeLocale="ca"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0x2d3d2ed0"
android:imeSubtypeLocale="cs"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
+ android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0x2df4605d"
android:imeSubtypeLocale="da"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
+ android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0x2e2cbe61"
android:imeSubtypeLocale="de"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
+ android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0x0e7802d3"
android:imeSubtypeLocale="el"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="KeyboardLayoutSet=greek"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=greek,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0x4090554a"
android:imeSubtypeLocale="eo"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0x30a6e00e"
android:imeSubtypeLocale="es"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
+ android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_es_US"
android:subtypeId="0x84d2efc6"
android:imeSubtypeLocale="es_US"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable,EmojiCapable"
/>
<!--
<subtype android:icon="@drawable/ic_subtype_keyboard"
@@ -212,7 +212,7 @@
android:subtypeId="0x623f9286"
android:imeSubtypeLocale="es_419"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable,EmojiCapable"
/>
-->
<subtype android:icon="@drawable/ic_subtype_keyboard"
@@ -220,63 +220,63 @@
android:subtypeId="0xec2d3955"
android:imeSubtypeLocale="et"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="KeyboardLayoutSet=nordic,AsciiCapable"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=nordic,AsciiCapable,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0xbe66c254"
android:imeSubtypeLocale="fa"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="KeyboardLayoutSet=farsi"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=farsi,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0x31cecda3"
android:imeSubtypeLocale="fi"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
+ android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0x324da12c"
android:imeSubtypeLocale="fr"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
+ android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0xeadbb691"
android:imeSubtypeLocale="fr_CA"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
+ android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0x39753b7f"
android:imeSubtypeLocale="hi"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="KeyboardLayoutSet=hindi"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=hindi,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0x35b7526a"
android:imeSubtypeLocale="hr"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
+ android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0x35e198ed"
android:imeSubtypeLocale="hu"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
+ android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0xe39ac3ca"
android:imeSubtypeLocale="hy"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="KeyboardLayoutSet=armenian_phonetic"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=armenian_phonetic,EmojiCapable"
/>
<!-- Java uses the deprecated "in" code instead of the standard "id" code for Indonesian. -->
<subtype android:icon="@drawable/ic_subtype_keyboard"
@@ -284,21 +284,21 @@
android:subtypeId="0x7daea460"
android:imeSubtypeLocale="in"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0x7df519e5"
android:imeSubtypeLocale="is"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0x37885a0b"
android:imeSubtypeLocale="it"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
+ android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
/>
<!-- Java uses the deprecated "iw" code instead of the standard "he" code for Hebrew. -->
<subtype android:icon="@drawable/ic_subtype_keyboard"
@@ -306,14 +306,14 @@
android:subtypeId="0x66fb18bd"
android:imeSubtypeLocale="iw"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="SupportTouchPositionCorrection"
+ android:imeSubtypeExtraValue="SupportTouchPositionCorrection,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0x6e119e6a"
android:imeSubtypeLocale="ka"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="KeyboardLayoutSet=georgian"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=georgian,EmojiCapable"
/>
<!--
<subtype android:icon="@drawable/ic_subtype_keyboard"
@@ -321,7 +321,7 @@
android:subtypeId="0x2d73d2f6"
android:imeSubtypeLocale="kk"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="KeyboardLayoutSet=east_slavic"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=east_slavic,EmojiCapable"
/>
-->
<subtype android:icon="@drawable/ic_subtype_keyboard"
@@ -329,140 +329,140 @@
android:subtypeId="0x2e391c04"
android:imeSubtypeLocale="ky"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="KeyboardLayoutSet=east_slavic"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=east_slavic,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0x8315772c"
android:imeSubtypeLocale="lo"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="KeyboardLayoutSet=lao"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=lao,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0x8321bb43"
android:imeSubtypeLocale="lt"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0x833dea45"
android:imeSubtypeLocale="lv"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0xaf50ab7c"
android:imeSubtypeLocale="mk"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="KeyboardLayoutSet=south_slavic"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=south_slavic,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0xcdcfc3ab"
android:imeSubtypeLocale="mn"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="KeyboardLayoutSet=mongolian"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=mongolian,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0x84c87c61"
android:imeSubtypeLocale="ms"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0x3f12ee14"
android:imeSubtypeLocale="nb"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
+ android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0xd80a4cee"
android:imeSubtypeLocale="ne"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="KeyboardLayoutSet=nepali_romanized"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=nepali_romanized,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_nepali_traditional"
android:subtypeId="0x5fafea88"
android:imeSubtypeLocale="ne"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="KeyboardLayoutSet=nepali_traditional"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=nepali_traditional,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0x3f9fd91e"
android:imeSubtypeLocale="nl"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
+ android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0x500ca92c"
android:imeSubtypeLocale="nl_BE"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="KeyboardLayoutSet=azerty,AsciiCapable"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=azerty,AsciiCapable,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0x43098a5c"
android:imeSubtypeLocale="pl"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
+ android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0xcafff4a6"
android:imeSubtypeLocale="pt_BR"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0xe2fffc5a"
android:imeSubtypeLocale="pt_PT"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0x8d185978"
android:imeSubtypeLocale="ro"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0x763a8752"
android:imeSubtypeLocale="ru"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="SupportTouchPositionCorrection"
+ android:imeSubtypeExtraValue="SupportTouchPositionCorrection,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0x8e94d413"
android:imeSubtypeLocale="sk"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0x8ea2eb94"
android:imeSubtypeLocale="sl"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0x77c5196e"
android:imeSubtypeLocale="sr"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="SupportTouchPositionCorrection"
+ android:imeSubtypeExtraValue="SupportTouchPositionCorrection,EmojiCapable"
/>
<!-- TODO: Uncomment once we can handle IETF language tag with script name specified.
<subtype android:icon="@drawable/ic_subtype_keyboard"
@@ -470,14 +470,14 @@
android:subtypeId="0xXXXXXXXX"
android:imeSubtypeLocale="sr"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="SupportTouchPositionCorrection"
+ android:imeSubtypeExtraValue="SupportTouchPositionCorrection,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_serbian_latin"
android:subtypeId="0xXXXXXXXX"
android:imeSubtypeLocale="sr-Latn"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
/>
-->
<subtype android:icon="@drawable/ic_subtype_keyboard"
@@ -485,63 +485,63 @@
android:subtypeId="0x48b4ff43"
android:imeSubtypeLocale="sv"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
+ android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0x8f3dee1f"
android:imeSubtypeLocale="sw"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0x1f94d5d4"
android:imeSubtypeLocale="th"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="KeyboardLayoutSet=thai"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=thai,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0xf08285ef"
android:imeSubtypeLocale="tl"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0x4a3179de"
android:imeSubtypeLocale="tr"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
+ android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0x3e84492c"
android:imeSubtypeLocale="uk"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="KeyboardLayoutSet=east_slavic"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=east_slavic,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0x93972eee"
android:imeSubtypeLocale="vi"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
android:subtypeId="0x9b13ab76"
android:imeSubtypeLocale="zu"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EmojiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_no_language_qwerty"
android:subtypeId="0xa239ebad"
android:imeSubtypeLocale="zz"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EnabledWhenDefaultIsNotAsciiCapable"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EnabledWhenDefaultIsNotAsciiCapable,EmojiCapable"
/>
<!-- Emoji subtype has to be an addtional subtype added at boot time because ICS doesn't
support Emoji. -->
@@ -551,7 +551,7 @@
android:subtypeId="0xc14d88b2"
android:imeSubtypeLocale="zz"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="KeyboardLayoutSet=emoji"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=emoji,EmojiCapable"
/>
-->
</input-method>
diff --git a/java/src/com/android/inputmethod/keyboard/EmojiKeyboardView.java b/java/src/com/android/inputmethod/keyboard/EmojiKeyboardView.java
index 702ed2075..546fa8140 100644
--- a/java/src/com/android/inputmethod/keyboard/EmojiKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/EmojiKeyboardView.java
@@ -505,10 +505,7 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange
@Override
public void onKeyClick(final Key key) {
- // TODO: Save emoticons to recents
- if (mEmojiCategory.getCurrentCategoryId() != CATEGORY_ID_EMOTICONS) {
- mEmojiKeyboardAdapter.addRecentKey(key);
- }
+ mEmojiKeyboardAdapter.addRecentKey(key);
mEmojiCategory.saveLastTypedCategoryPage();
final int code = key.getCode();
if (code == Constants.CODE_OUTPUT_TEXT) {
diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java
index 23f037fbd..bc1383aff 100644
--- a/java/src/com/android/inputmethod/keyboard/Keyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java
@@ -155,6 +155,15 @@ public class Keyboard {
return mKeys;
}
+ public Key getKeyFromOutputText(final String outputText) {
+ for (final Key key : getKeys()) {
+ if (outputText.equals(key.getOutputText())) {
+ return key;
+ }
+ }
+ return null;
+ }
+
public Key getKey(final int code) {
if (code == Constants.CODE_UNSPECIFIED) {
return null;
diff --git a/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java b/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java
index f203eb7d7..2976e2323 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java
@@ -18,25 +18,27 @@ package com.android.inputmethod.keyboard.internal;
import android.content.SharedPreferences;
import android.text.TextUtils;
+import android.util.Log;
import com.android.inputmethod.keyboard.EmojiKeyboardView;
import com.android.inputmethod.keyboard.Key;
import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.settings.Settings;
import com.android.inputmethod.latin.utils.CollectionUtils;
+import com.android.inputmethod.latin.utils.StringUtils;
import java.util.ArrayDeque;
+import java.util.ArrayList;
import java.util.Collection;
+import java.util.List;
/**
* This is a Keyboard class where you can add keys dynamically shown in a grid layout
*/
public class DynamicGridKeyboard extends Keyboard {
+ private static final String TAG = DynamicGridKeyboard.class.getSimpleName();
private static final int TEMPLATE_KEY_CODE_0 = 0x30;
private static final int TEMPLATE_KEY_CODE_1 = 0x31;
- // Recent codes are saved as an integer array, so we use comma as a separater.
- private static final String RECENT_KEY_SEPARATOR = Constants.STRING_COMMA;
private final SharedPreferences mPrefs;
private final int mLeftPadding;
@@ -84,6 +86,9 @@ public class DynamicGridKeyboard extends Keyboard {
}
private void addKey(final Key usedKey, final boolean addFirst) {
+ if (usedKey == null) {
+ return;
+ }
synchronized (mGridKeys) {
mCachedGridKeys = null;
final GridKey key = new GridKey(usedKey);
@@ -109,28 +114,45 @@ public class DynamicGridKeyboard extends Keyboard {
}
private void saveRecentKeys() {
- final StringBuilder sb = new StringBuilder();
+ final ArrayList<Object> keys = CollectionUtils.newArrayList();
for (final Key key : mGridKeys) {
- sb.append(key.getCode()).append(RECENT_KEY_SEPARATOR);
+ if (key.getOutputText() != null) {
+ keys.add(key.getOutputText());
+ } else {
+ keys.add(key.getCode());
+ }
}
- Settings.writeEmojiRecentKeys(mPrefs, sb.toString());
+ final String jsonStr = StringUtils.listToJsonStr(keys);
+ Settings.writeEmojiRecentKeys(mPrefs, jsonStr);
}
- public void loadRecentKeys(Collection<DynamicGridKeyboard> keyboards) {
- final String str = Settings.readEmojiRecentKeys(mPrefs);
- for (String s : str.split(RECENT_KEY_SEPARATOR)) {
- if (TextUtils.isEmpty(s)) {
- continue;
- }
- final int code = Integer.valueOf(s);
- for (DynamicGridKeyboard kbd : keyboards) {
+ private static Key getKey(final Collection<DynamicGridKeyboard> keyboards, final Object o) {
+ for (final DynamicGridKeyboard kbd : keyboards) {
+ if (o instanceof Integer) {
+ final int code = (Integer) o;
final Key key = kbd.getKey(code);
if (key != null) {
- addKeyLast(key);
- break;
+ return key;
+ }
+ } else if (o instanceof String) {
+ final String outputText = (String) o;
+ final Key key = kbd.getKeyFromOutputText(outputText);
+ if (key != null) {
+ return key;
}
+ } else {
+ Log.w(TAG, "Invalid object: " + o);
}
}
+ return null;
+ }
+
+ public void loadRecentKeys(Collection<DynamicGridKeyboard> keyboards) {
+ final String str = Settings.readEmojiRecentKeys(mPrefs);
+ final List<Object> keys = StringUtils.jsonStrToList(str);
+ for (final Object o : keys) {
+ addKeyLast(getKey(keyboards, o));
+ }
}
private int getKeyX(final int index) {
diff --git a/java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java b/java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java
index 55df263fe..845a9b987 100644
--- a/java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java
+++ b/java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java
@@ -58,7 +58,7 @@ abstract public class AbstractDictionaryWriter extends Dictionary {
final File file = new File(mContext.getFilesDir(), fileName);
final File tempFile = new File(mContext.getFilesDir(), tempFileName);
try {
- final DictEncoder dictEncoder = new Ver3DictEncoder(file);
+ final DictEncoder dictEncoder = new Ver3DictEncoder(tempFile);
writeDictionary(dictEncoder);
tempFile.renameTo(file);
} catch (IOException e) {
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index b49cd80ab..632ee0da4 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -109,7 +109,7 @@ public final class BinaryDictionary extends Dictionary {
private static native void flushWithGCNative(long dict, String filePath);
private static native void closeNative(long dict);
private static native int getProbabilityNative(long dict, int[] word);
- private static native boolean isValidBigramNative(long dict, int[] word0, int[] word1);
+ private static native int getBigramProbabilityNative(long dict, int[] word0, int[] word1);
private static native int getSuggestionsNative(long dict, long proximityInfo,
long traverseSession, int[] xCoordinates, int[] yCoordinates, int[] times,
int[] pointerIds, int[] inputCodePoints, int inputSize, int commitPoint,
@@ -122,6 +122,8 @@ public final class BinaryDictionary extends Dictionary {
private static native void addBigramWordsNative(long dict, int[] word0, int[] word1,
int probability);
private static native void removeBigramWordsNative(long dict, int[] word0, int[] word1);
+ private static native int calculateProbabilityNative(long dict, int unigramProbability,
+ int bigramProbability);
// TODO: Move native dict into session
private final void loadDictionary(final String path, final long startOffset,
@@ -219,12 +221,12 @@ public final class BinaryDictionary extends Dictionary {
@Override
public boolean isValidWord(final String word) {
- return getFrequency(word) >= 0;
+ return getFrequency(word) != NOT_A_PROBABILITY;
}
@Override
public int getFrequency(final String word) {
- if (word == null) return -1;
+ if (word == null) return NOT_A_PROBABILITY;
int[] codePoints = StringUtils.toCodePointArray(word);
return getProbabilityNative(mNativeDict, codePoints);
}
@@ -232,10 +234,14 @@ public final class BinaryDictionary extends Dictionary {
// TODO: Add a batch process version (isValidBigramMultiple?) to avoid excessive numbers of jni
// calls when checking for changes in an entire dictionary.
public boolean isValidBigram(final String word0, final String word1) {
- if (TextUtils.isEmpty(word0) || TextUtils.isEmpty(word1)) return false;
+ return getBigramProbability(word0, word1) != NOT_A_PROBABILITY;
+ }
+
+ public int getBigramProbability(final String word0, final String word1) {
+ if (TextUtils.isEmpty(word0) || TextUtils.isEmpty(word1)) return NOT_A_PROBABILITY;
final int[] codePoints0 = StringUtils.toCodePointArray(word0);
final int[] codePoints1 = StringUtils.toCodePointArray(word1);
- return isValidBigramNative(mNativeDict, codePoints0, codePoints1);
+ return getBigramProbabilityNative(mNativeDict, codePoints0, codePoints1);
}
// Add a unigram entry to binary dictionary in native code.
@@ -285,6 +291,12 @@ public final class BinaryDictionary extends Dictionary {
return needsToRunGCNative(mNativeDict);
}
+ @UsedForTesting
+ public int calculateProbability(final int unigramProbability, final int bigramProbability) {
+ if (!isValidDictionary()) return NOT_A_PROBABILITY;
+ return calculateProbabilityNative(mNativeDict, unigramProbability, bigramProbability);
+ }
+
@Override
public boolean shouldAutoCommit(final SuggestedWordInfo candidate) {
// TODO: actually use the confidence rather than use this completely broken heuristic
diff --git a/java/src/com/android/inputmethod/latin/Constants.java b/java/src/com/android/inputmethod/latin/Constants.java
index 9f754a7d6..28d9e8652 100644
--- a/java/src/com/android/inputmethod/latin/Constants.java
+++ b/java/src/com/android/inputmethod/latin/Constants.java
@@ -76,6 +76,11 @@ public final class Constants {
public static final String ASCII_CAPABLE = "AsciiCapable";
/**
+ * The subtype extra value used to indicate that the subtype keyboard layout is capable
+ * for typing EMOJI characters.
+ */
+ public static final String EMOJI_CAPABLE = "EmojiCapable";
+ /**
* The subtype extra value used to indicate that the subtype require network connection
* to work.
*/
@@ -219,7 +224,6 @@ public final class Constants {
}
public static final int MAX_INT_BIT_COUNT = 32;
- public static final String STRING_COMMA = ",";
private Constants() {
// This utility class is not publicly instantiable.
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 3595a19a3..9f779eb43 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -2686,6 +2686,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
return prevWord;
}
+ private boolean isResumableWord(final String word, final SettingsValues settings) {
+ final int firstCodePoint = word.codePointAt(0);
+ return settings.isWordCodePoint(firstCodePoint)
+ && Constants.CODE_SINGLE_QUOTE != firstCodePoint
+ && Constants.CODE_DASH != firstCodePoint;
+ }
+
/**
* Check if the cursor is touching a word. If so, restart suggestions on this word, else
* do nothing.
@@ -2715,6 +2722,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (numberOfCharsInWordBeforeCursor > mLastSelectionStart) return;
final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList();
final String typedWord = range.mWord.toString();
+ if (!isResumableWord(typedWord, currentSettings)) return;
int i = 0;
for (final SuggestionSpan span : range.getSuggestionSpansAtWord()) {
for (final String s : span.getSuggestions()) {
diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
index 0889f22ca..772e25200 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
@@ -59,16 +59,19 @@ public final class SubtypeSwitcher {
// Dummy no language QWERTY subtype. See {@link R.xml.method}.
private static final InputMethodSubtype DUMMY_NO_LANGUAGE_SUBTYPE = new InputMethodSubtype(
R.string.subtype_no_language_qwerty, R.drawable.ic_subtype_keyboard,
- SubtypeLocaleUtils.NO_LANGUAGE, "keyboard",
- "KeyboardLayoutSet=" + SubtypeLocaleUtils.QWERTY
- + ",AsciiCapable,EnabledWhenDefaultIsNotAsciiCapable",
+ SubtypeLocaleUtils.NO_LANGUAGE, "keyboard", "KeyboardLayoutSet="
+ + SubtypeLocaleUtils.QWERTY
+ + "," + Constants.Subtype.ExtraValue.ASCII_CAPABLE
+ + ",EnabledWhenDefaultIsNotAsciiCapable,"
+ + Constants.Subtype.ExtraValue.EMOJI_CAPABLE,
false /* isAuxiliary */, false /* overridesImplicitlyEnabledSubtype */);
// Caveat: We probably should remove this when we add an Emoji subtype in {@link R.xml.method}.
// Dummy Emoji subtype. See {@link R.xml.method}.
private static final InputMethodSubtype DUMMY_EMOJI_SUBTYPE = new InputMethodSubtype(
R.string.subtype_emoji, R.drawable.ic_subtype_keyboard,
- SubtypeLocaleUtils.NO_LANGUAGE, "keyboard",
- "KeyboardLayoutSet=" + SubtypeLocaleUtils.EMOJI,
+ SubtypeLocaleUtils.NO_LANGUAGE, "keyboard", "KeyboardLayoutSet="
+ + SubtypeLocaleUtils.EMOJI + ","
+ + Constants.Subtype.ExtraValue.EMOJI_CAPABLE,
false /* isAuxiliary */, false /* overridesImplicitlyEnabledSubtype */);
static final class NeedsToDisplayLanguage {
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java
index f333b0d86..70931f885 100644
--- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java
+++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java
@@ -758,8 +758,15 @@ public class BinaryDictEncoderUtils {
final FormatOptions formatOptions) {
int positionOfChildrenPosField = ptNode.mCachedAddressAfterUpdate
+ getNodeHeaderSize(ptNode, formatOptions);
- if (ptNode.mFrequency >= 0) {
- positionOfChildrenPosField += FormatSpec.PTNODE_FREQUENCY_SIZE;
+ if (ptNode.isTerminal()) {
+ // A terminal node has either the terminal id or the frequency.
+ // If positionOfChildrenPosField is incorrect, we may crash when jumping to the children
+ // position.
+ if (formatOptions.mHasTerminalId) {
+ positionOfChildrenPosField += FormatSpec.PTNODE_TERMINAL_ID_SIZE;
+ } else {
+ positionOfChildrenPosField += FormatSpec.PTNODE_FREQUENCY_SIZE;
+ }
}
return null == ptNode.mChildren ? FormatSpec.NO_CHILDREN_ADDRESS
: ptNode.mChildren.mCachedAddressAfterUpdate - positionOfChildrenPosField;
diff --git a/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java b/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java
index 215faa0c7..3082bf4b7 100644
--- a/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java
@@ -25,6 +25,7 @@ import android.os.Build;
import android.text.TextUtils;
import android.view.inputmethod.InputMethodSubtype;
+import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.R;
import java.util.ArrayList;
@@ -61,8 +62,9 @@ public final class AdditionalSubtypeUtils {
IS_ADDITIONAL_SUBTYPE, layoutDisplayNameExtraValue);
final int nameId = SubtypeLocaleUtils.getSubtypeNameId(localeString, keyboardLayoutSetName);
return new InputMethodSubtype(nameId, R.drawable.ic_subtype_keyboard,
- localeString, KEYBOARD_MODE,
- layoutExtraValue + "," + additionalSubtypeExtraValue, false, false);
+ localeString, KEYBOARD_MODE, layoutExtraValue + "," + additionalSubtypeExtraValue
+ + "," + Constants.Subtype.ExtraValue.ASCII_CAPABLE
+ + "," + Constants.Subtype.ExtraValue.EMOJI_CAPABLE, false, false);
}
public static String getPrefSubtype(final InputMethodSubtype subtype) {
diff --git a/java/src/com/android/inputmethod/latin/utils/StringUtils.java b/java/src/com/android/inputmethod/latin/utils/StringUtils.java
index be4184093..121aecf0f 100644
--- a/java/src/com/android/inputmethod/latin/utils/StringUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/StringUtils.java
@@ -16,16 +16,25 @@
package com.android.inputmethod.latin.utils;
-import android.text.TextUtils;
-
import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.settings.SettingsValues;
+import android.text.TextUtils;
+import android.util.JsonReader;
+import android.util.JsonWriter;
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
import java.util.Locale;
public final class StringUtils {
+ private static final String TAG = StringUtils.class.getSimpleName();
public static final int CAPITALIZE_NONE = 0; // No caps, or mixed case
public static final int CAPITALIZE_FIRST = 1; // First only
public static final int CAPITALIZE_ALL = 2; // All caps
@@ -390,4 +399,67 @@ public final class StringUtils {
}
return bytes;
}
+
+ public static List<Object> jsonStrToList(String s) {
+ final ArrayList<Object> retval = CollectionUtils.newArrayList();
+ final JsonReader reader = new JsonReader(new StringReader(s));
+ try {
+ reader.beginArray();
+ while(reader.hasNext()) {
+ reader.beginObject();
+ while (reader.hasNext()) {
+ final String name = reader.nextName();
+ if (name.equals(Integer.class.getSimpleName())) {
+ retval.add(reader.nextInt());
+ } else if (name.equals(String.class.getSimpleName())) {
+ retval.add(reader.nextString());
+ } else {
+ Log.w(TAG, "Invalid name: " + name);
+ reader.skipValue();
+ }
+ }
+ reader.endObject();
+ }
+ reader.endArray();
+ return retval;
+ } catch (IOException e) {
+ } finally {
+ try {
+ reader.close();
+ } catch (IOException e) {
+ }
+ }
+ return Collections.<Object>emptyList();
+ }
+
+ public static String listToJsonStr(List<Object> list) {
+ if (list == null || list.isEmpty()) {
+ return "";
+ }
+ final StringWriter sw = new StringWriter();
+ final JsonWriter writer = new JsonWriter(sw);
+ try {
+ writer.beginArray();
+ for (final Object o : list) {
+ writer.beginObject();
+ if (o instanceof Integer) {
+ writer.name(Integer.class.getSimpleName()).value((Integer)o);
+ } else if (o instanceof String) {
+ writer.name(String.class.getSimpleName()).value((String)o);
+ }
+ writer.endObject();
+ }
+ writer.endArray();
+ return sw.toString();
+ } catch (IOException e) {
+ } finally {
+ try {
+ if (writer != null) {
+ writer.close();
+ }
+ } catch (IOException e) {
+ }
+ }
+ return "";
+ }
}
diff --git a/native/jni/Android.mk b/native/jni/Android.mk
index d83bdadfa..ee93b4248 100644
--- a/native/jni/Android.mk
+++ b/native/jni/Android.mk
@@ -70,7 +70,7 @@ LATIN_IME_CORE_SRC_FILES := \
bigram/bigram_list_read_write_utils.cpp \
bigram/dynamic_bigram_list_policy.cpp \
header/header_policy.cpp \
- header/header_reading_utils.cpp \
+ header/header_read_write_utils.cpp \
shortcut/shortcut_list_reading_utils.cpp \
dictionary_structure_with_buffer_policy_factory.cpp \
dynamic_patricia_trie_node_reader.cpp \
diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
index a63fab6dc..7f47493b2 100644
--- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
+++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
@@ -188,8 +188,8 @@ static jint latinime_BinaryDictionary_getProbability(JNIEnv *env, jclass clazz,
return dictionary->getProbability(codePoints, wordLength);
}
-static jboolean latinime_BinaryDictionary_isValidBigram(JNIEnv *env, jclass clazz, jlong dict,
- jintArray word0, jintArray word1) {
+static jint latinime_BinaryDictionary_getBigramProbability(JNIEnv *env, jclass clazz,
+ jlong dict, jintArray word0, jintArray word1) {
Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict);
if (!dictionary) return JNI_FALSE;
const jsize word0Length = env->GetArrayLength(word0);
@@ -198,7 +198,8 @@ static jboolean latinime_BinaryDictionary_isValidBigram(JNIEnv *env, jclass claz
int word1CodePoints[word1Length];
env->GetIntArrayRegion(word0, 0, word0Length, word0CodePoints);
env->GetIntArrayRegion(word1, 0, word1Length, word1CodePoints);
- return dictionary->isValidBigram(word0CodePoints, word0Length, word1CodePoints, word1Length);
+ return dictionary->getBigramProbability(word0CodePoints, word0Length, word1CodePoints,
+ word1Length);
}
static jfloat latinime_BinaryDictionary_calcNormalizedScore(JNIEnv *env, jclass clazz,
@@ -269,6 +270,16 @@ static void latinime_BinaryDictionary_removeBigramWords(JNIEnv *env, jclass claz
word1Length);
}
+static int latinime_BinaryDictionary_calculateProbabilityNative(JNIEnv *env, jclass clazz,
+ jlong dict, jint unigramProbability, jint bigramProbability) {
+ Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict);
+ if (!dictionary) {
+ return NOT_A_PROBABILITY;
+ }
+ return dictionary->getDictionaryStructurePolicy()->getProbability(unigramProbability,
+ bigramProbability);
+}
+
static const JNINativeMethod sMethods[] = {
{
const_cast<char *>("openNative"),
@@ -306,9 +317,9 @@ static const JNINativeMethod sMethods[] = {
reinterpret_cast<void *>(latinime_BinaryDictionary_getProbability)
},
{
- const_cast<char *>("isValidBigramNative"),
- const_cast<char *>("(J[I[I)Z"),
- reinterpret_cast<void *>(latinime_BinaryDictionary_isValidBigram)
+ const_cast<char *>("getBigramProbabilityNative"),
+ const_cast<char *>("(J[I[I)I"),
+ reinterpret_cast<void *>(latinime_BinaryDictionary_getBigramProbability)
},
{
const_cast<char *>("calcNormalizedScoreNative"),
@@ -334,6 +345,11 @@ static const JNINativeMethod sMethods[] = {
const_cast<char *>("removeBigramWordsNative"),
const_cast<char *>("(J[I[I)V"),
reinterpret_cast<void *>(latinime_BinaryDictionary_removeBigramWords)
+ },
+ {
+ const_cast<char *>("calculateProbabilityNative"),
+ const_cast<char *>("(JII)I"),
+ reinterpret_cast<void *>(latinime_BinaryDictionary_calculateProbabilityNative)
}
};
diff --git a/native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp b/native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp
index 425b07624..5ba71c168 100644
--- a/native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp
+++ b/native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp
@@ -150,24 +150,26 @@ int BigramDictionary::getBigramListPositionForWord(const int *prevWord, const in
return mDictionaryStructurePolicy->getBigramsPositionOfNode(pos);
}
-bool BigramDictionary::isValidBigram(const int *word0, int length0, const int *word1,
+int BigramDictionary::getBigramProbability(const int *word0, int length0, const int *word1,
int length1) const {
int pos = getBigramListPositionForWord(word0, length0, false /* forceLowerCaseSearch */);
// getBigramListPositionForWord returns 0 if this word isn't in the dictionary or has no bigrams
- if (NOT_A_DICT_POS == pos) return false;
+ if (NOT_A_DICT_POS == pos) return NOT_A_PROBABILITY;
int nextWordPos = mDictionaryStructurePolicy->getTerminalNodePositionOfWord(word1, length1,
false /* forceLowerCaseSearch */);
- if (NOT_A_DICT_POS == nextWordPos) return false;
+ if (NOT_A_DICT_POS == nextWordPos) return NOT_A_PROBABILITY;
BinaryDictionaryBigramsIterator bigramsIt(
mDictionaryStructurePolicy->getBigramsStructurePolicy(), pos);
while (bigramsIt.hasNext()) {
bigramsIt.next();
if (bigramsIt.getBigramPos() == nextWordPos) {
- return true;
+ return mDictionaryStructurePolicy->getProbability(
+ mDictionaryStructurePolicy->getUnigramProbabilityOfPtNode(nextWordPos),
+ bigramsIt.getProbability());
}
}
- return false;
+ return NOT_A_PROBABILITY;
}
// TODO: Move functions related to bigram to here
diff --git a/native/jni/src/suggest/core/dictionary/bigram_dictionary.h b/native/jni/src/suggest/core/dictionary/bigram_dictionary.h
index 99b964c49..8af7ee75d 100644
--- a/native/jni/src/suggest/core/dictionary/bigram_dictionary.h
+++ b/native/jni/src/suggest/core/dictionary/bigram_dictionary.h
@@ -29,7 +29,7 @@ class BigramDictionary {
int getPredictions(const int *word, int length, int *outBigramCodePoints,
int *outBigramProbability, int *outputTypes) const;
- bool isValidBigram(const int *word1, int length1, const int *word2, int length2) const;
+ int getBigramProbability(const int *word1, int length1, const int *word2, int length2) const;
~BigramDictionary();
private:
diff --git a/native/jni/src/suggest/core/dictionary/dictionary.cpp b/native/jni/src/suggest/core/dictionary/dictionary.cpp
index 033572201..ec1b63a12 100644
--- a/native/jni/src/suggest/core/dictionary/dictionary.cpp
+++ b/native/jni/src/suggest/core/dictionary/dictionary.cpp
@@ -93,8 +93,9 @@ int Dictionary::getProbability(const int *word, int length) const {
return getDictionaryStructurePolicy()->getUnigramProbabilityOfPtNode(pos);
}
-bool Dictionary::isValidBigram(const int *word0, int length0, const int *word1, int length1) const {
- return mBigramDictionary->isValidBigram(word0, length0, word1, length1);
+int Dictionary::getBigramProbability(const int *word0, int length0, const int *word1,
+ int length1) const {
+ return mBigramDictionary->getBigramProbability(word0, length0, word1, length1);
}
void Dictionary::addUnigramWord(const int *const word, const int length, const int probability) {
diff --git a/native/jni/src/suggest/core/dictionary/dictionary.h b/native/jni/src/suggest/core/dictionary/dictionary.h
index 06e84bbfe..974447468 100644
--- a/native/jni/src/suggest/core/dictionary/dictionary.h
+++ b/native/jni/src/suggest/core/dictionary/dictionary.h
@@ -67,7 +67,7 @@ class Dictionary {
int getProbability(const int *word, int length) const;
- bool isValidBigram(const int *word0, int length0, const int *word1, int length1) const;
+ int getBigramProbability(const int *word0, int length0, const int *word1, int length1) const;
void addUnigramWord(const int *const word, const int length, const int probability);
diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.cpp
index 3cfbfd85b..f91dd0e56 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.cpp
@@ -248,7 +248,9 @@ void DynamicPatriciaTriePolicy::flush(const char *const filePath) {
AKLOGI("Warning: flush() is called for non-updatable dictionary.");
return;
}
- // TODO: Implement.
+ DynamicPatriciaTrieWritingHelper writingHelper(&mBufferWithExtendableBuffer,
+ &mBigramListPolicy, &mShortcutListPolicy);
+ writingHelper.writeToDictFile(filePath, &mHeaderPolicy);
}
void DynamicPatriciaTriePolicy::flushWithGC(const char *const filePath) {
diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.h b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.h
index 2cbb0ff3b..ebe1f3212 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.h
@@ -33,7 +33,7 @@ class DicNodeVector;
class DynamicPatriciaTriePolicy : public DictionaryStructureWithBufferPolicy {
public:
DynamicPatriciaTriePolicy(const MmappedBuffer *const buffer)
- : mBuffer(buffer), mHeaderPolicy(mBuffer->getBuffer()),
+ : mBuffer(buffer), mHeaderPolicy(mBuffer->getBuffer(), buffer->getBufferSize()),
mBufferWithExtendableBuffer(mBuffer->getBuffer() + mHeaderPolicy.getSize(),
mBuffer->getBufferSize() - mHeaderPolicy.getSize()),
mShortcutListPolicy(&mBufferWithExtendableBuffer),
diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.cpp b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.cpp
index 311d31e5d..31178fb5c 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.cpp
@@ -16,17 +16,23 @@
#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.h"
+#include <cstdio>
+#include <cstring>
+
#include "suggest/policyimpl/dictionary/bigram/dynamic_bigram_list_policy.h"
#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_node_reader.h"
#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.h"
#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h"
#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.h"
+#include "suggest/policyimpl/dictionary/header/header_policy.h"
#include "suggest/policyimpl/dictionary/patricia_trie_reading_utils.h"
#include "suggest/policyimpl/dictionary/shortcut/dynamic_shortcut_list_policy.h"
namespace latinime {
const int DynamicPatriciaTrieWritingHelper::CHILDREN_POSITION_FIELD_SIZE = 3;
+const char *const DynamicPatriciaTrieWritingHelper::TEMP_FILE_SUFFIX_FOR_WRITING_DICT_FILE =
+ ".tmp";
bool DynamicPatriciaTrieWritingHelper::addUnigramWord(
DynamicPatriciaTrieReadingHelper *const readingHelper,
@@ -131,6 +137,46 @@ bool DynamicPatriciaTrieWritingHelper::removeBigramWords(const int word0Pos, con
return mBigramPolicy->removeBigram(nodeReader.getBigramsPos(), word1Pos);
}
+void DynamicPatriciaTrieWritingHelper::writeToDictFile(const char *const fileName,
+ const HeaderPolicy *const headerPolicy) {
+ BufferWithExtendableBuffer headerBuffer(0 /* originalBuffer */, 0 /* originalBufferSize */);
+ if (!headerPolicy->writeHeaderToBuffer(&headerBuffer, false /* updatesLastUpdatedTime */)) {
+ return;
+ }
+ const int tmpFileNameBufSize = strlen(fileName)
+ + strlen(TEMP_FILE_SUFFIX_FOR_WRITING_DICT_FILE) + 1;
+ char tmpFileName[tmpFileNameBufSize];
+ snprintf(tmpFileName, tmpFileNameBufSize, "%s%s", fileName,
+ TEMP_FILE_SUFFIX_FOR_WRITING_DICT_FILE);
+ FILE *const file = fopen(tmpFileName, "wb");
+ if (!file) {
+ return;
+ }
+ // Write header.
+ if (fwrite(headerBuffer.getBuffer(true /* usesAdditionalBuffer */),
+ headerBuffer.getTailPosition(), 1, file) < 1) {
+ fclose(file);
+ remove(tmpFileName);
+ return;
+ }
+ // Write data in original buffer.
+ if (fwrite(mBuffer->getBuffer(false /* usesAdditionalBuffer */),
+ mBuffer->getOriginalBufferSize(), 1, file) < 1) {
+ fclose(file);
+ remove(tmpFileName);
+ return;
+ }
+ // Write data in additional buffer.
+ if (fwrite(mBuffer->getBuffer(true /* usesAdditionalBuffer */),
+ mBuffer->getTailPosition() - mBuffer->getOriginalBufferSize(), 1, file) < 1) {
+ fclose(file);
+ remove(tmpFileName);
+ return;
+ }
+ fclose(file);
+ rename(tmpFileName, fileName);
+}
+
bool DynamicPatriciaTrieWritingHelper::markNodeAsMovedAndSetPosition(
const DynamicPatriciaTrieNodeReader *const originalNode, const int movedPos,
const int bigramLinkedNodePos) {
diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.h b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.h
index 20e35abcf..219ea9857 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.h
@@ -17,6 +17,8 @@
#ifndef LATINIME_DYNAMIC_PATRICIA_TRIE_WRITING_HELPER_H
#define LATINIME_DYNAMIC_PATRICIA_TRIE_WRITING_HELPER_H
+#include <stdint.h>
+
#include "defines.h"
namespace latinime {
@@ -26,6 +28,7 @@ class DynamicBigramListPolicy;
class DynamicPatriciaTrieNodeReader;
class DynamicPatriciaTrieReadingHelper;
class DynamicShortcutListPolicy;
+class HeaderPolicy;
class DynamicPatriciaTrieWritingHelper {
public:
@@ -46,10 +49,15 @@ class DynamicPatriciaTrieWritingHelper {
// Remove a bigram relation from word0Pos to word1Pos.
bool removeBigramWords(const int word0Pos, const int word1Pos);
+ void writeToDictFile(const char *const fileName, const HeaderPolicy *const headerPolicy);
+
+ void writeToDictFileWithGC(const char *const fileName, const HeaderPolicy *const headerPolicy);
+
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(DynamicPatriciaTrieWritingHelper);
static const int CHILDREN_POSITION_FIELD_SIZE;
+ static const char *const TEMP_FILE_SUFFIX_FOR_WRITING_DICT_FILE;
BufferWithExtendableBuffer *const mBuffer;
DynamicBigramListPolicy *const mBigramPolicy;
diff --git a/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.cpp
index 196da5c97..47ace23a1 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.cpp
@@ -17,6 +17,8 @@
#include "suggest/policyimpl/dictionary/header/header_policy.h"
#include <cstddef>
+#include <cstdio>
+#include <ctime>
namespace latinime {
@@ -36,7 +38,7 @@ void HeaderPolicy::readHeaderValueOrQuestionMark(const char *const key, int *out
}
std::vector<int> keyCodePointVector;
insertCharactersIntoVector(key, &keyCodePointVector);
- HeaderReadingUtils::AttributeMap::const_iterator it = mAttributeMap.find(keyCodePointVector);
+ HeaderReadWriteUtils::AttributeMap::const_iterator it = mAttributeMap.find(keyCodePointVector);
if (it == mAttributeMap.end()) {
// The key was not found.
outValue[0] = '?';
@@ -85,7 +87,7 @@ int HeaderPolicy::readLastUpdatedTime() const {
bool HeaderPolicy::getAttributeValueAsInt(const char *const key, int *const outValue) const {
std::vector<int> keyVector;
insertCharactersIntoVector(key, &keyVector);
- HeaderReadingUtils::AttributeMap::const_iterator it = mAttributeMap.find(keyVector);
+ HeaderReadWriteUtils::AttributeMap::const_iterator it = mAttributeMap.find(keyVector);
if (it == mAttributeMap.end()) {
// The key was not found.
return false;
@@ -94,10 +96,56 @@ bool HeaderPolicy::getAttributeValueAsInt(const char *const key, int *const outV
return true;
}
-/* static */ HeaderReadingUtils::AttributeMap HeaderPolicy::createAttributeMapAndReadAllAttributes(
- const uint8_t *const dictBuf) {
- HeaderReadingUtils::AttributeMap attributeMap;
- HeaderReadingUtils::fetchAllHeaderAttributes(dictBuf, &attributeMap);
+bool HeaderPolicy::writeHeaderToBuffer(BufferWithExtendableBuffer *const bufferToWrite,
+ const bool updatesLastUpdatedTime) const {
+ int writingPos = 0;
+ if (!HeaderReadWriteUtils::writeDictionaryVersion(bufferToWrite, mDictFormatVersion,
+ &writingPos)) {
+ return false;
+ }
+ if (!HeaderReadWriteUtils::writeDictionaryFlags(bufferToWrite, mDictionaryFlags,
+ &writingPos)) {
+ return false;
+ }
+ // Temporarily writes a dummy header size.
+ int headerSizeFieldPos = writingPos;
+ if (!HeaderReadWriteUtils::writeDictionaryHeaderSize(bufferToWrite, 0 /* size */,
+ &writingPos)) {
+ return false;
+ }
+ if (updatesLastUpdatedTime) {
+ // Set current time as a last updated time.
+ HeaderReadWriteUtils::AttributeMap attributeMapTowrite(mAttributeMap);
+ std::vector<int> updatedTimekey;
+ insertCharactersIntoVector(LAST_UPDATED_TIME_KEY, &updatedTimekey);
+ const time_t currentTime = time(NULL);
+ std::vector<int> updatedTimeValue;
+ char charBuf[LARGEST_INT_DIGIT_COUNT + 1];
+ snprintf(charBuf, LARGEST_INT_DIGIT_COUNT + 1, "%ld", currentTime);
+ insertCharactersIntoVector(charBuf, &updatedTimeValue);
+ attributeMapTowrite[updatedTimekey] = updatedTimeValue;
+ if (!HeaderReadWriteUtils::writeHeaderAttributes(bufferToWrite, &attributeMapTowrite,
+ &writingPos)) {
+ return false;
+ }
+ } else {
+ if (!HeaderReadWriteUtils::writeHeaderAttributes(bufferToWrite, &mAttributeMap,
+ &writingPos)) {
+ return false;
+ }
+ }
+ // Writes an actual header size.
+ if (!HeaderReadWriteUtils::writeDictionaryHeaderSize(bufferToWrite, writingPos,
+ &headerSizeFieldPos)) {
+ return false;
+ }
+ return true;
+}
+
+/* static */ HeaderReadWriteUtils::AttributeMap
+ HeaderPolicy::createAttributeMapAndReadAllAttributes(const uint8_t *const dictBuf) {
+ HeaderReadWriteUtils::AttributeMap attributeMap;
+ HeaderReadWriteUtils::fetchAllHeaderAttributes(dictBuf, &attributeMap);
return attributeMap;
}
diff --git a/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h
index 930b475c7..6b396f3f2 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h
@@ -22,15 +22,18 @@
#include "defines.h"
#include "suggest/core/policy/dictionary_header_structure_policy.h"
-#include "suggest/policyimpl/dictionary/header/header_reading_utils.h"
+#include "suggest/policyimpl/dictionary/header/header_read_write_utils.h"
+#include "suggest/policyimpl/dictionary/utils/format_utils.h"
namespace latinime {
class HeaderPolicy : public DictionaryHeaderStructurePolicy {
public:
- explicit HeaderPolicy(const uint8_t *const dictBuf)
- : mDictBuf(dictBuf), mDictionaryFlags(HeaderReadingUtils::getFlags(dictBuf)),
- mSize(HeaderReadingUtils::getHeaderSize(dictBuf)),
+ explicit HeaderPolicy(const uint8_t *const dictBuf, const int dictSize)
+ : mDictBuf(dictBuf),
+ mDictFormatVersion(FormatUtils::detectFormatVersion(dictBuf, dictSize)),
+ mDictionaryFlags(HeaderReadWriteUtils::getFlags(dictBuf)),
+ mSize(HeaderReadWriteUtils::getHeaderSize(dictBuf)),
mAttributeMap(createAttributeMapAndReadAllAttributes(mDictBuf)),
mMultiWordCostMultiplier(readMultipleWordCostMultiplier()),
mUsesForgettingCurve(readUsesForgettingCurveFlag()),
@@ -43,16 +46,15 @@ class HeaderPolicy : public DictionaryHeaderStructurePolicy {
}
AK_FORCE_INLINE bool supportsDynamicUpdate() const {
- return HeaderReadingUtils::supportsDynamicUpdate(mDictionaryFlags);
+ return HeaderReadWriteUtils::supportsDynamicUpdate(mDictionaryFlags);
}
AK_FORCE_INLINE bool requiresGermanUmlautProcessing() const {
- return HeaderReadingUtils::requiresGermanUmlautProcessing(mDictionaryFlags);
+ return HeaderReadWriteUtils::requiresGermanUmlautProcessing(mDictionaryFlags);
}
AK_FORCE_INLINE bool requiresFrenchLigatureProcessing() const {
- return HeaderReadingUtils::requiresFrenchLigatureProcessing(
- mDictionaryFlags);
+ return HeaderReadWriteUtils::requiresFrenchLigatureProcessing(mDictionaryFlags);
}
AK_FORCE_INLINE float getMultiWordCostMultiplier() const {
@@ -70,6 +72,9 @@ class HeaderPolicy : public DictionaryHeaderStructurePolicy {
void readHeaderValueOrQuestionMark(const char *const key,
int *outValue, int outValueSize) const;
+ bool writeHeaderToBuffer(BufferWithExtendableBuffer *const bufferToWrite,
+ const bool updatesLastUpdatedTime) const;
+
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(HeaderPolicy);
@@ -80,9 +85,10 @@ class HeaderPolicy : public DictionaryHeaderStructurePolicy {
static const float MULTIPLE_WORD_COST_MULTIPLIER_SCALE;
const uint8_t *const mDictBuf;
- const HeaderReadingUtils::DictionaryFlags mDictionaryFlags;
+ const FormatUtils::FORMAT_VERSION mDictFormatVersion;
+ const HeaderReadWriteUtils::DictionaryFlags mDictionaryFlags;
const int mSize;
- HeaderReadingUtils::AttributeMap mAttributeMap;
+ HeaderReadWriteUtils::AttributeMap mAttributeMap;
const float mMultiWordCostMultiplier;
const bool mUsesForgettingCurve;
const int mLastUpdatedTime;
@@ -95,7 +101,7 @@ class HeaderPolicy : public DictionaryHeaderStructurePolicy {
bool getAttributeValueAsInt(const char *const key, int *const outValue) const;
- static HeaderReadingUtils::AttributeMap createAttributeMapAndReadAllAttributes(
+ static HeaderReadWriteUtils::AttributeMap createAttributeMapAndReadAllAttributes(
const uint8_t *const dictBuf);
static int parseIntAttributeValue(const std::vector<int> *const attributeValue);
diff --git a/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.cpp
new file mode 100644
index 000000000..80fe88671
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2013, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "suggest/policyimpl/dictionary/header/header_read_write_utils.h"
+
+#include <vector>
+
+#include "defines.h"
+#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h"
+#include "suggest/policyimpl/dictionary/utils/byte_array_utils.h"
+
+namespace latinime {
+
+const int HeaderReadWriteUtils::MAX_ATTRIBUTE_KEY_LENGTH = 256;
+const int HeaderReadWriteUtils::MAX_ATTRIBUTE_VALUE_LENGTH = 256;
+
+const int HeaderReadWriteUtils::HEADER_MAGIC_NUMBER_SIZE = 4;
+const int HeaderReadWriteUtils::HEADER_DICTIONARY_VERSION_SIZE = 2;
+const int HeaderReadWriteUtils::HEADER_FLAG_SIZE = 2;
+const int HeaderReadWriteUtils::HEADER_SIZE_FIELD_SIZE = 4;
+
+const HeaderReadWriteUtils::DictionaryFlags HeaderReadWriteUtils::NO_FLAGS = 0;
+// Flags for special processing
+// Those *must* match the flags in makedict (FormatSpec#*_PROCESSING_FLAG) or
+// something very bad (like, the apocalypse) will happen. Please update both at the same time.
+const HeaderReadWriteUtils::DictionaryFlags
+ HeaderReadWriteUtils::GERMAN_UMLAUT_PROCESSING_FLAG = 0x1;
+const HeaderReadWriteUtils::DictionaryFlags
+ HeaderReadWriteUtils::SUPPORTS_DYNAMIC_UPDATE_FLAG = 0x2;
+const HeaderReadWriteUtils::DictionaryFlags
+ HeaderReadWriteUtils::FRENCH_LIGATURE_PROCESSING_FLAG = 0x4;
+
+/* static */ int HeaderReadWriteUtils::getHeaderSize(const uint8_t *const dictBuf) {
+ // See the format of the header in the comment in
+ // BinaryDictionaryFormatUtils::detectFormatVersion()
+ return ByteArrayUtils::readUint32(dictBuf, HEADER_MAGIC_NUMBER_SIZE
+ + HEADER_DICTIONARY_VERSION_SIZE + HEADER_FLAG_SIZE);
+}
+
+/* static */ HeaderReadWriteUtils::DictionaryFlags
+ HeaderReadWriteUtils::getFlags(const uint8_t *const dictBuf) {
+ return ByteArrayUtils::readUint16(dictBuf,
+ HEADER_MAGIC_NUMBER_SIZE + HEADER_DICTIONARY_VERSION_SIZE);
+}
+
+/* static */ void HeaderReadWriteUtils::fetchAllHeaderAttributes(const uint8_t *const dictBuf,
+ AttributeMap *const headerAttributes) {
+ const int headerSize = getHeaderSize(dictBuf);
+ int pos = getHeaderOptionsPosition();
+ if (pos == NOT_A_DICT_POS) {
+ // The header doesn't have header options.
+ return;
+ }
+ int keyBuffer[MAX_ATTRIBUTE_KEY_LENGTH];
+ int valueBuffer[MAX_ATTRIBUTE_VALUE_LENGTH];
+ while (pos < headerSize) {
+ const int keyLength = ByteArrayUtils::readStringAndAdvancePosition(dictBuf,
+ MAX_ATTRIBUTE_KEY_LENGTH, keyBuffer, &pos);
+ std::vector<int> key;
+ key.insert(key.end(), keyBuffer, keyBuffer + keyLength);
+ const int valueLength = ByteArrayUtils::readStringAndAdvancePosition(dictBuf,
+ MAX_ATTRIBUTE_VALUE_LENGTH, valueBuffer, &pos);
+ std::vector<int> value;
+ value.insert(value.end(), valueBuffer, valueBuffer + valueLength);
+ headerAttributes->insert(AttributeMap::value_type(key, value));
+ }
+}
+
+/* static */ bool HeaderReadWriteUtils::writeDictionaryVersion(
+ BufferWithExtendableBuffer *const buffer, const FormatUtils::FORMAT_VERSION version,
+ int *const writingPos) {
+ if (!buffer->writeUintAndAdvancePosition(FormatUtils::MAGIC_NUMBER, HEADER_MAGIC_NUMBER_SIZE,
+ writingPos)) {
+ return false;
+ }
+ switch (version) {
+ case FormatUtils::VERSION_2:
+ // Version 2 dictionary writing is not supported.
+ return false;
+ case FormatUtils::VERSION_3:
+ return buffer->writeUintAndAdvancePosition(3 /* data */,
+ HEADER_DICTIONARY_VERSION_SIZE, writingPos);
+ default:
+ return false;
+ }
+}
+
+/* static */ bool HeaderReadWriteUtils::writeDictionaryFlags(
+ BufferWithExtendableBuffer *const buffer, const DictionaryFlags flags,
+ int *const writingPos) {
+ return buffer->writeUintAndAdvancePosition(flags, HEADER_FLAG_SIZE, writingPos);
+}
+
+/* static */ bool HeaderReadWriteUtils::writeDictionaryHeaderSize(
+ BufferWithExtendableBuffer *const buffer, const int size, int *const writingPos) {
+ return buffer->writeUintAndAdvancePosition(size, HEADER_SIZE_FIELD_SIZE, writingPos);
+}
+
+/* static */ bool HeaderReadWriteUtils::writeHeaderAttributes(
+ BufferWithExtendableBuffer *const buffer, const AttributeMap *const headerAttributes,
+ int *const writingPos) {
+ for (AttributeMap::const_iterator it = headerAttributes->begin();
+ it != headerAttributes->end(); ++it) {
+ // Write a key.
+ if (!buffer->writeCodePointsAndAdvancePosition(&(it->first.at(0)), it->first.size(),
+ true /* writesTerminator */, writingPos)) {
+ return false;
+ }
+ // Write a value.
+ if (!buffer->writeCodePointsAndAdvancePosition(&(it->second.at(0)), it->second.size(),
+ true /* writesTerminator */, writingPos)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace latinime
diff --git a/native/jni/src/suggest/policyimpl/dictionary/header/header_reading_utils.h b/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.h
index 5716198fb..6cce73375 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/header/header_reading_utils.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.h
@@ -14,18 +14,21 @@
* limitations under the License.
*/
-#ifndef LATINIME_HEADER_READING_UTILS_H
-#define LATINIME_HEADER_READING_UTILS_H
+#ifndef LATINIME_HEADER_READ_WRITE_UTILS_H
+#define LATINIME_HEADER_READ_WRITE_UTILS_H
#include <map>
#include <stdint.h>
#include <vector>
#include "defines.h"
+#include "suggest/policyimpl/dictionary/utils/format_utils.h"
namespace latinime {
-class HeaderReadingUtils {
+class BufferWithExtendableBuffer;
+
+class HeaderReadWriteUtils {
public:
typedef uint16_t DictionaryFlags;
typedef std::map<std::vector<int>, std::vector<int> > AttributeMap;
@@ -54,8 +57,20 @@ class HeaderReadingUtils {
static void fetchAllHeaderAttributes(const uint8_t *const dictBuf,
AttributeMap *const headerAttributes);
+ static bool writeDictionaryVersion(BufferWithExtendableBuffer *const buffer,
+ const FormatUtils::FORMAT_VERSION version, int *const writingPos);
+
+ static bool writeDictionaryFlags(BufferWithExtendableBuffer *const buffer,
+ const DictionaryFlags flags, int *const writingPos);
+
+ static bool writeDictionaryHeaderSize(BufferWithExtendableBuffer *const buffer,
+ const int size, int *const writingPos);
+
+ static bool writeHeaderAttributes(BufferWithExtendableBuffer *const buffer,
+ const AttributeMap *const headerAttributes, int *const writingPos);
+
private:
- DISALLOW_IMPLICIT_CONSTRUCTORS(HeaderReadingUtils);
+ DISALLOW_IMPLICIT_CONSTRUCTORS(HeaderReadWriteUtils);
static const int MAX_ATTRIBUTE_KEY_LENGTH;
static const int MAX_ATTRIBUTE_VALUE_LENGTH;
@@ -75,4 +90,4 @@ class HeaderReadingUtils {
static const DictionaryFlags CONTAINS_BIGRAMS_FLAG;
};
}
-#endif /* LATINIME_HEADER_READING_UTILS_H */
+#endif /* LATINIME_HEADER_READ_WRITE_UTILS_H */
diff --git a/native/jni/src/suggest/policyimpl/dictionary/header/header_reading_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/header/header_reading_utils.cpp
deleted file mode 100644
index 186c043c1..000000000
--- a/native/jni/src/suggest/policyimpl/dictionary/header/header_reading_utils.cpp
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2013, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "suggest/policyimpl/dictionary/header/header_reading_utils.h"
-
-#include <vector>
-
-#include "defines.h"
-#include "suggest/policyimpl/dictionary/utils/byte_array_utils.h"
-
-namespace latinime {
-
-const int HeaderReadingUtils::MAX_ATTRIBUTE_KEY_LENGTH = 256;
-const int HeaderReadingUtils::MAX_ATTRIBUTE_VALUE_LENGTH = 256;
-
-const int HeaderReadingUtils::HEADER_MAGIC_NUMBER_SIZE = 4;
-const int HeaderReadingUtils::HEADER_DICTIONARY_VERSION_SIZE = 2;
-const int HeaderReadingUtils::HEADER_FLAG_SIZE = 2;
-const int HeaderReadingUtils::HEADER_SIZE_FIELD_SIZE = 4;
-
-const HeaderReadingUtils::DictionaryFlags HeaderReadingUtils::NO_FLAGS = 0;
-// Flags for special processing
-// Those *must* match the flags in makedict (FormatSpec#*_PROCESSING_FLAG) or
-// something very bad (like, the apocalypse) will happen. Please update both at the same time.
-const HeaderReadingUtils::DictionaryFlags
- HeaderReadingUtils::GERMAN_UMLAUT_PROCESSING_FLAG = 0x1;
-const HeaderReadingUtils::DictionaryFlags
- HeaderReadingUtils::SUPPORTS_DYNAMIC_UPDATE_FLAG = 0x2;
-const HeaderReadingUtils::DictionaryFlags
- HeaderReadingUtils::FRENCH_LIGATURE_PROCESSING_FLAG = 0x4;
-
-/* static */ int HeaderReadingUtils::getHeaderSize(const uint8_t *const dictBuf) {
- // See the format of the header in the comment in
- // BinaryDictionaryFormatUtils::detectFormatVersion()
- return ByteArrayUtils::readUint32(dictBuf, HEADER_MAGIC_NUMBER_SIZE
- + HEADER_DICTIONARY_VERSION_SIZE + HEADER_FLAG_SIZE);
-}
-
-/* static */ HeaderReadingUtils::DictionaryFlags
- HeaderReadingUtils::getFlags(const uint8_t *const dictBuf) {
- return ByteArrayUtils::readUint16(dictBuf,
- HEADER_MAGIC_NUMBER_SIZE + HEADER_DICTIONARY_VERSION_SIZE);
-}
-
-/* static */ void HeaderReadingUtils::fetchAllHeaderAttributes(const uint8_t *const dictBuf,
- AttributeMap *const headerAttributes) {
- const int headerSize = getHeaderSize(dictBuf);
- int pos = getHeaderOptionsPosition();
- if (pos == NOT_A_DICT_POS) {
- // The header doesn't have header options.
- return;
- }
- int keyBuffer[MAX_ATTRIBUTE_KEY_LENGTH];
- int valueBuffer[MAX_ATTRIBUTE_VALUE_LENGTH];
- while (pos < headerSize) {
- const int keyLength = ByteArrayUtils::readStringAndAdvancePosition(dictBuf,
- MAX_ATTRIBUTE_KEY_LENGTH, keyBuffer, &pos);
- std::vector<int> key;
- key.insert(key.end(), keyBuffer, keyBuffer + keyLength);
- const int valueLength = ByteArrayUtils::readStringAndAdvancePosition(dictBuf,
- MAX_ATTRIBUTE_VALUE_LENGTH, valueBuffer, &pos);
- std::vector<int> value;
- value.insert(value.end(), valueBuffer, valueBuffer + valueLength);
- headerAttributes->insert(AttributeMap::value_type(key, value));
- }
-}
-
-} // namespace latinime
diff --git a/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.h b/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.h
index cee3e4ab2..697d0159c 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/patricia_trie_policy.h
@@ -34,7 +34,7 @@ class DicNodeVector;
class PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy {
public:
PatriciaTriePolicy(const MmappedBuffer *const buffer)
- : mBuffer(buffer), mHeaderPolicy(mBuffer->getBuffer()),
+ : mBuffer(buffer), mHeaderPolicy(mBuffer->getBuffer(), buffer->getBufferSize()),
mDictRoot(mBuffer->getBuffer() + mHeaderPolicy.getSize()),
mBigramListPolicy(mDictRoot), mShortcutListPolicy(mDictRoot) {}
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.cpp
index 3796c7b7b..1d77d5c27 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.cpp
@@ -20,20 +20,10 @@
namespace latinime {
-/**
- * Dictionary size
- */
-// Any file smaller than this is not a dictionary.
-const int FormatUtils::DICTIONARY_MINIMUM_SIZE = 4;
+const uint32_t FormatUtils::MAGIC_NUMBER = 0x9BC13AFE;
-/**
- * Format versions
- */
-// 32 bit magic number is stored at the beginning of the dictionary header to reject unsupported
-// or obsolete dictionary formats.
-const uint32_t FormatUtils::HEADER_VERSION_2_MAGIC_NUMBER = 0x9BC13AFE;
-// Magic number (4 bytes), version (2 bytes), options (2 bytes), header size (4 bytes) = 12
-const int FormatUtils::HEADER_VERSION_2_MINIMUM_SIZE = 12;
+// Magic number (4 bytes), version (2 bytes), flags (2 bytes), header size (4 bytes) = 12
+const int FormatUtils::DICTIONARY_MINIMUM_SIZE = 12;
/* static */ FormatUtils::FORMAT_VERSION FormatUtils::detectFormatVersion(
const uint8_t *const dict, const int dictSize) {
@@ -45,16 +35,10 @@ const int FormatUtils::HEADER_VERSION_2_MINIMUM_SIZE = 12;
}
const uint32_t magicNumber = ByteArrayUtils::readUint32(dict, 0);
switch (magicNumber) {
- case HEADER_VERSION_2_MAGIC_NUMBER:
- // Version 2 header are at least 12 bytes long.
- // If this header has the version 2 magic number but is less than 12 bytes long,
- // then it's an unknown format and we need to avoid confidently reading the next bytes.
- if (dictSize < HEADER_VERSION_2_MINIMUM_SIZE) {
- return UNKNOWN_VERSION;
- }
+ case MAGIC_NUMBER:
// Version 2 header is as follows:
// Magic number (4 bytes) 0x9B 0xC1 0x3A 0xFE
- // Version number (2 bytes)
+ // Dictionary format version number (2 bytes)
// Options (2 bytes)
// Header size (4 bytes) : integer, big endian
if (ByteArrayUtils::readUint16(dict, 4) == 2) {
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.h b/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.h
index f84321577..79ed0de29 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.h
@@ -34,14 +34,16 @@ class FormatUtils {
UNKNOWN_VERSION
};
+ // 32 bit magic number is stored at the beginning of the dictionary header to reject
+ // unsupported or obsolete dictionary formats.
+ static const uint32_t MAGIC_NUMBER;
+
static FORMAT_VERSION detectFormatVersion(const uint8_t *const dict, const int dictSize);
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(FormatUtils);
static const int DICTIONARY_MINIMUM_SIZE;
- static const uint32_t HEADER_VERSION_2_MAGIC_NUMBER;
- static const int HEADER_VERSION_2_MINIMUM_SIZE;
};
} // namespace latinime
#endif /* LATINIME_FORMAT_UTILS_H */
diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java
index 4d231cde7..00d76c990 100644
--- a/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java
+++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java
@@ -151,7 +151,7 @@ public class BinaryDictionaryTests extends AndroidTestCase {
final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random);
for (int i = 0; i < wordCount; ++i) {
final String word = CodePointUtils.generateWord(random, codePointSet);
- probabilityMap.put(word, random.nextInt() & 0xFF);
+ probabilityMap.put(word, random.nextInt(0xFF));
}
for (String word : probabilityMap.keySet()) {
binaryDictionary.addUnigramWord(word, probabilityMap.get(word));
@@ -163,8 +163,6 @@ public class BinaryDictionaryTests extends AndroidTestCase {
}
public void testAddBigramWords() {
- // TODO: Add a test to check the frequency of the bigram score which uses current value
- // calculated in the native code
File dictFile = null;
try {
dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary");
@@ -179,6 +177,7 @@ public class BinaryDictionaryTests extends AndroidTestCase {
final int unigramProbability = 100;
final int bigramProbability = 10;
+ final int updatedBigramProbability = 15;
binaryDictionary.addUnigramWord("aaa", unigramProbability);
binaryDictionary.addUnigramWord("abb", unigramProbability);
binaryDictionary.addUnigramWord("bcc", unigramProbability);
@@ -187,21 +186,49 @@ public class BinaryDictionaryTests extends AndroidTestCase {
binaryDictionary.addBigramWords("abb", "aaa", bigramProbability);
binaryDictionary.addBigramWords("abb", "bcc", bigramProbability);
+ final int probability = binaryDictionary.calculateProbability(unigramProbability,
+ bigramProbability);
assertEquals(true, binaryDictionary.isValidBigram("aaa", "abb"));
assertEquals(true, binaryDictionary.isValidBigram("aaa", "bcc"));
assertEquals(true, binaryDictionary.isValidBigram("abb", "aaa"));
assertEquals(true, binaryDictionary.isValidBigram("abb", "bcc"));
+ assertEquals(probability, binaryDictionary.getBigramProbability("aaa", "abb"));
+ assertEquals(probability, binaryDictionary.getBigramProbability("aaa", "bcc"));
+ assertEquals(probability, binaryDictionary.getBigramProbability("abb", "aaa"));
+ assertEquals(probability, binaryDictionary.getBigramProbability("abb", "bcc"));
+
+ binaryDictionary.addBigramWords("aaa", "abb", updatedBigramProbability);
+ final int updatedProbability = binaryDictionary.calculateProbability(unigramProbability,
+ updatedBigramProbability);
+ assertEquals(updatedProbability, binaryDictionary.getBigramProbability("aaa", "abb"));
assertEquals(false, binaryDictionary.isValidBigram("bcc", "aaa"));
assertEquals(false, binaryDictionary.isValidBigram("bcc", "bbc"));
assertEquals(false, binaryDictionary.isValidBigram("aaa", "aaa"));
+ assertEquals(Dictionary.NOT_A_PROBABILITY,
+ binaryDictionary.getBigramProbability("bcc", "aaa"));
+ assertEquals(Dictionary.NOT_A_PROBABILITY,
+ binaryDictionary.getBigramProbability("bcc", "bbc"));
+ assertEquals(Dictionary.NOT_A_PROBABILITY,
+ binaryDictionary.getBigramProbability("aaa", "aaa"));
+
+ // Testing bigram link.
+ binaryDictionary.addUnigramWord("abcde", unigramProbability);
+ binaryDictionary.addUnigramWord("fghij", unigramProbability);
+ binaryDictionary.addBigramWords("abcde", "fghij", bigramProbability);
+ binaryDictionary.addUnigramWord("fgh", unigramProbability);
+ binaryDictionary.addUnigramWord("abc", unigramProbability);
+ binaryDictionary.addUnigramWord("f", unigramProbability);
+ assertEquals(probability, binaryDictionary.getBigramProbability("abcde", "fghij"));
+ assertEquals(Dictionary.NOT_A_PROBABILITY,
+ binaryDictionary.getBigramProbability("abcde", "fgh"));
+ binaryDictionary.addBigramWords("abcde", "fghij", updatedBigramProbability);
+ assertEquals(updatedProbability, binaryDictionary.getBigramProbability("abcde", "fghij"));
dictFile.delete();
}
public void testRandomlyAddBigramWords() {
- // TODO: Add a test to check the frequency of the bigram score which uses current value
- // calculated in the native code
final int wordCount = 100;
final int bigramCount = 1000;
final int codePointSetSize = 50;
@@ -222,29 +249,38 @@ public class BinaryDictionaryTests extends AndroidTestCase {
// Test a word that isn't contained within the dictionary.
final Random random = new Random(seed);
final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random);
- final int unigramProbability = 100;
- final int bigramProbability = 10;
+ final int[] unigramProbabilities = new int[wordCount];
for (int i = 0; i < wordCount; ++i) {
final String word = CodePointUtils.generateWord(random, codePointSet);
words.add(word);
+ final int unigramProbability = random.nextInt(0xFF);
+ unigramProbabilities[i] = unigramProbability;
binaryDictionary.addUnigramWord(word, unigramProbability);
}
- final boolean[][] bigramRelations = new boolean[wordCount][wordCount];
+ final int[][] probabilities = new int[wordCount][wordCount];
+
+ for (int i = 0; i < wordCount; ++i) {
+ for (int j = 0; j < wordCount; ++j) {
+ probabilities[i][j] = Dictionary.NOT_A_PROBABILITY;
+ }
+ }
+
for (int i = 0; i < bigramCount; i++) {
final int word0Index = random.nextInt(wordCount);
final int word1Index = random.nextInt(wordCount);
final String word0 = words.get(word0Index);
final String word1 = words.get(word1Index);
-
- bigramRelations[word0Index][word1Index] = true;
+ final int bigramProbability = random.nextInt(0xF);
+ probabilities[word0Index][word1Index] = binaryDictionary.calculateProbability(
+ unigramProbabilities[word1Index], bigramProbability);
binaryDictionary.addBigramWords(word0, word1, bigramProbability);
}
for (int i = 0; i < words.size(); i++) {
for (int j = 0; j < words.size(); j++) {
- assertEquals(bigramRelations[i][j],
- binaryDictionary.isValidBigram(words.get(i), words.get(j)));
+ assertEquals(probabilities[i][j],
+ binaryDictionary.getBigramProbability(words.get(i), words.get(j)));
}
}
@@ -263,7 +299,6 @@ public class BinaryDictionaryTests extends AndroidTestCase {
BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
-
final int unigramProbability = 100;
final int bigramProbability = 10;
binaryDictionary.addUnigramWord("aaa", unigramProbability);
@@ -299,4 +334,54 @@ public class BinaryDictionaryTests extends AndroidTestCase {
dictFile.delete();
}
+
+ public void testFlushDictionary() {
+ File dictFile = null;
+ try {
+ dictFile = createEmptyDictionaryAndGetFile("TestBinaryDictionary");
+ } catch (IOException e) {
+ fail("IOException while writing an initial dictionary : " + e);
+ } catch (UnsupportedFormatException e) {
+ fail("UnsupportedFormatException while writing an initial dictionary : " + e);
+ }
+ BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
+ 0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
+ Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+
+ final int probability = 100;
+ binaryDictionary.addUnigramWord("aaa", probability);
+ binaryDictionary.addUnigramWord("abcd", probability);
+ // Close without flushing.
+ binaryDictionary.close();
+
+ binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
+ 0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
+ Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+
+ assertEquals(Dictionary.NOT_A_PROBABILITY, binaryDictionary.getFrequency("aaa"));
+ assertEquals(Dictionary.NOT_A_PROBABILITY, binaryDictionary.getFrequency("abcd"));
+
+ binaryDictionary.addUnigramWord("aaa", probability);
+ binaryDictionary.addUnigramWord("abcd", probability);
+ binaryDictionary.flush();
+ binaryDictionary.close();
+
+ binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
+ 0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
+ Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+
+ assertEquals(probability, binaryDictionary.getFrequency("aaa"));
+ assertEquals(probability, binaryDictionary.getFrequency("abcd"));
+ binaryDictionary.addUnigramWord("bcde", probability);
+ binaryDictionary.flush();
+ binaryDictionary.close();
+
+ binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
+ 0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
+ Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
+ assertEquals(probability, binaryDictionary.getFrequency("bcde"));
+ binaryDictionary.close();
+
+ dictFile.delete();
+ }
}
diff --git a/tests/src/com/android/inputmethod/latin/utils/StringUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/StringUtilsTests.java
index c6fa943fe..4e396a1cf 100644
--- a/tests/src/com/android/inputmethod/latin/utils/StringUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/utils/StringUtilsTests.java
@@ -21,6 +21,8 @@ import com.android.inputmethod.latin.settings.SettingsValues;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
+import java.util.Arrays;
+import java.util.List;
import java.util.Locale;
@SmallTest
@@ -268,4 +270,14 @@ public class StringUtilsTests extends AndroidTestCase {
final String bytesStr2 = StringUtils.byteArrayToHexString(bytes2);
assertTrue(bytesStr.equals(bytesStr2));
}
+
+ public void testJsonStringUtils() {
+ final Object[] objs = new Object[] { 1, "aaa", "bbb", 3 };
+ final List<Object> objArray = Arrays.asList(objs);
+ final String str = StringUtils.listToJsonStr(objArray);
+ final List<Object> newObjArray = StringUtils.jsonStrToList(str);
+ for (int i = 0; i < objs.length; ++i) {
+ assertEquals(objs[i], newObjArray.get(i));
+ }
+ }
}