diff options
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)); + } + } } |