Merge remote-tracking branch 'goog/jb-mr1-dev' into mergescriptpackage
This commit is contained in:
commit
379aeb1836
33 changed files with 1063 additions and 501 deletions
|
@ -100,7 +100,7 @@
|
|||
<fraction name="center_suggestion_percentile">36%</fraction>
|
||||
|
||||
<!-- Gesture preview trail parameters -->
|
||||
<dimen name="gesture_preview_trail_start_width">18.0dp</dimen>
|
||||
<dimen name="gesture_preview_trail_start_width">12.6dp</dimen>
|
||||
<dimen name="gesture_preview_trail_end_width">2.5dp</dimen>
|
||||
<!-- Gesture floating preview text parameters -->
|
||||
<dimen name="gesture_floating_preview_text_size">24dp</dimen>
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
<!-- Build.HARDWARE,duration_in_milliseconds -->
|
||||
<item>herring,5</item>
|
||||
<item>tuna,5</item>
|
||||
<item>mako,20</item>
|
||||
<item>mako,5</item>
|
||||
<item>manta,16</item>
|
||||
</string-array>
|
||||
</resources>
|
||||
|
|
|
@ -61,8 +61,10 @@
|
|||
<string name="include_other_imes_in_language_switch_list">Switch to other input methods</string>
|
||||
<!-- Option summary for including other IMEs in the language switch list [CHAR LIMIT=65] -->
|
||||
<string name="include_other_imes_in_language_switch_list_summary">Language switch key covers other input methods too</string>
|
||||
<!-- Option to suppress language switch key [CHAR LIMIT=30] -->
|
||||
<string name="suppress_language_switch_key">Suppress language switch key</string>
|
||||
<!-- Option to show language switch key [CHAR LIMIT=30] -->
|
||||
<string name="show_language_switch_key">Language switch key</string>
|
||||
<!-- Option summary for showing language switch key [CHAR LIMIT=65] -->
|
||||
<string name="show_language_switch_key_summary">Show when multiple input languages are enabled</string>
|
||||
|
||||
<!-- Option for the dismiss delay of the key popup [CHAR LIMIT=25] -->
|
||||
<string name="key_preview_popup_dismiss_delay">Key popup dismiss delay</string>
|
||||
|
|
|
@ -108,10 +108,11 @@
|
|||
android:persistent="true"
|
||||
android:defaultValue="true" />
|
||||
<CheckBoxPreference
|
||||
android:key="pref_suppress_language_switch_key"
|
||||
android:title="@string/suppress_language_switch_key"
|
||||
android:key="pref_show_language_switch_key"
|
||||
android:title="@string/show_language_switch_key"
|
||||
android:summary="@string/show_language_switch_key_summary"
|
||||
android:persistent="true"
|
||||
android:defaultValue="false" />
|
||||
android:defaultValue="true" />
|
||||
<CheckBoxPreference
|
||||
android:key="pref_include_other_imes_in_language_switch_list"
|
||||
android:title="@string/include_other_imes_in_language_switch_list"
|
||||
|
@ -138,11 +139,11 @@
|
|||
android:persistent="true"
|
||||
android:defaultValue="true" />
|
||||
<CheckBoxPreference
|
||||
android:key="pref_gesture_floating_preview_text"
|
||||
android:key="pref_show_gesture_floating_preview_text"
|
||||
android:title="@string/gesture_floating_preview_text"
|
||||
android:summary="@string/gesture_floating_preview_text_summary"
|
||||
android:persistent="true"
|
||||
android:defaultValue="true" />
|
||||
android:defaultValue="false" />
|
||||
</PreferenceScreen>
|
||||
</PreferenceCategory>
|
||||
</PreferenceScreen>
|
||||
|
|
|
@ -29,50 +29,61 @@
|
|||
U+0912/U+0902: "ऒं" DEVANAGARI LETTER SHORT O//DEVANAGARI SIGN ANUSVARA -->
|
||||
<Key
|
||||
latin:keyLabel="औ"
|
||||
latin:moreKeys="ऒं" />
|
||||
latin:moreKeys="ऒं"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+0910: "ऐ" DEVANAGARI LETTER AI
|
||||
U+0910/U+0902: "ऐं" DEVANAGARI LETTER AI/DEVANAGARI SIGN ANUSVARA -->
|
||||
<Key
|
||||
latin:keyLabel="ऐ"
|
||||
latin:moreKeys="ऐं" />
|
||||
latin:moreKeys="ऐं"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+0906: "आ" DEVANAGARI LETTER AA
|
||||
U+0906/U+0902: "आं" DEVANAGARI LETTER AA/DEVANAGARI SIGN ANUSVARA
|
||||
U+0906/U+0901: "आँ" DEVANAGARI LETTER AA/DEVANAGARI SIGN CANDRABINDU -->
|
||||
<Key
|
||||
latin:keyLabel="आ"
|
||||
latin:moreKeys="आं,आँ" />
|
||||
latin:moreKeys="आं,आँ"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+0908: "ई" DEVANAGARI LETTER II
|
||||
U+0908/U+0902: "ईं" DEVANAGARI LETTER II/DEVANAGARI SIGN ANUSVARA -->
|
||||
<Key
|
||||
latin:keyLabel="ई"
|
||||
latin:moreKeys="ईं" />
|
||||
latin:moreKeys="ईं"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+090A: "ऊ" DEVANAGARI LETTER UU
|
||||
U+090A/U+0902: "ऊं" DEVANAGARI LETTER UU/DEVANAGARI SIGN ANUSVARA
|
||||
U+090A/U+0901: "ऊँ" DEVANAGARI LETTER UU/DEVANAGARI SIGN CANDRABINDU -->
|
||||
<Key
|
||||
latin:keyLabel="ऊ"
|
||||
latin:moreKeys="ऊं,ऊँ" />
|
||||
latin:moreKeys="ऊं,ऊँ"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+092D: "भ" DEVANAGARI LETTER BHA -->
|
||||
<Key
|
||||
latin:keyLabel="भ" />
|
||||
latin:keyLabel="भ"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+0903: "ः" DEVANAGARI SIGN VISARGA -->
|
||||
<Key
|
||||
latin:keyLabel="ः" />
|
||||
latin:keyLabel="ः"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+0918: "घ" DEVANAGARI LETTER GHA -->
|
||||
<Key
|
||||
latin:keyLabel="घ" />
|
||||
latin:keyLabel="घ"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+0927: "ध" DEVANAGARI LETTER DHA
|
||||
U+0915/U+094D/U+0937: "क्ष" DEVANAGARI LETTER KA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER SSA
|
||||
U+0936/U+094D/U+0930: "श्र" DEVANAGARI LETTER SHA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER RA -->
|
||||
<Key
|
||||
latin:keyLabel="ध"
|
||||
latin:moreKeys="क्ष,श्र" />
|
||||
latin:moreKeys="क्ष,श्र"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+091D: "झ" DEVANAGARI LETTER JHA -->
|
||||
<Key
|
||||
latin:keyLabel="झ" />
|
||||
latin:keyLabel="झ"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+0922: "ढ" DEVANAGARI LETTER DDHA -->
|
||||
<Key
|
||||
latin:keyLabel="ढ" />
|
||||
latin:keyLabel="ढ"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
</case>
|
||||
<default>
|
||||
<!-- U+094C: "ौ" DEVANAGARI VOWEL SIGN AU
|
||||
|
@ -82,7 +93,8 @@
|
|||
latin:keyLabel="ौ"
|
||||
latin:moreKeys="ौं,%"
|
||||
latin:keyHintLabel="1"
|
||||
latin:additionalMoreKeys="१,1" />
|
||||
latin:additionalMoreKeys="१,1"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+0948: "ै" DEVANAGARI VOWEL SIGN AI
|
||||
U+0948/U+0902: "ैं" DEVANAGARI VOWEL SIGN AI/DEVANAGARI SIGN ANUSVARA
|
||||
U+0968: "२" DEVANAGARI DIGIT TWO -->
|
||||
|
@ -90,7 +102,8 @@
|
|||
latin:keyLabel="ै"
|
||||
latin:moreKeys="ैं,%"
|
||||
latin:keyHintLabel="2"
|
||||
latin:additionalMoreKeys="२,2" />
|
||||
latin:additionalMoreKeys="२,2"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+093E: "ा" DEVANAGARI VOWEL SIGN AA
|
||||
U+093E/U+0902: "ां" DEVANAGARI VOWEL SIGN AA/DEVANAGARI SIGN ANUSVARA
|
||||
U+093E/U+0901: "ाँ" DEVANAGARI VOWEL SIGN AA/DEVANAGARI SIGN CANDRABINDU
|
||||
|
@ -99,7 +112,8 @@
|
|||
latin:keyLabel="ा"
|
||||
latin:moreKeys="ां,ाँ,%"
|
||||
latin:keyHintLabel="3"
|
||||
latin:additionalMoreKeys="३,3" />
|
||||
latin:additionalMoreKeys="३,3"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+0940: "ी" DEVANAGARI VOWEL SIGN II
|
||||
U+0940/U+0902: "ीं" DEVANAGARI VOWEL SIGN II/DEVANAGARI SIGN ANUSVARA
|
||||
U+096A: "४" DEVANAGARI DIGIT FOUR -->
|
||||
|
@ -107,7 +121,8 @@
|
|||
latin:keyLabel="ी"
|
||||
latin:moreKeys="ीं,%"
|
||||
latin:keyHintLabel="4"
|
||||
latin:additionalMoreKeys="४,4" />
|
||||
latin:additionalMoreKeys="४,4"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+0942: "ू" DEVANAGARI VOWEL SIGN UU
|
||||
U+0942/U+0902: "ूं" DEVANAGARI VOWEL SIGN UU/DEVANAGARI SIGN ANUSVARA
|
||||
U+0942/U+0901: "ूँ" DEVANAGARI VOWEL SIGN UU/DEVANAGARI SIGN CANDRABINDU
|
||||
|
@ -116,20 +131,23 @@
|
|||
latin:keyLabel="ू"
|
||||
latin:moreKeys="ूं,ूँ,%"
|
||||
latin:keyHintLabel="5"
|
||||
latin:additionalMoreKeys="५,5" />
|
||||
latin:additionalMoreKeys="५,5"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+092C: "ब" DEVANAGARI LETTER BA
|
||||
U+092C/U+0952: "ब॒" DEVANAGARI LETTER BA/DEVANAGARI STRESS SIGN ANUDATTA -->
|
||||
<Key
|
||||
latin:keyLabel="ब"
|
||||
latin:moreKeys="ब॒,%"
|
||||
latin:keyHintLabel="6"
|
||||
latin:additionalMoreKeys="६,6" />
|
||||
latin:additionalMoreKeys="६,6"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+0939: "ह" DEVANAGARI LETTER HA
|
||||
U+096D: "७" DEVANAGARI DIGIT SEVEN -->
|
||||
<Key
|
||||
latin:keyLabel="ह"
|
||||
latin:keyHintLabel="7"
|
||||
latin:additionalMoreKeys="७,7" />
|
||||
latin:additionalMoreKeys="७,7"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+0917: "ग" DEVANAGARI LETTER GA
|
||||
U+091C/U+094D/U+091E: "ज्ञ" DEVANAGARI LETTER JA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER NYA
|
||||
U+0917/U+093C: "ग़" DEVANAGARI LETTER GA/DEVANAGARI SIGN NUKTA
|
||||
|
@ -139,13 +157,15 @@
|
|||
latin:keyLabel="ग"
|
||||
latin:moreKeys="ज्ञ,ग़,ग॒,%"
|
||||
latin:keyHintLabel="8"
|
||||
latin:additionalMoreKeys="८,8" />
|
||||
latin:additionalMoreKeys="८,8"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+0926: "द" DEVANAGARI LETTER DA
|
||||
U+096F: "९" DEVANAGARI DIGIT NINE -->
|
||||
<Key
|
||||
latin:keyLabel="द"
|
||||
latin:keyHintLabel="9"
|
||||
latin:additionalMoreKeys="9" />
|
||||
latin:additionalMoreKeys="9"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+091C: "ज" DEVANAGARI LETTER JA
|
||||
U+091C/U+0952: "ज॒" DEVANAGARI LETTER JA/DEVANAGARI STRESS SIGN ANUDATTA
|
||||
U+091C/U+094D/U+091E: "ज्ञ" DEVANAGARI LETTER JA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER NYA
|
||||
|
@ -155,13 +175,15 @@
|
|||
latin:keyLabel="ज"
|
||||
latin:moreKeys="ज॒,ज्ञ,ज़,%"
|
||||
latin:keyHintLabel="0"
|
||||
latin:additionalMoreKeys="०,0" />
|
||||
latin:additionalMoreKeys="०,0"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+0921: "ड" DEVANAGARI LETTER DDA
|
||||
U+0921/U+0952: "ड॒" DEVANAGARI LETTER DDA/DEVANAGARI STRESS SIGN ANUDATTA
|
||||
U+0921/U+093C: "ड़" DEVANAGARI LETTER DDA/DEVANAGARI SIGN NUKTA -->
|
||||
<Key
|
||||
latin:keyLabel="ड"
|
||||
latin:moreKeys="ड॒,ड़" />
|
||||
latin:moreKeys="ड॒,ड़"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
</default>
|
||||
</switch>
|
||||
</merge>
|
||||
|
|
|
@ -31,7 +31,8 @@
|
|||
U+0912: "ऒ" DEVANAGARI LETTER SHORT O -->
|
||||
<Key
|
||||
latin:keyLabel="ओ"
|
||||
latin:moreKeys="ओं,ऑ,ऒ" />
|
||||
latin:moreKeys="ओं,ऑ,ऒ"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+090F: "ए" DEVANAGARI LETTER E
|
||||
U+090F/U+0902: "एं" DEVANAGARI LETTER E/DEVANAGARI SIGN ANUSVARA
|
||||
U+090F/U+0901: "एँ" DEVANAGARI LETTER E/DEVANAGARI SIGN CANDRABINDU
|
||||
|
@ -39,50 +40,60 @@
|
|||
U+090E: "ऎ" DEVANAGARI LETTER SHORT E -->
|
||||
<Key
|
||||
latin:keyLabel="ए"
|
||||
latin:moreKeys="एं,एँ,ऍ,ऎ" />
|
||||
latin:moreKeys="एं,एँ,ऍ,ऎ"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+0905: "अ" DEVANAGARI LETTER A
|
||||
U+0905/U+0902: "अं" DEVANAGARI LETTER A/DEVANAGARI SIGN ANUSVARA
|
||||
U+0905/U+0901: "अँ" DEVANAGARI LETTER A/DEVANAGARI SIGN CANDRABINDU -->
|
||||
<Key
|
||||
latin:keyLabel="अ"
|
||||
latin:moreKeys="अं,अँ" />
|
||||
latin:moreKeys="अं,अँ"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+0907: "इ" DEVANAGARI LETTER I
|
||||
U+0907/U+0902: "इं" DEVANAGARI LETTER I/DEVANAGARI SIGN ANUSVARA
|
||||
U+0907/U+0901: "इं" DEVANAGARI LETTER I/DEVANAGARI SIGN CANDRABINDU -->
|
||||
<Key
|
||||
latin:keyLabel="इ"
|
||||
latin:moreKeys="इं,इँ" />
|
||||
latin:moreKeys="इं,इँ"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+0909: "उ" DEVANAGARI LETTER U
|
||||
U+0909/U+0902: "उं" DEVANAGARI LETTER U/DEVANAGARI SIGN ANUSVARA
|
||||
U+0909/U+0901: "उँ" DEVANAGARI LETTER U/DEVANAGARI SIGN CANDRABINDU -->
|
||||
<Key
|
||||
latin:keyLabel="उ"
|
||||
latin:moreKeys="उं,उँ" />
|
||||
latin:moreKeys="उं,उँ"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+092B: "फ" DEVANAGARI LETTER PHA
|
||||
U+092B/U+093C: "फ़" DEVANAGARI LETTER PHA/DEVANAGARI SIGN NUKTA -->
|
||||
<Key
|
||||
latin:keyLabel="फ"
|
||||
latin:moreKeys="फ़" />
|
||||
latin:moreKeys="फ़"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+0931: "ऱ" DEVANAGARI LETTER RRA
|
||||
U+094D/U+0930: "्र" DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER RA
|
||||
U+0930/U+094D: "र्" DEVANAGARI LETTER RA/DEVANAGARI SIGN VIRAMA -->
|
||||
<Key
|
||||
latin:keyLabel="ऱ"
|
||||
latin:moreKeys="्र,र्" />
|
||||
latin:moreKeys="्र,र्"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+0916: "ख" DEVANAGARI LETTER KHA
|
||||
U+0916/U+093C: "ख़" DEVANAGARI LETTER KHA/DEVANAGARI SIGN NUKTA -->
|
||||
<Key
|
||||
latin:keyLabel="ख"
|
||||
latin:moreKeys="ख़" />
|
||||
latin:moreKeys="ख़"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+0925: "थ" DEVANAGARI LETTER THA -->
|
||||
<Key
|
||||
latin:keyLabel="थ" />
|
||||
latin:keyLabel="थ"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+091B: "छ" DEVANAGARI LETTER CHA -->
|
||||
<Key
|
||||
latin:keyLabel="छ" />
|
||||
latin:keyLabel="छ"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+0920: "ठ" DEVANAGARI LETTER TTHA -->
|
||||
<Key
|
||||
latin:keyLabel="ठ" />
|
||||
latin:keyLabel="ठ"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
</case>
|
||||
<default>
|
||||
<!-- U+094B: "ो" DEVANAGARI VOWEL SIGN O
|
||||
|
@ -91,52 +102,63 @@
|
|||
U+094A: "ॊ" DEVANAGARI VOWEL SIGN SHORT O -->
|
||||
<Key
|
||||
latin:keyLabel="ो"
|
||||
latin:moreKeys="ों,ॉ,ॊ" />
|
||||
latin:moreKeys="ों,ॉ,ॊ"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+0947: "े" DEVANAGARI VOWEL SIGN E
|
||||
U+0947/U+0902: "ें" DEVANAGARI VOWEL SIGN E/DEVANAGARI SIGN ANUSVARA -->
|
||||
<Key
|
||||
latin:keyLabel="े"
|
||||
latin:moreKeys="ें" />
|
||||
latin:moreKeys="ें"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+094D: "्" DEVANAGARI SIGN VIRAMA -->
|
||||
<Key
|
||||
latin:keyLabel="्" />
|
||||
latin:keyLabel="्"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+093F: "ि" DEVANAGARI VOWEL SIGN I
|
||||
U+093F/U+0902: "िं" DEVANAGARI VOWEL SIGN I/DEVANAGARI SIGN ANUSVARA -->
|
||||
<Key
|
||||
latin:keyLabel="ि"
|
||||
latin:moreKeys="िं" />
|
||||
latin:moreKeys="िं"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+0941: "ु" DEVANAGARI VOWEL SIGN U
|
||||
U+0941/U+0902: "ुं" DEVANAGARI VOWEL SIGN U/DEVANAGARI SIGN ANUSVARA
|
||||
U+0941/U+0901: "ुँ" DEVANAGARI VOWEL SIGN U/DEVANAGARI SIGN CANDRABINDU -->
|
||||
<Key
|
||||
latin:keyLabel="ु"
|
||||
latin:moreKeys="ुं,ुँ" />
|
||||
latin:moreKeys="ुं,ुँ"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+092A: "प" DEVANAGARI LETTER PA -->
|
||||
<Key
|
||||
latin:keyLabel="प" />
|
||||
latin:keyLabel="प"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+0930: "र" DEVANAGARI LETTER RA
|
||||
U+090B: "ऋ" DEVANAGARI LETTER VOCALIC R
|
||||
U+0930/U+093C: "ऱ" DEVANAGARI LETTER RA/DEVANAGARI SIGN NUKTA
|
||||
U+0960: "ॠ" DEVANAGARI LETTER VOCALIC RR -->
|
||||
<Key
|
||||
latin:keyLabel="र"
|
||||
latin:moreKeys="ऋ,ऱ,ॠ" />
|
||||
latin:moreKeys="ऋ,ऱ,ॠ"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+0915: "क" DEVANAGARI LETTER KA
|
||||
U+0915/U+093C: "क़" DEVANAGARI LETTER KA/DEVANAGARI SIGN NUKTA -->
|
||||
<Key
|
||||
latin:keyLabel="क"
|
||||
latin:moreKeys="क़" />
|
||||
latin:moreKeys="क़"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+0924: "त" DEVANAGARI LETTER TA
|
||||
U+0924/U+094D/U+0930: "त्र" DEVANAGARI LETTER TA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER RA -->
|
||||
<Key
|
||||
latin:keyLabel="त"
|
||||
latin:moreKeys="त्र" />
|
||||
latin:moreKeys="त्र"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+091A: "च" DEVANAGARI LETTER CA -->
|
||||
<Key
|
||||
latin:keyLabel="च" />
|
||||
latin:keyLabel="च"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+091F: "ट" DEVANAGARI LETTER TTA -->
|
||||
<Key
|
||||
latin:keyLabel="ट" />
|
||||
latin:keyLabel="ट"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
</default>
|
||||
</switch>
|
||||
</merge>
|
||||
|
|
|
@ -27,15 +27,18 @@
|
|||
>
|
||||
<!-- U+0911: "ऑ" DEVANAGARI LETTER CANDRA O -->
|
||||
<Key
|
||||
latin:keyLabel="ऑ" />
|
||||
latin:keyLabel="ऑ"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+0901: "ँ" DEVANAGARI SIGN CANDRABINDU
|
||||
U+0945: "ॅ" DEVANAGARI VOWEL SIGN CANDRA E-->
|
||||
<Key
|
||||
latin:keyLabel="ँ"
|
||||
latin:moreKeys="ॅ" />
|
||||
latin:moreKeys="ॅ"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+0923: "ण" DEVANAGARI LETTER NNA -->
|
||||
<Key
|
||||
latin:keyLabel="ण" />
|
||||
latin:keyLabel="ण"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+0929: "ऩ" DEVANAGARI LETTER NNNA -->
|
||||
<Key
|
||||
latin:keyLabel="ऩ" />
|
||||
|
@ -43,65 +46,79 @@
|
|||
U+0934: "ऴ" DEVANAGARI LETTER LLLA -->
|
||||
<Key
|
||||
latin:keyLabel="ळ"
|
||||
latin:moreKeys="ऴ" />
|
||||
latin:moreKeys="ऴ"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+0936: "श" DEVANAGARI LETTER SHA -->
|
||||
<Key
|
||||
latin:keyLabel="श" />
|
||||
latin:keyLabel="श"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+0937: "ष" DEVANAGARI LETTER SSA -->
|
||||
<Key
|
||||
latin:keyLabel="ष" />
|
||||
latin:keyLabel="ष"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+0943: "ृ" DEVANAGARI VOWEL SIGN VOCALIC R
|
||||
U+0944: "ॄ" DEVANAGARI VOWEL SIGN VOCALIC RR -->
|
||||
<Key
|
||||
latin:keyLabel="ृ"
|
||||
latin:moreKeys="ॄ" />
|
||||
latin:moreKeys="ॄ"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+091E: "ञ" DEVANAGARI LETTER NYA -->
|
||||
<Key
|
||||
latin:keyLabel="ञ" />
|
||||
latin:keyLabel="ञ"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
</case>
|
||||
<default>
|
||||
<!-- U+0949: "ॉ" DEVANAGARI VOWEL SIGN CANDRA O -->
|
||||
<Key
|
||||
latin:keyLabel="ॉ" />
|
||||
latin:keyLabel="ॉ"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+0902: "ं" DEVANAGARI SIGN ANUSVARA -->
|
||||
<Key
|
||||
latin:keyLabel="ं" />
|
||||
latin:keyLabel="ं"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+092E: "म" DEVANAGARI LETTER MA
|
||||
U+0950: "ॐ" DEVANAGARI OM -->
|
||||
<Key
|
||||
latin:keyLabel="म"
|
||||
latin:moreKeys="ॐ" />
|
||||
latin:moreKeys="ॐ"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+0928: "न" DEVANAGARI LETTER NA
|
||||
U+091E: "ञ" DEVANAGARI LETTER NYA
|
||||
U+0919: "ङ" DEVANAGARI LETTER NGA
|
||||
U+0928/U+093C: "ऩ" DEVANAGARI LETTER NA/DEVANAGARI SIGN NUKTA -->
|
||||
<Key
|
||||
latin:keyLabel="न"
|
||||
latin:moreKeys="ञ,ङ,ऩ" />
|
||||
latin:moreKeys="ञ,ङ,ऩ"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+0935: "व" DEVANAGARI LETTER VA -->
|
||||
<Key
|
||||
latin:keyLabel="व" />
|
||||
latin:keyLabel="व"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+0932: "ल" DEVANAGARI LETTER LA
|
||||
U+090C: "ऌ" DEVANAGARI LETTER VOCALIC L
|
||||
U+0961: "ॡ" DEVANAGARI LETTER VOCALIC LL -->
|
||||
<Key
|
||||
latin:keyLabel="ल"
|
||||
latin:moreKeys="ऌ,ॡ" />
|
||||
latin:moreKeys="ऌ,ॡ"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+0938: "स" DEVANAGARI LETTER SA -->
|
||||
<Key
|
||||
latin:keyLabel="स" />
|
||||
latin:keyLabel="स"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+092F: "य" DEVANAGARI LETTER YA
|
||||
U+095F: "य़" DEVANAGARI LETTER YYA -->
|
||||
<Key
|
||||
latin:keyLabel="य"
|
||||
latin:moreKeys="य़" />
|
||||
latin:moreKeys="य़"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
<!-- U+093C: "़" DEVANAGARI SIGN NUKTA
|
||||
U+097D: "ॽ" DEVANAGARI LETTER GLOTTAL STOP
|
||||
U+0970: "॰" DEVANAGARI ABBREVIATION SIGN
|
||||
U+093D: "ऽ" DEVANAGARI SIGN AVAGRAHA -->
|
||||
<Key
|
||||
latin:keyLabel="़"
|
||||
latin:moreKeys="ॽ,॰,ऽ" />
|
||||
latin:moreKeys="ॽ,॰,ऽ"
|
||||
latin:keyLabelFlags="fontNormal" />
|
||||
</default>
|
||||
</switch>
|
||||
</merge>
|
||||
|
|
|
@ -23,7 +23,7 @@ import android.view.MotionEvent;
|
|||
|
||||
import com.android.inputmethod.accessibility.AccessibilityUtils;
|
||||
import com.android.inputmethod.keyboard.internal.GestureStroke;
|
||||
import com.android.inputmethod.keyboard.internal.GestureStrokeWithPreviewTrail;
|
||||
import com.android.inputmethod.keyboard.internal.GestureStrokeWithPreviewPoints;
|
||||
import com.android.inputmethod.keyboard.internal.PointerTrackerQueue;
|
||||
import com.android.inputmethod.latin.CollectionUtils;
|
||||
import com.android.inputmethod.latin.InputPointers;
|
||||
|
@ -47,8 +47,6 @@ public class PointerTracker implements PointerTrackerQueue.Element {
|
|||
private static boolean sGestureHandlingEnabledByInputField = false;
|
||||
private static boolean sGestureHandlingEnabledByUser = false;
|
||||
|
||||
private static final int MIN_GESTURE_RECOGNITION_TIME = 100; // msec
|
||||
|
||||
public interface KeyEventHandler {
|
||||
/**
|
||||
* Get KeyDetector object that is used for this PointerTracker.
|
||||
|
@ -208,7 +206,7 @@ public class PointerTracker implements PointerTrackerQueue.Element {
|
|||
private static final KeyboardActionListener EMPTY_LISTENER =
|
||||
new KeyboardActionListener.Adapter();
|
||||
|
||||
private final GestureStrokeWithPreviewTrail mGestureStrokeWithPreviewTrail;
|
||||
private final GestureStrokeWithPreviewPoints mGestureStrokeWithPreviewPoints;
|
||||
|
||||
public static void init(boolean hasDistinctMultitouch,
|
||||
boolean needsPhantomSuddenMoveEventHack) {
|
||||
|
@ -293,7 +291,7 @@ public class PointerTracker implements PointerTrackerQueue.Element {
|
|||
throw new NullPointerException();
|
||||
}
|
||||
mPointerId = id;
|
||||
mGestureStrokeWithPreviewTrail = new GestureStrokeWithPreviewTrail(id);
|
||||
mGestureStrokeWithPreviewPoints = new GestureStrokeWithPreviewPoints(id);
|
||||
setKeyDetectorInner(handler.getKeyDetector());
|
||||
mListener = handler.getKeyboardActionListener();
|
||||
mDrawingProxy = handler.getDrawingProxy();
|
||||
|
@ -392,7 +390,7 @@ public class PointerTracker implements PointerTrackerQueue.Element {
|
|||
private void setKeyDetectorInner(final KeyDetector keyDetector) {
|
||||
mKeyDetector = keyDetector;
|
||||
mKeyboard = keyDetector.getKeyboard();
|
||||
mGestureStrokeWithPreviewTrail.setGestureSampleLength(mKeyboard.mMostCommonKeyWidth);
|
||||
mGestureStrokeWithPreviewPoints.setKeyboardGeometry(mKeyboard.mMostCommonKeyWidth);
|
||||
final Key newKey = mKeyDetector.detectHitKey(mKeyX, mKeyY);
|
||||
if (newKey != mCurrentKey) {
|
||||
if (mDrawingProxy != null) {
|
||||
|
@ -502,8 +500,8 @@ public class PointerTracker implements PointerTrackerQueue.Element {
|
|||
mDrawingProxy.invalidateKey(key);
|
||||
}
|
||||
|
||||
public GestureStrokeWithPreviewTrail getGestureStrokeWithPreviewTrail() {
|
||||
return mGestureStrokeWithPreviewTrail;
|
||||
public GestureStrokeWithPreviewPoints getGestureStrokeWithPreviewPoints() {
|
||||
return mGestureStrokeWithPreviewPoints;
|
||||
}
|
||||
|
||||
public int getLastX() {
|
||||
|
@ -544,8 +542,8 @@ public class PointerTracker implements PointerTrackerQueue.Element {
|
|||
return (sPointerTrackerQueue == null) ? 1 : sPointerTrackerQueue.size();
|
||||
}
|
||||
|
||||
private void startBatchInput() {
|
||||
if (sInGesture || !mGestureStrokeWithPreviewTrail.isStartOfAGesture()) {
|
||||
private void mayStartBatchInput() {
|
||||
if (sInGesture || !mGestureStrokeWithPreviewPoints.isStartOfAGesture()) {
|
||||
return;
|
||||
}
|
||||
if (DEBUG_LISTENER) {
|
||||
|
@ -559,10 +557,10 @@ public class PointerTracker implements PointerTrackerQueue.Element {
|
|||
|
||||
private void updateBatchInput(final long eventTime) {
|
||||
synchronized (sAggregratedPointers) {
|
||||
mGestureStrokeWithPreviewTrail.appendIncrementalBatchPoints(sAggregratedPointers);
|
||||
mGestureStrokeWithPreviewPoints.appendIncrementalBatchPoints(sAggregratedPointers);
|
||||
final int size = sAggregratedPointers.getPointerSize();
|
||||
if (size > sLastRecognitionPointSize
|
||||
&& eventTime > sLastRecognitionTime + MIN_GESTURE_RECOGNITION_TIME) {
|
||||
&& GestureStroke.hasRecognitionTimePast(eventTime, sLastRecognitionTime)) {
|
||||
sLastRecognitionPointSize = size;
|
||||
sLastRecognitionTime = eventTime;
|
||||
if (DEBUG_LISTENER) {
|
||||
|
@ -575,10 +573,10 @@ public class PointerTracker implements PointerTrackerQueue.Element {
|
|||
mDrawingProxy.showGesturePreviewTrail(this, isOldestTracker);
|
||||
}
|
||||
|
||||
private void endBatchInput() {
|
||||
private void mayEndBatchInput() {
|
||||
synchronized (sAggregratedPointers) {
|
||||
mGestureStrokeWithPreviewTrail.appendAllBatchPoints(sAggregratedPointers);
|
||||
mGestureStrokeWithPreviewTrail.reset();
|
||||
mGestureStrokeWithPreviewPoints.appendAllBatchPoints(sAggregratedPointers);
|
||||
mGestureStrokeWithPreviewPoints.reset();
|
||||
if (getActivePointerTrackerCount() == 1) {
|
||||
if (DEBUG_LISTENER) {
|
||||
Log.d(TAG, "onEndBatchInput: batchPoints="
|
||||
|
@ -601,7 +599,7 @@ public class PointerTracker implements PointerTrackerQueue.Element {
|
|||
final int trackersSize = sTrackers.size();
|
||||
for (int i = 0; i < trackersSize; ++i) {
|
||||
final PointerTracker tracker = sTrackers.get(i);
|
||||
tracker.mGestureStrokeWithPreviewTrail.reset();
|
||||
tracker.mGestureStrokeWithPreviewPoints.reset();
|
||||
}
|
||||
sAggregratedPointers.reset();
|
||||
sLastRecognitionPointSize = 0;
|
||||
|
@ -678,18 +676,21 @@ public class PointerTracker implements PointerTrackerQueue.Element {
|
|||
&& mKeyboard.mId.isAlphabetKeyboard();
|
||||
if (isAlphabetKeyboard && !mIsShowingMoreKeysPanel && key != null
|
||||
&& Keyboard.isLetterCode(key.mCode)) {
|
||||
mIsDetectingGesture = true;
|
||||
sGestureFirstDownTime = eventTime;
|
||||
mGestureStrokeWithPreviewTrail.addPoint(x, y, 0, false /* isHistorical */);
|
||||
onGestureDownEvent(x, y, eventTime);
|
||||
}
|
||||
} else if (sInGesture && activePointerTrackerCount > 1) {
|
||||
mIsDetectingGesture = true;
|
||||
final int elapsedTimeFromFirstDown = (int)(eventTime - sGestureFirstDownTime);
|
||||
mGestureStrokeWithPreviewTrail.addPoint(x, y, elapsedTimeFromFirstDown,
|
||||
false /* isHistorical */);
|
||||
onGestureDownEvent(x, y, eventTime);
|
||||
}
|
||||
}
|
||||
|
||||
private void onGestureDownEvent(final int x, final int y, final long eventTime) {
|
||||
mIsDetectingGesture = true;
|
||||
final int elapsedTimeFromFirstDown = (int)(eventTime - sGestureFirstDownTime);
|
||||
mGestureStrokeWithPreviewPoints.addPoint(x, y, elapsedTimeFromFirstDown,
|
||||
false /* isHistorical */);
|
||||
}
|
||||
|
||||
private void onDownEventInternal(final int x, final int y, final long eventTime) {
|
||||
Key key = onDownKey(x, y, eventTime);
|
||||
// Sliding key is allowed when 1) enabled by configuration, 2) this pointer starts sliding
|
||||
|
@ -726,8 +727,8 @@ public class PointerTracker implements PointerTrackerQueue.Element {
|
|||
final boolean isHistorical, final Key key) {
|
||||
final int gestureTime = (int)(eventTime - sGestureFirstDownTime);
|
||||
if (mIsDetectingGesture) {
|
||||
mGestureStrokeWithPreviewTrail.addPoint(x, y, gestureTime, isHistorical);
|
||||
startBatchInput();
|
||||
mGestureStrokeWithPreviewPoints.addPoint(x, y, gestureTime, isHistorical);
|
||||
mayStartBatchInput();
|
||||
if (sInGesture && key != null) {
|
||||
updateBatchInput(eventTime);
|
||||
}
|
||||
|
@ -919,7 +920,7 @@ public class PointerTracker implements PointerTrackerQueue.Element {
|
|||
if (currentKey != null) {
|
||||
callListenerOnRelease(currentKey, currentKey.mCode, true);
|
||||
}
|
||||
endBatchInput();
|
||||
mayEndBatchInput();
|
||||
return;
|
||||
}
|
||||
// This event will be recognized as a regular code input. Clear unused possible batch points
|
||||
|
|
|
@ -17,7 +17,9 @@ package com.android.inputmethod.keyboard.internal;
|
|||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.os.SystemClock;
|
||||
|
||||
import com.android.inputmethod.latin.Constants;
|
||||
|
@ -25,7 +27,7 @@ import com.android.inputmethod.latin.R;
|
|||
import com.android.inputmethod.latin.ResizableIntArray;
|
||||
|
||||
final class GesturePreviewTrail {
|
||||
private static final int DEFAULT_CAPACITY = GestureStrokeWithPreviewTrail.PREVIEW_CAPACITY;
|
||||
private static final int DEFAULT_CAPACITY = GestureStrokeWithPreviewPoints.PREVIEW_CAPACITY;
|
||||
|
||||
private final ResizableIntArray mXCoordinates = new ResizableIntArray(DEFAULT_CAPACITY);
|
||||
private final ResizableIntArray mYCoordinates = new ResizableIntArray(DEFAULT_CAPACITY);
|
||||
|
@ -78,7 +80,7 @@ final class GesturePreviewTrail {
|
|||
? DOWN_EVENT_MARKER - xCoordOrMark : xCoordOrMark;
|
||||
}
|
||||
|
||||
public void addStroke(final GestureStrokeWithPreviewTrail stroke, final long downTime) {
|
||||
public void addStroke(final GestureStrokeWithPreviewPoints stroke, final long downTime) {
|
||||
final int trailSize = mEventTimes.getLength();
|
||||
stroke.appendPreviewStroke(mEventTimes, mXCoordinates, mYCoordinates);
|
||||
if (mEventTimes.getLength() == trailSize) {
|
||||
|
@ -116,6 +118,99 @@ final class GesturePreviewTrail {
|
|||
/ params.mTrailLingerDuration, 0.0f);
|
||||
}
|
||||
|
||||
static final class WorkingSet {
|
||||
// Input
|
||||
// Previous point (P1) coordinates and trail radius.
|
||||
public float p1x, p1y;
|
||||
public float r1;
|
||||
// Current point (P2) coordinates and trail radius.
|
||||
public float p2x, p2y;
|
||||
public float r2;
|
||||
|
||||
// Output
|
||||
// Closing point of arc at P1.
|
||||
public float p1ax, p1ay;
|
||||
// Opening point of arc at P1.
|
||||
public float p1bx, p1by;
|
||||
// Opening point of arc at P2.
|
||||
public float p2ax, p2ay;
|
||||
// Closing point of arc at P2.
|
||||
public float p2bx, p2by;
|
||||
// Start angle of the trail arcs.
|
||||
public float aa;
|
||||
// Sweep angle of the trail arc at P1.
|
||||
public float a1;
|
||||
public RectF arc1 = new RectF();
|
||||
// Sweep angle of the trail arc at P2.
|
||||
public float a2;
|
||||
public RectF arc2 = new RectF();
|
||||
}
|
||||
|
||||
private static final float RIGHT_ANGLE = (float)(Math.PI / 2.0d);
|
||||
private static final float RADIAN_TO_DEGREE = (float)(180.0d / Math.PI);
|
||||
|
||||
private static boolean calculatePathPoints(final WorkingSet w) {
|
||||
final float dx = w.p2x - w.p1x;
|
||||
final float dy = w.p2y - w.p1y;
|
||||
// Distance of the points.
|
||||
final double l = Math.hypot(dx, dy);
|
||||
if (Double.compare(0.0d, l) == 0) {
|
||||
return false;
|
||||
}
|
||||
// Angle of the line p1-p2
|
||||
final float a = (float)Math.atan2(dy, dx);
|
||||
// Difference of trail cap radius.
|
||||
final float dr = w.r2 - w.r1;
|
||||
// Variation of angle at trail cap.
|
||||
final float ar = (float)Math.asin(dr / l);
|
||||
// The start angle of trail cap arc at P1.
|
||||
final float aa = a - (RIGHT_ANGLE + ar);
|
||||
// The end angle of trail cap arc at P2.
|
||||
final float ab = a + (RIGHT_ANGLE + ar);
|
||||
final float cosa = (float)Math.cos(aa);
|
||||
final float sina = (float)Math.sin(aa);
|
||||
final float cosb = (float)Math.cos(ab);
|
||||
final float sinb = (float)Math.sin(ab);
|
||||
w.p1ax = w.p1x + w.r1 * cosa;
|
||||
w.p1ay = w.p1y + w.r1 * sina;
|
||||
w.p1bx = w.p1x + w.r1 * cosb;
|
||||
w.p1by = w.p1y + w.r1 * sinb;
|
||||
w.p2ax = w.p2x + w.r2 * cosa;
|
||||
w.p2ay = w.p2y + w.r2 * sina;
|
||||
w.p2bx = w.p2x + w.r2 * cosb;
|
||||
w.p2by = w.p2y + w.r2 * sinb;
|
||||
w.aa = aa * RADIAN_TO_DEGREE;
|
||||
final float ar2degree = ar * 2.0f * RADIAN_TO_DEGREE;
|
||||
w.a1 = -180.0f + ar2degree;
|
||||
w.a2 = 180.0f + ar2degree;
|
||||
w.arc1.set(w.p1x, w.p1y, w.p1x, w.p1y);
|
||||
w.arc1.inset(-w.r1, -w.r1);
|
||||
w.arc2.set(w.p2x, w.p2y, w.p2x, w.p2y);
|
||||
w.arc2.inset(-w.r2, -w.r2);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void createPath(final Path path, final WorkingSet w) {
|
||||
path.rewind();
|
||||
// Trail cap at P1.
|
||||
path.moveTo(w.p1x, w.p1y);
|
||||
path.arcTo(w.arc1, w.aa, w.a1);
|
||||
// Trail cap at P2.
|
||||
path.moveTo(w.p2x, w.p2y);
|
||||
path.arcTo(w.arc2, w.aa, w.a2);
|
||||
// Two trapezoids connecting P1 and P2.
|
||||
path.moveTo(w.p1ax, w.p1ay);
|
||||
path.lineTo(w.p1x, w.p1y);
|
||||
path.lineTo(w.p1bx, w.p1by);
|
||||
path.lineTo(w.p2bx, w.p2by);
|
||||
path.lineTo(w.p2x, w.p2y);
|
||||
path.lineTo(w.p2ax, w.p2ay);
|
||||
path.close();
|
||||
}
|
||||
|
||||
private final WorkingSet mWorkingSet = new WorkingSet();
|
||||
private final Path mPath = new Path();
|
||||
|
||||
/**
|
||||
* Draw gesture preview trail
|
||||
* @param canvas The canvas to draw the gesture preview trail
|
||||
|
@ -147,30 +242,38 @@ final class GesturePreviewTrail {
|
|||
|
||||
if (startIndex < trailSize) {
|
||||
paint.setColor(params.mTrailColor);
|
||||
paint.setStyle(Paint.Style.STROKE);
|
||||
paint.setStrokeCap(Paint.Cap.ROUND);
|
||||
int lastX = getXCoordValue(xCoords[startIndex]);
|
||||
int lastY = yCoords[startIndex];
|
||||
float maxWidth = getWidth(sinceDown - eventTimes[startIndex], params);
|
||||
paint.setStyle(Paint.Style.FILL);
|
||||
final Path path = mPath;
|
||||
final WorkingSet w = mWorkingSet;
|
||||
w.p1x = getXCoordValue(xCoords[startIndex]);
|
||||
w.p1y = yCoords[startIndex];
|
||||
int lastTime = sinceDown - eventTimes[startIndex];
|
||||
float maxWidth = getWidth(lastTime, params);
|
||||
w.r1 = maxWidth / 2.0f;
|
||||
// Initialize bounds rectangle.
|
||||
outBoundsRect.set(lastX, lastY, lastX, lastY);
|
||||
outBoundsRect.set((int)w.p1x, (int)w.p1y, (int)w.p1x, (int)w.p1y);
|
||||
for (int i = startIndex + 1; i < trailSize - 1; i++) {
|
||||
final int x = xCoords[i];
|
||||
final int y = yCoords[i];
|
||||
final int elapsedTime = sinceDown - eventTimes[i];
|
||||
w.p2x = getXCoordValue(xCoords[i]);
|
||||
w.p2y = yCoords[i];
|
||||
// Draw trail line only when the current point isn't a down point.
|
||||
if (!isDownEventXCoord(x)) {
|
||||
if (!isDownEventXCoord(xCoords[i])) {
|
||||
final int alpha = getAlpha(elapsedTime, params);
|
||||
paint.setAlpha(alpha);
|
||||
final float width = getWidth(elapsedTime, params);
|
||||
paint.setStrokeWidth(width);
|
||||
canvas.drawLine(lastX, lastY, x, y, paint);
|
||||
w.r2 = width / 2.0f;
|
||||
if (calculatePathPoints(w)) {
|
||||
createPath(path, w);
|
||||
canvas.drawPath(path, paint);
|
||||
outBoundsRect.union((int)w.p2x, (int)w.p2y);
|
||||
}
|
||||
// Take union for the bounds.
|
||||
outBoundsRect.union(x, y);
|
||||
maxWidth = Math.max(maxWidth, width);
|
||||
}
|
||||
lastX = getXCoordValue(x);
|
||||
lastY = y;
|
||||
w.p1x = w.p2x;
|
||||
w.p1y = w.p2y;
|
||||
w.r1 = w.r2;
|
||||
lastTime = elapsedTime;
|
||||
}
|
||||
// Take care of trail line width.
|
||||
final int inset = -((int)maxWidth + 1);
|
||||
|
|
|
@ -25,44 +25,50 @@ public class GestureStroke {
|
|||
private final ResizableIntArray mXCoordinates = new ResizableIntArray(DEFAULT_CAPACITY);
|
||||
private final ResizableIntArray mYCoordinates = new ResizableIntArray(DEFAULT_CAPACITY);
|
||||
private float mLength;
|
||||
private float mAngle;
|
||||
private int mIncrementalRecognitionSize;
|
||||
private int mLastIncrementalBatchSize;
|
||||
private long mLastPointTime;
|
||||
private int mLastPointX;
|
||||
private int mLastPointY;
|
||||
|
||||
private int mMinGestureLength;
|
||||
private int mMinGestureSampleLength;
|
||||
private int mMinGestureLength; // pixel
|
||||
private int mMinGestureSampleLength; // pixel
|
||||
private int mGestureRecognitionThreshold; // pixel / sec
|
||||
|
||||
// TODO: Move some of these to resource.
|
||||
private static final float MIN_GESTURE_LENGTH_RATIO_TO_KEY_WIDTH = 0.75f;
|
||||
private static final int MIN_GESTURE_DURATION = 100; // msec
|
||||
private static final int MIN_GESTURE_START_DURATION = 100; // msec
|
||||
private static final int MIN_GESTURE_RECOGNITION_TIME = 100; // msec
|
||||
private static final float MIN_GESTURE_SAMPLING_RATIO_TO_KEY_WIDTH = 1.0f / 6.0f;
|
||||
private static final float GESTURE_RECOG_SPEED_THRESHOLD = 0.4f; // dip/msec
|
||||
private static final float GESTURE_RECOG_CURVATURE_THRESHOLD = (float)(Math.PI / 4.0f);
|
||||
private static final float GESTURE_RECOGNITION_SPEED_THRESHOLD_RATIO_TO_KEY_WIDTH =
|
||||
5.5f; // keyWidth / sec
|
||||
private static final int MSEC_PER_SEC = 1000;
|
||||
|
||||
private static final float DOUBLE_PI = (float)(2.0f * Math.PI);
|
||||
public static final boolean hasRecognitionTimePast(
|
||||
final long currentTime, final long lastRecognitionTime) {
|
||||
return currentTime > lastRecognitionTime + MIN_GESTURE_RECOGNITION_TIME;
|
||||
}
|
||||
|
||||
public GestureStroke(final int pointerId) {
|
||||
mPointerId = pointerId;
|
||||
}
|
||||
|
||||
public void setGestureSampleLength(final int keyWidth) {
|
||||
public void setKeyboardGeometry(final int keyWidth) {
|
||||
// TODO: Find an appropriate base metric for these length. Maybe diagonal length of the key?
|
||||
mMinGestureLength = (int)(keyWidth * MIN_GESTURE_LENGTH_RATIO_TO_KEY_WIDTH);
|
||||
mMinGestureSampleLength = (int)(keyWidth * MIN_GESTURE_SAMPLING_RATIO_TO_KEY_WIDTH);
|
||||
mGestureRecognitionThreshold =
|
||||
(int)(keyWidth * GESTURE_RECOGNITION_SPEED_THRESHOLD_RATIO_TO_KEY_WIDTH);
|
||||
}
|
||||
|
||||
public boolean isStartOfAGesture() {
|
||||
final int size = mEventTimes.getLength();
|
||||
final int downDuration = (size > 0) ? mEventTimes.get(size - 1) : 0;
|
||||
return downDuration > MIN_GESTURE_DURATION && mLength > mMinGestureLength;
|
||||
return downDuration > MIN_GESTURE_START_DURATION && mLength > mMinGestureLength;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
mLength = 0;
|
||||
mAngle = 0;
|
||||
mIncrementalRecognitionSize = 0;
|
||||
mLastIncrementalBatchSize = 0;
|
||||
mLastPointTime = 0;
|
||||
|
@ -71,56 +77,43 @@ public class GestureStroke {
|
|||
mYCoordinates.setLength(0);
|
||||
}
|
||||
|
||||
private void updateLastPoint(final int x, final int y, final int time) {
|
||||
public void addPoint(final int x, final int y, final int time, final boolean isHistorical) {
|
||||
final boolean needsSampling;
|
||||
final int size = mEventTimes.getLength();
|
||||
if (size == 0) {
|
||||
needsSampling = true;
|
||||
} else {
|
||||
final int lastIndex = size - 1;
|
||||
final int lastX = mXCoordinates.get(lastIndex);
|
||||
final int lastY = mYCoordinates.get(lastIndex);
|
||||
final float dist = getDistance(lastX, lastY, x, y);
|
||||
needsSampling = dist > mMinGestureSampleLength;
|
||||
mLength += dist;
|
||||
}
|
||||
if (needsSampling) {
|
||||
mEventTimes.add(time);
|
||||
mXCoordinates.add(x);
|
||||
mYCoordinates.add(y);
|
||||
}
|
||||
if (!isHistorical) {
|
||||
updateIncrementalRecognitionSize(x, y, time);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateIncrementalRecognitionSize(final int x, final int y, final int time) {
|
||||
final int msecs = (int)(time - mLastPointTime);
|
||||
if (msecs > 0) {
|
||||
final int pixels = (int)getDistance(mLastPointX, mLastPointY, x, y);
|
||||
// Equivalent to (pixels / msecs < mGestureRecognitionThreshold / MSEC_PER_SEC)
|
||||
if (pixels * MSEC_PER_SEC < mGestureRecognitionThreshold * msecs) {
|
||||
mIncrementalRecognitionSize = mEventTimes.getLength();
|
||||
}
|
||||
}
|
||||
mLastPointTime = time;
|
||||
mLastPointX = x;
|
||||
mLastPointY = y;
|
||||
}
|
||||
|
||||
public void addPoint(final int x, final int y, final int time, final boolean isHistorical) {
|
||||
final int size = mEventTimes.getLength();
|
||||
if (size == 0) {
|
||||
mEventTimes.add(time);
|
||||
mXCoordinates.add(x);
|
||||
mYCoordinates.add(y);
|
||||
if (!isHistorical) {
|
||||
updateLastPoint(x, y, time);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
final int lastX = mXCoordinates.get(size - 1);
|
||||
final int lastY = mYCoordinates.get(size - 1);
|
||||
final float dist = getDistance(lastX, lastY, x, y);
|
||||
if (dist > mMinGestureSampleLength) {
|
||||
mEventTimes.add(time);
|
||||
mXCoordinates.add(x);
|
||||
mYCoordinates.add(y);
|
||||
mLength += dist;
|
||||
final float angle = getAngle(lastX, lastY, x, y);
|
||||
if (size > 1) {
|
||||
final float curvature = getAngleDiff(angle, mAngle);
|
||||
if (curvature > GESTURE_RECOG_CURVATURE_THRESHOLD) {
|
||||
if (size > mIncrementalRecognitionSize) {
|
||||
mIncrementalRecognitionSize = size;
|
||||
}
|
||||
}
|
||||
}
|
||||
mAngle = angle;
|
||||
}
|
||||
|
||||
if (!isHistorical) {
|
||||
final int duration = (int)(time - mLastPointTime);
|
||||
if (mLastPointTime != 0 && duration > 0) {
|
||||
final float speed = getDistance(mLastPointX, mLastPointY, x, y) / duration;
|
||||
if (speed < GESTURE_RECOG_SPEED_THRESHOLD) {
|
||||
mIncrementalRecognitionSize = size;
|
||||
}
|
||||
}
|
||||
updateLastPoint(x, y, time);
|
||||
}
|
||||
}
|
||||
|
||||
public void appendAllBatchPoints(final InputPointers out) {
|
||||
appendBatchPoints(out, mEventTimes.getLength());
|
||||
}
|
||||
|
@ -146,21 +139,4 @@ public class GestureStroke {
|
|||
// java.lang.Math due to the way the JIT optimizes java.lang.Math.
|
||||
return (float)Math.sqrt(dx * dx + dy * dy);
|
||||
}
|
||||
|
||||
private static float getAngle(final int x1, final int y1, final int x2, final int y2) {
|
||||
final int dx = x1 - x2;
|
||||
final int dy = y1 - y2;
|
||||
if (dx == 0 && dy == 0) return 0;
|
||||
// Would it be faster to call atan2f() directly via JNI? Not sure about what the JIT
|
||||
// does with Math.atan2().
|
||||
return (float)Math.atan2(dy, dx);
|
||||
}
|
||||
|
||||
private static float getAngleDiff(final float a1, final float a2) {
|
||||
final float diff = Math.abs(a1 - a2);
|
||||
if (diff > Math.PI) {
|
||||
return DOUBLE_PI - diff;
|
||||
}
|
||||
return diff;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ package com.android.inputmethod.keyboard.internal;
|
|||
|
||||
import com.android.inputmethod.latin.ResizableIntArray;
|
||||
|
||||
public class GestureStrokeWithPreviewTrail extends GestureStroke {
|
||||
public class GestureStrokeWithPreviewPoints extends GestureStroke {
|
||||
public static final int PREVIEW_CAPACITY = 256;
|
||||
|
||||
private final ResizableIntArray mPreviewEventTimes = new ResizableIntArray(PREVIEW_CAPACITY);
|
||||
|
@ -26,7 +26,14 @@ public class GestureStrokeWithPreviewTrail extends GestureStroke {
|
|||
private int mStrokeId;
|
||||
private int mLastPreviewSize;
|
||||
|
||||
public GestureStrokeWithPreviewTrail(final int pointerId) {
|
||||
private int mMinPreviewSampleLengthSquare;
|
||||
private int mLastX;
|
||||
private int mLastY;
|
||||
|
||||
// TODO: Move this to resource.
|
||||
private static final float MIN_PREVIEW_SAMPLE_LENGTH_RATIO_TO_KEY_WIDTH = 0.1f;
|
||||
|
||||
public GestureStrokeWithPreviewPoints(final int pointerId) {
|
||||
super(pointerId);
|
||||
}
|
||||
|
||||
|
@ -48,12 +55,32 @@ public class GestureStrokeWithPreviewTrail extends GestureStroke {
|
|||
return mPreviewEventTimes.getLength();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setKeyboardGeometry(final int keyWidth) {
|
||||
super.setKeyboardGeometry(keyWidth);
|
||||
final float sampleLength = keyWidth * MIN_PREVIEW_SAMPLE_LENGTH_RATIO_TO_KEY_WIDTH;
|
||||
mMinPreviewSampleLengthSquare = (int)(sampleLength * sampleLength);
|
||||
}
|
||||
|
||||
private boolean needsSampling(final int x, final int y) {
|
||||
final int dx = x - mLastX;
|
||||
final int dy = y - mLastY;
|
||||
final boolean needsSampling = (dx * dx + dy * dy >= mMinPreviewSampleLengthSquare);
|
||||
if (needsSampling) {
|
||||
mLastX = x;
|
||||
mLastY = y;
|
||||
}
|
||||
return needsSampling;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPoint(final int x, final int y, final int time, final boolean isHistorical) {
|
||||
super.addPoint(x, y, time, isHistorical);
|
||||
mPreviewEventTimes.add(time);
|
||||
mPreviewXCoordinates.add(x);
|
||||
mPreviewYCoordinates.add(y);
|
||||
if (mPreviewEventTimes.getLength() == 0 || isHistorical || needsSampling(x, y)) {
|
||||
mPreviewEventTimes.add(time);
|
||||
mPreviewXCoordinates.add(x);
|
||||
mPreviewYCoordinates.add(y);
|
||||
}
|
||||
}
|
||||
|
||||
public void appendPreviewStroke(final ResizableIntArray eventTimes,
|
|
@ -46,7 +46,6 @@ public class PreviewPlacerView extends RelativeLayout {
|
|||
private final float mGestureFloatingPreviewHorizontalPadding;
|
||||
private final float mGestureFloatingPreviewVerticalPadding;
|
||||
private final float mGestureFloatingPreviewRoundRadius;
|
||||
/* package */ final int mGestureFloatingPreviewTextLingerTimeout;
|
||||
|
||||
private int mXOrigin;
|
||||
private int mYOrigin;
|
||||
|
@ -78,11 +77,14 @@ public class PreviewPlacerView extends RelativeLayout {
|
|||
private static final int MSG_UPDATE_GESTURE_PREVIEW_TRAIL = 1;
|
||||
|
||||
private final Params mGesturePreviewTrailParams;
|
||||
private final int mGestureFloatingPreviewTextLingerTimeout;
|
||||
|
||||
public DrawingHandler(final PreviewPlacerView outerInstance,
|
||||
final Params gesturePreviewTrailParams) {
|
||||
final Params gesturePreviewTrailParams,
|
||||
final int getstureFloatinPreviewTextLinerTimeout) {
|
||||
super(outerInstance);
|
||||
mGesturePreviewTrailParams = gesturePreviewTrailParams;
|
||||
mGestureFloatingPreviewTextLingerTimeout = getstureFloatinPreviewTextLinerTimeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -105,10 +107,8 @@ public class PreviewPlacerView extends RelativeLayout {
|
|||
|
||||
public void dismissGestureFloatingPreviewText() {
|
||||
cancelDismissGestureFloatingPreviewText();
|
||||
final PreviewPlacerView placerView = getOuterInstance();
|
||||
sendMessageDelayed(
|
||||
obtainMessage(MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT),
|
||||
placerView.mGestureFloatingPreviewTextLingerTimeout);
|
||||
sendMessageDelayed(obtainMessage(MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT),
|
||||
mGestureFloatingPreviewTextLingerTimeout);
|
||||
}
|
||||
|
||||
private void cancelUpdateGestureTrailPreview() {
|
||||
|
@ -122,7 +122,6 @@ public class PreviewPlacerView extends RelativeLayout {
|
|||
}
|
||||
|
||||
public void cancelAllMessages() {
|
||||
cancelDismissGestureFloatingPreviewText();
|
||||
cancelUpdateGestureTrailPreview();
|
||||
}
|
||||
}
|
||||
|
@ -151,12 +150,13 @@ public class PreviewPlacerView extends RelativeLayout {
|
|||
R.styleable.KeyboardView_gestureFloatingPreviewVerticalPadding, 0.0f);
|
||||
mGestureFloatingPreviewRoundRadius = keyboardViewAttr.getDimension(
|
||||
R.styleable.KeyboardView_gestureFloatingPreviewRoundRadius, 0.0f);
|
||||
mGestureFloatingPreviewTextLingerTimeout = keyboardViewAttr.getInt(
|
||||
final int gestureFloatingPreviewTextLingerTimeout = keyboardViewAttr.getInt(
|
||||
R.styleable.KeyboardView_gestureFloatingPreviewTextLingerTimeout, 0);
|
||||
mGesturePreviewTrailParams = new Params(keyboardViewAttr);
|
||||
keyboardViewAttr.recycle();
|
||||
|
||||
mDrawingHandler = new DrawingHandler(this, mGesturePreviewTrailParams);
|
||||
mDrawingHandler = new DrawingHandler(this, mGesturePreviewTrailParams,
|
||||
gestureFloatingPreviewTextLingerTimeout);
|
||||
|
||||
final Paint gesturePaint = new Paint();
|
||||
gesturePaint.setAntiAlias(true);
|
||||
|
@ -189,22 +189,29 @@ public class PreviewPlacerView extends RelativeLayout {
|
|||
}
|
||||
|
||||
public void invalidatePointer(final PointerTracker tracker, final boolean isOldestTracker) {
|
||||
GesturePreviewTrail trail;
|
||||
synchronized (mGesturePreviewTrails) {
|
||||
trail = mGesturePreviewTrails.get(tracker.mPointerId);
|
||||
if (trail == null) {
|
||||
trail = new GesturePreviewTrail();
|
||||
mGesturePreviewTrails.put(tracker.mPointerId, trail);
|
||||
}
|
||||
}
|
||||
trail.addStroke(tracker.getGestureStrokeWithPreviewTrail(), tracker.getDownTime());
|
||||
|
||||
if (isOldestTracker) {
|
||||
final boolean needsToUpdateLastPointer =
|
||||
isOldestTracker && mDrawsGestureFloatingPreviewText;
|
||||
if (needsToUpdateLastPointer) {
|
||||
mLastPointerX = tracker.getLastX();
|
||||
mLastPointerY = tracker.getLastY();
|
||||
}
|
||||
|
||||
if (mDrawsGesturePreviewTrail) {
|
||||
GesturePreviewTrail trail;
|
||||
synchronized (mGesturePreviewTrails) {
|
||||
trail = mGesturePreviewTrails.get(tracker.mPointerId);
|
||||
if (trail == null) {
|
||||
trail = new GesturePreviewTrail();
|
||||
mGesturePreviewTrails.put(tracker.mPointerId, trail);
|
||||
}
|
||||
}
|
||||
trail.addStroke(tracker.getGestureStrokeWithPreviewPoints(), tracker.getDownTime());
|
||||
}
|
||||
|
||||
// TODO: Should narrow the invalidate region.
|
||||
invalidate();
|
||||
if (mDrawsGesturePreviewTrail || needsToUpdateLastPointer) {
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -262,6 +269,7 @@ public class PreviewPlacerView extends RelativeLayout {
|
|||
}
|
||||
|
||||
public void setGestureFloatingPreviewText(final String gestureFloatingPreviewText) {
|
||||
if (!mDrawsGestureFloatingPreviewText) return;
|
||||
mGestureFloatingPreviewText = gestureFloatingPreviewText;
|
||||
invalidate();
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ import android.util.Log;
|
|||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.BufferUnderflowException;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
@ -357,14 +357,15 @@ class BinaryDictionaryGetter {
|
|||
try {
|
||||
// Read the version of the file
|
||||
inStream = new FileInputStream(f);
|
||||
final ByteBuffer buffer = inStream.getChannel().map(
|
||||
FileChannel.MapMode.READ_ONLY, 0, f.length());
|
||||
final int magic = buffer.getInt();
|
||||
final BinaryDictInputOutput.ByteBufferWrapper buffer =
|
||||
new BinaryDictInputOutput.ByteBufferWrapper(inStream.getChannel().map(
|
||||
FileChannel.MapMode.READ_ONLY, 0, f.length()));
|
||||
final int magic = buffer.readInt();
|
||||
if (magic != FormatSpec.VERSION_2_MAGIC_NUMBER) {
|
||||
return false;
|
||||
}
|
||||
final int formatVersion = buffer.getInt();
|
||||
final int headerSize = buffer.getInt();
|
||||
final int formatVersion = buffer.readInt();
|
||||
final int headerSize = buffer.readInt();
|
||||
final HashMap<String, String> options = CollectionUtils.newHashMap();
|
||||
BinaryDictInputOutput.populateOptions(buffer, headerSize, options);
|
||||
|
||||
|
@ -382,6 +383,8 @@ class BinaryDictionaryGetter {
|
|||
return false;
|
||||
} catch (NumberFormatException e) {
|
||||
return false;
|
||||
} catch (BufferUnderflowException e) {
|
||||
return false;
|
||||
} finally {
|
||||
if (inStream != null) {
|
||||
try {
|
||||
|
|
|
@ -72,6 +72,7 @@ import com.android.inputmethod.keyboard.KeyboardSwitcher;
|
|||
import com.android.inputmethod.keyboard.KeyboardView;
|
||||
import com.android.inputmethod.keyboard.MainKeyboardView;
|
||||
import com.android.inputmethod.latin.LocaleUtils.RunInLocale;
|
||||
import com.android.inputmethod.latin.Utils.Stats;
|
||||
import com.android.inputmethod.latin.define.ProductionFlag;
|
||||
import com.android.inputmethod.latin.suggestions.SuggestionStripView;
|
||||
import com.android.inputmethod.research.ResearchLogger;
|
||||
|
@ -148,7 +149,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
|||
private boolean mIsUserDictionaryAvailable;
|
||||
|
||||
private LastComposedWord mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
|
||||
private WordComposer mWordComposer = new WordComposer();
|
||||
private final WordComposer mWordComposer = new WordComposer();
|
||||
private RichInputConnection mConnection = new RichInputConnection(this);
|
||||
|
||||
// Keep track of the last selection range to decide if we need to show word alternatives
|
||||
|
@ -741,6 +742,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
|||
// switcher.loadKeyboard; in apps like Talk, we come here when the text is sent and the
|
||||
// field gets emptied and we need to re-evaluate the shift state, but not the whole layout
|
||||
// which would be disruptive.
|
||||
// Space state must be updated before calling updateShiftState
|
||||
mKeyboardSwitcher.updateShiftState();
|
||||
|
||||
mHandler.cancelUpdateSuggestionStrip();
|
||||
|
@ -1100,25 +1102,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
|||
|
||||
final EditorInfo ei = getCurrentInputEditorInfo();
|
||||
if (ei == null) return Constants.TextUtils.CAP_MODE_OFF;
|
||||
|
||||
final int inputType = ei.inputType;
|
||||
if ((inputType & InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS) != 0) {
|
||||
return TextUtils.CAP_MODE_CHARACTERS;
|
||||
}
|
||||
|
||||
final boolean noNeedToCheckCapsMode = (inputType & (InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
|
||||
| InputType.TYPE_TEXT_FLAG_CAP_WORDS)) == 0;
|
||||
if (noNeedToCheckCapsMode) return Constants.TextUtils.CAP_MODE_OFF;
|
||||
|
||||
// Avoid making heavy round-trip IPC calls of {@link InputConnection#getCursorCapsMode}
|
||||
// unless needed.
|
||||
if (mWordComposer.isComposingWord()) return Constants.TextUtils.CAP_MODE_OFF;
|
||||
|
||||
// TODO: This blocking IPC call is heavy. Consider doing this without using IPC calls.
|
||||
// Note: getCursorCapsMode() returns the current capitalization mode that is any
|
||||
// combination of CAP_MODE_CHARACTERS, CAP_MODE_WORDS, and CAP_MODE_SENTENCES. 0 means none
|
||||
// of them.
|
||||
return mConnection.getCursorCapsMode(inputType, mSubtypeSwitcher.getCurrentSubtypeLocale());
|
||||
// Warning: this depends on mSpaceState, which may not be the most current value. If
|
||||
// mSpaceState gets updated later, whoever called this may need to be told about it.
|
||||
return mConnection.getCursorCapsMode(inputType, mSubtypeSwitcher.getCurrentSubtypeLocale(),
|
||||
SPACE_STATE_PHANTOM == mSpaceState);
|
||||
}
|
||||
|
||||
// Factor in auto-caps and manual caps and compute the current caps mode.
|
||||
|
@ -1344,6 +1332,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
|||
didAutoCorrect = handleSeparator(primaryCode, x, y, spaceState);
|
||||
} else {
|
||||
if (SPACE_STATE_PHANTOM == spaceState) {
|
||||
if (ProductionFlag.IS_INTERNAL) {
|
||||
if (mWordComposer.isComposingWord() && mWordComposer.isBatchMode()) {
|
||||
Stats.onAutoCorrection(
|
||||
"", mWordComposer.getTypedWord(), " ", mWordComposer);
|
||||
}
|
||||
}
|
||||
commitTyped(LastComposedWord.NOT_A_SEPARATOR);
|
||||
}
|
||||
final int keyX, keyY;
|
||||
|
@ -1391,9 +1385,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
|||
}
|
||||
mConnection.commitText(text, 1);
|
||||
mConnection.endBatchEdit();
|
||||
// Space state must be updated before calling updateShiftState
|
||||
mSpaceState = SPACE_STATE_NONE;
|
||||
mKeyboardSwitcher.updateShiftState();
|
||||
mKeyboardSwitcher.onCodeInput(Keyboard.CODE_OUTPUT_TEXT);
|
||||
mSpaceState = SPACE_STATE_NONE;
|
||||
mEnteredText = text;
|
||||
}
|
||||
|
||||
|
@ -1401,13 +1396,29 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
|||
public void onStartBatchInput() {
|
||||
mConnection.beginBatchEdit();
|
||||
if (mWordComposer.isComposingWord()) {
|
||||
commitTyped(LastComposedWord.NOT_A_SEPARATOR);
|
||||
if (ProductionFlag.IS_INTERNAL) {
|
||||
if (mWordComposer.isBatchMode()) {
|
||||
Stats.onAutoCorrection("", mWordComposer.getTypedWord(), " ", mWordComposer);
|
||||
}
|
||||
}
|
||||
if (mWordComposer.size() <= 1) {
|
||||
// We auto-correct the previous (typed, not gestured) string iff it's one character
|
||||
// long. The reason for this is, even in the middle of gesture typing, you'll still
|
||||
// tap one-letter words and you want them auto-corrected (typically, "i" in English
|
||||
// should become "I"). However for any longer word, we assume that the reason for
|
||||
// tapping probably is that the word you intend to type is not in the dictionary,
|
||||
// so we do not attempt to correct, on the assumption that if that was a dictionary
|
||||
// word, the user would probably have gestured instead.
|
||||
commitCurrentAutoCorrection(LastComposedWord.NOT_A_SEPARATOR);
|
||||
} else {
|
||||
commitTyped(LastComposedWord.NOT_A_SEPARATOR);
|
||||
}
|
||||
mExpectingUpdateSelection = true;
|
||||
// TODO: Can we remove this?
|
||||
// The following is necessary for the case where the user typed something but didn't
|
||||
// manual pick it and didn't input any separator.
|
||||
mSpaceState = SPACE_STATE_PHANTOM;
|
||||
}
|
||||
mConnection.endBatchEdit();
|
||||
// TODO: Should handle TextUtils.CAP_MODE_CHARACTER.
|
||||
mWordComposer.setCapitalizedModeAtStartComposingTime(getActualCapsMode());
|
||||
}
|
||||
|
||||
|
@ -1509,8 +1520,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
|||
mConnection.setComposingText(batchInputText, 1);
|
||||
mExpectingUpdateSelection = true;
|
||||
mConnection.endBatchEdit();
|
||||
mKeyboardSwitcher.updateShiftState();
|
||||
// Space state must be updated before calling updateShiftState
|
||||
mSpaceState = SPACE_STATE_PHANTOM;
|
||||
mKeyboardSwitcher.updateShiftState();
|
||||
}
|
||||
|
||||
private CharSequence specificTldProcessingOnTextInput(final CharSequence text) {
|
||||
|
@ -1558,7 +1570,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
|||
}
|
||||
} else {
|
||||
if (mLastComposedWord.canRevertCommit()) {
|
||||
Utils.Stats.onAutoCorrectionCancellation();
|
||||
if (ProductionFlag.IS_INTERNAL) {
|
||||
Stats.onAutoCorrectionCancellation();
|
||||
}
|
||||
revertCommit();
|
||||
return;
|
||||
}
|
||||
|
@ -1707,7 +1721,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
|||
if (null != mSuggestionStripView) mSuggestionStripView.dismissAddToDictionaryHint();
|
||||
}
|
||||
mHandler.postUpdateSuggestionStrip();
|
||||
Utils.Stats.onNonSeparator((char)primaryCode, x, y);
|
||||
if (ProductionFlag.IS_INTERNAL) {
|
||||
Utils.Stats.onNonSeparator((char)primaryCode, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if we did an autocorrection, false otherwise.
|
||||
|
@ -1771,8 +1787,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
|||
// already displayed or not, so it's okay.
|
||||
setPunctuationSuggestions();
|
||||
}
|
||||
|
||||
Utils.Stats.onSeparator((char)primaryCode, x, y);
|
||||
if (ProductionFlag.IS_INTERNAL) {
|
||||
Utils.Stats.onSeparator((char)primaryCode, x, y);
|
||||
}
|
||||
|
||||
mHandler.postUpdateShiftState();
|
||||
return didAutoCorrect;
|
||||
|
@ -1941,7 +1958,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
|||
throw new RuntimeException("We have an auto-correction but the typed word "
|
||||
+ "is empty? Impossible! I must commit suicide.");
|
||||
}
|
||||
Utils.Stats.onAutoCorrection(typedWord, autoCorrection.toString(), separatorString);
|
||||
if (ProductionFlag.IS_INTERNAL) {
|
||||
Stats.onAutoCorrection(
|
||||
typedWord, autoCorrection.toString(), separatorString, mWordComposer);
|
||||
}
|
||||
mExpectingUpdateSelection = true;
|
||||
commitChosenWord(autoCorrection, LastComposedWord.COMMIT_TYPE_DECIDED_WORD,
|
||||
separatorString);
|
||||
|
@ -2019,8 +2039,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
|||
mConnection.endBatchEdit();
|
||||
// Don't allow cancellation of manual pick
|
||||
mLastComposedWord.deactivate();
|
||||
// Space state must be updated before calling updateShiftState
|
||||
mSpaceState = SPACE_STATE_PHANTOM;
|
||||
// TODO: is this necessary?
|
||||
mKeyboardSwitcher.updateShiftState();
|
||||
|
||||
// We should show the "Touch again to save" hint if the user pressed the first entry
|
||||
|
@ -2031,8 +2051,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
|||
// If the suggestion is not in the dictionary, the hint should be shown.
|
||||
&& !AutoCorrection.isValidWord(mSuggest.getUnigramDictionaries(), suggestion, true);
|
||||
|
||||
Utils.Stats.onSeparator((char)Keyboard.CODE_SPACE,
|
||||
Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
|
||||
if (ProductionFlag.IS_INTERNAL) {
|
||||
Stats.onSeparator((char)Keyboard.CODE_SPACE,
|
||||
Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
|
||||
}
|
||||
if (showingAddToDictionaryHint && mIsUserDictionaryAvailable) {
|
||||
mSuggestionStripView.showAddToDictionaryHint(
|
||||
suggestion, mCurrentSettings.mHintToSaveText);
|
||||
|
@ -2149,8 +2171,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|
|||
previousWord.toString(), committedWord.toString());
|
||||
}
|
||||
mConnection.commitText(originallyTypedWord + mLastComposedWord.mSeparatorString, 1);
|
||||
Utils.Stats.onSeparator(mLastComposedWord.mSeparatorString,
|
||||
Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
|
||||
if (ProductionFlag.IS_INTERNAL) {
|
||||
Stats.onSeparator(mLastComposedWord.mSeparatorString,
|
||||
Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
|
||||
}
|
||||
if (ProductionFlag.IS_EXPERIMENTAL) {
|
||||
ResearchLogger.latinIME_revertCommit(originallyTypedWord);
|
||||
}
|
||||
|
|
|
@ -44,7 +44,12 @@ public class LatinImeLogger implements SharedPreferences.OnSharedPreferenceChang
|
|||
String before, String after, int position, SuggestedWords suggestedWords) {
|
||||
}
|
||||
|
||||
public static void logOnAutoCorrection(String before, String after, int separatorCode) {
|
||||
public static void logOnAutoCorrectionForTyping(
|
||||
String before, String after, int separatorCode) {
|
||||
}
|
||||
|
||||
public static void logOnAutoCorrectionForGeometric(String before, String after,
|
||||
int separatorCode, InputPointers inputPointers) {
|
||||
}
|
||||
|
||||
public static void logOnAutoCorrectionCancelled() {
|
||||
|
|
|
@ -190,10 +190,35 @@ public class RichInputConnection {
|
|||
}
|
||||
}
|
||||
|
||||
public int getCursorCapsMode(final int inputType, final Locale locale) {
|
||||
/**
|
||||
* Gets the caps modes we should be in after this specific string.
|
||||
*
|
||||
* This returns a bit set of TextUtils#CAP_MODE_*, masked by the inputType argument.
|
||||
* This method also supports faking an additional space after the string passed in argument,
|
||||
* to support cases where a space will be added automatically, like in phantom space
|
||||
* state for example.
|
||||
* Note that for English, we are using American typography rules (which are not specific to
|
||||
* American English, it's just the most common set of rules for English).
|
||||
*
|
||||
* @param inputType a mask of the caps modes to test for.
|
||||
* @param locale what language should be considered.
|
||||
* @param hasSpaceBefore if we should consider there should be a space after the string.
|
||||
* @return the caps modes that should be on as a set of bits
|
||||
*/
|
||||
public int getCursorCapsMode(final int inputType, final Locale locale,
|
||||
final boolean hasSpaceBefore) {
|
||||
mIC = mParent.getCurrentInputConnection();
|
||||
if (null == mIC) return Constants.TextUtils.CAP_MODE_OFF;
|
||||
if (!TextUtils.isEmpty(mComposingText)) return Constants.TextUtils.CAP_MODE_OFF;
|
||||
if (!TextUtils.isEmpty(mComposingText)) {
|
||||
if (hasSpaceBefore) {
|
||||
// If we have some composing text and a space before, then we should have
|
||||
// MODE_CHARACTERS and MODE_WORDS on.
|
||||
return (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS) & inputType;
|
||||
} else {
|
||||
// We have some composing text - we should be in MODE_CHARACTERS only.
|
||||
return TextUtils.CAP_MODE_CHARACTERS & inputType;
|
||||
}
|
||||
}
|
||||
// TODO: this will generally work, but there may be cases where the buffer contains SOME
|
||||
// information but not enough to determine the caps mode accurately. This may happen after
|
||||
// heavy pressing of delete, for example DEFAULT_TEXT_CACHE_SIZE - 5 times or so.
|
||||
|
@ -205,7 +230,8 @@ public class RichInputConnection {
|
|||
}
|
||||
// This never calls InputConnection#getCapsMode - in fact, it's a static method that
|
||||
// never blocks or initiates IPC.
|
||||
return StringUtils.getCapsMode(mCommittedTextBeforeComposingText, inputType, locale);
|
||||
return StringUtils.getCapsMode(mCommittedTextBeforeComposingText, inputType, locale,
|
||||
hasSpaceBefore);
|
||||
}
|
||||
|
||||
public CharSequence getTextBeforeCursor(final int i, final int j) {
|
||||
|
|
|
@ -42,9 +42,9 @@ import com.android.inputmethod.latin.define.ProductionFlag;
|
|||
import com.android.inputmethod.research.ResearchLogger;
|
||||
import com.android.inputmethodcommon.InputMethodSettingsFragment;
|
||||
|
||||
public class Settings extends InputMethodSettingsFragment
|
||||
public final class Settings extends InputMethodSettingsFragment
|
||||
implements SharedPreferences.OnSharedPreferenceChangeListener {
|
||||
public static final boolean ENABLE_EXPERIMENTAL_SETTINGS = false;
|
||||
public static final boolean ENABLE_INTERNAL_SETTINGS = ProductionFlag.IS_INTERNAL;
|
||||
|
||||
// In the same order as xml/prefs.xml
|
||||
public static final String PREF_GENERAL_SETTINGS = "general_settings";
|
||||
|
@ -63,8 +63,8 @@ public class Settings extends InputMethodSettingsFragment
|
|||
"last_user_dictionary_write_time";
|
||||
public static final String PREF_ADVANCED_SETTINGS = "pref_advanced_settings";
|
||||
public static final String PREF_KEY_USE_CONTACTS_DICT = "pref_key_use_contacts_dict";
|
||||
public static final String PREF_SUPPRESS_LANGUAGE_SWITCH_KEY =
|
||||
"pref_suppress_language_switch_key";
|
||||
public static final String PREF_SHOW_LANGUAGE_SWITCH_KEY =
|
||||
"pref_show_language_switch_key";
|
||||
public static final String PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST =
|
||||
"pref_include_other_imes_in_language_switch_list";
|
||||
public static final String PREF_CUSTOM_INPUT_STYLES = "custom_input_styles";
|
||||
|
@ -77,8 +77,8 @@ public class Settings extends InputMethodSettingsFragment
|
|||
public static final String PREF_KEYPRESS_SOUND_VOLUME =
|
||||
"pref_keypress_sound_volume";
|
||||
public static final String PREF_GESTURE_PREVIEW_TRAIL = "pref_gesture_preview_trail";
|
||||
public static final String PREF_GESTURE_FLOATING_PREVIEW_TEXT =
|
||||
"pref_gesture_floating_preview_text";
|
||||
public static final String PREF_SHOW_GESTURE_FLOATING_PREVIEW_TEXT =
|
||||
"pref_show_gesture_floating_preview_text";
|
||||
|
||||
public static final String PREF_INPUT_LANGUAGE = "input_language";
|
||||
public static final String PREF_SELECTED_LANGUAGES = "selected_languages";
|
||||
|
@ -97,7 +97,7 @@ public class Settings extends InputMethodSettingsFragment
|
|||
private TextView mKeypressVibrationDurationSettingsTextView;
|
||||
private TextView mKeypressSoundVolumeSettingsTextView;
|
||||
|
||||
private static void setPreferenceEnabled(Preference preference, boolean enabled) {
|
||||
private static void setPreferenceEnabled(final Preference preference, final boolean enabled) {
|
||||
if (preference != null) {
|
||||
preference.setEnabled(enabled);
|
||||
}
|
||||
|
@ -111,7 +111,7 @@ public class Settings extends InputMethodSettingsFragment
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
public void onCreate(final Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
setInputMethodSettingsCategoryTitle(R.string.language_selection_title);
|
||||
setSubtypeEnablerTitle(R.string.select_language);
|
||||
|
@ -192,7 +192,7 @@ public class Settings extends InputMethodSettingsFragment
|
|||
}
|
||||
|
||||
setPreferenceEnabled(findPreference(PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST),
|
||||
!SettingsValues.isLanguageSwitchKeySupressed(prefs));
|
||||
SettingsValues.showsLanguageSwitchKey(prefs));
|
||||
|
||||
final PreferenceScreen dictionaryLink =
|
||||
(PreferenceScreen) findPreference(PREF_CONFIGURE_DICTIONARIES_KEY);
|
||||
|
@ -207,7 +207,7 @@ public class Settings extends InputMethodSettingsFragment
|
|||
R.bool.config_gesture_input_enabled_by_build_config);
|
||||
final Preference gesturePreviewTrail = findPreference(PREF_GESTURE_PREVIEW_TRAIL);
|
||||
final Preference gestureFloatingPreviewText = findPreference(
|
||||
PREF_GESTURE_FLOATING_PREVIEW_TEXT);
|
||||
PREF_SHOW_GESTURE_FLOATING_PREVIEW_TEXT);
|
||||
if (!gestureInputEnabledByBuildConfig) {
|
||||
miscSettings.removePreference(findPreference(PREF_GESTURE_INPUT));
|
||||
miscSettings.removePreference(gesturePreviewTrail);
|
||||
|
@ -220,7 +220,7 @@ public class Settings extends InputMethodSettingsFragment
|
|||
|
||||
final boolean showUsabilityStudyModeOption =
|
||||
res.getBoolean(R.bool.config_enable_usability_study_mode_option)
|
||||
|| ProductionFlag.IS_EXPERIMENTAL || ENABLE_EXPERIMENTAL_SETTINGS;
|
||||
|| ProductionFlag.IS_EXPERIMENTAL || ENABLE_INTERNAL_SETTINGS;
|
||||
final Preference usabilityStudyPref = findPreference(PREF_USABILITY_STUDY_MODE);
|
||||
if (!showUsabilityStudyModeOption) {
|
||||
if (usabilityStudyPref != null) {
|
||||
|
@ -288,14 +288,14 @@ public class Settings extends InputMethodSettingsFragment
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
|
||||
public void onSharedPreferenceChanged(final SharedPreferences prefs, final String key) {
|
||||
(new BackupManager(getActivity())).dataChanged();
|
||||
if (key.equals(PREF_POPUP_ON)) {
|
||||
setPreferenceEnabled(findPreference(PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY),
|
||||
prefs.getBoolean(PREF_POPUP_ON, true));
|
||||
} else if (key.equals(PREF_SUPPRESS_LANGUAGE_SWITCH_KEY)) {
|
||||
} else if (key.equals(PREF_SHOW_LANGUAGE_SWITCH_KEY)) {
|
||||
setPreferenceEnabled(findPreference(PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST),
|
||||
!SettingsValues.isLanguageSwitchKeySupressed(prefs));
|
||||
SettingsValues.showsLanguageSwitchKey(prefs));
|
||||
} else if (key.equals(PREF_GESTURE_INPUT)) {
|
||||
final boolean gestureInputEnabledByConfig = getResources().getBoolean(
|
||||
R.bool.config_gesture_input_enabled_by_build_config);
|
||||
|
@ -304,7 +304,7 @@ public class Settings extends InputMethodSettingsFragment
|
|||
PREF_GESTURE_INPUT, true);
|
||||
setPreferenceEnabled(findPreference(PREF_GESTURE_PREVIEW_TRAIL),
|
||||
gestureInputEnabledByUser);
|
||||
setPreferenceEnabled(findPreference(PREF_GESTURE_FLOATING_PREVIEW_TEXT),
|
||||
setPreferenceEnabled(findPreference(PREF_SHOW_GESTURE_FLOATING_PREVIEW_TEXT),
|
||||
gestureInputEnabledByUser);
|
||||
}
|
||||
}
|
||||
|
@ -352,7 +352,7 @@ public class Settings extends InputMethodSettingsFragment
|
|||
}
|
||||
|
||||
private void refreshEnablingsOfKeypressSoundAndVibrationSettings(
|
||||
SharedPreferences sp, Resources res) {
|
||||
final SharedPreferences sp, final Resources res) {
|
||||
if (mKeypressVibrationDurationSettingsPref != null) {
|
||||
final boolean hasVibratorHardware = VibratorUtils.getInstance(getActivity())
|
||||
.hasVibrator();
|
||||
|
@ -370,7 +370,7 @@ public class Settings extends InputMethodSettingsFragment
|
|||
}
|
||||
|
||||
private void updateKeypressVibrationDurationSettingsSummary(
|
||||
SharedPreferences sp, Resources res) {
|
||||
final SharedPreferences sp, final Resources res) {
|
||||
if (mKeypressVibrationDurationSettingsPref != null) {
|
||||
mKeypressVibrationDurationSettingsPref.setSummary(
|
||||
SettingsValues.getCurrentVibrationDuration(sp, res)
|
||||
|
@ -428,7 +428,7 @@ public class Settings extends InputMethodSettingsFragment
|
|||
builder.create().show();
|
||||
}
|
||||
|
||||
private void updateKeypressSoundVolumeSummary(SharedPreferences sp, Resources res) {
|
||||
private void updateKeypressSoundVolumeSummary(final SharedPreferences sp, final Resources res) {
|
||||
if (mKeypressSoundVolumeSettingsPref != null) {
|
||||
mKeypressSoundVolumeSettingsPref.setSummary(String.valueOf(
|
||||
(int)(SettingsValues.getCurrentKeypressSoundVolume(sp, res) * 100)));
|
||||
|
|
|
@ -72,7 +72,7 @@ public final class SettingsValues {
|
|||
@SuppressWarnings("unused") // TODO: Use this
|
||||
private final boolean mUsabilityStudyMode;
|
||||
public final boolean mIncludesOtherImesInLanguageSwitchList;
|
||||
public final boolean mIsLanguageSwitchKeySuppressed;
|
||||
public final boolean mShowsLanguageSwitchKey;
|
||||
@SuppressWarnings("unused") // TODO: Use this
|
||||
private final String mKeyPreviewPopupDismissDelayRawValue;
|
||||
public final boolean mUseContactsDict;
|
||||
|
@ -151,7 +151,7 @@ public final class SettingsValues {
|
|||
mUsabilityStudyMode = getUsabilityStudyMode(prefs);
|
||||
mIncludesOtherImesInLanguageSwitchList = prefs.getBoolean(
|
||||
Settings.PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST, false);
|
||||
mIsLanguageSwitchKeySuppressed = isLanguageSwitchKeySupressed(prefs);
|
||||
mShowsLanguageSwitchKey = showsLanguageSwitchKey(prefs);
|
||||
mKeyPreviewPopupDismissDelayRawValue = prefs.getString(
|
||||
Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY,
|
||||
Integer.toString(res.getInteger(R.integer.config_key_preview_linger_timeout)));
|
||||
|
@ -178,7 +178,7 @@ public final class SettingsValues {
|
|||
&& prefs.getBoolean(Settings.PREF_GESTURE_INPUT, true);
|
||||
mGesturePreviewTrailEnabled = prefs.getBoolean(Settings.PREF_GESTURE_PREVIEW_TRAIL, true);
|
||||
mGestureFloatingPreviewTextEnabled = prefs.getBoolean(
|
||||
Settings.PREF_GESTURE_FLOATING_PREVIEW_TEXT, true);
|
||||
Settings.PREF_SHOW_GESTURE_FLOATING_PREVIEW_TEXT, false);
|
||||
mCorrectionEnabled = mAutoCorrectEnabled && !mInputAttributes.mInputTypeNoAutoCorrect;
|
||||
mSuggestionVisibility = createSuggestionVisibility(res);
|
||||
}
|
||||
|
@ -336,12 +336,25 @@ public final class SettingsValues {
|
|||
return mVoiceKeyOnMain;
|
||||
}
|
||||
|
||||
public static boolean isLanguageSwitchKeySupressed(final SharedPreferences prefs) {
|
||||
return prefs.getBoolean(Settings.PREF_SUPPRESS_LANGUAGE_SWITCH_KEY, false);
|
||||
// This preference key is deprecated. Use {@link #PREF_SHOW_LANGUAGE_SWITCH_KEY} instead.
|
||||
// This is being used only for the backward compatibility.
|
||||
private static final String PREF_SUPPRESS_LANGUAGE_SWITCH_KEY =
|
||||
"pref_suppress_language_switch_key";
|
||||
|
||||
public static boolean showsLanguageSwitchKey(final SharedPreferences prefs) {
|
||||
if (prefs.contains(PREF_SUPPRESS_LANGUAGE_SWITCH_KEY)) {
|
||||
final boolean suppressLanguageSwitchKey = prefs.getBoolean(
|
||||
PREF_SUPPRESS_LANGUAGE_SWITCH_KEY, false);
|
||||
final SharedPreferences.Editor editor = prefs.edit();
|
||||
editor.remove(PREF_SUPPRESS_LANGUAGE_SWITCH_KEY);
|
||||
editor.putBoolean(Settings.PREF_SHOW_LANGUAGE_SWITCH_KEY, !suppressLanguageSwitchKey);
|
||||
editor.apply();
|
||||
}
|
||||
return prefs.getBoolean(Settings.PREF_SHOW_LANGUAGE_SWITCH_KEY, true);
|
||||
}
|
||||
|
||||
public boolean isLanguageSwitchKeyEnabled(final Context context) {
|
||||
if (mIsLanguageSwitchKeySuppressed) {
|
||||
if (!mShowsLanguageSwitchKey) {
|
||||
return false;
|
||||
}
|
||||
if (mIncludesOtherImesInLanguageSwitchList) {
|
||||
|
@ -353,7 +366,7 @@ public final class SettingsValues {
|
|||
}
|
||||
}
|
||||
|
||||
public boolean isFullscreenModeAllowed(final Resources res) {
|
||||
public static boolean isFullscreenModeAllowed(final Resources res) {
|
||||
return res.getBoolean(R.bool.config_use_fullscreen_mode);
|
||||
}
|
||||
|
||||
|
|
|
@ -197,13 +197,15 @@ public final class StringUtils {
|
|||
* {@link TextUtils#CAP_MODE_CHARACTERS}, {@link TextUtils#CAP_MODE_WORDS}, and
|
||||
* {@link TextUtils#CAP_MODE_SENTENCES}.
|
||||
* @param locale The locale to consider for capitalization rules
|
||||
* @param hasSpaceBefore Whether we should consider there is a space inserted at the end of cs
|
||||
*
|
||||
* @return Returns the actual capitalization modes that can be in effect
|
||||
* at the current position, which is any combination of
|
||||
* {@link TextUtils#CAP_MODE_CHARACTERS}, {@link TextUtils#CAP_MODE_WORDS}, and
|
||||
* {@link TextUtils#CAP_MODE_SENTENCES}.
|
||||
*/
|
||||
public static int getCapsMode(final CharSequence cs, final int reqModes, final Locale locale) {
|
||||
public static int getCapsMode(final CharSequence cs, final int reqModes, final Locale locale,
|
||||
final boolean hasSpaceBefore) {
|
||||
// Quick description of what we want to do:
|
||||
// CAP_MODE_CHARACTERS is always on.
|
||||
// CAP_MODE_WORDS is on if there is some whitespace before the cursor.
|
||||
|
@ -230,11 +232,15 @@ public final class StringUtils {
|
|||
// single quote since they aren't start punctuation in the unicode sense, but should still
|
||||
// be skipped for English. TODO: does this depend on the language?
|
||||
int i;
|
||||
for (i = cs.length(); i > 0; i--) {
|
||||
final char c = cs.charAt(i - 1);
|
||||
if (c != Keyboard.CODE_DOUBLE_QUOTE && c != Keyboard.CODE_SINGLE_QUOTE
|
||||
&& Character.getType(c) != Character.START_PUNCTUATION) {
|
||||
break;
|
||||
if (hasSpaceBefore) {
|
||||
i = cs.length() + 1;
|
||||
} else {
|
||||
for (i = cs.length(); i > 0; i--) {
|
||||
final char c = cs.charAt(i - 1);
|
||||
if (c != Keyboard.CODE_DOUBLE_QUOTE && c != Keyboard.CODE_SINGLE_QUOTE
|
||||
&& Character.getType(c) != Character.START_PUNCTUATION) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -247,6 +253,7 @@ public final class StringUtils {
|
|||
// if the first char that's not a space or tab is a start of line (as in, either \n or
|
||||
// start of text).
|
||||
int j = i;
|
||||
if (hasSpaceBefore) --j;
|
||||
while (j > 0 && Character.isWhitespace(cs.charAt(j - 1))) {
|
||||
j--;
|
||||
}
|
||||
|
|
|
@ -306,6 +306,10 @@ public class Suggest {
|
|||
wordComposer, prevWordForBigram, proximityInfo, sessionId));
|
||||
}
|
||||
|
||||
for (SuggestedWordInfo wordInfo : suggestionsSet) {
|
||||
LatinImeLogger.onAddSuggestedWord(wordInfo.mWord.toString(), wordInfo.mSourceDict);
|
||||
}
|
||||
|
||||
final ArrayList<SuggestedWordInfo> suggestionsContainer =
|
||||
CollectionUtils.newArrayList(suggestionsSet);
|
||||
final int suggestionsCount = suggestionsContainer.size();
|
||||
|
|
|
@ -18,6 +18,7 @@ package com.android.inputmethod.latin;
|
|||
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.inputmethod.latin.makedict.BinaryDictIOUtils;
|
||||
import com.android.inputmethod.latin.makedict.BinaryDictInputOutput;
|
||||
import com.android.inputmethod.latin.makedict.BinaryDictInputOutput.FusionDictionaryBufferInterface;
|
||||
import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
|
||||
|
@ -96,6 +97,11 @@ public class UserHistoryDictIOUtils {
|
|||
public void put(final byte b) {
|
||||
mBuffer[mPosition++] = b;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int limit() {
|
||||
return mBuffer.length;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -162,7 +168,7 @@ public class UserHistoryDictIOUtils {
|
|||
final Map<Integer, ArrayList<PendingAttribute>> bigrams = CollectionUtils.newTreeMap();
|
||||
|
||||
try {
|
||||
BinaryDictInputOutput.readUnigramsAndBigramsBinary(buffer, unigrams, frequencies,
|
||||
BinaryDictIOUtils.readUnigramsAndBigramsBinary(buffer, unigrams, frequencies,
|
||||
bigrams);
|
||||
addWordsFromWordMap(unigrams, frequencies, bigrams, dict);
|
||||
} catch (IOException e) {
|
||||
|
|
|
@ -412,14 +412,24 @@ public final class Utils {
|
|||
}
|
||||
|
||||
public static void onAutoCorrection(final String typedWord, final String correctedWord,
|
||||
final String separatorString) {
|
||||
if (TextUtils.isEmpty(typedWord)) return;
|
||||
final String separatorString, final WordComposer wordComposer) {
|
||||
final boolean isBatchMode = wordComposer.isBatchMode();
|
||||
if (!isBatchMode && TextUtils.isEmpty(typedWord)) return;
|
||||
// TODO: this fails when the separator is more than 1 code point long, but
|
||||
// the backend can't handle it yet. The only case when this happens is with
|
||||
// smileys and other multi-character keys.
|
||||
final int codePoint = TextUtils.isEmpty(separatorString) ? Constants.NOT_A_CODE
|
||||
: separatorString.codePointAt(0);
|
||||
LatinImeLogger.logOnAutoCorrection(typedWord, correctedWord, codePoint);
|
||||
if (!isBatchMode) {
|
||||
LatinImeLogger.logOnAutoCorrectionForTyping(typedWord, correctedWord, codePoint);
|
||||
} else {
|
||||
if (!TextUtils.isEmpty(correctedWord)) {
|
||||
// We must make sure that InputPointer contains only the relative timestamps,
|
||||
// not actual timestamps.
|
||||
LatinImeLogger.logOnAutoCorrectionForGeometric(
|
||||
"", correctedWord, codePoint, wordComposer.getInputPointers());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void onAutoCorrectionCancellation() {
|
||||
|
|
|
@ -265,9 +265,12 @@ public class WordComposer {
|
|||
* @return true if all user typed chars are upper case, false otherwise
|
||||
*/
|
||||
public boolean isAllUpperCase() {
|
||||
return mCapitalizedMode == CAPS_MODE_AUTO_SHIFT_LOCKED
|
||||
|| mCapitalizedMode == CAPS_MODE_MANUAL_SHIFT_LOCKED
|
||||
|| (mCapsCount > 0) && (mCapsCount == size());
|
||||
if (size() <= 1) {
|
||||
return mCapitalizedMode == CAPS_MODE_AUTO_SHIFT_LOCKED
|
||||
|| mCapitalizedMode == CAPS_MODE_MANUAL_SHIFT_LOCKED;
|
||||
} else {
|
||||
return mCapsCount == size();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean wasShiftedNoLock() {
|
||||
|
|
|
@ -22,4 +22,5 @@ public final class ProductionFlag {
|
|||
}
|
||||
|
||||
public static final boolean IS_EXPERIMENTAL = false;
|
||||
public static final boolean IS_INTERNAL = false;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
* Copyright (C) 2012 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
||||
* use this file except in compliance with the License. You may obtain a copy of
|
||||
* the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package com.android.inputmethod.latin.makedict;
|
||||
|
||||
import com.android.inputmethod.latin.Constants;
|
||||
import com.android.inputmethod.latin.makedict.BinaryDictInputOutput.FusionDictionaryBufferInterface;
|
||||
import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
|
||||
import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
|
||||
import com.android.inputmethod.latin.makedict.FusionDictionary.CharGroup;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
import java.util.Stack;
|
||||
|
||||
public class BinaryDictIOUtils {
|
||||
private static final boolean DBG = false;
|
||||
|
||||
private static class Position {
|
||||
public static final int NOT_READ_GROUPCOUNT = -1;
|
||||
|
||||
public int mAddress;
|
||||
public int mNumOfCharGroup;
|
||||
public int mPosition;
|
||||
public int mLength;
|
||||
|
||||
public Position(int address, int length) {
|
||||
mAddress = address;
|
||||
mLength = length;
|
||||
mNumOfCharGroup = NOT_READ_GROUPCOUNT;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tours all node without recursive call.
|
||||
*/
|
||||
private static void readUnigramsAndBigramsBinaryInner(
|
||||
final FusionDictionaryBufferInterface buffer, final int headerSize,
|
||||
final Map<Integer, String> words, final Map<Integer, Integer> frequencies,
|
||||
final Map<Integer, ArrayList<PendingAttribute>> bigrams,
|
||||
final FormatOptions formatOptions) {
|
||||
int[] pushedChars = new int[FormatSpec.MAX_WORD_LENGTH + 1];
|
||||
|
||||
Stack<Position> stack = new Stack<Position>();
|
||||
int index = 0;
|
||||
|
||||
Position initPos = new Position(headerSize, 0);
|
||||
stack.push(initPos);
|
||||
|
||||
while (!stack.empty()) {
|
||||
Position p = stack.peek();
|
||||
|
||||
if (DBG) {
|
||||
MakedictLog.d("read: address=" + p.mAddress + ", numOfCharGroup=" +
|
||||
p.mNumOfCharGroup + ", position=" + p.mPosition + ", length=" + p.mLength);
|
||||
}
|
||||
|
||||
if (buffer.position() != p.mAddress) buffer.position(p.mAddress);
|
||||
if (index != p.mLength) index = p.mLength;
|
||||
|
||||
if (p.mNumOfCharGroup == Position.NOT_READ_GROUPCOUNT) {
|
||||
p.mNumOfCharGroup = BinaryDictInputOutput.readCharGroupCount(buffer);
|
||||
p.mAddress += BinaryDictInputOutput.getGroupCountSize(p.mNumOfCharGroup);
|
||||
p.mPosition = 0;
|
||||
}
|
||||
|
||||
CharGroupInfo info = BinaryDictInputOutput.readCharGroup(buffer,
|
||||
p.mAddress - headerSize, formatOptions);
|
||||
for (int i = 0; i < info.mCharacters.length; ++i) {
|
||||
pushedChars[index++] = info.mCharacters[i];
|
||||
}
|
||||
p.mPosition++;
|
||||
|
||||
if (info.mFrequency != FusionDictionary.CharGroup.NOT_A_TERMINAL) { // found word
|
||||
words.put(info.mOriginalAddress, new String(pushedChars, 0, index));
|
||||
frequencies.put(info.mOriginalAddress, info.mFrequency);
|
||||
if (info.mBigrams != null) bigrams.put(info.mOriginalAddress, info.mBigrams);
|
||||
}
|
||||
|
||||
if (p.mPosition == p.mNumOfCharGroup) {
|
||||
stack.pop();
|
||||
} else {
|
||||
// the node has more groups.
|
||||
p.mAddress = buffer.position();
|
||||
}
|
||||
|
||||
if (BinaryDictInputOutput.hasChildrenAddress(info.mChildrenAddress)) {
|
||||
Position childrenPos = new Position(info.mChildrenAddress + headerSize, index);
|
||||
stack.push(childrenPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads unigrams and bigrams from the binary file.
|
||||
* Doesn't make the memory representation of the dictionary.
|
||||
*
|
||||
* @param buffer the buffer to read.
|
||||
* @param words the map to store the address as a key and the word as a value.
|
||||
* @param frequencies the map to store the address as a key and the frequency as a value.
|
||||
* @param bigrams the map to store the address as a key and the list of address as a value.
|
||||
* @throws IOException
|
||||
* @throws UnsupportedFormatException
|
||||
*/
|
||||
public static void readUnigramsAndBigramsBinary(final FusionDictionaryBufferInterface buffer,
|
||||
final Map<Integer, String> words, final Map<Integer, Integer> frequencies,
|
||||
final Map<Integer, ArrayList<PendingAttribute>> bigrams) throws IOException,
|
||||
UnsupportedFormatException {
|
||||
// Read header
|
||||
final FileHeader header = BinaryDictInputOutput.readHeader(buffer);
|
||||
readUnigramsAndBigramsBinaryInner(buffer, header.mHeaderSize, words, frequencies, bigrams,
|
||||
header.mFormatOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the address of the last CharGroup of the exact matching word in the dictionary.
|
||||
* If no match is found, returns NOT_VALID_WORD.
|
||||
*
|
||||
* @param buffer the buffer to read.
|
||||
* @param word the word we search for.
|
||||
* @return the address of the terminal node.
|
||||
* @throws IOException
|
||||
* @throws UnsupportedFormatException
|
||||
*/
|
||||
public static int getTerminalPosition(final FusionDictionaryBufferInterface buffer,
|
||||
final String word) throws IOException, UnsupportedFormatException {
|
||||
if (word == null) return FormatSpec.NOT_VALID_WORD;
|
||||
if (buffer.position() != 0) buffer.position(0);
|
||||
|
||||
final FileHeader header = BinaryDictInputOutput.readHeader(buffer);
|
||||
int wordPos = 0;
|
||||
final int wordLen = word.codePointCount(0, word.length());
|
||||
for (int depth = 0; depth < Constants.Dictionary.MAX_WORD_LENGTH; ++depth) {
|
||||
if (wordPos >= wordLen) return FormatSpec.NOT_VALID_WORD;
|
||||
int groupOffset = buffer.position() - header.mHeaderSize;
|
||||
final int charGroupCount = BinaryDictInputOutput.readCharGroupCount(buffer);
|
||||
groupOffset += BinaryDictInputOutput.getGroupCountSize(charGroupCount);
|
||||
|
||||
for (int i = 0; i < charGroupCount; ++i) {
|
||||
final int charGroupPos = buffer.position();
|
||||
final CharGroupInfo currentInfo = BinaryDictInputOutput.readCharGroup(buffer,
|
||||
buffer.position(), header.mFormatOptions);
|
||||
boolean same = true;
|
||||
for (int p = 0, j = word.offsetByCodePoints(0, wordPos);
|
||||
p < currentInfo.mCharacters.length;
|
||||
++p, j = word.offsetByCodePoints(j, 1)) {
|
||||
if (wordPos + p >= wordLen
|
||||
|| word.codePointAt(j) != currentInfo.mCharacters[p]) {
|
||||
same = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (same) {
|
||||
if (wordPos + currentInfo.mCharacters.length == wordLen) {
|
||||
if (currentInfo.mFrequency == CharGroup.NOT_A_TERMINAL) {
|
||||
return FormatSpec.NOT_VALID_WORD;
|
||||
} else {
|
||||
return charGroupPos;
|
||||
}
|
||||
}
|
||||
wordPos += currentInfo.mCharacters.length;
|
||||
if (currentInfo.mChildrenAddress == FormatSpec.NO_CHILDREN_ADDRESS) {
|
||||
return FormatSpec.NOT_VALID_WORD;
|
||||
}
|
||||
buffer.position(currentInfo.mChildrenAddress);
|
||||
break;
|
||||
}
|
||||
groupOffset = currentInfo.mEndAddress;
|
||||
|
||||
// not found
|
||||
if (i >= charGroupCount - 1) {
|
||||
return FormatSpec.NOT_VALID_WORD;
|
||||
}
|
||||
}
|
||||
}
|
||||
return FormatSpec.NOT_VALID_WORD;
|
||||
}
|
||||
}
|
|
@ -63,6 +63,7 @@ public class BinaryDictInputOutput {
|
|||
public int position();
|
||||
public void position(int newPosition);
|
||||
public void put(final byte b);
|
||||
public int limit();
|
||||
}
|
||||
|
||||
public static final class ByteBufferWrapper implements FusionDictionaryBufferInterface {
|
||||
|
@ -107,6 +108,11 @@ public class BinaryDictInputOutput {
|
|||
public void put(final byte b) {
|
||||
mBuffer.put(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int limit() {
|
||||
return mBuffer.limit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -284,7 +290,7 @@ public class BinaryDictInputOutput {
|
|||
* @param count the group count
|
||||
* @return the size of the group count, either 1 or 2 bytes.
|
||||
*/
|
||||
private static int getGroupCountSize(final int count) {
|
||||
public static int getGroupCountSize(final int count) {
|
||||
if (FormatSpec.MAX_CHARGROUPS_FOR_ONE_BYTE_CHARGROUP_COUNT >= count) {
|
||||
return 1;
|
||||
} else if (FormatSpec.MAX_CHARGROUPS_IN_A_NODE >= count) {
|
||||
|
@ -370,13 +376,16 @@ public class BinaryDictInputOutput {
|
|||
g.mCachedSize = groupSize;
|
||||
size += groupSize;
|
||||
}
|
||||
if (options.mHasLinkedListNode) {
|
||||
size += FormatSpec.FORWARD_LINK_ADDRESS_SIZE;
|
||||
}
|
||||
node.mCachedSize = size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to hide the actual value of the no children address.
|
||||
*/
|
||||
private static boolean hasChildrenAddress(final int address) {
|
||||
public static boolean hasChildrenAddress(final int address) {
|
||||
return FormatSpec.NO_CHILDREN_ADDRESS != address;
|
||||
}
|
||||
|
||||
|
@ -521,6 +530,9 @@ public class BinaryDictInputOutput {
|
|||
group.mCachedSize = groupSize;
|
||||
size += groupSize;
|
||||
}
|
||||
if (formatOptions.mHasLinkedListNode) {
|
||||
size += FormatSpec.FORWARD_LINK_ADDRESS_SIZE;
|
||||
}
|
||||
if (node.mCachedSize != size) {
|
||||
node.mCachedSize = size;
|
||||
changed = true;
|
||||
|
@ -532,9 +544,11 @@ public class BinaryDictInputOutput {
|
|||
* Computes the byte size of a list of nodes and updates each node cached position.
|
||||
*
|
||||
* @param flatNodes the array of nodes.
|
||||
* @param formatOptions file format options.
|
||||
* @return the byte size of the entire stack.
|
||||
*/
|
||||
private static int stackNodes(final ArrayList<Node> flatNodes) {
|
||||
private static int stackNodes(final ArrayList<Node> flatNodes,
|
||||
final FormatOptions formatOptions) {
|
||||
int nodeOffset = 0;
|
||||
for (Node n : flatNodes) {
|
||||
n.mCachedAddress = nodeOffset;
|
||||
|
@ -544,7 +558,9 @@ public class BinaryDictInputOutput {
|
|||
g.mCachedAddress = groupCountSize + nodeOffset + groupOffset;
|
||||
groupOffset += g.mCachedSize;
|
||||
}
|
||||
if (groupOffset + groupCountSize != n.mCachedSize) {
|
||||
final int nodeSize = groupCountSize + groupOffset
|
||||
+ (formatOptions.mHasLinkedListNode ? FormatSpec.FORWARD_LINK_ADDRESS_SIZE : 0);
|
||||
if (nodeSize != n.mCachedSize) {
|
||||
throw new RuntimeException("Bug : Stored and computed node size differ");
|
||||
}
|
||||
nodeOffset += n.mCachedSize;
|
||||
|
@ -571,7 +587,7 @@ public class BinaryDictInputOutput {
|
|||
final ArrayList<Node> flatNodes, final FormatOptions formatOptions) {
|
||||
// First get the worst sizes and offsets
|
||||
for (Node n : flatNodes) setNodeMaximumSize(n, formatOptions);
|
||||
final int offset = stackNodes(flatNodes);
|
||||
final int offset = stackNodes(flatNodes, formatOptions);
|
||||
|
||||
MakedictLog.i("Compressing the array addresses. Original size : " + offset);
|
||||
MakedictLog.i("(Recursively seen size : " + offset + ")");
|
||||
|
@ -587,7 +603,7 @@ public class BinaryDictInputOutput {
|
|||
if (oldNodeSize < newNodeSize) throw new RuntimeException("Increased size ?!");
|
||||
changesDone |= changed;
|
||||
}
|
||||
stackNodes(flatNodes);
|
||||
stackNodes(flatNodes, formatOptions);
|
||||
++passes;
|
||||
if (passes > MAX_PASSES) throw new RuntimeException("Too many passes - probably a bug");
|
||||
} while (changesDone);
|
||||
|
@ -776,7 +792,8 @@ public class BinaryDictInputOutput {
|
|||
return (options.mFrenchLigatureProcessing ? FormatSpec.FRENCH_LIGATURE_PROCESSING_FLAG : 0)
|
||||
+ (options.mGermanUmlautProcessing ? FormatSpec.GERMAN_UMLAUT_PROCESSING_FLAG : 0)
|
||||
+ (hasBigrams ? FormatSpec.CONTAINS_BIGRAMS_FLAG : 0)
|
||||
+ (formatOptions.mHasParentAddress ? FormatSpec.HAS_PARENT_ADDRESS : 0);
|
||||
+ (formatOptions.mHasParentAddress ? FormatSpec.HAS_PARENT_ADDRESS : 0)
|
||||
+ (formatOptions.mHasLinkedListNode ? FormatSpec.HAS_LINKEDLIST_NODE : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -910,6 +927,11 @@ public class BinaryDictInputOutput {
|
|||
}
|
||||
|
||||
}
|
||||
if (formatOptions.mHasLinkedListNode) {
|
||||
buffer[index] = buffer[index + 1] = buffer[index + 2]
|
||||
= FormatSpec.NO_FORWARD_LINK_ADDRESS;
|
||||
index += FormatSpec.FORWARD_LINK_ADDRESS_SIZE;
|
||||
}
|
||||
if (index != node.mCachedAddress + node.mCachedSize) throw new RuntimeException(
|
||||
"Not the same size : written "
|
||||
+ (index - node.mCachedAddress) + " bytes out of a node that should have "
|
||||
|
@ -1083,7 +1105,7 @@ public class BinaryDictInputOutput {
|
|||
// readDictionaryBinary is the public entry point for them.
|
||||
|
||||
private static final int[] CHARACTER_BUFFER = new int[FormatSpec.MAX_WORD_LENGTH];
|
||||
private static CharGroupInfo readCharGroup(final FusionDictionaryBufferInterface buffer,
|
||||
public static CharGroupInfo readCharGroup(final FusionDictionaryBufferInterface buffer,
|
||||
final int originalGroupAddress, final FormatOptions options) {
|
||||
int addressPointer = originalGroupAddress;
|
||||
final int flags = buffer.readUnsignedByte();
|
||||
|
@ -1196,7 +1218,7 @@ public class BinaryDictInputOutput {
|
|||
/**
|
||||
* Reads and returns the char group count out of a buffer and forwards the pointer.
|
||||
*/
|
||||
private static int readCharGroupCount(final FusionDictionaryBufferInterface buffer) {
|
||||
public static int readCharGroupCount(final FusionDictionaryBufferInterface buffer) {
|
||||
final int msb = buffer.readUnsignedByte();
|
||||
if (FormatSpec.MAX_CHARGROUPS_FOR_ONE_BYTE_CHARGROUP_COUNT >= msb) {
|
||||
return msb;
|
||||
|
@ -1220,8 +1242,9 @@ public class BinaryDictInputOutput {
|
|||
* @param formatOptions file format options.
|
||||
* @return the word, as a string.
|
||||
*/
|
||||
private static String getWordAtAddress(final FusionDictionaryBufferInterface buffer,
|
||||
final int headerSize, final int address, final FormatOptions formatOptions) {
|
||||
/* packages for tests */ static String getWordAtAddress(
|
||||
final FusionDictionaryBufferInterface buffer, final int headerSize, final int address,
|
||||
final FormatOptions formatOptions) {
|
||||
final String cachedString = wordCache.get(address);
|
||||
if (null != cachedString) return cachedString;
|
||||
|
||||
|
@ -1325,146 +1348,67 @@ public class BinaryDictInputOutput {
|
|||
final Map<Integer, Node> reverseNodeMap, final Map<Integer, CharGroup> reverseGroupMap,
|
||||
final FormatOptions options)
|
||||
throws IOException {
|
||||
final int nodeOrigin = buffer.position() - headerSize;
|
||||
final int count = readCharGroupCount(buffer);
|
||||
final ArrayList<CharGroup> nodeContents = new ArrayList<CharGroup>();
|
||||
int groupOffset = nodeOrigin + getGroupCountSize(count);
|
||||
for (int i = count; i > 0; --i) {
|
||||
CharGroupInfo info = readCharGroup(buffer, groupOffset, options);
|
||||
ArrayList<WeightedString> shortcutTargets = info.mShortcutTargets;
|
||||
ArrayList<WeightedString> bigrams = null;
|
||||
if (null != info.mBigrams) {
|
||||
bigrams = new ArrayList<WeightedString>();
|
||||
for (PendingAttribute bigram : info.mBigrams) {
|
||||
final String word = getWordAtAddress(
|
||||
buffer, headerSize, bigram.mAddress, options);
|
||||
bigrams.add(new WeightedString(word, bigram.mFrequency));
|
||||
final int nodeOrigin = buffer.position() - headerSize;
|
||||
|
||||
do { // Scan the linked-list node.
|
||||
final int nodeHeadPosition = buffer.position() - headerSize;
|
||||
final int count = readCharGroupCount(buffer);
|
||||
int groupOffset = nodeHeadPosition + getGroupCountSize(count);
|
||||
for (int i = count; i > 0; --i) { // Scan the array of CharGroup.
|
||||
CharGroupInfo info = readCharGroup(buffer, groupOffset, options);
|
||||
ArrayList<WeightedString> shortcutTargets = info.mShortcutTargets;
|
||||
ArrayList<WeightedString> bigrams = null;
|
||||
if (null != info.mBigrams) {
|
||||
bigrams = new ArrayList<WeightedString>();
|
||||
for (PendingAttribute bigram : info.mBigrams) {
|
||||
final String word = getWordAtAddress(
|
||||
buffer, headerSize, bigram.mAddress, options);
|
||||
bigrams.add(new WeightedString(word, bigram.mFrequency));
|
||||
}
|
||||
}
|
||||
if (hasChildrenAddress(info.mChildrenAddress)) {
|
||||
Node children = reverseNodeMap.get(info.mChildrenAddress);
|
||||
if (null == children) {
|
||||
final int currentPosition = buffer.position();
|
||||
buffer.position(info.mChildrenAddress + headerSize);
|
||||
children = readNode(
|
||||
buffer, headerSize, reverseNodeMap, reverseGroupMap, options);
|
||||
buffer.position(currentPosition);
|
||||
}
|
||||
nodeContents.add(
|
||||
new CharGroup(info.mCharacters, shortcutTargets, bigrams,
|
||||
info.mFrequency,
|
||||
0 != (info.mFlags & FormatSpec.FLAG_IS_NOT_A_WORD),
|
||||
0 != (info.mFlags & FormatSpec.FLAG_IS_BLACKLISTED), children));
|
||||
} else {
|
||||
nodeContents.add(
|
||||
new CharGroup(info.mCharacters, shortcutTargets, bigrams,
|
||||
info.mFrequency,
|
||||
0 != (info.mFlags & FormatSpec.FLAG_IS_NOT_A_WORD),
|
||||
0 != (info.mFlags & FormatSpec.FLAG_IS_BLACKLISTED)));
|
||||
}
|
||||
groupOffset = info.mEndAddress;
|
||||
}
|
||||
|
||||
// reach the end of the array.
|
||||
if (options.mHasLinkedListNode) {
|
||||
final int nextAddress = buffer.readUnsignedInt24();
|
||||
if (nextAddress >= 0 && nextAddress < buffer.limit()) {
|
||||
buffer.position(nextAddress);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (hasChildrenAddress(info.mChildrenAddress)) {
|
||||
Node children = reverseNodeMap.get(info.mChildrenAddress);
|
||||
if (null == children) {
|
||||
final int currentPosition = buffer.position();
|
||||
buffer.position(info.mChildrenAddress + headerSize);
|
||||
children = readNode(
|
||||
buffer, headerSize, reverseNodeMap, reverseGroupMap, options);
|
||||
buffer.position(currentPosition);
|
||||
}
|
||||
nodeContents.add(
|
||||
new CharGroup(info.mCharacters, shortcutTargets, bigrams, info.mFrequency,
|
||||
0 != (info.mFlags & FormatSpec.FLAG_IS_NOT_A_WORD),
|
||||
0 != (info.mFlags & FormatSpec.FLAG_IS_BLACKLISTED), children));
|
||||
} else {
|
||||
nodeContents.add(
|
||||
new CharGroup(info.mCharacters, shortcutTargets, bigrams, info.mFrequency,
|
||||
0 != (info.mFlags & FormatSpec.FLAG_IS_NOT_A_WORD),
|
||||
0 != (info.mFlags & FormatSpec.FLAG_IS_BLACKLISTED)));
|
||||
}
|
||||
groupOffset = info.mEndAddress;
|
||||
}
|
||||
} while (options.mHasLinkedListNode &&
|
||||
buffer.position() != FormatSpec.NO_FORWARD_LINK_ADDRESS);
|
||||
|
||||
final Node node = new Node(nodeContents);
|
||||
node.mCachedAddress = nodeOrigin;
|
||||
reverseNodeMap.put(node.mCachedAddress, node);
|
||||
return node;
|
||||
}
|
||||
|
||||
// TODO: move these methods (readUnigramsAndBigramsBinary(|Inner)) and an inner class (Position)
|
||||
// out of this class.
|
||||
private static class Position {
|
||||
public static final int NOT_READ_GROUPCOUNT = -1;
|
||||
|
||||
public int mAddress;
|
||||
public int mNumOfCharGroup;
|
||||
public int mPosition;
|
||||
public int mLength;
|
||||
|
||||
public Position(int address, int length) {
|
||||
mAddress = address;
|
||||
mLength = length;
|
||||
mNumOfCharGroup = NOT_READ_GROUPCOUNT;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tours all node without recursive call.
|
||||
*/
|
||||
private static void readUnigramsAndBigramsBinaryInner(
|
||||
final FusionDictionaryBufferInterface buffer, final int headerSize,
|
||||
final Map<Integer, String> words, final Map<Integer, Integer> frequencies,
|
||||
final Map<Integer, ArrayList<PendingAttribute>> bigrams,
|
||||
final FormatOptions formatOptions) {
|
||||
int[] pushedChars = new int[FormatSpec.MAX_WORD_LENGTH + 1];
|
||||
|
||||
Stack<Position> stack = new Stack<Position>();
|
||||
int index = 0;
|
||||
|
||||
Position initPos = new Position(headerSize, 0);
|
||||
stack.push(initPos);
|
||||
|
||||
while (!stack.empty()) {
|
||||
Position p = stack.peek();
|
||||
|
||||
if (DBG) {
|
||||
MakedictLog.d("read: address=" + p.mAddress + ", numOfCharGroup=" +
|
||||
p.mNumOfCharGroup + ", position=" + p.mPosition + ", length=" + p.mLength);
|
||||
}
|
||||
|
||||
if (buffer.position() != p.mAddress) buffer.position(p.mAddress);
|
||||
if (index != p.mLength) index = p.mLength;
|
||||
|
||||
if (p.mNumOfCharGroup == Position.NOT_READ_GROUPCOUNT) {
|
||||
p.mNumOfCharGroup = readCharGroupCount(buffer);
|
||||
p.mAddress += getGroupCountSize(p.mNumOfCharGroup);
|
||||
p.mPosition = 0;
|
||||
}
|
||||
|
||||
CharGroupInfo info = readCharGroup(buffer, p.mAddress - headerSize, formatOptions);
|
||||
for (int i = 0; i < info.mCharacters.length; ++i) {
|
||||
pushedChars[index++] = info.mCharacters[i];
|
||||
}
|
||||
p.mPosition++;
|
||||
|
||||
if (info.mFrequency != FusionDictionary.CharGroup.NOT_A_TERMINAL) { // found word
|
||||
words.put(info.mOriginalAddress, new String(pushedChars, 0, index));
|
||||
frequencies.put(info.mOriginalAddress, info.mFrequency);
|
||||
if (info.mBigrams != null) bigrams.put(info.mOriginalAddress, info.mBigrams);
|
||||
}
|
||||
|
||||
if (p.mPosition == p.mNumOfCharGroup) {
|
||||
stack.pop();
|
||||
} else {
|
||||
// the node has more groups.
|
||||
p.mAddress = buffer.position();
|
||||
}
|
||||
|
||||
if (hasChildrenAddress(info.mChildrenAddress)) {
|
||||
Position childrenPos = new Position(info.mChildrenAddress + headerSize, index);
|
||||
stack.push(childrenPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads unigrams and bigrams from the binary file.
|
||||
* Doesn't make the memory representation of the dictionary.
|
||||
*
|
||||
* @param buffer the buffer to read.
|
||||
* @param words the map to store the address as a key and the word as a value.
|
||||
* @param frequencies the map to store the address as a key and the frequency as a value.
|
||||
* @param bigrams the map to store the address as a key and the list of address as a value.
|
||||
* @throws IOException
|
||||
* @throws UnsupportedFormatException
|
||||
*/
|
||||
public static void readUnigramsAndBigramsBinary(final FusionDictionaryBufferInterface buffer,
|
||||
final Map<Integer, String> words, final Map<Integer, Integer> frequencies,
|
||||
final Map<Integer, ArrayList<PendingAttribute>> bigrams) throws IOException,
|
||||
UnsupportedFormatException {
|
||||
// Read header
|
||||
final FileHeader header = readHeader(buffer);
|
||||
readUnigramsAndBigramsBinaryInner(buffer, header.mHeaderSize, words, frequencies, bigrams,
|
||||
header.mFormatOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to get the binary format version from the header.
|
||||
* @throws IOException
|
||||
|
@ -1501,7 +1445,7 @@ public class BinaryDictInputOutput {
|
|||
* @throws IOException
|
||||
* @throws UnsupportedFormatException
|
||||
*/
|
||||
private static FileHeader readHeader(final FusionDictionaryBufferInterface buffer)
|
||||
public static FileHeader readHeader(final FusionDictionaryBufferInterface buffer)
|
||||
throws IOException, UnsupportedFormatException {
|
||||
final int version = checkFormatVersion(buffer);
|
||||
final int optionsFlags = buffer.readUnsignedShort();
|
||||
|
@ -1525,7 +1469,8 @@ public class BinaryDictInputOutput {
|
|||
0 != (optionsFlags & FormatSpec.GERMAN_UMLAUT_PROCESSING_FLAG),
|
||||
0 != (optionsFlags & FormatSpec.FRENCH_LIGATURE_PROCESSING_FLAG)),
|
||||
new FormatOptions(version,
|
||||
0 != (optionsFlags & FormatSpec.HAS_PARENT_ADDRESS)));
|
||||
0 != (optionsFlags & FormatSpec.HAS_PARENT_ADDRESS),
|
||||
0 != (optionsFlags & FormatSpec.HAS_LINKEDLIST_NODE)));
|
||||
return header;
|
||||
}
|
||||
|
||||
|
@ -1543,11 +1488,6 @@ public class BinaryDictInputOutput {
|
|||
options.put(key, value);
|
||||
}
|
||||
}
|
||||
// TODO: remove this method.
|
||||
public static void populateOptions(final ByteBuffer buffer, final int headerSize,
|
||||
final HashMap<String, String> options) {
|
||||
populateOptions(new ByteBufferWrapper(buffer), headerSize, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a buffer and returns the memory representation of the dictionary.
|
||||
|
|
|
@ -41,6 +41,12 @@ public final class FormatSpec {
|
|||
* u |
|
||||
* ps
|
||||
*
|
||||
* f |
|
||||
* o | IF HAS_LINKEDLIST_NODE (defined in the file header)
|
||||
* r | forward link address, 3byte
|
||||
* w | the address must be positive.
|
||||
* a |
|
||||
* rdlinkaddress
|
||||
*/
|
||||
|
||||
/* Node(CharGroup) layout is as follows:
|
||||
|
@ -140,18 +146,23 @@ public final class FormatSpec {
|
|||
static final int NOT_A_VERSION_NUMBER = -1;
|
||||
static final int FIRST_VERSION_WITH_HEADER_SIZE = 2;
|
||||
static final int FIRST_VERSION_WITH_PARENT_ADDRESS = 3;
|
||||
static final int FIRST_VERSION_WITH_LINKEDLIST_NODE = 3;
|
||||
|
||||
// These options need to be the same numeric values as the one in the native reading code.
|
||||
static final int GERMAN_UMLAUT_PROCESSING_FLAG = 0x1;
|
||||
// TODO: Make the native reading code read this variable.
|
||||
static final int HAS_PARENT_ADDRESS = 0x2;
|
||||
static final int FRENCH_LIGATURE_PROCESSING_FLAG = 0x4;
|
||||
static final int CONTAINS_BIGRAMS_FLAG = 0x8;
|
||||
// TODO: Make the native reading code read this variable.
|
||||
static final int HAS_LINKEDLIST_NODE = 0x10;
|
||||
|
||||
// TODO: Make this value adaptative to content data, store it in the header, and
|
||||
// use it in the reading code.
|
||||
static final int MAX_WORD_LENGTH = Constants.Dictionary.MAX_WORD_LENGTH;
|
||||
|
||||
static final int PARENT_ADDRESS_SIZE = 3;
|
||||
static final int FORWARD_LINK_ADDRESS_SIZE = 3;
|
||||
|
||||
static final int MASK_GROUP_ADDRESS_TYPE = 0xC0;
|
||||
static final int FLAG_GROUP_ADDRESS_TYPE_NOADDRESS = 0x00;
|
||||
|
@ -187,6 +198,7 @@ public final class FormatSpec {
|
|||
|
||||
static final int NO_CHILDREN_ADDRESS = Integer.MIN_VALUE;
|
||||
static final int NO_PARENT_ADDRESS = 0;
|
||||
static final int NO_FORWARD_LINK_ADDRESS = 0;
|
||||
static final int INVALID_CHARACTER = -1;
|
||||
|
||||
static final int MAX_CHARGROUPS_FOR_ONE_BYTE_CHARGROUP_COUNT = 0x7F; // 127
|
||||
|
@ -195,22 +207,39 @@ public final class FormatSpec {
|
|||
static final int MAX_TERMINAL_FREQUENCY = 255;
|
||||
static final int MAX_BIGRAM_FREQUENCY = 15;
|
||||
|
||||
// This option needs to be the same numeric value as the one in binary_format.h.
|
||||
static final int NOT_VALID_WORD = -99;
|
||||
|
||||
/**
|
||||
* Options about file format.
|
||||
*/
|
||||
public static class FormatOptions {
|
||||
public final int mVersion;
|
||||
public final boolean mHasParentAddress;
|
||||
public final boolean mHasLinkedListNode;
|
||||
public FormatOptions(final int version) {
|
||||
this(version, false);
|
||||
}
|
||||
public FormatOptions(final int version, final boolean hasParentAddress) {
|
||||
this(version, hasParentAddress, false);
|
||||
}
|
||||
public FormatOptions(final int version, final boolean hasParentAddress,
|
||||
final boolean hasLinkedListNode) {
|
||||
mVersion = version;
|
||||
if (version < FormatSpec.FIRST_VERSION_WITH_PARENT_ADDRESS && hasParentAddress) {
|
||||
if (version < FIRST_VERSION_WITH_PARENT_ADDRESS && hasParentAddress) {
|
||||
throw new RuntimeException("Parent addresses are only supported with versions "
|
||||
+ FormatSpec.FIRST_VERSION_WITH_PARENT_ADDRESS + " and ulterior.");
|
||||
+ FIRST_VERSION_WITH_PARENT_ADDRESS + " and ulterior.");
|
||||
}
|
||||
mHasParentAddress = hasParentAddress;
|
||||
|
||||
if (version < FIRST_VERSION_WITH_LINKEDLIST_NODE && hasLinkedListNode) {
|
||||
throw new RuntimeException("Linked list nodes are only supported with versions "
|
||||
+ FIRST_VERSION_WITH_LINKEDLIST_NODE + " and ulterior.");
|
||||
}
|
||||
if (!hasParentAddress && hasLinkedListNode) {
|
||||
throw new RuntimeException("Linked list nodes need parent addresses.");
|
||||
}
|
||||
mHasLinkedListNode = hasLinkedListNode;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -556,6 +556,7 @@ public class FusionDictionary implements Iterable<Word> {
|
|||
final StringBuilder checker = DBG ? new StringBuilder() : null;
|
||||
|
||||
CharGroup currentGroup;
|
||||
final int codePointCountInS = s.codePointCount(0, s.length());
|
||||
do {
|
||||
int indexOfGroup = findIndexOfChar(node, s.codePointAt(index));
|
||||
if (CHARACTER_NOT_FOUND == indexOfGroup) return null;
|
||||
|
@ -570,12 +571,12 @@ public class FusionDictionary implements Iterable<Word> {
|
|||
index = newIndex;
|
||||
|
||||
if (DBG) checker.append(new String(currentGroup.mChars, 0, currentGroup.mChars.length));
|
||||
if (index < s.length()) {
|
||||
if (index < codePointCountInS) {
|
||||
node = currentGroup.mChildren;
|
||||
}
|
||||
} while (null != node && index < s.length());
|
||||
} while (null != node && index < codePointCountInS);
|
||||
|
||||
if (index < s.length()) return null;
|
||||
if (index < codePointCountInS) return null;
|
||||
if (!currentGroup.isTerminal()) return null;
|
||||
if (DBG && !s.equals(checker.toString())) return null;
|
||||
return currentGroup;
|
||||
|
|
|
@ -16,6 +16,9 @@
|
|||
|
||||
package com.android.inputmethod.latin.spellcheck;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
import android.database.ContentObserver;
|
||||
import android.provider.UserDictionary.Words;
|
||||
import android.service.textservice.SpellCheckerService.Session;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
@ -45,6 +48,7 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session {
|
|||
private int mScript; // One of SCRIPT_LATIN or SCRIPT_CYRILLIC for now.
|
||||
private final AndroidSpellCheckerService mService;
|
||||
protected final SuggestionsCache mSuggestionsCache = new SuggestionsCache();
|
||||
private final ContentObserver mObserver;
|
||||
|
||||
private static class SuggestionsParams {
|
||||
public final String[] mSuggestions;
|
||||
|
@ -83,10 +87,23 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session {
|
|||
mUnigramSuggestionsInfoCache.put(
|
||||
generateKey(query, prevWord), new SuggestionsParams(suggestions, flags));
|
||||
}
|
||||
|
||||
public void clearCache() {
|
||||
mUnigramSuggestionsInfoCache.evictAll();
|
||||
}
|
||||
}
|
||||
|
||||
AndroidWordLevelSpellCheckerSession(final AndroidSpellCheckerService service) {
|
||||
mService = service;
|
||||
final ContentResolver cres = service.getContentResolver();
|
||||
|
||||
mObserver = new ContentObserver(null) {
|
||||
@Override
|
||||
public void onChange(boolean self) {
|
||||
mSuggestionsCache.clearCache();
|
||||
}
|
||||
};
|
||||
cres.registerContentObserver(Words.CONTENT_URI, true, mObserver);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -97,6 +114,12 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session {
|
|||
mScript = AndroidSpellCheckerService.getScriptFromLocale(mLocale);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose() {
|
||||
final ContentResolver cres = mService.getContentResolver();
|
||||
cres.unregisterContentObserver(mObserver);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns whether the code point is a letter that makes sense for the specified
|
||||
* locale for this spell checker.
|
||||
|
|
|
@ -64,17 +64,8 @@ static inline float getAngleDiff(float a1, float a2) {
|
|||
return diff;
|
||||
}
|
||||
|
||||
// static float pointToLineSegSquaredDistanceFloat(
|
||||
// float x, float y, float x1, float y1, float x2, float y2) {
|
||||
// float A = x - x1;
|
||||
// float B = y - y1;
|
||||
// float C = x2 - x1;
|
||||
// float D = y2 - y1;
|
||||
// return fabsf(A * D - C * B) / sqrtf(C * C + D * D);
|
||||
// }
|
||||
|
||||
static inline float pointToLineSegSquaredDistanceFloat(
|
||||
float x, float y, float x1, float y1, float x2, float y2) {
|
||||
float x, float y, float x1, float y1, float x2, float y2, bool extend) {
|
||||
const float ray1x = x - x1;
|
||||
const float ray1y = y - y1;
|
||||
const float ray2x = x2 - x1;
|
||||
|
@ -86,10 +77,10 @@ static inline float pointToLineSegSquaredDistanceFloat(
|
|||
|
||||
float projectionX;
|
||||
float projectionY;
|
||||
if (projectionLengthSqr < 0.0f) {
|
||||
if (!extend && projectionLengthSqr < 0.0f) {
|
||||
projectionX = x1;
|
||||
projectionY = y1;
|
||||
} else if (projectionLengthSqr > 1.0f) {
|
||||
} else if (!extend && projectionLengthSqr > 1.0f) {
|
||||
projectionX = x2;
|
||||
projectionY = y2;
|
||||
} else {
|
||||
|
|
|
@ -409,11 +409,11 @@ bool ProximityInfoState::pushTouchPoint(const int inputIndex, const int nodeChar
|
|||
}
|
||||
NearKeysDistanceMap::const_iterator itPP =
|
||||
prevNearKeysDistances->find(minChar);
|
||||
if (DEBUG_GEO_FULL) {
|
||||
AKLOGI("p1: char = %c, minDist = %f, prevNear key minDist = %f",
|
||||
minChar, itPP->second, minDist);
|
||||
}
|
||||
if (itPP != prevNearKeysDistances->end() && minDist > itPP->second) {
|
||||
if (DEBUG_GEO_FULL) {
|
||||
AKLOGI("p1: char = %c, minDist = %f, prevNear key minDist = %f",
|
||||
minChar, itPP->second, minDist);
|
||||
}
|
||||
return popped;
|
||||
}
|
||||
}
|
||||
|
@ -464,8 +464,8 @@ float ProximityInfoState::calculateNormalizedSquaredDistance(
|
|||
}
|
||||
|
||||
int ProximityInfoState::getDuration(const int index) const {
|
||||
if (mInputSize > 0 && index > 0 && index < mInputSize - 1) {
|
||||
return mTimes[index + 1] - mTimes[index - 1];
|
||||
if (mInputSize > 0 && index >= 0 && index < mInputSize - 1) {
|
||||
return mTimes[index + 1] - mTimes[index];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -504,7 +504,7 @@ int32_t ProximityInfoState::getAllPossibleChars(
|
|||
if (index >= mInputXs.size()) {
|
||||
return filterSize;
|
||||
}
|
||||
int i = filterSize;
|
||||
int newFilterSize = filterSize;
|
||||
for (int j = 0; j < mProximityInfo->getKeyCount(); ++j) {
|
||||
if (mNearKeysVector[index].test(j)) {
|
||||
const int32_t keyCodePoint = mProximityInfo->getCodePointOf(j);
|
||||
|
@ -517,11 +517,11 @@ int32_t ProximityInfoState::getAllPossibleChars(
|
|||
}
|
||||
}
|
||||
if (insert) {
|
||||
filter[i++] = keyCodePoint;
|
||||
filter[newFilterSize++] = keyCodePoint;
|
||||
}
|
||||
}
|
||||
}
|
||||
return i;
|
||||
return newFilterSize;
|
||||
}
|
||||
|
||||
float ProximityInfoState::getAveragePointDuration() const {
|
||||
|
|
|
@ -93,22 +93,24 @@ public class StringUtilsTests extends AndroidTestCase {
|
|||
}
|
||||
|
||||
private void onePathForCaps(final CharSequence cs, final int expectedResult, final int mask,
|
||||
final Locale l) {
|
||||
final Locale l, final boolean hasSpaceBefore) {
|
||||
int oneTimeResult = expectedResult & mask;
|
||||
assertEquals("After >" + cs + "<", oneTimeResult, StringUtils.getCapsMode(cs, mask, l));
|
||||
assertEquals("After >" + cs + "<", oneTimeResult,
|
||||
StringUtils.getCapsMode(cs, mask, l, hasSpaceBefore));
|
||||
}
|
||||
|
||||
private void allPathsForCaps(final CharSequence cs, final int expectedResult, final Locale l) {
|
||||
private void allPathsForCaps(final CharSequence cs, final int expectedResult, final Locale l,
|
||||
final boolean hasSpaceBefore) {
|
||||
final int c = TextUtils.CAP_MODE_CHARACTERS;
|
||||
final int w = TextUtils.CAP_MODE_WORDS;
|
||||
final int s = TextUtils.CAP_MODE_SENTENCES;
|
||||
onePathForCaps(cs, expectedResult, c | w | s, l);
|
||||
onePathForCaps(cs, expectedResult, w | s, l);
|
||||
onePathForCaps(cs, expectedResult, c | s, l);
|
||||
onePathForCaps(cs, expectedResult, c | w, l);
|
||||
onePathForCaps(cs, expectedResult, c, l);
|
||||
onePathForCaps(cs, expectedResult, w, l);
|
||||
onePathForCaps(cs, expectedResult, s, l);
|
||||
onePathForCaps(cs, expectedResult, c | w | s, l, hasSpaceBefore);
|
||||
onePathForCaps(cs, expectedResult, w | s, l, hasSpaceBefore);
|
||||
onePathForCaps(cs, expectedResult, c | s, l, hasSpaceBefore);
|
||||
onePathForCaps(cs, expectedResult, c | w, l, hasSpaceBefore);
|
||||
onePathForCaps(cs, expectedResult, c, l, hasSpaceBefore);
|
||||
onePathForCaps(cs, expectedResult, w, l, hasSpaceBefore);
|
||||
onePathForCaps(cs, expectedResult, s, l, hasSpaceBefore);
|
||||
}
|
||||
|
||||
public void testGetCapsMode() {
|
||||
|
@ -116,26 +118,31 @@ public class StringUtilsTests extends AndroidTestCase {
|
|||
final int w = TextUtils.CAP_MODE_WORDS;
|
||||
final int s = TextUtils.CAP_MODE_SENTENCES;
|
||||
Locale l = Locale.ENGLISH;
|
||||
allPathsForCaps("", c | w | s, l);
|
||||
allPathsForCaps("Word", c, l);
|
||||
allPathsForCaps("Word.", c, l);
|
||||
allPathsForCaps("Word ", c | w, l);
|
||||
allPathsForCaps("Word. ", c | w | s, l);
|
||||
allPathsForCaps("Word..", c, l);
|
||||
allPathsForCaps("Word.. ", c | w | s, l);
|
||||
allPathsForCaps("Word... ", c | w | s, l);
|
||||
allPathsForCaps("Word ... ", c | w | s, l);
|
||||
allPathsForCaps("Word . ", c | w, l);
|
||||
allPathsForCaps("In the U.S ", c | w, l);
|
||||
allPathsForCaps("In the U.S. ", c | w, l);
|
||||
allPathsForCaps("Some stuff (e.g. ", c | w, l);
|
||||
allPathsForCaps("In the U.S.. ", c | w | s, l);
|
||||
allPathsForCaps("\"Word.\" ", c | w | s, l);
|
||||
allPathsForCaps("\"Word\". ", c | w | s, l);
|
||||
allPathsForCaps("\"Word\" ", c | w, l);
|
||||
allPathsForCaps("", c | w | s, l, false);
|
||||
allPathsForCaps("Word", c, l, false);
|
||||
allPathsForCaps("Word.", c, l, false);
|
||||
allPathsForCaps("Word ", c | w, l, false);
|
||||
allPathsForCaps("Word. ", c | w | s, l, false);
|
||||
allPathsForCaps("Word..", c, l, false);
|
||||
allPathsForCaps("Word.. ", c | w | s, l, false);
|
||||
allPathsForCaps("Word... ", c | w | s, l, false);
|
||||
allPathsForCaps("Word ... ", c | w | s, l, false);
|
||||
allPathsForCaps("Word . ", c | w, l, false);
|
||||
allPathsForCaps("In the U.S ", c | w, l, false);
|
||||
allPathsForCaps("In the U.S. ", c | w, l, false);
|
||||
allPathsForCaps("Some stuff (e.g. ", c | w, l, false);
|
||||
allPathsForCaps("In the U.S.. ", c | w | s, l, false);
|
||||
allPathsForCaps("\"Word.\" ", c | w | s, l, false);
|
||||
allPathsForCaps("\"Word\". ", c | w | s, l, false);
|
||||
allPathsForCaps("\"Word\" ", c | w, l, false);
|
||||
|
||||
// Test for phantom space
|
||||
allPathsForCaps("Word", c | w, l, true);
|
||||
allPathsForCaps("Word.", c | w | s, l, true);
|
||||
|
||||
l = Locale.FRENCH;
|
||||
allPathsForCaps("\"Word.\" ", c | w, l);
|
||||
allPathsForCaps("\"Word\". ", c | w | s, l);
|
||||
allPathsForCaps("\"Word\" ", c | w, l);
|
||||
allPathsForCaps("\"Word.\" ", c | w, l, false);
|
||||
allPathsForCaps("\"Word\". ", c | w | s, l, false);
|
||||
allPathsForCaps("\"Word\" ", c | w, l, false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ package com.android.inputmethod.latin.makedict;
|
|||
import com.android.inputmethod.latin.CollectionUtils;
|
||||
import com.android.inputmethod.latin.UserHistoryDictIOUtils;
|
||||
import com.android.inputmethod.latin.makedict.BinaryDictInputOutput.FusionDictionaryBufferInterface;
|
||||
import com.android.inputmethod.latin.makedict.FormatSpec;
|
||||
import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
|
||||
import com.android.inputmethod.latin.makedict.FusionDictionary.CharGroup;
|
||||
import com.android.inputmethod.latin.makedict.FusionDictionary.Node;
|
||||
import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
|
||||
|
@ -65,9 +65,12 @@ public class BinaryDictIOTests extends AndroidTestCase {
|
|||
|
||||
private static final FormatSpec.FormatOptions VERSION2 = new FormatSpec.FormatOptions(2);
|
||||
private static final FormatSpec.FormatOptions VERSION3_WITHOUT_PARENTADDRESS =
|
||||
new FormatSpec.FormatOptions(3, false);
|
||||
new FormatSpec.FormatOptions(3, false /* hasParentAddress */);
|
||||
private static final FormatSpec.FormatOptions VERSION3_WITH_PARENTADDRESS =
|
||||
new FormatSpec.FormatOptions(3, true);
|
||||
new FormatSpec.FormatOptions(3, true /* hasParentAddress */);
|
||||
private static final FormatSpec.FormatOptions VERSION3_WITH_LINKEDLIST_NODE =
|
||||
new FormatSpec.FormatOptions(3, true /* hasParentAddress */,
|
||||
true /* hasLinkedListNode */);
|
||||
|
||||
private static final String[] CHARACTERS = {
|
||||
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
|
||||
|
@ -236,7 +239,8 @@ public class BinaryDictIOTests extends AndroidTestCase {
|
|||
String result = " : buffer type = "
|
||||
+ ((bufferType == USE_BYTE_BUFFER) ? "byte buffer" : "byte array");
|
||||
result += " : version = " + formatOptions.mVersion;
|
||||
return result + ", hasParentAddress = " + formatOptions.mHasParentAddress;
|
||||
return result + ", hasParentAddress = " + formatOptions.mHasParentAddress
|
||||
+ ", hasLinkedListNode = " + formatOptions.mHasLinkedListNode;
|
||||
}
|
||||
|
||||
// Tests for readDictionaryBinary and writeDictionaryBinary
|
||||
|
@ -305,6 +309,7 @@ public class BinaryDictIOTests extends AndroidTestCase {
|
|||
runReadAndWriteTests(results, USE_BYTE_BUFFER, VERSION2);
|
||||
runReadAndWriteTests(results, USE_BYTE_BUFFER, VERSION3_WITHOUT_PARENTADDRESS);
|
||||
runReadAndWriteTests(results, USE_BYTE_BUFFER, VERSION3_WITH_PARENTADDRESS);
|
||||
runReadAndWriteTests(results, USE_BYTE_BUFFER, VERSION3_WITH_LINKEDLIST_NODE);
|
||||
|
||||
for (final String result : results) {
|
||||
Log.d(TAG, result);
|
||||
|
@ -317,6 +322,7 @@ public class BinaryDictIOTests extends AndroidTestCase {
|
|||
runReadAndWriteTests(results, USE_BYTE_ARRAY, VERSION2);
|
||||
runReadAndWriteTests(results, USE_BYTE_ARRAY, VERSION3_WITHOUT_PARENTADDRESS);
|
||||
runReadAndWriteTests(results, USE_BYTE_ARRAY, VERSION3_WITH_PARENTADDRESS);
|
||||
runReadAndWriteTests(results, USE_BYTE_ARRAY, VERSION3_WITH_LINKEDLIST_NODE);
|
||||
|
||||
for (final String result : results) {
|
||||
Log.d(TAG, result);
|
||||
|
@ -385,7 +391,7 @@ public class BinaryDictIOTests extends AndroidTestCase {
|
|||
assertNotNull("Can't get buffer.", buffer);
|
||||
try {
|
||||
now = System.currentTimeMillis();
|
||||
BinaryDictInputOutput.readUnigramsAndBigramsBinary(buffer, resultWords, resultFreqs,
|
||||
BinaryDictIOUtils.readUnigramsAndBigramsBinary(buffer, resultWords, resultFreqs,
|
||||
resultBigrams);
|
||||
diff = System.currentTimeMillis() - now;
|
||||
} catch (IOException e) {
|
||||
|
@ -450,6 +456,7 @@ public class BinaryDictIOTests extends AndroidTestCase {
|
|||
runReadUnigramsAndBigramsTests(results, USE_BYTE_BUFFER, VERSION2);
|
||||
runReadUnigramsAndBigramsTests(results, USE_BYTE_BUFFER, VERSION3_WITHOUT_PARENTADDRESS);
|
||||
runReadUnigramsAndBigramsTests(results, USE_BYTE_BUFFER, VERSION3_WITH_PARENTADDRESS);
|
||||
runReadUnigramsAndBigramsTests(results, USE_BYTE_BUFFER, VERSION3_WITH_LINKEDLIST_NODE);
|
||||
|
||||
for (final String result : results) {
|
||||
Log.d(TAG, result);
|
||||
|
@ -462,9 +469,99 @@ public class BinaryDictIOTests extends AndroidTestCase {
|
|||
runReadUnigramsAndBigramsTests(results, USE_BYTE_ARRAY, VERSION2);
|
||||
runReadUnigramsAndBigramsTests(results, USE_BYTE_ARRAY, VERSION3_WITHOUT_PARENTADDRESS);
|
||||
runReadUnigramsAndBigramsTests(results, USE_BYTE_ARRAY, VERSION3_WITH_PARENTADDRESS);
|
||||
runReadUnigramsAndBigramsTests(results, USE_BYTE_ARRAY, VERSION3_WITH_LINKEDLIST_NODE);
|
||||
|
||||
for (final String result : results) {
|
||||
Log.d(TAG, result);
|
||||
}
|
||||
}
|
||||
|
||||
// Tests for getTerminalPosition
|
||||
private String getWordFromBinary(final FusionDictionaryBufferInterface buffer,
|
||||
final int address) {
|
||||
if (buffer.position() != 0) buffer.position(0);
|
||||
|
||||
FileHeader header = null;
|
||||
try {
|
||||
header = BinaryDictInputOutput.readHeader(buffer);
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
} catch (UnsupportedFormatException e) {
|
||||
return null;
|
||||
}
|
||||
if (header == null) return null;
|
||||
return BinaryDictInputOutput.getWordAtAddress(buffer, header.mHeaderSize,
|
||||
address - header.mHeaderSize, header.mFormatOptions);
|
||||
}
|
||||
|
||||
private long runGetTerminalPosition(final FusionDictionaryBufferInterface buffer,
|
||||
final String word, int index, boolean contained) {
|
||||
final int expectedFrequency = (UNIGRAM_FREQ + index) % 255;
|
||||
long diff = -1;
|
||||
int position = -1;
|
||||
try {
|
||||
final long now = System.nanoTime();
|
||||
position = BinaryDictIOUtils.getTerminalPosition(buffer, word);
|
||||
diff = System.nanoTime() - now;
|
||||
} catch (IOException e) {
|
||||
Log.e(TAG, "IOException while getTerminalPosition: " + e);
|
||||
} catch (UnsupportedFormatException e) {
|
||||
Log.e(TAG, "UnsupportedFormatException while getTermianlPosition: " + e);
|
||||
}
|
||||
|
||||
assertEquals(FormatSpec.NOT_VALID_WORD != position, contained);
|
||||
if (contained) assertEquals(getWordFromBinary(buffer, position), word);
|
||||
return diff;
|
||||
}
|
||||
|
||||
public void testGetTerminalPosition() {
|
||||
File file = null;
|
||||
try {
|
||||
file = File.createTempFile("runReadUnigrams", ".dict");
|
||||
} catch (IOException e) {
|
||||
// do nothing
|
||||
}
|
||||
assertNotNull(file);
|
||||
|
||||
final FusionDictionary dict = new FusionDictionary(new Node(),
|
||||
new FusionDictionary.DictionaryOptions(
|
||||
new HashMap<String, String>(), false, false));
|
||||
addUnigrams(sWords.size(), dict, sWords, null /* shortcutMap */);
|
||||
timeWritingDictToFile(file, dict, VERSION3_WITH_LINKEDLIST_NODE);
|
||||
|
||||
final FusionDictionaryBufferInterface buffer = getBuffer(file, USE_BYTE_ARRAY);
|
||||
|
||||
try {
|
||||
// too long word
|
||||
final String longWord = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz";
|
||||
assertEquals(FormatSpec.NOT_VALID_WORD,
|
||||
BinaryDictIOUtils.getTerminalPosition(buffer, longWord));
|
||||
|
||||
// null
|
||||
assertEquals(FormatSpec.NOT_VALID_WORD,
|
||||
BinaryDictIOUtils.getTerminalPosition(buffer, null));
|
||||
|
||||
// empty string
|
||||
assertEquals(FormatSpec.NOT_VALID_WORD,
|
||||
BinaryDictIOUtils.getTerminalPosition(buffer, ""));
|
||||
} catch (IOException e) {
|
||||
} catch (UnsupportedFormatException e) {
|
||||
}
|
||||
|
||||
// Test a word that is contained within the dictionary.
|
||||
long sum = 0;
|
||||
for (int i = 0; i < sWords.size(); ++i) {
|
||||
final long time = runGetTerminalPosition(buffer, sWords.get(i), i, true);
|
||||
sum += time == -1 ? 0 : time;
|
||||
}
|
||||
Log.d(TAG, "per a search : " + (((double)sum) / sWords.size() / 1000000));
|
||||
|
||||
// Test a word that isn't contained within the dictionary.
|
||||
final Random random = new Random((int)System.currentTimeMillis());
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
final String word = generateWord(random.nextInt());
|
||||
if (sWords.indexOf(word) != -1) continue;
|
||||
runGetTerminalPosition(buffer, word, i, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue