aboutsummaryrefslogtreecommitdiffstats
path: root/java
diff options
context:
space:
mode:
Diffstat (limited to 'java')
-rw-r--r--java/Android.mk4
-rw-r--r--java/AndroidManifest.xml23
-rw-r--r--java/NOTICE2
-rw-r--r--java/proguard.flags25
-rw-r--r--java/res/drawable-hdpi/ic_ime_settings.pngbin1570 -> 0 bytes
-rw-r--r--java/res/drawable-mdpi/ic_ime_settings.pngbin1292 -> 0 bytes
-rw-r--r--java/res/drawable-xhdpi/ic_ime_settings.pngbin2065 -> 0 bytes
-rw-r--r--java/res/drawable/btn_keyboard_key.xml6
-rw-r--r--java/res/drawable/keyboard_key_feedback.xml10
-rw-r--r--java/res/drawable/keyboard_key_feedback_ics.xml21
-rw-r--r--java/res/drawable/keyboard_key_feedback_left_ics.xml21
-rw-r--r--java/res/drawable/keyboard_key_feedback_right_ics.xml21
-rw-r--r--java/res/layout/input_view.xml8
-rw-r--r--java/res/layout/key_preview.xml6
-rw-r--r--java/res/layout/key_preview_ics.xml (renamed from java/res/xml-sw768dp-land/kbd_thai.xml)16
-rw-r--r--java/res/layout/research_feedback_activity.xml31
-rw-r--r--java/res/layout/research_feedback_fragment_layout.xml112
-rw-r--r--java/res/layout/research_feedback_layout.xml50
-rw-r--r--java/res/mipmap-hdpi/ic_ime_settings.pngbin0 -> 2294 bytes
-rw-r--r--java/res/mipmap-mdpi/ic_ime_settings.pngbin0 -> 1370 bytes
-rw-r--r--java/res/mipmap-xhdpi/ic_ime_settings.pngbin0 -> 2775 bytes
-rw-r--r--java/res/mipmap-xxhdpi/ic_ime_settings.pngbin0 -> 3159 bytes
-rw-r--r--java/res/raw/main_en.dictbin1068816 -> 1069783 bytes
-rw-r--r--java/res/raw/main_es.dictbin1138855 -> 1138855 bytes
-rw-r--r--java/res/raw/main_fr.dictbin1328007 -> 1329511 bytes
-rw-r--r--java/res/raw/main_it.dictbin1143310 -> 1143310 bytes
-rw-r--r--java/res/raw/main_pt_br.dictbin1091964 -> 1091965 bytes
-rw-r--r--java/res/raw/main_ru.dictbin0 -> 2240073 bytes
-rw-r--r--java/res/values-af/bools.xml (renamed from java/res/xml-sw600dp/kbd_thai.xml)15
-rw-r--r--java/res/values-af/strings-appname.xml27
-rw-r--r--java/res/values-af/strings.xml30
-rw-r--r--java/res/values-am/strings-appname.xml27
-rw-r--r--java/res/values-am/strings.xml30
-rw-r--r--java/res/values-ar/bools.xml24
-rw-r--r--java/res/values-ar/strings-appname.xml27
-rw-r--r--java/res/values-ar/strings.xml30
-rw-r--r--java/res/values-be/bools.xml24
-rw-r--r--java/res/values-be/strings-appname.xml27
-rw-r--r--java/res/values-be/strings.xml30
-rw-r--r--java/res/values-bg/bools.xml24
-rw-r--r--java/res/values-bg/strings-appname.xml27
-rw-r--r--java/res/values-bg/strings.xml30
-rw-r--r--java/res/values-ca/bools.xml24
-rw-r--r--java/res/values-ca/strings-appname.xml27
-rw-r--r--java/res/values-ca/strings.xml60
-rw-r--r--java/res/values-cs/bools.xml22
-rw-r--r--java/res/values-cs/strings-appname.xml27
-rw-r--r--java/res/values-cs/strings.xml30
-rw-r--r--java/res/values-da/bools.xml24
-rw-r--r--java/res/values-da/strings-appname.xml27
-rw-r--r--java/res/values-da/strings.xml30
-rw-r--r--java/res/values-de/bools.xml22
-rw-r--r--java/res/values-de/strings-appname.xml27
-rw-r--r--java/res/values-de/strings.xml40
-rw-r--r--java/res/values-el/bools.xml24
-rw-r--r--java/res/values-el/strings-appname.xml27
-rw-r--r--java/res/values-el/strings.xml30
-rw-r--r--java/res/values-en-rGB/strings-appname.xml27
-rw-r--r--java/res/values-en-rGB/strings.xml30
-rw-r--r--java/res/values-en/bools.xml22
-rw-r--r--java/res/values-en/whitelist.xml411
-rw-r--r--java/res/values-eo/bools.xml24
-rw-r--r--java/res/values-es-rUS/strings-appname.xml27
-rw-r--r--java/res/values-es-rUS/strings.xml32
-rw-r--r--java/res/values-es/bools.xml22
-rw-r--r--java/res/values-es/strings-appname.xml27
-rw-r--r--java/res/values-es/strings.xml32
-rw-r--r--java/res/values-et/bools.xml24
-rw-r--r--java/res/values-et/strings-appname.xml27
-rw-r--r--java/res/values-et/strings.xml30
-rw-r--r--java/res/values-fa/bools.xml24
-rw-r--r--java/res/values-fa/strings-appname.xml27
-rw-r--r--java/res/values-fa/strings.xml74
-rw-r--r--java/res/values-fi/bools.xml24
-rw-r--r--java/res/values-fi/strings-appname.xml27
-rw-r--r--java/res/values-fi/strings.xml30
-rw-r--r--java/res/values-fr/bools.xml22
-rw-r--r--java/res/values-fr/donottranslate.xml4
-rw-r--r--java/res/values-fr/strings-appname.xml27
-rw-r--r--java/res/values-fr/strings.xml30
-rw-r--r--java/res/values-hi/bools.xml24
-rw-r--r--java/res/values-hi/strings-appname.xml27
-rw-r--r--java/res/values-hi/strings.xml36
-rw-r--r--java/res/values-hr/bools.xml24
-rw-r--r--java/res/values-hr/strings-appname.xml27
-rw-r--r--java/res/values-hr/strings.xml30
-rw-r--r--java/res/values-hu/bools.xml24
-rw-r--r--java/res/values-hu/strings-appname.xml27
-rw-r--r--java/res/values-hu/strings.xml30
-rw-r--r--java/res/values-in/bools.xml24
-rw-r--r--java/res/values-in/strings-appname.xml27
-rw-r--r--java/res/values-in/strings.xml32
-rw-r--r--java/res/values-is/bools.xml24
-rw-r--r--java/res/values-is/strings.xml269
-rw-r--r--java/res/values-it/bools.xml22
-rw-r--r--java/res/values-it/strings-appname.xml27
-rw-r--r--java/res/values-it/strings.xml30
-rw-r--r--java/res/values-iw/bools.xml24
-rw-r--r--java/res/values-iw/strings-appname.xml27
-rw-r--r--java/res/values-iw/strings.xml30
-rw-r--r--java/res/values-ja/strings-appname.xml27
-rw-r--r--java/res/values-ja/strings.xml30
-rw-r--r--java/res/values-ka/bools.xml24
-rw-r--r--java/res/values-ka/strings.xml269
-rw-r--r--java/res/values-ko/strings-appname.xml27
-rw-r--r--java/res/values-ko/strings.xml30
-rw-r--r--java/res/values-ky/bools.xml24
-rw-r--r--java/res/values-land/dimens.xml29
-rw-r--r--java/res/values-lt/bools.xml24
-rw-r--r--java/res/values-lt/strings-appname.xml27
-rw-r--r--java/res/values-lt/strings.xml30
-rw-r--r--java/res/values-lv/bools.xml24
-rw-r--r--java/res/values-lv/strings-appname.xml27
-rw-r--r--java/res/values-lv/strings.xml30
-rw-r--r--java/res/values-mk/bools.xml24
-rw-r--r--java/res/values-mk/strings.xml269
-rw-r--r--java/res/values-ms/bools.xml24
-rw-r--r--java/res/values-ms/strings-appname.xml27
-rw-r--r--java/res/values-ms/strings.xml30
-rw-r--r--java/res/values-nb/bools.xml24
-rw-r--r--java/res/values-nb/strings-appname.xml27
-rw-r--r--java/res/values-nb/strings.xml30
-rw-r--r--java/res/values-nl/bools.xml22
-rw-r--r--java/res/values-nl/strings-appname.xml27
-rw-r--r--java/res/values-nl/strings.xml30
-rw-r--r--java/res/values-pl/bools.xml22
-rw-r--r--java/res/values-pl/strings-appname.xml30
-rw-r--r--java/res/values-pl/strings.xml34
-rw-r--r--java/res/values-pt-rPT/strings-appname.xml27
-rw-r--r--java/res/values-pt-rPT/strings.xml30
-rw-r--r--java/res/values-pt/bools.xml24
-rw-r--r--java/res/values-pt/strings-appname.xml27
-rw-r--r--java/res/values-pt/strings.xml30
-rw-r--r--java/res/values-rm/strings.xml42
-rw-r--r--java/res/values-ro/bools.xml24
-rw-r--r--java/res/values-ro/strings-appname.xml27
-rw-r--r--java/res/values-ro/strings.xml30
-rw-r--r--java/res/values-ru/bools.xml24
-rw-r--r--java/res/values-ru/strings-appname.xml27
-rw-r--r--java/res/values-ru/strings.xml30
-rw-r--r--java/res/values-sk/bools.xml24
-rw-r--r--java/res/values-sk/strings-appname.xml27
-rw-r--r--java/res/values-sk/strings.xml30
-rw-r--r--java/res/values-sl/bools.xml24
-rw-r--r--java/res/values-sl/strings-appname.xml27
-rw-r--r--java/res/values-sl/strings.xml30
-rw-r--r--java/res/values-sr/bools.xml24
-rw-r--r--java/res/values-sr/strings-appname.xml27
-rw-r--r--java/res/values-sr/strings.xml30
-rw-r--r--java/res/values-sv/bools.xml24
-rw-r--r--java/res/values-sv/strings-appname.xml27
-rw-r--r--java/res/values-sv/strings.xml30
-rw-r--r--java/res/values-sw/bools.xml24
-rw-r--r--java/res/values-sw/strings-appname.xml27
-rw-r--r--java/res/values-sw/strings.xml32
-rw-r--r--java/res/values-sw600dp-land/dimens.xml11
-rw-r--r--java/res/values-sw600dp/config.xml5
-rw-r--r--java/res/values-sw600dp/dimens.xml14
-rw-r--r--java/res/values-sw600dp/touch-position-correction.xml60
-rw-r--r--java/res/values-sw768dp-land/dimens.xml11
-rw-r--r--java/res/values-sw768dp/config.xml4
-rw-r--r--java/res/values-sw768dp/dimens.xml14
-rw-r--r--java/res/values-th/bools.xml24
-rw-r--r--java/res/values-th/strings-appname.xml27
-rw-r--r--java/res/values-th/strings.xml30
-rw-r--r--java/res/values-tl/bools.xml24
-rw-r--r--java/res/values-tl/strings-appname.xml27
-rw-r--r--java/res/values-tl/strings.xml30
-rw-r--r--java/res/values-tr/bools.xml24
-rw-r--r--java/res/values-tr/strings-appname.xml27
-rw-r--r--java/res/values-tr/strings.xml30
-rw-r--r--java/res/values-uk/bools.xml24
-rw-r--r--java/res/values-uk/strings-appname.xml27
-rw-r--r--java/res/values-uk/strings.xml30
-rw-r--r--java/res/values-vi/bools.xml24
-rw-r--r--java/res/values-vi/strings-appname.xml27
-rw-r--r--java/res/values-vi/strings.xml30
-rw-r--r--java/res/values-zh-rCN/strings-appname.xml27
-rw-r--r--java/res/values-zh-rCN/strings.xml30
-rw-r--r--java/res/values-zh-rTW/strings-appname.xml27
-rw-r--r--java/res/values-zh-rTW/strings.xml30
-rw-r--r--java/res/values-zu/bools.xml24
-rw-r--r--java/res/values-zu/strings-appname.xml27
-rw-r--r--java/res/values-zu/strings.xml30
-rw-r--r--java/res/values/attrs.xml168
-rw-r--r--java/res/values/bools.xml20
-rw-r--r--java/res/values/config.xml35
-rw-r--r--java/res/values/dimens.xml38
-rw-r--r--java/res/values/donottranslate.xml4
-rw-r--r--java/res/values/gesture-input.xml (renamed from java/res/xml/kbd_esperanto.xml)10
-rw-r--r--java/res/values/keypress-vibration-durations.xml2
-rw-r--r--java/res/values/keypress-volumes.xml2
-rw-r--r--java/res/values/phantom-sudden-move-event-device-list.xml (renamed from java/res/values/phantom_sudden_move_event_device_list.xml)0
-rw-r--r--java/res/values/predefined-subtypes.xml7
-rw-r--r--java/res/values/research_strings.xml (renamed from java/res/values/whitelist.xml)13
-rw-r--r--java/res/values/strings-appname.xml (renamed from java/res/xml-sw768dp-land/kbd_thai_symbols.xml)22
-rw-r--r--java/res/values/strings.xml125
-rw-r--r--java/res/values/styles.xml95
-rw-r--r--java/res/values/themes-basic-highcontrast.xml4
-rw-r--r--java/res/values/themes-basic.xml4
-rw-r--r--java/res/values/themes-gingerbread.xml4
-rw-r--r--java/res/values/themes-ics.xml4
-rw-r--r--java/res/values/themes-stone-bold.xml4
-rw-r--r--java/res/values/themes-stone.xml4
-rw-r--r--java/res/values/touch-position-correction.xml18
-rw-r--r--java/res/values/urls.xml (renamed from java/res/values-it/donottranslate.xml)7
-rw-r--r--java/res/xml-land/kbd_number.xml1
-rw-r--r--java/res/xml-land/kbd_phone.xml1
-rw-r--r--java/res/xml-land/kbd_phone_symbols.xml1
-rw-r--r--java/res/xml-sw600dp-land/kbd_number.xml1
-rw-r--r--java/res/xml-sw600dp-land/kbd_phone.xml1
-rw-r--r--java/res/xml-sw600dp-land/kbd_phone_symbols.xml1
-rw-r--r--java/res/xml-sw600dp/kbd_10_10_7_symbols.xml1
-rw-r--r--java/res/xml-sw600dp/kbd_10_10_7_symbols_shift.xml1
-rw-r--r--java/res/xml-sw600dp/kbd_number.xml1
-rw-r--r--java/res/xml-sw600dp/kbd_phone.xml1
-rw-r--r--java/res/xml-sw600dp/kbd_phone_symbols.xml1
-rw-r--r--java/res/xml-sw600dp/key_styles_common.xml11
-rw-r--r--java/res/xml-sw600dp/row_symbols4.xml4
-rw-r--r--java/res/xml-sw600dp/row_symbols_shift4.xml4
-rw-r--r--java/res/xml-sw600dp/rowkeys_arabic1.xml33
-rw-r--r--java/res/xml-sw600dp/rowkeys_arabic2.xml33
-rw-r--r--java/res/xml-sw600dp/rowkeys_arabic3.xml33
-rw-r--r--java/res/xml-sw600dp/rowkeys_farsi1.xml36
-rw-r--r--java/res/xml-sw600dp/rowkeys_farsi2.xml33
-rw-r--r--java/res/xml-sw600dp/rowkeys_farsi3.xml30
-rw-r--r--java/res/xml-sw600dp/rowkeys_symbols3.xml2
-rw-r--r--java/res/xml-sw600dp/rowkeys_thai1.xml97
-rw-r--r--java/res/xml-sw600dp/rowkeys_thai2.xml108
-rw-r--r--java/res/xml-sw600dp/rowkeys_thai3.xml97
-rw-r--r--java/res/xml-sw600dp/rowkeys_thai4.xml89
-rw-r--r--java/res/xml-sw600dp/rows_esperanto.xml61
-rw-r--r--java/res/xml-sw600dp/rows_number_normal.xml3
-rw-r--r--java/res/xml-sw600dp/rows_thai.xml9
-rw-r--r--java/res/xml-sw768dp-land/kbd_number.xml1
-rw-r--r--java/res/xml-sw768dp-land/kbd_phone.xml1
-rw-r--r--java/res/xml-sw768dp-land/kbd_phone_symbols.xml1
-rw-r--r--java/res/xml-sw768dp-land/kbd_thai_symbols_shift.xml29
-rw-r--r--java/res/xml-sw768dp/kbd_number.xml1
-rw-r--r--java/res/xml-sw768dp/kbd_phone.xml1
-rw-r--r--java/res/xml-sw768dp/kbd_phone_symbols.xml1
-rw-r--r--java/res/xml-sw768dp/kbd_thai.xml29
-rw-r--r--java/res/xml-sw768dp/kbd_thai_symbols.xml6
-rw-r--r--java/res/xml-sw768dp/kbd_thai_symbols_shift.xml6
-rw-r--r--java/res/xml-sw768dp/key_space.xml25
-rw-r--r--java/res/xml-sw768dp/key_styles_common.xml19
-rw-r--r--java/res/xml-sw768dp/row_dvorak4.xml7
-rw-r--r--java/res/xml-sw768dp/row_hebrew4.xml7
-rw-r--r--java/res/xml-sw768dp/row_qwerty4.xml7
-rw-r--r--java/res/xml-sw768dp/row_symbols4.xml8
-rw-r--r--java/res/xml-sw768dp/row_symbols_shift4.xml9
-rw-r--r--java/res/xml-sw768dp/rowkeys_thai_digits.xml30
-rw-r--r--java/res/xml-sw768dp/rows_east_slavic.xml3
-rw-r--r--java/res/xml-sw768dp/rows_esperanto.xml69
-rw-r--r--java/res/xml-sw768dp/rows_number_normal.xml3
-rw-r--r--java/res/xml-sw768dp/rows_thai.xml6
-rw-r--r--java/res/xml/kbd_10_10_7_symbols.xml1
-rw-r--r--java/res/xml/kbd_10_10_7_symbols_shift.xml1
-rw-r--r--java/res/xml/kbd_more_keys_keyboard_template.xml18
-rw-r--r--java/res/xml/kbd_number.xml1
-rw-r--r--java/res/xml/kbd_pcqwerty.xml6
-rw-r--r--java/res/xml/kbd_pcqwerty_symbols.xml6
-rw-r--r--java/res/xml/kbd_phone.xml1
-rw-r--r--java/res/xml/kbd_phone_symbols.xml1
-rw-r--r--java/res/xml/kbd_symbols.xml1
-rw-r--r--java/res/xml/kbd_symbols_shift.xml1
-rw-r--r--java/res/xml/kbd_thai.xml5
-rw-r--r--java/res/xml/kbd_thai_symbols.xml1
-rw-r--r--java/res/xml/kbd_thai_symbols_shift.xml1
-rw-r--r--java/res/xml/key_styles_common.xml19
-rw-r--r--java/res/xml/key_styles_f1.xml43
-rw-r--r--java/res/xml/key_thai_kho_khuat.xml (renamed from java/res/xml-sw600dp-land/kbd_thai.xml)25
-rw-r--r--java/res/xml/keyboard_layout_set_esperanto.xml42
-rw-r--r--java/res/xml/method.xml127
-rw-r--r--java/res/xml/prefs.xml71
-rw-r--r--java/res/xml/prefs_for_debug.xml6
-rw-r--r--java/res/xml/rowkeys_arabic1.xml30
-rw-r--r--java/res/xml/rowkeys_arabic2.xml29
-rw-r--r--java/res/xml/rowkeys_arabic3.xml24
-rw-r--r--java/res/xml/rowkeys_east_slavic1.xml8
-rw-r--r--java/res/xml/rowkeys_east_slavic2.xml3
-rw-r--r--java/res/xml/rowkeys_esperanto1.xml125
-rw-r--r--java/res/xml/rowkeys_esperanto2.xml83
-rw-r--r--java/res/xml/rowkeys_esperanto3.xml58
-rw-r--r--java/res/xml/rowkeys_farsi1.xml30
-rw-r--r--java/res/xml/rowkeys_farsi2.xml30
-rw-r--r--java/res/xml/rowkeys_farsi3.xml24
-rw-r--r--java/res/xml/rowkeys_hindi1.xml66
-rw-r--r--java/res/xml/rowkeys_hindi2.xml66
-rw-r--r--java/res/xml/rowkeys_hindi3.xml51
-rw-r--r--java/res/xml/rowkeys_qwerty1.xml9
-rw-r--r--java/res/xml/rowkeys_qwerty3.xml3
-rw-r--r--java/res/xml/rowkeys_spanish2.xml2
-rw-r--r--java/res/xml/rowkeys_symbols3.xml2
-rw-r--r--java/res/xml/rowkeys_thai1.xml132
-rw-r--r--java/res/xml/rowkeys_thai2.xml171
-rw-r--r--java/res/xml/rowkeys_thai3.xml125
-rw-r--r--java/res/xml/rowkeys_thai4.xml122
-rw-r--r--java/res/xml/rows_esperanto.xml54
-rw-r--r--java/res/xml/rows_thai.xml27
-rw-r--r--java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java14
-rw-r--r--java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java35
-rw-r--r--java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java45
-rw-r--r--java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java22
-rw-r--r--java/src/com/android/inputmethod/compat/AudioManagerCompatWrapper.java2
-rw-r--r--java/src/com/android/inputmethod/compat/CompatUtils.java2
-rw-r--r--java/src/com/android/inputmethod/compat/EditorInfoCompatUtils.java2
-rw-r--r--java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java2
-rw-r--r--java/src/com/android/inputmethod/compat/InputMethodServiceCompatUtils.java34
-rw-r--r--java/src/com/android/inputmethod/compat/SettingsSecureCompatUtils.java2
-rw-r--r--java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java16
-rw-r--r--java/src/com/android/inputmethod/compat/SuggestionsInfoCompatUtils.java2
-rw-r--r--java/src/com/android/inputmethod/keyboard/Key.java311
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyDetector.java56
-rw-r--r--java/src/com/android/inputmethod/keyboard/Keyboard.java1184
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java51
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardId.java37
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java110
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java102
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardView.java853
-rw-r--r--java/src/com/android/inputmethod/keyboard/MainKeyboardView.java (renamed from java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java)396
-rw-r--r--java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java2
-rw-r--r--java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java434
-rw-r--r--java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java22
-rw-r--r--java/src/com/android/inputmethod/keyboard/PointerTracker.java787
-rw-r--r--java/src/com/android/inputmethod/keyboard/ProximityInfo.java85
-rw-r--r--java/src/com/android/inputmethod/keyboard/ViewLayoutUtils.java2
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/AlphabetShiftState.java2
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java297
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java341
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/GestureStrokeWithPreviewPoints.java94
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyDrawParams.java140
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyPreviewDrawParams.java44
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java80
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyStyle.java46
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyStylesSet.java (renamed from java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java)108
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyVisualAttributes.java130
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java813
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java11
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java13
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java137
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardRow.java154
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java21
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java1072
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeysCache.java40
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java86
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java174
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java348
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/ShiftKeyState.java2
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/SuddenJumpingTouchEventHandler.java (renamed from java/src/com/android/inputmethod/keyboard/SuddenJumpingTouchEventHandler.java)14
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/TouchPositionCorrection.java95
-rw-r--r--java/src/com/android/inputmethod/latin/AdditionalSubtype.java46
-rw-r--r--java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java105
-rw-r--r--java/src/com/android/inputmethod/latin/AssetFileAddress.java2
-rw-r--r--java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java2
-rw-r--r--java/src/com/android/inputmethod/latin/AutoCorrection.java98
-rw-r--r--java/src/com/android/inputmethod/latin/BackupAgent.java2
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionary.java213
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java13
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java91
-rw-r--r--java/src/com/android/inputmethod/latin/BoundedTreeSet.java49
-rw-r--r--java/src/com/android/inputmethod/latin/CollectionUtils.java95
-rw-r--r--java/src/com/android/inputmethod/latin/Constants.java26
-rw-r--r--java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java22
-rw-r--r--java/src/com/android/inputmethod/latin/ContactsDictionary.java178
-rw-r--r--java/src/com/android/inputmethod/latin/DebugSettings.java13
-rw-r--r--java/src/com/android/inputmethod/latin/DebugSettingsActivity.java2
-rw-r--r--java/src/com/android/inputmethod/latin/DicTraverseSession.java75
-rw-r--r--java/src/com/android/inputmethod/latin/Dictionary.java80
-rw-r--r--java/src/com/android/inputmethod/latin/DictionaryCollection.java53
-rw-r--r--java/src/com/android/inputmethod/latin/DictionaryFactory.java15
-rw-r--r--java/src/com/android/inputmethod/latin/DictionaryPackInstallBroadcastReceiver.java2
-rw-r--r--java/src/com/android/inputmethod/latin/EditingUtils.java182
-rw-r--r--java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java66
-rw-r--r--java/src/com/android/inputmethod/latin/ExpandableDictionary.java157
-rw-r--r--java/src/com/android/inputmethod/latin/FileTransforms.java2
-rw-r--r--java/src/com/android/inputmethod/latin/ImfUtils.java9
-rw-r--r--java/src/com/android/inputmethod/latin/InputAttributes.java13
-rw-r--r--java/src/com/android/inputmethod/latin/InputPointers.java133
-rw-r--r--java/src/com/android/inputmethod/latin/InputTypeUtils.java2
-rw-r--r--java/src/com/android/inputmethod/latin/InputView.java15
-rw-r--r--java/src/com/android/inputmethod/latin/JniUtils.java8
-rw-r--r--java/src/com/android/inputmethod/latin/LastComposedWord.java32
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java1905
-rw-r--r--java/src/com/android/inputmethod/latin/LatinImeLogger.java11
-rw-r--r--java/src/com/android/inputmethod/latin/LocaleUtils.java41
-rw-r--r--java/src/com/android/inputmethod/latin/ResearchLogger.java757
-rw-r--r--java/src/com/android/inputmethod/latin/ResizableIntArray.java146
-rw-r--r--java/src/com/android/inputmethod/latin/ResourceUtils.java128
-rw-r--r--java/src/com/android/inputmethod/latin/RichInputConnection.java709
-rw-r--r--java/src/com/android/inputmethod/latin/Settings.java188
-rw-r--r--java/src/com/android/inputmethod/latin/SettingsActivity.java2
-rw-r--r--java/src/com/android/inputmethod/latin/SettingsValues.java221
-rw-r--r--java/src/com/android/inputmethod/latin/StringUtils.java233
-rw-r--r--java/src/com/android/inputmethod/latin/SubtypeLocale.java12
-rw-r--r--java/src/com/android/inputmethod/latin/SubtypeSwitcher.java100
-rw-r--r--java/src/com/android/inputmethod/latin/Suggest.java589
-rw-r--r--java/src/com/android/inputmethod/latin/SuggestedWords.java77
-rw-r--r--java/src/com/android/inputmethod/latin/SuggestionSpanPickedNotificationReceiver.java2
-rw-r--r--java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java13
-rw-r--r--java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsDictionary.java54
-rw-r--r--java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java12
-rw-r--r--java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserDictionary.java56
-rw-r--r--java/src/com/android/inputmethod/latin/TargetApplicationGetter.java4
-rw-r--r--java/src/com/android/inputmethod/latin/UserBinaryDictionary.java47
-rw-r--r--java/src/com/android/inputmethod/latin/UserDictionary.java225
-rw-r--r--java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java214
-rw-r--r--java/src/com/android/inputmethod/latin/UserHistoryDictionary.java513
-rw-r--r--java/src/com/android/inputmethod/latin/UserHistoryDictionaryBigramList.java19
-rw-r--r--java/src/com/android/inputmethod/latin/UserHistoryForgettingCurveUtils.java22
-rw-r--r--java/src/com/android/inputmethod/latin/Utils.java165
-rw-r--r--java/src/com/android/inputmethod/latin/VibratorUtils.java2
-rw-r--r--java/src/com/android/inputmethod/latin/WhitelistDictionary.java109
-rw-r--r--java/src/com/android/inputmethod/latin/WordComposer.java179
-rw-r--r--java/src/com/android/inputmethod/latin/WordListInfo.java2
-rw-r--r--java/src/com/android/inputmethod/latin/XmlParseUtils.java10
-rw-r--r--java/src/com/android/inputmethod/latin/define/ProductionFlag.java1
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java282
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java1120
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/CharGroupInfo.java8
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/FormatSpec.java258
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java136
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/MakedictLog.java2
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/PendingAttribute.java2
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/UnsupportedFormatException.java2
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/Word.java25
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java502
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java154
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSessionFactory.java25
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java326
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/DictAndProximity.java2
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java61
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java16
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsActivity.java2
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsFragment.java2
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java276
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java8
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java (renamed from java/src/com/android/inputmethod/latin/suggestions/SuggestionsView.java)132
-rw-r--r--java/src/com/android/inputmethod/research/BootBroadcastReceiver.java33
-rw-r--r--java/src/com/android/inputmethod/research/FeedbackActivity.java54
-rw-r--r--java/src/com/android/inputmethod/research/FeedbackFragment.java73
-rw-r--r--java/src/com/android/inputmethod/research/FeedbackLayout.java62
-rw-r--r--java/src/com/android/inputmethod/research/LogBuffer.java113
-rw-r--r--java/src/com/android/inputmethod/research/LogUnit.java83
-rw-r--r--java/src/com/android/inputmethod/research/MainLogBuffer.java127
-rw-r--r--java/src/com/android/inputmethod/research/ResearchLog.java320
-rw-r--r--java/src/com/android/inputmethod/research/ResearchLogger.java1315
-rw-r--r--java/src/com/android/inputmethod/research/Statistics.java146
-rw-r--r--java/src/com/android/inputmethod/research/UploaderService.java191
449 files changed, 20831 insertions, 11360 deletions
diff --git a/java/Android.mk b/java/Android.mk
index 52cc18b26..364973bea 100644
--- a/java/Android.mk
+++ b/java/Android.mk
@@ -28,9 +28,7 @@ LOCAL_JNI_SHARED_LIBRARIES := libjni_latinime
# We want to install libjni_latinime.so to the system partition if LatinIME gets installed.
LOCAL_REQUIRED_MODULES := libjni_latinime
-LOCAL_STATIC_JAVA_LIBRARIES := android-common
-LOCAL_STATIC_JAVA_LIBRARIES += inputmethod-common
-LOCAL_STATIC_JAVA_LIBRARIES += android-support-v4
+LOCAL_STATIC_JAVA_LIBRARIES := android-common inputmethod-common android-support-v4
# Do not compress dictionary files to mmap dict data runtime
LOCAL_AAPT_FLAGS := -0 .dict
diff --git a/java/AndroidManifest.xml b/java/AndroidManifest.xml
index 06d852bb0..8ffa29d71 100644
--- a/java/AndroidManifest.xml
+++ b/java/AndroidManifest.xml
@@ -1,16 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
coreApp="true"
package="com.android.inputmethod.latin">
+ <uses-sdk android:minSdkVersion="16" android:targetSdkVersion="16" />
+
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.READ_USER_DICTIONARY" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<application android:label="@string/aosp_android_keyboard_ime_name"
- android:icon="@drawable/ic_ime_settings"
+ android:icon="@mipmap/ic_ime_settings"
android:backupAgent="BackupAgent"
- android:killAfterRestore="false">
+ android:killAfterRestore="false"
+ android:supportsRtl="true">
<service android:name="LatinIME"
android:label="@string/aosp_android_keyboard_ime_name"
diff --git a/java/NOTICE b/java/NOTICE
index 7340b9e35..aa325547e 100644
--- a/java/NOTICE
+++ b/java/NOTICE
@@ -188,3 +188,5 @@
END OF TERMS AND CONDITIONS
+
+Includes Dictionaries © Lexiteria LLC. Used by permission.
diff --git a/java/proguard.flags b/java/proguard.flags
index 34e23aa9a..ac5b7df16 100644
--- a/java/proguard.flags
+++ b/java/proguard.flags
@@ -3,10 +3,6 @@
<init>(...);
}
--keep class com.android.inputmethod.latin.Flag {
- *;
-}
-
-keep class com.android.inputmethod.keyboard.ProximityInfo {
<init>(com.android.inputmethod.keyboard.ProximityInfo);
}
@@ -24,11 +20,19 @@
boolean equalsIgnoreCase(...);
}
+-keep class com.android.inputmethod.latin.InputPointers {
+ *;
+}
+
+-keep class com.android.inputmethod.latin.ResizableIntArray {
+ *;
+}
+
-keep class com.android.inputmethod.latin.spellcheck.SpellCheckerSettingsFragment {
*;
}
--keep class com.android.inputmethod.keyboard.LatinKeyboardView {
+-keep class com.android.inputmethod.keyboard.MainKeyboardView {
# Keep getter/setter methods for ObjectAnimator
int getLanguageOnSpacebarAnimAlpha();
void setLanguageOnSpacebarAnimAlpha(int);
@@ -40,14 +44,13 @@
<init>(...);
}
--keep class com.android.inputmethod.latin.ResearchLogger {
- void setLogFileManager(...);
- void clearAll();
- com.android.inputmethod.latin.ResearchLogger$LogFileManager getLogFileManager();
+-keepclasseswithmembernames class * {
+ native <methods>;
}
--keep class com.android.inputmethod.latin.ResearchLogger$LogFileManager {
- java.lang.String getContents();
+-keep class com.android.inputmethod.research.ResearchLogger {
+ void flush();
+ void publishCurrentLogUnit(...);
}
-keep class com.android.inputmethod.keyboard.KeyboardLayoutSet$Builder {
diff --git a/java/res/drawable-hdpi/ic_ime_settings.png b/java/res/drawable-hdpi/ic_ime_settings.png
deleted file mode 100644
index f8f80732d..000000000
--- a/java/res/drawable-hdpi/ic_ime_settings.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/ic_ime_settings.png b/java/res/drawable-mdpi/ic_ime_settings.png
deleted file mode 100644
index 060e8620e..000000000
--- a/java/res/drawable-mdpi/ic_ime_settings.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/ic_ime_settings.png b/java/res/drawable-xhdpi/ic_ime_settings.png
deleted file mode 100644
index d674be06e..000000000
--- a/java/res/drawable-xhdpi/ic_ime_settings.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable/btn_keyboard_key.xml b/java/res/drawable/btn_keyboard_key.xml
index 45578e582..797bc105e 100644
--- a/java/res/drawable/btn_keyboard_key.xml
+++ b/java/res/drawable/btn_keyboard_key.xml
@@ -17,8 +17,8 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Toggle keys. Use checkable/checked state. -->
-
- <item android:state_checkable="true" android:state_checked="true"
+
+ <item android:state_checkable="true" android:state_checked="true"
android:state_pressed="true"
android:drawable="@drawable/btn_keyboard_key_pressed_on" />
<item android:state_checkable="true" android:state_pressed="true"
@@ -34,5 +34,5 @@
android:drawable="@drawable/btn_keyboard_key_pressed" />
<item
android:drawable="@drawable/btn_keyboard_key_normal" />
-
+
</selector>
diff --git a/java/res/drawable/keyboard_key_feedback.xml b/java/res/drawable/keyboard_key_feedback.xml
index 159ba8686..397e948d8 100644
--- a/java/res/drawable/keyboard_key_feedback.xml
+++ b/java/res/drawable/keyboard_key_feedback.xml
@@ -14,9 +14,11 @@
limitations under the License.
-->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_long_pressable="true"
- android:drawable="@drawable/keyboard_key_feedback_more_background" />
-
+<selector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+ <item latin:state_has_morekeys="true"
+ android:drawable="@drawable/keyboard_key_feedback_more_background" />
<item android:drawable="@drawable/keyboard_key_feedback_background" />
</selector>
diff --git a/java/res/drawable/keyboard_key_feedback_ics.xml b/java/res/drawable/keyboard_key_feedback_ics.xml
index 04c86794f..3c8850e6c 100644
--- a/java/res/drawable/keyboard_key_feedback_ics.xml
+++ b/java/res/drawable/keyboard_key_feedback_ics.xml
@@ -14,8 +14,23 @@
limitations under the License.
-->
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_long_pressable="true"
- android:drawable="@drawable/keyboard_key_feedback_more_background_holo" />
+<selector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+ <!-- Left edge -->
+ <item latin:state_left_edge="true" latin:state_has_morekeys="true"
+ android:drawable="@drawable/keyboard_key_feedback_left_more_background_holo" />
+ <item latin:state_left_edge="true"
+ android:drawable="@drawable/keyboard_key_feedback_left_background_holo" />
+
+ <!-- Right edge -->
+ <item latin:state_right_edge="true" latin:state_has_morekeys="true"
+ android:drawable="@drawable/keyboard_key_feedback_right_more_background_holo" />
+ <item latin:state_right_edge="true"
+ android:drawable="@drawable/keyboard_key_feedback_right_background_holo" />
+
+ <item latin:state_has_morekeys="true"
+ android:drawable="@drawable/keyboard_key_feedback_more_background_holo" />
<item android:drawable="@drawable/keyboard_key_feedback_background_holo" />
</selector>
diff --git a/java/res/drawable/keyboard_key_feedback_left_ics.xml b/java/res/drawable/keyboard_key_feedback_left_ics.xml
deleted file mode 100644
index b68b37f6b..000000000
--- a/java/res/drawable/keyboard_key_feedback_left_ics.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_long_pressable="true"
- android:drawable="@drawable/keyboard_key_feedback_left_more_background_holo" />
- <item android:drawable="@drawable/keyboard_key_feedback_left_background_holo" />
-</selector>
diff --git a/java/res/drawable/keyboard_key_feedback_right_ics.xml b/java/res/drawable/keyboard_key_feedback_right_ics.xml
deleted file mode 100644
index 830678a5a..000000000
--- a/java/res/drawable/keyboard_key_feedback_right_ics.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2011 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_long_pressable="true"
- android:drawable="@drawable/keyboard_key_feedback_right_more_background_holo" />
- <item android:drawable="@drawable/keyboard_key_feedback_right_background_holo" />
-</selector>
diff --git a/java/res/layout/input_view.xml b/java/res/layout/input_view.xml
index 3863534be..40eff3839 100644
--- a/java/res/layout/input_view.xml
+++ b/java/res/layout/input_view.xml
@@ -43,20 +43,20 @@
android:layout_width="@dimen/suggestions_strip_padding"
android:layout_height="@dimen/suggestions_strip_height"
style="?attr/suggestionsStripBackgroundStyle" />
- <com.android.inputmethod.latin.suggestions.SuggestionsView
- android:id="@+id/suggestions_view"
+ <com.android.inputmethod.latin.suggestions.SuggestionStripView
+ android:id="@+id/suggestion_strip_view"
android:layout_weight="1.0"
android:layout_width="0dp"
android:layout_height="@dimen/suggestions_strip_height"
android:gravity="center_vertical"
- style="?attr/suggestionsViewStyle" />
+ style="?attr/suggestionStripViewStyle" />
<View
android:layout_width="@dimen/suggestions_strip_padding"
android:layout_height="@dimen/suggestions_strip_height"
style="?attr/suggestionsStripBackgroundStyle" />
</LinearLayout>
- <com.android.inputmethod.keyboard.LatinKeyboardView
+ <com.android.inputmethod.keyboard.MainKeyboardView
android:id="@+id/keyboard_view"
android:layout_alignParentBottom="true"
android:layout_width="match_parent"
diff --git a/java/res/layout/key_preview.xml b/java/res/layout/key_preview.xml
index 6ed892e28..2fcd0c4dd 100644
--- a/java/res/layout/key_preview.xml
+++ b/java/res/layout/key_preview.xml
@@ -20,8 +20,8 @@
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
- android:layout_height="80dp"
- android:textSize="40dp"
+ android:layout_height="wrap_content"
+ android:background="@drawable/keyboard_key_feedback"
android:minWidth="32dp"
android:gravity="center"
- />
+/>
diff --git a/java/res/xml-sw768dp-land/kbd_thai.xml b/java/res/layout/key_preview_ics.xml
index b2cdbc373..222e8846c 100644
--- a/java/res/xml-sw768dp-land/kbd_thai.xml
+++ b/java/res/layout/key_preview_ics.xml
@@ -18,12 +18,10 @@
*/
-->
-<Keyboard
- xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
- latin:rowHeight="20%p"
- latin:verticalGap="2.65%p"
- latin:touchPositionCorrectionData="@null"
->
- <include
- latin:keyboardLayout="@xml/rows_thai" />
-</Keyboard>
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:background="@drawable/keyboard_key_feedback_ics"
+ android:minWidth="32dp"
+ android:gravity="center"
+/>
diff --git a/java/res/layout/research_feedback_activity.xml b/java/res/layout/research_feedback_activity.xml
new file mode 100644
index 000000000..a6b8b8a43
--- /dev/null
+++ b/java/res/layout/research_feedback_activity.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<com.android.inputmethod.research.FeedbackLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:id="@+id/research_feedback_layout"
+>
+
+ <fragment
+ android:id="@+id/research_feedback_fragment"
+ android:name="com.android.inputmethod.research.FeedbackFragment"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ />
+</com.android.inputmethod.research.FeedbackLayout>
diff --git a/java/res/layout/research_feedback_fragment_layout.xml b/java/res/layout/research_feedback_fragment_layout.xml
new file mode 100644
index 000000000..cc04cedf4
--- /dev/null
+++ b/java/res/layout/research_feedback_fragment_layout.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical"
+>
+
+ <!-- Mimic a dialog title. Necessary since the dialog is actually an activity, so the normal
+ dialog title construction code is not available. -->
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ >
+ <com.android.internal.widget.DialogTitle
+ style="?android:attr/windowTitleStyle"
+ android:singleLine="true"
+ android:ellipsize="end"
+ android:layout_width="match_parent"
+ android:layout_height="64dip"
+ android:layout_marginLeft="16dip"
+ android:layout_marginRight="16dip"
+ android:gravity="center_vertical|left"
+ android:text="@string/research_feedback_dialog_title" />
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="2dip"
+ android:background="@android:color/holo_blue_light" />
+ </LinearLayout>
+
+ <EditText
+ android:id="@+id/research_feedback_contents"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_gravity="fill_horizontal|center_vertical"
+ android:layout_marginLeft="8dip"
+ android:layout_marginRight="8dip"
+ android:layout_marginBottom="8dip"
+ android:layout_marginTop="8dip"
+ android:lines="2"
+ android:hint="@string/research_feedback_hint"
+ android:inputType="textMultiLine"
+ android:imeOptions="flagNoFullscreen"
+ >
+ <requestFocus />
+ </EditText>
+
+ <CheckBox
+ android:id="@+id/research_feedback_include_history"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_marginBottom="8dip"
+ android:checked="true"
+ android:text="@string/research_feedback_include_history_label"
+ />
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:divider="?android:attr/dividerHorizontal"
+ android:showDividers="beginning"
+ android:dividerPadding="0dip"
+ >
+ <LinearLayout
+ style="?android:attr/buttonBarStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:measureWithLargestChild="true"
+ >
+ <Button
+ android:id="@+id/research_feedback_cancel_button"
+ android:layout_width="0dip"
+ android:layout_gravity="left"
+ android:layout_weight="1"
+ android:maxLines="2"
+ style="?android:attr/buttonBarButtonStyle"
+ android:textSize="14sp"
+ android:text="@string/research_feedback_cancel"
+ android:layout_height="wrap_content"
+ />
+ <Button
+ android:id="@+id/research_feedback_send_button"
+ android:layout_width="0dip"
+ android:layout_gravity="right"
+ android:layout_weight="1"
+ android:maxLines="2"
+ style="?android:attr/buttonBarButtonStyle"
+ android:textSize="14sp"
+ android:text="@string/research_feedback_send"
+ android:layout_height="wrap_content"
+ />
+ </LinearLayout>
+ </LinearLayout>
+</LinearLayout>
diff --git a/java/res/layout/research_feedback_layout.xml b/java/res/layout/research_feedback_layout.xml
new file mode 100644
index 000000000..bacd19101
--- /dev/null
+++ b/java/res/layout/research_feedback_layout.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical"
+>
+
+ <EditText
+ android:id="@+id/research_feedback_contents"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_gravity="fill_horizontal|center_vertical"
+ android:layout_marginLeft="8dip"
+ android:layout_marginRight="8dip"
+ android:layout_marginBottom="8dip"
+ android:layout_marginTop="8dip"
+ android:lines="2"
+ android:hint="@string/research_feedback_hint"
+ android:inputType="textMultiLine"
+ android:imeOptions="flagNoFullscreen"
+ android:focusable="true"
+ >
+ <requestFocus />
+ </EditText>
+
+ <CheckBox
+ android:id="@+id/research_feedback_include_history"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_marginBottom="8dip"
+ android:checked="true"
+ android:text="@string/research_feedback_include_history_label"
+ />
+</LinearLayout>
diff --git a/java/res/mipmap-hdpi/ic_ime_settings.png b/java/res/mipmap-hdpi/ic_ime_settings.png
new file mode 100644
index 000000000..2de998bb9
--- /dev/null
+++ b/java/res/mipmap-hdpi/ic_ime_settings.png
Binary files differ
diff --git a/java/res/mipmap-mdpi/ic_ime_settings.png b/java/res/mipmap-mdpi/ic_ime_settings.png
new file mode 100644
index 000000000..209bab42a
--- /dev/null
+++ b/java/res/mipmap-mdpi/ic_ime_settings.png
Binary files differ
diff --git a/java/res/mipmap-xhdpi/ic_ime_settings.png b/java/res/mipmap-xhdpi/ic_ime_settings.png
new file mode 100644
index 000000000..371ac1fc0
--- /dev/null
+++ b/java/res/mipmap-xhdpi/ic_ime_settings.png
Binary files differ
diff --git a/java/res/mipmap-xxhdpi/ic_ime_settings.png b/java/res/mipmap-xxhdpi/ic_ime_settings.png
new file mode 100644
index 000000000..17c52dd01
--- /dev/null
+++ b/java/res/mipmap-xxhdpi/ic_ime_settings.png
Binary files differ
diff --git a/java/res/raw/main_en.dict b/java/res/raw/main_en.dict
index 98a9361b5..de1170afd 100644
--- a/java/res/raw/main_en.dict
+++ b/java/res/raw/main_en.dict
Binary files differ
diff --git a/java/res/raw/main_es.dict b/java/res/raw/main_es.dict
index 71370aac2..7a4daf1f2 100644
--- a/java/res/raw/main_es.dict
+++ b/java/res/raw/main_es.dict
Binary files differ
diff --git a/java/res/raw/main_fr.dict b/java/res/raw/main_fr.dict
index 717078c93..c607d0e36 100644
--- a/java/res/raw/main_fr.dict
+++ b/java/res/raw/main_fr.dict
Binary files differ
diff --git a/java/res/raw/main_it.dict b/java/res/raw/main_it.dict
index 82579078c..b93a55c93 100644
--- a/java/res/raw/main_it.dict
+++ b/java/res/raw/main_it.dict
Binary files differ
diff --git a/java/res/raw/main_pt_br.dict b/java/res/raw/main_pt_br.dict
index d31752e07..66ac3f953 100644
--- a/java/res/raw/main_pt_br.dict
+++ b/java/res/raw/main_pt_br.dict
Binary files differ
diff --git a/java/res/raw/main_ru.dict b/java/res/raw/main_ru.dict
new file mode 100644
index 000000000..050b0b8c4
--- /dev/null
+++ b/java/res/raw/main_ru.dict
Binary files differ
diff --git a/java/res/xml-sw600dp/kbd_thai.xml b/java/res/values-af/bools.xml
index b75980f2f..840d20c21 100644
--- a/java/res/xml-sw600dp/kbd_thai.xml
+++ b/java/res/values-af/bools.xml
@@ -17,13 +17,8 @@
** limitations under the License.
*/
-->
-
-<Keyboard
- xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
- latin:rowHeight="20%p"
- latin:verticalGap="3.20%p"
- latin:touchPositionCorrectionData="@null"
->
- <include
- latin:keyboardLayout="@xml/rows_thai" />
-</Keyboard>
+<resources>
+ <!-- Whether this input method should be used as the default for a locale. Override it
+ for supported languages. -->
+ <bool name="im_is_default">true</bool>
+</resources>
diff --git a/java/res/values-af/strings-appname.xml b/java/res/values-af/strings-appname.xml
new file mode 100644
index 000000000..d6bb52f52
--- /dev/null
+++ b/java/res/values-af/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Android-sleutelbord"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Android-speltoetser"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Android-sleutelbordinstellings"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Speltoets tans instellings"</string>
+</resources>
diff --git a/java/res/values-af/strings.xml b/java/res/values-af/strings.xml
index 7431fced8..a2de92ed6 100644
--- a/java/res/values-af/strings.xml
+++ b/java/res/values-af/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Android-sleutelbord"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android-sleutelbord (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Android-sleutelbordinstellings"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Invoeropsies"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Android-speltoetser"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Navorsing-loglêerbevele"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android-speltoetser (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Speltoetser se instellings"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Soek kontakname op"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Speltoetser gebruik inskrywings uit jou kontaklys"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibreer met sleuteldruk"</string>
@@ -34,37 +31,41 @@
<string name="popup_on_keypress" msgid="123894815723512944">"Opspring met sleuteldruk"</string>
<string name="general_category" msgid="1859088467017573195">"Algemeen"</string>
<string name="correction_category" msgid="2236750915056607613">"Tekskorrigering"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"Gebaar-tik"</string>
<string name="misc_category" msgid="6894192814868233453">"Ander opsies"</string>
<string name="advanced_settings" msgid="362895144495591463">"Gevorderde instellings"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"Opsies vir kundiges"</string>
<string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Skakel oor na die ander invoermetodes"</string>
<string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Taal-wisselsleutel dek ook ander invoermetodes"</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"Onderdruk taal-wisselsleutel"</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"Taal-wisselsleutel"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Wys wanneer meervoudige invoertale geaktiveer is"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Sleutelopspringer-wagperiode"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Geen wagperiode nie"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Verstek"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Stel kontakname voor"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Gebruik name van kontakte vir voorstelle en korreksies"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Aktiveer herkorrigerings"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Stel voorstelle vir herkorrigerings"</string>
<string name="auto_cap" msgid="1719746674854628252">"Outohoofletters"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"Die eerste woord van elke sin moet met \'n hoofletter begin"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Voeg woordeboeke by"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Hoofwoordeboek"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"Wys voorstelle vir korrigering"</string>
<string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Wys voorgestelde woorde terwyl jy tik"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Wys altyd"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Wys in portretmodus"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Wys in portretmodus"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Versteek altyd"</string>
- <string name="auto_correction" msgid="4979925752001319458">"Outokorrigering"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"Outokorrigering"</string>
<string name="auto_correction_summary" msgid="5625751551134658006">"Spasiebalk en leestekens korrigeer outomaties woorde wat verkeerd gespel is"</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Af"</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Matig"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Aggressief"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Baie aggressief"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Volgendewoordvoorstelle"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Gebruik vorige woord om voorstelle te verbeter"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Volgendewoordvoorspelling"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Gebruik vorige woord ook vir voorspelling"</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"Stel volgende woord voor"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"Gebruik die vorige woord om voorstelle te maak"</string>
+ <string name="gesture_input" msgid="826951152254563827">"Aktiveer gebaar-tik"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"Voer \'n woord in deur te gly deur die letters"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Wys gebaarspoor"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"Dinamiese sweefvoorskou"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"Sien die voorgestelde woord tydens gebare"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Gestoor"</string>
<string name="label_go_key" msgid="1635148082137219148">"Gaan"</string>
<string name="label_next_key" msgid="362972844525672568">"Volgende"</string>
@@ -95,6 +96,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Enter"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Soek"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Punt"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Verander taal"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Volgende"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Vorige"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift geaktiveer"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Kasslot geaktiveer"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift gedeaktiveer"</string>
diff --git a/java/res/values-am/strings-appname.xml b/java/res/values-am/strings-appname.xml
new file mode 100644
index 000000000..fd93114f3
--- /dev/null
+++ b/java/res/values-am/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"የAndroid ቁልፍ ሰሌዳ"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Android የፊደል አራሚ"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Android የቁልፍ ሰሌዳ ቅንብሮች"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"የፊደል አራሚ ቅንብሮች"</string>
+</resources>
diff --git a/java/res/values-am/strings.xml b/java/res/values-am/strings.xml
index d70c05da9..df75c10b8 100644
--- a/java/res/values-am/strings.xml
+++ b/java/res/values-am/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"የAndroid ቁልፍሰሌዳ"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"የAndroid ቁልፍ ሰሌዳ (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"የAndroid ቁልፍሰሌዳ ቅንብሮች"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"ግቤት አማራጮች"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Android የፊደል ማረሚያ"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"የጥናት የምዝግብ ማስታወሻ ትዕዛዞች"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android የፊደል ማረሚያ (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"የፊደል አራሚ ቅንብሮች"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"የእውቅያ ስሞችን ተመልከት"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"ፊደል አራሚ ከእውቅያ ዝርዝርህ የገቡትን ይጠቀማል"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"በቁልፍመጫንጊዜ አንዝር"</string>
@@ -34,37 +31,41 @@
<string name="popup_on_keypress" msgid="123894815723512944">"ቁልፍ ጫን ላይ ብቅ ባይ"</string>
<string name="general_category" msgid="1859088467017573195">"አጠቃላይ"</string>
<string name="correction_category" msgid="2236750915056607613">"ፅሁፍ አስተካክል"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"በምልክት መተየብ"</string>
<string name="misc_category" msgid="6894192814868233453">"ሌሎች አማራጮች"</string>
<string name="advanced_settings" msgid="362895144495591463">"የላቁ ቅንብሮች"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"ለብቁ ተጠቃሚዎች አማራጮች"</string>
<string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"ወደ ሌሎች የግቤት ስልቶች ቀይር"</string>
<string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"የቋንቋ መቀየሪያ ቁልፍ ሌሎች የግቤት ስልቶችንም ይሸፍናል"</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"የቋንቋ መቀየሪያ ቁልፍ አፍን"</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"የቋንቋ መቀየሪያ ቁልፍ"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"በርካታ የግቤት ቋንቋዎች ሲነቁ አሳይ"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"የቁልፍ ብቅ ባይ መዘግየትን ያስወገዳል"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"የዘገየ የለም"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"ነባሪ"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"የዕውቂያ ስም ጠቁም"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"ከዕውቂያዎች ለጥቆማዎች እና ማስተካከያዎች ስሞች ተጠቀም"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"ድጋሚ ለማስተካከል አንቃ"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"ድጋሚ ለማስተካከል ጥቆማዎችን አዘጋጅ"</string>
<string name="auto_cap" msgid="1719746674854628252">"ራስ-ሰር አቢይ ማድረግ"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"የእያንዳንዱ ዓረፍተ ነገር የመጀመሪያ ቃል በአቢይ ሆሄ ያስቀምጡ"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"መዝገበ ቃላቶች ጨምር"</string>
<string name="main_dictionary" msgid="4798763781818361168">"ዋና መዝገበ ቃላት"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"የማስተካከያ ጥቆማዎች አሳይ"</string>
<string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"እየተየብክ ተመራጭ ቃላትን አሳይ"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"ሁልጊዜ አሳይ"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"በቁመት ሁነታ አሳይ"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"በቁም አቀማመጥ ሁነታ አሳይ"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"ሁልጊዜ ደብቅ"</string>
- <string name="auto_correction" msgid="4979925752001319458">"በራስ ማስተካከል"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"በራስ-ማስተካከል"</string>
<string name="auto_correction_summary" msgid="5625751551134658006">"የቦታ ቁልፍ እና ሥርዓተ ነጥብ በስህተት የተተየቡ ቃላትን በራስሰር ያስተካክላሉ ።"</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"ውጪ"</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"መጠነኛ"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"ኃይለኛ"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"በጣም ቁጡ"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"የቀጣይ ቃል አስተያየቶች"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"ምክሮችን ለማሻሻል ቀዳሚ ቃል ተጠቀም"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"የቀጣይ ቃል ግምት"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"ለትንበያ የቀደመ ቃል እንዲሁ ተጠቀም"</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"የቀጣይ ቃል አስተያየቶች"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"አስተያየቶች መስጠት ላይ ቀዳሚውን ቃል ተጠቀም"</string>
+ <string name="gesture_input" msgid="826951152254563827">"በምልክት መተየብ ያንቁ"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"በፊደሎች መካከል በማንሸራተት ቃል ያስገቡ"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"ምልክት የሚሄድበት መንገድ አሳይ"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"ተለዋዋጭ ተንሳፋፊ ቅድመ-እይታ"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"ምልክት እየሰጡ ሳሉ በአስተያየት የተጠቆመው ቃል ይመልከቱ"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : ተቀምጧል"</string>
<string name="label_go_key" msgid="1635148082137219148">"ሂድ"</string>
<string name="label_next_key" msgid="362972844525672568">"በመቀጠል"</string>
@@ -95,6 +96,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"ተመለስ"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"ፍለጋ"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"ነጥብ"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"ቋንቋ ቀይር"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"ቀጣይ"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"ቀዳሚ"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"ቅያር ቁልፍ ነቅቷል"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"አቢያት ማድረጊያ ነቅቷል"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"ቅያር ተሰናክሏል"</string>
diff --git a/java/res/values-ar/bools.xml b/java/res/values-ar/bools.xml
new file mode 100644
index 000000000..840d20c21
--- /dev/null
+++ b/java/res/values-ar/bools.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<resources>
+ <!-- Whether this input method should be used as the default for a locale. Override it
+ for supported languages. -->
+ <bool name="im_is_default">true</bool>
+</resources>
diff --git a/java/res/values-ar/strings-appname.xml b/java/res/values-ar/strings-appname.xml
new file mode 100644
index 000000000..3d81e5d4b
--- /dev/null
+++ b/java/res/values-ar/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"لوحة مفاتيح Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"التدقيق الإملائي في Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"إعدادات لوحة مفاتيح Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"إعدادات التدقيق الإملائي"</string>
+</resources>
diff --git a/java/res/values-ar/strings.xml b/java/res/values-ar/strings.xml
index 5678c40c8..14a6a0751 100644
--- a/java/res/values-ar/strings.xml
+++ b/java/res/values-ar/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"لوحة مفاتيح Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"لوحة مفاتيح Android ‏(AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"إعدادات لوحة مفاتيح Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"خيارات الإرسال"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"التدقيق الإملائي في Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"أوامر سجلات البحث"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"التدقيق الإملائي في Android‏ (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"إعدادات التدقيق الإملائي"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"بحث في أسماء جهات الاتصال"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"يستخدم المدقق الإملائي إدخالات من قائمة جهات الاتصال"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"اهتزاز عند ضغط مفتاح"</string>
@@ -34,37 +31,41 @@
<string name="popup_on_keypress" msgid="123894815723512944">"انبثاق عند ضغط مفتاح"</string>
<string name="general_category" msgid="1859088467017573195">"عام"</string>
<string name="correction_category" msgid="2236750915056607613">"تصحيح النص"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"الكتابة بالإيماءة"</string>
<string name="misc_category" msgid="6894192814868233453">"خيارات أخرى"</string>
<string name="advanced_settings" msgid="362895144495591463">"الإعدادات المتقدمة"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"خيارات للخبراء"</string>
<string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"التبديل إلى أسلوب إدخال آخر"</string>
<string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"يغطي مفتاح تبديل اللغات أساليب الإدخال الأخرى أيضًا"</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"إيقاف مفتاح تبديل اللغات"</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"مفتاح تبديل اللغة"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"يظهر عندما يتم تمكين لغات الإدخال متعددة"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"تأخير إزالة النافذة المنبثقة الأساسية"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"بلا تأخير"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"افتراضي"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"اقتراح أسماء جهات الاتصال"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"استخدام الأسماء من جهات الاتصال للاقتراحات والتصحيحات"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"تمكين عمليات إعادة التصحيح"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"تعيين اقتراحات لعمليات إعادة التصحيح"</string>
<string name="auto_cap" msgid="1719746674854628252">"أحرف كبيرة تلقائيًا"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"كتابة الحرف الأول من كل جملة بحرف كبير."</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"القواميس الإضافية"</string>
<string name="main_dictionary" msgid="4798763781818361168">"القاموس الرئيسي"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"عرض اقتراحات التصحيح"</string>
<string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"عرض الكلمات المقترحة أثناء الكتابة"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"عرض دومًا"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"عرض في وضع رأسي"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"العرض في وضع رأسي"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"إخفاء دومًا"</string>
- <string name="auto_correction" msgid="4979925752001319458">"التصحيح التلقائي"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"التصحيح التلقائي"</string>
<string name="auto_correction_summary" msgid="5625751551134658006">"تؤدي المسافة والترقيم إلى تصحيح الكلمات المكتوبة بشكل غير صحيح"</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"إيقاف"</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"معتدل"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"حاد"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"شديد الصرامة"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"اقتراحات الكلمات التالية"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"استخدام الكلمة السابقة لتحسين الاقتراحات"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"تنبؤ الكلمات التالية"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"استخدام الكلمة السابقة أيضًا للتنبؤ"</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"اقتراحات الكلمات التالية"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"استخدام الكلمة السابقة في تقديم الاقتراحات"</string>
+ <string name="gesture_input" msgid="826951152254563827">"تمكين الكتابة بالإيماءة"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"إدخال كلمة من خلال التمرير على الأحرف"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"عرض مسار الإيماءة"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"معاينة نصوص متحركة ديناميكية"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"مشاهدة الكلمة المقترحة أثناء الإيماءة"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : تم الحفظ"</string>
<string name="label_go_key" msgid="1635148082137219148">"تنفيذ"</string>
<string name="label_next_key" msgid="362972844525672568">"التالي"</string>
@@ -95,6 +96,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"رجوع"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"بحث"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"نقطة"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"تبديل اللغة"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"التالي"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"السابق"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"تم تمكين Shift"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"تم تمكين Caps lock"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"تم تعطيل Shift"</string>
diff --git a/java/res/values-be/bools.xml b/java/res/values-be/bools.xml
new file mode 100644
index 000000000..840d20c21
--- /dev/null
+++ b/java/res/values-be/bools.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<resources>
+ <!-- Whether this input method should be used as the default for a locale. Override it
+ for supported languages. -->
+ <bool name="im_is_default">true</bool>
+</resources>
diff --git a/java/res/values-be/strings-appname.xml b/java/res/values-be/strings-appname.xml
new file mode 100644
index 000000000..e0aadfa3c
--- /dev/null
+++ b/java/res/values-be/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Клавіятура Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Iнструмент праверкi правапiсу для Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Налады клавіятуры Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Налады праверкі арфаграфіі"</string>
+</resources>
diff --git a/java/res/values-be/strings.xml b/java/res/values-be/strings.xml
index 4d14565b9..f43840832 100644
--- a/java/res/values-be/strings.xml
+++ b/java/res/values-be/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Клавіятура Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Клавіятура Android (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Налады клавіятуры Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Параметры ўводу"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Iнструмент праверкi правапiсу для Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Каманды гiсторыя даследаванняў"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Iнструмент праверкi правапiсу для Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Налады праверкі арфаграфіі"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Шукаць імёны кантактаў"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Модуль праверкі правапісу выкарыстоўвае запісы са спісу кантактаў"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Вібрацыя пры націску клавіш"</string>
@@ -34,37 +31,41 @@
<string name="popup_on_keypress" msgid="123894815723512944">"Па націску на клавішы ўсплывае акно"</string>
<string name="general_category" msgid="1859088467017573195">"Агульныя"</string>
<string name="correction_category" msgid="2236750915056607613">"Выпраўленне тэксту"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"Набор жэстамі"</string>
<string name="misc_category" msgid="6894192814868233453">"Іншыя параметры"</string>
<string name="advanced_settings" msgid="362895144495591463">"Адмысловыя налады"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"Функцыi для спецыялістаў"</string>
<string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Перакл. да інш. спос. ув."</string>
<string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Кнопка пераключэння мовы звязана i з iншымi спосабамi ўводу"</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"Забаранiць кнопку пераключэння мовы"</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"Кнопка пераключэння мовы"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Паказваць, калі ўключана некалькі моў ўводу"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Затрым. скр. падк. клав."</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Няма затрымкі"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Па змаўчанні"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Прапан. імёны кантактаў"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Выкарыстоўваць імёны са спісу кантактаў для прапаноў і выпраўл."</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Уключыць карэкцiроўкі"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Задаць прапановы для карэкцiроўкі"</string>
<string name="auto_cap" msgid="1719746674854628252">"Аўтаматычна рабіць вялікія літары"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"Пісаць з загалоўнай літары першае слова ў кожным сказе"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Дадатковыя слоўнікі"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Асноўны слоўнік"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"Паказаць прапановы на выпраўленне"</string>
<string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Паказваць прапанаваныя словы падчас набору тэксту"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Заўсёды паказваць"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Паказаць у партрэтным рэжыме"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Паказаць у партрэтным рэжыме"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Заўседы хаваць"</string>
- <string name="auto_correction" msgid="4979925752001319458">"Аўтамат. выпраўленне"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"Аўтавыпраўленне"</string>
<string name="auto_correction_summary" msgid="5625751551134658006">"Прабелы і пунктуацыйныя знакі дазваляюць аўтаматычна выпраўляць памылкова ўведзеныя словы"</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Адключаны"</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Сціплы"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Агрэсіўны"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Вельмі агрэсіўны"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Падказкi для наступнага слова"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Выкарыстаць папярэдняе слова, каб палепшыць прапановы"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Падказка наступнага слова"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Выкарыстанне папярэдняга слова для падказак"</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"Падказкi для наступнага слова"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"Выкарыстоўваць папярэдняе слова, каб атрымлiваць падказкi"</string>
+ <string name="gesture_input" msgid="826951152254563827">"Уключыць набор жэстамі"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"Уводзьце слова, перасоўваючы палец па літарах"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Паказаць след жэста"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"Дынамічны плаваючы прагляд"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"Праглядаць прапанаванае слова падчас жэсту"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Захаваныя"</string>
<string name="label_go_key" msgid="1635148082137219148">"Пачаць"</string>
<string name="label_next_key" msgid="362972844525672568">"Далей"</string>
@@ -95,6 +96,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Увод"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Пошук"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Кропка"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Пераключыць мову"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Далей"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Назад"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift уключаны"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps Lock уключаны"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift адключаны"</string>
diff --git a/java/res/values-bg/bools.xml b/java/res/values-bg/bools.xml
new file mode 100644
index 000000000..840d20c21
--- /dev/null
+++ b/java/res/values-bg/bools.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<resources>
+ <!-- Whether this input method should be used as the default for a locale. Override it
+ for supported languages. -->
+ <bool name="im_is_default">true</bool>
+</resources>
diff --git a/java/res/values-bg/strings-appname.xml b/java/res/values-bg/strings-appname.xml
new file mode 100644
index 000000000..49e301d32
--- /dev/null
+++ b/java/res/values-bg/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Клавиатура на Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Програма за правописна проверка за Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Настройки на клавиатурата на Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Настройки за проверка на правописа"</string>
+</resources>
diff --git a/java/res/values-bg/strings.xml b/java/res/values-bg/strings.xml
index 106a91806..8d9c36e02 100644
--- a/java/res/values-bg/strings.xml
+++ b/java/res/values-bg/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Клавиатура на Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Клавиатура на Android (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Настройки на клавиатурата на Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Опции за въвеждане"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Програма за правописна проверка за Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Команди за рег. файл за проучвания"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Програма за правописна проверка за Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Настройки за проверка на правописа"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Търсене на имена"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"За проверка на правописа се ползват записи от списъка с контакти"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Да вибрира при натискане на клавиш"</string>
@@ -34,37 +31,41 @@
<string name="popup_on_keypress" msgid="123894815723512944">"Изскачащ прозорец при натискане на клавиш"</string>
<string name="general_category" msgid="1859088467017573195">"Общи"</string>
<string name="correction_category" msgid="2236750915056607613">"Корекция на текста"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"Въвеждане чрез жест"</string>
<string name="misc_category" msgid="6894192814868233453">"Други опции"</string>
<string name="advanced_settings" msgid="362895144495591463">"Разширени настройки"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"Опции за експерти"</string>
<string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Други методи за въвеждане"</string>
<string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Клавишът за превкл. на езика обхваща и други методи за въвеждане"</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"Потискане"</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"Клавиш за превкл. на езика"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Показване при няколко активирани езика за въвеждане"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Отхвърляне на подсказката"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Без задържане"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"По подразбиране"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Предложения за контакти"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Използване на имена от „Контакти“ за предложения и поправки"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Повторни поправки: Актив."</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Задаване на предложения за повторни поправки"</string>
<string name="auto_cap" msgid="1719746674854628252">"Автоматично поставяне на главни букви"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"Правене на първата дума от всяко изречение главна"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Добавени речници"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Основен речник"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"Показване на предложения за поправка"</string>
<string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Показване на предложения, докато пишете"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Винаги да се показва"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Показване с вертикална ориентация"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Показване във вертикална ориентация"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Винаги да се скрива"</string>
- <string name="auto_correction" msgid="4979925752001319458">"Автоко"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"Автоматична поправка"</string>
<string name="auto_correction_summary" msgid="5625751551134658006">"Клавишът за интервал и пунктуация авт. поправя сгрешени думи"</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Изкл."</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Умерено"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Агресивно"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Много агресивно"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Предложения за следващата дума"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Използване на предишната дума за подобряване на предложенията"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Предвиждане на следващата дума"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Използване на предишната дума и за предвиждане"</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"Предложения за следващата дума"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"Използване на предишната дума при даване на предложения"</string>
+ <string name="gesture_input" msgid="826951152254563827">"Активиране на въвеждането чрез жест"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"Въвеждане на дума чрез плъзгане на пръст през буквите"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Следа на жестовете: Показване"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"Динамична плаваща визуализация"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"Преглед на предложената дума при използване на жестове"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Запазено"</string>
<string name="label_go_key" msgid="1635148082137219148">"Старт"</string>
<string name="label_next_key" msgid="362972844525672568">"Напред"</string>
@@ -95,6 +96,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Return"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Търсене"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Точка"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Смяна на езика"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Следващ"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Предишен"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"„Shift“ е активиран"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"„Caps Lock“ е активиран"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"„Shift“ е деактивиран"</string>
diff --git a/java/res/values-ca/bools.xml b/java/res/values-ca/bools.xml
new file mode 100644
index 000000000..840d20c21
--- /dev/null
+++ b/java/res/values-ca/bools.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<resources>
+ <!-- Whether this input method should be used as the default for a locale. Override it
+ for supported languages. -->
+ <bool name="im_is_default">true</bool>
+</resources>
diff --git a/java/res/values-ca/strings-appname.xml b/java/res/values-ca/strings-appname.xml
new file mode 100644
index 000000000..add5c3f2f
--- /dev/null
+++ b/java/res/values-ca/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Teclat Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Corrector ortogràfic d\'Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Configuració del teclat d\'Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Configuració de la correcció ortogràfica"</string>
+</resources>
diff --git a/java/res/values-ca/strings.xml b/java/res/values-ca/strings.xml
index e3adbcff4..ba6589268 100644
--- a/java/res/values-ca/strings.xml
+++ b/java/res/values-ca/strings.xml
@@ -20,51 +20,52 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Teclat Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Teclat d\'Android (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Configuració del teclat d\'Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Opcions d\'entrada"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Corrector ortogràfic d\'Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Recerca d\'ordres de reg."</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Corrector ortogràfic d\'Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Configuració de la correcció ortogràfica"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Cerca noms de contactes"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"El corrector ortogràfic utilitza entrades de la llista de cont."</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibra en prémer tecles"</string>
- <string name="sound_on_keypress" msgid="6093592297198243644">"So en prémer una tecla"</string>
- <string name="popup_on_keypress" msgid="123894815723512944">"Finestra emergent en prémer un botó"</string>
+ <string name="sound_on_keypress" msgid="6093592297198243644">"So en prémer tecles"</string>
+ <string name="popup_on_keypress" msgid="123894815723512944">"Amplia en prémer tecles"</string>
<string name="general_category" msgid="1859088467017573195">"General"</string>
<string name="correction_category" msgid="2236750915056607613">"Correcció de text"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"Escriptura per gestos"</string>
<string name="misc_category" msgid="6894192814868233453">"Altres opcions"</string>
<string name="advanced_settings" msgid="362895144495591463">"Configuració avançada"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"Opcions per a experts"</string>
- <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Canvia mètode d\'entrada"</string>
- <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"La tecla de canvi d\'idioma també cobreix altres mètodes d\'entrada"</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"Suprimeix tecla d\'idioma"</string>
- <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Retard d\'om. em. de tecla"</string>
+ <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Altres mètodes d\'entrada"</string>
+ <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"La tecla de canvi d\'idioma serveix també per a altres mètodes d\'entrada"</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"Tecla de canvi d\'idioma"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Mostra-la quan hi hagi diversos idiomes d\'entrada activats"</string>
+ <string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Retard en ampliar tecla"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Sense retard"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Predeterminat"</string>
- <string name="use_contacts_dict" msgid="4435317977804180815">"Suggereix noms contactes"</string>
- <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Utilitza els noms de Contactes per a suggeriments i correccions"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Activa la capacitat de tornar a corregir"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Estableix suggeriments per tornar a corregir"</string>
+ <string name="use_contacts_dict" msgid="4435317977804180815">"Suggereix noms de contactes"</string>
+ <string name="use_contacts_dict_summary" msgid="6599983334507879959">"Utilitza els noms de contactes per fer suggeriments i correccions"</string>
<string name="auto_cap" msgid="1719746674854628252">"Majúscules automàtiques"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"Posa en majúscula la primera paraula de cada frase"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Diccionaris complementaris"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Diccionari principal"</string>
- <string name="prefs_show_suggestions" msgid="8026799663445531637">"Mostra els suggeriments de correcció"</string>
+ <string name="prefs_show_suggestions" msgid="8026799663445531637">"Suggeriments de correcció"</string>
<string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Mostra paraules suggerides mentre s\'escriu"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Mostra sempre"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Mostra en mode vertical"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Mostra en mode vertical"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Amaga sempre"</string>
- <string name="auto_correction" msgid="4979925752001319458">"Correcció automàtica"</string>
- <string name="auto_correction_summary" msgid="5625751551134658006">"Barra espaiadora i punt. correg. autom. paraules mal escrites"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"Correcció automàtica"</string>
+ <string name="auto_correction_summary" msgid="5625751551134658006">"Prémer tecla d\'espai o punt. per corregir errors"</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Desactiva"</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Moderada"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Total"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Molt agressiu"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Suggeriments de paraula següent"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Utilitza la paraula anterior per millorar els suggeriments"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Predicció de paraula següent"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Utilitza també la paraula anterior per a la predicció"</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"Suggeriments de paraula següent"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"Utilitza la paraula anterior a l\'hora de fer suggeriments"</string>
+ <string name="gesture_input" msgid="826951152254563827">"Activa l\'escriptura per gestos"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"Escriu una paraula fent lliscar el dit per les lletres"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Mostra el recorregut del gest"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"Previs. text flotant dinàmic"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"Consulta la paraula suggerida mentre fas el gest"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: desada"</string>
<string name="label_go_key" msgid="1635148082137219148">"Vés"</string>
<string name="label_next_key" msgid="362972844525672568">"Següent"</string>
@@ -95,6 +96,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Retorn"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Cerca"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Punt"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Canvia l\'idioma"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Següent"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Anterior"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Maj activat"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Bloq Maj activat"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Maj desactivat"</string>
@@ -102,11 +106,11 @@
<string name="spoken_description_mode_alpha" msgid="3528307674390156956">"Mode de lletres"</string>
<string name="spoken_description_mode_phone" msgid="6520207943132026264">"Mode de telèfon"</string>
<string name="spoken_description_mode_phone_shift" msgid="5499629753962641227">"Mode de símbols de telèfon"</string>
- <string name="voice_input" msgid="3583258583521397548">"Clau de l\'entrada de veu"</string>
+ <string name="voice_input" msgid="3583258583521397548">"Tecla d\'entrada de veu"</string>
<string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"Al teclat principal"</string>
- <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"Al tecl. de símb."</string>
+ <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"Al teclat de símbols"</string>
<string name="voice_input_modes_off" msgid="3745699748218082014">"Desactivada"</string>
- <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Micro en tecl. princ."</string>
+ <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Micròfon al teclat principal"</string>
<string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Micro en tecl. símb."</string>
<string name="voice_input_modes_summary_off" msgid="63875609591897607">"Entr. veu desactiv."</string>
<string name="configure_input_method" msgid="373356270290742459">"Configura mètodes d\'entrada"</string>
@@ -128,7 +132,7 @@
<string name="subtype_no_language_dvorak" msgid="3122976737669823935">"Cap idioma (Dvorak)"</string>
<string name="subtype_no_language_colemak" msgid="4205992994906097244">"Cap idioma (Colemak)"</string>
<string name="subtype_no_language_pcqwerty" msgid="8840928374394180189">"Cap idioma (PC)"</string>
- <string name="custom_input_styles_title" msgid="8429952441821251512">"Estils d\'entrada pers."</string>
+ <string name="custom_input_styles_title" msgid="8429952441821251512">"Estils d\'entrada personalitzats"</string>
<string name="add_style" msgid="6163126614514489951">"Afeg. estil"</string>
<string name="add" msgid="8299699805688017798">"Afegeix"</string>
<string name="remove" msgid="4486081658752944606">"Elimina"</string>
@@ -140,6 +144,6 @@
<string name="not_now" msgid="6172462888202790482">"Ara no"</string>
<string name="custom_input_style_already_exists" msgid="8008728952215449707">"Ja existeix aquest estil d\'entrada: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string>
<string name="prefs_usability_study_mode" msgid="1261130555134595254">"Mode d\'estudi d\'usabilitat"</string>
- <string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Configuració de la durada de les vibracions per pulsació de tecla"</string>
- <string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Configuració del volum de so de pulsació de tecla"</string>
+ <string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Configuració de durada de vibracions en prémer tecles"</string>
+ <string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Configuració del volum de so en prémer tecles"</string>
</resources>
diff --git a/java/res/values-cs/bools.xml b/java/res/values-cs/bools.xml
index 897f4b3db..c289e5bf3 100644
--- a/java/res/values-cs/bools.xml
+++ b/java/res/values-cs/bools.xml
@@ -1,22 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-/*
+/*
**
** Copyright 2008, 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
+** 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
+** 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
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<resources>
- <bool name="im_is_default">true</bool>
+ <!-- Whether this input method should be used as the default for a locale. Override it
+ for supported languages. -->
+ <bool name="im_is_default">true</bool>
</resources>
diff --git a/java/res/values-cs/strings-appname.xml b/java/res/values-cs/strings-appname.xml
new file mode 100644
index 000000000..0eeac88b4
--- /dev/null
+++ b/java/res/values-cs/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Klávesnice Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Kontrola pravopisu Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Nastavení klávesnice Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Nastavení kontroly pravopisu"</string>
+</resources>
diff --git a/java/res/values-cs/strings.xml b/java/res/values-cs/strings.xml
index 90d840cd8..1cd59f52a 100644
--- a/java/res/values-cs/strings.xml
+++ b/java/res/values-cs/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Klávesnice Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Klávesnice Android (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Nastavení klávesnice Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Možnosti zadávání textu a dat"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Kontrola pravopisu Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Příkazy vývoj. protokolu"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Kontrola pravopisu Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Nastavení kontroly pravopisu"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Vyhledat kontakty"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Kontrola pravopisu používá záznamy z vašeho seznamu kontaktů."</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Při stisku klávesy vibrovat"</string>
@@ -34,37 +31,41 @@
<string name="popup_on_keypress" msgid="123894815723512944">"Detail znaku při stisku klávesy"</string>
<string name="general_category" msgid="1859088467017573195">"Obecné"</string>
<string name="correction_category" msgid="2236750915056607613">"Oprava textu"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"Psaní gesty"</string>
<string name="misc_category" msgid="6894192814868233453">"Další možnosti"</string>
<string name="advanced_settings" msgid="362895144495591463">"Pokročilá nastavení"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"Možnosti pro odborníky"</string>
<string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Přepínat metody zadávání"</string>
<string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Klávesa pro přepínání jazyka ovládá i další metody zadávání"</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"Zakázat kl. přepínání jazyka"</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"Klávesa přepínání jazyka"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Zobrazit, když je aktivováno více vstupních jazyků"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Prodleva vysk. okna klávesnice"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Bez prodlevy"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Výchozí"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Navrhovat jména kontaktů"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Použít jména ze seznamu kontaktů k návrhům a opravám"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Povolit opětovné opravy"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Nastavit návrhy pro opětovné opravy"</string>
<string name="auto_cap" msgid="1719746674854628252">"Velká písmena automaticky"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"Kapitalizace prvního slova každé věty"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Doplňkové slovníky"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Hlavní slovník"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"Zobrazit návrhy oprav"</string>
<string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Zobrazovat navržená slova během psaní"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Vždy zobrazovat"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Zobrazit v režimu na výšku"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Zobrazovat v režimu na výšku"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Vždy skrývat"</string>
- <string name="auto_correction" msgid="4979925752001319458">"Automatické opravy"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"Automatické opravy"</string>
<string name="auto_correction_summary" msgid="5625751551134658006">"Stisknutím mezerníku a interpunkce se automaticky opravují chybně napsaná slova"</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Vypnuto"</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Mírné"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agresivní"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Velmi agresivní"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Návrh dalšího slova"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Použít předchozí slovo ke zlepšení návrhů"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Odhad dalšího slova"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Použít předchozí slovo také pro odhad"</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"Návrhy dalšího slova"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"Návrhy na základě předchozího slova"</string>
+ <string name="gesture_input" msgid="826951152254563827">"Aktivovat psaní gesty"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"Zadávání slov přejetím po písmenech"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Zobrazovat stopu gesta"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"Dynamický plovoucí náhled"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"Zobrazení navrhovaného slova při psaní gesty"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: Uloženo"</string>
<string name="label_go_key" msgid="1635148082137219148">"Přejít"</string>
<string name="label_next_key" msgid="362972844525672568">"Další"</string>
@@ -95,6 +96,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Enter"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"vyhledávací tlačítko"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Tečka"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Přepnout jazyk"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Další"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Předchozí"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Klávesa Shift je aktivní"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Klávesa Caps Lock je aktivní"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Klávesa Shift je neaktivní"</string>
diff --git a/java/res/values-da/bools.xml b/java/res/values-da/bools.xml
new file mode 100644
index 000000000..840d20c21
--- /dev/null
+++ b/java/res/values-da/bools.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<resources>
+ <!-- Whether this input method should be used as the default for a locale. Override it
+ for supported languages. -->
+ <bool name="im_is_default">true</bool>
+</resources>
diff --git a/java/res/values-da/strings-appname.xml b/java/res/values-da/strings-appname.xml
new file mode 100644
index 000000000..faef5824b
--- /dev/null
+++ b/java/res/values-da/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Android-tastatur"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Android-stavekontrol"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Indstillinger for Android-tastatur"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Indstillinger for stavekontrol"</string>
+</resources>
diff --git a/java/res/values-da/strings.xml b/java/res/values-da/strings.xml
index 50b0b0a9f..7e4e0fa00 100644
--- a/java/res/values-da/strings.xml
+++ b/java/res/values-da/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Android-tastatur"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android-tastatur (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Android-tastatur-indstillinger"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Indstillinger for input"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Android-stavekontrol"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Forskningslogkommandoer"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android-stavekontrol (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Indstillinger for stavekontrol"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Slå kontaktnavne op"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Stavekontrollen bruger poster fra listen over kontaktpersoner"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibration ved tastetryk"</string>
@@ -34,37 +31,41 @@
<string name="popup_on_keypress" msgid="123894815723512944">"Pop op ved tastetryk"</string>
<string name="general_category" msgid="1859088467017573195">"Generelt"</string>
<string name="correction_category" msgid="2236750915056607613">"Tekstkorrigering"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"Skrivning med berøring"</string>
<string name="misc_category" msgid="6894192814868233453">"Andre valgmuligheder"</string>
<string name="advanced_settings" msgid="362895144495591463">"Avancerede indstillinger"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"Muligheder for eksperter"</string>
<string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Skift inputmetode"</string>
<string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Tasten til sprogskift gælder også for andre inputmetoder"</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"Ignorer sprogskifttast"</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"Tast til sprogskift"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Vis, når der er aktiveret flere inputsprog"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Forsink. afvis. af taste-pop op"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Ingen forsink."</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Standard"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Foreslå navne på kontakter"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Brug navne fra Kontaktpersoner til forslag og rettelser"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Aktivér fornyet rettelse"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Angiv forslag til fornyet rettelse"</string>
<string name="auto_cap" msgid="1719746674854628252">"Skriv aut. med stort"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"Skriv det første ord i hver sætning med stort"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Tillægsordbøger"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Hovedordbog"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"Vis rettelsesforslag"</string>
<string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Vis ordforslag under indtastning"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Vis altid"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Vis i portrættilstand"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Vis i portræt"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Skjul altid"</string>
- <string name="auto_correction" msgid="4979925752001319458">"Automatisk retning"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"Automatisk rettelse"</string>
<string name="auto_correction_summary" msgid="5625751551134658006">"Mellemrumstast og tegnsætning retter automatisk forkerte ord"</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Fra"</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Moderat"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Aggressiv"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Meget aggressiv"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Forslag til næste ord"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Brug forrige ord til at forbedre forslag"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Forudsigelse af næste ord"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Brug også tidligere ord til forudsigelse"</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"Forslag til næste ord"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"Brug det forrige ord til at give forslag"</string>
+ <string name="gesture_input" msgid="826951152254563827">"Aktivér skrivning med berøring"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"Skriv et ord ved at glide mellem bogstaverne"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Vis spor af berøring"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"Dynamisk flydende eks.visning"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"Se det foreslåede ord, mens berøringer udføres"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: Gemt"</string>
<string name="label_go_key" msgid="1635148082137219148">"Gå"</string>
<string name="label_next_key" msgid="362972844525672568">"Næste"</string>
@@ -95,6 +96,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Tilbage"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Søg"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Punktum"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Skift sprog"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Næste"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Forrige"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Skift er aktiveret"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps lock er aktiveret"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Skift er deaktiveret"</string>
diff --git a/java/res/values-de/bools.xml b/java/res/values-de/bools.xml
index 897f4b3db..c289e5bf3 100644
--- a/java/res/values-de/bools.xml
+++ b/java/res/values-de/bools.xml
@@ -1,22 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-/*
+/*
**
** Copyright 2008, 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
+** 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
+** 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
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<resources>
- <bool name="im_is_default">true</bool>
+ <!-- Whether this input method should be used as the default for a locale. Override it
+ for supported languages. -->
+ <bool name="im_is_default">true</bool>
</resources>
diff --git a/java/res/values-de/strings-appname.xml b/java/res/values-de/strings-appname.xml
new file mode 100644
index 000000000..fc5fb8902
--- /dev/null
+++ b/java/res/values-de/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Android-Tastatur"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Android-Rechtschreibprüfung"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Android-Tastatureinstellungen"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Einstellungen für Rechtschreibprüfung"</string>
+</resources>
diff --git a/java/res/values-de/strings.xml b/java/res/values-de/strings.xml
index 216aac5d9..897bfb712 100644
--- a/java/res/values-de/strings.xml
+++ b/java/res/values-de/strings.xml
@@ -20,51 +20,52 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Android-Tastatur"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android-Tastatur (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Android-Tastatureinstellungen"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Eingabeoptionen"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Android-Rechtschreibprüfung"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Forschungsprotokollbefehle"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android-Rechtschreibprüfung (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Einstellungen für Rechtschreibprüfung"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Kontaktnamen prüfen"</string>
- <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Rechtschreibprüfung verwendet Einträge aus Ihrer Kontaktliste."</string>
+ <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Rechtschreibprüfung kann Einträge aus meiner Kontaktliste verwenden"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Bei Tastendruck vibrieren"</string>
<string name="sound_on_keypress" msgid="6093592297198243644">"Ton bei Tastendruck"</string>
<string name="popup_on_keypress" msgid="123894815723512944">"Pop-up bei Tastendruck"</string>
<string name="general_category" msgid="1859088467017573195">"Allgemein"</string>
<string name="correction_category" msgid="2236750915056607613">"Textkorrektur"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"Bewegungseingabe"</string>
<string name="misc_category" msgid="6894192814868233453">"Sonstige Optionen"</string>
<string name="advanced_settings" msgid="362895144495591463">"Erweiterte Einstellungen"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"Optionen für Experten"</string>
<string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Eingabemethoden wechseln"</string>
<string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Sprachwechseltaste umfasst auch andere Eingabemethoden."</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"Sprachwechsel unterdrücken"</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"Sprachwechsel"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Anzeigen, wenn mehrere Eingabesprachen aktiviert sind"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Tasten-Pop-up"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Keine Verzögerung"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Standard"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Kontakte vorschlagen"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Namen aus \"Kontakte\" als Vorschläge und Korrekturmöglichkeiten anzeigen"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Korrekturen aktivieren"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Vorschläge für Korrekturen festlegen"</string>
- <string name="auto_cap" msgid="1719746674854628252">"Autom. Groß-/Kleinschr."</string>
+ <string name="auto_cap" msgid="1719746674854628252">"Autom. Groß-/Kleinschreibung"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"Das erste Wort jedes Satzes großschreiben"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Erweiterte Wörterbücher"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Allgemeines Wörterbuch"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"Änderungsvorschläge"</string>
<string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Vorgeschlagene Wörter während des Tippens anzeigen"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Immer anzeigen"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Im Hochformat anzeigen"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Im Hochformat anzeigen"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Nie anzeigen"</string>
- <string name="auto_correction" msgid="4979925752001319458">"Autokorrektur"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"Autokorrektur"</string>
<string name="auto_correction_summary" msgid="5625751551134658006">"Korrektur fehlerhafter Wörter durch Leertaste und Satzzeichen"</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Aus"</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Mäßig"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Stark"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Sehr stark"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Vorschläge für nächstes Wort"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Zur Verbesserung von Vorschlägen vorheriges Wort verwenden"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Vervollständigung für nächstes Wort"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Vorheriges Wort auch für Vervollständigung verwenden"</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"Vorschläge für nächstes Wort"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"Vorschläge anhand des vorherigen Wortes machen"</string>
+ <string name="gesture_input" msgid="826951152254563827">"Bewegungseingabe aktivieren"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"Durch Bewegen der Finger über die Buchstaben ein Wort eingeben"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Spur der Bewegung anzeigen"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"Dyn. unverankerter Vorschlag"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"Vorgeschlagenes Wort bei Bewegung anzeigen"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: gespeichert"</string>
<string name="label_go_key" msgid="1635148082137219148">"Los"</string>
<string name="label_next_key" msgid="362972844525672568">"Weiter"</string>
@@ -95,6 +96,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Eingabe"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Suchen"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Aufzählungspunkt"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Sprache wechseln"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Nächste"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Vorherige"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Umschalttaste aktiviert"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Feststelltaste aktiviert"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Umschalttaste deaktiviert"</string>
@@ -139,7 +143,7 @@
<string name="enable" msgid="5031294444630523247">"Aktivieren"</string>
<string name="not_now" msgid="6172462888202790482">"Später"</string>
<string name="custom_input_style_already_exists" msgid="8008728952215449707">"Der gleiche Eingabestil ist bereits vorhanden: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string>
- <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Modus der Studie zur Benutzerfreundlichkeit"</string>
- <string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Einstellungen für Vibrationsdauer bei Tastendruck"</string>
- <string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Einstellungen für Tonlautstärke bei Tastendruck"</string>
+ <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Studie zur Benutzerfreundlichkeit"</string>
+ <string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Vibrationsdauer bei Tastendruck"</string>
+ <string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Tonlautstärke bei Tastendruck"</string>
</resources>
diff --git a/java/res/values-el/bools.xml b/java/res/values-el/bools.xml
new file mode 100644
index 000000000..840d20c21
--- /dev/null
+++ b/java/res/values-el/bools.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<resources>
+ <!-- Whether this input method should be used as the default for a locale. Override it
+ for supported languages. -->
+ <bool name="im_is_default">true</bool>
+</resources>
diff --git a/java/res/values-el/strings-appname.xml b/java/res/values-el/strings-appname.xml
new file mode 100644
index 000000000..a199655f2
--- /dev/null
+++ b/java/res/values-el/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Πληκτρολόγιο Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Ορθογραφικός έλεγχος Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Ρυθμίσεις πληκτρολογίου Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Ρυθμίσεις ορθογραφικού ελέγχου"</string>
+</resources>
diff --git a/java/res/values-el/strings.xml b/java/res/values-el/strings.xml
index 486346a09..2ce68b5d7 100644
--- a/java/res/values-el/strings.xml
+++ b/java/res/values-el/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Πληκτρολόγιο Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Πληκτρολόγιο Android (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Ρυθμίσεις πληκτρολογίου Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Επιλογές εισόδου"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Ορθογραφικός έλεγχος Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Έρευνα εντολών καταγραφής"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Ορθογραφικός έλεγχος Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Ρυθμίσεις ορθογραφικού ελέγχου"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Αναζήτηση ονομάτων επαφών"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Ο ορθογρ. έλεγχος χρησιμοπ. καταχωρίσεις από τη λίστα επαφών σας"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Δόνηση κατά το πάτημα πλήκτρων"</string>
@@ -34,37 +31,41 @@
<string name="popup_on_keypress" msgid="123894815723512944">"Εμφάνιση με το πάτημα πλήκτρου"</string>
<string name="general_category" msgid="1859088467017573195">"Γενικά"</string>
<string name="correction_category" msgid="2236750915056607613">"Διόρθωση κειμένου"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"Πληκτρολόγηση με κινήσεις"</string>
<string name="misc_category" msgid="6894192814868233453">"Άλλες επιλογές"</string>
<string name="advanced_settings" msgid="362895144495591463">"Σύνθετες ρυθμίσεις"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"Επιλογές για έμπειρους χρήστες"</string>
<string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Άλλη μέθοδος εισόδου"</string>
<string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Το κλειδί αλλαγής γλώσσας καλύπτει και άλλες μεθόδους εισόδου"</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"Κατάργ. κλειδιού γλώσσας"</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"Πλήκτρο εναλλαγής γλώσσας"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Εμφάνιση κατά την ενεργοποίηση πολλών γλωσσών εισόδου"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Χρόνος εξαφ. αναδ. παραθ."</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Χωρίς καθυστέρ."</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Προεπιλογή"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Πρόταση ονομάτων επαφών"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Χρησιμοποιήστε ονόματα από τις Επαφές για προτάσεις και διορθ."</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Ενεργ. επανάλ. διορθώσεων"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Ορισμός προτάσεων για επαναλήψεις διορθώσεων"</string>
<string name="auto_cap" msgid="1719746674854628252">"Αυτόματη χρήση κεφαλαίων"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"Χρήση κεφαλαίου στην πρώτη λέξη κάθε πρότασης"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Πρόσθετα λεξικά"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Κύριο λεξικό"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"Εμφάνιση προτάσεων διόρθωσης"</string>
<string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Προβολή προτεινόμενων λέξεων κατά την πληκτρολόγηση"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Να εμφανίζεται πάντα"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Εμφάνιση σε λειτουργία κατακόρυφης προβολής"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Εμφάνιση σε κατακόρυφο προσανατολισμό"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Πάντα απόκρυψη"</string>
- <string name="auto_correction" msgid="4979925752001319458">"Αυτόματη διόρθωση"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"Αυτόματη διόρθωση"</string>
<string name="auto_correction_summary" msgid="5625751551134658006">"Τα πλήκτρα διαστήματος και στίξης διορθ. αυτόμ. λάθος λέξεις"</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Απενεργοποίηση"</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Μέτρια"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Υψηλή"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Πολύ επιθετική"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Προτάσεις επόμενων λέξεων"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Χρήση προηγούμενης λέξης για τη βελτίωση προτάσεων"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Πρόβλεψη επόμενης λέξης"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Χρησιμοποιήστε, επίσης, την προηγούμενη λέξη για πρόβλεψη"</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"Προτάσεις επόμενων λέξεων"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"Χρήση της προηγούμενης λέξης για τη δημιουργία προτάσεων"</string>
+ <string name="gesture_input" msgid="826951152254563827">"Ενεργ. πληκτρολ. με κινήσεις"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"Εισαγάγετε μια λέξη με ολίσθηση μεταξύ των γραμμάτων"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Εμφάνιση διαδρομής χειρονομίας"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"Προεπισκόπ. δυναμικής κίνησης"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"Εμφάνιση της προτεινόμενης λέξης κατά την κίνηση"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Αποθηκεύτηκε"</string>
<string name="label_go_key" msgid="1635148082137219148">"Μετ."</string>
<string name="label_next_key" msgid="362972844525672568">"Επόμενο"</string>
@@ -95,6 +96,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Πλήκτρο Return"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Αναζήτηση"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Κουκκίδα"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Αλλαγή γλώσσας"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Επόμενο"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Προηγούμενο"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Το Shift ενεργοποιημένο"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Το Caps lock είναι ενεργοποιημένο"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Το Shift είναι απενεργοποιημένο"</string>
diff --git a/java/res/values-en-rGB/strings-appname.xml b/java/res/values-en-rGB/strings-appname.xml
new file mode 100644
index 000000000..ad9e782b0
--- /dev/null
+++ b/java/res/values-en-rGB/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Android keyboard"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Android spell checker"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Android keyboard settings"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Spell checking settings"</string>
+</resources>
diff --git a/java/res/values-en-rGB/strings.xml b/java/res/values-en-rGB/strings.xml
index 502007666..f9af2a739 100644
--- a/java/res/values-en-rGB/strings.xml
+++ b/java/res/values-en-rGB/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Android keyboard"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android keyboard (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Android keyboard settings"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Input options"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Android spell checker"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Research Log Commands"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android spell checker (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Spellchecking settings"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Look up contact names"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Spell checker uses entries from your contact list"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrate on key-press"</string>
@@ -34,37 +31,41 @@
<string name="popup_on_keypress" msgid="123894815723512944">"Pop-up on key press"</string>
<string name="general_category" msgid="1859088467017573195">"General"</string>
<string name="correction_category" msgid="2236750915056607613">"Text correction"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"Gesture typing"</string>
<string name="misc_category" msgid="6894192814868233453">"Other Options"</string>
<string name="advanced_settings" msgid="362895144495591463">"Advanced settings"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"Options for experts"</string>
<string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Switch to other input methods"</string>
<string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Language switch key also covers other input methods"</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"Suppress language switch key"</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"Language switch key"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Show when multiple input languages are enabled"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Key pop-up dismiss delay"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"No delay"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Default"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Suggest Contact names"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Use names from Contacts for suggestions and corrections"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Enable recorrections"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Set suggestions for recorrections"</string>
<string name="auto_cap" msgid="1719746674854628252">"Auto-capitalisation"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"Capitalise the first word of each sentence"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Add-on dictionaries"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Main dictionary"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"Show correction suggestions"</string>
<string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Display suggested words while typing"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Always show"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Show in portrait mode"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Show in portrait mode"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Always hide"</string>
- <string name="auto_correction" msgid="4979925752001319458">"Auto-correction"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"Auto-correction"</string>
<string name="auto_correction_summary" msgid="5625751551134658006">"Correct mistyped words automatically with spacebar and punctuation"</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Off"</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Modest"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Aggressive"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Very aggressive"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Next word suggestions"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Use previous word to improve suggestion"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Next word prediction"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Use previous word also for prediction"</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"Next word suggestions"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"Use the previous word when making suggestions"</string>
+ <string name="gesture_input" msgid="826951152254563827">"Enable gesture typing"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"Input a word by sliding through the letters"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Show gesture trail"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"Dynamic floating preview"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"See the suggested word while gesturing"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Saved"</string>
<string name="label_go_key" msgid="1635148082137219148">"Go"</string>
<string name="label_next_key" msgid="362972844525672568">"Next"</string>
@@ -95,6 +96,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Return"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Search"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Dot"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Switch language"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Next"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Previous"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift enabled"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps lock enabled"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift disabled"</string>
diff --git a/java/res/values-en/bools.xml b/java/res/values-en/bools.xml
index 897f4b3db..c289e5bf3 100644
--- a/java/res/values-en/bools.xml
+++ b/java/res/values-en/bools.xml
@@ -1,22 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-/*
+/*
**
** Copyright 2008, 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
+** 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
+** 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
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<resources>
- <bool name="im_is_default">true</bool>
+ <!-- Whether this input method should be used as the default for a locale. Override it
+ for supported languages. -->
+ <bool name="im_is_default">true</bool>
</resources>
diff --git a/java/res/values-en/whitelist.xml b/java/res/values-en/whitelist.xml
deleted file mode 100644
index 262017916..000000000
--- a/java/res/values-en/whitelist.xml
+++ /dev/null
@@ -1,411 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2011, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-** http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!--
- An entry of the whitelist word should be:
- 1. (int)frequency
- 2. (String)before
- 3. (String)after
- -->
- <string-array name="wordlist_whitelist" translatable="false">
-
- <item>255</item>
- <item>ill</item>
- <item>I\'ll</item>
-
- <!-- TODO: Trim down more entries by removing ones that get auto-corrected by the
- Android keyboard's own typing error correction algorithms. -->
-
- <item>255</item>
- <item>acomodate</item>
- <item>accommodate</item>
-
- <item>255</item>
- <item>aint</item>
- <item>ain\'t</item>
-
- <item>255</item>
- <item>alot</item>
- <item>a lot</item>
-
- <item>255</item>
- <item>andteh</item>
- <item>and the</item>
-
- <item>255</item>
- <item>arent</item>
- <item>aren\'t</item>
-
- <item>255</item>
- <item>bot</item>
- <item>not</item>
-
- <item>255</item>
- <item>bern</item>
- <item>been</item>
-
- <item>255</item>
- <item>bot</item>
- <item>not</item>
-
- <item>255</item>
- <item>bur</item>
- <item>but</item>
-
- <item>255</item>
- <item>cam</item>
- <item>can</item>
-
- <item>255</item>
- <item>cant</item>
- <item>can\'t</item>
-
- <item>255</item>
- <item>dame</item>
- <item>same</item>
-
- <item>255</item>
- <item>didint</item>
- <item>didn\'t</item>
-
- <item>255</item>
- <item>dormer</item>
- <item>former</item>
-
- <item>255</item>
- <item>dud</item>
- <item>did</item>
-
- <item>255</item>
- <item>fay</item>
- <item>day</item>
-
- <item>255</item>
- <item>fife</item>
- <item>five</item>
-
- <item>255</item>
- <item>foo</item>
- <item>for</item>
-
- <item>255</item>
- <item>fora</item>
- <item>for a</item>
-
- <item>255</item>
- <item>galled</item>
- <item>called</item>
-
- <item>255</item>
- <item>goo</item>
- <item>too</item>
-
- <item>255</item>
- <item>hed</item>
- <item>he\'d</item>
-
- <item>255</item>
- <item>hel</item>
- <item>he\'ll</item>
-
- <item>255</item>
- <item>heres</item>
- <item>here\'s</item>
-
- <item>255</item>
- <item>hew</item>
- <item>new</item>
-
- <item>255</item>
- <item>hoe</item>
- <item>how</item>
-
- <item>255</item>
- <item>hoes</item>
- <item>how\'s</item>
-
- <item>255</item>
- <item>howd</item>
- <item>how\'d</item>
-
- <item>255</item>
- <item>howll</item>
- <item>how\'ll</item>
-
- <item>255</item>
- <item>hows</item>
- <item>how\'s</item>
-
- <item>255</item>
- <item>howve</item>
- <item>how\'ve</item>
-
- <item>255</item>
- <item>hum</item>
- <item>him</item>
-
- <item>255</item>
- <item>i</item>
- <item>I</item>
-
- <item>255</item>
- <item>ifs</item>
- <item>its</item>
-
- <item>255</item>
- <item>il</item>
- <item>I\'ll</item>
-
- <item>255</item>
- <item>im</item>
- <item>I\'m</item>
-
- <item>255</item>
- <item>inteh</item>
- <item>in the</item>
-
- <item>255</item>
- <item>itd</item>
- <item>it\'d</item>
-
- <item>255</item>
- <item>itsa</item>
- <item>it\'s a</item>
-
- <item>255</item>
- <item>lets</item>
- <item>let\'s</item>
-
- <item>255</item>
- <item>maam</item>
- <item>ma\'am</item>
-
- <item>255</item>
- <item>manu</item>
- <item>many</item>
-
- <item>255</item>
- <item>mare</item>
- <item>made</item>
-
- <item>255</item>
- <item>mew</item>
- <item>new</item>
-
- <item>255</item>
- <item>mire</item>
- <item>more</item>
-
- <item>255</item>
- <item>moat</item>
- <item>most</item>
-
- <item>255</item>
- <item>mot</item>
- <item>not</item>
-
- <item>255</item>
- <item>mote</item>
- <item>note</item>
-
- <item>255</item>
- <item>motes</item>
- <item>notes</item>
-
- <item>255</item>
- <item>mow</item>
- <item>now</item>
-
- <item>255</item>
- <item>namer</item>
- <item>named</item>
-
- <item>255</item>
- <item>nave</item>
- <item>have</item>
-
- <item>255</item>
- <item>nee</item>
- <item>new</item>
-
- <item>255</item>
- <item>nigh</item>
- <item>high</item>
-
- <item>255</item>
- <item>nit</item>
- <item>not</item>
-
- <item>255</item>
- <item>oft</item>
- <item>off</item>
-
- <item>255</item>
- <item>os</item>
- <item>is</item>
-
- <item>255</item>
- <item>pater</item>
- <item>later</item>
-
- <item>255</item>
- <item>rook</item>
- <item>took</item>
-
- <item>255</item>
- <item>shel</item>
- <item>she\'ll</item>
-
- <item>255</item>
- <item>shouldent</item>
- <item>shouldn\'t</item>
-
- <item>255</item>
- <item>sill</item>
- <item>will</item>
-
- <item>255</item>
- <item>sown</item>
- <item>down</item>
-
- <item>255</item>
- <item>thatd</item>
- <item>that\'d</item>
-
- <item>255</item>
- <item>tine</item>
- <item>time</item>
-
- <item>255</item>
- <item>thong</item>
- <item>thing</item>
-
- <item>255</item>
- <item>tome</item>
- <item>time</item>
-
- <!-- through additional proximity, 'uf' becomes 'of'. 'o' is not next to 'u' so anyone
- typing 'uf' probably meant 'if', but 'of' is much more common and should be left
- higher than 'if', hence the need for this entry. -->
- <item>255</item>
- <item>uf</item>
- <item>if</item>
-
- <!-- 'un' becomes 'UN' because of perfect match ; even if we remove 'UN', then 'un'
- will become 'on' for the same reason as above. So list this here. -->
- <item>255</item>
- <item>un</item>
- <item>in</item>
-
- <!-- does it really make any sense to have the following here? -->
- <item>255</item>
- <item>UnitedStates</item>
- <item>United States</item>
-
- <item>255</item>
- <item>unitedstates</item>
- <item>United States</item>
-
- <item>255</item>
- <item>visavis</item>
- <item>vis-a-vis</item>
-
- <item>255</item>
- <item>wierd</item>
- <item>weird</item>
-
- <item>255</item>
- <item>wel</item>
- <item>we\'ll</item>
-
- <item>255</item>
- <item>wer</item>
- <item>we\'re</item>
-
- <item>255</item>
- <item>whatd</item>
- <item>what\'d</item>
-
- <item>255</item>
- <item>whatm</item>
- <item>what\'m</item>
-
- <item>255</item>
- <item>whatre</item>
- <item>what\'re</item>
-
- <item>255</item>
- <item>whats</item>
- <item>what\'s</item>
-
- <item>255</item>
- <item>whens</item>
- <item>when\'s</item>
-
- <item>255</item>
- <item>whered</item>
- <item>where\'d</item>
-
- <item>255</item>
- <item>wherell</item>
- <item>where\'ll</item>
-
- <item>255</item>
- <item>wheres</item>
- <item>where\'s</item>
-
- <item>255</item>
- <item>wholl</item>
- <item>who\'ll</item>
-
- <item>255</item>
- <item>whove</item>
- <item>who\'ve</item>
-
- <item>255</item>
- <item>whyd</item>
- <item>why\'d</item>
-
- <item>255</item>
- <item>whyll</item>
- <item>why\'ll</item>
-
- <item>255</item>
- <item>whys</item>
- <item>why\'s</item>
-
- <item>255</item>
- <item>whyve</item>
- <item>why\'ve</item>
-
- <item>255</item>
- <item>wont</item>
- <item>won\'t</item>
-
- <item>255</item>
- <item>yall</item>
- <item>y\'all</item>
-
- <item>255</item>
- <item>youd</item>
- <item>you\'d</item>
-
- </string-array>
-</resources>
diff --git a/java/res/values-eo/bools.xml b/java/res/values-eo/bools.xml
new file mode 100644
index 000000000..840d20c21
--- /dev/null
+++ b/java/res/values-eo/bools.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<resources>
+ <!-- Whether this input method should be used as the default for a locale. Override it
+ for supported languages. -->
+ <bool name="im_is_default">true</bool>
+</resources>
diff --git a/java/res/values-es-rUS/strings-appname.xml b/java/res/values-es-rUS/strings-appname.xml
new file mode 100644
index 000000000..5f08afba4
--- /dev/null
+++ b/java/res/values-es-rUS/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Teclado de Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Corrector ortográfico de Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Configuración de teclado de Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Configuración del corrector ortográfico"</string>
+</resources>
diff --git a/java/res/values-es-rUS/strings.xml b/java/res/values-es-rUS/strings.xml
index 34ad0a420..bc9b0c30a 100644
--- a/java/res/values-es-rUS/strings.xml
+++ b/java/res/values-es-rUS/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Teclado de Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Teclado de Android (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Configuración de teclado de Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Opciones de entrada"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Corrector ortográfico de Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Comandos registro invest."</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Corrector ortográfico de Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Configuración del corrector ortográfico"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Buscar nombres contactos"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"El corrector ortográfico usa entradas de tu lista de contactos."</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrar al pulsar teclas"</string>
@@ -34,42 +31,46 @@
<string name="popup_on_keypress" msgid="123894815723512944">"Aviso emergente al pulsar tecla"</string>
<string name="general_category" msgid="1859088467017573195">"General"</string>
<string name="correction_category" msgid="2236750915056607613">"Corrección de texto"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"Escritura gestual"</string>
<string name="misc_category" msgid="6894192814868233453">"Otras opciones"</string>
<string name="advanced_settings" msgid="362895144495591463">"Configuración avanzada"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"Opciones para expertos"</string>
<string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Otros métodos de entrada"</string>
<string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"La tecla de cambio de idioma abarca otros métodos de entrada."</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"Elim. tecla cambio de idioma"</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"Tecla de selección de idioma"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Mostrar cuando se habiliten varios idiomas de entrada"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Retraso en rechazo de alerta de tecla"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Sin demora"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Predeterminada"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Sugerir nombres de contacto"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Usar nombres de los contactos para sugerencias y correcciones"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Activar correcciones"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Establecer sugerencias para realizar correcciones"</string>
<string name="auto_cap" msgid="1719746674854628252">"Mayúsculas automáticas"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"Escribe con mayúscula la primera palabra de cada frase"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Diccionarios complementarios"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Diccionario principal"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"Mostrar sugerencias de correcciones"</string>
<string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Mostrar palabras sugeridas al escribir"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Mostrar siempre"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Mostrar en modo retrato"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Mostrar en modo de retrato"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Ocultar siempre"</string>
- <string name="auto_correction" msgid="4979925752001319458">"Corrección automática"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"Corrección automática"</string>
<string name="auto_correction_summary" msgid="5625751551134658006">"La barra espaciadora y las teclas de puntuación insertan automáticamente la palabra corregida"</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Desactivado"</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Moderado"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Total"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Muy agresivo"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Sugerencias para la palabra siguiente"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Usar la palabra anterior para mejorar las sugerencias"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Predicción de la palabra siguiente"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Usar la palabra anterior también para predicción."</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"Sugerencias para la palabra siguiente"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"Usa la palabra anterior para hacer sugerencias"</string>
+ <string name="gesture_input" msgid="826951152254563827">"Habilitar escritura gestual"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"Ingresa una palabra al deslizarte sobre las letras."</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Mostrar recorrido de gesto"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"Vista previa dinámica flotante"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"Mira la palabra sugerida mientras haces gestos"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: guardada"</string>
<string name="label_go_key" msgid="1635148082137219148">"Ir"</string>
<string name="label_next_key" msgid="362972844525672568">"Siguiente"</string>
<string name="label_previous_key" msgid="1211868118071386787">"Ant."</string>
- <string name="label_done_key" msgid="2441578748772529288">"Hecho"</string>
+ <string name="label_done_key" msgid="2441578748772529288">"Listo"</string>
<string name="label_send_key" msgid="2815056534433717444">"Enviar"</string>
<string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string>
<string name="label_to_symbol_key" msgid="8516904117128967293">"?123"</string>
@@ -95,6 +96,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Volver"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Buscar"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Punto"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Cambiar idioma"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Siguiente"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Anterior"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Se activó el modo Mayúscula."</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Se activó el bloqueo de mayúsculas."</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Se desactivó el modo Mayúscula"</string>
diff --git a/java/res/values-es/bools.xml b/java/res/values-es/bools.xml
index 897f4b3db..c289e5bf3 100644
--- a/java/res/values-es/bools.xml
+++ b/java/res/values-es/bools.xml
@@ -1,22 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-/*
+/*
**
** Copyright 2008, 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
+** 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
+** 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
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<resources>
- <bool name="im_is_default">true</bool>
+ <!-- Whether this input method should be used as the default for a locale. Override it
+ for supported languages. -->
+ <bool name="im_is_default">true</bool>
</resources>
diff --git a/java/res/values-es/strings-appname.xml b/java/res/values-es/strings-appname.xml
new file mode 100644
index 000000000..cce9a176d
--- /dev/null
+++ b/java/res/values-es/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Teclado de Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Corrector ortográfico de Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Ajustes del teclado de Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Ajustes del corrector ortográfico"</string>
+</resources>
diff --git a/java/res/values-es/strings.xml b/java/res/values-es/strings.xml
index 3c60aa6e2..d5196a597 100644
--- a/java/res/values-es/strings.xml
+++ b/java/res/values-es/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Teclado de Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Teclado Android (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Ajustes del teclado de Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Opciones entrada texto"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Corrector de Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Comandos registro investigación"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Corrector de Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Ajustes del corrector ortográfico"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Nombres de contactos"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Añadir nombres de tu lista de contactos al corrector"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrar al pulsar tecla"</string>
@@ -34,37 +31,41 @@
<string name="popup_on_keypress" msgid="123894815723512944">"Ampliar al pulsar tecla"</string>
<string name="general_category" msgid="1859088467017573195">"General"</string>
<string name="correction_category" msgid="2236750915056607613">"Corrección ortográfica"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"Escritura gestual"</string>
<string name="misc_category" msgid="6894192814868233453">"Otras opciones"</string>
<string name="advanced_settings" msgid="362895144495591463">"Ajustes avanzados"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"Opciones para expertos"</string>
<string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Otros métodos de entrada"</string>
<string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"La tecla de cambio de idioma sirve también para otros métodos"</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"Quitar tecla de idioma"</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"Tecla para cambiar de idioma"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Mostrar cuando haya varios idiomas de entrada habilitados"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Retraso al ampliar tecla"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Sin retraso"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Predeterminado"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Sugerir contactos"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Utilizar nombres de contactos para sugerencias y correcciones"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Activar nuevas correcciones"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Establecer sugerencias para nuevas correcciones"</string>
<string name="auto_cap" msgid="1719746674854628252">"Mayúsculas automáticas"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"Poner la primera letra de cada palabra en mayúscula"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Diccionarios complementarios"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Diccionario principal"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"Sugerencias de correcciones"</string>
<string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Muestra las palabras sugeridas mientras se escribe."</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Mostrar siempre"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Mostrar en modo vertical"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Mostrar en modo vertical"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Ocultar siempre"</string>
- <string name="auto_correction" msgid="4979925752001319458">"Autocorrección"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"Autocorrección"</string>
<string name="auto_correction_summary" msgid="5625751551134658006">"Pulsar la tecla de espacio o punto para corregir errores"</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Desactivada"</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Parcial"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Total"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Muy agresiva"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Sugerir siguiente palabra"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Usar palabra anterior para mejorar las sugerencias"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Predecir siguiente palabra"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Utilizar también la palabra anterior para realizar la predicción"</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"Sugerir siguiente palabra"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"Usar la palabra anterior para hacer sugerencias"</string>
+ <string name="gesture_input" msgid="826951152254563827">"Habilitar escritura gestual"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"Introducir una palabra al deslizar el dedo por las letras"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Mostrar recorrido del gesto"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"Vista previa dinámica flotante"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"Ver palabra sugerida al hacer gestos"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: guardada"</string>
<string name="label_go_key" msgid="1635148082137219148">"Ir"</string>
<string name="label_next_key" msgid="362972844525672568">"Sig."</string>
@@ -95,6 +96,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Tecla Intro"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Buscar"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Punto"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Cambiar idioma"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Siguiente"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Anterior"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Mayúsculas habilitadas"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Bloqueo de mayúsculas habilitado"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Mayúsculas inhabilitadas"</string>
@@ -139,7 +143,7 @@
<string name="enable" msgid="5031294444630523247">"Habilitar"</string>
<string name="not_now" msgid="6172462888202790482">"Ahora no"</string>
<string name="custom_input_style_already_exists" msgid="8008728952215449707">"Ya existe el estilo de entrada <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>."</string>
- <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Estudiar usabilidad"</string>
+ <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Modo estudio de usabilidad"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Duración de la vibración al pulsar tecla"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Volumen sonido al pulsar tecla"</string>
</resources>
diff --git a/java/res/values-et/bools.xml b/java/res/values-et/bools.xml
new file mode 100644
index 000000000..840d20c21
--- /dev/null
+++ b/java/res/values-et/bools.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<resources>
+ <!-- Whether this input method should be used as the default for a locale. Override it
+ for supported languages. -->
+ <bool name="im_is_default">true</bool>
+</resources>
diff --git a/java/res/values-et/strings-appname.xml b/java/res/values-et/strings-appname.xml
new file mode 100644
index 000000000..181d597f9
--- /dev/null
+++ b/java/res/values-et/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Androidi klaviatuur"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Androidi õigekirjakontroll"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Androidi klaviatuuri seaded"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Õigekirjakontrolli seaded"</string>
+</resources>
diff --git a/java/res/values-et/strings.xml b/java/res/values-et/strings.xml
index 4a592c36b..4aa31ef2b 100644
--- a/java/res/values-et/strings.xml
+++ b/java/res/values-et/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Androidi klaviatuur"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android-klaviatuur (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Androidi klaviatuuriseaded"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Sisestusvalikud"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Androidi õigekirjakontroll"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Uuringulogi käsud"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Androidi õigekirjakontroll (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Õigekirjakontrolli seaded"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Kontakti nimede kontroll."</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Õigekirjakontroll kasutab teie kontaktisikute loendi sissekandeid"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibreeri klahvivajutusel"</string>
@@ -34,37 +31,41 @@
<string name="popup_on_keypress" msgid="123894815723512944">"Klahvivajutusel kuva hüpik"</string>
<string name="general_category" msgid="1859088467017573195">"Üldine"</string>
<string name="correction_category" msgid="2236750915056607613">"Teksti parandamine"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"Joonistusega sisestamine"</string>
<string name="misc_category" msgid="6894192814868233453">"Muud valikud"</string>
<string name="advanced_settings" msgid="362895144495591463">"Täpsemad seaded"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"Valikud ekspertidele"</string>
<string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Vaheta sisestusmeetodit"</string>
<string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Keelevahetuse võti hõlmab ka muid sisestusmeetodeid"</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"Keela keelevahetuse võti"</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"Keelevahetuse nupp"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Kuva, kui lubatud on mitu sisendkeelt"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Hüpiku loobumisviivitus"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Viivituseta"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Vaikeseade"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Soovita kontaktkirjeid"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Kasuta soovitusteks ja parandusteks nimesid kontaktiloendist"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Uute paranduste lubamine"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Soovituste seadmine uute paranduste jaoks"</string>
<string name="auto_cap" msgid="1719746674854628252">"Automaatne suurtähtede kasutamine"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"Iga lause esimese sõna kirjutamine suure algustähega"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Pistiksõnaraamatud"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Peamine sõnaraamat"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"Kuva parandussoovitusi"</string>
<string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Kuva sisestamise ajal sõnasoovitusi"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Kuva alati"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Kuva portreerežiimis"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Kuva vertikaalrežiimis"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Peida alati"</string>
- <string name="auto_correction" msgid="4979925752001319458">"Automaatparandus"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"Automaatparandus"</string>
<string name="auto_correction_summary" msgid="5625751551134658006">"Tühik ja kirjavahemärgid parand. autom. kirjavigadega sõnad"</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Väljas"</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Mõõdukas"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agressiivne"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Väga agressiivne"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Järgmise sõna soovitused"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Kasuta soovituste täiustamiseks eelmist sõna"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Järgmise sõna ennustus"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Kasuta ennustuseks ka eelmist sõna"</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"Järgmise sõna soovitused"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"Soovituste tegemisel eelmise sõna kasutamine"</string>
+ <string name="gesture_input" msgid="826951152254563827">"Luba joonistusega sisestamine"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"Sõna sisestamine tähtede lohistamisega"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Näita liigutuse jälge"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"Dünaamiline ujuv eelvaade"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"Soovitatud sõna vaatamine joonistusega sisestamise ajal"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : salvestatud"</string>
<string name="label_go_key" msgid="1635148082137219148">"Mine"</string>
<string name="label_next_key" msgid="362972844525672568">"Edasi"</string>
@@ -95,6 +96,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Tagasi"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Otsing"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Punkt"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Keele vahetamine"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Järgmine"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Eelmine"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Tõstuklahv on lubatud"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Suurtähelukk on lubatud"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Tõstuklahv on keelatud"</string>
diff --git a/java/res/values-fa/bools.xml b/java/res/values-fa/bools.xml
new file mode 100644
index 000000000..840d20c21
--- /dev/null
+++ b/java/res/values-fa/bools.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<resources>
+ <!-- Whether this input method should be used as the default for a locale. Override it
+ for supported languages. -->
+ <bool name="im_is_default">true</bool>
+</resources>
diff --git a/java/res/values-fa/strings-appname.xml b/java/res/values-fa/strings-appname.xml
new file mode 100644
index 000000000..ba2a76ff1
--- /dev/null
+++ b/java/res/values-fa/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"صفحه‌کلید Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"غلط‌گیر املای Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"تنظیمات صفحه‌کلید Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"تنظیمات غلط‌‌ گیر املا"</string>
+</resources>
diff --git a/java/res/values-fa/strings.xml b/java/res/values-fa/strings.xml
index bc0e85b77..dacfd1efe 100644
--- a/java/res/values-fa/strings.xml
+++ b/java/res/values-fa/strings.xml
@@ -20,51 +20,52 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"صفحه کلید Android"</string>
- <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"صفحه کلید (Android (AOSP"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"تنظیمات صفحه کلید Android"</string>
- <string name="english_ime_input_options" msgid="3909945612939668554">"گزینه های ورودی"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"غلط‌گیر املای Android"</string>
+ <string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"صفحه‌کلید (Android (AOSP"</string>
+ <string name="english_ime_input_options" msgid="3909945612939668554">"گزینه‌های ورودی"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"فرمان‌های گزارش‌گیری پژوهش"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"غلط‌گیر املای Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"تنظیمات غلط گیری املایی"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"جستجوی نام مخاطبین"</string>
- <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"غلط‌گیر املا از ورودی‌های لیست مخاطبین شما استفاده میکند"</string>
+ <string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"غلط‌گیر املا از ورودی‌های لیست مخاطبین شما استفاده می‌کند"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"لرزش با فشار کلید"</string>
<string name="sound_on_keypress" msgid="6093592297198243644">"صدا با فشار کلید"</string>
<string name="popup_on_keypress" msgid="123894815723512944">"بازشدن با فشار کلید"</string>
<string name="general_category" msgid="1859088467017573195">"کلی"</string>
<string name="correction_category" msgid="2236750915056607613">"تصحیح متن"</string>
- <string name="misc_category" msgid="6894192814868233453">"سایر گزینه ها"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"تایپ حرکتی"</string>
+ <string name="misc_category" msgid="6894192814868233453">"سایر گزینه‌ها"</string>
<string name="advanced_settings" msgid="362895144495591463">"تنظیمات پیشرفته"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"گزینه‌هایی برای حرفه‌ای‌ها"</string>
- <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"تغییر به دیگر روشهای ورودی"</string>
- <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"کلید تغییر زبان، سایر ورودیهای زبان را نیز پوشش می‌دهد"</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"کلید تغییر زبان را فشار دهید"</string>
+ <string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"تغییر به دیگر روش‌های ورودی"</string>
+ <string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"کلید تغییر زبان، سایر ورودی‌های زبان را نیز پوشش می‌دهد"</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"کلید تغییر زبان"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"وقتی چند زبان ورودی فعال است نشان داده شود"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"تأخیر در رد کردن کلید نمایشی"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"بدون تأخیر"</string>
- <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"پیش فرض"</string>
- <string name="use_contacts_dict" msgid="4435317977804180815">"پیشنهاد نام های مخاطب"</string>
+ <string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"پیش‌فرض"</string>
+ <string name="use_contacts_dict" msgid="4435317977804180815">"پیشنهاد نام‌های مخاطب"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"برای پیشنهاد و تصحیح از نام مخاطبین استفاده شود"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"فعال کردن تصحیح مجدد"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"تنظیم پیشنهادات برای تصحیح مجدد"</string>
- <string name="auto_cap" msgid="1719746674854628252">"نوشتن با حروف بزرگ خودکار"</string>
+ <string name="auto_cap" msgid="1719746674854628252">"بزرگ‌کردن خودکار حروف"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"بزرگ‌نویسی کلمه اول هر جمله"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"فرهنگ‌های لغت افزودنی"</string>
<string name="main_dictionary" msgid="4798763781818361168">"فرهنگ‌ لغت اصلی"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"نمایش پیشنهادات تصحیح"</string>
- <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"نمایش واژه های پیشنهادی در حین تایپ"</string>
+ <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"نمایش واژه‌های پیشنهادی در حین تایپ"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"همیشه نمایش داده شود"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"نمایش در حالت عمودی"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"نمایش در حالت عمودی"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"همیشه پنهان شود"</string>
- <string name="auto_correction" msgid="4979925752001319458">"تصحیح خودکار"</string>
- <string name="auto_correction_summary" msgid="5625751551134658006">"کلید فاصله و علائم نگارشی به صورت خودکار کلماتی را که غلط تایپ شده اند تصحیح می کنند"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"تصحیح خودکار"</string>
+ <string name="auto_correction_summary" msgid="5625751551134658006">"کلید فاصله و علائم نگارشی به صورت خودکار کلماتی را که غلط تایپ شده‌اند تصحیح می‌کنند"</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"خاموش"</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"متوسط"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"فعال"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"بسیار پرخاشگرانه"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"پیشنهادات کلمه بعدی"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"برای بهبود پیشنهاد از کلمه قبلی استفاده شود"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"پیش‌بینی کلمه بعدی"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"استفاده از کلمه قبلی برای پیش بینی"</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"پیشنهادات کلمه بعدی"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"استفاده از کلمه قبلی در ایجاد پیشنهادات"</string>
+ <string name="gesture_input" msgid="826951152254563827">"فعال کردن تایپ حرکتی"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"ورود یک کلمه با کشیدن انگشت در میان حروف"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"نمایش نسخه آزمایشی حرکت"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"پیش‌نمایش متحرک پویا"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"مشاهده کلمه پیشنهادی در حین انجام حرکات"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : ذخیره شد"</string>
<string name="label_go_key" msgid="1635148082137219148">"برو"</string>
<string name="label_next_key" msgid="362972844525672568">"بعدی"</string>
@@ -99,6 +100,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Return"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"جستجو"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"نقطه"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"تغییر زبان"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"بعدی"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"قبلی"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift فعال است"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps lock فعال شد"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift غیرفعال است"</string>
@@ -107,26 +111,26 @@
<string name="spoken_description_mode_phone" msgid="6520207943132026264">"حالت تلفن"</string>
<string name="spoken_description_mode_phone_shift" msgid="5499629753962641227">"حالت نمادهای تلفن"</string>
<string name="voice_input" msgid="3583258583521397548">"کلید ورودی صدا"</string>
- <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"در صفحه کلید اصلی"</string>
- <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"در صفحه کلید نمادها"</string>
+ <string name="voice_input_modes_main_keyboard" msgid="3360660341121083174">"در صفحه‌کلید اصلی"</string>
+ <string name="voice_input_modes_symbols_keyboard" msgid="7203213240786084067">"در صفحه‌کلید نمادها"</string>
<string name="voice_input_modes_off" msgid="3745699748218082014">"خاموش"</string>
- <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"میکروفن در صفحه کلید اصلی"</string>
- <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"میکروفن در صفحه کلید نمادها"</string>
+ <string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"میکروفن در صفحه‌کلید اصلی"</string>
+ <string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"میکروفن در صفحه‌کلید نمادها"</string>
<string name="voice_input_modes_summary_off" msgid="63875609591897607">"ورودی صدا غیرفعال است"</string>
- <string name="configure_input_method" msgid="373356270290742459">"پیکربندی روش های ورودی"</string>
- <string name="language_selection_title" msgid="1651299598555326750">"زبان های ورودی"</string>
+ <string name="configure_input_method" msgid="373356270290742459">"پیکربندی روش‌های ورودی"</string>
+ <string name="language_selection_title" msgid="1651299598555326750">"زبان‌های ورودی"</string>
<string name="select_language" msgid="3693815588777926848">"زبان‌های ورودی"</string>
<string name="hint_add_to_dictionary" msgid="573678656946085380">"برای ذخیره دوباره لمس کنید"</string>
<string name="has_dictionary" msgid="6071847973466625007">"دیکشنری موجود است"</string>
<string name="prefs_enable_log" msgid="6620424505072963557">"فعال کردن بازخورد کاربر"</string>
- <string name="prefs_description_log" msgid="5827825607258246003">"با ارسال خودکار آمارهای کاربرد و گزارش های خرابی به Google، به بهبود این ویرایشگر روش ورودی کمک کنید."</string>
- <string name="keyboard_layout" msgid="8451164783510487501">"طرح زمینه صفحه کلید"</string>
- <string name="subtype_en_GB" msgid="88170601942311355">"انگیسی (UK)"</string>
- <string name="subtype_en_US" msgid="6160452336634534239">"انگیسی (US)"</string>
+ <string name="prefs_description_log" msgid="5827825607258246003">"با ارسال خودکار آمارهای کاربرد و گزارش‌های خرابی به Google، به بهبود این ویرایشگر روش ورودی کمک کنید."</string>
+ <string name="keyboard_layout" msgid="8451164783510487501">"طرح زمینه صفحه‌کلید"</string>
+ <string name="subtype_en_GB" msgid="88170601942311355">"انگلیسی (بریتانیا)"</string>
+ <string name="subtype_en_US" msgid="6160452336634534239">"انگلیسی (امریکا)"</string>
<string name="subtype_with_layout_en_GB" msgid="2179097748724725906">"انگلیسی (انگلستان) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_en_US" msgid="1362581347576714579">"انگلیسی (ایالات متحده) (<xliff:g id="LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_no_language" msgid="141420857808801746">"زبانی موجود نیست"</string>
- <string name="subtype_no_language_qwerty" msgid="2956121451616633133">"هیچ کدام از زبانها (QWERTY)"</string>
+ <string name="subtype_no_language_qwerty" msgid="2956121451616633133">"بدون زبان (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="1177848172397202890">"هیچکدام از زبان‌ها (QWERTZ)"</string>
<string name="subtype_no_language_azerty" msgid="8721460968141187394">"هیچکدام از زبان‌ها (AZERTY)"</string>
<string name="subtype_no_language_dvorak" msgid="3122976737669823935">"هیچکدام از زبان‌ها (Dvorak)"</string>
diff --git a/java/res/values-fi/bools.xml b/java/res/values-fi/bools.xml
new file mode 100644
index 000000000..840d20c21
--- /dev/null
+++ b/java/res/values-fi/bools.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<resources>
+ <!-- Whether this input method should be used as the default for a locale. Override it
+ for supported languages. -->
+ <bool name="im_is_default">true</bool>
+</resources>
diff --git a/java/res/values-fi/strings-appname.xml b/java/res/values-fi/strings-appname.xml
new file mode 100644
index 000000000..b2e23d552
--- /dev/null
+++ b/java/res/values-fi/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Android-näppäimistö"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Android-oikoluku"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Android-näppäimistön asetukset"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Oikolukuasetukset"</string>
+</resources>
diff --git a/java/res/values-fi/strings.xml b/java/res/values-fi/strings.xml
index 97002edfe..61763bf0e 100644
--- a/java/res/values-fi/strings.xml
+++ b/java/res/values-fi/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Android-näppäimistö"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android-näppäimistö (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Android-näppäimistön asetukset"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Syöttövalinnat"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Android-oikoluku"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Tutkimuslokin komennot"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android-oikoluku (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Oikoluvun asetukset"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Hae kontaktien nimiä"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Oikeinkirjoituksen tarkistus käyttää kontaktiluettelosi tietoja."</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Käytä värinää näppäimiä painettaessa"</string>
@@ -34,37 +31,41 @@
<string name="popup_on_keypress" msgid="123894815723512944">"Ponnahdusikkuna painalluksella"</string>
<string name="general_category" msgid="1859088467017573195">"Yleinen"</string>
<string name="correction_category" msgid="2236750915056607613">"Tekstin korjaus"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"Piirtokirjoitus"</string>
<string name="misc_category" msgid="6894192814868233453">"Muut vaihtoehdot"</string>
<string name="advanced_settings" msgid="362895144495591463">"Lisäasetukset"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"Valinnat asiantuntijoille"</string>
<string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Käytä toista syöttötapaa"</string>
<string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Kielenvaihtonäppäin kattaa myös muut syöttötavat"</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"Piilota kielenvaihtonäpp."</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"Kielenvaihtonäppäin"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Näytä, kun käytössä on useita syöttökieliä"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Näppäimen hylkäysviive"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Ei viivettä"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Oletus"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Ehdota yhteystietojen nimiä"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Käytä yhteystietojen nimiä ehdotuksissa ja korjauksissa"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Ota korjaukset käyttöön"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Aseta korjausehdotuksia"</string>
<string name="auto_cap" msgid="1719746674854628252">"Automaattiset isot kirjaimet"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"Kirjoita jokaisen lauseen ensimmäinen sana isolla alkukirjaimella"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Lisäsanakirjat"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Pääsanakirja"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"Näytä korjausehdotukset"</string>
<string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Näytä sanaehdotukset kirjoitettaessa"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Näytä aina"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Näytä pystysuunnassa"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Näytä pystyasennossa"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Piilota aina"</string>
- <string name="auto_correction" msgid="4979925752001319458">"Autom. korjaus"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"Autom. korjaus"</string>
<string name="auto_correction_summary" msgid="5625751551134658006">"Välilyönnit ja välimerkit korjaavat väärinkirjoitetut sanat automaattisesti"</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Älä käytä"</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Osittainen"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Täysi"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Hyvin aggressiivinen"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Seuraavan sanan ehdotukset"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Paranna ehdotuksia aiempien sanojen avulla"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Seuraavan sanan ennakointi"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Käytä edellistä sanaa myös ennakointiin"</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"Seuraavan sanan ehdotukset"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"Käytä edellistä sanaa ehdotuksien perusteena"</string>
+ <string name="gesture_input" msgid="826951152254563827">"Ota piirtokirjoitus käyttöön"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"Syötä sana piirtämällä kirjaimet sormellasi"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Näytä eleen jälki"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"Dynaaminen kelluva esikatselu"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"Näytä ehdotettu sana piirron aikana"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Tallennettu"</string>
<string name="label_go_key" msgid="1635148082137219148">"Siirry"</string>
<string name="label_next_key" msgid="362972844525672568">"Seur."</string>
@@ -95,6 +96,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Enter"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Haku"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Piste"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Vaihda kieli"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Seuraava"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Edellinen"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Vaihto päällä"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps lock päällä"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Vaihto pois käytöstä"</string>
diff --git a/java/res/values-fr/bools.xml b/java/res/values-fr/bools.xml
index 897f4b3db..c289e5bf3 100644
--- a/java/res/values-fr/bools.xml
+++ b/java/res/values-fr/bools.xml
@@ -1,22 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-/*
+/*
**
** Copyright 2008, 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
+** 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
+** 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
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<resources>
- <bool name="im_is_default">true</bool>
+ <!-- Whether this input method should be used as the default for a locale. Override it
+ for supported languages. -->
+ <bool name="im_is_default">true</bool>
</resources>
diff --git a/java/res/values-fr/donottranslate.xml b/java/res/values-fr/donottranslate.xml
index 8cf2516a6..5288bd7d1 100644
--- a/java/res/values-fr/donottranslate.xml
+++ b/java/res/values-fr/donottranslate.xml
@@ -25,5 +25,7 @@
<!-- Symbols that should promote magic spaces into real space -->
<string name="phantom_space_promoting_symbols">;:!?([*&amp;@{&lt;&gt;+=|</string>
<!-- Symbols that do NOT separate words -->
- <string name="symbols_excluded_from_word_separators">\'</string>
+ <!-- Note that this is identical to the default value, but since the above ones are different
+ and those variables only make sense together, this is kept here for readability. -->
+ <string name="symbols_excluded_from_word_separators">\'-</string>
</resources>
diff --git a/java/res/values-fr/strings-appname.xml b/java/res/values-fr/strings-appname.xml
new file mode 100644
index 000000000..8e2a6e088
--- /dev/null
+++ b/java/res/values-fr/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Clavier Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Correcteur orthographique Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Paramètres du clavier Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Paramètres du correcteur orthographique"</string>
+</resources>
diff --git a/java/res/values-fr/strings.xml b/java/res/values-fr/strings.xml
index 285d2226c..58393b041 100644
--- a/java/res/values-fr/strings.xml
+++ b/java/res/values-fr/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Clavier Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Clavier Android (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Paramètres du clavier Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Options de saisie"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Correcteur orthographique Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Commandes journaux rech."</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Correcteur orthographique Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Paramètre du correcteur orthographique"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Rechercher noms contacts"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Correcteur orthographique utilise entrées de liste de contacts."</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrer à chaque touche"</string>
@@ -34,37 +31,41 @@
<string name="popup_on_keypress" msgid="123894815723512944">"Agrandir les caractères"</string>
<string name="general_category" msgid="1859088467017573195">"Général"</string>
<string name="correction_category" msgid="2236750915056607613">"Correction du texte"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"Saisie gestuelle"</string>
<string name="misc_category" msgid="6894192814868233453">"Autres options"</string>
<string name="advanced_settings" msgid="362895144495591463">"Paramètres avancés"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"Options destinées aux experts"</string>
<string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Autres modes de saisie"</string>
<string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"La touche de sélection de langue couvre d\'autres modes de saisie."</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"Suppr. touche sélect. langue"</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"Touche de sélection de langue"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Afficher lorsque plusieurs langues de saisie sont activées"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Masquer touche agrandie"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Sans délai"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Par défaut"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Proposer noms de contacts"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Utiliser des noms de contacts pour les suggestions et corrections"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Activer la recorrection"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Définir des suggestions de recorrection"</string>
<string name="auto_cap" msgid="1719746674854628252">"Majuscules auto"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"Majuscule au premier mot de chaque phrase"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Dictionnaires complémentaires"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Dictionnaire principal"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"Afficher les suggestions de correction"</string>
<string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Afficher les suggestions de terme lors de la saisie"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Toujours afficher"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Afficher en mode Portrait"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Afficher en mode Portrait"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Toujours masquer"</string>
- <string name="auto_correction" msgid="4979925752001319458">"Correction automatique"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"Correction auto"</string>
<string name="auto_correction_summary" msgid="5625751551134658006">"Corriger autom. orthographe (pression sur barre espace/signes ponctuation)"</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Désactiver"</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Simple"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Proactive"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Très exigeante"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Suggestions pour le mot suivant"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Améliorer les suggestions grâce au mot précédent"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Prédiction du mot suivant"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Utiliser le mot précédent pour la prédiction"</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"Suggestions pour le mot suivant"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"Utiliser le mot précédent pour les suggestions"</string>
+ <string name="gesture_input" msgid="826951152254563827">"Activer la saisie gestuelle"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"Saisir un mot en faisant glisser le doigt sur les lettres"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Afficher le tracé du geste"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"Aperçu flottant dynamique"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"Afficher le mot suggéré lors des gestes"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : enregistré"</string>
<string name="label_go_key" msgid="1635148082137219148">"OK"</string>
<string name="label_next_key" msgid="362972844525672568">"Suiv."</string>
@@ -95,6 +96,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Entrée"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Rechercher"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Point"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Changer de langue"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Touche suivante"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Touche précédente"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Touche Maj activée"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Verrouillage des majuscules activé"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Touche Maj désactivée"</string>
diff --git a/java/res/values-hi/bools.xml b/java/res/values-hi/bools.xml
new file mode 100644
index 000000000..840d20c21
--- /dev/null
+++ b/java/res/values-hi/bools.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<resources>
+ <!-- Whether this input method should be used as the default for a locale. Override it
+ for supported languages. -->
+ <bool name="im_is_default">true</bool>
+</resources>
diff --git a/java/res/values-hi/strings-appname.xml b/java/res/values-hi/strings-appname.xml
new file mode 100644
index 000000000..02283af9a
--- /dev/null
+++ b/java/res/values-hi/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Android कीबोर्ड"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Android वर्तनी परीक्षक"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Android कीबोर्ड सेटिंग"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"वर्तनी जांच सेटिंग"</string>
+</resources>
diff --git a/java/res/values-hi/strings.xml b/java/res/values-hi/strings.xml
index 2b1808460..7e463f758 100644
--- a/java/res/values-hi/strings.xml
+++ b/java/res/values-hi/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Android कीबोर्ड"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android कीबोर्ड (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Android कीबोर्ड सेटिंग"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"इनपुट विकल्‍प"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Android वर्तनी परीक्षक"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"लॉग आदेशों का शोध करें"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android वर्तनी परीक्षक (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"वर्तनी जांच सेटिंग"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"संपर्क नामों को खोजें"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"वर्तनी परीक्षक आपकी संपर्क सूची की प्रविष्टियों का उपयोग करता है"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"कुंजी दबाने पर कंपन करता है"</string>
@@ -34,37 +31,41 @@
<string name="popup_on_keypress" msgid="123894815723512944">"कुंजी दबाने पर पॉपअप दिखाएं"</string>
<string name="general_category" msgid="1859088467017573195">"सामान्य"</string>
<string name="correction_category" msgid="2236750915056607613">"पाठ सुधार"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"जेस्चर लिखना"</string>
<string name="misc_category" msgid="6894192814868233453">"अन्य विकल्प"</string>
<string name="advanced_settings" msgid="362895144495591463">"उन्नत सेटिंग"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"विशेषज्ञों के लिए विकल्‍प"</string>
<string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"अन्‍य इनपुट पद्धतियों पर जाएं"</string>
<string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"भाषा स्‍विच कुंजी में अन्‍य इनपुट पद्धतियां भी शामिल हैं"</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"भाषा स्‍विच कुंजी रोकें"</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"भाषा स्विच कुंजी"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"एकाधिक इनपुट भाषाएं सक्षम होने पर दिखाएं"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"कुंजी पॉपअप खारिज़ विलंब"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"कोई विलंब नहीं"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"डिफ़ॉल्ट"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"संपर्क नाम सुझाएं"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"सुझाव और सुधार के लिए संपर्क से नामों का उपयोग करें"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"पुन: सुधार सक्षम करें"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"पुन: सुधार के लि‍ए सुझाव सेट करें"</string>
<string name="auto_cap" msgid="1719746674854628252">"स्‍वत: अक्षर बड़े करना"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"प्रत्येक वाक्य के पहले शब्द को बड़ा लिखें"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"एड-ऑन डिक्शनरी"</string>
<string name="main_dictionary" msgid="4798763781818361168">"मुख्‍य डिक्‍शनरी"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"सुधार सुझाव दिखाएं"</string>
- <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"लिखते समय सुझाए गए शब्‍द प्रदर्शित करें"</string>
+ <string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"लिखते समय सुझाए गए शब्‍द दिखाएं"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"हमेशा दिखाएं"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"र्पोट्रेट मोड पर प्रदर्शित करें"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"पोर्ट्रेट मोड में दिखाएं"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"हमेशा छुपाएं"</string>
- <string name="auto_correction" msgid="4979925752001319458">"स्‍वत: सुधार"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"स्‍वत: सुधार"</string>
<string name="auto_correction_summary" msgid="5625751551134658006">"Spacebar और विराम चिह्न गलत लिखे गए शब्‍दों को स्‍वचालित रूप से ठीक करते हैं"</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"बंद"</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"साधारण"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"तीव्र"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"बहुत तीव्र"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"अगला शब्‍द सुझाव"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"सुझावों को बेहतर बनाने के लिए पिछले शब्‍द का उपयोग करें"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"अगला शब्‍द पूर्वानुमान"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"पूर्वानुमान के लिए पिछले शब्द का उपयोग करें"</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"अगले शब्द के सुझाव"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"सुझाव बनाने में पिछले शब्द का उपयोग करें"</string>
+ <string name="gesture_input" msgid="826951152254563827">"जेस्‍चर लिखना सक्षम करें"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"अक्षरों से स्लाइड करते हुए शब्द इनपुट करें"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"जेस्चर ट्रेल दिखाएं"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"गतिशील फ़्लोटिंग पूर्वावलोकन"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"जेस्‍चर बनाते समय सुझाया गया शब्द देखें"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: सहेजा गया"</string>
<string name="label_go_key" msgid="1635148082137219148">"जाएं"</string>
<string name="label_next_key" msgid="362972844525672568">"अगला"</string>
@@ -74,7 +75,7 @@
<string name="label_to_alpha_key" msgid="4793983863798817523">"कखग"</string>
<string name="label_to_symbol_key" msgid="8516904117128967293">"?१२३"</string>
<string name="label_to_symbol_with_microphone_key" msgid="9035925553010061906">"१२३"</string>
- <string name="label_pause_key" msgid="181098308428035340">"रोकें"</string>
+ <string name="label_pause_key" msgid="181098308428035340">"पॉज़ करें"</string>
<string name="label_wait_key" msgid="6402152600878093134">"प्रतीक्षा करें"</string>
<string name="spoken_use_headphones" msgid="896961781287283493">"ज़ोर से बोली गई पासवर्ड कुंजियां सुनने के लिए हेडसेट प्‍लग इन करें."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"वर्तमान पाठ %s है"</string>
@@ -95,6 +96,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"रिटर्न"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"खोजें"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"बिंदु"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"भाषा स्विच करें"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"अगला"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"पिछला"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift सक्षम किया गया"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps lock सक्षम किया गया"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift अक्षम किया गया"</string>
@@ -115,7 +119,7 @@
<string name="hint_add_to_dictionary" msgid="573678656946085380">"सहेजने के लिए पुन: स्‍पर्श करें"</string>
<string name="has_dictionary" msgid="6071847973466625007">"शब्‍दकोश उपलब्‍ध है"</string>
<string name="prefs_enable_log" msgid="6620424505072963557">"उपयोगकर्ता फ़ीडबैक सक्षम करें"</string>
- <string name="prefs_description_log" msgid="5827825607258246003">"उपयोग के आंकड़े और क्रैश रिपोर्ट Google को स्वचालित रूप से भेज कर इस इनपुट पद्धति संपादक को बेहतर बनाने में सहायता करें."</string>
+ <string name="prefs_description_log" msgid="5827825607258246003">"उपयोग के आंकड़े और क्रैश रिपोर्ट Google को अपने आप भेज कर इस इनपुट पद्धति संपादक को बेहतर बनाने में सहायता करें."</string>
<string name="keyboard_layout" msgid="8451164783510487501">"कीबोर्ड थीम"</string>
<string name="subtype_en_GB" msgid="88170601942311355">"अंग्रेज़ी (यूके)"</string>
<string name="subtype_en_US" msgid="6160452336634534239">"अंग्रेज़ी (यूएस)"</string>
diff --git a/java/res/values-hr/bools.xml b/java/res/values-hr/bools.xml
new file mode 100644
index 000000000..840d20c21
--- /dev/null
+++ b/java/res/values-hr/bools.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<resources>
+ <!-- Whether this input method should be used as the default for a locale. Override it
+ for supported languages. -->
+ <bool name="im_is_default">true</bool>
+</resources>
diff --git a/java/res/values-hr/strings-appname.xml b/java/res/values-hr/strings-appname.xml
new file mode 100644
index 000000000..69fa2e9a1
--- /dev/null
+++ b/java/res/values-hr/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Androidova tipkovnica"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Androidova provjera pravopisa"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Postavke Androidove tipkovnice"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Postavke provjere pravopisa"</string>
+</resources>
diff --git a/java/res/values-hr/strings.xml b/java/res/values-hr/strings.xml
index a59d4698f..bae1fc202 100644
--- a/java/res/values-hr/strings.xml
+++ b/java/res/values-hr/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Android tipkovnica"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android tipkovnica (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Postavke tipkovnice za Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Opcije ulaza"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Androidova provjera pravopisa"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Istraživanje naredbi dnevnika"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Androidova provjera pravopisa (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Postavke provjere pravopisa"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Potražite imena kontakata"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Provjera pravopisa upotrebljava unose iz vašeg popisa kontakata"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibracija pri pritisku na tipku"</string>
@@ -34,37 +31,41 @@
<string name="popup_on_keypress" msgid="123894815723512944">"Povećanja na pritisak tipke"</string>
<string name="general_category" msgid="1859088467017573195">"Općenito"</string>
<string name="correction_category" msgid="2236750915056607613">"Ispravak teksta"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"Pisanje kretnjama"</string>
<string name="misc_category" msgid="6894192814868233453">"Ostale opcije"</string>
<string name="advanced_settings" msgid="362895144495591463">"Napredne postavke"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"Opcije za stručnjake"</string>
<string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Prebaci na druge unose"</string>
<string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Tipka za prebacivanje jezika pokriva i druge načine unosa"</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"Spriječi tipku za jezike"</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"Tipka za izmjenjivanje jezika"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Prikaži kada je omogućen unos na više jezika"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Odgoda prikaza tipki"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Bez odgode"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Zadano"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Predlaži imena kontakata"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Upotreba imena iz Kontakata za prijedloge i ispravke"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Omogući ponovne ispravke"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Postavite prijedloge za ponovne ispravke"</string>
<string name="auto_cap" msgid="1719746674854628252">"Automatsko pisanje velikih slova"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"Napiši velikim slovom prvu riječ svake rečenice"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Rječnici-dodaci"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Glavni rječnik"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"Pokaži prijedloge ispravka"</string>
<string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Prikazivanje predloženih riječi prilikom upisivanja"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Uvijek prikaži"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Prikaži u portretnom načinu"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Prikaži u portretnom načinu"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Uvijek sakrij"</string>
- <string name="auto_correction" msgid="4979925752001319458">"Samoispravak"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"Automatski ispravak"</string>
<string name="auto_correction_summary" msgid="5625751551134658006">"Razmak i interpunkcija automatski ispravljaju krive riječi"</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Isključeno"</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Skromno"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agresivno"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Vrlo agresivno"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Prijedlozi za sljedeću riječ"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Upotrijebi prethodnu riječ radi poboljšanja prijedloga"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Predviđanje sljedeće riječi"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Upotrijebite prethodnu riječ i za predviđanje"</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"Prijedlozi za sljedeću riječ"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"Koristi se prethodnom riječi u izradi prijedloga"</string>
+ <string name="gesture_input" msgid="826951152254563827">"Omogući pisanje kretnjama"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"Unesi riječ klizanjem preko slova"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Prikaži trag pokreta"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"Dinamički plutajući pregled"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"Vidi predloženu riječ tijekom pokreta"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Spremljeno"</string>
<string name="label_go_key" msgid="1635148082137219148">"Idi"</string>
<string name="label_next_key" msgid="362972844525672568">"Dalje"</string>
@@ -95,6 +96,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Enter"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Pretraživanje"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Točka"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Promijeni jezik"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Sljedeće"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Prethodno"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Omogućena tipka Shift"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Omogućeno pisanje velikih slova"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Onemogućena tipka Shift"</string>
diff --git a/java/res/values-hu/bools.xml b/java/res/values-hu/bools.xml
new file mode 100644
index 000000000..840d20c21
--- /dev/null
+++ b/java/res/values-hu/bools.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<resources>
+ <!-- Whether this input method should be used as the default for a locale. Override it
+ for supported languages. -->
+ <bool name="im_is_default">true</bool>
+</resources>
diff --git a/java/res/values-hu/strings-appname.xml b/java/res/values-hu/strings-appname.xml
new file mode 100644
index 000000000..ad511cfbc
--- /dev/null
+++ b/java/res/values-hu/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Android-billentyűzet"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Androidos helyesírás-ellenőrző"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Android-billentyűzet beállításai"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"A helyesírás-ellenőrzés beállításai"</string>
+</resources>
diff --git a/java/res/values-hu/strings.xml b/java/res/values-hu/strings.xml
index 0eac1a95b..d1b715796 100644
--- a/java/res/values-hu/strings.xml
+++ b/java/res/values-hu/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Android-billentyűzet"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android-billentyűzet (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Android billentyűzetbeállítások"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Beviteli beállítások"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Androidos helyesírás-ellenőrző"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Naplózási parancsok"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Androidos helyesírás-ellenőrző (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Helyesírás-ellenőrzés beállításai"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Névjegyek keresése"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"A helyesírás-ellenőrző használja a névjegyek bejegyzéseit"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Rezgés billentyű megnyomása esetén"</string>
@@ -34,37 +31,41 @@
<string name="popup_on_keypress" msgid="123894815723512944">"Legyen nagyobb billentyű lenyomásakor"</string>
<string name="general_category" msgid="1859088467017573195">"Általános"</string>
<string name="correction_category" msgid="2236750915056607613">"Szövegjavítás"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"Kézmozdulatokkal történő gépelés"</string>
<string name="misc_category" msgid="6894192814868233453">"Egyéb beállítások"</string>
<string name="advanced_settings" msgid="362895144495591463">"Speciális beállítások"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"Beállítások gyakorlott felhasználóknak"</string>
<string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Váltás más beviteli módra"</string>
<string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"A nyelvkapcsoló gomb egyéb beviteli módokat is tartalmaz"</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"A nyelvkapcsoló elrejtése"</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"A nyelvkapcsoló"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Megjelenítés, ha több beviteli nyelv engedélyezett"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Gombeltüntetés késése"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Nincs késés"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Alapbeállítás"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Javasolt névjegyek"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"A névjegyek használata a javaslatokhoz és javításokhoz"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Újbóli javítás engedélyezése"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Javaslatok beállítása az újbóli javításokhoz"</string>
<string name="auto_cap" msgid="1719746674854628252">"Automatikusan nagy kezdőbetű"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"Minden mondat első szava nagybetűvel"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Bővítmények: szótárak"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Fő szótár"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"Javítási ajánlások megjelenítése"</string>
<string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"A javasolt szavak megjelenítése gépelés közben"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Mindig látszik"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Megjelenítés álló tájolásban"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Megjelenítés álló tájolásban"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Mindig rejtve"</string>
- <string name="auto_correction" msgid="4979925752001319458">"Automatikus javítás"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"Automatikus javítás"</string>
<string name="auto_correction_summary" msgid="5625751551134658006">"Szóköz és központozás automatikusan javítja az elgépelést"</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Ki"</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Mérsékelt"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agresszív"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Nagyon agresszív"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Következő szóra vonatkozó javaslatok"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Javaslatok fejlesztése az előző szó használatával"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Következő szó előrejelzése"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Az előző szó használata a prediktív bevitelhez is"</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"Következő szóra vonatkozó javaslatok"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"Az előző szó felhasználása a javaslatoknál"</string>
+ <string name="gesture_input" msgid="826951152254563827">"Kézmozdulatokkal gépelés"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"Szó beírása úgy, hogy ujjait végigcsúsztatja a betűkön"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Mozdulat irányának mutatása"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"Dinamikus lebegő előnézet"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"A javasolt szó megtekintése kézmozdulat közben"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : mentve"</string>
<string name="label_go_key" msgid="1635148082137219148">"Ugrás"</string>
<string name="label_next_key" msgid="362972844525672568">"Tovább"</string>
@@ -95,6 +96,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Enter"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Keresés"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Pont"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Nyelvek felcserélése"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Következő"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Előző"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift bekapcsolva"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps lock bekapcsolva"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift kikapcsolva"</string>
diff --git a/java/res/values-in/bools.xml b/java/res/values-in/bools.xml
new file mode 100644
index 000000000..840d20c21
--- /dev/null
+++ b/java/res/values-in/bools.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<resources>
+ <!-- Whether this input method should be used as the default for a locale. Override it
+ for supported languages. -->
+ <bool name="im_is_default">true</bool>
+</resources>
diff --git a/java/res/values-in/strings-appname.xml b/java/res/values-in/strings-appname.xml
new file mode 100644
index 000000000..283d69247
--- /dev/null
+++ b/java/res/values-in/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Keyboard Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Pemeriksa ejaan Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Setelan keyboard Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Setelan pemeriksa ejaan"</string>
+</resources>
diff --git a/java/res/values-in/strings.xml b/java/res/values-in/strings.xml
index e0e92b5bd..b8a957314 100644
--- a/java/res/values-in/strings.xml
+++ b/java/res/values-in/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Keyboard Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Keyboard Android (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Setelan keyboard Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Opsi masukan"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Pemeriksa ejaan Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Riset Perintah Log"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Pemeriksa ejaan Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Setelan pemeriksaan ejaan"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Cari nama kontak"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Pemeriksa ejaan menggunakan entri dari daftar kontak Anda"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Getar jika tombol ditekan"</string>
@@ -34,37 +31,41 @@
<string name="popup_on_keypress" msgid="123894815723512944">"Muncul saat tombol ditekan"</string>
<string name="general_category" msgid="1859088467017573195">"Umum"</string>
<string name="correction_category" msgid="2236750915056607613">"Koreksi teks"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"Pengetikan isyarat"</string>
<string name="misc_category" msgid="6894192814868233453">"Opsi lain"</string>
<string name="advanced_settings" msgid="362895144495591463">"Setelan lanjutan"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"Opsi untuk ahli"</string>
<string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Beralih ke metode masukan lain"</string>
<string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Tombol beralih bahasa juga mencakup metode masukan lain"</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"Redam tombol alih bahasa"</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"Tombol pengalih bahasa"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Tampilkan saat beberapa bahasa masukan diaktifkan"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Tundaan singkir munculan kunci"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Tanpa penundaan"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Default"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Sarankan nama Kontak"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Menggunakan nama dari Kontak untuk saran dan koreksi"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Aktifkan koreksi ulang"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Setel saran untuk koreksi ulang"</string>
<string name="auto_cap" msgid="1719746674854628252">"Kapitalisasi otomatis"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"Kapitalisasi kata pertama di setiap kalimat"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Kamus pengaya"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Kamus utama"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"Tampilkan saran koreksi"</string>
<string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Tampilkan kata yang disarankan ketika mengetik"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Selalu tampilkan"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Tampilkan pada mode potret"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Tampilkan dalam mode potret"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Selalu sembunyikan"</string>
- <string name="auto_correction" msgid="4979925752001319458">"Koreksi otomatis"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"Koreksi otomatis"</string>
<string name="auto_correction_summary" msgid="5625751551134658006">"Bilah spasi dan tanda baca secara otomatis dikoreksi pada kata yang salah ketik"</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Mati"</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Sederhana"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agresif"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Sangat agresif"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Saran kata berikutnya"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Gunakan kata sebelumnya untuk meningkatkan saran"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Prediksi kata berikutnya"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Gunakan kata sebelumnya juga untuk prediksi"</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"Saran kata berikutnya"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"Gunakan kata sebelumnya dalam membuat saran"</string>
+ <string name="gesture_input" msgid="826951152254563827">"Aktifkan pengetikan isyarat"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"Masukkan kata dengan menggeser huruf ke kanan/kiri"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Tampilkan jalur isyarat"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"Pratinjau mengambang dinamis"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"Lihat kata yang disarankan saat melakukan isyarat"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Telah disimpan"</string>
<string name="label_go_key" msgid="1635148082137219148">"Buka"</string>
<string name="label_next_key" msgid="362972844525672568">"Berikutnya"</string>
@@ -95,6 +96,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Kembali"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Telusuri"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Titik"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Ganti bahasa"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Berikutnya"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Sebelumnya"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift diaktifkan"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps lock diaktifkan"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift dinonaktifkan"</string>
@@ -139,7 +143,7 @@
<string name="enable" msgid="5031294444630523247">"Aktifkan"</string>
<string name="not_now" msgid="6172462888202790482">"Nanti saja"</string>
<string name="custom_input_style_already_exists" msgid="8008728952215449707">"Sudah ada gaya masukan yang sama: <xliff:g id="INPUT_STYLE_NAME">%s</xliff:g>"</string>
- <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Modus studi daya guna"</string>
+ <string name="prefs_usability_study_mode" msgid="1261130555134595254">"Mode studi daya guna"</string>
<string name="prefs_keypress_vibration_duration_settings" msgid="1829950405285211668">"Setelan durasi getaran saat tombol ditekan"</string>
<string name="prefs_keypress_sound_volume_settings" msgid="5875933757082305040">"Setelan volume suara saat tombol ditekan"</string>
</resources>
diff --git a/java/res/values-is/bools.xml b/java/res/values-is/bools.xml
new file mode 100644
index 000000000..840d20c21
--- /dev/null
+++ b/java/res/values-is/bools.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<resources>
+ <!-- Whether this input method should be used as the default for a locale. Override it
+ for supported languages. -->
+ <bool name="im_is_default">true</bool>
+</resources>
diff --git a/java/res/values-is/strings.xml b/java/res/values-is/strings.xml
new file mode 100644
index 000000000..a53e727f3
--- /dev/null
+++ b/java/res/values-is/strings.xml
@@ -0,0 +1,269 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for aosp_android_keyboard_ime_name (7877134937939182296) -->
+ <skip />
+ <!-- no translation found for english_ime_input_options (3909945612939668554) -->
+ <skip />
+ <!-- no translation found for english_ime_research_log (8492602295696577851) -->
+ <skip />
+ <!-- no translation found for aosp_spell_checker_service_name (6985142605330377819) -->
+ <skip />
+ <!-- no translation found for use_contacts_for_spellchecking_option_title (5374120998125353898) -->
+ <skip />
+ <!-- no translation found for use_contacts_for_spellchecking_option_summary (8754413382543307713) -->
+ <skip />
+ <!-- no translation found for vibrate_on_keypress (5258079494276955460) -->
+ <skip />
+ <!-- no translation found for sound_on_keypress (6093592297198243644) -->
+ <skip />
+ <!-- no translation found for popup_on_keypress (123894815723512944) -->
+ <skip />
+ <!-- no translation found for general_category (1859088467017573195) -->
+ <skip />
+ <!-- no translation found for correction_category (2236750915056607613) -->
+ <skip />
+ <!-- no translation found for gesture_typing_category (497263612130532630) -->
+ <skip />
+ <!-- no translation found for misc_category (6894192814868233453) -->
+ <skip />
+ <!-- no translation found for advanced_settings (362895144495591463) -->
+ <skip />
+ <!-- no translation found for advanced_settings_summary (4487980456152830271) -->
+ <skip />
+ <!-- no translation found for include_other_imes_in_language_switch_list (4533689960308565519) -->
+ <skip />
+ <!-- no translation found for include_other_imes_in_language_switch_list_summary (840637129103317635) -->
+ <skip />
+ <!-- no translation found for show_language_switch_key (5915478828318774384) -->
+ <skip />
+ <!-- no translation found for show_language_switch_key_summary (7343403647474265713) -->
+ <skip />
+ <!-- no translation found for key_preview_popup_dismiss_delay (6213164897443068248) -->
+ <skip />
+ <!-- no translation found for key_preview_popup_dismiss_no_delay (2096123151571458064) -->
+ <skip />
+ <!-- no translation found for key_preview_popup_dismiss_default_delay (2166964333903906734) -->
+ <skip />
+ <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+ <skip />
+ <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+ <skip />
+ <!-- no translation found for auto_cap (1719746674854628252) -->
+ <skip />
+ <!-- no translation found for auto_cap_summary (7934452761022946874) -->
+ <skip />
+ <!-- no translation found for configure_dictionaries_title (4238652338556902049) -->
+ <skip />
+ <!-- no translation found for main_dictionary (4798763781818361168) -->
+ <skip />
+ <!-- no translation found for prefs_show_suggestions (8026799663445531637) -->
+ <skip />
+ <!-- no translation found for prefs_show_suggestions_summary (1583132279498502825) -->
+ <skip />
+ <!-- no translation found for prefs_suggestion_visibility_show_name (3219916594067551303) -->
+ <skip />
+ <!-- no translation found for prefs_suggestion_visibility_show_only_portrait_name (3859783767435239118) -->
+ <skip />
+ <!-- no translation found for prefs_suggestion_visibility_hide_name (6309143926422234673) -->
+ <skip />
+ <!-- no translation found for auto_correction (7630720885194996950) -->
+ <skip />
+ <!-- no translation found for auto_correction_summary (5625751551134658006) -->
+ <skip />
+ <!-- no translation found for auto_correction_threshold_mode_off (8470882665417944026) -->
+ <skip />
+ <!-- no translation found for auto_correction_threshold_mode_modest (8788366690620799097) -->
+ <skip />
+ <!-- no translation found for auto_correction_threshold_mode_aggeressive (3524029103734923819) -->
+ <skip />
+ <!-- no translation found for auto_correction_threshold_mode_very_aggeressive (3386782235540547678) -->
+ <skip />
+ <!-- no translation found for bigram_prediction (1084449187723948550) -->
+ <skip />
+ <!-- no translation found for bigram_prediction_summary (3896362682751109677) -->
+ <skip />
+ <!-- no translation found for gesture_input (826951152254563827) -->
+ <skip />
+ <!-- no translation found for gesture_input_summary (9180350639305731231) -->
+ <skip />
+ <!-- no translation found for gesture_preview_trail (3802333369335722221) -->
+ <skip />
+ <!-- no translation found for gesture_floating_preview_text (4443240334739381053) -->
+ <skip />
+ <!-- no translation found for gesture_floating_preview_text_summary (4472696213996203533) -->
+ <skip />
+ <!-- no translation found for added_word (8993883354622484372) -->
+ <skip />
+ <string name="label_go_key" msgid="1635148082137219148">"Áfram"</string>
+ <string name="label_next_key" msgid="362972844525672568">"Næsta"</string>
+ <string name="label_previous_key" msgid="1211868118071386787">"Fyrra"</string>
+ <string name="label_done_key" msgid="2441578748772529288">"Lokið"</string>
+ <string name="label_send_key" msgid="2815056534433717444">"Senda"</string>
+ <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string>
+ <!-- no translation found for label_to_symbol_key (8516904117128967293) -->
+ <skip />
+ <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+ <skip />
+ <!-- no translation found for label_pause_key (181098308428035340) -->
+ <skip />
+ <!-- no translation found for label_wait_key (6402152600878093134) -->
+ <skip />
+ <!-- no translation found for spoken_use_headphones (896961781287283493) -->
+ <skip />
+ <!-- no translation found for spoken_current_text_is (2485723011272583845) -->
+ <skip />
+ <!-- no translation found for spoken_no_text_entered (7479685225597344496) -->
+ <skip />
+ <!-- no translation found for spoken_description_unknown (3197434010402179157) -->
+ <skip />
+ <!-- no translation found for spoken_description_shift (244197883292549308) -->
+ <skip />
+ <!-- no translation found for spoken_description_shift_shifted (1681877323344195035) -->
+ <skip />
+ <!-- no translation found for spoken_description_caps_lock (3276478269526304432) -->
+ <skip />
+ <!-- no translation found for spoken_description_delete (8740376944276199801) -->
+ <skip />
+ <!-- no translation found for spoken_description_to_symbol (5486340107500448969) -->
+ <skip />
+ <!-- no translation found for spoken_description_to_alpha (23129338819771807) -->
+ <skip />
+ <!-- no translation found for spoken_description_to_numeric (591752092685161732) -->
+ <skip />
+ <!-- no translation found for spoken_description_settings (4627462689603838099) -->
+ <skip />
+ <!-- no translation found for spoken_description_tab (2667716002663482248) -->
+ <skip />
+ <!-- no translation found for spoken_description_space (2582521050049860859) -->
+ <skip />
+ <!-- no translation found for spoken_description_mic (615536748882611950) -->
+ <skip />
+ <!-- no translation found for spoken_description_smiley (2256309826200113918) -->
+ <skip />
+ <!-- no translation found for spoken_description_return (8178083177238315647) -->
+ <skip />
+ <!-- no translation found for spoken_description_search (1247236163755920808) -->
+ <skip />
+ <!-- no translation found for spoken_description_dot (40711082435231673) -->
+ <skip />
+ <!-- no translation found for spoken_description_language_switch (5507091328222331316) -->
+ <skip />
+ <!-- no translation found for spoken_description_action_next (8636078276664150324) -->
+ <skip />
+ <!-- no translation found for spoken_description_action_previous (800872415009336208) -->
+ <skip />
+ <!-- no translation found for spoken_description_shiftmode_on (5700440798609574589) -->
+ <skip />
+ <!-- no translation found for spoken_description_shiftmode_locked (593175803181701830) -->
+ <skip />
+ <!-- no translation found for spoken_description_shiftmode_off (657219998449174808) -->
+ <skip />
+ <!-- no translation found for spoken_description_mode_symbol (7183343879909747642) -->
+ <skip />
+ <!-- no translation found for spoken_description_mode_alpha (3528307674390156956) -->
+ <skip />
+ <!-- no translation found for spoken_description_mode_phone (6520207943132026264) -->
+ <skip />
+ <!-- no translation found for spoken_description_mode_phone_shift (5499629753962641227) -->
+ <skip />
+ <!-- no translation found for voice_input (3583258583521397548) -->
+ <skip />
+ <!-- no translation found for voice_input_modes_main_keyboard (3360660341121083174) -->
+ <skip />
+ <!-- no translation found for voice_input_modes_symbols_keyboard (7203213240786084067) -->
+ <skip />
+ <!-- no translation found for voice_input_modes_off (3745699748218082014) -->
+ <skip />
+ <!-- no translation found for voice_input_modes_summary_main_keyboard (6586544292900314339) -->
+ <skip />
+ <!-- no translation found for voice_input_modes_summary_symbols_keyboard (5233725927281932391) -->
+ <skip />
+ <!-- no translation found for voice_input_modes_summary_off (63875609591897607) -->
+ <skip />
+ <!-- no translation found for configure_input_method (373356270290742459) -->
+ <skip />
+ <!-- no translation found for language_selection_title (1651299598555326750) -->
+ <skip />
+ <!-- no translation found for select_language (3693815588777926848) -->
+ <skip />
+ <!-- no translation found for hint_add_to_dictionary (573678656946085380) -->
+ <skip />
+ <!-- no translation found for has_dictionary (6071847973466625007) -->
+ <skip />
+ <!-- no translation found for prefs_enable_log (6620424505072963557) -->
+ <skip />
+ <!-- no translation found for prefs_description_log (5827825607258246003) -->
+ <skip />
+ <!-- no translation found for keyboard_layout (8451164783510487501) -->
+ <skip />
+ <!-- no translation found for subtype_en_GB (88170601942311355) -->
+ <skip />
+ <!-- no translation found for subtype_en_US (6160452336634534239) -->
+ <skip />
+ <!-- no translation found for subtype_with_layout_en_GB (2179097748724725906) -->
+ <skip />
+ <!-- no translation found for subtype_with_layout_en_US (1362581347576714579) -->
+ <skip />
+ <!-- no translation found for subtype_no_language (141420857808801746) -->
+ <skip />
+ <!-- no translation found for subtype_no_language_qwerty (2956121451616633133) -->
+ <skip />
+ <!-- no translation found for subtype_no_language_qwertz (1177848172397202890) -->
+ <skip />
+ <!-- no translation found for subtype_no_language_azerty (8721460968141187394) -->
+ <skip />
+ <!-- no translation found for subtype_no_language_dvorak (3122976737669823935) -->
+ <skip />
+ <!-- no translation found for subtype_no_language_colemak (4205992994906097244) -->
+ <skip />
+ <!-- no translation found for subtype_no_language_pcqwerty (8840928374394180189) -->
+ <skip />
+ <!-- no translation found for custom_input_styles_title (8429952441821251512) -->
+ <skip />
+ <!-- no translation found for add_style (6163126614514489951) -->
+ <skip />
+ <!-- no translation found for add (8299699805688017798) -->
+ <skip />
+ <!-- no translation found for remove (4486081658752944606) -->
+ <skip />
+ <!-- no translation found for save (7646738597196767214) -->
+ <skip />
+ <!-- no translation found for subtype_locale (8576443440738143764) -->
+ <skip />
+ <!-- no translation found for keyboard_layout_set (4309233698194565609) -->
+ <skip />
+ <!-- no translation found for custom_input_style_note_message (8826731320846363423) -->
+ <skip />
+ <!-- no translation found for enable (5031294444630523247) -->
+ <skip />
+ <!-- no translation found for not_now (6172462888202790482) -->
+ <skip />
+ <!-- no translation found for custom_input_style_already_exists (8008728952215449707) -->
+ <skip />
+ <!-- no translation found for prefs_usability_study_mode (1261130555134595254) -->
+ <skip />
+ <!-- no translation found for prefs_keypress_vibration_duration_settings (1829950405285211668) -->
+ <skip />
+ <!-- no translation found for prefs_keypress_sound_volume_settings (5875933757082305040) -->
+ <skip />
+</resources>
diff --git a/java/res/values-it/bools.xml b/java/res/values-it/bools.xml
index 897f4b3db..c289e5bf3 100644
--- a/java/res/values-it/bools.xml
+++ b/java/res/values-it/bools.xml
@@ -1,22 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-/*
+/*
**
** Copyright 2008, 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
+** 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
+** 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
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<resources>
- <bool name="im_is_default">true</bool>
+ <!-- Whether this input method should be used as the default for a locale. Override it
+ for supported languages. -->
+ <bool name="im_is_default">true</bool>
</resources>
diff --git a/java/res/values-it/strings-appname.xml b/java/res/values-it/strings-appname.xml
new file mode 100644
index 000000000..b84896b9d
--- /dev/null
+++ b/java/res/values-it/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Tastiera Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Controllo ortografico Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Impostazioni tastiera Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Impostazioni di controllo ortografico"</string>
+</resources>
diff --git a/java/res/values-it/strings.xml b/java/res/values-it/strings.xml
index a54958c05..50941cb39 100644
--- a/java/res/values-it/strings.xml
+++ b/java/res/values-it/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Tastiera Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Tastiera Android (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Impostazioni tastiera Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Opzioni inserimento"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Controllo ortografico Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Ricerca comandi di log"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Controllo ortografico Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Impostazioni di controllo ortografico"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Cerca in nomi contatti"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"La funzione di controllo ortografico usa voci dell\'elenco contatti"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrazione tasti"</string>
@@ -34,37 +31,41 @@
<string name="popup_on_keypress" msgid="123894815723512944">"Popup sui tasti"</string>
<string name="general_category" msgid="1859088467017573195">"Generali"</string>
<string name="correction_category" msgid="2236750915056607613">"Correzione testo"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"Digitazione a gesti"</string>
<string name="misc_category" msgid="6894192814868233453">"Altre opzioni"</string>
<string name="advanced_settings" msgid="362895144495591463">"Impostazioni avanzate"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"Opzioni per esperti"</string>
<string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Altri metodi immissione"</string>
<string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Il tasto per cambiare lingua offre altri metodi di immissione"</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"Elimina tasto cambio lingua"</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"Tasto cambio lingua"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Mostra quando sono attive più lingue di immissione"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Ritardo eliminaz. popup tasto"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Nessun ritardo"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Predefinito"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Suggerisci nomi di contatti"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Utilizza nomi di Contatti per suggerimenti e correzioni"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Attiva nuove correzioni"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Imposta suggerimenti per nuove correzioni"</string>
<string name="auto_cap" msgid="1719746674854628252">"Maiuscole automatiche"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"Iniziale maiuscola per la prima parola di ogni frase"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Dizionari aggiuntivi"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Dizionario principale"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"Mostra suggerimenti correzioni"</string>
<string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Visualizza le parole suggerite durante la digitazione"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Mostra sempre"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Mostra in modalità verticale"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Mostra in modalità verticale"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Nascondi sempre"</string>
- <string name="auto_correction" msgid="4979925752001319458">"Correzione automatica"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"Correzione automatica"</string>
<string name="auto_correction_summary" msgid="5625751551134658006">"Barra spaziatrice/punteggiatura correggono parole con errori"</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Off"</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Media"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Massima"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Massima"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Suggerimenti parola successiva"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Usa parola precedente per migliorare suggerimenti"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Previsione parola successiva"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Usa anche la parola precedente per la previsione"</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"Suggerimenti parola successiva"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"Usa la parola precedente per i suggerimenti"</string>
+ <string name="gesture_input" msgid="826951152254563827">"Abilita digitazione a gesti"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"Inserisci una parola scorrendo tra le lettere"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Mostra traccia con gesto"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"Anteprima mobile dinamica"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"Visualizza la parola suggerita durante il gesto"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : parola salvata"</string>
<string name="label_go_key" msgid="1635148082137219148">"Vai"</string>
<string name="label_next_key" msgid="362972844525672568">"Avanti"</string>
@@ -95,6 +96,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Invio"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Cerca"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Pallino"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Cambia lingua"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Successivo"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Precedente"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Maiuscolo attivo"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Blocco maiuscole attivo"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Maiuscolo disattivato"</string>
diff --git a/java/res/values-iw/bools.xml b/java/res/values-iw/bools.xml
new file mode 100644
index 000000000..840d20c21
--- /dev/null
+++ b/java/res/values-iw/bools.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<resources>
+ <!-- Whether this input method should be used as the default for a locale. Override it
+ for supported languages. -->
+ <bool name="im_is_default">true</bool>
+</resources>
diff --git a/java/res/values-iw/strings-appname.xml b/java/res/values-iw/strings-appname.xml
new file mode 100644
index 000000000..f3f4b674c
--- /dev/null
+++ b/java/res/values-iw/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"מקלדת Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"בודק האיות של Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"הגדרות מקלדת Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"הגדרות בדיקת איות"</string>
+</resources>
diff --git a/java/res/values-iw/strings.xml b/java/res/values-iw/strings.xml
index 323cac198..645b0c5a8 100644
--- a/java/res/values-iw/strings.xml
+++ b/java/res/values-iw/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"מקלדת Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"מקלדת Android ‏(AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"הגדרות מקלדת של Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"אפשרויות קלט"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"בודק האיות של Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"פקודות יומן מחקר"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"בודק האיות של Android ‏(AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"הגדרות בדיקת איות"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"חפש שמות של אנשי קשר"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"בודק האיות משתמש בערכים מרשימת אנשי הקשר שלך"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"רטט בלחיצה על מקשים"</string>
@@ -34,37 +31,41 @@
<string name="popup_on_keypress" msgid="123894815723512944">"חלון קופץ בלחיצה על מקש"</string>
<string name="general_category" msgid="1859088467017573195">"כללי"</string>
<string name="correction_category" msgid="2236750915056607613">"תיקון טקסט"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"הקלדה ללא הרמת אצבע"</string>
<string name="misc_category" msgid="6894192814868233453">"אפשרויות אחרות"</string>
<string name="advanced_settings" msgid="362895144495591463">"הגדרות מתקדמות"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"אפשרויות למומחים"</string>
<string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"עבור לשיטות קלט אחרות"</string>
<string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"מתג החלפת השפה מכסה גם שיטות קלט אחרות"</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"הסתר את מתג החלפת השפה"</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"מתג החלפת שפה"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"הצג כאשר ניתן להשתמש בשפות קלט מרובות"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"עיכוב סגירת חלון קופץ של מקש"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"ללא עיכוב"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"ברירת מחדל"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"הצע שמות של אנשי קשר"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"השתמש בשמות מרשימת אנשי הקשר עבור הצעות ותיקונים"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"הפוך תיקונים חוזרים לפעילים"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"הגדר הצעות עבור תיקונים חוזרים"</string>
<string name="auto_cap" msgid="1719746674854628252">"הפיכת אותיות לרישיות באופן אוטומטי"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"השתמש באות גדולה במילה הראשונה של כל משפט"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"הוספת מילונים"</string>
<string name="main_dictionary" msgid="4798763781818361168">"מילון ראשי"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"הצג הצעות לתיקונים"</string>
<string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"הצג הצעות למילים בעת הקלדה"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"הצג תמיד"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"הצג בפריסה לאורך"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"הצג בפריסה לאורך"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"הסתר תמיד"</string>
- <string name="auto_correction" msgid="4979925752001319458">"תיקון אוטומטי"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"תיקון אוטומטי"</string>
<string name="auto_correction_summary" msgid="5625751551134658006">"מקש הרווח ופיסוק מתקנים אוטומטית שגיאות הקלדה"</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"כבוי"</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"מצומצם"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"מחמיר"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"מחמיר מאוד"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"הצעות המילה הבאה"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"השתמש במילה הקודמת כדי לשפר את ההצעות"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"חיזוי המילה הבאה"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"השתמש במילה הקודמת גם עבור חיזוי"</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"הצעות למילה הבאה"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"השתמש במילה הקודמת ביצירת הצעות"</string>
+ <string name="gesture_input" msgid="826951152254563827">"אפשר הקלדה ללא הרמת אצבע"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"הזן מילה על ידי החלקת האצבע מאות לאות"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"הצג שובל מחווה"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"תצוגה מקדימה דינמית צפה"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"ראה את המילה המוצעת תוך כדי הזזת האצבע"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : נשמרה"</string>
<string name="label_go_key" msgid="1635148082137219148">"בצע"</string>
<string name="label_next_key" msgid="362972844525672568">"הבא"</string>
@@ -95,6 +96,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"חזור"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"חיפוש"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"נקודה"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"החלף שפה"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"הבא"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"הקודם"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift מופעל"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps Lock מופעל"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift מושבת"</string>
diff --git a/java/res/values-ja/strings-appname.xml b/java/res/values-ja/strings-appname.xml
new file mode 100644
index 000000000..16c1c05c6
--- /dev/null
+++ b/java/res/values-ja/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Androidキーボード"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Androidスペルチェッカー"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Androidキーボードの設定"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"スペルチェックの設定"</string>
+</resources>
diff --git a/java/res/values-ja/strings.xml b/java/res/values-ja/strings.xml
index 3a865166f..d7ae04f07 100644
--- a/java/res/values-ja/strings.xml
+++ b/java/res/values-ja/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Androidキーボード"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Androidキーボード(AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Androidキーボードの設定"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"入力オプション"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Androidスペルチェッカー"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"ログコマンドの検索"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Androidスペルチェッカー(AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"スペルチェックの設定"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"連絡先名の検索"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"スペルチェッカーでは連絡先リストのエントリを使用します"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"キー操作バイブ"</string>
@@ -34,37 +31,41 @@
<string name="popup_on_keypress" msgid="123894815723512944">"キー押下時ポップアップ"</string>
<string name="general_category" msgid="1859088467017573195">"全般"</string>
<string name="correction_category" msgid="2236750915056607613">"テキストの修正"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"ジェスチャー入力"</string>
<string name="misc_category" msgid="6894192814868233453">"他のオプション"</string>
<string name="advanced_settings" msgid="362895144495591463">"詳細設定"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"上級者向けオプション"</string>
<string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"他の入力方法に切り替え"</string>
<string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"言語切り替えキーは他の入力方法にも対応しています"</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"言語切り替えキーを非表示"</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"言語切り替えキー"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"複数の入力言語が有効なときに表示"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"キーのポップアップ時間"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"すぐに消去"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"デフォルト"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"候補の連絡先名を表示"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"連絡先の名前を使用して候補表示や自動修正を行います"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"再修正を有効にする"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"再修正の候補を挿入する"</string>
<string name="auto_cap" msgid="1719746674854628252">"自動大文字変換"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"英字入力で各文の最初の単語を大文字にします"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"アドオン辞書"</string>
<string name="main_dictionary" msgid="4798763781818361168">"メイン辞書"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"修正候補を表示する"</string>
<string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"入力中に入力候補を表示する"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"常に表示"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"縦向きで表示"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"縦向きで表示"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"常に非表示"</string>
- <string name="auto_correction" msgid="4979925752001319458">"自動修正"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"自動修正"</string>
<string name="auto_correction_summary" msgid="5625751551134658006">"誤入力をスペースまたは句読点キーで修正する"</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"OFF"</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"中"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"強"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"最も強い"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"次の入力候補"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"直前の単語から入力候補を予測します"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"次の入力候補を予測"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"前の語句も予測に使用"</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"次の入力候補"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"前の単語に基づいて入力候補を表示します"</string>
+ <string name="gesture_input" msgid="826951152254563827">"ジェスチャー入力を有効にする"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"文字間をスライドして単語を入力できます"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"ジェスチャートレイルを表示"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"動的フローティングプレビュー"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"ジェスチャーで入力候補を表示できます"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>:保存しました"</string>
<string name="label_go_key" msgid="1635148082137219148">"実行"</string>
<string name="label_next_key" msgid="362972844525672568">"次へ"</string>
@@ -95,6 +96,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Enter"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"検索"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"中点"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"言語を切り替え"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"次へ"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"前へ"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift有効"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps lock有効"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift解除"</string>
diff --git a/java/res/values-ka/bools.xml b/java/res/values-ka/bools.xml
new file mode 100644
index 000000000..840d20c21
--- /dev/null
+++ b/java/res/values-ka/bools.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<resources>
+ <!-- Whether this input method should be used as the default for a locale. Override it
+ for supported languages. -->
+ <bool name="im_is_default">true</bool>
+</resources>
diff --git a/java/res/values-ka/strings.xml b/java/res/values-ka/strings.xml
new file mode 100644
index 000000000..cd4dfd903
--- /dev/null
+++ b/java/res/values-ka/strings.xml
@@ -0,0 +1,269 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for aosp_android_keyboard_ime_name (7877134937939182296) -->
+ <skip />
+ <!-- no translation found for english_ime_input_options (3909945612939668554) -->
+ <skip />
+ <!-- no translation found for english_ime_research_log (8492602295696577851) -->
+ <skip />
+ <!-- no translation found for aosp_spell_checker_service_name (6985142605330377819) -->
+ <skip />
+ <!-- no translation found for use_contacts_for_spellchecking_option_title (5374120998125353898) -->
+ <skip />
+ <!-- no translation found for use_contacts_for_spellchecking_option_summary (8754413382543307713) -->
+ <skip />
+ <!-- no translation found for vibrate_on_keypress (5258079494276955460) -->
+ <skip />
+ <!-- no translation found for sound_on_keypress (6093592297198243644) -->
+ <skip />
+ <!-- no translation found for popup_on_keypress (123894815723512944) -->
+ <skip />
+ <!-- no translation found for general_category (1859088467017573195) -->
+ <skip />
+ <!-- no translation found for correction_category (2236750915056607613) -->
+ <skip />
+ <!-- no translation found for gesture_typing_category (497263612130532630) -->
+ <skip />
+ <!-- no translation found for misc_category (6894192814868233453) -->
+ <skip />
+ <!-- no translation found for advanced_settings (362895144495591463) -->
+ <skip />
+ <!-- no translation found for advanced_settings_summary (4487980456152830271) -->
+ <skip />
+ <!-- no translation found for include_other_imes_in_language_switch_list (4533689960308565519) -->
+ <skip />
+ <!-- no translation found for include_other_imes_in_language_switch_list_summary (840637129103317635) -->
+ <skip />
+ <!-- no translation found for show_language_switch_key (5915478828318774384) -->
+ <skip />
+ <!-- no translation found for show_language_switch_key_summary (7343403647474265713) -->
+ <skip />
+ <!-- no translation found for key_preview_popup_dismiss_delay (6213164897443068248) -->
+ <skip />
+ <!-- no translation found for key_preview_popup_dismiss_no_delay (2096123151571458064) -->
+ <skip />
+ <!-- no translation found for key_preview_popup_dismiss_default_delay (2166964333903906734) -->
+ <skip />
+ <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+ <skip />
+ <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+ <skip />
+ <!-- no translation found for auto_cap (1719746674854628252) -->
+ <skip />
+ <!-- no translation found for auto_cap_summary (7934452761022946874) -->
+ <skip />
+ <!-- no translation found for configure_dictionaries_title (4238652338556902049) -->
+ <skip />
+ <!-- no translation found for main_dictionary (4798763781818361168) -->
+ <skip />
+ <!-- no translation found for prefs_show_suggestions (8026799663445531637) -->
+ <skip />
+ <!-- no translation found for prefs_show_suggestions_summary (1583132279498502825) -->
+ <skip />
+ <!-- no translation found for prefs_suggestion_visibility_show_name (3219916594067551303) -->
+ <skip />
+ <!-- no translation found for prefs_suggestion_visibility_show_only_portrait_name (3859783767435239118) -->
+ <skip />
+ <!-- no translation found for prefs_suggestion_visibility_hide_name (6309143926422234673) -->
+ <skip />
+ <!-- no translation found for auto_correction (7630720885194996950) -->
+ <skip />
+ <!-- no translation found for auto_correction_summary (5625751551134658006) -->
+ <skip />
+ <!-- no translation found for auto_correction_threshold_mode_off (8470882665417944026) -->
+ <skip />
+ <!-- no translation found for auto_correction_threshold_mode_modest (8788366690620799097) -->
+ <skip />
+ <!-- no translation found for auto_correction_threshold_mode_aggeressive (3524029103734923819) -->
+ <skip />
+ <!-- no translation found for auto_correction_threshold_mode_very_aggeressive (3386782235540547678) -->
+ <skip />
+ <!-- no translation found for bigram_prediction (1084449187723948550) -->
+ <skip />
+ <!-- no translation found for bigram_prediction_summary (3896362682751109677) -->
+ <skip />
+ <!-- no translation found for gesture_input (826951152254563827) -->
+ <skip />
+ <!-- no translation found for gesture_input_summary (9180350639305731231) -->
+ <skip />
+ <!-- no translation found for gesture_preview_trail (3802333369335722221) -->
+ <skip />
+ <!-- no translation found for gesture_floating_preview_text (4443240334739381053) -->
+ <skip />
+ <!-- no translation found for gesture_floating_preview_text_summary (4472696213996203533) -->
+ <skip />
+ <!-- no translation found for added_word (8993883354622484372) -->
+ <skip />
+ <string name="label_go_key" msgid="1635148082137219148">"გადასვლა"</string>
+ <string name="label_next_key" msgid="362972844525672568">"შემდეგი"</string>
+ <string name="label_previous_key" msgid="1211868118071386787">"წინა"</string>
+ <string name="label_done_key" msgid="2441578748772529288">"შესრულებულია"</string>
+ <string name="label_send_key" msgid="2815056534433717444">"გაგზავნა"</string>
+ <string name="label_to_alpha_key" msgid="4793983863798817523">"ABC"</string>
+ <!-- no translation found for label_to_symbol_key (8516904117128967293) -->
+ <skip />
+ <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+ <skip />
+ <!-- no translation found for label_pause_key (181098308428035340) -->
+ <skip />
+ <!-- no translation found for label_wait_key (6402152600878093134) -->
+ <skip />
+ <!-- no translation found for spoken_use_headphones (896961781287283493) -->
+ <skip />
+ <!-- no translation found for spoken_current_text_is (2485723011272583845) -->
+ <skip />
+ <!-- no translation found for spoken_no_text_entered (7479685225597344496) -->
+ <skip />
+ <!-- no translation found for spoken_description_unknown (3197434010402179157) -->
+ <skip />
+ <!-- no translation found for spoken_description_shift (244197883292549308) -->
+ <skip />
+ <!-- no translation found for spoken_description_shift_shifted (1681877323344195035) -->
+ <skip />
+ <!-- no translation found for spoken_description_caps_lock (3276478269526304432) -->
+ <skip />
+ <!-- no translation found for spoken_description_delete (8740376944276199801) -->
+ <skip />
+ <!-- no translation found for spoken_description_to_symbol (5486340107500448969) -->
+ <skip />
+ <!-- no translation found for spoken_description_to_alpha (23129338819771807) -->
+ <skip />
+ <!-- no translation found for spoken_description_to_numeric (591752092685161732) -->
+ <skip />
+ <!-- no translation found for spoken_description_settings (4627462689603838099) -->
+ <skip />
+ <!-- no translation found for spoken_description_tab (2667716002663482248) -->
+ <skip />
+ <!-- no translation found for spoken_description_space (2582521050049860859) -->
+ <skip />
+ <!-- no translation found for spoken_description_mic (615536748882611950) -->
+ <skip />
+ <!-- no translation found for spoken_description_smiley (2256309826200113918) -->
+ <skip />
+ <!-- no translation found for spoken_description_return (8178083177238315647) -->
+ <skip />
+ <!-- no translation found for spoken_description_search (1247236163755920808) -->
+ <skip />
+ <!-- no translation found for spoken_description_dot (40711082435231673) -->
+ <skip />
+ <!-- no translation found for spoken_description_language_switch (5507091328222331316) -->
+ <skip />
+ <!-- no translation found for spoken_description_action_next (8636078276664150324) -->
+ <skip />
+ <!-- no translation found for spoken_description_action_previous (800872415009336208) -->
+ <skip />
+ <!-- no translation found for spoken_description_shiftmode_on (5700440798609574589) -->
+ <skip />
+ <!-- no translation found for spoken_description_shiftmode_locked (593175803181701830) -->
+ <skip />
+ <!-- no translation found for spoken_description_shiftmode_off (657219998449174808) -->
+ <skip />
+ <!-- no translation found for spoken_description_mode_symbol (7183343879909747642) -->
+ <skip />
+ <!-- no translation found for spoken_description_mode_alpha (3528307674390156956) -->
+ <skip />
+ <!-- no translation found for spoken_description_mode_phone (6520207943132026264) -->
+ <skip />
+ <!-- no translation found for spoken_description_mode_phone_shift (5499629753962641227) -->
+ <skip />
+ <!-- no translation found for voice_input (3583258583521397548) -->
+ <skip />
+ <!-- no translation found for voice_input_modes_main_keyboard (3360660341121083174) -->
+ <skip />
+ <!-- no translation found for voice_input_modes_symbols_keyboard (7203213240786084067) -->
+ <skip />
+ <!-- no translation found for voice_input_modes_off (3745699748218082014) -->
+ <skip />
+ <!-- no translation found for voice_input_modes_summary_main_keyboard (6586544292900314339) -->
+ <skip />
+ <!-- no translation found for voice_input_modes_summary_symbols_keyboard (5233725927281932391) -->
+ <skip />
+ <!-- no translation found for voice_input_modes_summary_off (63875609591897607) -->
+ <skip />
+ <!-- no translation found for configure_input_method (373356270290742459) -->
+ <skip />
+ <!-- no translation found for language_selection_title (1651299598555326750) -->
+ <skip />
+ <!-- no translation found for select_language (3693815588777926848) -->
+ <skip />
+ <!-- no translation found for hint_add_to_dictionary (573678656946085380) -->
+ <skip />
+ <!-- no translation found for has_dictionary (6071847973466625007) -->
+ <skip />
+ <!-- no translation found for prefs_enable_log (6620424505072963557) -->
+ <skip />
+ <!-- no translation found for prefs_description_log (5827825607258246003) -->
+ <skip />
+ <!-- no translation found for keyboard_layout (8451164783510487501) -->
+ <skip />
+ <!-- no translation found for subtype_en_GB (88170601942311355) -->
+ <skip />
+ <!-- no translation found for subtype_en_US (6160452336634534239) -->
+ <skip />
+ <!-- no translation found for subtype_with_layout_en_GB (2179097748724725906) -->
+ <skip />
+ <!-- no translation found for subtype_with_layout_en_US (1362581347576714579) -->
+ <skip />
+ <!-- no translation found for subtype_no_language (141420857808801746) -->
+ <skip />
+ <!-- no translation found for subtype_no_language_qwerty (2956121451616633133) -->
+ <skip />
+ <!-- no translation found for subtype_no_language_qwertz (1177848172397202890) -->
+ <skip />
+ <!-- no translation found for subtype_no_language_azerty (8721460968141187394) -->
+ <skip />
+ <!-- no translation found for subtype_no_language_dvorak (3122976737669823935) -->
+ <skip />
+ <!-- no translation found for subtype_no_language_colemak (4205992994906097244) -->
+ <skip />
+ <!-- no translation found for subtype_no_language_pcqwerty (8840928374394180189) -->
+ <skip />
+ <!-- no translation found for custom_input_styles_title (8429952441821251512) -->
+ <skip />
+ <!-- no translation found for add_style (6163126614514489951) -->
+ <skip />
+ <!-- no translation found for add (8299699805688017798) -->
+ <skip />
+ <!-- no translation found for remove (4486081658752944606) -->
+ <skip />
+ <!-- no translation found for save (7646738597196767214) -->
+ <skip />
+ <!-- no translation found for subtype_locale (8576443440738143764) -->
+ <skip />
+ <!-- no translation found for keyboard_layout_set (4309233698194565609) -->
+ <skip />
+ <!-- no translation found for custom_input_style_note_message (8826731320846363423) -->
+ <skip />
+ <!-- no translation found for enable (5031294444630523247) -->
+ <skip />
+ <!-- no translation found for not_now (6172462888202790482) -->
+ <skip />
+ <!-- no translation found for custom_input_style_already_exists (8008728952215449707) -->
+ <skip />
+ <!-- no translation found for prefs_usability_study_mode (1261130555134595254) -->
+ <skip />
+ <!-- no translation found for prefs_keypress_vibration_duration_settings (1829950405285211668) -->
+ <skip />
+ <!-- no translation found for prefs_keypress_sound_volume_settings (5875933757082305040) -->
+ <skip />
+</resources>
diff --git a/java/res/values-ko/strings-appname.xml b/java/res/values-ko/strings-appname.xml
new file mode 100644
index 000000000..3d7db6136
--- /dev/null
+++ b/java/res/values-ko/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Android 키보드"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Android 맞춤법 검사기"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Android 키보드 설정"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"맞춤법 검사 설정"</string>
+</resources>
diff --git a/java/res/values-ko/strings.xml b/java/res/values-ko/strings.xml
index 536f06dbd..2be5db2ab 100644
--- a/java/res/values-ko/strings.xml
+++ b/java/res/values-ko/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Android 키보드"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android 키보드(AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Android 키보드 설정"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"입력 옵션"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Android 맞춤법 검사기"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"로그 명령 탐색"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android 맞춤법 검사기(AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"맞춤법 검사 설정"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"연락처 이름 조회"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"맞춤법 검사기가 주소록의 항목을 사용합니다."</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"키를 누를 때 진동 발생"</string>
@@ -34,37 +31,41 @@
<string name="popup_on_keypress" msgid="123894815723512944">"키를 누를 때 팝업"</string>
<string name="general_category" msgid="1859088467017573195">"일반"</string>
<string name="correction_category" msgid="2236750915056607613">"텍스트 수정"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"제스처 타이핑"</string>
<string name="misc_category" msgid="6894192814868233453">"기타 옵션"</string>
<string name="advanced_settings" msgid="362895144495591463">"고급 설정"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"전문가용 옵션"</string>
<string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"다른 입력 방법으로 전환"</string>
<string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"언어 전환 키가 제공하는 기타 입력 방법"</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"언어 전환 키 제거"</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"언어 전환 키"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"여러 입력 언어를 사용하도록 설정할 때 표시"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"키 팝업 해제 지연"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"지연 없음"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"기본값"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"주소록 이름 활용"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"추천 및 수정에 주소록의 이름 사용"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"재수정 가능 설정"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"재수정 추천어 사전 활성화"</string>
<string name="auto_cap" msgid="1719746674854628252">"자동 대문자화"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"문장의 첫 단어를 대문자로 표시"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"사전 추가"</string>
<string name="main_dictionary" msgid="4798763781818361168">"기본 사전"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"수정 제안 표시"</string>
<string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"글자를 입력하는 동안 추천 단어 표시"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"항상 표시"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"세로 화면일 때만 표시"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"세로 모드로 표시"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"항상 숨기기"</string>
- <string name="auto_correction" msgid="4979925752001319458">"자동 수정"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"자동 수정"</string>
<string name="auto_correction_summary" msgid="5625751551134658006">"스페이스바와 문장부호 키를 사용하면 오타가 자동으로 교정됩니다."</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"사용 안함"</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"약"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"중"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"강"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"다음 추천 검색어"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"이전 단어를 사용하여 추천 검색어 개선"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"다음 예상 검색어"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"이전 단어를 사용하여 예상 검색어 표시"</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"다음 단어 추천"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"추천할 때 이전 단어를 사용"</string>
+ <string name="gesture_input" msgid="826951152254563827">"제스처 타이핑 사용"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"문자를 슬라이드하여 단어 입력"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"동작 흔적 표시"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"동적 플로팅 미리보기"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"동작에 따라 추천 단어 보기"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: 저장됨"</string>
<string name="label_go_key" msgid="1635148082137219148">"이동"</string>
<string name="label_next_key" msgid="362972844525672568">"다음"</string>
@@ -95,6 +96,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"리턴 키"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"검색"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"점"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"언어 전환"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"다음"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"이전"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift 사용"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps Lock 사용"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift 사용중지"</string>
diff --git a/java/res/values-ky/bools.xml b/java/res/values-ky/bools.xml
new file mode 100644
index 000000000..840d20c21
--- /dev/null
+++ b/java/res/values-ky/bools.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<resources>
+ <!-- Whether this input method should be used as the default for a locale. Override it
+ for supported languages. -->
+ <bool name="im_is_default">true</bool>
+</resources>
diff --git a/java/res/values-land/dimens.xml b/java/res/values-land/dimens.xml
index 62597258c..c78c25f86 100644
--- a/java/res/values-land/dimens.xml
+++ b/java/res/values-land/dimens.xml
@@ -1,19 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-/*
+/*
**
** Copyright 2008, 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
+** 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
+** 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
+** 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.
*/
-->
@@ -55,6 +55,11 @@
<fraction name="spacebar_text_ratio">40.000%</fraction>
<dimen name="key_preview_offset">0.0dp</dimen>
+ <!-- For 5-row keyboard -->
+ <fraction name="key_bottom_gap_5row">3.20%p</fraction>
+ <fraction name="key_letter_ratio_5row">78%</fraction>
+ <fraction name="key_uppercase_letter_ratio_5row">48%</fraction>
+
<dimen name="key_preview_offset_ics">1.6dp</dimen>
<!-- popup_key_height x -0.5 -->
<dimen name="more_keys_keyboard_vertical_correction_ics">-22.4dp</dimen>
@@ -68,4 +73,10 @@
<dimen name="more_keys_keyboard_slide_allowance">53.76dp</dimen>
<!-- popup_key_height x -1.0 -->
<dimen name="more_keys_keyboard_vertical_correction">-44.8dp</dimen>
+
+ <!-- Gesture floating preview text parameters -->
+ <dimen name="gesture_floating_preview_text_size">23dp</dimen>
+ <dimen name="gesture_floating_preview_text_offset">54dp</dimen>
+ <dimen name="gesture_floating_preview_horizontal_padding">23dp</dimen>
+ <dimen name="gesture_floating_preview_vertical_padding">15dp</dimen>
</resources>
diff --git a/java/res/values-lt/bools.xml b/java/res/values-lt/bools.xml
new file mode 100644
index 000000000..840d20c21
--- /dev/null
+++ b/java/res/values-lt/bools.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<resources>
+ <!-- Whether this input method should be used as the default for a locale. Override it
+ for supported languages. -->
+ <bool name="im_is_default">true</bool>
+</resources>
diff --git a/java/res/values-lt/strings-appname.xml b/java/res/values-lt/strings-appname.xml
new file mode 100644
index 000000000..668d27531
--- /dev/null
+++ b/java/res/values-lt/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"„Android“ klaviatūra"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"„Android“ rašybos tikrinimo programa"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"„Android“ klaviatūros nustatymai"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Rašybos tikrinimo nustatymai"</string>
+</resources>
diff --git a/java/res/values-lt/strings.xml b/java/res/values-lt/strings.xml
index a0b8fc87d..dee3479aa 100644
--- a/java/res/values-lt/strings.xml
+++ b/java/res/values-lt/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"„Android“ klaviatūra"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"„Android“ klaviatūra (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"„Android“ klaviatūros nustatymai"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Įvesties parinktys"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"„Android“ rašybos tikrinimo programa"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Tyrinėti žurnalo komandas"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"„Android“ rašybos tikrinimo programa (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Rašybos tikrinimo nustatymai"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Kontaktų vardų paieška"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Rašybos tikrinimo progr. naudoja įrašus, esančius kontaktų sąraše"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibruoti, kai paspaudžiami klavišai"</string>
@@ -34,37 +31,41 @@
<string name="popup_on_keypress" msgid="123894815723512944">"Iššoka paspaudus klavišą"</string>
<string name="general_category" msgid="1859088467017573195">"Bendra"</string>
<string name="correction_category" msgid="2236750915056607613">"Teksto taisymas"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"Teksto vedimas gestais"</string>
<string name="misc_category" msgid="6894192814868233453">"Kitos parinktys"</string>
<string name="advanced_settings" msgid="362895144495591463">"Išplėstiniai nustatymai"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"Parinktys ekspertams"</string>
<string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Perj. į kt. įvesties būd."</string>
<string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Kalbos perjungimo klavišu taip pat perjungiami įvesties būdai"</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"Nerodyti klb. keit. klav."</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"Kalbos keitimo klavišas"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Rodyti, kai įgalintos kelios įvesties kalbos"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Pagr. išš. l. atsis. d."</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Be delsos"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Numatytasis"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Siūlyti kontaktų vardus"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Siūlant ir taisant naudoti vardus iš „Kontaktų“"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Įdiegti pakartotinius pataisymus"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Nustatyti pakartotinio pataisymo pasiūlymus"</string>
<string name="auto_cap" msgid="1719746674854628252">"Automatinis didžiųjų raidžių rašymas"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"Kiekvieno sakinio pirmą žodį rašyti iš didžiosios raidės"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Papildomi žodynai"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Pagrindinis žodynas"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"Rodyti taisymo pasiūlymus"</string>
<string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Įvedant tekstą pateikti siūlomų žodžių"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Visada rodyti"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Rodyti stačiuoju režimu"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Rodyti portreto režimu"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Visada slėpti"</string>
- <string name="auto_correction" msgid="4979925752001319458">"Automatinis taisymas"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"Automatinis taisymas"</string>
<string name="auto_correction_summary" msgid="5625751551134658006">"Tarpo kl. ir skyr. ženkl. aut. išt. neteis. įv. žodž."</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Išjungta"</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Vidutinis"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Atkaklus"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Labai agresyviai"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Kito žodžio pasiūlymai"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Naudoti ankstesnį žodį pasiūlymams patobulinti"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Kito žodžio numatymas"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Numatant naudoti ir ankstesnį žodį"</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"Kito žodžio pasiūlymai"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"Sudarant pasiūlymus naudoti ankstesnį žodį"</string>
+ <string name="gesture_input" msgid="826951152254563827">"Įgalinti teksto vedimą gestais"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"Įvesti žodį slenkant raidėmis"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Rodyti gestų kelią"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"Dinaminė slankioji peržiūra"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"Gestikuliuojant peržiūrėti siūlomą žodį"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: išsaugota"</string>
<string name="label_go_key" msgid="1635148082137219148">"Pradėti"</string>
<string name="label_next_key" msgid="362972844525672568">"Kitas"</string>
@@ -95,6 +96,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Grįžti"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Ieškoti"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Taškas"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Keisti kalbą"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Kitas"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Ankstesnis"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Įgalintas antrasis lygis"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Įgalintos didžiosios raidės"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Antrasis lygis išjungtas"</string>
diff --git a/java/res/values-lv/bools.xml b/java/res/values-lv/bools.xml
new file mode 100644
index 000000000..840d20c21
--- /dev/null
+++ b/java/res/values-lv/bools.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<resources>
+ <!-- Whether this input method should be used as the default for a locale. Override it
+ for supported languages. -->
+ <bool name="im_is_default">true</bool>
+</resources>
diff --git a/java/res/values-lv/strings-appname.xml b/java/res/values-lv/strings-appname.xml
new file mode 100644
index 000000000..e5657a237
--- /dev/null
+++ b/java/res/values-lv/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Android tastatūra"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Android pareizrakstības pārbaudītājs"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Android tastatūras iestatījumi"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Pareizrakstības pārbaudes iestatījumi"</string>
+</resources>
diff --git a/java/res/values-lv/strings.xml b/java/res/values-lv/strings.xml
index 93727a8ba..b548444b9 100644
--- a/java/res/values-lv/strings.xml
+++ b/java/res/values-lv/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Android tastatūra"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android tastatūra (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Android tastatūras iestatījumi"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Ievades opcijas"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Android pareizrakstības pārbaudītājs"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Izpētes žurnāla komandas"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android pareizrakstības pārbaudītājs (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Pareizrakstības pārbaudes iestatījumi"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Meklēt kontaktp. vārdus"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Pareizrakst. pārbaudītājs lieto ierakstus no kontaktp. saraksta."</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrēt, nospiežot taustiņu"</string>
@@ -34,37 +31,41 @@
<string name="popup_on_keypress" msgid="123894815723512944">"Nospiežot taustiņu, parādīt uznirstošo izvēlni"</string>
<string name="general_category" msgid="1859088467017573195">"Vispārīgi"</string>
<string name="correction_category" msgid="2236750915056607613">"Teksta korekcija"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"Ievade ar žestiem"</string>
<string name="misc_category" msgid="6894192814868233453">"Citas opcijas"</string>
<string name="advanced_settings" msgid="362895144495591463">"Papildu iestatījumi"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"Opcijas ekspertiem"</string>
<string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Pārsl. uz citām iev. met."</string>
<string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Valodas pārslēgš. taustiņu var lietot arī citām ievades metodēm."</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"Atsp. val. pārsl. taust."</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"Valodas pārslēgšanas taustiņš"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Parādīt, ja ir iespējotas vairākas ievades valodas"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Taust. uzn. loga noraid. aizk."</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Bez aizkaves"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Noklusējums"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Ieteikt kontaktp. vārdus"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Izmantot kontaktpersonu vārdus kā ieteikumus un labojumus"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Iespējot atk. labojumus"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Iestatīt atkārtotu labojumu ieteikumus"</string>
<string name="auto_cap" msgid="1719746674854628252">"Automātiska lielo burtu lietošana"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"Katra teikuma pirmo vārdu rakstīt ar lielo burtu."</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Papildinājumu vārdnīcas"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Galvenā vārdnīca"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"Rādīt labojumu ieteikumus"</string>
<string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Ievades laikā attēlot ieteiktos vārdus"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Vienmēr rādīt"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Rādīt portreta režīmā"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Rādīt portreta režīmā"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Vienmēr slēpt"</string>
- <string name="auto_correction" msgid="4979925752001319458">"Automāt. korekcija"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"Automātiska labošana"</string>
<string name="auto_correction_summary" msgid="5625751551134658006">"Atstarpes taustiņš un interpunkcija; automātiska kļūdainu vārdu labošana"</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Izslēgta"</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Mērena"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agresīva"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Ļoti radikāla"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Nākamā vārda ieteikumi"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Ieteikumu uzlabošanai izmantot iepriekšējo vārdu"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Nākamā vārda prognozēšana"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Izmantot iepriekšējo vārdu arī prognozēm"</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"Nākamā vārda ieteikumi"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"Veidojot ieteikumus, izmantot iepriekšējo vārdu."</string>
+ <string name="gesture_input" msgid="826951152254563827">"Iespējot ievadi ar žestiem"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"Ievadiet vārdu, velkot ar pirkstu pa burtiem."</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Rādīt žesta pēdas"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"Dinamisk. peldošais priekšsk."</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"Skatiet ieteikto vārdu, veicot žestu."</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: saglabāts"</string>
<string name="label_go_key" msgid="1635148082137219148">"Sākt"</string>
<string name="label_next_key" msgid="362972844525672568">"Tālāk"</string>
@@ -95,6 +96,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Ievadīšanas taustiņš"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Meklēt"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Punkts"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Mainīt valodu"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Nākamā"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Iepriekšējā"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Pārslēgšanas režīms iespējots"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Burtslēgs iespējots"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Pārslēgšanas režīms atspējots"</string>
diff --git a/java/res/values-mk/bools.xml b/java/res/values-mk/bools.xml
new file mode 100644
index 000000000..840d20c21
--- /dev/null
+++ b/java/res/values-mk/bools.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<resources>
+ <!-- Whether this input method should be used as the default for a locale. Override it
+ for supported languages. -->
+ <bool name="im_is_default">true</bool>
+</resources>
diff --git a/java/res/values-mk/strings.xml b/java/res/values-mk/strings.xml
new file mode 100644
index 000000000..824107785
--- /dev/null
+++ b/java/res/values-mk/strings.xml
@@ -0,0 +1,269 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- no translation found for aosp_android_keyboard_ime_name (7877134937939182296) -->
+ <skip />
+ <!-- no translation found for english_ime_input_options (3909945612939668554) -->
+ <skip />
+ <!-- no translation found for english_ime_research_log (8492602295696577851) -->
+ <skip />
+ <!-- no translation found for aosp_spell_checker_service_name (6985142605330377819) -->
+ <skip />
+ <!-- no translation found for use_contacts_for_spellchecking_option_title (5374120998125353898) -->
+ <skip />
+ <!-- no translation found for use_contacts_for_spellchecking_option_summary (8754413382543307713) -->
+ <skip />
+ <!-- no translation found for vibrate_on_keypress (5258079494276955460) -->
+ <skip />
+ <!-- no translation found for sound_on_keypress (6093592297198243644) -->
+ <skip />
+ <!-- no translation found for popup_on_keypress (123894815723512944) -->
+ <skip />
+ <!-- no translation found for general_category (1859088467017573195) -->
+ <skip />
+ <!-- no translation found for correction_category (2236750915056607613) -->
+ <skip />
+ <!-- no translation found for gesture_typing_category (497263612130532630) -->
+ <skip />
+ <!-- no translation found for misc_category (6894192814868233453) -->
+ <skip />
+ <!-- no translation found for advanced_settings (362895144495591463) -->
+ <skip />
+ <!-- no translation found for advanced_settings_summary (4487980456152830271) -->
+ <skip />
+ <!-- no translation found for include_other_imes_in_language_switch_list (4533689960308565519) -->
+ <skip />
+ <!-- no translation found for include_other_imes_in_language_switch_list_summary (840637129103317635) -->
+ <skip />
+ <!-- no translation found for show_language_switch_key (5915478828318774384) -->
+ <skip />
+ <!-- no translation found for show_language_switch_key_summary (7343403647474265713) -->
+ <skip />
+ <!-- no translation found for key_preview_popup_dismiss_delay (6213164897443068248) -->
+ <skip />
+ <!-- no translation found for key_preview_popup_dismiss_no_delay (2096123151571458064) -->
+ <skip />
+ <!-- no translation found for key_preview_popup_dismiss_default_delay (2166964333903906734) -->
+ <skip />
+ <!-- no translation found for use_contacts_dict (4435317977804180815) -->
+ <skip />
+ <!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
+ <skip />
+ <!-- no translation found for auto_cap (1719746674854628252) -->
+ <skip />
+ <!-- no translation found for auto_cap_summary (7934452761022946874) -->
+ <skip />
+ <!-- no translation found for configure_dictionaries_title (4238652338556902049) -->
+ <skip />
+ <!-- no translation found for main_dictionary (4798763781818361168) -->
+ <skip />
+ <!-- no translation found for prefs_show_suggestions (8026799663445531637) -->
+ <skip />
+ <!-- no translation found for prefs_show_suggestions_summary (1583132279498502825) -->
+ <skip />
+ <!-- no translation found for prefs_suggestion_visibility_show_name (3219916594067551303) -->
+ <skip />
+ <!-- no translation found for prefs_suggestion_visibility_show_only_portrait_name (3859783767435239118) -->
+ <skip />
+ <!-- no translation found for prefs_suggestion_visibility_hide_name (6309143926422234673) -->
+ <skip />
+ <!-- no translation found for auto_correction (7630720885194996950) -->
+ <skip />
+ <!-- no translation found for auto_correction_summary (5625751551134658006) -->
+ <skip />
+ <!-- no translation found for auto_correction_threshold_mode_off (8470882665417944026) -->
+ <skip />
+ <!-- no translation found for auto_correction_threshold_mode_modest (8788366690620799097) -->
+ <skip />
+ <!-- no translation found for auto_correction_threshold_mode_aggeressive (3524029103734923819) -->
+ <skip />
+ <!-- no translation found for auto_correction_threshold_mode_very_aggeressive (3386782235540547678) -->
+ <skip />
+ <!-- no translation found for bigram_prediction (1084449187723948550) -->
+ <skip />
+ <!-- no translation found for bigram_prediction_summary (3896362682751109677) -->
+ <skip />
+ <!-- no translation found for gesture_input (826951152254563827) -->
+ <skip />
+ <!-- no translation found for gesture_input_summary (9180350639305731231) -->
+ <skip />
+ <!-- no translation found for gesture_preview_trail (3802333369335722221) -->
+ <skip />
+ <!-- no translation found for gesture_floating_preview_text (4443240334739381053) -->
+ <skip />
+ <!-- no translation found for gesture_floating_preview_text_summary (4472696213996203533) -->
+ <skip />
+ <!-- no translation found for added_word (8993883354622484372) -->
+ <skip />
+ <string name="label_go_key" msgid="1635148082137219148">"Оди"</string>
+ <string name="label_next_key" msgid="362972844525672568">"Следно"</string>
+ <string name="label_previous_key" msgid="1211868118071386787">"Претходно"</string>
+ <string name="label_done_key" msgid="2441578748772529288">"Готово"</string>
+ <string name="label_send_key" msgid="2815056534433717444">"Испрати"</string>
+ <string name="label_to_alpha_key" msgid="4793983863798817523">"АБВ"</string>
+ <!-- no translation found for label_to_symbol_key (8516904117128967293) -->
+ <skip />
+ <!-- no translation found for label_to_symbol_with_microphone_key (9035925553010061906) -->
+ <skip />
+ <!-- no translation found for label_pause_key (181098308428035340) -->
+ <skip />
+ <!-- no translation found for label_wait_key (6402152600878093134) -->
+ <skip />
+ <!-- no translation found for spoken_use_headphones (896961781287283493) -->
+ <skip />
+ <!-- no translation found for spoken_current_text_is (2485723011272583845) -->
+ <skip />
+ <!-- no translation found for spoken_no_text_entered (7479685225597344496) -->
+ <skip />
+ <!-- no translation found for spoken_description_unknown (3197434010402179157) -->
+ <skip />
+ <!-- no translation found for spoken_description_shift (244197883292549308) -->
+ <skip />
+ <!-- no translation found for spoken_description_shift_shifted (1681877323344195035) -->
+ <skip />
+ <!-- no translation found for spoken_description_caps_lock (3276478269526304432) -->
+ <skip />
+ <!-- no translation found for spoken_description_delete (8740376944276199801) -->
+ <skip />
+ <!-- no translation found for spoken_description_to_symbol (5486340107500448969) -->
+ <skip />
+ <!-- no translation found for spoken_description_to_alpha (23129338819771807) -->
+ <skip />
+ <!-- no translation found for spoken_description_to_numeric (591752092685161732) -->
+ <skip />
+ <!-- no translation found for spoken_description_settings (4627462689603838099) -->
+ <skip />
+ <!-- no translation found for spoken_description_tab (2667716002663482248) -->
+ <skip />
+ <!-- no translation found for spoken_description_space (2582521050049860859) -->
+ <skip />
+ <!-- no translation found for spoken_description_mic (615536748882611950) -->
+ <skip />
+ <!-- no translation found for spoken_description_smiley (2256309826200113918) -->
+ <skip />
+ <!-- no translation found for spoken_description_return (8178083177238315647) -->
+ <skip />
+ <!-- no translation found for spoken_description_search (1247236163755920808) -->
+ <skip />
+ <!-- no translation found for spoken_description_dot (40711082435231673) -->
+ <skip />
+ <!-- no translation found for spoken_description_language_switch (5507091328222331316) -->
+ <skip />
+ <!-- no translation found for spoken_description_action_next (8636078276664150324) -->
+ <skip />
+ <!-- no translation found for spoken_description_action_previous (800872415009336208) -->
+ <skip />
+ <!-- no translation found for spoken_description_shiftmode_on (5700440798609574589) -->
+ <skip />
+ <!-- no translation found for spoken_description_shiftmode_locked (593175803181701830) -->
+ <skip />
+ <!-- no translation found for spoken_description_shiftmode_off (657219998449174808) -->
+ <skip />
+ <!-- no translation found for spoken_description_mode_symbol (7183343879909747642) -->
+ <skip />
+ <!-- no translation found for spoken_description_mode_alpha (3528307674390156956) -->
+ <skip />
+ <!-- no translation found for spoken_description_mode_phone (6520207943132026264) -->
+ <skip />
+ <!-- no translation found for spoken_description_mode_phone_shift (5499629753962641227) -->
+ <skip />
+ <!-- no translation found for voice_input (3583258583521397548) -->
+ <skip />
+ <!-- no translation found for voice_input_modes_main_keyboard (3360660341121083174) -->
+ <skip />
+ <!-- no translation found for voice_input_modes_symbols_keyboard (7203213240786084067) -->
+ <skip />
+ <!-- no translation found for voice_input_modes_off (3745699748218082014) -->
+ <skip />
+ <!-- no translation found for voice_input_modes_summary_main_keyboard (6586544292900314339) -->
+ <skip />
+ <!-- no translation found for voice_input_modes_summary_symbols_keyboard (5233725927281932391) -->
+ <skip />
+ <!-- no translation found for voice_input_modes_summary_off (63875609591897607) -->
+ <skip />
+ <!-- no translation found for configure_input_method (373356270290742459) -->
+ <skip />
+ <!-- no translation found for language_selection_title (1651299598555326750) -->
+ <skip />
+ <!-- no translation found for select_language (3693815588777926848) -->
+ <skip />
+ <!-- no translation found for hint_add_to_dictionary (573678656946085380) -->
+ <skip />
+ <!-- no translation found for has_dictionary (6071847973466625007) -->
+ <skip />
+ <!-- no translation found for prefs_enable_log (6620424505072963557) -->
+ <skip />
+ <!-- no translation found for prefs_description_log (5827825607258246003) -->
+ <skip />
+ <!-- no translation found for keyboard_layout (8451164783510487501) -->
+ <skip />
+ <!-- no translation found for subtype_en_GB (88170601942311355) -->
+ <skip />
+ <!-- no translation found for subtype_en_US (6160452336634534239) -->
+ <skip />
+ <!-- no translation found for subtype_with_layout_en_GB (2179097748724725906) -->
+ <skip />
+ <!-- no translation found for subtype_with_layout_en_US (1362581347576714579) -->
+ <skip />
+ <!-- no translation found for subtype_no_language (141420857808801746) -->
+ <skip />
+ <!-- no translation found for subtype_no_language_qwerty (2956121451616633133) -->
+ <skip />
+ <!-- no translation found for subtype_no_language_qwertz (1177848172397202890) -->
+ <skip />
+ <!-- no translation found for subtype_no_language_azerty (8721460968141187394) -->
+ <skip />
+ <!-- no translation found for subtype_no_language_dvorak (3122976737669823935) -->
+ <skip />
+ <!-- no translation found for subtype_no_language_colemak (4205992994906097244) -->
+ <skip />
+ <!-- no translation found for subtype_no_language_pcqwerty (8840928374394180189) -->
+ <skip />
+ <!-- no translation found for custom_input_styles_title (8429952441821251512) -->
+ <skip />
+ <!-- no translation found for add_style (6163126614514489951) -->
+ <skip />
+ <!-- no translation found for add (8299699805688017798) -->
+ <skip />
+ <!-- no translation found for remove (4486081658752944606) -->
+ <skip />
+ <!-- no translation found for save (7646738597196767214) -->
+ <skip />
+ <!-- no translation found for subtype_locale (8576443440738143764) -->
+ <skip />
+ <!-- no translation found for keyboard_layout_set (4309233698194565609) -->
+ <skip />
+ <!-- no translation found for custom_input_style_note_message (8826731320846363423) -->
+ <skip />
+ <!-- no translation found for enable (5031294444630523247) -->
+ <skip />
+ <!-- no translation found for not_now (6172462888202790482) -->
+ <skip />
+ <!-- no translation found for custom_input_style_already_exists (8008728952215449707) -->
+ <skip />
+ <!-- no translation found for prefs_usability_study_mode (1261130555134595254) -->
+ <skip />
+ <!-- no translation found for prefs_keypress_vibration_duration_settings (1829950405285211668) -->
+ <skip />
+ <!-- no translation found for prefs_keypress_sound_volume_settings (5875933757082305040) -->
+ <skip />
+</resources>
diff --git a/java/res/values-ms/bools.xml b/java/res/values-ms/bools.xml
new file mode 100644
index 000000000..840d20c21
--- /dev/null
+++ b/java/res/values-ms/bools.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<resources>
+ <!-- Whether this input method should be used as the default for a locale. Override it
+ for supported languages. -->
+ <bool name="im_is_default">true</bool>
+</resources>
diff --git a/java/res/values-ms/strings-appname.xml b/java/res/values-ms/strings-appname.xml
new file mode 100644
index 000000000..73b553751
--- /dev/null
+++ b/java/res/values-ms/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Papan kekunci Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Penyemak ejaan Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Tetapan papan kekunci Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Tetapan penyemakan ejaan"</string>
+</resources>
diff --git a/java/res/values-ms/strings.xml b/java/res/values-ms/strings.xml
index a07c44f2c..9b9d6ad80 100644
--- a/java/res/values-ms/strings.xml
+++ b/java/res/values-ms/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Papan kekunci Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Papan kekunci Android (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Tetapan papan kekunci Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Pilihan input"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Penyemak ejaan Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Arahan Log Penyelidikan"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Penyemak ejaan Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Tetapan penyemakan ejaan"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Cari nama kenalan"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Penyemak ejaan menggunakan entri dari senarai kenalan anda"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Getar pada tekanan kekunci"</string>
@@ -34,37 +31,41 @@
<string name="popup_on_keypress" msgid="123894815723512944">"Pop timbul pada tekanan kunci"</string>
<string name="general_category" msgid="1859088467017573195">"Umum"</string>
<string name="correction_category" msgid="2236750915056607613">"Pembetulan teks"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"Taipan gerak isyarat"</string>
<string name="misc_category" msgid="6894192814868233453">"Pilihan lain"</string>
<string name="advanced_settings" msgid="362895144495591463">"Tetapan terperinci"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"Pilihan untuk pakar"</string>
<string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Tukar ke kaedah input lain"</string>
<string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Kunci pertukaran bahasa meliputi kaedah masukan lain juga"</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"Tekan kunci alih bahasa"</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"Kekunci tukar bahasa"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Tunjukkan apabila berbilang bahasa input didayakan"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Pop tmbl knci ketpkn lengah"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Tiada lengah"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Lalai"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Cadangkan nama Kenalan"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Menggunakan nama daripada Kenalan untuk cadangan dan pembetulan"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Dayakan pembetulan semula"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Tetapkan cadangan untuk pembetulan semula"</string>
<string name="auto_cap" msgid="1719746674854628252">"Huruf besar auto"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"Besarkan perkataan pertama setiap ayat"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Kamus tambahan"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Kamus utama"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"Tunjukkan cadangan pembetulan"</string>
<string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Paparkan cadangan perkataan semasa menaip"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Sentiasa tunjukkan"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Tunjukkan pada mod potret"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Tunjukkan dalam mod potret"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Sentiasa sembunyikan"</string>
- <string name="auto_correction" msgid="4979925752001319458">"Auto Pembetulan"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"Auto pembetulan"</string>
<string name="auto_correction_summary" msgid="5625751551134658006">"Bar ruang dan tanda baca secara automatik membetulkan perkataan yang ditaip salah"</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Matikan"</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Sederhana"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agresif"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Sangat agresif"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Cadangan perkataan seterusnya"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Gunakan perkataan sebelumnya untuk memperbaik cadangan"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Ramalan perkataan seterusnya"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Gunakan juga perkataan sebelumnya untuk ramalan"</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"Cadangan perkataan seterusnya"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"Gunakan perkataan sebelumnya dalam membuat cadangan"</string>
+ <string name="gesture_input" msgid="826951152254563827">"Dayakan taipan gerak isyarat"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"Input perkataan dengan meluncur melalui huruf"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Tunjukkan jejak gerak isyarat"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"Pratonton terapung dinamik"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"Lihat perkataan yang dicadangkan semasa membuat gerak isyarat"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Disimpan"</string>
<string name="label_go_key" msgid="1635148082137219148">"Pergi"</string>
<string name="label_next_key" msgid="362972844525672568">"Seterusnya"</string>
@@ -95,6 +96,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Return"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Carian"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Titik"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Tukar bahasa"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Seterusnya"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Sebelumnya"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Kunci anjak didayakan"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Kunci huruf besar didayakan"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Kunci anjak dilumpuhkan"</string>
diff --git a/java/res/values-nb/bools.xml b/java/res/values-nb/bools.xml
new file mode 100644
index 000000000..840d20c21
--- /dev/null
+++ b/java/res/values-nb/bools.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<resources>
+ <!-- Whether this input method should be used as the default for a locale. Override it
+ for supported languages. -->
+ <bool name="im_is_default">true</bool>
+</resources>
diff --git a/java/res/values-nb/strings-appname.xml b/java/res/values-nb/strings-appname.xml
new file mode 100644
index 000000000..56c1c3c71
--- /dev/null
+++ b/java/res/values-nb/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Android-tastatur"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Android-stavekontroll"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Innstillinger for Android-tastatur"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Innstillinger for stavekontroll"</string>
+</resources>
diff --git a/java/res/values-nb/strings.xml b/java/res/values-nb/strings.xml
index 75c1295a9..90a5dfba7 100644
--- a/java/res/values-nb/strings.xml
+++ b/java/res/values-nb/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Skjermtastatur"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android-tastatur (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Innstillinger for skjermtastatur"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Inndataalternativer"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Android-stavekontroll"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Kommandoer for undersøkelseslogging"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android-stavekontroll (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Innstillinger for stavekontroll"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Slå opp kontaktnavn"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Stavekontrollen bruker oppføringer fra kontaktlisten din"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrer ved tastetrykk"</string>
@@ -34,37 +31,41 @@
<string name="popup_on_keypress" msgid="123894815723512944">"Hurtigvindu ved tastetrykk"</string>
<string name="general_category" msgid="1859088467017573195">"Generelt"</string>
<string name="correction_category" msgid="2236750915056607613">"Tekstkorrigering"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"Ordføring"</string>
<string name="misc_category" msgid="6894192814868233453">"Andre alternativer"</string>
<string name="advanced_settings" msgid="362895144495591463">"Avanserte innstillinger"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"Alternativer for eksperter"</string>
<string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Bytt inndatametode"</string>
<string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Tasten for språkbytte dekker også andre inndatametoder"</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"Skjul språkbyttetasten"</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"Nøkkel for språkskifte"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Vis når flere inndataspråk er aktivert"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Tregt tastevindu"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"U/ forsinkelse"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Standard"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Foreslå kontaktnavn"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Bruk navn fra Kontakter til forslag og korrigeringer"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Aktiver korrigeringer"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Angi forslag for korrigeringer"</string>
<string name="auto_cap" msgid="1719746674854628252">"Stor forbokstav"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"Sett stor bokstav i det første ordet i hver setning"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Tilleggsordbøker"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Hovedordliste"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"Vis rettingsforslag"</string>
<string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Vis ordforslag under skriving"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Vis alltid"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Vis i stående modus"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Vis i stående modus"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Skjul alltid"</string>
- <string name="auto_correction" msgid="4979925752001319458">"Automatisk retting"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"Autokorrektur"</string>
<string name="auto_correction_summary" msgid="5625751551134658006">"Mellomromstast og skilletegn retter automat. feilstavede ord"</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Av"</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Moderat"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Omfattende"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Veldig aggressiv"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Forslag til rettelser av neste ord"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Bruk forrige ord til å forbedre forslagene"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Forslag til neste ord"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Bruk forrige ord også for forslag"</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"Forslag til neste ord"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"Bruk forrige ord til å lage forslag"</string>
+ <string name="gesture_input" msgid="826951152254563827">"Aktiver ordføring"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"Skriv inn et ord ved å sveipe langsmed bokstavene"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Vis bevegelsesspor"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"Dynamisk flytende forhåndsvsn."</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"Se det foreslåtte ordet mens du utfører bevegelser"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: Lagret"</string>
<string name="label_go_key" msgid="1635148082137219148">"Utfør"</string>
<string name="label_next_key" msgid="362972844525672568">"Neste"</string>
@@ -95,6 +96,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Return"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Søk"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Prikk"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Bytt språk"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Neste"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Forrige"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift er aktivert"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps Lock er aktivert"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift er deaktivert"</string>
diff --git a/java/res/values-nl/bools.xml b/java/res/values-nl/bools.xml
index 897f4b3db..c289e5bf3 100644
--- a/java/res/values-nl/bools.xml
+++ b/java/res/values-nl/bools.xml
@@ -1,22 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-/*
+/*
**
** Copyright 2008, 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
+** 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
+** 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
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<resources>
- <bool name="im_is_default">true</bool>
+ <!-- Whether this input method should be used as the default for a locale. Override it
+ for supported languages. -->
+ <bool name="im_is_default">true</bool>
</resources>
diff --git a/java/res/values-nl/strings-appname.xml b/java/res/values-nl/strings-appname.xml
new file mode 100644
index 000000000..ee288efbb
--- /dev/null
+++ b/java/res/values-nl/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Android-toetsenbord"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Spellingcontrole van Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Instellingen voor Android-toetsenbord"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Instellingen voor spellingcontrole"</string>
+</resources>
diff --git a/java/res/values-nl/strings.xml b/java/res/values-nl/strings.xml
index a7d499bd0..f564b1b6b 100644
--- a/java/res/values-nl/strings.xml
+++ b/java/res/values-nl/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Android-toetsenbord"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android-toetsenbord (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Instellingen voor Android-toetsenbord"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Invoeropties"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Spellingcontrole van Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Opdrachten in onderzoekslogbestand"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Spellingcontrole van Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Instellingen voor spellingcontrole"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Contactnamen opzoeken"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"De spellingcontrole gebruikt items uit uw contactenlijst"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Trillen bij toetsaanslag"</string>
@@ -34,37 +31,41 @@
<string name="popup_on_keypress" msgid="123894815723512944">"Pop-up bij toetsaanslag"</string>
<string name="general_category" msgid="1859088467017573195">"Algemeen"</string>
<string name="correction_category" msgid="2236750915056607613">"Tekstcorrectie"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"Typen via tekenen"</string>
<string name="misc_category" msgid="6894192814868233453">"Andere opties"</string>
<string name="advanced_settings" msgid="362895144495591463">"Geavanceerde instellingen"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"Opties voor experts"</string>
<string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Invoermeth. overschakelen"</string>
<string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Schakelknop voor taal ook van toepassing op andere invoermethoden"</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"Taal schakelen onderdr."</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"Schakelknop voor taal"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Weergeven wanneer meerdere invoertalen zijn geselecteerd"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Afwijz.vertr. toetspop-up"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Geen vertraging"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Standaard"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Contactnamen suggereren"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Namen uit Contacten gebruiken voor suggesties en correcties"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Verbeteringen inschakelen"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Suggesties instellen voor verbeteringen"</string>
<string name="auto_cap" msgid="1719746674854628252">"Auto-hoofdlettergebruik"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"Het eerste woord van elke zin met een hoofdletter schrijven"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Woordenboeken toevoegen"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Algemeen woordenboek"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"Correctievoorstellen weergeven"</string>
<string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Voorgestelde woorden weergeven tijdens typen"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Altijd weergeven"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Weergeven in staande modus"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Weergeven in staande modus"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Altijd verbergen"</string>
- <string name="auto_correction" msgid="4979925752001319458">"Autocorrectie"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"Autocorrectie"</string>
<string name="auto_correction_summary" msgid="5625751551134658006">"Met spatiebalk en interpunctie worden verkeerd gespelde woorden automatisch gecorrigeerd"</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Uitgeschakeld"</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Normaal"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agressief"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Zeer agressief"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Volgende woordsuggesties"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Vorig woord gebruiken om suggesties te verbeteren"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Volgende woordvoorspelling"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Het voorgaande woord ook voor voorspelling gebruiken"</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"Suggesties voor volgend woord"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"Het vorige woord gebruiken bij het doen van suggesties"</string>
+ <string name="gesture_input" msgid="826951152254563827">"Typen via tekenen inschakelen"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"Voer een woord in door van letter naar letter te schuiven"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Gebarenspoor weergeven"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"Dynamisch zwevend voorbeeld"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"Het voorgestelde woord weergeven tijdens het tekenen"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: opgeslagen"</string>
<string name="label_go_key" msgid="1635148082137219148">"Start"</string>
<string name="label_next_key" msgid="362972844525672568">"Verder"</string>
@@ -95,6 +96,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Return"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Zoeken"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Stip"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Taal wijzigen"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Volgende"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Vorige"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift ingeschakeld"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps Lock ingeschakeld"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift uitgeschakeld"</string>
diff --git a/java/res/values-pl/bools.xml b/java/res/values-pl/bools.xml
index 897f4b3db..c289e5bf3 100644
--- a/java/res/values-pl/bools.xml
+++ b/java/res/values-pl/bools.xml
@@ -1,22 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-/*
+/*
**
** Copyright 2008, 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
+** 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
+** 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
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<resources>
- <bool name="im_is_default">true</bool>
+ <!-- Whether this input method should be used as the default for a locale. Override it
+ for supported languages. -->
+ <bool name="im_is_default">true</bool>
</resources>
diff --git a/java/res/values-pl/strings-appname.xml b/java/res/values-pl/strings-appname.xml
new file mode 100644
index 000000000..e460644a3
--- /dev/null
+++ b/java/res/values-pl/strings-appname.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Klawiatura Android"</string>
+ <!-- no translation found for spell_checker_service_name (6268342166872202903) -->
+ <skip />
+ <!-- no translation found for english_ime_settings (7470027018752707691) -->
+ <skip />
+ <!-- no translation found for android_spell_checker_settings (8397842018475560441) -->
+ <skip />
+</resources>
diff --git a/java/res/values-pl/strings.xml b/java/res/values-pl/strings.xml
index bf27782b6..9b73ad12d 100644
--- a/java/res/values-pl/strings.xml
+++ b/java/res/values-pl/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Klawiatura Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Klawiatura Androida (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Ustawienia klawiatury Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Opcje wprowadzania"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Słownik Androida"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Polecenia dziennika badań"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Sprawdzanie pisowni na Androidzie (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Ustawienia sprawdzania pisowni"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Przeszukaj kontakty"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Sprawdzanie pisowni bierze pod uwagę wpisy z listy kontaktów."</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Wibracja przy naciśnięciu"</string>
@@ -34,37 +31,41 @@
<string name="popup_on_keypress" msgid="123894815723512944">"Powiększ po naciśnięciu"</string>
<string name="general_category" msgid="1859088467017573195">"Ogólne"</string>
<string name="correction_category" msgid="2236750915056607613">"Poprawianie tekstu"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"Pisanie gestami"</string>
<string name="misc_category" msgid="6894192814868233453">"Inne opcje"</string>
<string name="advanced_settings" msgid="362895144495591463">"Ustawienia zaawansowane"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"Opcje dla ekspertów"</string>
<string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Włącz inne metody wprowadzania"</string>
<string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Klawisz zmiany języka obejmuje też inne metody wprowadzania"</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"Wyłącz klawisz zmiany języka"</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"Klawisz zmiany języka"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Pokaż, gdy włączonych jest kilka języków wprowadzania"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Opóźnienie znikania klawiszy"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Bez opóźnienia"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Wartość domyślna"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Proponuj osoby z kontaktów"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"W propozycjach i poprawkach użyj nazwisk z kontaktów"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Włącz poprawki"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Ustaw sugestie poprawek"</string>
<string name="auto_cap" msgid="1719746674854628252">"Wstawiaj wielkie litery"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"Zaczynaj każde zdanie wielką literą"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Dodatkowe słowniki"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Słownik główny"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"Pokazuj propozycje poprawek"</string>
<string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Wyświetl proponowane słowa podczas wpisywania"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Zawsze pokazuj"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Pokaż w trybie pionowym"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Pokaż w trybie pionowym"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Zawsze ukrywaj"</string>
- <string name="auto_correction" msgid="4979925752001319458">"Autokorekta"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"Autokorekta"</string>
<string name="auto_correction_summary" msgid="5625751551134658006">"Spacja i znaki przestankowe poprawiają błędnie wpisane słowa"</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Wyłącz"</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Umiarkowana"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agresywna"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Bardzo agresywna"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Sugestie kolejnych słów"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Używaj poprzedniego wyrazu, by polepszyć sugestie"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Przewidywanie następnego wyrazu"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Przewiduj również na podstawie poprzedniego słowa"</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"Podpowiadanie kolejnego słowa"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"Pokazuj podpowiedzi na podstawie poprzedniego słowa"</string>
+ <string name="gesture_input" msgid="826951152254563827">"Włącz pisanie gestami"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"Wpisz słowo, przesuwając palcem po literach"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Pokazuj ślad gestu"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"Dynamiczny podgląd słowa"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"Podczas gestykulacji będzie widoczne podpowiadane słowo"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Zapisano"</string>
<string name="label_go_key" msgid="1635148082137219148">"OK"</string>
<string name="label_next_key" msgid="362972844525672568">"Dalej"</string>
@@ -90,11 +91,14 @@
<string name="spoken_description_settings" msgid="4627462689603838099">"Ustawienia"</string>
<string name="spoken_description_tab" msgid="2667716002663482248">"Tab"</string>
<string name="spoken_description_space" msgid="2582521050049860859">"Spacja"</string>
- <string name="spoken_description_mic" msgid="615536748882611950">"Wprowadzanie głosowe"</string>
+ <string name="spoken_description_mic" msgid="615536748882611950">"Rozpoznawanie mowy"</string>
<string name="spoken_description_smiley" msgid="2256309826200113918">"Uśmiechnięta buźka"</string>
<string name="spoken_description_return" msgid="8178083177238315647">"Enter"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Szukaj"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Punkt"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Przełącz język"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Dalej"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Wstecz"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift włączony"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps lock włączony"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift wyłączony"</string>
@@ -108,7 +112,7 @@
<string name="voice_input_modes_off" msgid="3745699748218082014">"Wyłącz"</string>
<string name="voice_input_modes_summary_main_keyboard" msgid="6586544292900314339">"Mikrofon na klawiaturze głównej"</string>
<string name="voice_input_modes_summary_symbols_keyboard" msgid="5233725927281932391">"Mikrofon na klawiaturze z symbolami"</string>
- <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Wprowadzanie głosowe jest wyłączone"</string>
+ <string name="voice_input_modes_summary_off" msgid="63875609591897607">"Rozpoznawanie mowy jest wyłączone"</string>
<string name="configure_input_method" msgid="373356270290742459">"Konfiguruj metody wprowadzania"</string>
<string name="language_selection_title" msgid="1651299598555326750">"Języki wprowadzania"</string>
<string name="select_language" msgid="3693815588777926848">"Języki wprowadzania"</string>
diff --git a/java/res/values-pt-rPT/strings-appname.xml b/java/res/values-pt-rPT/strings-appname.xml
new file mode 100644
index 000000000..1b88acb69
--- /dev/null
+++ b/java/res/values-pt-rPT/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Teclado do Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Verificador ortográfico do Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Definições de teclado do Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Definições da verificação ortográfica"</string>
+</resources>
diff --git a/java/res/values-pt-rPT/strings.xml b/java/res/values-pt-rPT/strings.xml
index ccb042e47..0f5490057 100644
--- a/java/res/values-pt-rPT/strings.xml
+++ b/java/res/values-pt-rPT/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Teclado do Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Teclado Android (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Definições de teclado do Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Opções de introdução"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Verificador ortográfico do Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Comandos de Reg. Invest."</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Verificador ortográfico do Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Definições da verificação ortográfica"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Procurar nomes de contac."</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"O corretor ortográfico utiliza entradas da sua lista de contactos"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrar ao primir as teclas"</string>
@@ -34,37 +31,41 @@
<string name="popup_on_keypress" msgid="123894815723512944">"Mostrar popup ao premir tecla"</string>
<string name="general_category" msgid="1859088467017573195">"Geral"</string>
<string name="correction_category" msgid="2236750915056607613">"Correção de texto"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"Escrita por toque"</string>
<string name="misc_category" msgid="6894192814868233453">"Outras opções"</string>
<string name="advanced_settings" msgid="362895144495591463">"Definições avançadas"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"Opções para especialistas"</string>
<string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Mudar p/ outros mét. ent."</string>
<string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"A tecla de mudança de idioma abrange outros métodos de entrada"</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"Suprimir tecla alt idioma"</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"Tecla alterar idioma"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Mostrar quando estão ativados vários idiomas de entrada"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Atraso p/ ignorar pop-up"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Sem atraso"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Predefinido"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Sugerir nomes de Contactos"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Utilizar nomes dos Contactos para sugestões e correções"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Ativar correções"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Definir sugestões para correções"</string>
<string name="auto_cap" msgid="1719746674854628252">"Letras maiúsculas automáticas"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"Maiúscula no início da frase"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Dicionários extras"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Dicionário principal"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"Mostrar sugestões de correcção"</string>
<string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Apresentar sugestões de palavras ao escrever"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Mostrar sempre"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Mostrar no modo de retrato"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Mostrar em modo retrato"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Ocultar sempre"</string>
- <string name="auto_correction" msgid="4979925752001319458">"Auto correcção"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"Correção automática"</string>
<string name="auto_correction_summary" msgid="5625751551134658006">"Correcção automática de palavras mal escritas c/ barra de espaços e pontuação"</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Desligar"</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Moderada"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agressiva"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Muito agressivo"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Sugestões da palavra seguinte"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Utilizar palavra anterior para melhorar sugestões"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Previsão da palavra seguinte"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Utilizar a palavra anterior também para predição"</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"Sugestões da palavra seguinte"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"Utilizar palavra anterior para fazer sugestões"</string>
+ <string name="gesture_input" msgid="826951152254563827">"Ativar escrita por toque"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"Introduzir uma palavra deslizando lentamente pelas letras"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Mostrar percurso do gesto"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"Pré-visual. flutuante dinâmica"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"Ver palavra sugerida enquanto toca"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: guardada"</string>
<string name="label_go_key" msgid="1635148082137219148">"OK"</string>
<string name="label_next_key" msgid="362972844525672568">"Avançar"</string>
@@ -95,6 +96,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Enter"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Pesquisar"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Ponto"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Mudar de idioma"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Seguinte"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Anterior"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift ativado"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps lock ativado"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift desativado"</string>
diff --git a/java/res/values-pt/bools.xml b/java/res/values-pt/bools.xml
new file mode 100644
index 000000000..840d20c21
--- /dev/null
+++ b/java/res/values-pt/bools.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<resources>
+ <!-- Whether this input method should be used as the default for a locale. Override it
+ for supported languages. -->
+ <bool name="im_is_default">true</bool>
+</resources>
diff --git a/java/res/values-pt/strings-appname.xml b/java/res/values-pt/strings-appname.xml
new file mode 100644
index 000000000..d78786d63
--- /dev/null
+++ b/java/res/values-pt/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Teclado do Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Corretor ortográfico do Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Configurações de teclado do Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Configurações de verificação ortográfica"</string>
+</resources>
diff --git a/java/res/values-pt/strings.xml b/java/res/values-pt/strings.xml
index 7d64759d8..098d24565 100644
--- a/java/res/values-pt/strings.xml
+++ b/java/res/values-pt/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Teclado Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Teclado Android (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Configurações de teclado Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Opções de entrada"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Corretor ortográfico do Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Pesq. comandos de reg."</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Corretor ortográfico do Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Configurações de verificação ortográfica"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Buscar nomes de contatos"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"O corretor ortográfico usa entradas de sua lista de contatos"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrar ao tocar a tecla"</string>
@@ -34,37 +31,41 @@
<string name="popup_on_keypress" msgid="123894815723512944">"Exibir pop-up ao digitar"</string>
<string name="general_category" msgid="1859088467017573195">"Geral"</string>
<string name="correction_category" msgid="2236750915056607613">"Correção de texto"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"Escrita com gestos"</string>
<string name="misc_category" msgid="6894192814868233453">"Outras opções"</string>
<string name="advanced_settings" msgid="362895144495591463">"Configurações avançadas"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"Opções para especialistas"</string>
<string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Outros métodos de entrada"</string>
<string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"A tecla p/ mudar o idioma também cobre outros métodos de entrada"</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"Ocult. tecla mudar idioma"</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"Tecla de seleção de idioma"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Mostrar quando vários idiomas de entrada estiverem ativados"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Dispens. atraso chave princ."</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Sem atraso"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Padrão"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Sugerir nomes de contato"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Usar nomes dos Contatos para sugestões e correções"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Ativar recorreções"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Definir sugestões para recorreções"</string>
<string name="auto_cap" msgid="1719746674854628252">"Capitaliz. automática"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"Iniciar a primeira palavra de cada frase com letra maiúscula"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Dicionários complementares"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Dicionário principal"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"Exibir sugestões de correção"</string>
<string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Exibir sugestões de palavras durante a digitação"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Mostrar sempre"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Mostrar em modo retrato"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Mostrar em modo de retrato"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Sempre ocultar"</string>
- <string name="auto_correction" msgid="4979925752001319458">"Autocorreção"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"Correção automática"</string>
<string name="auto_correction_summary" msgid="5625751551134658006">"A barra de espaço e a pontuação corrigem automaticamente palavras com erro de digitação"</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Desativado"</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Moderado"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agressivo"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Muito agressivo"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Sugestões p/ palavra seguinte"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Usar palavra anterior para melhorar as sugestões"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Previsão da palavra seguinte"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Use também a palavra anterior para prever"</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"Sugestões para a palavra seguinte"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"Usar a palavra anterior ao fazer sugestões"</string>
+ <string name="gesture_input" msgid="826951152254563827">"Ativar a escrita com gestos"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"Inserir uma palavra deslizando os dedos pelas letras"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Mostrar percurso do gesto"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"Visualizaç. dinâmica flutuante"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"Ver a palavra sugerida ao usar gestos"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Salvo"</string>
<string name="label_go_key" msgid="1635148082137219148">"Ir"</string>
<string name="label_next_key" msgid="362972844525672568">"Avançar"</string>
@@ -95,6 +96,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Voltar"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Pesquisar"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Ponto"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Alterar idioma"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Próximo"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Anterior"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift ativado"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps lock ativado"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift desativado"</string>
diff --git a/java/res/values-rm/strings.xml b/java/res/values-rm/strings.xml
index 18741f816..26cf8f877 100644
--- a/java/res/values-rm/strings.xml
+++ b/java/res/values-rm/strings.xml
@@ -20,18 +20,14 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Tastatura Android"</string>
<!-- no translation found for aosp_android_keyboard_ime_name (7877134937939182296) -->
<skip />
- <string name="english_ime_settings" msgid="6661589557206947774">"Parameters da la tastatura Android"</string>
<!-- no translation found for english_ime_input_options (3909945612939668554) -->
<skip />
- <!-- no translation found for spell_checker_service_name (7338064335159755926) -->
+ <!-- no translation found for english_ime_research_log (8492602295696577851) -->
<skip />
<!-- no translation found for aosp_spell_checker_service_name (6985142605330377819) -->
<skip />
- <!-- no translation found for android_spell_checker_settings (5822324635435443689) -->
- <skip />
<!-- no translation found for use_contacts_for_spellchecking_option_title (5374120998125353898) -->
<skip />
<!-- no translation found for use_contacts_for_spellchecking_option_summary (8754413382543307713) -->
@@ -43,6 +39,8 @@
<skip />
<!-- no translation found for correction_category (2236750915056607613) -->
<skip />
+ <!-- no translation found for gesture_typing_category (497263612130532630) -->
+ <skip />
<!-- no translation found for misc_category (6894192814868233453) -->
<skip />
<!-- no translation found for advanced_settings (362895144495591463) -->
@@ -53,7 +51,9 @@
<skip />
<!-- no translation found for include_other_imes_in_language_switch_list_summary (840637129103317635) -->
<skip />
- <!-- no translation found for suppress_language_switch_key (8003788410354806368) -->
+ <!-- no translation found for show_language_switch_key (5915478828318774384) -->
+ <skip />
+ <!-- no translation found for show_language_switch_key_summary (7343403647474265713) -->
<skip />
<!-- no translation found for key_preview_popup_dismiss_delay (6213164897443068248) -->
<skip />
@@ -65,11 +65,9 @@
<skip />
<!-- no translation found for use_contacts_dict_summary (6599983334507879959) -->
<skip />
- <!-- no translation found for enable_span_insert (7204653105667167620) -->
- <skip />
- <!-- no translation found for enable_span_insert_summary (2947317657871394467) -->
- <skip />
<string name="auto_cap" msgid="1719746674854628252">"Maiusclas automaticas"</string>
+ <!-- no translation found for auto_cap_summary (7934452761022946874) -->
+ <skip />
<!-- no translation found for configure_dictionaries_title (4238652338556902049) -->
<skip />
<!-- no translation found for main_dictionary (4798763781818361168) -->
@@ -80,11 +78,11 @@
<skip />
<!-- no translation found for prefs_suggestion_visibility_show_name (3219916594067551303) -->
<skip />
- <!-- no translation found for prefs_suggestion_visibility_show_only_portrait_name (3551821800439659812) -->
+ <!-- no translation found for prefs_suggestion_visibility_show_only_portrait_name (3859783767435239118) -->
<skip />
<!-- no translation found for prefs_suggestion_visibility_hide_name (6309143926422234673) -->
<skip />
- <!-- no translation found for auto_correction (4979925752001319458) -->
+ <!-- no translation found for auto_correction (7630720885194996950) -->
<skip />
<!-- no translation found for auto_correction_summary (5625751551134658006) -->
<skip />
@@ -96,13 +94,19 @@
<skip />
<!-- no translation found for auto_correction_threshold_mode_very_aggeressive (3386782235540547678) -->
<skip />
- <!-- no translation found for bigram_suggestion (8169311444438922902) -->
+ <!-- no translation found for bigram_prediction (1084449187723948550) -->
<skip />
- <!-- no translation found for bigram_suggestion_summary (6635527607242625713) -->
+ <!-- no translation found for bigram_prediction_summary (3896362682751109677) -->
<skip />
- <!-- no translation found for bigram_prediction (3216364899483135294) -->
+ <!-- no translation found for gesture_input (826951152254563827) -->
<skip />
- <!-- no translation found for bigram_prediction_summary (1747261921174300098) -->
+ <!-- no translation found for gesture_input_summary (9180350639305731231) -->
+ <skip />
+ <!-- no translation found for gesture_preview_trail (3802333369335722221) -->
+ <skip />
+ <!-- no translation found for gesture_floating_preview_text (4443240334739381053) -->
+ <skip />
+ <!-- no translation found for gesture_floating_preview_text_summary (4472696213996203533) -->
<skip />
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Memorisà"</string>
<string name="label_go_key" msgid="1635148082137219148">"Dai"</string>
@@ -159,6 +163,12 @@
<skip />
<!-- no translation found for spoken_description_dot (40711082435231673) -->
<skip />
+ <!-- no translation found for spoken_description_language_switch (5507091328222331316) -->
+ <skip />
+ <!-- no translation found for spoken_description_action_next (8636078276664150324) -->
+ <skip />
+ <!-- no translation found for spoken_description_action_previous (800872415009336208) -->
+ <skip />
<!-- no translation found for spoken_description_shiftmode_on (5700440798609574589) -->
<skip />
<!-- no translation found for spoken_description_shiftmode_locked (593175803181701830) -->
diff --git a/java/res/values-ro/bools.xml b/java/res/values-ro/bools.xml
new file mode 100644
index 000000000..840d20c21
--- /dev/null
+++ b/java/res/values-ro/bools.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<resources>
+ <!-- Whether this input method should be used as the default for a locale. Override it
+ for supported languages. -->
+ <bool name="im_is_default">true</bool>
+</resources>
diff --git a/java/res/values-ro/strings-appname.xml b/java/res/values-ro/strings-appname.xml
new file mode 100644
index 000000000..dfa642204
--- /dev/null
+++ b/java/res/values-ro/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Tastatură Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Verificator ortografic Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Setările tastaturii Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Setările de verificare ortografică"</string>
+</resources>
diff --git a/java/res/values-ro/strings.xml b/java/res/values-ro/strings.xml
index 0c4d8bcfb..44f2bb1bd 100644
--- a/java/res/values-ro/strings.xml
+++ b/java/res/values-ro/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Tastatură Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Tastatură Android (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Setările tastaturii Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Opţiuni de introducere text"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Verificator ortografic Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Comenzi jurnal cercetare"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Verificator ortografic Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Setări de verificare ortografică"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Verificare nume în agendă"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Verificatorul ortografic utilizează intrări din lista de contacte"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrare la apăsarea tastei"</string>
@@ -34,37 +31,41 @@
<string name="popup_on_keypress" msgid="123894815723512944">"Fereastră pop-up la apăsarea tastei"</string>
<string name="general_category" msgid="1859088467017573195">"General"</string>
<string name="correction_category" msgid="2236750915056607613">"Corectare text"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"Tastare gestuală"</string>
<string name="misc_category" msgid="6894192814868233453">"Alte opţiuni"</string>
<string name="advanced_settings" msgid="362895144495591463">"Setări avansate"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"Opţiuni pentru experţi"</string>
<string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Comut. alte metode de introd."</string>
<string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Tasta de comutare între limbi include şi alte metode de introd."</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"Suprim. tasta comut. limbi"</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"Tastă comutare limbi"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Afişaţi când sunt activate mai multe limbi de intrare"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Înt. înch. pop-up esenţ."</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Fără întârziere"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Prestabilit"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Sugeraţi nume din Agendă"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Utilizaţi numele din Agendă pentru sugestii şi corecţii"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Activaţi rectificările"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Setaţi sugestii pentru rectificări"</string>
<string name="auto_cap" msgid="1719746674854628252">"Auto-capitalizare"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"Scrie cu majusculă primul cuvânt din fiecare propoziţie"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Dicţionare suplimentare"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Dicţionar principal"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"Afişaţi sugestii de corectare"</string>
<string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Afişaţi sugestii de cuvinte în timpul introducerii textului"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Afişaţi întotdeauna"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Afişaţi în modul Portret"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Afişaţi în modul Portret"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Ascundeţi întotdeauna"</string>
- <string name="auto_correction" msgid="4979925752001319458">"Autocorecţie"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"Autocorectare"</string>
<string name="auto_correction_summary" msgid="5625751551134658006">"Corectare automată cuvinte prin bară spaţiu/semne punctuaţie"</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Dezactivată"</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Moderată"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agresivă"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Foarte exigentă"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Sugestii pentru cuvântul următor"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Utilizaţi cuvântul anterior pentru a îmbunătăţi sugestiile"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Predicţia cuvântului următor"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Se utilizează şi cuvântul precedent pentru predicţii"</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"Sugestii pentru cuvântul următor"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"Utilizează cuvântul anterior pentru sugestii"</string>
+ <string name="gesture_input" msgid="826951152254563827">"Activați tastarea gestuală"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"Introduceţi un cuvânt glisând uşor între litere"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Se afişează urma gestului"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"Sugestie flotantă dinamică"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"Afişaţi cuvântul sugerat când utilizaţi gesturi"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: salvat"</string>
<string name="label_go_key" msgid="1635148082137219148">"OK"</string>
<string name="label_next_key" msgid="362972844525672568">"Înainte"</string>
@@ -95,6 +96,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Enter"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Căutaţi"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Punct"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Schimbaţi limba"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Înainte"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Înapoi"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Tasta Shift a fost activată"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Tasta Caps Lock a fost activată"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Tasta Shift a fost dezactivată"</string>
diff --git a/java/res/values-ru/bools.xml b/java/res/values-ru/bools.xml
new file mode 100644
index 000000000..840d20c21
--- /dev/null
+++ b/java/res/values-ru/bools.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<resources>
+ <!-- Whether this input method should be used as the default for a locale. Override it
+ for supported languages. -->
+ <bool name="im_is_default">true</bool>
+</resources>
diff --git a/java/res/values-ru/strings-appname.xml b/java/res/values-ru/strings-appname.xml
new file mode 100644
index 000000000..5db1d0bc9
--- /dev/null
+++ b/java/res/values-ru/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Клавиатура Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Проверка правописания Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Настройки клавиатуры Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Настройки проверки правописания"</string>
+</resources>
diff --git a/java/res/values-ru/strings.xml b/java/res/values-ru/strings.xml
index f76cc8eaf..00e574e90 100644
--- a/java/res/values-ru/strings.xml
+++ b/java/res/values-ru/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Клавиатура Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Клавиатура Android (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Клавиатура Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Настройки"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Проверка правописания Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Все команды"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Проверка правописания Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Настройка проверки правописания"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Поиск контактов"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Обращаться к списку контактов при проверке правописания"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Виброотклик клавиш"</string>
@@ -34,37 +31,41 @@
<string name="popup_on_keypress" msgid="123894815723512944">"Увеличение нажатых"</string>
<string name="general_category" msgid="1859088467017573195">"Общие"</string>
<string name="correction_category" msgid="2236750915056607613">"Исправление текста"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"Непрерывный ввод"</string>
<string name="misc_category" msgid="6894192814868233453">"Другие варианты"</string>
<string name="advanced_settings" msgid="362895144495591463">"Расширенные настройки"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"Для опытных пользователей"</string>
<string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Другой способ ввода"</string>
<string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Клавиша переключения языков также служит для смены способа ввода"</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"Блок. кл. перекл. языков"</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"Клавиша переключения языков"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Показывать, когда включено несколько раскладок"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Задержка закрытия"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Без задержки"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"По умолчанию"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Подсказывать имена"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Подсказывать исправления на основе имен из списка контактов"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Автоисправление"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Показывать варианты исправления"</string>
<string name="auto_cap" msgid="1719746674854628252">"Заглавные автоматически"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"Писать первое слово предложения с прописной буквы"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Дополнительные словари"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Основной словарь"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"Показать варианты исправлений"</string>
<string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Предлагать варианты слов во время ввода"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Всегда показывать"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Показать вертикально"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Показать вертикально"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Всегда скрывать"</string>
- <string name="auto_correction" msgid="4979925752001319458">"Автоисправление"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"Автоисправление"</string>
<string name="auto_correction_summary" msgid="5625751551134658006">"Автоматическое исправление опечаток при вводе знака препинания или пробела"</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Откл."</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Умеренное"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Активное"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Очень активно"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Следующие варианты"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Использовать предыдущее слово, чтобы исправить предложенные варианты"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Следующая подсказка"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Использовать предыдущее слово для прогнозирования"</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"Подсказка следующего слова"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"Предлагать подсказки на основе предыдущего слова"</string>
+ <string name="gesture_input" msgid="826951152254563827">"Включить функцию"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"Вводите слова, не отрывая пальца от клавиатуры"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Рисовать линию"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"Показывать подсказки"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"Показывать подсказки при вводе текста"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: сохранено"</string>
<string name="label_go_key" msgid="1635148082137219148">"Поиск"</string>
<string name="label_next_key" msgid="362972844525672568">"Далее"</string>
@@ -95,6 +96,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Клавиша \"Ввод\""</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Поиск"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Точка"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Сменить язык"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Далее"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Назад"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Верхний регистр включен"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps Lock включен"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Верхний регистр отключен"</string>
diff --git a/java/res/values-sk/bools.xml b/java/res/values-sk/bools.xml
new file mode 100644
index 000000000..840d20c21
--- /dev/null
+++ b/java/res/values-sk/bools.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<resources>
+ <!-- Whether this input method should be used as the default for a locale. Override it
+ for supported languages. -->
+ <bool name="im_is_default">true</bool>
+</resources>
diff --git a/java/res/values-sk/strings-appname.xml b/java/res/values-sk/strings-appname.xml
new file mode 100644
index 000000000..5b5590000
--- /dev/null
+++ b/java/res/values-sk/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Klávesnica Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Kontrola pravopisu Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Nastavenia klávesnice Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Nastavenia kontroly pravopisu"</string>
+</resources>
diff --git a/java/res/values-sk/strings.xml b/java/res/values-sk/strings.xml
index 44eecdc74..915fe14b7 100644
--- a/java/res/values-sk/strings.xml
+++ b/java/res/values-sk/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Klávesnica Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Klávesnica Android (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Nastavenia klávesnice Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Možnosti zadávania textu a údajov"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Kontrola pravopisu Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Príkazy denníka výskumu"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Kontrola pravopisu Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Nastavenia kontroly pravopisu"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Vyhľadať kontakty"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Kontrola pravopisu používa záznamy z vášho zoznamu kontaktov"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Pri stlačení klávesu vibrovať"</string>
@@ -34,37 +31,41 @@
<string name="popup_on_keypress" msgid="123894815723512944">"Zobraziť znaky pri stlačení klávesu"</string>
<string name="general_category" msgid="1859088467017573195">"Všeobecné"</string>
<string name="correction_category" msgid="2236750915056607613">"Oprava textu"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"Písanie gest"</string>
<string name="misc_category" msgid="6894192814868233453">"Ďalšie možnosti"</string>
<string name="advanced_settings" msgid="362895144495591463">"Rozšírené nastavenia"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"Možnosti pre odborníkov"</string>
<string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Prepnúť na iné metódy vstupu"</string>
<string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Kláves na prepnutie jazyka pokrýva aj ďalšie metódy vstupu"</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"Blok. kláves prep. jazyka"</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"Kľúč na prepínanie jazyka"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Zobraziť, keď je povolených viac jazykov vstupu"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Onesk. zrušenia kľúč. kon. okna"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Bez oneskorenia"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Predvolená"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Navrhnúť mená kontaktov"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Používať mená z Kontaktov na návrhy a opravy"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Povoliť opätovné opravy"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Nastaviť návrhy pre opätovné opravy"</string>
<string name="auto_cap" msgid="1719746674854628252">"Veľké písmená automaticky"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"Písanie prvého slova v každej vete veľkým písmenom"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Doplnkové slovníky"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Hlavný slovník"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"Zobraziť návrhy opráv"</string>
<string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Zobrazovať navrhované slová počas písania"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Vždy zobrazovať"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Zobraziť v režime na výšku"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Zobraziť v režime na výšku"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Vždy skrývať"</string>
- <string name="auto_correction" msgid="4979925752001319458">"Automatické opravy"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"Automatické opravy"</string>
<string name="auto_correction_summary" msgid="5625751551134658006">"Stlačením medzerníka a interpunkcie sa aut. opravia chybné slová"</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Vypnuté"</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Mierne"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agresívne"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Veľmi agresívne"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Návrhy ďalšieho slova"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Na zlepšenie návrhov použiť predchádzajúce slovo"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Odhad ďalšieho slova"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Použiť predchádzajúce slovo aj pre predpoveď"</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"Návrhy ďalšieho slova"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"Návrhy podľa predchádzajúceho slova"</string>
+ <string name="gesture_input" msgid="826951152254563827">"Povoliť písanie gest"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"Vkladanie slov prejdením prstom po písmenách"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Zobrazovať stopu gesta"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"Dynamická plávajúca ukážka"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"Zobrazenie navrhovaného slova pri písaní gestami"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Uložené"</string>
<string name="label_go_key" msgid="1635148082137219148">"Hľadať"</string>
<string name="label_next_key" msgid="362972844525672568">"Ďalej"</string>
@@ -95,6 +96,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Enter"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Hľadať"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Bodka"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Prepnúť jazyk"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Ďalšie"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Predchádzajúce"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Kláves Shift je povolený"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Kláves Caps Lock je povolený"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Kláves Shift je zakázaný"</string>
diff --git a/java/res/values-sl/bools.xml b/java/res/values-sl/bools.xml
new file mode 100644
index 000000000..840d20c21
--- /dev/null
+++ b/java/res/values-sl/bools.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<resources>
+ <!-- Whether this input method should be used as the default for a locale. Override it
+ for supported languages. -->
+ <bool name="im_is_default">true</bool>
+</resources>
diff --git a/java/res/values-sl/strings-appname.xml b/java/res/values-sl/strings-appname.xml
new file mode 100644
index 000000000..fd303d8dd
--- /dev/null
+++ b/java/res/values-sl/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Tipkovnica Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Črkovalnik za Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Nastavitve tipkovnice Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Nastavitve preverjanja črkovanja"</string>
+</resources>
diff --git a/java/res/values-sl/strings.xml b/java/res/values-sl/strings.xml
index ce7dc70bb..f90cbab4f 100644
--- a/java/res/values-sl/strings.xml
+++ b/java/res/values-sl/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Tipkovnica Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Tipkovnica Android (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Nastavitve tipkovnice Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Možnosti vnosa"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Črkovalnik za Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Ukazi za dnevnik raziskav"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Črkovalnik za Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Nastavitve preverjanja črkovanja"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Iskanje imen stikov"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Črkovalnik uporablja vnose s seznama stikov"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibriranje ob pritisku tipke"</string>
@@ -34,37 +31,41 @@
<string name="popup_on_keypress" msgid="123894815723512944">"Povečaj črko ob pritisku"</string>
<string name="general_category" msgid="1859088467017573195">"Splošno"</string>
<string name="correction_category" msgid="2236750915056607613">"Popravljanje besedila"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"Vnos s potezami"</string>
<string name="misc_category" msgid="6894192814868233453">"Druge možnosti"</string>
<string name="advanced_settings" msgid="362895144495591463">"Dodatne nastavitve"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"Možnosti za strokovnjake"</string>
<string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Prekl. na drug nač. vnosa"</string>
<string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Tipka za preklop jezika, ki vključuje tudi druge načine vnosa"</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"Onemogoči tipko za preklop jezika"</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"Tipka za preklop med jeziki"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Pokaži, ko je omogočenih več jezikov vnosa"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Trajanje povečanja tipke"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Brez zakasnitve"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Privzeto"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Predlagaj imena stikov"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Uporaba imen iz stikov za predloge in popravke"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Omogoči vnovične popravke"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Nastavitev predlogov za vnovične popravke"</string>
<string name="auto_cap" msgid="1719746674854628252">"Samod. velike začetnice"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"Prvo besedo stavka piši z veliko začetnico"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Dodatni slovarji"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Glavni slovar"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"Pokaži predloge popravkov"</string>
<string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Pokaži predlagane besede med tipkanjem"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Vedno pokaži"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Pokaži v pokončnem načinu"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Prikaži v pokončnem načinu"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Vedno skrij"</string>
- <string name="auto_correction" msgid="4979925752001319458">"Samodejni popravek"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"Samodejni popravek"</string>
<string name="auto_correction_summary" msgid="5625751551134658006">"Preslednica in ločila samodejno popravijo napačno vtipkane besede"</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Izklopljeno"</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Zmerno"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Strogo"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Zelo strogo"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Predlogi naslednje besede"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Predloge izboljšaj s prejšnjo besedo"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Predvidevanje naslednje besede"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Uporabi prejšnjo besedo tudi za predvidevanje"</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"Predlogi za naslednjo besedo"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"Pri predlogu upoštevaj prejšnjo besedo"</string>
+ <string name="gesture_input" msgid="826951152254563827">"Omogoči vnos besedila s potezo"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"Vnos besede s podrsavanjem od črke do črke"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Prikaži pot poteze"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"Dinamični plavajoči predogled"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"Prikaz predlagane besede med vnosom s prstom"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: shranjeno"</string>
<string name="label_go_key" msgid="1635148082137219148">"Pojdi"</string>
<string name="label_next_key" msgid="362972844525672568">"Naprej"</string>
@@ -95,6 +96,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Vračalka"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Iskanje"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Pika"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Preklop jezika"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Naprej"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Nazaj"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Način »Shift« je omogočen"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Način »Caps Lock« je omogočen"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Način »Shift« je onemogočen"</string>
diff --git a/java/res/values-sr/bools.xml b/java/res/values-sr/bools.xml
new file mode 100644
index 000000000..840d20c21
--- /dev/null
+++ b/java/res/values-sr/bools.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<resources>
+ <!-- Whether this input method should be used as the default for a locale. Override it
+ for supported languages. -->
+ <bool name="im_is_default">true</bool>
+</resources>
diff --git a/java/res/values-sr/strings-appname.xml b/java/res/values-sr/strings-appname.xml
new file mode 100644
index 000000000..449fe551a
--- /dev/null
+++ b/java/res/values-sr/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Android тастатура"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Android провера правописа"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Подешавања Android тастатуре"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Подешавања провере правописа"</string>
+</resources>
diff --git a/java/res/values-sr/strings.xml b/java/res/values-sr/strings.xml
index 6d2899b47..1f10380ca 100644
--- a/java/res/values-sr/strings.xml
+++ b/java/res/values-sr/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Android тастатура"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android тастатура (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Подешавања Android тастатуре"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Опције уноса"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Android провера правописа"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Команде евиденције истраживања"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android провера правописа (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Подешавања провере правописа"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Потражи имена контаката"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Контролор правописа користи уносе са листе контаката"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Вибрирај на притисак тастера"</string>
@@ -34,37 +31,41 @@
<string name="popup_on_keypress" msgid="123894815723512944">"Искачући прозор приликом притиска тастера"</string>
<string name="general_category" msgid="1859088467017573195">"Опште"</string>
<string name="correction_category" msgid="2236750915056607613">"Исправљање текста"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"Унос покретом"</string>
<string name="misc_category" msgid="6894192814868233453">"Друге опције"</string>
<string name="advanced_settings" msgid="362895144495591463">"Напредна подешавања"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"Опције за стручњаке"</string>
<string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Пребаци на друге методе уноса"</string>
<string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Тастер за пребацивање језика обухвата и друге методе уноса"</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"Искључи тастер за језике"</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"Тастер за пребацивање језика"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Прикажи када је омогућено више језика уноса"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Одложи одбац. иск. прозора тастера"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Без одлагања"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Подразумевано"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Предложи имена контаката"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Користи имена из Контаката за предлоге и исправке"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Омогући поновне исправке"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Подешавање предлога за поновне исправке"</string>
<string name="auto_cap" msgid="1719746674854628252">"Аутоматски унос великих слова"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"Писање великог слова на почетку сваке реченице"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Помоћни речници"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Главни речник"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"Прикажи предлоге за исправку"</string>
<string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Приказивање предложених речи током уноса текста"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Увек прикажи"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Прикажи у усправном режиму"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Прикажи у усправном режиму"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Увек сакриј"</string>
- <string name="auto_correction" msgid="4979925752001319458">"Аутоматско исправљање"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"Аутом. исправљање"</string>
<string name="auto_correction_summary" msgid="5625751551134658006">"Размак и интерпункција аутоматски исправљају грешке у куцању"</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Искључи"</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Умерено"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Агресивно"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Веома агресивно"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Предлози за следећу реч"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Користи претходну реч за побољшање предлога"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Предвиђање следеће речи"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Користи претходну реч и за предвиђање"</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"Предлози за следећу реч"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"Користи претходну реч при давању предлога"</string>
+ <string name="gesture_input" msgid="826951152254563827">"Омогући унос покретом"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"Унесите реч превлачењем прста преко од слова до слова на екрану"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Прикажи траг покрета"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"Динамички плутајући преглед"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"Приказује предложену реч при уносу покретом"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Сачувано"</string>
<string name="label_go_key" msgid="1635148082137219148">"Иди"</string>
<string name="label_next_key" msgid="362972844525672568">"Следеће"</string>
@@ -95,6 +96,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Return"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Претражи"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Тачка"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Пребаци језик"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Следеће"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Претходно"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift је омогућен"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps lock је омогућен"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift је онемогућен"</string>
diff --git a/java/res/values-sv/bools.xml b/java/res/values-sv/bools.xml
new file mode 100644
index 000000000..840d20c21
--- /dev/null
+++ b/java/res/values-sv/bools.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<resources>
+ <!-- Whether this input method should be used as the default for a locale. Override it
+ for supported languages. -->
+ <bool name="im_is_default">true</bool>
+</resources>
diff --git a/java/res/values-sv/strings-appname.xml b/java/res/values-sv/strings-appname.xml
new file mode 100644
index 000000000..9b4a7dbd1
--- /dev/null
+++ b/java/res/values-sv/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Androids tangentbord"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Stavningskontroll i Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Inställningar för Androids tangentbord"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Inställningar för stavningskontroll"</string>
+</resources>
diff --git a/java/res/values-sv/strings.xml b/java/res/values-sv/strings.xml
index 75e80d42d..e986a79dd 100644
--- a/java/res/values-sv/strings.xml
+++ b/java/res/values-sv/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Androids tangentbord"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Androids tangentbord (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Inställningar för Androids tangentbord"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Inmatningsalternativ"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Stavningskontroll i Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Loggkommandon"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Stavningskontroll i Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Inställningar för stavningskontroll"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Sök namn på kontakter"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"I stavningskontrollen används poster från kontaktlistan"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrera vid tangenttryck"</string>
@@ -34,37 +31,41 @@
<string name="popup_on_keypress" msgid="123894815723512944">"Popup vid knapptryck"</string>
<string name="general_category" msgid="1859088467017573195">"Allmänt"</string>
<string name="correction_category" msgid="2236750915056607613">"Textkorrigering"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"Svepskrivning"</string>
<string name="misc_category" msgid="6894192814868233453">"Andra alternativ"</string>
<string name="advanced_settings" msgid="362895144495591463">"Avancerade inställningar"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"Alternativ för experter"</string>
<string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Byt till annan inmatning"</string>
<string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Språkbytesknappen omfattar även andra inmatningsmetoder"</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"Stäng av språkbytesknapp"</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"Knapp för att byta språk"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Visa när flera inmatningsspråk är aktiverade"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Ta bort popup-fördröjning"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Fördröj inte"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Standard"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Föreslå kontaktnamn"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Använd namn från Kontakter för förslag och korrigeringar"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Aktivera omkorrigeringar"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Ställ in förslag för omkorrigeringar"</string>
<string name="auto_cap" msgid="1719746674854628252">"Automatiska versaler"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"Automatisk stor bokstav först i varje mening"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Tilläggsordlistor"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Huvudordlistan"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"Visa rättningsförslag"</string>
<string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Visar ordförslag när du skriver"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Visa alltid"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Visa stående"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Visa i stående format"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Dölj alltid"</string>
- <string name="auto_correction" msgid="4979925752001319458">"Autokorrigering"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"Autokorrigering"</string>
<string name="auto_correction_summary" msgid="5625751551134658006">"Blanksteg/skiljetecken rättar felstavning"</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Av"</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Måttlig"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Aggressiv"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Mycket aggressivt"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Föreslå nästa ord"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Förbättra förslagen med föregående ord"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Förutspå nästa ord"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Använd även föregående ord för att ge förslag"</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"Föreslå nästa ord"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"Ge förslag utifrån föregående ord"</string>
+ <string name="gesture_input" msgid="826951152254563827">"Aktivera svepskrivning"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"Skriv genom att dra från tecken till tecken utan att lyfta handen"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Visa spår efter rörelse"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"Visa ordförslag vid svepskrivning"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"Ordförslaget visas i rörelsen medan du skriver"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: sparat"</string>
<string name="label_go_key" msgid="1635148082137219148">"Kör"</string>
<string name="label_next_key" msgid="362972844525672568">"Nästa"</string>
@@ -95,6 +96,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Retur"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Sök"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Punkt"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Byt språk"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Nästa"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Föregående"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Skift är aktiverat"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps Lock är aktiverat"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Skift är inaktiverat"</string>
diff --git a/java/res/values-sw/bools.xml b/java/res/values-sw/bools.xml
new file mode 100644
index 000000000..840d20c21
--- /dev/null
+++ b/java/res/values-sw/bools.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<resources>
+ <!-- Whether this input method should be used as the default for a locale. Override it
+ for supported languages. -->
+ <bool name="im_is_default">true</bool>
+</resources>
diff --git a/java/res/values-sw/strings-appname.xml b/java/res/values-sw/strings-appname.xml
new file mode 100644
index 000000000..51de0a6b8
--- /dev/null
+++ b/java/res/values-sw/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Kibodi ya Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Kikagua tahajia cha Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Mipangilio ya kibodi ya Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Mipangilio ya kukagua tahajia"</string>
+</resources>
diff --git a/java/res/values-sw/strings.xml b/java/res/values-sw/strings.xml
index da51cb3b9..4388a27fd 100644
--- a/java/res/values-sw/strings.xml
+++ b/java/res/values-sw/strings.xml
@@ -20,51 +20,52 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Kibodi ya Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Kicharazio cha Android (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Mipangilio ya kibodi ya Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Chaguo za uingizaji"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Kikagua tahajia cha Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Amri za Kumbukumbu za Utafiti"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Kikagua tahajia cha Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Mipangilio ya kukagua sarufi"</string>
- <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Angalia majina ya wasiliani"</string>
+ <string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Angalia majina ya unaowasiliana nao"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Kikagua tahajia hutumia ingizo kutoka kwa orodha yako ya anwani"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Tetema unabofya kitufe"</string>
<string name="sound_on_keypress" msgid="6093592297198243644">"Toa sauti unapobofya kitufe"</string>
<string name="popup_on_keypress" msgid="123894815723512944">"Ibuka kitufe kinapobonyezwa"</string>
<string name="general_category" msgid="1859088467017573195">"Kawaida"</string>
<string name="correction_category" msgid="2236750915056607613">"Marekebisho ya maandishi"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"Kuandika kwa ishara"</string>
<string name="misc_category" msgid="6894192814868233453">"Chaguo zingine"</string>
<string name="advanced_settings" msgid="362895144495591463">"Mipangilio mahiri"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"Chaguo za wataalamu"</string>
<string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Badilisha hadi kwa mbinu zingine za ingizo"</string>
<string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Ufunguo wa kubadilisha lugha unashughulikia mbinu zingine za ingizo pia"</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"Zuia ufunguo wa kubadili lugha"</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"Kitufe cha kubadilisha lugha"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Onyesha wakati lugha ingizo mbalimbali zinapowezeshwa"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Kuchelewesha kutupa kitufe ibukizi"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Hakuna kuchelewa"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Chaguo-msingi"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Pendekeza majini ya Anwani"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Tumia majina kutoka kwa Anwani kwa mapendekezo na marekebisho"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Wezesha masahihisho mapya"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Weka mapendekezo kwa ajili ya kusahihisha upya"</string>
<string name="auto_cap" msgid="1719746674854628252">"Uwekaji wa herufi kubwa kiotomatiki"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"Fanya herufi kubwa neno la kwanza la kila sentensi"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Nyongeza za kamusi"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Kamusi kuu"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"Onyesha mapendekezo ya marekebisho"</string>
<string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Onyesha maneno yaliyopendekezwa wakati unachapa"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Onyesha kila wakati"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Onyesha kwenye hali wima"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Onyesha katika hali wima"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Ficha kila wakati"</string>
- <string name="auto_correction" msgid="4979925752001319458">"Usahihishaji Kioto"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"Usahihishaji otomatiki"</string>
<string name="auto_correction_summary" msgid="5625751551134658006">"Kiaamba na kiakifishi hurekebisha maneno ambayo yamechapishwa vibaya"</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Zima"</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Ya wastani"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Ya hima"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Changamfu zaidi"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Mapendekezo ya neno lifuatalo"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Tumia neno la awali ili kuboresha mapendekezo"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Utabiri wa neno lifuatalo"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Tumia neno la awali pia kwa udadisi"</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"Mapendekezo ya neno lifuatalo"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"Tumia nelo la awali katika kufanya mapendekezo"</string>
+ <string name="gesture_input" msgid="826951152254563827">"Washa kuandika kwa ishara"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"Ingiza neno kwa kutelezesha juu ya herufi"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Onyesha njia ya ishara"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"Kihakiki kinachobadilika cha kuelea"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"Onyesha neno lililopendekezwa unapoonyesha ishara"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Imehifadhiwa"</string>
<string name="label_go_key" msgid="1635148082137219148">"Nenda"</string>
<string name="label_next_key" msgid="362972844525672568">"Ifuatayo"</string>
@@ -95,6 +96,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Rudi"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Tafuta"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Nukta"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Badili lugha"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Inayofuata"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Iliyotangulia"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift imewezeshwa"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps lock imewezeshwa"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift imelemazwa"</string>
diff --git a/java/res/values-sw600dp-land/dimens.xml b/java/res/values-sw600dp-land/dimens.xml
index a478df89a..51c710fa4 100644
--- a/java/res/values-sw600dp-land/dimens.xml
+++ b/java/res/values-sw600dp-land/dimens.xml
@@ -53,7 +53,18 @@
<fraction name="spacebar_text_ratio">30.0%</fraction>
<dimen name="key_uppercase_letter_padding">4dp</dimen>
+ <!-- For 5-row keyboard -->
+ <fraction name="key_bottom_gap_5row">3.20%p</fraction>
+ <fraction name="key_letter_ratio_5row">62%</fraction>
+ <fraction name="key_uppercase_letter_ratio_5row">36%</fraction>
+
<dimen name="suggestions_strip_padding">252.0dp</dimen>
<integer name="max_more_suggestions_row">5</integer>
<fraction name="min_more_suggestions_width">50%</fraction>
+
+ <!-- Gesture floating preview text parameters -->
+ <dimen name="gesture_floating_preview_text_size">26dp</dimen>
+ <dimen name="gesture_floating_preview_text_offset">76dp</dimen>
+ <dimen name="gesture_floating_preview_horizontal_padding">26dp</dimen>
+ <dimen name="gesture_floating_preview_vertical_padding">17dp</dimen>
</resources>
diff --git a/java/res/values-sw600dp/config.xml b/java/res/values-sw600dp/config.xml
index 2f35d9ae5..e296623b2 100644
--- a/java/res/values-sw600dp/config.xml
+++ b/java/res/values-sw600dp/config.xml
@@ -19,6 +19,8 @@
-->
<resources>
+ <!-- Device form factor. This value must be aligned with {@link KeyboardId.DEVICE_FORM_FACTOR_TABLET7} -->
+ <integer name="config_device_form_factor">1</integer>
<bool name="config_enable_show_voice_key_option">false</bool>
<bool name="config_enable_show_popup_on_keypress_option">false</bool>
<bool name="config_enable_bigram_suggestions_option">false</bool>
@@ -32,8 +34,9 @@
<string name="config_default_keyboard_theme_index" translatable="false">5</string>
<integer name="config_max_more_keys_column">5</integer>
<!--
- Configuration for LatinKeyboardView
+ Configuration for MainKeyboardView
-->
+ <dimen name="config_key_hysteresis_distance">40.0dp</dimen>
<bool name="config_sliding_key_input_enabled">false</bool>
<!-- Showing more keys keyboard, just above the touched point if true, aligned to the key if
false -->
diff --git a/java/res/values-sw600dp/dimens.xml b/java/res/values-sw600dp/dimens.xml
index 5596ba41c..586fbe6da 100644
--- a/java/res/values-sw600dp/dimens.xml
+++ b/java/res/values-sw600dp/dimens.xml
@@ -66,6 +66,11 @@
<dimen name="key_preview_height">94.5dp</dimen>
<dimen name="key_preview_offset">16.0dp</dimen>
+ <!-- For 5-row keyboard -->
+ <fraction name="key_bottom_gap_5row">3.20%p</fraction>
+ <fraction name="key_letter_ratio_5row">52%</fraction>
+ <fraction name="key_uppercase_letter_ratio_5row">27%</fraction>
+
<dimen name="key_preview_offset_ics">8.0dp</dimen>
<!-- popup_key_height x -0.5 -->
<dimen name="more_keys_keyboard_vertical_correction_ics">-31.5dp</dimen>
@@ -79,4 +84,13 @@
<dimen name="suggestion_padding">12dp</dimen>
<dimen name="suggestion_text_size">22dp</dimen>
<dimen name="more_suggestions_hint_text_size">33dp</dimen>
+
+ <!-- Gesture preview trail parameters -->
+ <dimen name="gesture_preview_trail_width">2.5dp</dimen>
+ <!-- Gesture floating preview text parameters -->
+ <dimen name="gesture_floating_preview_text_size">28dp</dimen>
+ <dimen name="gesture_floating_preview_text_offset">87dp</dimen>
+ <dimen name="gesture_floating_preview_horizontal_padding">28dp</dimen>
+ <dimen name="gesture_floating_preview_vertical_padding">19dp</dimen>
+ <dimen name="gesture_floating_preview_round_radius">3dp</dimen>
</resources>
diff --git a/java/res/values-sw600dp/touch-position-correction.xml b/java/res/values-sw600dp/touch-position-correction.xml
new file mode 100644
index 000000000..f77d3ae83
--- /dev/null
+++ b/java/res/values-sw600dp/touch-position-correction.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Note that correctionX is obsolete (See com.android.inputmethod.keyboard.internal.TouchPositionCorrection)
+ An entry of the touch_position_correction word should be:
+ 1. correctionX: (touch_center_x - hitbox_center_x) / hitbox_width
+ 2. correctionY: (touch_center_y - hitbox_center_y) / hitbox_height
+ 3. correctionR: sweet_spot_radius / sqrt(hitbox_width^2 + hitbox_height^2)
+ -->
+
+ <string-array
+ name="touch_position_correction_data_default"
+ translatable="false"
+ >
+ <!-- The default touch position data (See com.android.inputmethod.keyboard.ProximityInfo)
+ correctionX = 0.0f
+ correctionY = 0.0f
+ correctionR = DEFAULT_TOUCH_POSITION_CORRECTION_RADIUS
+ -->
+ </string-array>
+
+ <string-array
+ name="touch_position_correction_data_gingerbread"
+ translatable="false"
+ >
+ <!-- The default touch position data (See com.android.inputmethod.keyboard.ProximityInfo)
+ correctionX = 0.0f
+ correctionY = 0.0f
+ correctionR = DEFAULT_TOUCH_POSITION_CORRECTION_RADIUS
+ -->
+ </string-array>
+
+ <string-array
+ name="touch_position_correction_data_ice_cream_sandwich"
+ translatable="false"
+ >
+ <!-- The default touch position data (See com.android.inputmethod.keyboard.ProximityInfo)
+ correctionX = 0.0f
+ correctionY = 0.0f
+ correctionR = DEFAULT_TOUCH_POSITION_CORRECTION_RADIUS
+ -->
+ </string-array>
+</resources>
diff --git a/java/res/values-sw768dp-land/dimens.xml b/java/res/values-sw768dp-land/dimens.xml
index b95c858dc..f4a57ffb0 100644
--- a/java/res/values-sw768dp-land/dimens.xml
+++ b/java/res/values-sw768dp-land/dimens.xml
@@ -55,8 +55,19 @@
<fraction name="spacebar_text_ratio">24.00%</fraction>
<dimen name="key_preview_height">107.1dp</dimen>
+ <!-- For 5-row keyboard -->
+ <fraction name="key_bottom_gap_5row">2.65%p</fraction>
+ <fraction name="key_letter_ratio_5row">53%</fraction>
+ <fraction name="key_uppercase_letter_ratio_5row">30%</fraction>
+
<dimen name="key_preview_offset_ics">8.0dp</dimen>
<dimen name="suggestions_strip_padding">252.0dp</dimen>
<fraction name="min_more_suggestions_width">50%</fraction>
+
+ <!-- Gesture floating preview text parameters -->
+ <dimen name="gesture_floating_preview_text_size">32dp</dimen>
+ <dimen name="gesture_floating_preview_text_offset">100dp</dimen>
+ <dimen name="gesture_floating_preview_horizontal_padding">32dp</dimen>
+ <dimen name="gesture_floating_preview_vertical_padding">21dp</dimen>
</resources>
diff --git a/java/res/values-sw768dp/config.xml b/java/res/values-sw768dp/config.xml
index 5fcaeeb41..346fa9979 100644
--- a/java/res/values-sw768dp/config.xml
+++ b/java/res/values-sw768dp/config.xml
@@ -19,6 +19,8 @@
-->
<resources>
+ <!-- Device form factor. This value must be aligned with {@link KeyboardId.DEVICE_FORM_FACTOR_TABLET10} -->
+ <integer name="config_device_form_factor">2</integer>
<bool name="config_enable_show_voice_key_option">false</bool>
<bool name="config_enable_show_popup_on_keypress_option">false</bool>
<bool name="config_enable_bigram_suggestions_option">false</bool>
@@ -30,7 +32,7 @@
<string name="config_default_keyboard_theme_index" translatable="false">5</string>
<integer name="config_max_more_keys_column">5</integer>
<!--
- Configuration for LatinKeyboardView
+ Configuration for MainKeyboardView
-->
<bool name="config_sliding_key_input_enabled">false</bool>
<!-- Showing more keys keyboard, just above the touched point if true, aligned to the key if
diff --git a/java/res/values-sw768dp/dimens.xml b/java/res/values-sw768dp/dimens.xml
index ce33b73cb..2fd732293 100644
--- a/java/res/values-sw768dp/dimens.xml
+++ b/java/res/values-sw768dp/dimens.xml
@@ -67,6 +67,11 @@
<dimen name="key_preview_height">94.5dp</dimen>
<dimen name="key_preview_offset">16.0dp</dimen>
+ <!-- For 5-row keyboard -->
+ <fraction name="key_bottom_gap_5row">2.95%p</fraction>
+ <fraction name="key_letter_ratio_5row">51%</fraction>
+ <fraction name="key_uppercase_letter_ratio_5row">33%</fraction>
+
<dimen name="key_preview_offset_ics">8.0dp</dimen>
<!-- popup_key_height x -0.5 -->
<dimen name="more_keys_keyboard_vertical_correction_ics">-31.5dp</dimen>
@@ -80,4 +85,13 @@
<dimen name="suggestion_padding">8dp</dimen>
<dimen name="suggestion_text_size">22dp</dimen>
<dimen name="more_suggestions_hint_text_size">33dp</dimen>
+
+ <!-- Gesture preview trail parameters -->
+ <dimen name="gesture_preview_trail_width">2.5dp</dimen>
+ <!-- Gesture floating preview text parameters -->
+ <dimen name="gesture_floating_preview_text_size">26dp</dimen>
+ <dimen name="gesture_floating_preview_text_offset">86dp</dimen>
+ <dimen name="gesture_floating_preview_horizontal_padding">26dp</dimen>
+ <dimen name="gesture_floating_preview_vertical_padding">17dp</dimen>
+ <dimen name="gesture_floating_preview_round_radius">3dp</dimen>
</resources>
diff --git a/java/res/values-th/bools.xml b/java/res/values-th/bools.xml
new file mode 100644
index 000000000..840d20c21
--- /dev/null
+++ b/java/res/values-th/bools.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<resources>
+ <!-- Whether this input method should be used as the default for a locale. Override it
+ for supported languages. -->
+ <bool name="im_is_default">true</bool>
+</resources>
diff --git a/java/res/values-th/strings-appname.xml b/java/res/values-th/strings-appname.xml
new file mode 100644
index 000000000..7fc7e3e43
--- /dev/null
+++ b/java/res/values-th/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"แป้นพิมพ์แอนดรอยด์"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"การตรวจสอบการสะกดของแอนดรอยด์"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"การตั้งค่าแป้นพิมพ์แอนดรอยด์"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"การตั้งค่าการตรวจสอบการสะกด"</string>
+</resources>
diff --git a/java/res/values-th/strings.xml b/java/res/values-th/strings.xml
index da20d6607..9c63a13f7 100644
--- a/java/res/values-th/strings.xml
+++ b/java/res/values-th/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"แป้นพิมพ์ Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android keyboard (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"การตั้งค่าแป้นพิมพ์ Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"ตัวเลือกการป้อนข้อมูล"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"แอนดรอยด์ตรวจสอบการสะกด"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"คำสั่งบันทึกการวิจัย"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"แอนดรอยด์ตรวจสอบการสะกด (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"การตั้งค่าการตรวจสอบการสะกด"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"ค้นหารายชื่อติดต่อ"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"เครื่องมือตรวจการสะกดใช้รายการจากรายชื่อติดต่อของคุณ"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"สั่นเมื่อกดปุ่ม"</string>
@@ -34,37 +31,41 @@
<string name="popup_on_keypress" msgid="123894815723512944">"ป๊อปอัปเมื่อกดแป้น"</string>
<string name="general_category" msgid="1859088467017573195">"ทั่วไป"</string>
<string name="correction_category" msgid="2236750915056607613">"การแก้ไขข้อความ"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"การพิมพ์ด้วยท่าทางสัมผัส"</string>
<string name="misc_category" msgid="6894192814868233453">"ตัวเลือกอื่นๆ"</string>
<string name="advanced_settings" msgid="362895144495591463">"การตั้งค่าขั้นสูง"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"ตัวเลือกสำหรับผู้เชี่ยวชาญ"</string>
<string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"ใช้วิธีการป้อนข้อมูลอื่น"</string>
<string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"แป้นสลับภาษาครอบคลุมวิธีการป้อนข้อมูลอื่นๆ ด้วย"</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"ยกเลิกแป้นสลับภาษา"</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"แป้นสลับภาษา"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"แสดงเมื่อเปิดใช้งานภาษาสำหรับอินพุตหลายภาษา"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"หน่วงเวลาก่อนปิดป๊อปอัพหลัก"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"ไม่มีการหน่วงเวลา"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"ค่าเริ่มต้น"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"แนะนำชื่อผู้ติดต่อ"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"ใช้ชื่อจากรายชื่อติดต่อสำหรับคำแนะนำและการแก้ไข"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"เปิดใช้งานการแก้ไขซ้ำ"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"ตั้งค่าคำแนะนำสำหรับการแก้ไขซ้ำ"</string>
<string name="auto_cap" msgid="1719746674854628252">"ปรับเป็นตัวพิมพ์ใหญ่อัตโนมัติ"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"ทำให้คำแรกของทุกประโยคเป็นตัวพิมพ์ใหญ่"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"พจนานุกรม Add-On"</string>
<string name="main_dictionary" msgid="4798763781818361168">"พจนานุกรมหลัก"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"แสดงคำแนะนำการแก้ไข"</string>
<string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"แสดงคำที่แนะนำขณะพิมพ์"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"แสดงทุกครั้ง"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"แสดงในโหมดแนวตั้ง"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"แสดงในโหมดแนวตั้ง"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"ซ่อนทุกครั้ง"</string>
- <string name="auto_correction" msgid="4979925752001319458">"การแก้ไขอัตโนมัติ"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"การแก้ไขอัตโนมัติ"</string>
<string name="auto_correction_summary" msgid="5625751551134658006">"กดเว้นวรรคและเครื่องหมายจะแก้คำผิดอัตโนมัติ"</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"ปิด"</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"ปานกลาง"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"เข้มงวด"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"เข้มงวดมาก"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"คำแนะนำสำหรับคำถัดไป"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"ใช้คำก่อนหน้านี้เพื่อปรับปรุงคำแนะนำ"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"การคาดคะเนคำถัดไป"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"ใช้คำก่อนหน้านี้สำหรับการคาดคะเน"</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"คำแนะนำสำหรับคำถัดไป"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"ใช้คำก่อนหน้าในการสร้างข้อเสนอแนะ"</string>
+ <string name="gesture_input" msgid="826951152254563827">"เปิดการพิมพ์ด้วยท่าทางสัมผัส"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"ป้อนคำด้วยการเลื่อนผ่านตัวอักษร"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"แสดงรอยทางเดินของท่าทางสัมผัส"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"ดูตัวอย่างลอยแบบไดนามิก"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"ดูคำแนะนำในขณะที่ใช้ท่าทางสัมผัส"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : บันทึกแล้ว"</string>
<string name="label_go_key" msgid="1635148082137219148">"ไป"</string>
<string name="label_next_key" msgid="362972844525672568">"ถัดไป"</string>
@@ -95,6 +96,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Return"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"ค้นหา"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"เครื่องหมายจุด"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"เปลี่ยนภาษา"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"ถัดไป"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"ก่อนหน้า"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"เปิดใช้งาน Shift แล้ว"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"เปิดใช้งาน Caps Lock แล้ว"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"ปิดใช้งาน Shift แล้ว"</string>
diff --git a/java/res/values-tl/bools.xml b/java/res/values-tl/bools.xml
new file mode 100644
index 000000000..840d20c21
--- /dev/null
+++ b/java/res/values-tl/bools.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<resources>
+ <!-- Whether this input method should be used as the default for a locale. Override it
+ for supported languages. -->
+ <bool name="im_is_default">true</bool>
+</resources>
diff --git a/java/res/values-tl/strings-appname.xml b/java/res/values-tl/strings-appname.xml
new file mode 100644
index 000000000..fd2b3f55b
--- /dev/null
+++ b/java/res/values-tl/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Keyboard ng Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Spell checker ng Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Mga setting ng keyboard ng Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Mga setting ng pag-spell check"</string>
+</resources>
diff --git a/java/res/values-tl/strings.xml b/java/res/values-tl/strings.xml
index 7d365b2dc..18cd0afb3 100644
--- a/java/res/values-tl/strings.xml
+++ b/java/res/values-tl/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Android keyboard"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android keyboard (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Mga setting ng Android keyboard"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Mga pagpipilian sa input"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Pang-check ng pagbabaybay ng Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Cmmnd sa Log ng Pnnliksik"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Pang-check ng pagbabaybay ng Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Mga setting ng pang-check ng pagbabaybay"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Maghanap pangalan contact"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Gumagamit pang-check pagbabaybay entry sa iyong listahan contact"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Mag-vibrate sa keypress"</string>
@@ -34,37 +31,41 @@
<string name="popup_on_keypress" msgid="123894815723512944">"Popup sa keypress"</string>
<string name="general_category" msgid="1859088467017573195">"Pangkalahatan"</string>
<string name="correction_category" msgid="2236750915056607613">"Pagwawasto ng teksto"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"Gesture na pag-type"</string>
<string name="misc_category" msgid="6894192814868233453">"Iba pang mga pagpipilian"</string>
<string name="advanced_settings" msgid="362895144495591463">"Mga advanced na setting"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"Mga pagpipilian para sa mga dalubhasa"</string>
<string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Lipat iba paraan ng input"</string>
<string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Saklaw din ng key ng pagpalit ng wika ang ibang paraan ng input"</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"Pigilan key pagpalit wika"</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"Key ng panlipat ng wika"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Ipakita kapag maraming wika ng input na pinagana"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Balewala antala key popup"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Walang antala"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Default"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Mungkahi pangalan Contact"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Gamitin pangalan mula Mga Contact sa mga mungkahi\'t pagwawasto"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Paganahin ang mga muling pagtatama"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Magtakda ng mga suhestyon para sa mga muling pagtatama"</string>
<string name="auto_cap" msgid="1719746674854628252">"Auto-capitalization"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"I-capitalize ang unang salita ng bawat pangungusap"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Mga diksyunaryo na add-on"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Pangunahing diksyunaryo"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"Magpakita ng mga suhestiyon ng pagwawasto"</string>
<string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Ipakita ang mga iminumungkahing salita habang nagta-type"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Palaging ipakita"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Ipakita sa portrait mode"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Ipakita sa portrait na mode"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Palaging itago"</string>
- <string name="auto_correction" msgid="4979925752001319458">"Awtomatikong pagwasto"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"Awtomatiko pagwasto"</string>
<string name="auto_correction_summary" msgid="5625751551134658006">"Awto tinatama ng spacebar at bantas ang maling na-type"</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Naka-off"</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Modest"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agresibo"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Napaka-agresibo"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Mga paghuhula sa susunod na salita"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Gamitin ang naunang salita para mapahusay ang mga suhestiyon"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Paghuhula sa susunod na salita"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Gamitin ang nakaraang salita para din sa hula"</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"Mga suhestiyon sa susunod na salita"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"Gamitin ang nakaraang salita sa paggawa ng mga suhestiyon"</string>
+ <string name="gesture_input" msgid="826951152254563827">"Paganahin ang gesture na pag-type"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"Mag-input ng salita sa pamamagitan ng pag-slide sa mga titik"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Ipakita ang trail ng galaw"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"Dynamic na floating preview"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"Tingnan ang iminungkahing salita habang gumagalaw"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Na-save"</string>
<string name="label_go_key" msgid="1635148082137219148">"Punta"</string>
<string name="label_next_key" msgid="362972844525672568">"Susunod"</string>
@@ -95,6 +96,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Bumalik"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Paghahanap"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Tuldok"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Magpalit ng wika"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Susunod"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Nakaraan"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Pinagana ang shift"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Pinagana ang caps lock"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Hindi pinagana ang shift"</string>
diff --git a/java/res/values-tr/bools.xml b/java/res/values-tr/bools.xml
new file mode 100644
index 000000000..840d20c21
--- /dev/null
+++ b/java/res/values-tr/bools.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<resources>
+ <!-- Whether this input method should be used as the default for a locale. Override it
+ for supported languages. -->
+ <bool name="im_is_default">true</bool>
+</resources>
diff --git a/java/res/values-tr/strings-appname.xml b/java/res/values-tr/strings-appname.xml
new file mode 100644
index 000000000..f5e36d2e8
--- /dev/null
+++ b/java/res/values-tr/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Android klavyesi"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Android yazım denetleyici"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Android klavye ayarları"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Yazım denetimi ayarları"</string>
+</resources>
diff --git a/java/res/values-tr/strings.xml b/java/res/values-tr/strings.xml
index b2f73912e..a57c58ab3 100644
--- a/java/res/values-tr/strings.xml
+++ b/java/res/values-tr/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Android klavyesi"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android klavye (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Android klavye ayarları"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Giriş seçenekleri"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Android yazım denetleyici"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Araştırma Günlüğü Komutları"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android yazım denetleyici (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Yazım denetimi ayarları"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Kişi adlarını denetle"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Yazım denetleyici, kişi listenizdeki girişleri kullanır"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Tuşa basıldığında titret"</string>
@@ -34,37 +31,41 @@
<string name="popup_on_keypress" msgid="123894815723512944">"Tuşa basıldığında pop-up aç"</string>
<string name="general_category" msgid="1859088467017573195">"Genel"</string>
<string name="correction_category" msgid="2236750915056607613">"Metin düzeltme"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"Hareketle yazma"</string>
<string name="misc_category" msgid="6894192814868233453">"Diğer seçenekler"</string>
<string name="advanced_settings" msgid="362895144495591463">"Gelişmiş ayarlar"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"Uzmanlar için seçenekler"</string>
<string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Diğer giriş yöntemine geç"</string>
<string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Dil geçiş tuşu diğer giriş yöntemlerini de kapsar"</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"Dil geçiş tuşunu gösterme"</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"Dil değiştirme tuşu"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Birden fazla giriş dili etkin olduğunda göster"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Tuş popup\'ının kapatılmasını geciktirme"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Gecikme yok"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Varsayılan"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Kişi Adları öner"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Öneri ve düzeltmeler için Kişiler\'deki adları kullan"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Düzeltmeleri etkinleştir"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Yeniden düzeltmeler için önerileri ayarla"</string>
<string name="auto_cap" msgid="1719746674854628252">"Otomatik olarak büyük fark yap"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"Her cümlenin ilk kelimesini büyük harf yap"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Ek sözlükler"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Ana sözlük"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"Düzeltme önerilerini göster"</string>
<string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Yazarken, önerilen kelimeleri görüntüle"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Her zaman göster"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Dikey modda göster"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Dikey modda göster"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Her zaman gizle"</string>
- <string name="auto_correction" msgid="4979925752001319458">"Otomatik düzeltme"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"Otomatik düzeltme"</string>
<string name="auto_correction_summary" msgid="5625751551134658006">"Boşluk tuşu ve noktalama işaretleri yanlış yazılan kelimeleri otomatikman düzeltir"</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Kapalı"</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Ölçülü"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Agresif"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Çok geniş ölçekte"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Sonraki kelime önerileri"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Önerileri geliştirmek için önceki kelimeyi kullan"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Sonraki kelime tahmini"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Önceki kelimeyi de tahmin için kullan"</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"Sonraki kelime önerileri"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"Önerilerde bulunurken önceki kelimeyi kullan"</string>
+ <string name="gesture_input" msgid="826951152254563827">"Hareketle yazmayı etkinleştir"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"Harflerin üzerinden parmağınızı kaydırarak kelime girin"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Hareket izini göster"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"Dinamik kayan önizleme"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"Hareket sırasında önerilen kelimeyi göster"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Kaydedildi"</string>
<string name="label_go_key" msgid="1635148082137219148">"Git"</string>
<string name="label_next_key" msgid="362972844525672568">"İleri"</string>
@@ -95,6 +96,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Enter"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Ara"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Nokta"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Dili değiştir"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Sonraki"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Önceki"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Üst karakter etkin"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Büyük harf kilidi etkin"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Üst karakter devre dışı"</string>
diff --git a/java/res/values-uk/bools.xml b/java/res/values-uk/bools.xml
new file mode 100644
index 000000000..840d20c21
--- /dev/null
+++ b/java/res/values-uk/bools.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<resources>
+ <!-- Whether this input method should be used as the default for a locale. Override it
+ for supported languages. -->
+ <bool name="im_is_default">true</bool>
+</resources>
diff --git a/java/res/values-uk/strings-appname.xml b/java/res/values-uk/strings-appname.xml
new file mode 100644
index 000000000..fdbb89fd9
--- /dev/null
+++ b/java/res/values-uk/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Клавіатура Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Засіб перевірки орфографії Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Налаштування клавіатури Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Налаштування перевірки орфографії"</string>
+</resources>
diff --git a/java/res/values-uk/strings.xml b/java/res/values-uk/strings.xml
index e994e1a15..cd9fdfa66 100644
--- a/java/res/values-uk/strings.xml
+++ b/java/res/values-uk/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Клавіатура Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Клавіатура Android (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Налашт-ня клавіат. Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Парам. введення"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Засіб перевірки орфографії Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Команди журналу дослідж."</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Засіб перевірки орфографії Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Налаштування перевірки орфографії"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Шукати імена контактів"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Програма перевірки правопису використ. записи зі списку контактів"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Вібр. при натисканні клавіш"</string>
@@ -34,37 +31,41 @@
<string name="popup_on_keypress" msgid="123894815723512944">"Сплив. при нат.клав."</string>
<string name="general_category" msgid="1859088467017573195">"Загальні"</string>
<string name="correction_category" msgid="2236750915056607613">"Виправлення тексту"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"Ввід жестами"</string>
<string name="misc_category" msgid="6894192814868233453">"Інші опції"</string>
<string name="advanced_settings" msgid="362895144495591463">"Розширені налаштування"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"Налаштування для досвідчених користувачів"</string>
<string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Інші методи введення"</string>
<string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Клавіша зміни мови дозволяє змінювати методи введення"</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"Заблок.клавішу зміни мови"</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"Клавіша зміни мови"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Показувати, коли ввімкнено декілька мов введення"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Затримка клавіши закриття"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Без затримки"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"За умовчанням"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Пропон. імена контактів"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Використ. імена зі списку контактів для пропозицій і виправлень"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Увімкнути виправлення"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Показувати варіанти автовиправлень"</string>
<string name="auto_cap" msgid="1719746674854628252">"Авто викор. вел. літер"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"Писати перше слово в кожному реченні з великої літери"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Додані словники"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Основний словник"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"Показувати пропозиції виправлень"</string>
<string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Відображати пропоновані слова під час вводу"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Завжди показувати"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Показувати в книжковому режимі"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Показувати в книжковій орієнтації"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Завжди ховати"</string>
- <string name="auto_correction" msgid="4979925752001319458">"Автомат. виправлення"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"Автовиправлення"</string>
<string name="auto_correction_summary" msgid="5625751551134658006">"Пробіл і пунктуація автоматично виправляють слова з помилками"</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Вимк."</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Помірне"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Активне"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Дуже активне"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Пропозиції наступного слова"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Використати попереднє слово для покращення пропозицій"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Передбачення наступного слова"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Використовувати попереднє слово також як передбачений запит"</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"Пропозиції наступного слова"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"Використовувати попереднє слово, щоб надавати пропозиції"</string>
+ <string name="gesture_input" msgid="826951152254563827">"Увімкнути ввід жестами"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"Вводити слово, пересуваючи палець по буквах"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Показувати слід жестів"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"Динамічний спливаючий перегляд"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"Показувати пропоноване слово під час введення тексту жестами"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : збережено"</string>
<string name="label_go_key" msgid="1635148082137219148">"Іти"</string>
<string name="label_next_key" msgid="362972844525672568">"Далі"</string>
@@ -95,6 +96,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Клавіша Return"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Пошук"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Крапка"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Змінити мову"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Далі"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Назад"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift увімкнено"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Caps Lock увімкнено"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift вимкнено"</string>
diff --git a/java/res/values-vi/bools.xml b/java/res/values-vi/bools.xml
new file mode 100644
index 000000000..840d20c21
--- /dev/null
+++ b/java/res/values-vi/bools.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<resources>
+ <!-- Whether this input method should be used as the default for a locale. Override it
+ for supported languages. -->
+ <bool name="im_is_default">true</bool>
+</resources>
diff --git a/java/res/values-vi/strings-appname.xml b/java/res/values-vi/strings-appname.xml
new file mode 100644
index 000000000..6e32d0370
--- /dev/null
+++ b/java/res/values-vi/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Bàn phím Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Trình kiểm tra chính tả Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Cài đặt bàn phím Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Cài đặt kiểm tra chính tả"</string>
+</resources>
diff --git a/java/res/values-vi/strings.xml b/java/res/values-vi/strings.xml
index 753af1840..658a4c4ff 100644
--- a/java/res/values-vi/strings.xml
+++ b/java/res/values-vi/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Bàn phím Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Bàn phím Android (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Cài đặt bàn phím Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Tùy chọn nhập"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Trình kiểm tra chính tả Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Lệnh ghi nhật ký cho nghiên cứu"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Trình kiểm tra chính tả Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Cài đặt kiểm tra chính tả"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Tra cứu tên liên hệ"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Trình kiểm tra chính tả sử dụng các mục nhập từ danh sách liên hệ của bạn"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Rung khi nhấn phím"</string>
@@ -34,37 +31,41 @@
<string name="popup_on_keypress" msgid="123894815723512944">"Cửa sổ bật lên khi nhấn phím"</string>
<string name="general_category" msgid="1859088467017573195">"Chung"</string>
<string name="correction_category" msgid="2236750915056607613">"Sửa văn bản"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"Nhập bằng cử chỉ"</string>
<string name="misc_category" msgid="6894192814868233453">"Tùy chọn khác"</string>
<string name="advanced_settings" msgid="362895144495591463">"Cài đặt nâng cao"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"Tùy chọn dành cho chuyên gia"</string>
<string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Phương thức nhập khác"</string>
<string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Khóa chuyển ngôn ngữ bao gồm cả các phương thức nhập liệu khác"</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"Bỏ khóa chuyển ngôn ngữ"</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"Phím chuyển đổi ngôn ngữ"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Hiển thị khi nhiều ngôn ngữ đầu vào được bật"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Loại bỏ hiển thị phím trễ"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Không có tgian trễ"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Mặc định"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Đề xuất tên liên hệ"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Sử dụng tên từ Danh bạ cho các đề xuất và chỉnh sửa"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Bật sửa đổi lại"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Đặt đề xuất cho các sửa đổi lại"</string>
<string name="auto_cap" msgid="1719746674854628252">"Tự động viết hoa"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"Viết hoa chữ đầu tiên của mỗi câu"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Thêm từ điển"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Từ điển chính"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"Hiển thị gợi ý sửa"</string>
<string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Hiển thị từ được đề xuất khi nhập"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Luôn hiển thị"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Hiển thị trên chế độ khổ đứng"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Hiển thị ở chế độ dọc"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Luôn ẩn"</string>
- <string name="auto_correction" msgid="4979925752001319458">"Tự động sửa"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"Tự động sửa"</string>
<string name="auto_correction_summary" msgid="5625751551134658006">"Phím cách và dấu câu tự động sửa từ nhập sai"</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Tắt"</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Đơn giản"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Linh hoạt"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Rất linh hoạt"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Đề xuất từ tiếp theo"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Sử dụng từ trước đó để cải tiến đề xuất"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Dự đoán từ tiếp theo"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Cũng sử dụng từ trước đó để dự đoán"</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"Đề xuất từ tiếp theo"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"Sử dụng từ trước đó khi đưa ra đề xuất"</string>
+ <string name="gesture_input" msgid="826951152254563827">"Bật nhập bằng cử chỉ"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"Nhập từ bằng cách trượt qua các chữ cái"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Hiển thị vệt cử chỉ"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"Xem trước nổi động"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"Xem từ được đề xuất trong khi dùng cử chỉ"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Đã lưu"</string>
<string name="label_go_key" msgid="1635148082137219148">"Tìm"</string>
<string name="label_next_key" msgid="362972844525672568">"Tiếp theo"</string>
@@ -95,6 +96,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Quay lại"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Tìm kiếm"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Dấu chấm"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Chuyển ngôn ngữ"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Tiếp theo"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Trước"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Đã bật Shift"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Đã bật Caps lock"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Đã tắt Shift"</string>
diff --git a/java/res/values-zh-rCN/strings-appname.xml b/java/res/values-zh-rCN/strings-appname.xml
new file mode 100644
index 000000000..f5e12fd5e
--- /dev/null
+++ b/java/res/values-zh-rCN/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Android 键盘"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Android 拼写检查工具"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Android 键盘设置"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"拼写检查设置"</string>
+</resources>
diff --git a/java/res/values-zh-rCN/strings.xml b/java/res/values-zh-rCN/strings.xml
index efde54103..98fdef036 100644
--- a/java/res/values-zh-rCN/strings.xml
+++ b/java/res/values-zh-rCN/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Android 键盘"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android 键盘 (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Android 键盘设置"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"输入选项"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Android 拼写检查工具"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"研究记录命令"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android 拼写检查工具 (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"拼写检查设置"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"查找联系人姓名"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"拼写检查工具会使用您的联系人列表中的条目"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"按键振动"</string>
@@ -34,37 +31,41 @@
<string name="popup_on_keypress" msgid="123894815723512944">"按键时弹出显示字符"</string>
<string name="general_category" msgid="1859088467017573195">"常规"</string>
<string name="correction_category" msgid="2236750915056607613">"文本更正"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"滑行输入"</string>
<string name="misc_category" msgid="6894192814868233453">"其他选项"</string>
<string name="advanced_settings" msgid="362895144495591463">"高级设置"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"高级选项"</string>
<string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"切换到其他输入法"</string>
<string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"语言切换键也可用于切换其他输入法"</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"隐藏语言切换键"</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"语言切换键"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"启用了多种输入语言时显示"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"弹出字符隐藏延迟"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"无延迟"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"默认"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"联系人姓名建议"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"使用联系人中的姓名提供建议和更正"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"允许再次更正"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"设置建议以用于再次更正"</string>
<string name="auto_cap" msgid="1719746674854628252">"自动大写"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"句首字词大写"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"附加词典"</string>
<string name="main_dictionary" msgid="4798763781818361168">"主词典"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"显示更正建议"</string>
<string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"键入时显示建议的字词"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"始终显示"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"在纵向模式时显示"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"在纵向模式中显示"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"始终隐藏"</string>
- <string name="auto_correction" msgid="4979925752001319458">"自动更正"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"自动更正"</string>
<string name="auto_correction_summary" msgid="5625751551134658006">"按空格键和标点可自动更正错别字"</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"关闭"</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"小改"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"大改"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"改动极大"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"下一字词建议"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"使用上一字词改进建议"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"下一字词预测"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"结合前一个字词进行预测"</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"下一个字词建议"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"根据上一个字词提供建议"</string>
+ <string name="gesture_input" msgid="826951152254563827">"启用滑行输入"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"以滑动方式写出字词中字母来输入字词"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"显示滑行输入轨迹"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"动态漂浮预览"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"使用手势输入时显示建议字词"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>:已保存"</string>
<string name="label_go_key" msgid="1635148082137219148">"开始"</string>
<string name="label_next_key" msgid="362972844525672568">"下一步"</string>
@@ -95,6 +96,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"返回"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"搜索"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"点"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"切换语言"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"下一个"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"上一个"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift 模式已启用"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"大写锁定已启用"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift 模式已停用"</string>
diff --git a/java/res/values-zh-rTW/strings-appname.xml b/java/res/values-zh-rTW/strings-appname.xml
new file mode 100644
index 000000000..8cc663826
--- /dev/null
+++ b/java/res/values-zh-rTW/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Android 鍵盤"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Android 拼字檢查"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Android 鍵盤設定"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"拼字檢查設定"</string>
+</resources>
diff --git a/java/res/values-zh-rTW/strings.xml b/java/res/values-zh-rTW/strings.xml
index 51df022aa..d030d506d 100644
--- a/java/res/values-zh-rTW/strings.xml
+++ b/java/res/values-zh-rTW/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Android 鍵盤"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Android 鍵盤 (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Android 鍵盤設定"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"輸入選項"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Android 拼字檢查"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"研究紀錄指令"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Android 拼字檢查 (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"拼字檢查設定"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"查詢聯絡人姓名"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"拼字檢查程式使用您的聯絡人清單項目"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"按鍵時震動"</string>
@@ -34,37 +31,41 @@
<string name="popup_on_keypress" msgid="123894815723512944">"按鍵時顯示彈出式視窗"</string>
<string name="general_category" msgid="1859088467017573195">"一般設定"</string>
<string name="correction_category" msgid="2236750915056607613">"文字修正"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"手勢輸入"</string>
<string name="misc_category" msgid="6894192814868233453">"其他選項"</string>
<string name="advanced_settings" msgid="362895144495591463">"進階設定"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"進階選項"</string>
<string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"切換至其他輸入法"</string>
<string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"使語言切換鍵包含其他輸入法"</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"隱藏語言切換鍵"</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"語言切換鍵"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"有多種輸入語言可選用時顯示切換鍵"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"關閉彈出式鍵盤的延遲時間"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"不延遲"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"預設"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"建議聯絡人名稱"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"根據「聯絡人」名稱提供建議與修正"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"啟用重新更正"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"設定建議供重新更正"</string>
<string name="auto_cap" msgid="1719746674854628252">"自動大寫"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"句首字詞大寫"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"外掛字典"</string>
<string name="main_dictionary" msgid="4798763781818361168">"主要字典"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"顯示修正建議"</string>
<string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"輸入時顯示建議字詞"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"一律顯示"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"以垂直模式顯示"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"在垂直模式中顯示"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"永遠隱藏"</string>
- <string name="auto_correction" msgid="4979925752001319458">"自動修正"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"自動修正"</string>
<string name="auto_correction_summary" msgid="5625751551134658006">"按空白鍵或標點符號時,自動修正前面的錯字"</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"關閉"</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"更正範圍小"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"更正範圍大"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"更正範圍極大"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"下一個字詞建議"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"根據前一個字詞找出更適合的建議"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"下一個字詞預測"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"同樣使用前一個字詞進行預測"</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"建議下一個字詞"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"根據前一個字詞提供建議"</string>
+ <string name="gesture_input" msgid="826951152254563827">"啟用手勢輸入"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"以滑動方式寫出字詞中字母來輸入字詞"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"顯示手勢軌跡"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"動態浮動預覽"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"使用手勢輸入時顯示建議字詞"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>:已儲存"</string>
<string name="label_go_key" msgid="1635148082137219148">"開始"</string>
<string name="label_next_key" msgid="362972844525672568">"繼續"</string>
@@ -95,6 +96,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"返回"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"搜尋"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"點"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"切換語言"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"下一步"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"上一步"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"Shift 鍵已啟用"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"大寫鎖定已啟用"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"Shift 鍵已停用"</string>
diff --git a/java/res/values-zu/bools.xml b/java/res/values-zu/bools.xml
new file mode 100644
index 000000000..840d20c21
--- /dev/null
+++ b/java/res/values-zu/bools.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+<resources>
+ <!-- Whether this input method should be used as the default for a locale. Override it
+ for supported languages. -->
+ <bool name="im_is_default">true</bool>
+</resources>
diff --git a/java/res/values-zu/strings-appname.xml b/java/res/values-zu/strings-appname.xml
new file mode 100644
index 000000000..a0fb51716
--- /dev/null
+++ b/java/res/values-zu/strings-appname.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <string name="english_ime_name" msgid="178705338187710493">"Ikhibhodi ye-Android"</string>
+ <string name="spell_checker_service_name" msgid="6268342166872202903">"Isihloli sokupela se-Android"</string>
+ <string name="english_ime_settings" msgid="7470027018752707691">"Izilungiselelo zekhibhodi ye-Android"</string>
+ <string name="android_spell_checker_settings" msgid="8397842018475560441">"Izilungiselelo zokuhlola ukupela"</string>
+</resources>
diff --git a/java/res/values-zu/strings.xml b/java/res/values-zu/strings.xml
index d3f80e42a..8b8b4da61 100644
--- a/java/res/values-zu/strings.xml
+++ b/java/res/values-zu/strings.xml
@@ -20,13 +20,10 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="english_ime_name" msgid="7252517407088836577">"Ikhibhodi ye-Android"</string>
<string name="aosp_android_keyboard_ime_name" msgid="7877134937939182296">"Ikhibhodi ye-Android (AOSP)"</string>
- <string name="english_ime_settings" msgid="6661589557206947774">"Izilungiselelo zekhibhodi ye-Android"</string>
<string name="english_ime_input_options" msgid="3909945612939668554">"Okukhethwa kukho kokungenayo"</string>
- <string name="spell_checker_service_name" msgid="7338064335159755926">"Isihloli sokupela se-Android"</string>
+ <string name="english_ime_research_log" msgid="8492602295696577851">"Imiyalo yefayela lokungena lokucwaninga"</string>
<string name="aosp_spell_checker_service_name" msgid="6985142605330377819">"Isihloli sokupela se-Android (AOSP)"</string>
- <string name="android_spell_checker_settings" msgid="5822324635435443689">"Izilungiselelo zokuhlola ukupela"</string>
<string name="use_contacts_for_spellchecking_option_title" msgid="5374120998125353898">"Bheka amagama woxhumana nabo"</string>
<string name="use_contacts_for_spellchecking_option_summary" msgid="8754413382543307713">"Isihloli sokupela sisebenzisa okungenayo kusuka kuhlu lalabo oxhumana nabo"</string>
<string name="vibrate_on_keypress" msgid="5258079494276955460">"Dlidlizelisa ngokucindezela inkinobho"</string>
@@ -34,37 +31,41 @@
<string name="popup_on_keypress" msgid="123894815723512944">"Ugaxekile ngokucindezela ukhiye"</string>
<string name="general_category" msgid="1859088467017573195">"Okuvamile"</string>
<string name="correction_category" msgid="2236750915056607613">"Ukulungiswa kombhalo"</string>
+ <string name="gesture_typing_category" msgid="497263612130532630">"Ukuthayipha ngokuthinta"</string>
<string name="misc_category" msgid="6894192814868233453">"Okunye okukhethwa kukho"</string>
<string name="advanced_settings" msgid="362895144495591463">"Izilungiselelo ezithuthukisiwe"</string>
<string name="advanced_settings_summary" msgid="4487980456152830271">"Izinketho zezingcwenti"</string>
<string name="include_other_imes_in_language_switch_list" msgid="4533689960308565519">"Shintshela kwezinye izindlela zokungena"</string>
<string name="include_other_imes_in_language_switch_list_summary" msgid="840637129103317635">"Ukhiye wokushintsha ulimi ubandakanya ezinye izindlela zokungenayo"</string>
- <string name="suppress_language_switch_key" msgid="8003788410354806368">"Ukhiye wokushintshela wokuvimbela ulimi"</string>
+ <string name="show_language_switch_key" msgid="5915478828318774384">"Ukhiye wokushintsha ullimi"</string>
+ <string name="show_language_switch_key_summary" msgid="7343403647474265713">"Bonisa uma izilimi zokufaka zinikwe amandla"</string>
<string name="key_preview_popup_dismiss_delay" msgid="6213164897443068248">"Ukuvela kokhiye cashisa ukulibazisa"</string>
<string name="key_preview_popup_dismiss_no_delay" msgid="2096123151571458064">"Cha ukulibazisa"</string>
<string name="key_preview_popup_dismiss_default_delay" msgid="2166964333903906734">"Okuzenzakalelayo"</string>
<string name="use_contacts_dict" msgid="4435317977804180815">"Sikisela amagama Othintana nabo"</string>
<string name="use_contacts_dict_summary" msgid="6599983334507879959">"Amagama abasebenzisi kusuka Kothintana nabo bokusikisela nokulungisa"</string>
- <string name="enable_span_insert" msgid="7204653105667167620">"Vumela ukulungiswa kabusha"</string>
- <string name="enable_span_insert_summary" msgid="2947317657871394467">"Setha iziphakamiso zokulungisa kabusha"</string>
<string name="auto_cap" msgid="1719746674854628252">"Ukwenza ofeleba okuzenzakalelayo"</string>
+ <string name="auto_cap_summary" msgid="7934452761022946874">"Yenza ufeleba wegama lokuqala lomusho ngamunye"</string>
<string name="configure_dictionaries_title" msgid="4238652338556902049">"Faka izichazamazwi"</string>
<string name="main_dictionary" msgid="4798763781818361168">"Isichazamazwi sakho ngqangi"</string>
<string name="prefs_show_suggestions" msgid="8026799663445531637">"Bonisa ukusikesela kokulungisa"</string>
<string name="prefs_show_suggestions_summary" msgid="1583132279498502825">"Bonisa amagama aphakamisiwe ngenkathi uthayipha"</string>
<string name="prefs_suggestion_visibility_show_name" msgid="3219916594067551303">"Bonisa njalo"</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3551821800439659812">"Bonisa kwimodi emile"</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name" msgid="3859783767435239118">"Bonisa ngomumo oqondile"</string>
<string name="prefs_suggestion_visibility_hide_name" msgid="6309143926422234673">"Fihla njalo"</string>
- <string name="auto_correction" msgid="4979925752001319458">"Ukulungisa okuzenzakalelayo"</string>
+ <string name="auto_correction" msgid="7630720885194996950">"Ukulungisa okuzenzakalelayo"</string>
<string name="auto_correction_summary" msgid="5625751551134658006">"Ibha yesikhala nokubhala ngamagama amakhulu kulungisa amaphutha amagama athayiphwe kabi"</string>
<string name="auto_correction_threshold_mode_off" msgid="8470882665417944026">"Valiwe"</string>
<string name="auto_correction_threshold_mode_modest" msgid="8788366690620799097">"Thobekile"</string>
<string name="auto_correction_threshold_mode_aggeressive" msgid="3524029103734923819">"Bukhali"</string>
<string name="auto_correction_threshold_mode_very_aggeressive" msgid="3386782235540547678">"Nobudlova kakhulu"</string>
- <string name="bigram_suggestion" msgid="8169311444438922902">"Iziphakamiso zegama elilandelayo"</string>
- <string name="bigram_suggestion_summary" msgid="6635527607242625713">"Sebenzisa igama elandulele ukuthuthukisa iziphakamiso"</string>
- <string name="bigram_prediction" msgid="3216364899483135294">"Ukuqagela kwegama elilandelayo"</string>
- <string name="bigram_prediction_summary" msgid="1747261921174300098">"Sebenzisa igama langaphambilini ukuze uqagele"</string>
+ <string name="bigram_prediction" msgid="1084449187723948550">"Iziphakamiso zegama elilandelayo"</string>
+ <string name="bigram_prediction_summary" msgid="3896362682751109677">"Sebenzisa igama langaphambilini ekwenzeni iziphakamiso"</string>
+ <string name="gesture_input" msgid="826951152254563827">"Nika amandla okuthayipha ngokuthinta"</string>
+ <string name="gesture_input_summary" msgid="9180350639305731231">"Faka igama ngokushelelisa ezinhlamvini"</string>
+ <string name="gesture_preview_trail" msgid="3802333369335722221">"Bonisa i-trail yokuthinta"</string>
+ <string name="gesture_floating_preview_text" msgid="4443240334739381053">"Ukuhlola kuqala okuntantayo okunamandla"</string>
+ <string name="gesture_floating_preview_text_summary" msgid="4472696213996203533">"Bona igama eliphakanyisiwe ngenkathi uthinta"</string>
<string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Kulondoloziwe"</string>
<string name="label_go_key" msgid="1635148082137219148">"Iya"</string>
<string name="label_next_key" msgid="362972844525672568">"Okulandelayo"</string>
@@ -95,6 +96,9 @@
<string name="spoken_description_return" msgid="8178083177238315647">"Buyisela"</string>
<string name="spoken_description_search" msgid="1247236163755920808">"Sesha"</string>
<string name="spoken_description_dot" msgid="40711082435231673">"Icashazi"</string>
+ <string name="spoken_description_language_switch" msgid="5507091328222331316">"Shintsha ulimi"</string>
+ <string name="spoken_description_action_next" msgid="8636078276664150324">"Okulandelayo"</string>
+ <string name="spoken_description_action_previous" msgid="800872415009336208">"Okwangaphambilini"</string>
<string name="spoken_description_shiftmode_on" msgid="5700440798609574589">"U-Shift uvunyelwe"</string>
<string name="spoken_description_shiftmode_locked" msgid="593175803181701830">"Ofeleba bavunyelwe"</string>
<string name="spoken_description_shiftmode_off" msgid="657219998449174808">"U-Shift uvimbelwe"</string>
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml
index a18371fc9..53051d033 100644
--- a/java/res/values/attrs.xml
+++ b/java/res/values/attrs.xml
@@ -18,13 +18,10 @@
<declare-styleable name="KeyboardTheme">
<!-- Keyboard style -->
<attr name="keyboardStyle" format="reference" />
- <!-- TODO: Get rid of latinKeyboardStyle -->
- <!-- LatinKeyboard style -->
- <attr name="latinKeyboardStyle" format="reference" />
<!-- KeyboardView style -->
<attr name="keyboardViewStyle" format="reference" />
- <!-- LatinKeyboardView style -->
- <attr name="latinKeyboardViewStyle" format="reference" />
+ <!-- MainKeyboardView style -->
+ <attr name="mainKeyboardViewStyle" format="reference" />
<!-- MoreKeysKeyboard style -->
<attr name="moreKeysKeyboardStyle" format="reference" />
<!-- MoreKeysKeyboardView style -->
@@ -32,7 +29,7 @@
<attr name="moreKeysKeyboardPanelStyle" format="reference" />
<!-- Suggestions strip style -->
<attr name="suggestionsStripBackgroundStyle" format="reference" />
- <attr name="suggestionsViewStyle" format="reference" />
+ <attr name="suggestionStripViewStyle" format="reference" />
<attr name="moreSuggestionsViewStyle" format="reference" />
<attr name="suggestionBackgroundStyle" format="reference" />
<attr name="suggestionPreviewBackgroundStyle" format="reference" />
@@ -44,26 +41,6 @@
checkable+checked+pressed. -->
<attr name="keyBackground" format="reference" />
- <!-- Size of the text for one letter keys. If not defined, keyLetterRatio takes effect. -->
- <attr name="keyLetterSize" format="dimension" />
- <!-- Size of the text for keys with multiple letters. If not defined, keyLabelRatio takes
- effect. -->
- <attr name="keyLabelSize" format="dimension" />
- <!-- Size of the text for one letter keys, in the proportion of key height. -->
- <attr name="keyLetterRatio" format="float" />
- <!-- Large size of the text for one letter keys, in the proportion of key height. -->
- <attr name="keyLargeLetterRatio" format="float" />
- <!-- Size of the text for keys with multiple letters, in the proportion of key height. -->
- <attr name="keyLabelRatio" format="float" />
- <!-- Large size of the text for keys with multiple letters, in the proportion of key height. -->
- <attr name="keyLargeLabelRatio" format="float" />
- <!-- Size of the text for hint letter (= one character hint label), in the proportion of
- key height. -->
- <attr name="keyHintLetterRatio" format="float" />
- <!-- Size of the text for hint label, in the proportion of key height. -->
- <attr name="keyHintLabelRatio" format="float" />
- <!-- Size of the text for shifted letter hint, in the proportion of key height. -->
- <attr name="keyShiftedLetterHintRatio" format="float" />
<!-- Horizontal padding of left/right aligned key label to the edge of the key. -->
<attr name="keyLabelHorizontalPadding" format="dimension" />
<!-- Right padding of hint letter to the edge of the key.-->
@@ -72,35 +49,19 @@
<attr name="keyPopupHintLetterPadding" format="dimension" />
<!-- Right padding of shifted letter hint to the edge of the key.-->
<attr name="keyShiftedLetterHintPadding" format="dimension" />
-
- <!-- Color to use for the label in a key. -->
- <attr name="keyTextColor" format="color" />
- <!-- Color to use for the label in a key when in inactivated state. -->
- <attr name="keyTextInactivatedColor" format="color" />
- <!-- Key hint letter (= one character hint label) color -->
- <attr name="keyHintLetterColor" format="color" />
- <!-- Key hint label color -->
- <attr name="keyHintLabelColor" format="color" />
- <!-- Shifted letter hint colors -->
- <attr name="keyShiftedLetterHintInactivatedColor" format="color" />
- <attr name="keyShiftedLetterHintActivatedColor" format="color" />
+ <!-- Blur radius of key text shadow. -->
+ <attr name="keyTextShadowRadius" format="float" />
<!-- Layout resource for key press feedback.-->
<attr name="keyPreviewLayout" format="reference" />
- <!-- The background for key press feedback. -->
- <attr name="keyPreviewBackground" format="reference" />
- <!-- The background for the left edge key press feedback. -->
- <attr name="keyPreviewLeftBackground" format="reference" />
- <!-- The background for the right edge key press feedback. -->
- <attr name="keyPreviewRightBackground" format="reference" />
- <!-- The text color for key press feedback. -->
- <attr name="keyPreviewTextColor" format="color" />
+ <!-- Key preview background states -->
+ <attr name="state_left_edge" format="boolean" />
+ <attr name="state_right_edge" format="boolean" />
+ <attr name="state_has_morekeys" format="boolean" />
<!-- Vertical offset of the key press feedback from the key. -->
<attr name="keyPreviewOffset" format="dimension" />
<!-- Height of the key press feedback popup. -->
<attr name="keyPreviewHeight" format="dimension" />
- <!-- Size of the text for key press feedback popup, int the proportion of key height -->
- <attr name="keyPreviewTextRatio" format="float" />
<!-- Delay after key releasing and key press feedback dismissing in millisecond -->
<attr name="keyPreviewLingerTimeout" format="integer" />
@@ -110,20 +71,30 @@
<!-- Layout resource for more keys panel -->
<attr name="moreKeysLayout" format="reference" />
- <attr name="shadowColor" format="color" />
- <attr name="shadowRadius" format="float" />
<attr name="backgroundDimAlpha" format="integer" />
- <attr name="keyTextStyle" format="enum">
- <!-- This should be aligned with Typeface.NORMAL etc. -->
- <enum name="normal" value="0" />
- <enum name="bold" value="1" />
- <enum name="italic" value="2" />
- <enum name="boldItalic" value="3" />
- </attr>
+ <!-- Attributes for PreviewPlacerView -->
+ <attr name="gestureFloatingPreviewTextSize" format="dimension" />
+ <attr name="gestureFloatingPreviewTextColor" format="color" />
+ <attr name="gestureFloatingPreviewTextOffset" format="dimension" />
+ <attr name="gestureFloatingPreviewColor" format="color" />
+ <attr name="gestureFloatingPreviewHorizontalPadding" format="dimension" />
+ <attr name="gestureFloatingPreviewVerticalPadding" format="dimension" />
+ <attr name="gestureFloatingPreviewRoundRadius" format="dimension" />
+ <!-- Delay after gesture input and gesture floating preview text dismissing in millisecond -->
+ <attr name="gestureFloatingPreviewTextLingerTimeout" format="integer" />
+ <!-- Delay after gesture trail starts fading out in millisecond. -->
+ <attr name="gesturePreviewTrailFadeoutStartDelay" format="integer" />
+ <!-- Duration while gesture preview trail is fading out in millisecond. -->
+ <attr name="gesturePreviewTrailFadeoutDuration" format="integer" />
+ <!-- Interval of updating gesture preview trail in millisecond. -->
+ <attr name="gesturePreviewTrailUpdateInterval" format="integer" />
+ <attr name="gesturePreviewTrailColor" format="color" />
+ <attr name="gesturePreviewTrailStartWidth" format="dimension" />
+ <attr name="gesturePreviewTrailEndWidth" format="dimension" />
</declare-styleable>
- <declare-styleable name="LatinKeyboardView">
+ <declare-styleable name="MainKeyboardView">
<attr name="autoCorrectionSpacebarLedEnabled" format="boolean" />
<attr name="autoCorrectionSpacebarLedIcon" format="reference" />
<!-- Size of the text for spacebar language label, in the proportion of key height. -->
@@ -138,6 +109,8 @@
<attr name="altCodeKeyWhileTypingFadeinAnimator" format="reference" />
<!-- Key detection hysteresis distance. -->
<attr name="keyHysteresisDistance" format="dimension" />
+ <!-- Key detection hysteresis distance for shift/symbols sliding input. -->
+ <attr name="keyHysteresisDistanceForSlidingModifier" format="dimension" />
<!-- Touch noise threshold time in millisecond -->
<attr name="touchNoiseThresholdTime" format="integer" />
<!-- Touch noise threshold distance in millimeter -->
@@ -156,11 +129,30 @@
<attr name="ignoreAltCodeKeyTimeout" format="integer" />
<!-- More keys keyboard will shown at touched point. -->
<attr name="showMoreKeysKeyboardAtTouchedPoint" format="boolean" />
+ <!-- Static threshold for gesture after fast typing (msec) -->
+ <attr name="gestureStaticTimeThresholdAfterFastTyping" format="integer" />
+ <!-- Static threshold for starting gesture detection (keyWidth%/sec) -->
+ <attr name="gestureDetectFastMoveSpeedThreshold" format="fraction" />
+ <!-- Dynamic threshold for gesture after fast typing (msec) -->
+ <attr name="gestureDynamicThresholdDecayDuration" format="integer" />
+ <!-- Time based threshold values for gesture detection (msec) -->
+ <attr name="gestureDynamicTimeThresholdFrom" format="integer" />
+ <attr name="gestureDynamicTimeThresholdTo" format="integer" />
+ <!-- Distance based threshold values for gesture detection (keyWidth%/sec) -->
+ <attr name="gestureDynamicDistanceThresholdFrom" format="fraction" />
+ <attr name="gestureDynamicDistanceThresholdTo" format="fraction" />
+ <!-- Parameter for gesture sampling (keyWidth%/sec) -->
+ <attr name="gestureSamplingMinimumDistance" format="fraction" />
+ <!-- Parameters for gesture recognition (msec) and (keyWidth%/sec) -->
+ <attr name="gestureRecognitionMinimumTime" format="integer" />
+ <attr name="gestureRecognitionSpeedThreshold" format="fraction" />
+ <!-- Suppress showing key preview duration after batch input in millisecond -->
+ <attr name="suppressKeyPreviewAfterBatchInputDuration" format="integer" />
</declare-styleable>
- <declare-styleable name="SuggestionsView">
+ <declare-styleable name="SuggestionStripView">
<attr name="suggestionStripOption" format="integer">
- <!-- This should be aligned with SuggestionsViewParams.AUTO_CORRECT_* and etc. -->
+ <!-- This should be aligned with SuggestionStripViewParams.AUTO_CORRECT_* and etc. -->
<flag name="autoCorrectBold" value="0x01" />
<flag name="autoCorrectUnderline" value="0x02" />
<flag name="validTypedWordBold" value="0x04" />
@@ -169,13 +161,13 @@
<attr name="colorTypedWord" format="color" />
<attr name="colorAutoCorrect" format="color" />
<attr name="colorSuggested" format="color" />
- <attr name="alphaValidTypedWord" format="integer" />
- <attr name="alphaTypedWord" format="integer" />
- <attr name="alphaAutoCorrect" format="integer" />
- <attr name="alphaSuggested" format="integer" />
- <attr name="alphaObsoleted" format="integer" />
+ <attr name="alphaValidTypedWord" format="fraction" />
+ <attr name="alphaTypedWord" format="fraction" />
+ <attr name="alphaAutoCorrect" format="fraction" />
+ <attr name="alphaSuggested" format="fraction" />
+ <attr name="alphaObsoleted" format="fraction" />
<attr name="suggestionsCountInStrip" format="integer" />
- <attr name="centerSuggestionPercentile" format="integer" />
+ <attr name="centerSuggestionPercentile" format="fraction" />
<attr name="maxMoreSuggestionsRow" format="integer" />
<attr name="minMoreSuggestionsWidth" format="float" />
</declare-styleable>
@@ -319,6 +311,50 @@
<!-- The X-coordinate of upper right corner of this key including horizontal gap.
If the value is negative, the origin is the right edge of the keyboard. -->
<attr name="keyXPos" format="dimension|fraction" />
+
+ <!-- Key top visual attributes -->
+ <attr name="keyTypeface" format="enum">
+ <!-- This should be aligned with Typeface.NORMAL etc. -->
+ <enum name="normal" value="0" />
+ <enum name="bold" value="1" />
+ <enum name="italic" value="2" />
+ <enum name="boldItalic" value="3" />
+ </attr>
+ <!-- Size of the text for one letter keys. If specified as fraction, the text size is
+ measured in the proportion of key height. -->
+ <attr name="keyLetterSize" format="dimension|fraction" />
+ <!-- Size of the text for keys with multiple letters. If specified as fraction, the text
+ size is measured in the proportion of key height. -->
+ <attr name="keyLabelSize" format="dimension|fraction" />
+ <!-- Large size of the text for one letter keys, in the proportion of key height. -->
+ <attr name="keyLargeLetterRatio" format="fraction" />
+ <!-- Large size of the text for keys with multiple letters, in the proportion of key height. -->
+ <attr name="keyLargeLabelRatio" format="fraction" />
+ <!-- Size of the text for hint letter (= one character hint label), in the proportion of
+ key height. -->
+ <attr name="keyHintLetterRatio" format="fraction" />
+ <!-- Size of the text for hint label, in the proportion of key height. -->
+ <attr name="keyHintLabelRatio" format="fraction" />
+ <!-- Size of the text for shifted letter hint, in the proportion of key height. -->
+ <attr name="keyShiftedLetterHintRatio" format="fraction" />
+ <!-- Color to use for the label in a key. -->
+ <attr name="keyTextColor" format="color" />
+ <attr name="keyTextShadowColor" format="color" />
+ <!-- Color to use for the label in a key when in inactivated state. -->
+ <attr name="keyTextInactivatedColor" format="color" />
+ <!-- Key hint letter (= one character hint label) color -->
+ <attr name="keyHintLetterColor" format="color" />
+ <!-- Key hint label color -->
+ <attr name="keyHintLabelColor" format="color" />
+ <!-- Shifted letter hint colors -->
+ <attr name="keyShiftedLetterHintInactivatedColor" format="color" />
+ <attr name="keyShiftedLetterHintActivatedColor" format="color" />
+
+ <!-- Key preview visual parameters -->
+ <!-- The text color for key press feedback. -->
+ <attr name="keyPreviewTextColor" format="color" />
+ <!-- Size of the text for key press feedback popup, in the proportion of key height. -->
+ <attr name="keyPreviewTextRatio" format="fraction" />
</declare-styleable>
<declare-styleable name="Keyboard_Include">
diff --git a/java/res/values/bools.xml b/java/res/values/bools.xml
index 889d8f784..10d217985 100644
--- a/java/res/values/bools.xml
+++ b/java/res/values/bools.xml
@@ -1,24 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-/*
+/*
**
** Copyright 2008, 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
+** 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
+** 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
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
*/
-->
<resources>
<!-- Whether this input method should be used as the default for a locale. Override it
- for latin languages. -->
+ for supported languages. -->
<bool name="im_is_default">false</bool>
</resources>
diff --git a/java/res/values/config.xml b/java/res/values/config.xml
index 8d3319dae..cb1358726 100644
--- a/java/res/values/config.xml
+++ b/java/res/values/config.xml
@@ -19,16 +19,15 @@
-->
<resources>
+ <!-- Device form factor. This value must be aligned with {@link KeyboardId.DEVICE_FORM_FACTOR_PHONE} -->
+ <integer name="config_device_form_factor">0</integer>
<bool name="config_use_fullscreen_mode">false</bool>
<bool name="config_enable_show_voice_key_option">true</bool>
<bool name="config_enable_show_popup_on_keypress_option">true</bool>
- <bool name="config_enable_next_word_suggestions_option">true</bool>
- <bool name="config_enable_usability_study_mode_option">false</bool>
+ <!-- TODO: Disable the following configuration for production. -->
+ <bool name="config_enable_usability_study_mode_option">true</bool>
<!-- Whether or not Popup on key press is enabled by default -->
<bool name="config_default_popup_preview">true</bool>
- <!-- Default value for next word suggestion: while showing suggestions for a word should we weigh
- in the previous word? -->
- <bool name="config_default_next_word_suggestions">true</bool>
<!-- Default value for next word prediction: after entering a word and a space only, should we look
at input history to suggest a hopefully helpful suggestions for the next word? -->
<bool name="config_default_next_word_prediction">true</bool>
@@ -50,10 +49,15 @@
Configuration for KeyboardView
-->
<integer name="config_key_preview_linger_timeout">70</integer>
+ <integer name="config_gesture_floating_preview_text_linger_timeout">200</integer>
+ <integer name="config_gesture_preview_trail_fadeout_start_delay">100</integer>
+ <integer name="config_gesture_preview_trail_fadeout_duration">800</integer>
+ <integer name="config_gesture_preview_trail_update_interval">20</integer>
<!--
- Configuration for LatinKeyboardView
+ Configuration for MainKeyboardView
-->
<dimen name="config_key_hysteresis_distance">8.0dp</dimen>
+ <dimen name="config_key_hysteresis_distance_for_sliding_modifier">8.0dp</dimen>
<integer name="config_touch_noise_threshold_time">40</integer>
<dimen name="config_touch_noise_threshold_distance">12.6dp</dimen>
<bool name="config_sliding_key_input_enabled">true</bool>
@@ -66,6 +70,25 @@
<!-- Showing more keys keyboard, just above the touched point if true, aligned to the key if
false -->
<bool name="config_show_more_keys_keyboard_at_touched_point">false</bool>
+ <!-- Static threshold for gesture after fast typing (msec) -->
+ <integer name="config_gesture_static_time_threshold_after_fast_typing">500</integer>
+ <!-- Static threshold for starting gesture detection (keyWidth%/sec) -->
+ <fraction name="config_gesture_detect_fast_move_speed_threshold">150%</fraction>
+ <!-- Dynamic threshold for gesture after fast typing (msec) -->
+ <integer name="config_gesture_dynamic_threshold_decay_duration">450</integer>
+ <!-- Time based threshold values for gesture detection (msec) -->
+ <integer name="config_gesture_dynamic_time_threshold_from">300</integer>
+ <integer name="config_gesture_dynamic_time_threshold_to">20</integer>
+ <!-- Distance based threshold values for gesture detection (keyWidth%/sec) -->
+ <fraction name="config_gesture_dynamic_distance_threshold_from">600%</fraction>
+ <fraction name="config_gesture_dynamic_distance_threshold_to">50%</fraction>
+ <!-- Parameter for gesture sampling (keyWidth%/sec) -->
+ <fraction name="config_gesture_sampling_minimum_distance">16.6666%</fraction>
+ <!-- Parameters for gesture recognition (msec) and (keyWidth%/sec) -->
+ <integer name="config_gesture_recognition_minimum_time">100</integer>
+ <fraction name="config_gesture_recognition_speed_threshold">550%</fraction>
+ <!-- Suppress showing key preview duration after batch input in millisecond -->
+ <integer name="config_suppress_key_preview_after_batch_input_duration">1000</integer>
<!--
Configuration for auto correction
-->
diff --git a/java/res/values/dimens.xml b/java/res/values/dimens.xml
index 925eb55fa..c7d993698 100644
--- a/java/res/values/dimens.xml
+++ b/java/res/values/dimens.xml
@@ -1,19 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-/*
+/*
**
** Copyright 2008, 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
+** 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
+** 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
+** 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.
*/
-->
@@ -73,6 +73,11 @@
<dimen name="key_popup_hint_letter_padding">2dp</dimen>
<dimen name="key_uppercase_letter_padding">2dp</dimen>
+ <!-- For 5-row keyboard -->
+ <fraction name="key_bottom_gap_5row">3.20%p</fraction>
+ <fraction name="key_letter_ratio_5row">64%</fraction>
+ <fraction name="key_uppercase_letter_ratio_5row">41%</fraction>
+
<dimen name="key_preview_offset_ics">8.0dp</dimen>
<!-- popup_key_height x -0.5 -->
<dimen name="more_keys_keyboard_vertical_correction_ics">-26.4dp</dimen>
@@ -92,5 +97,18 @@
<dimen name="suggestion_text_size">18dp</dimen>
<dimen name="more_suggestions_hint_text_size">27dp</dimen>
<integer name="suggestions_count_in_strip">3</integer>
- <integer name="center_suggestion_percentile">36</integer>
+ <fraction name="center_suggestion_percentile">36%</fraction>
+
+ <!-- Gesture preview trail parameters -->
+ <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>
+ <dimen name="gesture_floating_preview_text_offset">73dp</dimen>
+ <dimen name="gesture_floating_preview_horizontal_padding">24dp</dimen>
+ <dimen name="gesture_floating_preview_vertical_padding">16dp</dimen>
+ <dimen name="gesture_floating_preview_round_radius">3dp</dimen>
+
+ <!-- Inset used in Accessibility mode to avoid accidental key presses when a finger slides off the screen. -->
+ <dimen name="accessibility_edge_slop">8dp</dimen>
</resources>
diff --git a/java/res/values/donottranslate.xml b/java/res/values/donottranslate.xml
index 0970aeee0..9e07b2248 100644
--- a/java/res/values/donottranslate.xml
+++ b/java/res/values/donottranslate.xml
@@ -23,9 +23,9 @@
<!-- Symbols that should be swapped with a weak space -->
<string name="weak_space_swapping_symbols">.,;:!?)]}\"</string>
<!-- Symbols that should strip a weak space -->
- <string name="weak_space_stripping_symbols">"&#x0009;&#x0020;\n/_\'-"</string>
+ <string name="weak_space_stripping_symbols">"&#x0009;&#x0020;\n/_\'-"@</string>
<!-- Symbols that should convert weak spaces into real space -->
- <string name="phantom_space_promoting_symbols">([*&amp;@{&lt;&gt;+=|</string>
+ <string name="phantom_space_promoting_symbols">([*&amp;{&lt;&gt;+=|</string>
<!-- Symbols that do NOT separate words -->
<string name="symbols_excluded_from_word_separators">\'-</string>
<!-- Word separator list is the union of all symbols except those that are not separators:
diff --git a/java/res/xml/kbd_esperanto.xml b/java/res/values/gesture-input.xml
index c0c45dd11..235616fbe 100644
--- a/java/res/xml/kbd_esperanto.xml
+++ b/java/res/values/gesture-input.xml
@@ -17,10 +17,6 @@
** limitations under the License.
*/
-->
-
-<Keyboard
- xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
- <include
- latin:keyboardLayout="@xml/rows_esperanto" />
-</Keyboard>
+<resources>
+ <bool name="config_gesture_input_enabled_by_build_config">false</bool>
+</resources>
diff --git a/java/res/values/keypress-vibration-durations.xml b/java/res/values/keypress-vibration-durations.xml
index 2569f2317..370959c1a 100644
--- a/java/res/values/keypress-vibration-durations.xml
+++ b/java/res/values/keypress-vibration-durations.xml
@@ -22,5 +22,7 @@
<!-- Build.HARDWARE,duration_in_milliseconds -->
<item>herring,5</item>
<item>tuna,5</item>
+ <item>mako,5</item>
+ <item>manta,16</item>
</string-array>
</resources>
diff --git a/java/res/values/keypress-volumes.xml b/java/res/values/keypress-volumes.xml
index 3b433e4ab..d1120694b 100644
--- a/java/res/values/keypress-volumes.xml
+++ b/java/res/values/keypress-volumes.xml
@@ -24,5 +24,7 @@
<item>tuna,0.5</item>
<item>stingray,0.4</item>
<item>grouper,0.3</item>
+ <item>mako,0.3</item>
+ <item>manta,0.2</item>
</string-array>
</resources>
diff --git a/java/res/values/phantom_sudden_move_event_device_list.xml b/java/res/values/phantom-sudden-move-event-device-list.xml
index 63d12e96e..63d12e96e 100644
--- a/java/res/values/phantom_sudden_move_event_device_list.xml
+++ b/java/res/values/phantom-sudden-move-event-device-list.xml
diff --git a/java/res/values/predefined-subtypes.xml b/java/res/values/predefined-subtypes.xml
index 602f53eac..3bf0e617f 100644
--- a/java/res/values/predefined-subtypes.xml
+++ b/java/res/values/predefined-subtypes.xml
@@ -18,6 +18,9 @@
*/
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- Predefined subtypes (language:layout[:extraValue]) in semicolon separated format -->
- <string name="predefined_subtypes" translatable="false">de:qwerty:AsciiCapable;fr:qwertz:AsciiCapable</string>
+ <!-- Predefined subtypes (language:layout[:extraValue]) -->
+ <string-array name="predefined_subtypes" translatable="false">
+ <item>de:qwerty:AsciiCapable</item>
+ <item>fr:qwertz:AsciiCapable</item>
+ </string-array>
</resources>
diff --git a/java/res/values/whitelist.xml b/java/res/values/research_strings.xml
index d4ecbfaa4..2cad15eb0 100644
--- a/java/res/values/whitelist.xml
+++ b/java/res/values/research_strings.xml
@@ -2,7 +2,7 @@
<!--
/*
**
-** Copyright 2011, The Android Open Source Project
+** Copyright 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.
@@ -18,12 +18,7 @@
*/
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!--
- An entry of the whitelist word should be:
- 1. (int)frequency
- 2. (String)before
- 3. (String)after
- -->
- <string-array name="wordlist_whitelist">
- </string-array>
+ <!-- Contents of note explaining what data is collected and how. -->
+ <!-- TODO: remove translatable=false attribute once text is stable -->
+ <string name="research_splash_content" translatable="false"></string>
</resources>
diff --git a/java/res/xml-sw768dp-land/kbd_thai_symbols.xml b/java/res/values/strings-appname.xml
index 1531458ea..19aaa2513 100644
--- a/java/res/xml-sw768dp-land/kbd_thai_symbols.xml
+++ b/java/res/values/strings-appname.xml
@@ -18,12 +18,16 @@
*/
-->
-<Keyboard
- xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
- latin:rowHeight="20%p"
- latin:verticalGap="2.65%p"
- latin:touchPositionCorrectionData="@null"
->
- <include
- latin:keyboardLayout="@xml/rows_thai_symbols" />
-</Keyboard>
+<resources>
+ <!-- Title for Latin Keyboard -->
+ <string name="english_ime_name">Android keyboard</string>
+
+ <!-- Name of Android spell checker service -->
+ <string name="spell_checker_service_name">Android spell checker</string>
+
+ <!-- Title for Latin keyboard settings activity / dialog -->
+ <string name="english_ime_settings">Android keyboard settings</string>
+
+ <!-- Title for the spell checking service settings screen -->
+ <string name="android_spell_checker_settings">Spell checking settings</string>
+</resources>
diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml
index d51d3789a..13aca717f 100644
--- a/java/res/values/strings.xml
+++ b/java/res/values/strings.xml
@@ -18,23 +18,16 @@
*/
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- Title for Latin keyboard -->
- <string name="english_ime_name">Android keyboard</string>
<!-- Application name for opensource Android keyboard. AOSP(Android Open Source Project) should not be translated. -->
<string name="aosp_android_keyboard_ime_name">Android keyboard (AOSP)</string>
- <!-- Title for Latin keyboard settings activity / dialog -->
- <string name="english_ime_settings">Android keyboard settings</string>
<!-- Title for Latin keyboard input options dialog [CHAR LIMIT=25] -->
<string name="english_ime_input_options">Input options</string>
+ <!-- Title for Latin keyboard research log dialog, which contains special commands for users that contribute data for research. [CHAR LIMIT=33] -->
+ <string name="english_ime_research_log">Research Log Commands</string>
- <!-- Name of Android spell checker service -->
- <string name="spell_checker_service_name">Android spell checker</string>
<!-- Name of Android spell checker service. AOSP(Android Open Source Project) should not be translated. -->
<string name="aosp_spell_checker_service_name">Android spell checker (AOSP)</string>
- <!-- Title for the spell checking service settings screen -->
- <string name="android_spell_checker_settings">Spell checking settings</string>
-
<!-- Title for the spell checker option to turn on/off contact names lookup [CHAR LIMIT=25] -->
<string name="use_contacts_for_spellchecking_option_title">Look up contact names</string>
@@ -56,6 +49,9 @@
<!-- Category title for text prediction -->
<string name="correction_category">Text correction</string>
+ <!-- Category title for gesture typing -->
+ <string name="gesture_typing_category">Gesture typing</string>
+
<!-- Category title for misc options -->
<string name="misc_category">Other options</string>
@@ -68,8 +64,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>
@@ -83,13 +81,10 @@
<!-- Description for option enabling or disabling the use of names of people in Contacts for suggestion and correction [CHAR LIMIT=65] -->
<string name="use_contacts_dict_summary">Use names from Contacts for suggestions and corrections</string>
- <!-- Option name for enabling insertion of suggestion spans (advanced option) [CHAR LIMIT=25] -->
- <string name="enable_span_insert">Enable recorrections</string>
- <!-- Option summary for enabling insertion of suggestion spans (advanced option) [CHAR LIMIT=65] -->
- <string name="enable_span_insert_summary">Set suggestions for recorrections</string>
-
<!-- Option to enable auto capitalization of sentences -->
<string name="auto_cap">Auto-capitalization</string>
+ <!-- Description for option to enable auto capitalization of sentences -->
+ <string name="auto_cap_summary">Capitalize the first word of each sentence</string>
<!-- Option to configure dictionaries -->
<string name="configure_dictionaries_title">Add-on dictionaries</string>
@@ -101,12 +96,12 @@
<!-- Description for show suggestions -->
<string name="prefs_show_suggestions_summary">Display suggested words while typing</string>
<string name="prefs_suggestion_visibility_show_name">Always show</string>
- <string name="prefs_suggestion_visibility_show_only_portrait_name">Show on portrait mode</string>
+ <string name="prefs_suggestion_visibility_show_only_portrait_name">Show in portrait mode</string>
<string name="prefs_suggestion_visibility_hide_name">Always hide</string>
<!-- Option to decide the auto correction threshold score -->
<!-- Option to enable auto correction [CHAR LIMIT=20]-->
- <string name="auto_correction">Auto correction</string>
+ <string name="auto_correction">Auto-correction</string>
<!-- Description for auto correction [CHAR LIMIT=65 (two lines) or 30 (fits on one line, preferable)] -->
<string name="auto_correction_summary">Spacebar and punctuation automatically correct mistyped words</string>
<!-- Option to disable auto correction. [CHAR LIMIT=20] -->
@@ -118,14 +113,21 @@
<!-- Option to suggest auto correction suggestions very aggressively. Auto-corrects to a word which has even large edit distance from typed word. [CHAR LIMIT=20] -->
<string name="auto_correction_threshold_mode_very_aggeressive">Very aggressive</string>
- <!-- Option to enable next word correction -->
- <string name="bigram_suggestion">Next word suggestions</string>
- <!-- Option to enable next word suggestion. This uses the previous word in an attempt to improve the suggestions quality -->
- <string name="bigram_suggestion_summary">Use previous word to improve suggestions</string>
- <!-- Option to enable using next word prediction -->
- <string name="bigram_prediction">Next word prediction</string>
- <!-- Description for "next word prediction" option. This displays suggestions even when there is no input, based on the previous word. -->
- <string name="bigram_prediction_summary">Use previous word also for prediction</string>
+ <!-- Option to enable using next word suggestions. After the user types a space, with this option on, the keyboard will try to predict the next word. -->
+ <string name="bigram_prediction">Next-word suggestions</string>
+ <!-- Description for "next word suggestion" option. This displays suggestions even when there is no input, based on the previous word. -->
+ <string name="bigram_prediction_summary">Use the previous word in making suggestions</string>
+
+ <!-- Option to enable gesture input. The user can input a word by tracing the letters of a word without releasing the finger from the screen. [CHAR LIMIT=30]-->
+ <string name="gesture_input">Enable gesture typing</string>
+ <!-- Description for "gesture_input" option. The user can input a word by tracing the letters of a word without releasing the finger from the screen. [CHAR LIMIT=65]-->
+ <string name="gesture_input_summary">Input a word by sliding through the letters</string>
+ <!-- Option to enable gesture trail preview. The user can see a trail of the gesture during gesture input. [CHAR LIMIT=30]-->
+ <string name="gesture_preview_trail">Show gesture trail</string>
+ <!-- Option to enable gesture floating text preview. The user can see a suggested word floating under the moving finger during a gesture input. [CHAR LIMIT=30]-->
+ <string name="gesture_floating_preview_text">Dynamic floating preview</string>
+ <!-- Description for "gesture_floating_preview_text" option. The user can see a suggested word floating under the moving finger during a gesture input. [CHAR LIMIT=65]-->
+ <string name="gesture_floating_preview_text_summary">See the suggested word while gesturing</string>
<!-- Indicates that a word has been added to the dictionary -->
<string name="added_word"><xliff:g id="word">%s</xliff:g> : Saved</string>
@@ -192,6 +194,12 @@
<string name="spoken_description_search">Search</string>
<!-- Spoken description for the "U+2022" (BULLET) keyboard key. -->
<string name="spoken_description_dot">Dot</string>
+ <!-- Spoken description for the "Switch language" keyboard key. -->
+ <string name="spoken_description_language_switch">Switch language</string>
+ <!-- Spoken description for the "Next" action keyboard key. -->
+ <string name="spoken_description_action_next">Next</string>
+ <!-- Spoken description for the "Previous" action keyboard key. -->
+ <string name="spoken_description_action_previous">Previous</string>
<!-- Spoken feedback after turning "Shift" mode on. -->
<string name="spoken_description_shiftmode_on">Shift enabled</string>
@@ -233,6 +241,60 @@
<!-- Title for input language selection screen -->
<string name="language_selection_title">Input languages</string>
+ <!-- Title for dialog option to let users cancel logging and delete log for this session [CHAR LIMIT=35] -->
+ <!-- TODO: remove translatable=false attribute once text is stable -->
+ <string name="research_do_not_log_this_session" translatable="false">Suspend logging</string>
+ <!-- Title for dialog option to let users reenable logging [CHAR LIMIT=35] -->
+ <!-- TODO: remove translatable=false attribute once text is stable -->
+ <string name="research_enable_session_logging" translatable="false">Enable logging</string>
+ <!-- Toast notification that the system is processing the request to delete the log for this session [CHAR LIMIT=35] -->
+ <!-- TODO: remove translatable=false attribute once text is stable -->
+ <string name="research_notify_session_log_deleting" translatable="false">Deleting session log</string>
+ <!-- Toast notification that the system has successfully deleted the log for this session [CHAR LIMIT=35] -->
+ <!-- TODO: remove translatable=false attribute once text is stable -->
+ <string name="research_notify_logging_suspended" translatable="false">Logging temporarily suspended. To disable permanently, go to Android Keyboard Settings</string>
+ <!-- Toast notification that the system has failed to delete the log for this session [CHAR LIMIT=35] -->
+ <!-- TODO: remove translatable=false attribute once text is stable -->
+ <string name="research_notify_session_log_not_deleted" translatable="false">Session log NOT deleted</string>
+ <!-- Toast notification that the system is enabling logging [CHAR LIMIT=35] -->
+ <!-- TODO: remove translatable=false attribute once text is stable -->
+ <string name="research_notify_session_logging_enabled" translatable="false">Session logging enabled</string>
+
+ <!-- Menu option that lets user send feedback for research purposes about the IME [CHAR LIMIT=38] -->
+ <!-- TODO: remove translatable=false attribute once text is stable -->
+ <string name="research_feedback_menu_option" translatable="false">Send feedback</string>
+ <!-- Dialog box title that lets user send feedback for research purposes about the IME [CHAR LIMIT=38] -->
+ <!-- TODO: remove translatable=false attribute once text is stable -->
+ <string name="research_feedback_dialog_title" translatable="false">Send feedback</string>
+ <!-- Text for checkbox option to include user data in feedback for research purposes [CHAR LIMIT=50] -->
+ <!-- TODO: remove translatable=false attribute once text is stable -->
+ <!-- TODO: handle multilingual plurals -->
+ <string name="research_feedback_include_history_label" translatable="false">Include last <xliff:g id="word">%d</xliff:g> words entered</string>
+ <!-- Hint to user about the text entry field where they should enter research feedback [CHAR LIMIT=40] -->
+ <!-- TODO: remove translatable=false attribute once text is stable -->
+ <string name="research_feedback_hint" translatable="false">Enter your feedback here.</string>
+ <!-- Dialog button choice to send research feedback [CHAR LIMIT=35] -->
+ <!-- TODO: remove translatable=false attribute once text is stable -->
+ <string name="research_feedback_send" translatable="false">Send</string>
+ <!-- Dialog button choice to cancel sending research feedback [CHAR LIMIT=35] -->
+ <!-- TODO: remove translatable=false attribute once text is stable -->
+ <string name="research_feedback_cancel" translatable="false">Cancel</string>
+ <!-- Toast notification to ask user to quit the research feedback dialog to perform this operation [CHAR LIMIT=100] -->
+ <!-- TODO: remove translatable=false attribute once text is stable -->
+ <string name="research_please_exit_feedback_form" translatable="false">Please exit the feedback dialog to access the research log menu</string>
+
+ <!-- Title of dialog shown at start informing users about contributing research usage data-->
+ <!-- TODO: remove translatable=false attribute once text is stable -->
+ <string name="research_splash_title" translatable="false">Warning</string>
+
+ <!-- Toast message informing users that logging has been disabled -->
+ <!-- TODO: remove translatable=false attribute once text is stable -->
+ <string name="research_logging_disabled" translatable="false">Logging Disabled</string>
+
+ <!-- Name for the research uploading service to be displayed to users. [CHAR LIMIT=50] -->
+ <!-- TODO: remove translatable=false attribute once text is stable -->
+ <string name="research_log_uploader_name" translatable="false">Research Uploader Service</string>
+
<!-- Preference for input language selection -->
<string name="select_language">Input languages</string>
@@ -260,6 +322,15 @@
<!-- Description for English (United States) keyboard subtype with explicit keyboard layout [CHAR LIMIT=25]
This should be identical to subtype_en_US aside from the trailing (%s). -->
<string name="subtype_with_layout_en_US">English (US) (<xliff:g id="layout">%s</xliff:g>)</string>
+ <!-- TODO: Uncomment once we can handle IETF language tag with script name specified.
+ Description for Serbian Cyrillic keyboard subtype [CHAR LIMIT=25]
+ <string name="subtype_serbian_cyrillic">Serbian (Cyrillic)</string>
+ Description for Serbian Latin keyboard subtype [CHAR LIMIT=25]
+ <string name="subtype_serbian_latin">Serbian (Latin)</string>
+ Description for Serbian Latin keyboard subtype with explicit keyboard layout [CHAR LIMIT=25]
+ This should be identical to subtype_serbian_latin aside from the trailing (%s).
+ <string name="subtype_with_layout_sr-Latn">Serbian (Latin) (<xliff:g id="layout">%s</xliff:g>)</string>
+ -->
<!-- Description for language agnostic keyboard subtype [CHAR LIMIT=25] -->
<string name="subtype_no_language">No language</string>
<!-- Description for language agnostic QWERTY keyboard subtype [CHAR LIMIT=25] -->
@@ -289,7 +360,7 @@
<string name="subtype_locale">Language</string>
<!-- Title of the spinner for choosing a keyboard layout of custom style in the settings dialog [CHAR LIMIT=15] -->
<string name="keyboard_layout_set">Layout</string>
- <!-- The message of the dialog to note that a custom input style needs to be enabled. [CHAR LIMIT=64] -->
+ <!-- The message of the dialog to note that a custom input style needs to be enabled. [CHAR LIMIT=130] -->
<string name="custom_input_style_note_message">"Your custom input style needs to be enabled before you start using it. Do you want to enable it now?"</string>
<!-- Title of the button to enable a custom input style entry in the settings dialog [CHAR LIMIT=15] -->
<string name="enable">Enable</string>
diff --git a/java/res/values/styles.xml b/java/res/values/styles.xml
index e9b0470ea..dbb56ab4d 100644
--- a/java/res/values/styles.xml
+++ b/java/res/values/styles.xml
@@ -14,12 +14,12 @@
limitations under the License.
-->
-<resources>
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Theme "Basic" -->
<style name="Keyboard">
<!-- This should be aligned with KeyboardSwitcher.KEYBOARD_THEMES[] -->
<item name="themeId">0</item>
- <item name="touchPositionCorrectionData">@array/touch_position_correction_data_empty</item>
+ <item name="touchPositionCorrectionData">@array/touch_position_correction_data_default</item>
<item name="rowHeight">25%p</item>
<item name="keyboardHeight">@dimen/keyboardHeight</item>
<item name="maxKeyboardHeight">@fraction/maxKeyboardHeight</item>
@@ -35,14 +35,14 @@
<style name="KeyboardView">
<item name="android:background">@drawable/keyboard_background</item>
<item name="keyBackground">@drawable/btn_keyboard_key</item>
- <item name="keyLetterRatio">@fraction/key_letter_ratio</item>
+ <item name="keyLetterSize">@fraction/key_letter_ratio</item>
<item name="keyLargeLetterRatio">@fraction/key_large_letter_ratio</item>
- <item name="keyLabelRatio">@fraction/key_label_ratio</item>
+ <item name="keyLabelSize">@fraction/key_label_ratio</item>
<item name="keyLargeLabelRatio">@fraction/key_large_label_ratio</item>
<item name="keyHintLetterRatio">@fraction/key_hint_letter_ratio</item>
<item name="keyHintLabelRatio">@fraction/key_hint_label_ratio</item>
<item name="keyShiftedLetterHintRatio">@fraction/key_uppercase_letter_ratio</item>
- <item name="keyTextStyle">normal</item>
+ <item name="keyTypeface">normal</item>
<item name="keyTextColor">#FFFFFFFF</item>
<item name="keyTextInactivatedColor">#FFFFFFFF</item>
<item name="keyHintLetterColor">#80000000</item>
@@ -54,9 +54,6 @@
<item name="keyPopupHintLetterPadding">@dimen/key_popup_hint_letter_padding</item>
<item name="keyShiftedLetterHintPadding">@dimen/key_uppercase_letter_padding</item>
<item name="keyPreviewLayout">@layout/key_preview</item>
- <item name="keyPreviewBackground">@drawable/keyboard_key_feedback</item>
- <item name="keyPreviewLeftBackground">@null</item>
- <item name="keyPreviewRightBackground">@null</item>
<item name="keyPreviewTextColor">#FFFFFFFF</item>
<item name="keyPreviewOffset">@dimen/key_preview_offset</item>
<item name="keyPreviewHeight">@dimen/key_preview_height</item>
@@ -64,11 +61,27 @@
<item name="keyPreviewLingerTimeout">@integer/config_key_preview_linger_timeout</item>
<item name="moreKeysLayout">@layout/more_keys_keyboard</item>
<item name="verticalCorrection">@dimen/keyboard_vertical_correction</item>
- <item name="shadowColor">#BB000000</item>
- <item name="shadowRadius">2.75</item>
+ <item name="keyTextShadowColor">#BB000000</item>
+ <item name="keyTextShadowRadius">2.75</item>
<item name="backgroundDimAlpha">128</item>
- <!-- Common attributes of LatinKeyboardView -->
+ <!-- android:color/holo_blue_light=#FF33B5E5 -->
+ <item name="gestureFloatingPreviewTextSize">@dimen/gesture_floating_preview_text_size</item>
+ <item name="gestureFloatingPreviewTextColor">@android:color/holo_blue_light</item>
+ <item name="gestureFloatingPreviewTextOffset">@dimen/gesture_floating_preview_text_offset</item>
+ <item name="gestureFloatingPreviewColor">#C0000000</item>
+ <item name="gestureFloatingPreviewHorizontalPadding">@dimen/gesture_floating_preview_horizontal_padding</item>
+ <item name="gestureFloatingPreviewVerticalPadding">@dimen/gesture_floating_preview_vertical_padding</item>
+ <item name="gestureFloatingPreviewRoundRadius">@dimen/gesture_floating_preview_round_radius</item>
+ <item name="gestureFloatingPreviewTextLingerTimeout">@integer/config_gesture_floating_preview_text_linger_timeout</item>
+ <item name="gesturePreviewTrailFadeoutStartDelay">@integer/config_gesture_preview_trail_fadeout_start_delay</item>
+ <item name="gesturePreviewTrailFadeoutDuration">@integer/config_gesture_preview_trail_fadeout_duration</item>
+ <item name="gesturePreviewTrailUpdateInterval">@integer/config_gesture_preview_trail_update_interval</item>
+ <item name="gesturePreviewTrailColor">@android:color/holo_blue_light</item>
+ <item name="gesturePreviewTrailStartWidth">@dimen/gesture_preview_trail_start_width</item>
+ <item name="gesturePreviewTrailEndWidth">@dimen/gesture_preview_trail_end_width</item>
+ <!-- Common attributes of MainKeyboardView -->
<item name="keyHysteresisDistance">@dimen/config_key_hysteresis_distance</item>
+ <item name="keyHysteresisDistanceForSlidingModifier">@dimen/config_key_hysteresis_distance_for_sliding_modifier</item>
<item name="touchNoiseThresholdTime">@integer/config_touch_noise_threshold_time</item>
<item name="touchNoiseThresholdDistance">@dimen/config_touch_noise_threshold_distance</item>
<item name="slidingKeyInputEnable">@bool/config_sliding_key_input_enabled</item>
@@ -82,9 +95,21 @@
<item name="languageOnSpacebarFadeoutAnimator">@anim/language_on_spacebar_fadeout</item>
<item name="altCodeKeyWhileTypingFadeoutAnimator">@anim/alt_code_key_while_typing_fadeout</item>
<item name="altCodeKeyWhileTypingFadeinAnimator">@anim/alt_code_key_while_typing_fadein</item>
+ <!-- Common attributes of MainKeyboardView for gesture typing detection and recognition -->
+ <item name="gestureStaticTimeThresholdAfterFastTyping">@integer/config_gesture_static_time_threshold_after_fast_typing</item>
+ <item name="gestureDetectFastMoveSpeedThreshold">@fraction/config_gesture_detect_fast_move_speed_threshold</item>
+ <item name="gestureDynamicThresholdDecayDuration">@integer/config_gesture_dynamic_threshold_decay_duration</item>
+ <item name="gestureDynamicTimeThresholdFrom">@integer/config_gesture_dynamic_time_threshold_from</item>
+ <item name="gestureDynamicTimeThresholdTo">@integer/config_gesture_dynamic_time_threshold_to</item>
+ <item name="gestureDynamicDistanceThresholdFrom">@fraction/config_gesture_dynamic_distance_threshold_from</item>
+ <item name="gestureDynamicDistanceThresholdTo">@fraction/config_gesture_dynamic_distance_threshold_to</item>
+ <item name="gestureSamplingMinimumDistance">@fraction/config_gesture_sampling_minimum_distance</item>
+ <item name="gestureRecognitionMinimumTime">@integer/config_gesture_recognition_minimum_time</item>
+ <item name="gestureRecognitionSpeedThreshold">@fraction/config_gesture_recognition_speed_threshold</item>
+ <item name="suppressKeyPreviewAfterBatchInputDuration">@integer/config_suppress_key_preview_after_batch_input_duration</item>
</style>
<style
- name="LatinKeyboardView"
+ name="MainKeyboardView"
parent="KeyboardView">
<item name="autoCorrectionSpacebarLedEnabled">true</item>
<item name="autoCorrectionSpacebarLedIcon">@drawable/sym_keyboard_space_led</item>
@@ -114,7 +139,7 @@
<item name="android:background">@drawable/keyboard_suggest_strip</item>
</style>
<style
- name="SuggestionsViewStyle"
+ name="SuggestionStripViewStyle"
parent="SuggestionsStripBackgroundStyle"
>
<item name="suggestionStripOption">autoCorrectBold|validTypedWordBold</item>
@@ -122,9 +147,9 @@
<item name="colorTypedWord">@android:color/white</item>
<item name="colorAutoCorrect">#FFFCAE00</item>
<item name="colorSuggested">#FFFCAE00</item>
- <item name="alphaObsoleted">50</item>
+ <item name="alphaObsoleted">50%</item>
<item name="suggestionsCountInStrip">@integer/suggestions_count_in_strip</item>
- <item name="centerSuggestionPercentile">@integer/center_suggestion_percentile</item>
+ <item name="centerSuggestionPercentile">@fraction/center_suggestion_percentile</item>
<item name="maxMoreSuggestionsRow">@integer/max_more_suggestions_row</item>
<item name="minMoreSuggestionsWidth">@fraction/min_more_suggestions_width</item>
</style>
@@ -155,7 +180,7 @@
<item name="keyBackground">@drawable/btn_keyboard_key3</item>
</style>
<style
- name="LatinKeyboardView.HighContrast"
+ name="MainKeyboardView.HighContrast"
parent="KeyboardView.HighContrast"
>
<item name="autoCorrectionSpacebarLedEnabled">true</item>
@@ -187,10 +212,10 @@
<item name="keyHintLabelColor">#E0000000</item>
<item name="keyShiftedLetterHintInactivatedColor">#66000000</item>
<item name="keyShiftedLetterHintActivatedColor">#CC000000</item>
- <item name="shadowColor">#FFFFFFFF</item>
+ <item name="keyTextShadowColor">#FFFFFFFF</item>
</style>
<style
- name="LatinKeyboardView.Stone"
+ name="MainKeyboardView.Stone"
parent="KeyboardView.Stone"
>
<item name="autoCorrectionSpacebarLedEnabled">true</item>
@@ -213,7 +238,7 @@
>
<item name="keyBackground">@drawable/btn_keyboard_key_stone</item>
<item name="keyTextColor">#FF000000</item>
- <item name="shadowColor">#FFFFFFFF</item>
+ <item name="keyTextShadowColor">#FFFFFFFF</item>
</style>
<!-- Theme "Stone bold" -->
<style
@@ -227,10 +252,10 @@
name="KeyboardView.Stone.Bold"
parent="KeyboardView.Stone"
>
- <item name="keyTextStyle">bold</item>
+ <item name="keyTypeface">bold</item>
</style>
<style
- name="LatinKeyboardView.Stone.Bold"
+ name="MainKeyboardView.Stone.Bold"
parent="KeyboardView.Stone.Bold"
>
<item name="autoCorrectionSpacebarLedEnabled">true</item>
@@ -256,10 +281,10 @@
>
<item name="android:background">@drawable/keyboard_dark_background</item>
<item name="keyBackground">@drawable/btn_keyboard_key_gingerbread</item>
- <item name="keyTextStyle">bold</item>
+ <item name="keyTypeface">bold</item>
</style>
<style
- name="LatinKeyboardView.Gingerbread"
+ name="MainKeyboardView.Gingerbread"
parent="KeyboardView.Gingerbread"
>
<item name="autoCorrectionSpacebarLedEnabled">true</item>
@@ -301,22 +326,20 @@
>
<item name="android:background">@drawable/keyboard_background_holo</item>
<item name="keyBackground">@drawable/btn_keyboard_key_ics</item>
- <item name="keyTextStyle">bold</item>
+ <item name="keyTypeface">bold</item>
<item name="keyTextInactivatedColor">#66E0E4E5</item>
<item name="keyHintLetterColor">#80000000</item>
<item name="keyHintLabelColor">#A0FFFFFF</item>
<item name="keyShiftedLetterHintInactivatedColor">#66E0E4E5</item>
<item name="keyShiftedLetterHintActivatedColor">#FFFFFFFF</item>
- <item name="keyPreviewBackground">@drawable/keyboard_key_feedback_ics</item>
- <item name="keyPreviewLeftBackground">@drawable/keyboard_key_feedback_left_ics</item>
- <item name="keyPreviewRightBackground">@drawable/keyboard_key_feedback_right_ics</item>
+ <item name="keyPreviewLayout">@layout/key_preview_ics</item>
<item name="keyPreviewTextColor">#FFFFFFFF</item>
<item name="keyPreviewOffset">@dimen/key_preview_offset_ics</item>
- <item name="shadowColor">#00000000</item>
- <item name="shadowRadius">0.0</item>
+ <item name="keyTextShadowColor">#00000000</item>
+ <item name="keyTextShadowRadius">0.0</item>
</style>
<style
- name="LatinKeyboardView.IceCreamSandwich"
+ name="MainKeyboardView.IceCreamSandwich"
parent="KeyboardView.IceCreamSandwich"
>
<item name="autoCorrectionSpacebarLedEnabled">false</item>
@@ -348,7 +371,7 @@
<item name="android:background">@drawable/keyboard_suggest_strip_holo</item>
</style>
<style
- name="SuggestionsViewStyle.IceCreamSandwich"
+ name="SuggestionStripViewStyle.IceCreamSandwich"
parent="SuggestionsStripBackgroundStyle.IceCreamSandwich"
>
<item name="suggestionStripOption">autoCorrectBold|validTypedWordBold</item>
@@ -357,12 +380,12 @@
<item name="colorTypedWord">@android:color/holo_blue_light</item>
<item name="colorAutoCorrect">@android:color/holo_blue_light</item>
<item name="colorSuggested">@android:color/holo_blue_light</item>
- <item name="alphaValidTypedWord">85</item>
- <item name="alphaTypedWord">85</item>
- <item name="alphaSuggested">70</item>
- <item name="alphaObsoleted">70</item>
+ <item name="alphaValidTypedWord">85%</item>
+ <item name="alphaTypedWord">85%</item>
+ <item name="alphaSuggested">70%</item>
+ <item name="alphaObsoleted">70%</item>
<item name="suggestionsCountInStrip">@integer/suggestions_count_in_strip</item>
- <item name="centerSuggestionPercentile">@integer/center_suggestion_percentile</item>
+ <item name="centerSuggestionPercentile">@fraction/center_suggestion_percentile</item>
<item name="maxMoreSuggestionsRow">@integer/max_more_suggestions_row</item>
<item name="minMoreSuggestionsWidth">@fraction/min_more_suggestions_width</item>
</style>
diff --git a/java/res/values/themes-basic-highcontrast.xml b/java/res/values/themes-basic-highcontrast.xml
index 19df42ce1..b3ea05045 100644
--- a/java/res/values/themes-basic-highcontrast.xml
+++ b/java/res/values/themes-basic-highcontrast.xml
@@ -18,12 +18,12 @@
<style name="KeyboardTheme.HighContrast" parent="KeyboardIcons">
<item name="keyboardStyle">@style/Keyboard.HighContrast</item>
<item name="keyboardViewStyle">@style/KeyboardView.HighContrast</item>
- <item name="latinKeyboardViewStyle">@style/LatinKeyboardView.HighContrast</item>
+ <item name="mainKeyboardViewStyle">@style/MainKeyboardView.HighContrast</item>
<item name="moreKeysKeyboardStyle">@style/MoreKeysKeyboard</item>
<item name="moreKeysKeyboardViewStyle">@style/MoreKeysKeyboardView</item>
<item name="moreKeysKeyboardPanelStyle">@style/MoreKeysKeyboardPanelStyle</item>
<item name="suggestionsStripBackgroundStyle">@style/SuggestionsStripBackgroundStyle</item>
- <item name="suggestionsViewStyle">@style/SuggestionsViewStyle</item>
+ <item name="suggestionStripViewStyle">@style/SuggestionStripViewStyle</item>
<item name="moreSuggestionsViewStyle">@style/MoreSuggestionsViewStyle</item>
<item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle</item>
<item name="suggestionPreviewBackgroundStyle">@style/SuggestionPreviewBackgroundStyle</item>
diff --git a/java/res/values/themes-basic.xml b/java/res/values/themes-basic.xml
index 5d477206d..ff6a70a08 100644
--- a/java/res/values/themes-basic.xml
+++ b/java/res/values/themes-basic.xml
@@ -18,12 +18,12 @@
<style name="KeyboardTheme" parent="KeyboardIcons">
<item name="keyboardStyle">@style/Keyboard</item>
<item name="keyboardViewStyle">@style/KeyboardView</item>
- <item name="latinKeyboardViewStyle">@style/LatinKeyboardView</item>
+ <item name="mainKeyboardViewStyle">@style/MainKeyboardView</item>
<item name="moreKeysKeyboardStyle">@style/MoreKeysKeyboard</item>
<item name="moreKeysKeyboardViewStyle">@style/MoreKeysKeyboardView</item>
<item name="moreKeysKeyboardPanelStyle">@style/MoreKeysKeyboardPanelStyle</item>
<item name="suggestionsStripBackgroundStyle">@style/SuggestionsStripBackgroundStyle</item>
- <item name="suggestionsViewStyle">@style/SuggestionsViewStyle</item>
+ <item name="suggestionStripViewStyle">@style/SuggestionStripViewStyle</item>
<item name="moreSuggestionsViewStyle">@style/MoreSuggestionsViewStyle</item>
<item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle</item>
<item name="suggestionPreviewBackgroundStyle">@style/SuggestionPreviewBackgroundStyle</item>
diff --git a/java/res/values/themes-gingerbread.xml b/java/res/values/themes-gingerbread.xml
index a13979818..0ce0b8a9b 100644
--- a/java/res/values/themes-gingerbread.xml
+++ b/java/res/values/themes-gingerbread.xml
@@ -18,12 +18,12 @@
<style name="KeyboardTheme.Gingerbread" parent="KeyboardIcons">
<item name="keyboardStyle">@style/Keyboard.Gingerbread</item>
<item name="keyboardViewStyle">@style/KeyboardView.Gingerbread</item>
- <item name="latinKeyboardViewStyle">@style/LatinKeyboardView.Gingerbread</item>
+ <item name="mainKeyboardViewStyle">@style/MainKeyboardView.Gingerbread</item>
<item name="moreKeysKeyboardStyle">@style/MoreKeysKeyboard.Gingerbread</item>
<item name="moreKeysKeyboardViewStyle">@style/MoreKeysKeyboardView.Gingerbread</item>
<item name="moreKeysKeyboardPanelStyle">@style/MoreKeysKeyboardPanelStyle</item>
<item name="suggestionsStripBackgroundStyle">@style/SuggestionsStripBackgroundStyle</item>
- <item name="suggestionsViewStyle">@style/SuggestionsViewStyle</item>
+ <item name="suggestionStripViewStyle">@style/SuggestionStripViewStyle</item>
<item name="moreSuggestionsViewStyle">@style/MoreSuggestionsViewStyle</item>
<item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle</item>
<item name="suggestionPreviewBackgroundStyle">@style/SuggestionPreviewBackgroundStyle</item>
diff --git a/java/res/values/themes-ics.xml b/java/res/values/themes-ics.xml
index e6fd4f451..8df58c594 100644
--- a/java/res/values/themes-ics.xml
+++ b/java/res/values/themes-ics.xml
@@ -18,12 +18,12 @@
<style name="KeyboardTheme.IceCreamSandwich" parent="KeyboardIcons.IceCreamSandwich">
<item name="keyboardStyle">@style/Keyboard.IceCreamSandwich</item>
<item name="keyboardViewStyle">@style/KeyboardView.IceCreamSandwich</item>
- <item name="latinKeyboardViewStyle">@style/LatinKeyboardView.IceCreamSandwich</item>
+ <item name="mainKeyboardViewStyle">@style/MainKeyboardView.IceCreamSandwich</item>
<item name="moreKeysKeyboardStyle">@style/MoreKeysKeyboard.IceCreamSandwich</item>
<item name="moreKeysKeyboardViewStyle">@style/MoreKeysKeyboardView.IceCreamSandwich</item>
<item name="moreKeysKeyboardPanelStyle">@style/MoreKeysKeyboardPanelStyle.IceCreamSandwich</item>
<item name="suggestionsStripBackgroundStyle">@style/SuggestionsStripBackgroundStyle.IceCreamSandwich</item>
- <item name="suggestionsViewStyle">@style/SuggestionsViewStyle.IceCreamSandwich</item>
+ <item name="suggestionStripViewStyle">@style/SuggestionStripViewStyle.IceCreamSandwich</item>
<item name="moreSuggestionsViewStyle">@style/MoreSuggestionsViewStyle.IceCreamSandwich</item>
<item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle.IceCreamSandwich</item>
<item name="suggestionPreviewBackgroundStyle">@style/SuggestionPreviewBackgroundStyle.IceCreamSandwich</item>
diff --git a/java/res/values/themes-stone-bold.xml b/java/res/values/themes-stone-bold.xml
index 47de99e47..355a97f7b 100644
--- a/java/res/values/themes-stone-bold.xml
+++ b/java/res/values/themes-stone-bold.xml
@@ -18,12 +18,12 @@
<style name="KeyboardTheme.Stone.Bold" parent="KeyboardIcons.Black">
<item name="keyboardStyle">@style/Keyboard.Stone.Bold</item>
<item name="keyboardViewStyle">@style/KeyboardView.Stone.Bold</item>
- <item name="latinKeyboardViewStyle">@style/LatinKeyboardView.Stone.Bold</item>
+ <item name="mainKeyboardViewStyle">@style/MainKeyboardView.Stone.Bold</item>
<item name="moreKeysKeyboardStyle">@style/MoreKeysKeyboard.Stone</item>
<item name="moreKeysKeyboardViewStyle">@style/MoreKeysKeyboardView.Stone</item>
<item name="moreKeysKeyboardPanelStyle">@style/MoreKeysKeyboardPanelStyle</item>
<item name="suggestionsStripBackgroundStyle">@style/SuggestionsStripBackgroundStyle</item>
- <item name="suggestionsViewStyle">@style/SuggestionsViewStyle</item>
+ <item name="suggestionStripViewStyle">@style/SuggestionStripViewStyle</item>
<item name="moreSuggestionsViewStyle">@style/MoreSuggestionsViewStyle</item>
<item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle</item>
<item name="suggestionPreviewBackgroundStyle">@style/SuggestionPreviewBackgroundStyle</item>
diff --git a/java/res/values/themes-stone.xml b/java/res/values/themes-stone.xml
index a0b39e3e6..23437f780 100644
--- a/java/res/values/themes-stone.xml
+++ b/java/res/values/themes-stone.xml
@@ -18,12 +18,12 @@
<style name="KeyboardTheme.Stone" parent="KeyboardIcons.Black">
<item name="keyboardStyle">@style/Keyboard.Stone</item>
<item name="keyboardViewStyle">@style/KeyboardView.Stone</item>
- <item name="latinKeyboardViewStyle">@style/LatinKeyboardView.Stone</item>
+ <item name="mainKeyboardViewStyle">@style/MainKeyboardView.Stone</item>
<item name="moreKeysKeyboardStyle">@style/MoreKeysKeyboard.Stone</item>
<item name="moreKeysKeyboardViewStyle">@style/MoreKeysKeyboardView.Stone</item>
<item name="moreKeysKeyboardPanelStyle">@style/MoreKeysKeyboardPanelStyle</item>
<item name="suggestionsStripBackgroundStyle">@style/SuggestionsStripBackgroundStyle</item>
- <item name="suggestionsViewStyle">@style/SuggestionsViewStyle</item>
+ <item name="suggestionStripViewStyle">@style/SuggestionStripViewStyle</item>
<item name="moreSuggestionsViewStyle">@style/MoreSuggestionsViewStyle</item>
<item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle</item>
<item name="suggestionPreviewBackgroundStyle">@style/SuggestionPreviewBackgroundStyle</item>
diff --git a/java/res/values/touch-position-correction.xml b/java/res/values/touch-position-correction.xml
index 41b435ad0..7df86f467 100644
--- a/java/res/values/touch-position-correction.xml
+++ b/java/res/values/touch-position-correction.xml
@@ -18,18 +18,22 @@
*/
-->
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!--
+ <!-- Note that correctionX is obsolete (See com.android.inputmethod.keyboard.internal.TouchPositionCorrection)
An entry of the touch_position_correction word should be:
- 1. (float) (touch_center_x - key_center_x) / key_width
- 2. (float) (touch_center_y - key_center_y) / key_height
- 3. (float) sweet_spot_radius / (key_width^2 + key_height^2)
- -->
+ 1. correctionX: (touch_center_x - hitbox_center_x) / hitbox_width
+ 2. correctionY: (touch_center_y - hitbox_center_y) / hitbox_height
+ 3. correctionR: sweet_spot_radius / sqrt(hitbox_width^2 + hitbox_height^2)
+ -->
<string-array
- name="touch_position_correction_data_empty"
+ name="touch_position_correction_data_default"
translatable="false"
>
- <!-- empty -->
+ <!-- The default touch position data (See com.android.inputmethod.keyboard.ProximityInfo)
+ correctionX = 0.0f
+ correctionY = 0.0f
+ correctionR = DEFAULT_TOUCH_POSITION_CORRECTION_RADIUS
+ -->
</string-array>
<string-array
diff --git a/java/res/values-it/donottranslate.xml b/java/res/values/urls.xml
index 58e94361b..a8e9ad7d3 100644
--- a/java/res/values-it/donottranslate.xml
+++ b/java/res/values/urls.xml
@@ -2,7 +2,7 @@
<!--
/*
**
-** Copyright 2009, The Android Open Source Project
+** Copyright 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.
@@ -17,7 +17,6 @@
** limitations under the License.
*/
-->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <!-- Symbols that do NOT separate words -->
- <string name="symbols_excluded_from_word_separators"></string>
+<resources>
+ <string name="research_logger_upload_url" translatable="false"></string>
</resources>
diff --git a/java/res/xml-land/kbd_number.xml b/java/res/xml-land/kbd_number.xml
index 7cc0fb274..8d31df1f8 100644
--- a/java/res/xml-land/kbd_number.xml
+++ b/java/res/xml-land/kbd_number.xml
@@ -22,6 +22,7 @@
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
latin:keyboardHorizontalEdgesPadding="10%p"
latin:keyWidth="26.67%p"
+ latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
>
<include
latin:keyboardLayout="@xml/rows_number" />
diff --git a/java/res/xml-land/kbd_phone.xml b/java/res/xml-land/kbd_phone.xml
index aa54b8390..2f8fc3560 100644
--- a/java/res/xml-land/kbd_phone.xml
+++ b/java/res/xml-land/kbd_phone.xml
@@ -22,6 +22,7 @@
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
latin:keyboardHorizontalEdgesPadding="10%p"
latin:keyWidth="26.67%p"
+ latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
>
<include
latin:keyboardLayout="@xml/rows_phone" />
diff --git a/java/res/xml-land/kbd_phone_symbols.xml b/java/res/xml-land/kbd_phone_symbols.xml
index 41ba6cf3b..0e6bcdd6a 100644
--- a/java/res/xml-land/kbd_phone_symbols.xml
+++ b/java/res/xml-land/kbd_phone_symbols.xml
@@ -22,6 +22,7 @@
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
latin:keyboardHorizontalEdgesPadding="10%p"
latin:keyWidth="26.67%p"
+ latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
>
<include
latin:keyboardLayout="@xml/rows_phone_symbols" />
diff --git a/java/res/xml-sw600dp-land/kbd_number.xml b/java/res/xml-sw600dp-land/kbd_number.xml
index cb86b3b2f..63dfc90d0 100644
--- a/java/res/xml-sw600dp-land/kbd_number.xml
+++ b/java/res/xml-sw600dp-land/kbd_number.xml
@@ -22,6 +22,7 @@
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
latin:keyboardHorizontalEdgesPadding="10%p"
latin:keyWidth="18%p"
+ latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
>
<include
latin:keyboardLayout="@xml/rows_number" />
diff --git a/java/res/xml-sw600dp-land/kbd_phone.xml b/java/res/xml-sw600dp-land/kbd_phone.xml
index 71c7c04a1..b6161111b 100644
--- a/java/res/xml-sw600dp-land/kbd_phone.xml
+++ b/java/res/xml-sw600dp-land/kbd_phone.xml
@@ -22,6 +22,7 @@
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
latin:keyboardHorizontalEdgesPadding="10%p"
latin:keyWidth="18%p"
+ latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
>
<include
latin:keyboardLayout="@xml/rows_phone" />
diff --git a/java/res/xml-sw600dp-land/kbd_phone_symbols.xml b/java/res/xml-sw600dp-land/kbd_phone_symbols.xml
index 39bdae3c7..9b0bee026 100644
--- a/java/res/xml-sw600dp-land/kbd_phone_symbols.xml
+++ b/java/res/xml-sw600dp-land/kbd_phone_symbols.xml
@@ -22,6 +22,7 @@
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
latin:keyboardHorizontalEdgesPadding="10%p"
latin:keyWidth="18%p"
+ latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
>
<!-- Tablet doesn't have phone symbols keyboard -->
<include
diff --git a/java/res/xml-sw600dp/kbd_10_10_7_symbols.xml b/java/res/xml-sw600dp/kbd_10_10_7_symbols.xml
index 66254dea0..dd545b5ef 100644
--- a/java/res/xml-sw600dp/kbd_10_10_7_symbols.xml
+++ b/java/res/xml-sw600dp/kbd_10_10_7_symbols.xml
@@ -20,6 +20,7 @@
<Keyboard
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+ latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
>
<include
latin:keyboardLayout="@xml/rows_10_10_7_symbols" />
diff --git a/java/res/xml-sw600dp/kbd_10_10_7_symbols_shift.xml b/java/res/xml-sw600dp/kbd_10_10_7_symbols_shift.xml
index 3c5ed5e09..c36f0097e 100644
--- a/java/res/xml-sw600dp/kbd_10_10_7_symbols_shift.xml
+++ b/java/res/xml-sw600dp/kbd_10_10_7_symbols_shift.xml
@@ -20,6 +20,7 @@
<Keyboard
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+ latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
>
<include
latin:keyboardLayout="@xml/rows_10_10_7_symbols_shift" />
diff --git a/java/res/xml-sw600dp/kbd_number.xml b/java/res/xml-sw600dp/kbd_number.xml
index 4a8b08c2a..71d662267 100644
--- a/java/res/xml-sw600dp/kbd_number.xml
+++ b/java/res/xml-sw600dp/kbd_number.xml
@@ -21,6 +21,7 @@
<Keyboard
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
latin:keyWidth="18%p"
+ latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
>
<include
latin:keyboardLayout="@xml/rows_number" />
diff --git a/java/res/xml-sw600dp/kbd_phone.xml b/java/res/xml-sw600dp/kbd_phone.xml
index f63f1c648..5fdbea27b 100644
--- a/java/res/xml-sw600dp/kbd_phone.xml
+++ b/java/res/xml-sw600dp/kbd_phone.xml
@@ -21,6 +21,7 @@
<Keyboard
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
latin:keyWidth="18%p"
+ latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
>
<include
latin:keyboardLayout="@xml/rows_phone" />
diff --git a/java/res/xml-sw600dp/kbd_phone_symbols.xml b/java/res/xml-sw600dp/kbd_phone_symbols.xml
index a0f55b732..ce24d2b39 100644
--- a/java/res/xml-sw600dp/kbd_phone_symbols.xml
+++ b/java/res/xml-sw600dp/kbd_phone_symbols.xml
@@ -21,6 +21,7 @@
<Keyboard
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
latin:keyWidth="18%p"
+ latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
>
<!-- Tablet doesn't have phone symbols keyboard -->
<include
diff --git a/java/res/xml-sw600dp/key_styles_common.xml b/java/res/xml-sw600dp/key_styles_common.xml
index a1b2eb475..bf2e76a6b 100644
--- a/java/res/xml-sw600dp/key_styles_common.xml
+++ b/java/res/xml-sw600dp/key_styles_common.xml
@@ -133,6 +133,17 @@
latin:keyIconPreview="!icon/tab_key_preview"
latin:backgroundType="functional" />
</case>
+ <case
+ latin:keyboardLayoutSetElement="alphabet|alphabetAutomaticShifted|alphabetShiftLocked"
+ latin:navigateNext="true"
+ >
+ <key-style
+ latin:styleName="tabKeyStyle"
+ latin:code="!code/key_action_next"
+ latin:keyIcon="!icon/tab_key"
+ latin:keyIconPreview="!icon/tab_key_preview"
+ latin:backgroundType="functional" />
+ </case>
<default>
<key-style
latin:styleName="tabKeyStyle"
diff --git a/java/res/xml-sw600dp/row_symbols4.xml b/java/res/xml-sw600dp/row_symbols4.xml
index 73a5b1703..f138d8ef4 100644
--- a/java/res/xml-sw600dp/row_symbols4.xml
+++ b/java/res/xml-sw600dp/row_symbols4.xml
@@ -41,6 +41,8 @@
latin:moreKeys="!text/more_keys_for_tablet_double_quote" />
<Key
latin:keyLabel="_" />
- <!-- Here is empty space. -->
+ <!-- Note: This Spacer prevents the above key from being marked as a right edge key. -->
+ <Spacer
+ latin:keyWidth="fillRight" />
</Row>
</merge>
diff --git a/java/res/xml-sw600dp/row_symbols_shift4.xml b/java/res/xml-sw600dp/row_symbols_shift4.xml
index 6f3aac7c6..29befa92a 100644
--- a/java/res/xml-sw600dp/row_symbols_shift4.xml
+++ b/java/res/xml-sw600dp/row_symbols_shift4.xml
@@ -33,6 +33,8 @@
latin:keyXPos="28.0%p"
latin:keyboardLayout="@xml/key_space"
latin:backgroundType="normal" />
- <!-- Here is empty space. -->
+ <!-- Note: This Spacer prevents the above key from being marked as a right edge key. -->
+ <Spacer
+ latin:keyWidth="fillRight" />
</Row>
</merge>
diff --git a/java/res/xml-sw600dp/rowkeys_arabic1.xml b/java/res/xml-sw600dp/rowkeys_arabic1.xml
index 44fdc676d..6a0e25786 100644
--- a/java/res/xml-sw600dp/rowkeys_arabic1.xml
+++ b/java/res/xml-sw600dp/rowkeys_arabic1.xml
@@ -23,19 +23,23 @@
>
<!-- U+0636: "ض" ARABIC LETTER DAD -->
<Key
- latin:keyLabel="&#x0636;" />
+ latin:keyLabel="&#x0636;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0635: "ص" ARABIC LETTER SAD -->
<Key
- latin:keyLabel="&#x0635;" />
+ latin:keyLabel="&#x0635;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+062B: "ث" ARABIC LETTER THEH -->
<Key
- latin:keyLabel="&#x062B;" />
+ latin:keyLabel="&#x062B;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0642: "ق" ARABIC LETTER QAF
U+06A8: "ڨ" ARABIC LETTER QAF WITH THREE DOTS ABOVE -->
<!-- TODO: DroidSansArabic lacks the glyph of U+06A8 ARABIC LETTER QAF WITH THREE DOTS ABOVE -->
<Key
latin:keyLabel="&#x0642;"
- latin:moreKeys="&#x06A8;" />
+ latin:moreKeys="&#x06A8;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0641: "ف" ARABIC LETTER FEH
U+06A4: "ڤ" ARABIC LETTER VEH
U+06A2: "ڢ" ARABIC LETTER FEH WITH DOT MOVED BELOW
@@ -44,28 +48,35 @@
<!-- TODO: DroidSansArabic lacks the glyph of U+06A5 ARABIC LETTER FEH WITH THREE DOTS BELOW -->
<Key
latin:keyLabel="&#x0641;"
- latin:moreKeys="&#x06A4;,&#x06A2;,&#x06A5;" />
+ latin:moreKeys="&#x06A4;,&#x06A2;,&#x06A5;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+063A: "غ" ARABIC LETTER GHAIN -->
<Key
- latin:keyLabel="&#x063A;" />
+ latin:keyLabel="&#x063A;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0639: "ع" ARABIC LETTER AIN -->
<Key
- latin:keyLabel="&#x0639;" />
+ latin:keyLabel="&#x0639;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0647: "ه" ARABIC LETTER HEH
U+FEEB: "ﻫ" ARABIC LETTER HEH INITIAL FORM
U+0647 U+200D: ARABIC LETTER HEH + ZERO WIDTH JOINER -->
<Key
latin:keyLabel="&#x0647;"
- latin:moreKeys="&#xFEEB;|&#x0647;&#x200D;" />
+ latin:moreKeys="&#xFEEB;|&#x0647;&#x200D;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+062E: "خ" ARABIC LETTER KHAH -->
<Key
- latin:keyLabel="&#x062E;" />
+ latin:keyLabel="&#x062E;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+062D: "ح" ARABIC LETTER HAH -->
<Key
- latin:keyLabel="&#x062D;" />
+ latin:keyLabel="&#x062D;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+062C: "ج" ARABIC LETTER JEEM
U+0686: "چ" ARABIC LETTER TCHEH -->
<Key
latin:keyLabel="&#x062C;"
- latin:moreKeys="&#x0686;" />
+ latin:moreKeys="&#x0686;"
+ latin:keyLabelFlags="fontNormal" />
</merge>
diff --git a/java/res/xml-sw600dp/rowkeys_arabic2.xml b/java/res/xml-sw600dp/rowkeys_arabic2.xml
index 3eba2fbf3..00e69ace7 100644
--- a/java/res/xml-sw600dp/rowkeys_arabic2.xml
+++ b/java/res/xml-sw600dp/rowkeys_arabic2.xml
@@ -26,21 +26,25 @@
<!-- TODO: DroidSansArabic lacks the glyph of U+069C ARABIC LETTER SEEN WITH THREE DOTS BELOW AND THREE DOTS ABOVE -->
<Key
latin:keyLabel="&#x0634;"
- latin:moreKeys="&#x069C;" />
+ latin:moreKeys="&#x069C;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0633: "س" ARABIC LETTER SEEN -->
<Key
- latin:keyLabel="&#x0633;" />
+ latin:keyLabel="&#x0633;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+064A: "ي" ARABIC LETTER YEH
U+0626: "ئ" ARABIC LETTER YEH WITH HAMZA ABOVE
U+0649: "ى" ARABIC LETTER ALEF MAKSURA -->
<Key
latin:keyLabel="&#x064A;"
- latin:moreKeys="&#x0626;,&#x0649;" />
+ latin:moreKeys="&#x0626;,&#x0649;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0628: "ب" ARABIC LETTER BEH
U+067E: "پ" ARABIC LETTER PEH -->
<Key
latin:keyLabel="&#x0628;"
- latin:moreKeys="&#x067E;" />
+ latin:moreKeys="&#x067E;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0644: "ل" ARABIC LETTER LAM
U+FEFB: "ﻻ" ARABIC LIGATURE LAM WITH ALEF ISOLATED FORM
U+0627: "ا" ARABIC LETTER ALEF
@@ -52,7 +56,8 @@
U+0622: "آ" ARABIC LETTER ALEF WITH MADDA ABOVE -->
<Key
latin:keyLabel="&#x0644;"
- latin:moreKeys="&#xFEFB;|&#x0644;&#x0627;,&#xFEF7;|&#x0644;&#x0623;,&#xFEF9;|&#x0644;&#x0625;,&#xFEF5;|&#x0644;&#x0622;" />
+ latin:moreKeys="&#xFEFB;|&#x0644;&#x0627;,&#xFEF7;|&#x0644;&#x0623;,&#xFEF9;|&#x0644;&#x0625;,&#xFEF5;|&#x0644;&#x0622;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0627: "ا" ARABIC LETTER ALEF
U+0621: "ء" ARABIC LETTER HAMZA
U+0671: "ٱ" ARABIC LETTER ALEF WASLA
@@ -61,23 +66,29 @@
U+0622: "آ" ARABIC LETTER ALEF WITH MADDA ABOVE -->
<Key
latin:keyLabel="&#x0627;"
- latin:moreKeys="&#x0621;,&#x0671;,&#x0623;,&#x0625;,&#x0622;" />
+ latin:moreKeys="&#x0621;,&#x0671;,&#x0623;,&#x0625;,&#x0622;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+062A: "ت" ARABIC LETTER TEH -->
<Key
- latin:keyLabel="&#x062A;" />
+ latin:keyLabel="&#x062A;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0646: "ن" ARABIC LETTER NOON -->
<Key
- latin:keyLabel="&#x0646;" />
+ latin:keyLabel="&#x0646;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0645: "م" ARABIC LETTER MEEM -->
<Key
- latin:keyLabel="&#x0645;" />
+ latin:keyLabel="&#x0645;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0643: "ك" ARABIC LETTER KAF
U+06AF: "گ" ARABIC LETTER GAF
U+06A9: "ک" ARABIC LETTER KEHEH -->
<Key
latin:keyLabel="&#x0643;"
- latin:moreKeys="&#x06AF;,&#x06A9;" />
+ latin:moreKeys="&#x06AF;,&#x06A9;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0637: "ط" ARABIC LETTER TAH -->
<Key
- latin:keyLabel="&#x0637;" />
+ latin:keyLabel="&#x0637;"
+ latin:keyLabelFlags="fontNormal" />
</merge>
diff --git a/java/res/xml-sw600dp/rowkeys_arabic3.xml b/java/res/xml-sw600dp/rowkeys_arabic3.xml
index 911550f4a..b0bcd78d6 100644
--- a/java/res/xml-sw600dp/rowkeys_arabic3.xml
+++ b/java/res/xml-sw600dp/rowkeys_arabic3.xml
@@ -23,37 +23,48 @@
>
<!-- U+0626: "ئ" ARABIC LETTER YEH WITH HAMZA ABOVE -->
<Key
- latin:keyLabel="&#x0626;" />
+ latin:keyLabel="&#x0626;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0621: "ء" ARABIC LETTER HAMZA -->
<Key
- latin:keyLabel="&#x0621;" />
+ latin:keyLabel="&#x0621;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0624: "ؤ" ARABIC LETTER WAW WITH HAMZA ABOVE -->
<Key
- latin:keyLabel="&#x0624;" />
+ latin:keyLabel="&#x0624;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0631: "ر" ARABIC LETTER REH -->
<Key
- latin:keyLabel="&#x0631;" />
+ latin:keyLabel="&#x0631;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0630: "ذ" ARABIC LETTER THAL -->
<Key
- latin:keyLabel="&#x0630;" />
+ latin:keyLabel="&#x0630;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0649: "ى" ARABIC LETTER ALEF MAKSURA -->
<Key
- latin:keyLabel="&#x0649;" />
+ latin:keyLabel="&#x0649;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0629: "ة" ARABIC LETTER TEH MARBUTA -->
<Key
- latin:keyLabel="&#x0629;" />
+ latin:keyLabel="&#x0629;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0648: "و" ARABIC LETTER WAW -->
<Key
- latin:keyLabel="&#x0648;" />
+ latin:keyLabel="&#x0648;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0632: "ز" ARABIC LETTER ZAIN
U+0698: "ژ" ARABIC LETTER JEH -->
<Key
latin:keyLabel="&#x0632;"
- latin:moreKeys="&#x0698;" />
+ latin:moreKeys="&#x0698;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0638: "ظ" ARABIC LETTER ZAH -->
<Key
- latin:keyLabel="&#x0638;" />
+ latin:keyLabel="&#x0638;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+062F: "د" ARABIC LETTER DAL -->
<Key
- latin:keyLabel="&#x062F;" />
+ latin:keyLabel="&#x062F;"
+ latin:keyLabelFlags="fontNormal" />
</merge>
diff --git a/java/res/xml-sw600dp/rowkeys_farsi1.xml b/java/res/xml-sw600dp/rowkeys_farsi1.xml
index 53208f286..7b312404a 100644
--- a/java/res/xml-sw600dp/rowkeys_farsi1.xml
+++ b/java/res/xml-sw600dp/rowkeys_farsi1.xml
@@ -23,25 +23,32 @@
>
<!-- U+0636: "ض" ARABIC LETTER DAD -->
<Key
- latin:keyLabel="&#x0636;" />
+ latin:keyLabel="&#x0636;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0635: "ص" ARABIC LETTER SAD -->
<Key
- latin:keyLabel="&#x0635;" />
+ latin:keyLabel="&#x0635;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+062B: "ث" ARABIC LETTER THEH -->
<Key
- latin:keyLabel="&#x062B;" />
+ latin:keyLabel="&#x062B;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0642: "ق" ARABIC LETTER QAF -->
<Key
- latin:keyLabel="&#x0642;" />
+ latin:keyLabel="&#x0642;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0641: "ف" ARABIC LETTER FEH -->
<Key
- latin:keyLabel="&#x0641;" />
+ latin:keyLabel="&#x0641;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+063A: "غ" ARABIC LETTER GHAIN -->
<Key
- latin:keyLabel="&#x063A;" />
+ latin:keyLabel="&#x063A;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0639: "ع" ARABIC LETTER AIN -->
<Key
- latin:keyLabel="&#x0639;" />
+ latin:keyLabel="&#x0639;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0647: "ه" ARABIC LETTER HEH
U+FEEB: "ﻫ" ARABIC LETTER HEH INITIAL FORM
U+0647/U+200D: ARABIC LETTER HEH + ZERO WIDTH JOINER
@@ -49,17 +56,22 @@
U+0629: "ة" ARABIC LETTER TEH MARBUTA -->
<Key
latin:keyLabel="&#x0647;"
- latin:moreKeys="&#xFEEB;|&#x0647;&#x200D;,&#x0647;&#x0654;,&#x0629;,%" />
+ latin:moreKeys="&#xFEEB;|&#x0647;&#x200D;,&#x0647;&#x0654;,&#x0629;,%"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+062E: "خ" ARABIC LETTER KHAH -->
<Key
- latin:keyLabel="&#x062E;" />
+ latin:keyLabel="&#x062E;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+062D: "ح" ARABIC LETTER HAH -->
<Key
- latin:keyLabel="&#x062D;" />
+ latin:keyLabel="&#x062D;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+062C: "ج" ARABIC LETTER JEEM -->
<Key
- latin:keyLabel="&#x062C;" />
+ latin:keyLabel="&#x062C;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0686: "چ" ARABIC LETTER TCHEH -->
<Key
- latin:keyLabel="&#x0686;" />
+ latin:keyLabel="&#x0686;"
+ latin:keyLabelFlags="fontNormal" />
</merge>
diff --git a/java/res/xml-sw600dp/rowkeys_farsi2.xml b/java/res/xml-sw600dp/rowkeys_farsi2.xml
index 234f98430..3b759b66c 100644
--- a/java/res/xml-sw600dp/rowkeys_farsi2.xml
+++ b/java/res/xml-sw600dp/rowkeys_farsi2.xml
@@ -23,10 +23,12 @@
>
<!-- U+0634: "ش" ARABIC LETTER SHEEN -->
<Key
- latin:keyLabel="&#x0634;" />
+ latin:keyLabel="&#x0634;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0633: "س" ARABIC LETTER SEEN -->
<Key
- latin:keyLabel="&#x0633;" />
+ latin:keyLabel="&#x0633;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+06CC: "ی" ARABIC LETTER FARSI YEH
U+0626: "ئ" ARABIC LETTER YEH WITH HAMZA ABOVE
U+064A: "ي" ARABIC LETTER YEH
@@ -34,13 +36,16 @@
U+0649: "ى" ARABIC LETTER ALEF MAKSURA -->
<Key
latin:keyLabel="&#x06CC;"
- latin:moreKeys="&#x0626;,&#x064A;,&#xFBE8;|&#x0649;" />
+ latin:moreKeys="&#x0626;,&#x064A;,&#xFBE8;|&#x0649;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0628: "ب" ARABIC LETTER BEH -->
<Key
- latin:keyLabel="&#x0628;" />
+ latin:keyLabel="&#x0628;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0644: "ل" ARABIC LETTER LAM -->
<Key
- latin:keyLabel="&#x0644;" />
+ latin:keyLabel="&#x0644;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0627: "ا" ARABIC LETTER ALEF
U+0621: "ء" ARABIC LETTER HAMZA
U+0622: "آ" ARABIC LETTER ALEF WITH MADDA ABOVE
@@ -49,25 +54,31 @@
U+0625: "إ" ARABIC LETTER ALEF WITH HAMZA BELOW -->
<Key
latin:keyLabel="&#x0627;"
- latin:moreKeys="&#x0621;,&#x0622;,&#x0623;,&#x0671;,&#x0625;" />
+ latin:moreKeys="&#x0621;,&#x0622;,&#x0623;,&#x0671;,&#x0625;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+062A: "ت" ARABIC LETTER TEH
U+062B: "ﺙ" ARABIC LETTER THEH
U+0629: "ة": ARABIC LETTER TEH MARBUTA -->
<Key
latin:keyLabel="&#x062A;"
- latin:moreKeys="&#x062B;,&#x0629;" />
+ latin:moreKeys="&#x062B;,&#x0629;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0646: "ن" ARABIC LETTER NOON -->
<Key
- latin:keyLabel="&#x0646;" />
+ latin:keyLabel="&#x0646;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0645: "م" ARABIC LETTER MEEM -->
<Key
- latin:keyLabel="&#x0645;" />
+ latin:keyLabel="&#x0645;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+06A9: "ک" ARABIC LETTER KEHEH
U+0643: "ك" ARABIC LETTER KAF -->
<Key
latin:keyLabel="&#x06A9;"
- latin:moreKeys="&#x0643;" />
+ latin:moreKeys="&#x0643;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+06AF: "گ" ARABIC LETTER GAF -->
<Key
- latin:keyLabel="&#x06AF;" />
+ latin:keyLabel="&#x06AF;"
+ latin:keyLabelFlags="fontNormal" />
</merge>
diff --git a/java/res/xml-sw600dp/rowkeys_farsi3.xml b/java/res/xml-sw600dp/rowkeys_farsi3.xml
index 998ba72d6..3597618ce 100644
--- a/java/res/xml-sw600dp/rowkeys_farsi3.xml
+++ b/java/res/xml-sw600dp/rowkeys_farsi3.xml
@@ -23,34 +23,44 @@
>
<!-- U+0638: "ظ" ARABIC LETTER ZAH -->
<Key
- latin:keyLabel="&#x0638;" />
+ latin:keyLabel="&#x0638;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0637: "ط" ARABIC LETTER TAH -->
<Key
- latin:keyLabel="&#x0637;" />
+ latin:keyLabel="&#x0637;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0698: "ژ" ARABIC LETTER JEH -->
<Key
- latin:keyLabel="&#x0698;" />
+ latin:keyLabel="&#x0698;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0632: "ز" ARABIC LETTER ZAIN -->
<Key
- latin:keyLabel="&#x0632;" />
+ latin:keyLabel="&#x0632;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0631: "ر" ARABIC LETTER REH -->
<Key
- latin:keyLabel="&#x0631;" />
+ latin:keyLabel="&#x0631;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0630: "ذ" ARABIC LETTER THAL -->
<Key
- latin:keyLabel="&#x0630;" />
+ latin:keyLabel="&#x0630;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+062F: "د" ARABIC LETTER DAL -->
<Key
- latin:keyLabel="&#x062F;" />
+ latin:keyLabel="&#x062F;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+067E: "پ" ARABIC LETTER PEH -->
<Key
- latin:keyLabel="&#x067E;" />
+ latin:keyLabel="&#x067E;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0648: "و" ARABIC LETTER WAW
U+0624: "ؤ" ARABIC LETTER WAW WITH HAMZA ABOVE -->
<Key
latin:keyLabel="&#x0648;"
- latin:moreKeys="&#x0624;" />
+ latin:moreKeys="&#x0624;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0622: "آ" ARABIC LETTER ALEF WITH MADDA ABOVE -->
<Key
- latin:keyLabel="&#x0622;" />
+ latin:keyLabel="&#x0622;"
+ latin:keyLabelFlags="fontNormal" />
</merge>
diff --git a/java/res/xml-sw600dp/rowkeys_symbols3.xml b/java/res/xml-sw600dp/rowkeys_symbols3.xml
index 4bfa0d730..30fba3812 100644
--- a/java/res/xml-sw600dp/rowkeys_symbols3.xml
+++ b/java/res/xml-sw600dp/rowkeys_symbols3.xml
@@ -49,7 +49,7 @@
<Key
latin:keyLabel="." />
<Key
- latin:keyLabel="!text/keylabel_for_symbols_exclamation"
+ latin:keyLabel="!"
latin:moreKeys="!text/more_keys_for_symbols_exclamation" />
<Key
latin:keyLabel="!text/keylabel_for_symbols_question"
diff --git a/java/res/xml-sw600dp/rowkeys_thai1.xml b/java/res/xml-sw600dp/rowkeys_thai1.xml
deleted file mode 100644
index 6aec7c2c5..000000000
--- a/java/res/xml-sw600dp/rowkeys_thai1.xml
+++ /dev/null
@@ -1,97 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 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.
-*/
--->
-
-<merge
- xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
- <switch>
- <case
- latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted"
- >
- <!-- U+0E51: "๑" THAI DIGIT ONE -->
- <Key
- latin:keyLabel="&#x0E51;" />
- <!-- U+0E52: "๒" THAI DIGIT TWO -->
- <Key
- latin:keyLabel="&#x0E52;" />
- <!-- U+0E53: "๓" THAI DIGIT THREE -->
- <Key
- latin:keyLabel="&#x0E53;" />
- <!-- U+0E54: "๔" THAI DIGIT FOUR -->
- <Key
- latin:keyLabel="&#x0E54;" />
- <!-- U+0E39: " ู" THAI CHARACTER SARA UU -->
- <Key
- latin:keyLabel="&#x0E39;" />
- <!-- U+0E3F: "฿" THAI CURRENCY SYMBOL BAHT -->
- <Key
- latin:keyLabel="&#x0E3F;" />
- <!-- U+0E55: "๕" THAI DIGIT FIVE -->
- <Key
- latin:keyLabel="&#x0E55;" />
- <!-- U+0E56: "๖" THAI DIGIT SIX -->
- <Key
- latin:keyLabel="&#x0E56;" />
- <!-- U+0E57: "๗" THAI DIGIT SEVEN -->
- <Key
- latin:keyLabel="&#x0E57;" />
- <!-- U+0E58: "๘" THAI DIGIT EIGHT -->
- <Key
- latin:keyLabel="&#x0E58;" />
- <!-- U+0E59: "๙" THAI DIGIT NINE -->
- <Key
- latin:keyLabel="&#x0E59;" />
- </case>
- <default>
- <!-- U+0E45: "ๅ" THAI CHARACTER LAKKHANGYAO -->
- <Key
- latin:keyLabel="&#x0E45;" />
- <Key
- latin:keyLabel="/" />
- <!-- U+0E20: "ภ" THAI CHARACTER PHO SAMPHAO -->
- <Key
- latin:keyLabel="&#x0E20;" />
- <!-- U+0E16: "ถ" THAI CHARACTER THO THUNG -->
- <Key
- latin:keyLabel="&#x0E16;" />
- <!-- U+0E38: " ุ" THAI CHARACTER SARA U -->
- <Key
- latin:keyLabel="&#x0E38;" />
- <!-- U+0E36: " ึ" THAI CHARACTER SARA UE -->
- <Key
- latin:keyLabel="&#x0E36;" />
- <!-- U+0E04: "ค" THAI CHARACTER KHO KHWAI -->
- <Key
- latin:keyLabel="&#x0E04;" />
- <!-- U+0E15: "ต" THAI CHARACTER TO TAO -->
- <Key
- latin:keyLabel="&#x0E15;" />
- <!-- U+0E08: "จ" THAI CHARACTER CHO CHAN -->
- <Key
- latin:keyLabel="&#x0E08;" />
- <!-- U+0E02: "ข" THAI CHARACTER KHO KHAI -->
- <Key
- latin:keyLabel="&#x0E02;" />
- <!-- U+0E0A: "ช" THAI CHARACTER CHO CHANG -->
- <Key
- latin:keyLabel="&#x0E0A;" />
- </default>
- </switch>
-</merge>
diff --git a/java/res/xml-sw600dp/rowkeys_thai2.xml b/java/res/xml-sw600dp/rowkeys_thai2.xml
deleted file mode 100644
index edb759a89..000000000
--- a/java/res/xml-sw600dp/rowkeys_thai2.xml
+++ /dev/null
@@ -1,108 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 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.
-*/
--->
-
-<merge
- xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
- <switch>
- <case
- latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted"
- >
- <!-- U+0E50: "๐" THAI DIGIT ZERO -->
- <Key
- latin:keyLabel="&#x0E50;" />
- <Key
- latin:keyLabel="&quot;" />
- <!-- U+0E0E: "ฎ" THAI CHARACTER DO CHADA -->
- <Key
- latin:keyLabel="&#x0E0E;" />
- <!-- U+0E11: "ฑ" THAI CHARACTER THO NANGMONTHO -->
- <Key
- latin:keyLabel="&#x0E11;" />
- <!-- U+0E18: "ธ" THAI CHARACTER THO THONG -->
- <Key
- latin:keyLabel="&#x0E18;" />
- <!-- U+0E4D: " ํ" THAI CHARACTER THANTHAKHAT -->
- <Key
- latin:keyLabel="&#x0E4D;" />
- <!-- U+0E4A: " ๊" THAI CHARACTER MAI TRI -->
- <Key
- latin:keyLabel="&#x0E4A;" />
- <!-- U+0E13: "ณ" THAI CHARACTER NO NEN -->
- <Key
- latin:keyLabel="&#x0E13;" />
- <!-- U+0E2F: "ฯ" THAI CHARACTER PAIYANNOI -->
- <Key
- latin:keyLabel="&#x0E2F;" />
- <!-- U+0E0D: "ญ" THAI CHARACTER YO YING -->
- <Key
- latin:keyLabel="&#x0E0D;" />
- <!-- U+0E10: "ฐ" THAI CHARACTER THO THAN -->
- <Key
- latin:keyLabel="&#x0E10;" />
- <Key
- latin:keyLabel="," />
- <!-- U+0E05: "ฅ" THAI CHARACTER KHO KHON -->
- <Key
- latin:keyLabel="&#x0E05;" />
- </case>
- <default>
- <!-- U+0E46: "ๆ" THAI CHARACTER MAIYAMOK -->
- <Key
- latin:keyLabel="&#x0E46;" />
- <!-- U+0E44: "ไ" THAI CHARACTER SARA AI MAIMALAI -->
- <Key
- latin:keyLabel="&#x0E44;" />
- <!-- U+0E33: "ำ" THAI CHARACTER SARA AM -->
- <Key
- latin:keyLabel="&#x0E33;" />
- <!-- U+0E1E: "พ" THAI CHARACTER PHO PHAN -->
- <Key
- latin:keyLabel="&#x0E1E;" />
- <!-- U+0E30: "ะ" THAI CHARACTER SARA A -->
- <Key
- latin:keyLabel="&#x0E30;" />
- <!-- U+0E31: " ั" THAI CHARACTER MAI HAN-AKAT -->
- <Key
- latin:keyLabel="&#x0E31;" />
- <!-- U+0E35: " ี" HAI CHARACTER SARA II -->
- <Key
- latin:keyLabel="&#x0E35;" />
- <!-- U+0E23: "ร" THAI CHARACTER RO RUA -->
- <Key
- latin:keyLabel="&#x0E23;" />
- <!-- U+0E19: "น" THAI CHARACTER NO NU -->
- <Key
- latin:keyLabel="&#x0E19;" />
- <!-- U+0E22: "ย" THAI CHARACTER YO YAK -->
- <Key
- latin:keyLabel="&#x0E22;" />
- <!-- U+0E1A: "บ" THAI CHARACTER BO BAIMAI -->
- <Key
- latin:keyLabel="&#x0E1A;" />
- <!-- U+0E25: "ล" THAI CHARACTER LO LING -->
- <Key
- latin:keyLabel="&#x0E25;" />
- <!-- U+0E03: "ฃ" THAI CHARACTER KHO KHUAT -->
- <Key
- latin:keyLabel="&#x0E03;" />
- </default>
- </switch>
-</merge>
diff --git a/java/res/xml-sw600dp/rowkeys_thai3.xml b/java/res/xml-sw600dp/rowkeys_thai3.xml
deleted file mode 100644
index 7507dde86..000000000
--- a/java/res/xml-sw600dp/rowkeys_thai3.xml
+++ /dev/null
@@ -1,97 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 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.
-*/
--->
-
-<merge
- xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
- <switch>
- <case
- latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted"
- >
- <!-- U+0E24: "ฤ" THAI CHARACTER RU -->
- <Key
- latin:keyLabel="&#x0E24;" />
- <!-- U+0E06: "ฆ" THAI CHARACTER KHO RAKHANG -->
- <Key
- latin:keyLabel="&#x0E06;" />
- <!-- U+0E0F: "ฏ" THAI CHARACTER TO PATAK -->
- <Key
- latin:keyLabel="&#x0E0F;" />
- <!-- U+0E42: "โ" THAI CHARACTER SARA O -->
- <Key
- latin:keyLabel="&#x0E42;" />
- <!-- U+0E0C: "ฌ" THAI CHARACTER CHO CHOE -->
- <Key
- latin:keyLabel="&#x0E0C;" />
- <!-- U+0E47: " ็" THAI CHARACTER MAITAIKHU -->
- <Key
- latin:keyLabel="&#x0E47;" />
- <!-- U+0E4B: " ๋" THAI CHARACTER MAI CHATTAWA -->
- <Key
- latin:keyLabel="&#x0E4B;" />
- <!-- U+0E29: "ษ" THAI CHARACTER SO RUSI -->
- <Key
- latin:keyLabel="&#x0E29;" />
- <!-- U+0E28: "ศ" THAI CHARACTER SO SALA -->
- <Key
- latin:keyLabel="&#x0E28;" />
- <!-- U+0E0B: "ซ" THAI CHARACTER SO SO -->
- <Key
- latin:keyLabel="&#x0E0B;" />
- <Key
- latin:keyLabel="." />
- </case>
- <default>
- <!-- U+0E1F: "ฟ" THAI CHARACTER FO FAN -->
- <Key
- latin:keyLabel="&#x0E1F;" />
- <!-- U+0E2B: "ห" THAI CHARACTER HO HIP -->
- <Key
- latin:keyLabel="&#x0E2B;" />
- <!-- U+0E01: "ก" THAI CHARACTER KO KAI -->
- <Key
- latin:keyLabel="&#x0E01;" />
- <!-- U+0E14: "ด" THAI CHARACTER DO DEK -->
- <Key
- latin:keyLabel="&#x0E14;" />
- <!-- U+0E40: "เ" THAI CHARACTER SARA E -->
- <Key
- latin:keyLabel="&#x0E40;" />
- <!-- U+0E49: " ้" THAI CHARACTER MAI THO -->
- <Key
- latin:keyLabel="&#x0E49;" />
- <!-- U+0E48: " ฺ" THAI CHARACTER MAI EK -->
- <Key
- latin:keyLabel="&#x0E48;" />
- <!-- U+0E32: "า" THAI CHARACTER SARA AA -->
- <Key
- latin:keyLabel="&#x0E32;" />
- <!-- U+0E2A: "ส" THAI CHARACTER SO SUA -->
- <Key
- latin:keyLabel="&#x0E2A;" />
- <!-- U+0E27: "ว" THAI CHARACTER WO WAEN -->
- <Key
- latin:keyLabel="&#x0E27;" />
- <!-- U+0E07: "ง" THAI CHARACTER NGO NGU -->
- <Key
- latin:keyLabel="&#x0E07;" />
- </default>
- </switch>
-</merge>
diff --git a/java/res/xml-sw600dp/rowkeys_thai4.xml b/java/res/xml-sw600dp/rowkeys_thai4.xml
deleted file mode 100644
index 64549bdce..000000000
--- a/java/res/xml-sw600dp/rowkeys_thai4.xml
+++ /dev/null
@@ -1,89 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 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.
-*/
--->
-
-<merge
- xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
- <switch>
- <case
- latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted"
- >
- <Key
- latin:keyLabel="(" />
- <Key
- latin:keyLabel=")" />
- <!-- U+0E09: "ฉ" THAI CHARACTER CHO CHING -->
- <Key
- latin:keyLabel="&#x0E09;" />
- <!-- U+0E2E: "ฮ" THAI CHARACTER HO NOKHUK -->
- <Key
- latin:keyLabel="&#x0E2E;" />
- <!-- U+0E3A: " ฺ" THAI CHARACTER PHINTHU -->
- <Key
- latin:keyLabel="&#x0E3A;" />
- <!-- U+0E4C: " ์" THAI CHARACTER THANTHAKHAT -->
- <Key
- latin:keyLabel="&#x0E4C;" />
- <Key
- latin:keyLabel="\?" />
- <!-- U+0E12: "ฒ" THAI CHARACTER THO PHUTHAO -->
- <Key
- latin:keyLabel="&#x0E12;" />
- <!-- U+0E2C: "ฬ" THAI CHARACTER LO CHULA -->
- <Key
- latin:keyLabel="&#x0E2C;" />
- <!-- U+0E26: "ฦ" THAI CHARACTER LU -->
- <Key
- latin:keyLabel="&#x0E26;" />
- </case>
- <default>
- <!-- U+0E1C: "ผ" THAI CHARACTER PHO PHUNG -->
- <Key
- latin:keyLabel="&#x0E1C;" />
- <!-- U+0E1B: "ป" THAI CHARACTER PO PLA -->
- <Key
- latin:keyLabel="&#x0E1B;" />
- <!-- U+0E41: "แ" THAI CHARACTER SARA AE -->
- <Key
- latin:keyLabel="&#x0E41;" />
- <!-- U+0E2D: "อ" THAI CHARACTER O ANG -->
- <Key
- latin:keyLabel="&#x0E2D;" />
- <!-- U+0E34: " ิ" THAI CHARACTER SARA I -->
- <Key
- latin:keyLabel="&#x0E34;" />
- <!-- U+0E37: " ื" THAI CHARACTER SARA UEE -->
- <Key
- latin:keyLabel="&#x0E37;" />
- <!-- U+0E17: "ท" THAI CHARACTER THO THAHAN -->
- <Key
- latin:keyLabel="&#x0E17;" />
- <!-- U+0E21: "ม" THAI CHARACTER MO MA -->
- <Key
- latin:keyLabel="&#x0E21;" />
- <!-- U+0E43: "ใ" THAI CHARACTER SARA AI MAIMUAN -->
- <Key
- latin:keyLabel="&#x0E43;" />
- <!-- U+0E1D: "ฝ" THAI CHARACTER FO FA -->
- <Key
- latin:keyLabel="&#x0E1D;" />
- </default>
- </switch>
-</merge>
diff --git a/java/res/xml-sw600dp/rows_esperanto.xml b/java/res/xml-sw600dp/rows_esperanto.xml
deleted file mode 100644
index e0c62fed7..000000000
--- a/java/res/xml-sw600dp/rows_esperanto.xml
+++ /dev/null
@@ -1,61 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 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.
-*/
--->
-
-<merge
- xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
- <include
- latin:keyboardLayout="@xml/key_styles_common" />
- <Row
- latin:keyWidth="9.0%p"
- >
- <include
- latin:keyboardLayout="@xml/rowkeys_esperanto1"
- latin:keyLabelFlags="disableAdditionalMoreKeys|disableKeyHintLabel" />
- <Key
- latin:keyStyle="deleteKeyStyle"
- latin:keyWidth="fillRight" />
- </Row>
- <Row
- latin:keyWidth="9.0%p"
- >
- <include
- latin:keyboardLayout="@xml/rowkeys_esperanto2" />
- <Key
- latin:keyStyle="enterKeyStyle"
- latin:keyWidth="fillRight" />
- </Row>
- <Row
- latin:keyWidth="9.0%p"
- >
- <Key
- latin:keyStyle="shiftKeyStyle"
- latin:keyWidth="10.0%p" />
- <include
- latin:keyboardLayout="@xml/rowkeys_esperanto3" />
- <include
- latin:keyboardLayout="@xml/keys_comma_period" />
- <Key
- latin:keyStyle="shiftKeyStyle"
- latin:keyWidth="fillRight" />
- </Row>
- <include
- latin:keyboardLayout="@xml/row_qwerty4" />
-</merge>
diff --git a/java/res/xml-sw600dp/rows_number_normal.xml b/java/res/xml-sw600dp/rows_number_normal.xml
index 48b304089..f69239456 100644
--- a/java/res/xml-sw600dp/rows_number_normal.xml
+++ b/java/res/xml-sw600dp/rows_number_normal.xml
@@ -153,5 +153,8 @@
<Key
latin:keyLabel="#"
latin:keyStyle="numKeyStyle" />
+ <!-- Note: This Spacer prevents the above key from being marked as a right edge key. -->
+ <Spacer
+ latin:keyWidth="fillRight" />
</Row>
</merge>
diff --git a/java/res/xml-sw600dp/rows_thai.xml b/java/res/xml-sw600dp/rows_thai.xml
index c1fe55b39..bc89640ac 100644
--- a/java/res/xml-sw600dp/rows_thai.xml
+++ b/java/res/xml-sw600dp/rows_thai.xml
@@ -27,8 +27,7 @@
latin:keyWidth="7.5%p"
>
<include
- latin:keyboardLayout="@xml/rowkeys_thai1"
- latin:keyXPos="3.75%p" />
+ latin:keyboardLayout="@xml/rowkeys_thai1" />
<Key
latin:keyStyle="deleteKeyStyle"
latin:keyWidth="fillRight" />
@@ -38,14 +37,16 @@
>
<include
latin:keyboardLayout="@xml/rowkeys_thai2"
- latin:keyXPos="0.719%p" />
+ latin:keyXPos="2.5%p" />
+ <include
+ latin:keyboardLayout="@xml/key_thai_kho_khuat" />
</Row>
<Row
latin:keyWidth="7.5%p"
>
<include
latin:keyboardLayout="@xml/rowkeys_thai3"
- latin:keyXPos="3.75%p" />
+ latin:keyXPos="5.0%p" />
<Key
latin:keyStyle="enterKeyStyle"
latin:keyWidth="fillRight" />
diff --git a/java/res/xml-sw768dp-land/kbd_number.xml b/java/res/xml-sw768dp-land/kbd_number.xml
index 3ad25a392..de8d55904 100644
--- a/java/res/xml-sw768dp-land/kbd_number.xml
+++ b/java/res/xml-sw768dp-land/kbd_number.xml
@@ -22,6 +22,7 @@
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
latin:keyboardHorizontalEdgesPadding="10%p"
latin:keyWidth="13.250%p"
+ latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
>
<include
latin:keyboardLayout="@xml/rows_number" />
diff --git a/java/res/xml-sw768dp-land/kbd_phone.xml b/java/res/xml-sw768dp-land/kbd_phone.xml
index abe7e7c41..f88a076f6 100644
--- a/java/res/xml-sw768dp-land/kbd_phone.xml
+++ b/java/res/xml-sw768dp-land/kbd_phone.xml
@@ -22,6 +22,7 @@
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
latin:keyboardHorizontalEdgesPadding="10%p"
latin:keyWidth="13.250%p"
+ latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
>
<include
latin:keyboardLayout="@xml/rows_phone" />
diff --git a/java/res/xml-sw768dp-land/kbd_phone_symbols.xml b/java/res/xml-sw768dp-land/kbd_phone_symbols.xml
index 641464dbe..eaa413e7d 100644
--- a/java/res/xml-sw768dp-land/kbd_phone_symbols.xml
+++ b/java/res/xml-sw768dp-land/kbd_phone_symbols.xml
@@ -22,6 +22,7 @@
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
latin:keyboardHorizontalEdgesPadding="10%p"
latin:keyWidth="13.250%p"
+ latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
>
<!-- Tablet doesn't have phone symbols keyboard -->
<include
diff --git a/java/res/xml-sw768dp-land/kbd_thai_symbols_shift.xml b/java/res/xml-sw768dp-land/kbd_thai_symbols_shift.xml
deleted file mode 100644
index fa30f24c0..000000000
--- a/java/res/xml-sw768dp-land/kbd_thai_symbols_shift.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 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.
-*/
--->
-
-<Keyboard
- xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
- latin:rowHeight="20%p"
- latin:verticalGap="2.65%p"
- latin:touchPositionCorrectionData="@null"
->
- <include
- latin:keyboardLayout="@xml/rows_thai_symbols_shift" />
-</Keyboard>
diff --git a/java/res/xml-sw768dp/kbd_number.xml b/java/res/xml-sw768dp/kbd_number.xml
index b20123c80..1b46edd50 100644
--- a/java/res/xml-sw768dp/kbd_number.xml
+++ b/java/res/xml-sw768dp/kbd_number.xml
@@ -21,6 +21,7 @@
<Keyboard
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
latin:keyWidth="13.250%p"
+ latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
>
<include
latin:keyboardLayout="@xml/rows_number" />
diff --git a/java/res/xml-sw768dp/kbd_phone.xml b/java/res/xml-sw768dp/kbd_phone.xml
index fa9bf1bf4..947ede050 100644
--- a/java/res/xml-sw768dp/kbd_phone.xml
+++ b/java/res/xml-sw768dp/kbd_phone.xml
@@ -21,6 +21,7 @@
<Keyboard
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
latin:keyWidth="13.250%p"
+ latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
>
<include
latin:keyboardLayout="@xml/rows_phone" />
diff --git a/java/res/xml-sw768dp/kbd_phone_symbols.xml b/java/res/xml-sw768dp/kbd_phone_symbols.xml
index e1a359e84..dd9a6aebd 100644
--- a/java/res/xml-sw768dp/kbd_phone_symbols.xml
+++ b/java/res/xml-sw768dp/kbd_phone_symbols.xml
@@ -21,6 +21,7 @@
<Keyboard
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
latin:keyWidth="13.250%p"
+ latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
>
<!-- Tablet doesn't have phone symbols keyboard -->
<include
diff --git a/java/res/xml-sw768dp/kbd_thai.xml b/java/res/xml-sw768dp/kbd_thai.xml
deleted file mode 100644
index 593ccbd48..000000000
--- a/java/res/xml-sw768dp/kbd_thai.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 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.
-*/
--->
-
-<Keyboard
- xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
- latin:rowHeight="20%p"
- latin:verticalGap="2.95%p"
- latin:touchPositionCorrectionData="@null"
->
- <include
- latin:keyboardLayout="@xml/rows_thai" />
-</Keyboard>
diff --git a/java/res/xml-sw768dp/kbd_thai_symbols.xml b/java/res/xml-sw768dp/kbd_thai_symbols.xml
index e2e5f5d56..5ddf57446 100644
--- a/java/res/xml-sw768dp/kbd_thai_symbols.xml
+++ b/java/res/xml-sw768dp/kbd_thai_symbols.xml
@@ -21,8 +21,10 @@
<Keyboard
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
latin:rowHeight="20%p"
- latin:verticalGap="2.95%p"
- latin:touchPositionCorrectionData="@null"
+ latin:verticalGap="@fraction/key_bottom_gap_5row"
+ latin:keyLetterSize="@fraction/key_letter_ratio_5row"
+ latin:keyShiftedLetterHintRatio="@fraction/key_uppercase_letter_ratio_5row"
+ latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
>
<include
latin:keyboardLayout="@xml/rows_thai_symbols" />
diff --git a/java/res/xml-sw768dp/kbd_thai_symbols_shift.xml b/java/res/xml-sw768dp/kbd_thai_symbols_shift.xml
index a1358d4a2..135222b22 100644
--- a/java/res/xml-sw768dp/kbd_thai_symbols_shift.xml
+++ b/java/res/xml-sw768dp/kbd_thai_symbols_shift.xml
@@ -21,8 +21,10 @@
<Keyboard
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
latin:rowHeight="20%p"
- latin:verticalGap="2.95%p"
- latin:touchPositionCorrectionData="@null"
+ latin:verticalGap="@fraction/key_bottom_gap_5row"
+ latin:keyLetterSize="@fraction/key_letter_ratio_5row"
+ latin:keyShiftedLetterHintRatio="@fraction/key_uppercase_letter_ratio_5row"
+ latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
>
<include
latin:keyboardLayout="@xml/rows_thai_symbols_shift" />
diff --git a/java/res/xml-sw768dp/key_space.xml b/java/res/xml-sw768dp/key_space.xml
index 8968f080a..58e71d807 100644
--- a/java/res/xml-sw768dp/key_space.xml
+++ b/java/res/xml-sw768dp/key_space.xml
@@ -24,15 +24,36 @@
<switch>
<case
latin:languageCode="fa"
+ latin:languageSwitchKeyEnabled="true"
+ >
+ <Key
+ latin:keyStyle="languageSwitchKeyStyle" />
+ <Key
+ latin:keyStyle="spaceKeyStyle"
+ latin:keyWidth="24.141%p" />
+ <Key
+ latin:keyStyle="zwnjKeyStyle" />
+ </case>
+ <case
+ latin:languageCode="fa"
+ latin:languageSwitchKeyEnabled="false"
>
<Key
latin:keyStyle="spaceKeyStyle"
latin:keyWidth="32.188%p" />
- <!-- U+200C: "" ZERO WIDTH NON-JOINER
- U+200D: "" ZERO WIDTH JOINER -->
<Key
latin:keyStyle="zwnjKeyStyle" />
</case>
+ <case
+ latin:languageSwitchKeyEnabled="true"
+ >
+ <Key
+ latin:keyStyle="languageSwitchKeyStyle" />
+ <Key
+ latin:keyStyle="spaceKeyStyle"
+ latin:keyWidth="32.188%p" />
+ </case>
+ <!-- languageSwitchKeyEnabled="false" -->
<default>
<Key
latin:keyStyle="spaceKeyStyle"
diff --git a/java/res/xml-sw768dp/key_styles_common.xml b/java/res/xml-sw768dp/key_styles_common.xml
index 40082ac35..537e76800 100644
--- a/java/res/xml-sw768dp/key_styles_common.xml
+++ b/java/res/xml-sw768dp/key_styles_common.xml
@@ -76,7 +76,7 @@
<key-style
latin:styleName="spaceKeyStyle"
latin:code="!code/key_space"
- latin:keyActionFlags="noKeyPreview" />
+ latin:keyActionFlags="noKeyPreview|enableLongPress" />
<!-- U+200C: ZERO WIDTH NON-JOINER
U+200D: ZERO WIDTH JOINER -->
<key-style
@@ -100,6 +100,12 @@
latin:keyActionFlags="noKeyPreview"
latin:backgroundType="functional" />
<key-style
+ latin:styleName="languageSwitchKeyStyle"
+ latin:code="!code/key_language_switch"
+ latin:keyIcon="!icon/language_switch_key"
+ latin:keyActionFlags="noKeyPreview|altCodeWhileTyping|enableLongPress"
+ latin:altCode="!code/key_space" />
+ <key-style
latin:styleName="settingsKeyStyle"
latin:code="!code/key_settings"
latin:keyIcon="!icon/settings_key"
@@ -117,6 +123,17 @@
latin:keyLabelFlags="fontNormal|preserveCase"
latin:backgroundType="functional" />
</case>
+ <case
+ latin:keyboardLayoutSetElement="alphabet|alphabetAutomaticShifted|alphabetShiftLocked"
+ latin:navigateNext="true"
+ >
+ <key-style
+ latin:styleName="tabKeyStyle"
+ latin:code="!code/key_action_next"
+ latin:keyLabel="!text/label_tab_key"
+ latin:keyLabelFlags="fontNormal|preserveCase"
+ latin:backgroundType="functional" />
+ </case>
<default>
<key-style
latin:styleName="tabKeyStyle"
diff --git a/java/res/xml-sw768dp/row_dvorak4.xml b/java/res/xml-sw768dp/row_dvorak4.xml
index 0827815c9..8f9230d4a 100644
--- a/java/res/xml-sw768dp/row_dvorak4.xml
+++ b/java/res/xml-sw768dp/row_dvorak4.xml
@@ -25,8 +25,10 @@
latin:keyWidth="8.047%p"
latin:backgroundType="functional"
>
+ <!-- Note: This Spacer prevents the below key from being marked as a left edge key. -->
+ <Spacer
+ latin:keyWidth="5.782%p" />
<include
- latin:keyXPos="5.782%p"
latin:keyboardLayout="@xml/key_settings" />
<include
latin:keyboardLayout="@xml/key_shortcut" />
@@ -42,5 +44,8 @@
latin:keyboardLayout="@xml/key_dash" />
<include
latin:keyboardLayout="@xml/key_f2" />
+ <!-- Note: This Spacer prevents the above key from being marked as a right edge key. -->
+ <Spacer
+ latin:keyWidth="fillRight" />
</Row>
</merge>
diff --git a/java/res/xml-sw768dp/row_hebrew4.xml b/java/res/xml-sw768dp/row_hebrew4.xml
index 180c5641d..ae14f0296 100644
--- a/java/res/xml-sw768dp/row_hebrew4.xml
+++ b/java/res/xml-sw768dp/row_hebrew4.xml
@@ -25,8 +25,10 @@
latin:keyWidth="8.047%p"
latin:backgroundType="functional"
>
+ <!-- Note: This Spacer prevents the below key from being marked as a left edge key. -->
+ <Spacer
+ latin:keyWidth="5.782%p" />
<include
- latin:keyXPos="5.782%p"
latin:keyboardLayout="@xml/key_settings" />
<include
latin:keyboardLayout="@xml/key_shortcut" />
@@ -40,5 +42,8 @@
latin:keyboardLayout="@xml/keys_comma_period" />
<include
latin:keyboardLayout="@xml/key_f2" />
+ <!-- Note: This Spacer prevents the above key from being marked as a right edge key. -->
+ <Spacer
+ latin:keyWidth="fillRight" />
</Row>
</merge>
diff --git a/java/res/xml-sw768dp/row_qwerty4.xml b/java/res/xml-sw768dp/row_qwerty4.xml
index 92411f54e..f1f4214ca 100644
--- a/java/res/xml-sw768dp/row_qwerty4.xml
+++ b/java/res/xml-sw768dp/row_qwerty4.xml
@@ -25,8 +25,10 @@
latin:keyWidth="8.047%p"
latin:backgroundType="functional"
>
+ <!-- Note: This Spacer prevents the below key from being marked as a left edge key. -->
+ <Spacer
+ latin:keyWidth="5.782%p" />
<include
- latin:keyXPos="5.782%p"
latin:keyboardLayout="@xml/key_settings" />
<include
latin:keyboardLayout="@xml/key_shortcut" />
@@ -42,5 +44,8 @@
latin:keyboardLayout="@xml/key_dash" />
<include
latin:keyboardLayout="@xml/key_f2" />
+ <!-- Note: This Spacer prevents the above key from being marked as a right edge key. -->
+ <Spacer
+ latin:keyWidth="fillRight" />
</Row>
</merge>
diff --git a/java/res/xml-sw768dp/row_symbols4.xml b/java/res/xml-sw768dp/row_symbols4.xml
index 4e1c11994..b801a12a7 100644
--- a/java/res/xml-sw768dp/row_symbols4.xml
+++ b/java/res/xml-sw768dp/row_symbols4.xml
@@ -25,8 +25,10 @@
latin:keyWidth="8.047%p"
latin:backgroundType="functional"
>
+ <!-- Note: This Spacer prevents the below key from being marked as a left edge key. -->
+ <Spacer
+ latin:keyWidth="13.829%p" />
<Key
- latin:keyXPos="13.829%p"
latin:keyLabel="/" />
<include
latin:keyboardLayout="@xml/key_f1" />
@@ -39,6 +41,8 @@
latin:moreKeys="!text/more_keys_for_tablet_double_quote" />
<Key
latin:keyLabel="_" />
- <!-- Here is empty space. -->
+ <!-- Note: This Spacer prevents the above key from being marked as a right edge key. -->
+ <Spacer
+ latin:keyWidth="fillRight" />
</Row>
</merge>
diff --git a/java/res/xml-sw768dp/row_symbols_shift4.xml b/java/res/xml-sw768dp/row_symbols_shift4.xml
index 561351cab..f71864bc3 100644
--- a/java/res/xml-sw768dp/row_symbols_shift4.xml
+++ b/java/res/xml-sw768dp/row_symbols_shift4.xml
@@ -25,11 +25,14 @@
latin:keyWidth="8.047%p"
latin:backgroundType="functional"
>
- <!-- Here is empty space. -->
+ <!-- Note: This Spacer prevents the below key from being marked as a left edge key. -->
+ <Spacer
+ latin:keyWidth="29.923%p" />
<include
- latin:keyXPos="29.923%p"
latin:keyboardLayout="@xml/key_space"
latin:backgroundType="normal" />
- <!-- Here is empty space. -->
+ <!-- Note: This Spacer prevents the above key from being marked as a right edge key. -->
+ <Spacer
+ latin:keyWidth="fillRight" />
</Row>
</merge>
diff --git a/java/res/xml-sw768dp/rowkeys_thai_digits.xml b/java/res/xml-sw768dp/rowkeys_thai_digits.xml
index 512283096..55196ebc3 100644
--- a/java/res/xml-sw768dp/rowkeys_thai_digits.xml
+++ b/java/res/xml-sw768dp/rowkeys_thai_digits.xml
@@ -23,32 +23,42 @@
>
<!-- U+0E51: "๑" THAI DIGIT ONE -->
<Key
- latin:keyLabel="&#x0E51;" />
+ latin:keyLabel="&#x0E51;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0E52: "๒" THAI DIGIT TWO -->
<Key
- latin:keyLabel="&#x0E52;" />
+ latin:keyLabel="&#x0E52;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0E53: "๓" THAI DIGIT THREE -->
<Key
- latin:keyLabel="&#x0E53;" />
+ latin:keyLabel="&#x0E53;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0E54: "๔" THAI DIGIT FOUR -->
<Key
- latin:keyLabel="&#x0E54;" />
+ latin:keyLabel="&#x0E54;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0E55: "๕" THAI DIGIT FIVE -->
<Key
- latin:keyLabel="&#x0E55;" />
+ latin:keyLabel="&#x0E55;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0E56: "๖" THAI DIGIT SIX -->
<Key
- latin:keyLabel="&#x0E56;" />
+ latin:keyLabel="&#x0E56;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0E57: "๗" THAI DIGIT SEVEN -->
<Key
- latin:keyLabel="&#x0E57;" />
+ latin:keyLabel="&#x0E57;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0E58: "๘" THAI DIGIT EIGHT -->
<Key
- latin:keyLabel="&#x0E58;" />
+ latin:keyLabel="&#x0E58;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0E59: "๙" THAI DIGIT NINE -->
<Key
- latin:keyLabel="&#x0E59;" />
+ latin:keyLabel="&#x0E59;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0E50: "๐" THAI DIGIT ZERO -->
<Key
- latin:keyLabel="&#x0E50;" />
+ latin:keyLabel="&#x0E50;"
+ latin:keyLabelFlags="fontNormal" />
</merge>
diff --git a/java/res/xml-sw768dp/rows_east_slavic.xml b/java/res/xml-sw768dp/rows_east_slavic.xml
index 0316c76f6..a4287f162 100644
--- a/java/res/xml-sw768dp/rows_east_slavic.xml
+++ b/java/res/xml-sw768dp/rows_east_slavic.xml
@@ -33,9 +33,8 @@
<include
latin:keyboardLayout="@xml/rowkeys_east_slavic1"
latin:keyLabelFlags="disableAdditionalMoreKeys|disableKeyHintLabel" />
- <!-- U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN -->
<Key
- latin:keyLabel="&#x044A;" />
+ latin:keyLabel="!text/keylabel_for_east_slavic_row1_12" />
<Key
latin:keyStyle="deleteKeyStyle"
latin:keyWidth="fillRight" />
diff --git a/java/res/xml-sw768dp/rows_esperanto.xml b/java/res/xml-sw768dp/rows_esperanto.xml
deleted file mode 100644
index 0b3bb1fe0..000000000
--- a/java/res/xml-sw768dp/rows_esperanto.xml
+++ /dev/null
@@ -1,69 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 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.
-*/
--->
-
-<merge
- xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
- <include
- latin:keyboardLayout="@xml/key_styles_common" />
- <Row
- latin:keyWidth="8.282%p"
- >
- <Key
- latin:keyStyle="tabKeyStyle"
- latin:keyLabelFlags="alignLeft"
- latin:keyWidth="7.969%p" />
- <include
- latin:keyboardLayout="@xml/rowkeys_esperanto1"
- latin:keyLabelFlags="disableAdditionalMoreKeys|disableKeyHintLabel" />
- <Key
- latin:keyStyle="deleteKeyStyle"
- latin:keyWidth="fillRight"/>
- </Row>
- <Row
- latin:keyWidth="8.125%p"
- >
- <Key
- latin:keyStyle="toSymbolKeyStyle"
- latin:keyLabelFlags="alignLeft"
- latin:keyWidth="10.167%"/>
- <include
- latin:keyboardLayout="@xml/rowkeys_esperanto2" />
- <Key
- latin:keyStyle="enterKeyStyle"
- latin:keyWidth="fillRight" />
- </Row>
- <Row
- latin:keyWidth="8.047%p"
- >
- <Key
- latin:keyStyle="shiftKeyStyle"
- latin:keyWidth="13.829%p"/>
- <include
- latin:keyboardLayout="@xml/rowkeys_esperanto3" />
- <include
- latin:keyboardLayout="@xml/keys_comma_period" />
- <Key
- latin:keyStyle="shiftKeyStyle"
- latin:keyWidth="fillRight" />
- </Row>
- <include
- latin:keyboardLayout="@xml/row_qwerty4" />
-</merge>
diff --git a/java/res/xml-sw768dp/rows_number_normal.xml b/java/res/xml-sw768dp/rows_number_normal.xml
index 84910a88f..d4d7c722a 100644
--- a/java/res/xml-sw768dp/rows_number_normal.xml
+++ b/java/res/xml-sw768dp/rows_number_normal.xml
@@ -168,5 +168,8 @@
<Key
latin:keyLabel="#"
latin:keyStyle="numKeyStyle" />
+ <!-- Note: This Spacer prevents the above key from being marked as a right edge key. -->
+ <Spacer
+ latin:keyWidth="fillRight" />
</Row>
</merge>
diff --git a/java/res/xml-sw768dp/rows_thai.xml b/java/res/xml-sw768dp/rows_thai.xml
index 7721bc5a9..5f9b383f8 100644
--- a/java/res/xml-sw768dp/rows_thai.xml
+++ b/java/res/xml-sw768dp/rows_thai.xml
@@ -28,7 +28,7 @@
>
<include
latin:keyboardLayout="@xml/rowkeys_thai1"
- latin:keyXPos="11.508%p" />
+ latin:keyXPos="3.799%p" />
<Key
latin:keyStyle="deleteKeyStyle"
latin:keyWidth="fillRight"/>
@@ -42,9 +42,11 @@
latin:keyWidth="7.969%p" />
<include
latin:keyboardLayout="@xml/rowkeys_thai2" />
+ <include
+ latin:keyboardLayout="@xml/key_thai_kho_khuat" />
</Row>
<Row
- latin:keyWidth="7.125%p"
+ latin:keyWidth="7.079%p"
>
<Key
latin:keyStyle="toSymbolKeyStyle"
diff --git a/java/res/xml/kbd_10_10_7_symbols.xml b/java/res/xml/kbd_10_10_7_symbols.xml
index 7e075df48..4d9861b73 100644
--- a/java/res/xml/kbd_10_10_7_symbols.xml
+++ b/java/res/xml/kbd_10_10_7_symbols.xml
@@ -20,6 +20,7 @@
<Keyboard
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+ latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
>
<include
latin:keyboardLayout="@xml/rows_symbols" />
diff --git a/java/res/xml/kbd_10_10_7_symbols_shift.xml b/java/res/xml/kbd_10_10_7_symbols_shift.xml
index 25db3c84d..a2d67caf4 100644
--- a/java/res/xml/kbd_10_10_7_symbols_shift.xml
+++ b/java/res/xml/kbd_10_10_7_symbols_shift.xml
@@ -20,6 +20,7 @@
<Keyboard
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+ latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
>
<include
latin:keyboardLayout="@xml/rows_symbols_shift" />
diff --git a/java/res/xml/kbd_more_keys_keyboard_template.xml b/java/res/xml/kbd_more_keys_keyboard_template.xml
index 8e977c5ad..537973d03 100644
--- a/java/res/xml/kbd_more_keys_keyboard_template.xml
+++ b/java/res/xml/kbd_more_keys_keyboard_template.xml
@@ -1,19 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-/*
+/*
**
** Copyright 2008, 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
+** 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
+** 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
+** 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.
*/
-->
diff --git a/java/res/xml/kbd_number.xml b/java/res/xml/kbd_number.xml
index 8b0deea97..aa8872f26 100644
--- a/java/res/xml/kbd_number.xml
+++ b/java/res/xml/kbd_number.xml
@@ -21,6 +21,7 @@
<Keyboard
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
latin:keyWidth="26.67%p"
+ latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
>
<include
latin:keyboardLayout="@xml/rows_number" />
diff --git a/java/res/xml/kbd_pcqwerty.xml b/java/res/xml/kbd_pcqwerty.xml
index cebca4ff7..5155bc510 100644
--- a/java/res/xml/kbd_pcqwerty.xml
+++ b/java/res/xml/kbd_pcqwerty.xml
@@ -21,8 +21,10 @@
<Keyboard
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
latin:rowHeight="20%p"
- latin:verticalGap="3.20%p"
- latin:touchPositionCorrectionData="@null"
+ latin:verticalGap="@fraction/key_bottom_gap_5row"
+ latin:keyLetterSize="@fraction/key_letter_ratio_5row"
+ latin:keyShiftedLetterHintRatio="@fraction/key_uppercase_letter_ratio_5row"
+ latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
>
<include
latin:keyboardLayout="@xml/rows_pcqwerty" />
diff --git a/java/res/xml/kbd_pcqwerty_symbols.xml b/java/res/xml/kbd_pcqwerty_symbols.xml
index fd64e5bf4..bfb39e8aa 100644
--- a/java/res/xml/kbd_pcqwerty_symbols.xml
+++ b/java/res/xml/kbd_pcqwerty_symbols.xml
@@ -21,8 +21,10 @@
<Keyboard
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
latin:rowHeight="20%p"
- latin:verticalGap="3.20%p"
- latin:touchPositionCorrectionData="@null"
+ latin:verticalGap="@fraction/key_bottom_gap_5row"
+ latin:keyLetterSize="@fraction/key_letter_ratio_5row"
+ latin:keyShiftedLetterHintRatio="@fraction/key_uppercase_letter_ratio_5row"
+ latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
>
<include
latin:keyboardLayout="@xml/rows_pcqwerty_symbols" />
diff --git a/java/res/xml/kbd_phone.xml b/java/res/xml/kbd_phone.xml
index 91637b62c..dab3d494a 100644
--- a/java/res/xml/kbd_phone.xml
+++ b/java/res/xml/kbd_phone.xml
@@ -21,6 +21,7 @@
<Keyboard
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
latin:keyWidth="26.67%p"
+ latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
>
<include
latin:keyboardLayout="@xml/rows_phone" />
diff --git a/java/res/xml/kbd_phone_symbols.xml b/java/res/xml/kbd_phone_symbols.xml
index 7f59a855a..ba4e4646d 100644
--- a/java/res/xml/kbd_phone_symbols.xml
+++ b/java/res/xml/kbd_phone_symbols.xml
@@ -21,6 +21,7 @@
<Keyboard
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
latin:keyWidth="26.67%p"
+ latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
>
<include
latin:keyboardLayout="@xml/rows_phone_symbols" />
diff --git a/java/res/xml/kbd_symbols.xml b/java/res/xml/kbd_symbols.xml
index f6612a2f7..47e08d57f 100644
--- a/java/res/xml/kbd_symbols.xml
+++ b/java/res/xml/kbd_symbols.xml
@@ -20,6 +20,7 @@
<Keyboard
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+ latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
>
<include
latin:keyboardLayout="@xml/rows_symbols" />
diff --git a/java/res/xml/kbd_symbols_shift.xml b/java/res/xml/kbd_symbols_shift.xml
index 41a5571ef..932ec017e 100644
--- a/java/res/xml/kbd_symbols_shift.xml
+++ b/java/res/xml/kbd_symbols_shift.xml
@@ -20,6 +20,7 @@
<Keyboard
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+ latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
>
<include
latin:keyboardLayout="@xml/rows_symbols_shift" />
diff --git a/java/res/xml/kbd_thai.xml b/java/res/xml/kbd_thai.xml
index 058ca16a3..294bffb5b 100644
--- a/java/res/xml/kbd_thai.xml
+++ b/java/res/xml/kbd_thai.xml
@@ -20,6 +20,11 @@
<Keyboard
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+ latin:rowHeight="20%p"
+ latin:verticalGap="@fraction/key_bottom_gap_5row"
+ latin:keyLetterSize="@fraction/key_letter_ratio_5row"
+ latin:keyShiftedLetterHintRatio="@fraction/key_uppercase_letter_ratio_5row"
+ latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
>
<include
latin:keyboardLayout="@xml/rows_thai" />
diff --git a/java/res/xml/kbd_thai_symbols.xml b/java/res/xml/kbd_thai_symbols.xml
index 7e075df48..4d9861b73 100644
--- a/java/res/xml/kbd_thai_symbols.xml
+++ b/java/res/xml/kbd_thai_symbols.xml
@@ -20,6 +20,7 @@
<Keyboard
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+ latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
>
<include
latin:keyboardLayout="@xml/rows_symbols" />
diff --git a/java/res/xml/kbd_thai_symbols_shift.xml b/java/res/xml/kbd_thai_symbols_shift.xml
index 25db3c84d..a2d67caf4 100644
--- a/java/res/xml/kbd_thai_symbols_shift.xml
+++ b/java/res/xml/kbd_thai_symbols_shift.xml
@@ -20,6 +20,7 @@
<Keyboard
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+ latin:touchPositionCorrectionData="@array/touch_position_correction_data_default"
>
<include
latin:keyboardLayout="@xml/rows_symbols_shift" />
diff --git a/java/res/xml/key_styles_common.xml b/java/res/xml/key_styles_common.xml
index 622da2120..162119dab 100644
--- a/java/res/xml/key_styles_common.xml
+++ b/java/res/xml/key_styles_common.xml
@@ -22,23 +22,8 @@
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
>
<!-- Base key style for the key which may have settings or tab key as popup key. -->
- <switch>
- <case
- latin:clobberSettingsKey="true"
- >
- <key-style
- latin:styleName="f1MoreKeysStyle"
- latin:backgroundType="functional" />
- </case>
- <!-- clobberSettingsKey="false" -->
- <default>
- <key-style
- latin:styleName="f1MoreKeysStyle"
- latin:keyLabelFlags="hasPopupHint"
- latin:moreKeys="!text/settings_as_more_key"
- latin:backgroundType="functional" />
- </default>
- </switch>
+ <include
+ latin:keyboardLayout="@xml/key_styles_f1" />
<!-- Functional key styles -->
<switch>
<case
diff --git a/java/res/xml/key_styles_f1.xml b/java/res/xml/key_styles_f1.xml
new file mode 100644
index 000000000..8dfc3cb84
--- /dev/null
+++ b/java/res/xml/key_styles_f1.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+
+<merge
+ xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+ <!-- Base key style for the key which may have settings or tab key as popup key. -->
+ <!-- Kept as a separate file for cleaner overriding by an overlay. -->
+ <switch>
+ <case
+ latin:clobberSettingsKey="true"
+ >
+ <key-style
+ latin:styleName="f1MoreKeysStyle"
+ latin:backgroundType="functional" />
+ </case>
+ <!-- clobberSettingsKey="false" -->
+ <default>
+ <key-style
+ latin:styleName="f1MoreKeysStyle"
+ latin:keyLabelFlags="hasPopupHint"
+ latin:moreKeys="!text/settings_as_more_key"
+ latin:backgroundType="functional" />
+ </default>
+ </switch>
+</merge>
diff --git a/java/res/xml-sw600dp-land/kbd_thai.xml b/java/res/xml/key_thai_kho_khuat.xml
index b75980f2f..0ffd0f924 100644
--- a/java/res/xml-sw600dp-land/kbd_thai.xml
+++ b/java/res/xml/key_thai_kho_khuat.xml
@@ -18,12 +18,23 @@
*/
-->
-<Keyboard
+<merge
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
- latin:rowHeight="20%p"
- latin:verticalGap="3.20%p"
- latin:touchPositionCorrectionData="@null"
>
- <include
- latin:keyboardLayout="@xml/rows_thai" />
-</Keyboard>
+ <switch>
+ <case
+ latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted"
+ >
+ <!-- U+0E05: "ฅ" THAI CHARACTER KHO KHON -->
+ <Key
+ latin:keyLabel="&#x0E05;"
+ latin:keyLabelFlags="fontNormal" />
+ </case>
+ <default>
+ <!-- U+0E03: "ฃ" THAI CHARACTER KHO KHUAT -->
+ <Key
+ latin:keyLabel="&#x0E03;"
+ latin:keyLabelFlags="fontNormal" />
+ </default>
+ </switch>
+</merge>
diff --git a/java/res/xml/keyboard_layout_set_esperanto.xml b/java/res/xml/keyboard_layout_set_esperanto.xml
deleted file mode 100644
index 94a386d6c..000000000
--- a/java/res/xml/keyboard_layout_set_esperanto.xml
+++ /dev/null
@@ -1,42 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 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.
-*/
--->
-
-<KeyboardLayoutSet
- xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin">
- <Element
- latin:elementName="alphabet"
- latin:elementKeyboard="@xml/kbd_esperanto"
- latin:enableProximityCharsCorrection="true" />
- <Element
- latin:elementName="symbols"
- latin:elementKeyboard="@xml/kbd_symbols" />
- <Element
- latin:elementName="symbolsShifted"
- latin:elementKeyboard="@xml/kbd_symbols_shift" />
- <Element
- latin:elementName="phone"
- latin:elementKeyboard="@xml/kbd_phone" />
- <Element
- latin:elementName="phoneSymbols"
- latin:elementKeyboard="@xml/kbd_phone_symbols" />
- <Element
- latin:elementName="number"
- latin:elementKeyboard="@xml/kbd_number" />
-</KeyboardLayoutSet>
diff --git a/java/res/xml/method.xml b/java/res/xml/method.xml
index 8d4b85d9e..3d360a8f0 100644
--- a/java/res/xml/method.xml
+++ b/java/res/xml/method.xml
@@ -22,17 +22,19 @@
<!-- Supported subtypes
keyboard_locale: script_name/keyboard_layout_set[:keyboard_locale]
+ af: Afrikaans/qwerty
ar: Arabic/arabic
be: Belarusian/east_slavic
bg: Bulgarian/bulgarian
bg: Bulgarian/bulgarian_bds
+ ca: Catalan/spanish
cs: Czech/qwertz
da: Danish/nordic
de: German/qwertz
el: Greek/greek
en_US: English United States/qwerty
en_GB: English Great Britain/qwerty
- eo: Esperanto/esperanto
+ eo: Esperanto/spanish
es: Spanish/spanish
et: Estonian/nordic
fa: Persian/arabic
@@ -42,14 +44,16 @@
hi: Hindi/hindi
hr: Croatian/qwertz
hu: Hungarian/qwertz
+ in: Indonesian/qwerty # "id" is official language code of Indonesian.
is: Icelandic/qwerty
it: Italian/qwerty
- iw: Hebrew/hebrew
+ iw: Hebrew/hebrew # "he" is official language code of Hebrew.
ka: Georgian/georgian
ky: Kyrgyz/east_slavic
lt: Lithuanian/qwerty
lv: Latvian/qwerty
mk: Macedonian/south_slavic
+ ms: Malay/qwerty
nb: Norwegian Bokmål/nordic
nl: Dutch/qwerty
nl_BE: Dutch Belgium/azerty
@@ -61,14 +65,20 @@
sk: Slovak/qwerty
sl: Slovenian/qwerty
sr: Serbian/south_slavic
+ (sr-Latn: Serbian/qwerty) # not yet implemented.
sv: Swedish/nordic
+ sw: Swahili/qwerty
th: Thai/thai
+ tl: Tagalog/spanish
tr: Turkish/qwerty
uk: Ukrainian/east_slavic
vi: Vietnamese/qwerty
+ zu: Zulu/qwerty
zz: QWERTY/qwerty
-->
<!-- TODO: use <lang>_keyboard icon instead of a common keyboard icon. -->
+<!-- Note: SupportTouchPositionCorrection extra value is obsolete and maintained for backward
+ compatibility. -->
<!-- If IME doesn't have an applicable subtype, the first subtype will be used as a default
subtype.-->
<input-method xmlns:android="http://schemas.android.com/apk/res/android"
@@ -76,132 +86,176 @@
android:isDefault="@bool/im_is_default">
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_en_US"
+ android:subtypeId="-921088104"
android:imeSubtypeLocale="en_US"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="TrySuppressingImeSwitcher,AsciiCapable,SupportTouchPositionCorrection"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_en_GB"
+ android:subtypeId="-1337596075"
android:imeSubtypeLocale="en_GB"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="TrySuppressingImeSwitcher,AsciiCapable,SupportTouchPositionCorrection"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
+ android:subtypeId="1872175968"
+ android:imeSubtypeLocale="af"
+ android:imeSubtypeMode="keyboard"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
+ />
+ <subtype android:icon="@drawable/ic_subtype_keyboard"
+ android:label="@string/subtype_generic"
+ android:subtypeId="1494081088"
android:imeSubtypeLocale="ar"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="SupportTouchPositionCorrection"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
+ android:subtypeId="499361881"
android:imeSubtypeLocale="be"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=east_slavic"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
+ android:subtypeId="195674344"
android:imeSubtypeLocale="bg"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=bulgarian"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_bulgarian_bds"
+ android:subtypeId="1599191706"
android:imeSubtypeLocale="bg"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=bulgarian_bds"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
+ android:subtypeId="-756735787"
+ android:imeSubtypeLocale="ca"
+ android:imeSubtypeMode="keyboard"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable"
+ />
+ <subtype android:icon="@drawable/ic_subtype_keyboard"
+ android:label="@string/subtype_generic"
+ android:subtypeId="758984400"
android:imeSubtypeLocale="cs"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
+ android:subtypeId="770990173"
android:imeSubtypeLocale="da"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
+ android:subtypeId="774684257"
android:imeSubtypeLocale="de"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
+ android:subtypeId="242746067"
android:imeSubtypeLocale="el"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=greek"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
+ android:subtypeId="1083200842"
android:imeSubtypeLocale="eo"
android:imeSubtypeMode="keyboard"
- android:imeSubtypeExtraValue="KeyboardLayoutSet=esperanto,AsciiCapable"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
+ android:subtypeId="816242702"
android:imeSubtypeLocale="es"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
+ android:subtypeId="-332580523"
android:imeSubtypeLocale="et"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=nordic,AsciiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
+ android:subtypeId="-1100561836"
android:imeSubtypeLocale="fa"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=farsi"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
+ android:subtypeId="835636643"
android:imeSubtypeLocale="fi"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
+ android:subtypeId="843948332"
android:imeSubtypeLocale="fr"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
+ android:subtypeId="-354699631"
android:imeSubtypeLocale="fr_CA"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
+ android:subtypeId="963984255"
android:imeSubtypeLocale="hi"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=hindi"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
+ android:subtypeId="901206634"
android:imeSubtypeLocale="hr"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
+ android:subtypeId="903977197"
android:imeSubtypeLocale="hu"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
/>
+ <!-- Java uses the deprecated "in" code instead of the standard "id" code for Indonesian. -->
+ <subtype android:icon="@drawable/ic_subtype_keyboard"
+ android:label="@string/subtype_generic"
+ android:subtypeId="2108597344"
+ android:imeSubtypeLocale="in"
+ android:imeSubtypeMode="keyboard"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
+ />
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
+ android:subtypeId="2113214949"
android:imeSubtypeLocale="is"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
+ android:subtypeId="931682827"
android:imeSubtypeLocale="it"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
@@ -209,138 +263,205 @@
<!-- Java uses the deprecated "iw" code instead of the standard "he" code for Hebrew. -->
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
+ android:subtypeId="1727731901"
android:imeSubtypeLocale="iw"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="SupportTouchPositionCorrection"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
+ android:subtypeId="1846648426"
android:imeSubtypeLocale="ka"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=georgian"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
+ android:subtypeId="775494660"
android:imeSubtypeLocale="ky"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=east_slavic"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
+ android:subtypeId="-2094941373"
android:imeSubtypeLocale="lt"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
+ android:subtypeId="-2093094331"
android:imeSubtypeLocale="lv"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
+ android:subtypeId="-1353667716"
android:imeSubtypeLocale="mk"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=south_slavic"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
+ android:subtypeId="-2067235743"
+ android:imeSubtypeLocale="ms"
+ android:imeSubtypeMode="keyboard"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
+ />
+ <subtype android:icon="@drawable/ic_subtype_keyboard"
+ android:label="@string/subtype_generic"
+ android:subtypeId="1058205204"
android:imeSubtypeLocale="nb"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
+ android:subtypeId="1067440414"
android:imeSubtypeLocale="nl"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
+ android:subtypeId="1343007020"
android:imeSubtypeLocale="nl_BE"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=azerty,AsciiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
+ android:subtypeId="1124698716"
android:imeSubtypeLocale="pl"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
+ android:subtypeId="-889195354"
android:imeSubtypeLocale="pt_BR"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
+ android:subtypeId="-486540198"
android:imeSubtypeLocale="pt_PT"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
+ android:subtypeId="-1927784072"
android:imeSubtypeLocale="ro"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
+ android:subtypeId="1983547218"
android:imeSubtypeLocale="ru"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="SupportTouchPositionCorrection"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
+ android:subtypeId="-1902849005"
android:imeSubtypeLocale="sk"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
+ android:subtypeId="-1901925484"
android:imeSubtypeLocale="sl"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
+ android:subtypeId="2009405806"
android:imeSubtypeLocale="sr"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="SupportTouchPositionCorrection"
/>
+ <!-- TODO: Uncomment once we can handle IETF language tag with script name specified.
+ <subtype android:icon="@drawable/ic_subtype_keyboard"
+ android:label="@string/subtype_serbian_cyrillic"
+ android:subtypeId="XXXXXX"
+ android:imeSubtypeLocale="sr"
+ android:imeSubtypeMode="keyboard"
+ android:imeSubtypeExtraValue="SupportTouchPositionCorrection"
+ />
+ <subtype android:icon="@drawable/ic_subtype_keyboard"
+ android:label="@string/subtype_serbian_latin"
+ android:subtypeId="XXXXXX"
+ android:imeSubtypeLocale="sr-Latn"
+ android:imeSubtypeMode="keyboard"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
+ />
+ -->
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
+ android:subtypeId="1219821379"
android:imeSubtypeLocale="sv"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
+ android:subtypeId="-1891766753"
+ android:imeSubtypeLocale="sw"
+ android:imeSubtypeMode="keyboard"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
+ />
+ <subtype android:icon="@drawable/ic_subtype_keyboard"
+ android:label="@string/subtype_generic"
+ android:subtypeId="529847764"
android:imeSubtypeLocale="th"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=thai"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
+ android:subtypeId="-259881489"
+ android:imeSubtypeLocale="tl"
+ android:imeSubtypeMode="keyboard"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable"
+ />
+ <subtype android:icon="@drawable/ic_subtype_keyboard"
+ android:label="@string/subtype_generic"
+ android:subtypeId="1244756446"
android:imeSubtypeLocale="tr"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
+ android:subtypeId="1048856876"
android:imeSubtypeLocale="uk"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=east_slavic"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
+ android:subtypeId="-1818808594"
android:imeSubtypeLocale="vi"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
+ android:label="@string/subtype_generic"
+ android:subtypeId="-1693209738"
+ android:imeSubtypeLocale="zu"
+ android:imeSubtypeMode="keyboard"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
+ />
+ <subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_no_language_qwerty"
+ android:subtypeId="-1573262419"
android:imeSubtypeLocale="zz"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable,EnabledWhenDefaultIsNotAsciiCapable"
diff --git a/java/res/xml/prefs.xml b/java/res/xml/prefs.xml
index 137981949..9172b2415 100644
--- a/java/res/xml/prefs.xml
+++ b/java/res/xml/prefs.xml
@@ -24,6 +24,7 @@
<CheckBoxPreference
android:key="auto_cap"
android:title="@string/auto_cap"
+ android:summary="@string/auto_cap_summary"
android:persistent="true"
android:defaultValue="true" />
<CheckBoxPreference
@@ -78,22 +79,51 @@
android:defaultValue="@string/prefs_suggestion_visibility_default_value" />
</PreferenceCategory>
<PreferenceCategory
+ android:title="@string/gesture_typing_category"
+ android:key="gesture_typing_settings">
+ <CheckBoxPreference
+ android:key="gesture_input"
+ android:title="@string/gesture_input"
+ android:summary="@string/gesture_input_summary"
+ android:persistent="true"
+ android:defaultValue="true" />
+ <CheckBoxPreference
+ android:key="pref_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" />
+ <CheckBoxPreference
+ android:key="pref_gesture_preview_trail"
+ android:title="@string/gesture_preview_trail"
+ android:persistent="true"
+ android:defaultValue="true" />
+ </PreferenceCategory>
+ <PreferenceCategory
android:title="@string/misc_category"
android:key="misc_settings">
<CheckBoxPreference
- android:key="usability_study_mode"
- android:title="@string/prefs_usability_study_mode"
+ android:key="next_word_prediction"
+ android:title="@string/bigram_prediction"
+ android:summary="@string/bigram_prediction_summary"
android:persistent="true"
- android:defaultValue="false" />
+ android:defaultValue="true" />
<PreferenceScreen
android:key="pref_advanced_settings"
android:title="@string/advanced_settings"
android:summary="@string/advanced_settings_summary">
<CheckBoxPreference
- android:key="pref_suppress_language_switch_key"
- android:title="@string/suppress_language_switch_key"
+ android:key="pref_key_use_contacts_dict"
+ android:title="@string/use_contacts_dict"
+ android:summary="@string/use_contacts_dict_summary"
android:persistent="true"
- android:defaultValue="false" />
+ android:defaultValue="true" />
+ <CheckBoxPreference
+ 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="true" />
<CheckBoxPreference
android:key="pref_include_other_imes_in_language_switch_list"
android:title="@string/include_other_imes_in_language_switch_list"
@@ -108,30 +138,6 @@
<ListPreference
android:key="pref_key_preview_popup_dismiss_delay"
android:title="@string/key_preview_popup_dismiss_delay" />
- <CheckBoxPreference
- android:key="pref_key_use_contacts_dict"
- android:title="@string/use_contacts_dict"
- android:summary="@string/use_contacts_dict_summary"
- android:persistent="true"
- android:defaultValue="true" />
- <CheckBoxPreference
- android:key="next_word_suggestion"
- android:title="@string/bigram_suggestion"
- android:summary="@string/bigram_suggestion_summary"
- android:persistent="true"
- android:defaultValue="true" />
- <CheckBoxPreference
- android:key="next_word_prediction"
- android:title="@string/bigram_prediction"
- android:summary="@string/bigram_prediction_summary"
- android:persistent="true"
- android:defaultValue="true" />
- <CheckBoxPreference
- android:key="enable_span_insert"
- android:title="@string/enable_span_insert"
- android:summary="@string/enable_span_insert_summary"
- android:persistent="true"
- android:defaultValue="true" />
<PreferenceScreen
android:key="pref_vibration_duration_settings"
android:title="@string/prefs_keypress_vibration_duration_settings"/>
@@ -139,5 +145,10 @@
android:key="pref_keypress_sound_volume"
android:title="@string/prefs_keypress_sound_volume_settings" />
</PreferenceScreen>
+ <PreferenceScreen
+ android:key="debug_settings"
+ android:title="Debug settings"
+ android:persistent="true"
+ android:defaultValue="false" />
</PreferenceCategory>
</PreferenceScreen>
diff --git a/java/res/xml/prefs_for_debug.xml b/java/res/xml/prefs_for_debug.xml
index b926ed065..605a02f07 100644
--- a/java/res/xml/prefs_for_debug.xml
+++ b/java/res/xml/prefs_for_debug.xml
@@ -48,4 +48,10 @@
android:persistent="true"
android:defaultValue="false"
/>
+
+ <CheckBoxPreference
+ android:key="usability_study_mode"
+ android:title="@string/prefs_usability_study_mode"
+ android:persistent="true"
+ android:defaultValue="false" />
</PreferenceScreen>
diff --git a/java/res/xml/rowkeys_arabic1.xml b/java/res/xml/rowkeys_arabic1.xml
index b1bf790e4..a4bef83c6 100644
--- a/java/res/xml/rowkeys_arabic1.xml
+++ b/java/res/xml/rowkeys_arabic1.xml
@@ -26,13 +26,15 @@
<Key
latin:keyLabel="&#x0636;"
latin:keyHintLabel="1"
- latin:additionalMoreKeys="1,&#x0661;" />
+ latin:additionalMoreKeys="1,&#x0661;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0635: "ص" ARABIC LETTER SAD
U+0662: "٢" ARABIC-INDIC DIGIT TWO -->
<Key
latin:keyLabel="&#x0635;"
latin:keyHintLabel="2"
- latin:additionalMoreKeys="2,&#x0662;" />
+ latin:additionalMoreKeys="2,&#x0662;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0642: "ق" ARABIC LETTER QAF
U+06A8: "ڨ" ARABIC LETTER QAF WITH THREE DOTS ABOVE
U+0663: "٣" ARABIC-INDIC DIGIT THREE -->
@@ -41,7 +43,8 @@
latin:keyLabel="&#x0642;"
latin:keyHintLabel="3"
latin:additionalMoreKeys="3,&#x0663;"
- latin:moreKeys="&#x06A8;" />
+ latin:moreKeys="&#x06A8;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0641: "ف" ARABIC LETTER FEH
U+06A4: "ڤ" ARABIC LETTER VEH
U+06A2: "ڢ" ARABIC LETTER FEH WITH DOT MOVED BELOW
@@ -53,19 +56,22 @@
latin:keyLabel="&#x0641;"
latin:keyHintLabel="4"
latin:additionalMoreKeys="4,&#x0664;"
- latin:moreKeys="&#x06A4;,&#x06A2;,&#x06A5;" />
+ latin:moreKeys="&#x06A4;,&#x06A2;,&#x06A5;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+063A: "غ" ARABIC LETTER GHAIN
U+0665: "٥" ARABIC-INDIC DIGIT FIVE -->
<Key
latin:keyLabel="&#x063A;"
latin:keyHintLabel="5"
- latin:additionalMoreKeys="5,&#x0665;" />
+ latin:additionalMoreKeys="5,&#x0665;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0639: "ع" ARABIC LETTER AIN
U+0666: "٦" ARABIC-INDIC DIGIT SIX -->
<Key
latin:keyLabel="&#x0639;"
latin:keyHintLabel="6"
- latin:additionalMoreKeys="6,&#x0666;" />
+ latin:additionalMoreKeys="6,&#x0666;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0647: "ه" ARABIC LETTER HEH
U+FEEB: "ﻫ" ARABIC LETTER HEH INITIAL FORM
U+0647 U+200D: ARABIC LETTER HEH + ZERO WIDTH JOINER
@@ -74,19 +80,22 @@
latin:keyLabel="&#x0647;"
latin:keyHintLabel="7"
latin:additionalMoreKeys="7,&#x0667;"
- latin:moreKeys="&#xFEEB;|&#x0647;&#x200D;" />
+ latin:moreKeys="&#xFEEB;|&#x0647;&#x200D;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+062E: "خ" ARABIC LETTER KHAH
U+0668: "٨" ARABIC-INDIC DIGIT EIGHT -->
<Key
latin:keyLabel="&#x062E;"
latin:keyHintLabel="8"
- latin:additionalMoreKeys="8,&#x0668;" />
+ latin:additionalMoreKeys="8,&#x0668;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+062D: "ح" ARABIC LETTER HAH
U+0669: "٩" ARABIC-INDIC DIGIT NINE -->
<Key
latin:keyLabel="&#x062D;"
latin:keyHintLabel="9"
- latin:additionalMoreKeys="9,&#x0669;" />
+ latin:additionalMoreKeys="9,&#x0669;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+062C: "ج" ARABIC LETTER JEEM
U+0686: "چ" ARABIC LETTER TCHEH
U+0660: "٠" ARABIC-INDIC DIGIT ZERO -->
@@ -94,5 +103,6 @@
latin:keyLabel="&#x062C;"
latin:keyHintLabel="0"
latin:additionalMoreKeys="0,&#x0660;"
- latin:moreKeys="&#x0686;" />
+ latin:moreKeys="&#x0686;"
+ latin:keyLabelFlags="fontNormal" />
</merge>
diff --git a/java/res/xml/rowkeys_arabic2.xml b/java/res/xml/rowkeys_arabic2.xml
index f86aae014..d733f6411 100644
--- a/java/res/xml/rowkeys_arabic2.xml
+++ b/java/res/xml/rowkeys_arabic2.xml
@@ -26,21 +26,25 @@
<!-- TODO: DroidSansArabic lacks the glyph of U+069C ARABIC LETTER SEEN WITH THREE DOTS BELOW AND THREE DOTS ABOVE -->
<Key
latin:keyLabel="&#x0634;"
- latin:moreKeys="&#x069C;" />
+ latin:moreKeys="&#x069C;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0633: "س" ARABIC LETTER SEEN -->
<Key
- latin:keyLabel="&#x0633;" />
+ latin:keyLabel="&#x0633;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+064A: "ي" ARABIC LETTER YEH
U+0626: "ئ" ARABIC LETTER YEH WITH HAMZA ABOVE
U+0649: "ى" ARABIC LETTER ALEF MAKSURA -->
<Key
latin:keyLabel="&#x064A;"
- latin:moreKeys="&#x0626;,&#x0649;" />
+ latin:moreKeys="&#x0626;,&#x0649;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0628: "ب" ARABIC LETTER BEH
U+067E: "پ" ARABIC LETTER PEH -->
<Key
latin:keyLabel="&#x0628;"
- latin:moreKeys="&#x067E;" />
+ latin:moreKeys="&#x067E;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0644: "ل" ARABIC LETTER LAM
U+FEFB: "ﻻ" ARABIC LIGATURE LAM WITH ALEF ISOLATED FORM
U+0627: "ا" ARABIC LETTER ALEF
@@ -52,7 +56,8 @@
U+0622: "آ" ARABIC LETTER ALEF WITH MADDA ABOVE -->
<Key
latin:keyLabel="&#x0644;"
- latin:moreKeys="&#xFEFB;|&#x0644;&#x0627;,&#xFEF7;|&#x0644;&#x0623;,&#xFEF9;|&#x0644;&#x0625;,&#xFEF5;|&#x0644;&#x0622;" />
+ latin:moreKeys="&#xFEFB;|&#x0644;&#x0627;,&#xFEF7;|&#x0644;&#x0623;,&#xFEF9;|&#x0644;&#x0625;,&#xFEF5;|&#x0644;&#x0622;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0627: "ا" ARABIC LETTER ALEF
U+0621: "ء" ARABIC LETTER HAMZA
U+0671: "ٱ" ARABIC LETTER ALEF WASLA
@@ -61,23 +66,27 @@
U+0622: "آ" ARABIC LETTER ALEF WITH MADDA ABOVE -->
<Key
latin:keyLabel="&#x0627;"
- latin:moreKeys="&#x0621;,&#x0671;,&#x0623;,&#x0625;,&#x0622;" />
+ latin:moreKeys="&#x0621;,&#x0671;,&#x0623;,&#x0625;,&#x0622;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+062A: "ت" ARABIC LETTER TEH
U+062B: "ﺙ" ARABIC LETTER THEH -->
<Key
latin:keyLabel="&#x062A;"
- latin:moreKeys="&#x062B;" />
+ latin:moreKeys="&#x062B;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0646: "ن" ARABIC LETTER NOON -->
<Key
- latin:keyLabel="&#x0646;" />
+ latin:keyLabel="&#x0646;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0645: "م" ARABIC LETTER MEEM -->
<Key
- latin:keyLabel="&#x0645;" />
+ latin:keyLabel="&#x0645;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0643: "ك" ARABIC LETTER KAF
U+06AF: "گ" ARABIC LETTER GAF
U+06A9: "ک" ARABIC LETTER KEHEH -->
<Key
latin:keyLabel="&#x0643;"
latin:moreKeys="&#x06AF;,&#x06A9;"
- latin:keyWidth="fillRight" />
+ latin:keyLabelFlags="fontNormal" />
</merge>
diff --git a/java/res/xml/rowkeys_arabic3.xml b/java/res/xml/rowkeys_arabic3.xml
index 9e9eac0d9..e4e694812 100644
--- a/java/res/xml/rowkeys_arabic3.xml
+++ b/java/res/xml/rowkeys_arabic3.xml
@@ -23,30 +23,38 @@
>
<!-- U+0638: "ظ" ARABIC LETTER ZAH -->
<Key
- latin:keyLabel="&#x0638;" />
+ latin:keyLabel="&#x0638;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0637: "ط" ARABIC LETTER TAH -->
<Key
- latin:keyLabel="&#x0637;" />
+ latin:keyLabel="&#x0637;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0630: "ذ" ARABIC LETTER THAL -->
<Key
- latin:keyLabel="&#x0630;" />
+ latin:keyLabel="&#x0630;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+062F: "د" ARABIC LETTER DAL -->
<Key
- latin:keyLabel="&#x062F;" />
+ latin:keyLabel="&#x062F;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0632: "ز" ARABIC LETTER ZAIN
U+0698: "ژ" ARABIC LETTER JEH -->
<Key
latin:keyLabel="&#x0632;"
- latin:moreKeys="&#x0698;" />
+ latin:moreKeys="&#x0698;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0631: "ر" ARABIC LETTER REH -->
<Key
- latin:keyLabel="&#x0631;" />
+ latin:keyLabel="&#x0631;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0629: "ة" ARABIC LETTER TEH MARBUTA -->
<Key
- latin:keyLabel="&#x0629;" />
+ latin:keyLabel="&#x0629;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0648: "و" ARABIC LETTER WAW
U+0624: "ﺅ" ARABIC LETTER WAW WITH HAMZA ABOVE -->
<Key
latin:keyLabel="&#x0648;"
- latin:moreKeys="&#x0624;" />
+ latin:moreKeys="&#x0624;"
+ latin:keyLabelFlags="fontNormal" />
</merge>
diff --git a/java/res/xml/rowkeys_east_slavic1.xml b/java/res/xml/rowkeys_east_slavic1.xml
index 00cb6a973..c1b43bd36 100644
--- a/java/res/xml/rowkeys_east_slavic1.xml
+++ b/java/res/xml/rowkeys_east_slavic1.xml
@@ -47,7 +47,7 @@
latin:keyLabel="&#x0435;"
latin:keyHintLabel="5"
latin:additionalMoreKeys="5"
- latin:moreKeys="!text/more_keys_for_cyrillic_ye" />
+ latin:moreKeys="!text/more_keys_for_cyrillic_ie" />
<!-- U+043D: "н" CYRILLIC SMALL LETTER EN -->
<Key
latin:keyLabel="&#x043D;"
@@ -58,7 +58,8 @@
<Key
latin:keyLabel="&#x0433;"
latin:keyHintLabel="7"
- latin:additionalMoreKeys="7" />
+ latin:additionalMoreKeys="7"
+ latin:moreKeys="!text/more_keys_for_cyrillic_ghe" />
<!-- U+0448: "ш" CYRILLIC SMALL LETTER SHA -->
<Key
latin:keyLabel="&#x0448;"
@@ -75,6 +76,5 @@
latin:additionalMoreKeys="0" />
<!-- U+0445: "х" CYRILLIC SMALL LETTER HA -->
<Key
- latin:keyLabel="&#x0445;"
- latin:moreKeys="!text/more_keys_for_cyrillic_ha" />
+ latin:keyLabel="&#x0445;" />
</merge>
diff --git a/java/res/xml/rowkeys_east_slavic2.xml b/java/res/xml/rowkeys_east_slavic2.xml
index c635af2d9..9743727c1 100644
--- a/java/res/xml/rowkeys_east_slavic2.xml
+++ b/java/res/xml/rowkeys_east_slavic2.xml
@@ -52,7 +52,6 @@
<!-- U+0436: "ж" CYRILLIC SMALL LETTER ZHE -->
<Key
latin:keyLabel="&#x0436;" />
- <!-- U+044D: "э" CYRILLIC SMALL LETTER E -->
<Key
- latin:keyLabel="&#x044D;" />
+ latin:keyLabel="!text/keylabel_for_east_slavic_row2_11" />
</merge>
diff --git a/java/res/xml/rowkeys_esperanto1.xml b/java/res/xml/rowkeys_esperanto1.xml
deleted file mode 100644
index 6994d4b5e..000000000
--- a/java/res/xml/rowkeys_esperanto1.xml
+++ /dev/null
@@ -1,125 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 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.
-*/
--->
-
-<merge
- xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
- <!-- U+015D: "ŝ" LATIN SMALL LETTER S WITH CIRCUMFLEX -->
- <Key
- latin:keyLabel="&#x015D;"
- latin:keyHintLabel="1"
- latin:additionalMoreKeys="1"
- latin:moreKeys="q" />
- <!-- U+011D: "ĝ" LATIN SMALL LETTER G WITH CIRCUMFLEX
- U+0175: "ŵ" LATIN SMALL LETTER W WITH CIRCUMFLEX -->
- <Key
- latin:keyLabel="&#x011D;"
- latin:keyHintLabel="2"
- latin:additionalMoreKeys="2"
- latin:moreKeys="w,&#x0175;" />
- <!-- U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
- U+011B: "ě" LATIN SMALL LETTER E WITH CARON
- U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
- U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
- U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
- U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
- U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
- U+0113: "ē" LATIN SMALL LETTER E WITH MACRON -->
- <Key
- latin:keyLabel="e"
- latin:keyHintLabel="3"
- latin:additionalMoreKeys="3"
- latin:moreKeys="&#x00E9;,&#x011B;,&#x00E8;,&#x00EA;,&#x00EB;,&#x0119;,&#x0117;,&#x0113;" />
- <!-- U+0159: "ř" LATIN SMALL LETTER R WITH CARON
- U+0155: "ŕ" LATIN SMALL LETTER R WITH ACUTE
- U+0157: "ŗ" LATIN SMALL LETTER R WITH CEDILLA -->
- <Key
- latin:keyLabel="r"
- latin:keyHintLabel="4"
- latin:additionalMoreKeys="4"
- latin:moreKeys="&#x0159;,&#x0155;,&#x0157;" />
- <!-- U+0165: "ť" LATIN SMALL LETTER T WITH CARON
- U+021B: "ț" LATIN SMALL LETTER T WITH COMMA BELOW
- U+0163: "ţ" LATIN SMALL LETTER T WITH CEDILLA
- U+0167: "ŧ" LATIN SMALL LETTER T WITH STROKE -->
- <Key
- latin:keyLabel="t"
- latin:keyHintLabel="5"
- latin:additionalMoreKeys="5"
- latin:moreKeys="&#x0165;,&#x021B;,&#x0163;,&#x0167;" />
- <!-- U+016D: "ŭ" LATIN SMALL LETTER U WITH BREVE
- U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
- U+0177: "ŷ" LATIN SMALL LETTER Y WITH CIRCUMFLEX
- U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS
- U+00FE: "þ" LATIN SMALL LETTER THORN -->
- <Key
- latin:keyLabel="&#x016D;"
- latin:keyHintLabel="6"
- latin:additionalMoreKeys="6"
- latin:moreKeys="y,&#x00FD;,&#x0177;,&#x00FF;,&#x00FE;" />
- <!-- U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
- U+016F: "ů" LATIN SMALL LETTER U WITH RING ABOVE
- U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
- U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
- U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
- U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
- U+0169: "ũ" LATIN SMALL LETTER U WITH TILDE
- U+0171: "ű" LATIN SMALL LETTER U WITH DOUBLE ACUTE
- U+0173: "ų" LATIN SMALL LETTER U WITH OGONEK
- U+00B5: "µ" MICRO SIGN -->
- <Key
- latin:keyLabel="u"
- latin:keyHintLabel="7"
- latin:additionalMoreKeys="7"
- latin:moreKeys="&#x00FA;,&#x016F;,&#x00FB;,&#x00FC;,&#x00F9;,&#x016B;,&#x0169;,&#x0171;,&#x0173;,&#x00B5;" />
- <!-- U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
- U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
- U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
- U+0129: "ĩ" LATIN SMALL LETTER I WITH TILDE
- U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
- U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
- U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
- U+0131: "ı" LATIN SMALL LETTER DOTLESS I
- U+0133: "ij" LATIN SMALL LIGATURE IJ -->
- <Key
- latin:keyLabel="i"
- latin:keyHintLabel="8"
- latin:additionalMoreKeys="8"
- latin:moreKeys="&#x00ED;,&#x00EE;,&#x00EF;,&#x0129;,&#x00EC;,&#x012F;,&#x012B;,&#x0131;,&#x0133;" />
- <!-- U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
- U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
- U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
- U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
- U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
- U+0153: "œ" LATIN SMALL LIGATURE OE
- U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
- U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
- U+0151: "ő" LATIN SMALL LETTER O WITH DOUBLE ACUTE
- U+00BA: "º" MASCULINE ORDINAL INDICATOR -->
- <Key
- latin:keyLabel="o"
- latin:keyHintLabel="9"
- latin:additionalMoreKeys="9"
- latin:moreKeys="&#x00F3;,&#x00F6;,&#x00F4;,&#x00F2;,&#x00F5;,&#x0153;,&#x00F8;,&#x014D;,&#x0151;,&#x00BA;" />
- <Key
- latin:keyLabel="p"
- latin:keyHintLabel="0"
- latin:additionalMoreKeys="0" />
-</merge>
diff --git a/java/res/xml/rowkeys_esperanto2.xml b/java/res/xml/rowkeys_esperanto2.xml
deleted file mode 100644
index ebc968a70..000000000
--- a/java/res/xml/rowkeys_esperanto2.xml
+++ /dev/null
@@ -1,83 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 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.
-*/
--->
-
-<merge
- xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
- <!-- U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
- U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
- U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
- U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
- U+00E6: "æ" LATIN SMALL LETTER AE
- U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
- U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
- U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
- U+0103: "ă" LATIN SMALL LETTER A WITH BREVE
- U+0105: "ą" LATIN SMALL LETTER A WITH OGONEK
- U+00AA: "ª" FEMININE ORDINAL INDICATOR -->
- <Key
- latin:keyLabel="a"
- latin:moreKeys="&#x00E1;,&#x00E0;,&#x00E2;,&#x00E4;,&#x00E6;,&#x00E3;,&#x00E5;,&#x0101;,&#x0103;,&#x0105;,&#x00AA;" />
- <!-- U+00DF: "ß" LATIN SMALL LETTER SHARP S
- U+0161: "š" LATIN SMALL LETTER S WITH CARON
- U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
- U+0219: "ș" LATIN SMALL LETTER S WITH COMMA BELOW
- U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA -->
- <Key
- latin:keyLabel="s"
- latin:moreKeys="&#x00DF;,&#x0161;,&#x015B;,&#x0219;,&#x015F;" />
- <!-- U+00F0: "ð" LATIN SMALL LETTER ETH
- U+010F: "ď" LATIN SMALL LETTER D WITH CARON
- U+0111: "đ" LATIN SMALL LETTER D WITH STROKE -->
- <Key
- latin:keyLabel="d"
- latin:moreKeys="&#x00F0;,&#x010F;,&#x0111;" />
- <Key
- latin:keyLabel="f" />
- <!-- U+011F: "ğ" LATIN SMALL LETTER G WITH BREVE
- U+0121: "ġ" LATIN SMALL LETTER G WITH DOT ABOVE
- U+0123: "ģ" LATIN SMALL LETTER G WITH CEDILLA -->
- <Key
- latin:keyLabel="g"
- latin:moreKeys="&#x011F;,&#x0121;,&#x0123;" />
- <!-- U+0125: "ĥ" LATIN SMALL LETTER H WITH CIRCUMFLEX
- U+0127: "ħ" LATIN SMALL LETTER H WITH STROKE -->
- <Key
- latin:keyLabel="h"
- latin:moreKeys="&#x0125;,&#x0127;" />
- <Key
- latin:keyLabel="j" />
- <!-- U+0137: "ķ" LATIN SMALL LETTER K WITH CEDILLA
- U+0138: "ĸ" LATIN SMALL LETTER KRA -->
- <Key
- latin:keyLabel="k"
- latin:moreKeys="&#x0137;,&#x0138;" />
- <!-- U+013A: "ĺ" LATIN SMALL LETTER L WITH ACUTE
- U+013C: "ļ" LATIN SMALL LETTER L WITH CEDILLA
- U+013E: "ľ" LATIN SMALL LETTER L WITH CARON
- U+0140: "ŀ" LATIN SMALL LETTER L WITH MIDDLE DOT
- U+0142: "ł" LATIN SMALL LETTER L WITH STROKE -->
- <Key
- latin:keyLabel="l"
- latin:moreKeys="&#x013A;,&#x013C;,&#x013E;,&#x0140;,&#x0142;" />
- <!-- U+0135: "ĵ" LATIN SMALL LETTER J WITH CIRCUMFLEX -->
- <Key
- latin:keyLabel="&#x0135;" />
-</merge>
diff --git a/java/res/xml/rowkeys_esperanto3.xml b/java/res/xml/rowkeys_esperanto3.xml
deleted file mode 100644
index b2eab8d60..000000000
--- a/java/res/xml/rowkeys_esperanto3.xml
+++ /dev/null
@@ -1,58 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 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.
-*/
--->
-
-<merge
- xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
- <!-- U+017A: "ź" LATIN SMALL LETTER Z WITH ACUTE
- U+017C: "ż" LATIN SMALL LETTER Z WITH DOT ABOVE
- U+017E: "ž" LATIN SMALL LETTER Z WITH CARON -->
- <Key
- latin:keyLabel="z"
- latin:moreKeys="&#x017A;,&#x017C;,&#x017E;" />
- <!-- U+0109: "ĉ" LATIN SMALL LETTER C WITH CIRCUMFLEX -->
- <Key
- latin:keyLabel="&#x0109;"
- latin:moreKeys="x" />
- <!-- U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
- U+010D: "č" LATIN SMALL LETTER C WITH CARON
- U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
- U+010B: "ċ" LATIN SMALL LETTER C WITH DOT ABOVE -->
- <Key
- latin:keyLabel="c"
- latin:moreKeys="&#x0107;,&#x010D;,&#x00E7;,&#x010B;" />
- <!-- U+0175: "ŵ" LATIN SMALL LETTER W WITH CIRCUMFLEX -->
- <Key
- latin:keyLabel="v"
- latin:moreKeys="w,&#x0175;" />
- <Key
- latin:keyLabel="b" />
- <!-- U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
- U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
- U+0146: "ņ" LATIN SMALL LETTER N WITH CEDILLA
- U+0148: "ň" LATIN SMALL LETTER N WITH CARON
- U+0149: "ʼn" LATIN SMALL LETTER N PRECEDED BY APOSTROPHE
- U+014B: "ŋ" LATIN SMALL LETTER ENG -->
- <Key
- latin:keyLabel="n"
- latin:moreKeys="&#x00F1;,&#x0144;,&#x0146;,&#x0148;,&#x0149;,&#x014B;" />
- <Key
- latin:keyLabel="m" />
-</merge>
diff --git a/java/res/xml/rowkeys_farsi1.xml b/java/res/xml/rowkeys_farsi1.xml
index 840b048f7..0ccf1ab54 100644
--- a/java/res/xml/rowkeys_farsi1.xml
+++ b/java/res/xml/rowkeys_farsi1.xml
@@ -28,31 +28,36 @@
latin:keyLabel="&#x0635;"
latin:moreKeys="&#x0636;,%"
latin:keyHintLabel="&#x06F1;"
- latin:additionalMoreKeys="&#x06F1;,1" />
+ latin:additionalMoreKeys="&#x06F1;,1"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0642: "ق" ARABIC LETTER QAF
U+06F2: "۲" EXTENDED ARABIC-INDIC DIGIT TWO -->
<Key
latin:keyLabel="&#x0642;"
latin:keyHintLabel="&#x06F2;"
- latin:additionalMoreKeys="&#x06F2;,2" />
+ latin:additionalMoreKeys="&#x06F2;,2"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0641: "ف" ARABIC LETTER FEH
U+06F3: "۳" EXTENDED ARABIC-INDIC DIGIT THREE -->
<Key
latin:keyLabel="&#x0641;"
latin:keyHintLabel="&#x06F3;"
- latin:additionalMoreKeys="&#x06F3;,3" />
+ latin:additionalMoreKeys="&#x06F3;,3"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+063A: "غ" ARABIC LETTER GHAIN
U+06F4: "۴" EXTENDED ARABIC-INDIC DIGIT FOUR -->
<Key
latin:keyLabel="&#x063A;"
latin:keyHintLabel="&#x06F4;"
- latin:additionalMoreKeys="&#x06F4;,4" />
+ latin:additionalMoreKeys="&#x06F4;,4"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0639: "ع" ARABIC LETTER AIN
U+06F5: "۵" EXTENDED ARABIC-INDIC DIGIT FIVE -->
<Key
latin:keyLabel="&#x0639;"
latin:keyHintLabel="&#x06F5;"
- latin:additionalMoreKeys="&#x06F5;,5" />
+ latin:additionalMoreKeys="&#x06F5;,5"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0647: "ه" ARABIC LETTER HEH
U+FEEB: "ﻫ" ARABIC LETTER HEH INITIAL FORM
U+0647/U+200D: ARABIC LETTER HEH + ZERO WIDTH JOINER
@@ -63,29 +68,34 @@
latin:keyLabel="&#x0647;"
latin:moreKeys="&#xFEEB;|&#x0647;&#x200D;,&#x0647;&#x0654;,&#x0629;,%"
latin:keyHintLabel="&#x06F6;"
- latin:additionalMoreKeys="&#x06F6;,6" />
+ latin:additionalMoreKeys="&#x06F6;,6"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+062E: "خ" ARABIC LETTER KHAH
U+06F7: "۷" EXTENDED ARABIC-INDIC DIGIT SEVEN -->
<Key
latin:keyLabel="&#x062E;"
latin:keyHintLabel="&#x06F7;"
- latin:additionalMoreKeys="&#x06F7;,7" />
+ latin:additionalMoreKeys="&#x06F7;,7"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+062D: "ح" ARABIC LETTER HAH
U+06F8: "۸" EXTENDED ARABIC-INDIC DIGIT EIGHT -->
<Key
latin:keyLabel="&#x062D;"
latin:keyHintLabel="&#x06F8;"
- latin:additionalMoreKeys="&#x06F8;,8" />
+ latin:additionalMoreKeys="&#x06F8;,8"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+062C: "ج" ARABIC LETTER JEEM
U+06F9: "۹" EXTENDED ARABIC-INDIC DIGIT NINE -->
<Key
latin:keyLabel="&#x062C;"
latin:keyHintLabel="&#x06F9;"
- latin:additionalMoreKeys="&#x06F9;,9" />
+ latin:additionalMoreKeys="&#x06F9;,9"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0686: "چ" ARABIC LETTER TCHEH
U+06F0: "۰" EXTENDED ARABIC-INDIC DIGIT ZERO -->
<Key
latin:keyLabel="&#x0686;"
latin:keyHintLabel="&#x06F0;"
- latin:additionalMoreKeys="&#x06F0;,0" />
+ latin:additionalMoreKeys="&#x06F0;,0"
+ latin:keyLabelFlags="fontNormal" />
</merge>
diff --git a/java/res/xml/rowkeys_farsi2.xml b/java/res/xml/rowkeys_farsi2.xml
index 21548936e..4b6abe2ab 100644
--- a/java/res/xml/rowkeys_farsi2.xml
+++ b/java/res/xml/rowkeys_farsi2.xml
@@ -23,12 +23,14 @@
>
<!-- U+0634: "ش" ARABIC LETTER SHEEN -->
<Key
- latin:keyLabel="&#x0634;" />
+ latin:keyLabel="&#x0634;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0633: "س" ARABIC LETTER SEEN
U+0636: "ض" ARABIC LETTER DAD -->
<Key
latin:keyLabel="&#x0633;"
- latin:moreKeys="&#x0636;" />
+ latin:moreKeys="&#x0636;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+06CC: "ی" ARABIC LETTER FARSI YEH
U+0626: "ئ" ARABIC LETTER YEH WITH HAMZA ABOVE
U+064A: "ي" ARABIC LETTER YEH
@@ -36,13 +38,16 @@
U+0649: "ى" ARABIC LETTER ALEF MAKSURA -->
<Key
latin:keyLabel="&#x06CC;"
- latin:moreKeys="&#x0626;,&#x064A;,&#xFBE8;|&#x0649;" />
+ latin:moreKeys="&#x0626;,&#x064A;,&#xFBE8;|&#x0649;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0628: "ب" ARABIC LETTER BEH -->
<Key
- latin:keyLabel="&#x0628;" />
+ latin:keyLabel="&#x0628;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0644: "ل" ARABIC LETTER LAM -->
<Key
- latin:keyLabel="&#x0644;" />
+ latin:keyLabel="&#x0644;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0627: "ا" ARABIC LETTER ALEF
U+0621: "ء" ARABIC LETTER HAMZA
U+0622: "آ" ARABIC LETTER ALEF WITH MADDA ABOVE
@@ -51,22 +56,27 @@
U+0625: "إ" ARABIC LETTER ALEF WITH HAMZA BELOW -->
<Key
latin:keyLabel="&#x0627;"
- latin:moreKeys="&#x0621;,&#x0622;,&#x0623;,&#x0671;,&#x0625;" />
+ latin:moreKeys="&#x0621;,&#x0622;,&#x0623;,&#x0671;,&#x0625;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+062A: "ت" ARABIC LETTER TEH
U+062B: "ﺙ" ARABIC LETTER THEH
U+0629: "ة": ARABIC LETTER TEH MARBUTA -->
<Key
latin:keyLabel="&#x062A;"
- latin:moreKeys="&#x062B;,&#x0629;" />
+ latin:moreKeys="&#x062B;,&#x0629;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0646: "ن" ARABIC LETTER NOON -->
<Key
- latin:keyLabel="&#x0646;" />
+ latin:keyLabel="&#x0646;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0645: "م" ARABIC LETTER MEEM -->
<Key
- latin:keyLabel="&#x0645;" />
+ latin:keyLabel="&#x0645;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+06A9: "ک" ARABIC LETTER KEHEH
U+0643: "ك" ARABIC LETTER KAF -->
<Key
latin:keyLabel="&#x06A9;"
- latin:moreKeys="&#x0643;" />
+ latin:moreKeys="&#x0643;"
+ latin:keyLabelFlags="fontNormal" />
</merge>
diff --git a/java/res/xml/rowkeys_farsi3.xml b/java/res/xml/rowkeys_farsi3.xml
index 29c35134c..7d2e81f7d 100644
--- a/java/res/xml/rowkeys_farsi3.xml
+++ b/java/res/xml/rowkeys_farsi3.xml
@@ -25,30 +25,38 @@
U+0638: "ظ" ARABIC LETTER ZAH -->
<Key
latin:keyLabel="&#x0637;"
- latin:moreKeys="&#x0638;" />
+ latin:moreKeys="&#x0638;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0632: "ز" ARABIC LETTER ZAIN
U+0698: "ژ" ARABIC LETTER JEH -->
<Key
latin:keyLabel="&#x0632;"
- latin:moreKeys="&#x0698;" />
+ latin:moreKeys="&#x0698;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0631: "ر" ARABIC LETTER REH -->
<Key
- latin:keyLabel="&#x0631;" />
+ latin:keyLabel="&#x0631;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0630: "ذ" ARABIC LETTER THAL -->
<Key
- latin:keyLabel="&#x0630;" />
+ latin:keyLabel="&#x0630;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+062F: "د" ARABIC LETTER DAL -->
<Key
- latin:keyLabel="&#x062F;" />
+ latin:keyLabel="&#x062F;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+067E: "پ" ARABIC LETTER PEH -->
<Key
- latin:keyLabel="&#x067E;" />
+ latin:keyLabel="&#x067E;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0648: "و" ARABIC LETTER WAW
U+0624: "ؤ" ARABIC LETTER WAW WITH HAMZA ABOVE -->
<Key
latin:keyLabel="&#x0648;"
- latin:moreKeys="&#x0624;" />
+ latin:moreKeys="&#x0624;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+06AF: "گ" ARABIC LETTER GAF -->
<Key
- latin:keyLabel="&#x06AF;" />
+ latin:keyLabel="&#x06AF;"
+ latin:keyLabelFlags="fontNormal" />
</merge>
diff --git a/java/res/xml/rowkeys_hindi1.xml b/java/res/xml/rowkeys_hindi1.xml
index 656ba01c4..11208045c 100644
--- a/java/res/xml/rowkeys_hindi1.xml
+++ b/java/res/xml/rowkeys_hindi1.xml
@@ -29,50 +29,61 @@
U+0912/U+0902: "ऒं" DEVANAGARI LETTER SHORT O//DEVANAGARI SIGN ANUSVARA -->
<Key
latin:keyLabel="&#x0914;"
- latin:moreKeys="&#x0912;&#x0902;" />
+ latin:moreKeys="&#x0912;&#x0902;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0910: "ऐ" DEVANAGARI LETTER AI
U+0910/U+0902: "ऐं" DEVANAGARI LETTER AI/DEVANAGARI SIGN ANUSVARA -->
<Key
latin:keyLabel="&#x0910;"
- latin:moreKeys="&#x0910;&#x0902;" />
+ latin:moreKeys="&#x0910;&#x0902;"
+ 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="&#x0906;"
- latin:moreKeys="&#x0906;&#x0902;,&#x0906;&#x0901;" />
+ latin:moreKeys="&#x0906;&#x0902;,&#x0906;&#x0901;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0908: "ई" DEVANAGARI LETTER II
U+0908/U+0902: "ईं" DEVANAGARI LETTER II/DEVANAGARI SIGN ANUSVARA -->
<Key
latin:keyLabel="&#x0908;"
- latin:moreKeys="&#x0908;&#x0902;" />
+ latin:moreKeys="&#x0908;&#x0902;"
+ 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="&#x090A;"
- latin:moreKeys="&#x090A;&#x0902;,&#x090A;&#x0901;" />
+ latin:moreKeys="&#x090A;&#x0902;,&#x090A;&#x0901;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+092D: "भ" DEVANAGARI LETTER BHA -->
<Key
- latin:keyLabel="&#x092D;" />
+ latin:keyLabel="&#x092D;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0903: "ः" DEVANAGARI SIGN VISARGA -->
<Key
- latin:keyLabel="&#x0903;" />
+ latin:keyLabel="&#x0903;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0918: "घ" DEVANAGARI LETTER GHA -->
<Key
- latin:keyLabel="&#x0918;" />
+ latin:keyLabel="&#x0918;"
+ 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="&#x0927;"
- latin:moreKeys="&#x0915;&#x094D;&#x0937;,&#x0936;&#x094D;&#x0930;" />
+ latin:moreKeys="&#x0915;&#x094D;&#x0937;,&#x0936;&#x094D;&#x0930;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+091D: "झ" DEVANAGARI LETTER JHA -->
<Key
- latin:keyLabel="&#x091D;" />
+ latin:keyLabel="&#x091D;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0922: "ढ" DEVANAGARI LETTER DDHA -->
<Key
- latin:keyLabel="&#x0922;" />
+ latin:keyLabel="&#x0922;"
+ latin:keyLabelFlags="fontNormal" />
</case>
<default>
<!-- U+094C: "ौ" DEVANAGARI VOWEL SIGN AU
@@ -82,7 +93,8 @@
latin:keyLabel="&#x094C;"
latin:moreKeys="&#x094C;&#x0902;,%"
latin:keyHintLabel="1"
- latin:additionalMoreKeys="&#x0967;,1" />
+ latin:additionalMoreKeys="&#x0967;,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="&#x0948;"
latin:moreKeys="&#x0948;&#x0902;,%"
latin:keyHintLabel="2"
- latin:additionalMoreKeys="&#x0968;,2" />
+ latin:additionalMoreKeys="&#x0968;,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="&#x093E;"
latin:moreKeys="&#x093E;&#x0902;,&#x093E;&#x0901;,%"
latin:keyHintLabel="3"
- latin:additionalMoreKeys="&#x0969;,3" />
+ latin:additionalMoreKeys="&#x0969;,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="&#x0940;"
latin:moreKeys="&#x0940;&#x0902;,%"
latin:keyHintLabel="4"
- latin:additionalMoreKeys="&#x096A;,4" />
+ latin:additionalMoreKeys="&#x096A;,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="&#x0942;"
latin:moreKeys="&#x0942;&#x0902;,&#x0942;&#x0901;,%"
latin:keyHintLabel="5"
- latin:additionalMoreKeys="&#x096B;,5" />
+ latin:additionalMoreKeys="&#x096B;,5"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+092C: "ब" DEVANAGARI LETTER BA
U+092C/U+0952: "ब॒" DEVANAGARI LETTER BA/DEVANAGARI STRESS SIGN ANUDATTA -->
<Key
latin:keyLabel="&#x092C;"
latin:moreKeys="&#x092C;&#x0952;,%"
latin:keyHintLabel="6"
- latin:additionalMoreKeys="&#x096C;,6" />
+ latin:additionalMoreKeys="&#x096C;,6"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0939: "ह" DEVANAGARI LETTER HA
U+096D: "७" DEVANAGARI DIGIT SEVEN -->
<Key
latin:keyLabel="&#x0939;"
latin:keyHintLabel="7"
- latin:additionalMoreKeys="&#x096D;,7" />
+ latin:additionalMoreKeys="&#x096D;,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="&#x0917;"
latin:moreKeys="&#x091C;&#x094D;&#x091E;,&#x0917;&#x093C;,&#x0917;&#x0952;,%"
latin:keyHintLabel="8"
- latin:additionalMoreKeys="&#x096E;,8" />
+ latin:additionalMoreKeys="&#x096E;,8"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0926: "द" DEVANAGARI LETTER DA
U+096F: "९" DEVANAGARI DIGIT NINE -->
<Key
latin:keyLabel="&#x0926;"
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="&#x091C;"
latin:moreKeys="&#x091C;&#x0952;,&#x091C;&#x094D;&#x091E;,&#x091C;&#x093C;,%"
latin:keyHintLabel="0"
- latin:additionalMoreKeys="&#x0966;,0" />
+ latin:additionalMoreKeys="&#x0966;,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="&#x0921;"
- latin:moreKeys="&#x0921;&#x0952;,&#x0921;&#x093C;" />
+ latin:moreKeys="&#x0921;&#x0952;,&#x0921;&#x093C;"
+ latin:keyLabelFlags="fontNormal" />
</default>
</switch>
</merge>
diff --git a/java/res/xml/rowkeys_hindi2.xml b/java/res/xml/rowkeys_hindi2.xml
index 05e3db202..e7c67dbe7 100644
--- a/java/res/xml/rowkeys_hindi2.xml
+++ b/java/res/xml/rowkeys_hindi2.xml
@@ -31,7 +31,8 @@
U+0912: "ऒ" DEVANAGARI LETTER SHORT O -->
<Key
latin:keyLabel="&#x0913;"
- latin:moreKeys="&#x0913;&#x0902;,&#x0911;,&#x0912;" />
+ latin:moreKeys="&#x0913;&#x0902;,&#x0911;,&#x0912;"
+ 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="&#x090F;"
- latin:moreKeys="&#x090F;&#x0902;,&#x090F;&#x0901;,&#x090D;,&#x090E;" />
+ latin:moreKeys="&#x090F;&#x0902;,&#x090F;&#x0901;,&#x090D;,&#x090E;"
+ 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="&#x0905;"
- latin:moreKeys="&#x0905;&#x0902;,&#x0905;&#x0901;" />
+ latin:moreKeys="&#x0905;&#x0902;,&#x0905;&#x0901;"
+ 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="&#x0907;"
- latin:moreKeys="&#x0907;&#x0902;,&#x0907;&#x0901;" />
+ latin:moreKeys="&#x0907;&#x0902;,&#x0907;&#x0901;"
+ 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="&#x0909;"
- latin:moreKeys="&#x0909;&#x0902;,&#x0909;&#x0901;" />
+ latin:moreKeys="&#x0909;&#x0902;,&#x0909;&#x0901;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+092B: "फ" DEVANAGARI LETTER PHA
U+092B/U+093C: "फ़" DEVANAGARI LETTER PHA/DEVANAGARI SIGN NUKTA -->
<Key
latin:keyLabel="&#x092B;"
- latin:moreKeys="&#x092B;&#x093C;" />
+ latin:moreKeys="&#x092B;&#x093C;"
+ 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="&#x0931;"
- latin:moreKeys="&#x094D;&#x0930;,&#x0930;&#x094D;" />
+ latin:moreKeys="&#x094D;&#x0930;,&#x0930;&#x094D;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0916: "ख" DEVANAGARI LETTER KHA
U+0916/U+093C: "ख़" DEVANAGARI LETTER KHA/DEVANAGARI SIGN NUKTA -->
<Key
latin:keyLabel="&#x0916;"
- latin:moreKeys="&#x0916;&#x093C;" />
+ latin:moreKeys="&#x0916;&#x093C;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0925: "थ" DEVANAGARI LETTER THA -->
<Key
- latin:keyLabel="&#x0925;" />
+ latin:keyLabel="&#x0925;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+091B: "छ" DEVANAGARI LETTER CHA -->
<Key
- latin:keyLabel="&#x091B;" />
+ latin:keyLabel="&#x091B;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0920: "ठ" DEVANAGARI LETTER TTHA -->
<Key
- latin:keyLabel="&#x0920;" />
+ latin:keyLabel="&#x0920;"
+ 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="&#x094B;"
- latin:moreKeys="&#x094B;&#x0902;,&#x0949;,&#x094A;" />
+ latin:moreKeys="&#x094B;&#x0902;,&#x0949;,&#x094A;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0947: "े" DEVANAGARI VOWEL SIGN E
U+0947/U+0902: "ें" DEVANAGARI VOWEL SIGN E/DEVANAGARI SIGN ANUSVARA -->
<Key
latin:keyLabel="&#x0947;"
- latin:moreKeys="&#x0947;&#x0902;" />
+ latin:moreKeys="&#x0947;&#x0902;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+094D: "्" DEVANAGARI SIGN VIRAMA -->
<Key
- latin:keyLabel="&#x094D;" />
+ latin:keyLabel="&#x094D;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+093F: "ि" DEVANAGARI VOWEL SIGN I
U+093F/U+0902: "िं" DEVANAGARI VOWEL SIGN I/DEVANAGARI SIGN ANUSVARA -->
<Key
latin:keyLabel="&#x093F;"
- latin:moreKeys="&#x093F;&#x0902;" />
+ latin:moreKeys="&#x093F;&#x0902;"
+ 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="&#x0941;"
- latin:moreKeys="&#x0941;&#x0902;,&#x0941;&#x0901;" />
+ latin:moreKeys="&#x0941;&#x0902;,&#x0941;&#x0901;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+092A: "प" DEVANAGARI LETTER PA -->
<Key
- latin:keyLabel="&#x092A;" />
+ latin:keyLabel="&#x092A;"
+ 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="&#x0930;"
- latin:moreKeys="&#x090B;,&#x0930;&#x093C;,&#x0960;" />
+ latin:moreKeys="&#x090B;,&#x0930;&#x093C;,&#x0960;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0915: "क" DEVANAGARI LETTER KA
U+0915/U+093C: "क़" DEVANAGARI LETTER KA/DEVANAGARI SIGN NUKTA -->
<Key
latin:keyLabel="&#x0915;"
- latin:moreKeys="&#x0915;&#x093C;" />
+ latin:moreKeys="&#x0915;&#x093C;"
+ 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="&#x0924;"
- latin:moreKeys="&#x0924;&#x094D;&#x0930;" />
+ latin:moreKeys="&#x0924;&#x094D;&#x0930;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+091A: "च" DEVANAGARI LETTER CA -->
<Key
- latin:keyLabel="&#x091A;" />
+ latin:keyLabel="&#x091A;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+091F: "ट" DEVANAGARI LETTER TTA -->
<Key
- latin:keyLabel="&#x091F;" />
+ latin:keyLabel="&#x091F;"
+ latin:keyLabelFlags="fontNormal" />
</default>
</switch>
</merge>
diff --git a/java/res/xml/rowkeys_hindi3.xml b/java/res/xml/rowkeys_hindi3.xml
index 92bcb56b3..ebbff3e33 100644
--- a/java/res/xml/rowkeys_hindi3.xml
+++ b/java/res/xml/rowkeys_hindi3.xml
@@ -27,15 +27,18 @@
>
<!-- U+0911: "ऑ" DEVANAGARI LETTER CANDRA O -->
<Key
- latin:keyLabel="&#x0911;" />
+ latin:keyLabel="&#x0911;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0901: "ँ" DEVANAGARI SIGN CANDRABINDU
U+0945: "ॅ" DEVANAGARI VOWEL SIGN CANDRA E-->
<Key
latin:keyLabel="&#x0901;"
- latin:moreKeys="&#x0945;" />
+ latin:moreKeys="&#x0945;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0923: "ण" DEVANAGARI LETTER NNA -->
<Key
- latin:keyLabel="&#x0923;" />
+ latin:keyLabel="&#x0923;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0929: "ऩ" DEVANAGARI LETTER NNNA -->
<Key
latin:keyLabel="&#x0929;" />
@@ -43,65 +46,79 @@
U+0934: "ऴ" DEVANAGARI LETTER LLLA -->
<Key
latin:keyLabel="&#x0933;"
- latin:moreKeys="&#x0934;" />
+ latin:moreKeys="&#x0934;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0936: "श" DEVANAGARI LETTER SHA -->
<Key
- latin:keyLabel="&#x0936;" />
+ latin:keyLabel="&#x0936;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0937: "ष" DEVANAGARI LETTER SSA -->
<Key
- latin:keyLabel="&#x0937;" />
+ latin:keyLabel="&#x0937;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0943: "ृ" DEVANAGARI VOWEL SIGN VOCALIC R
U+0944: "ॄ" DEVANAGARI VOWEL SIGN VOCALIC RR -->
<Key
latin:keyLabel="&#x0943;"
- latin:moreKeys="&#x0944;" />
+ latin:moreKeys="&#x0944;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+091E: "ञ" DEVANAGARI LETTER NYA -->
<Key
- latin:keyLabel="&#x091E;" />
+ latin:keyLabel="&#x091E;"
+ latin:keyLabelFlags="fontNormal" />
</case>
<default>
<!-- U+0949: "ॉ" DEVANAGARI VOWEL SIGN CANDRA O -->
<Key
- latin:keyLabel="&#x0949;" />
+ latin:keyLabel="&#x0949;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0902: "ं" DEVANAGARI SIGN ANUSVARA -->
<Key
- latin:keyLabel="&#x0902;" />
+ latin:keyLabel="&#x0902;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+092E: "म" DEVANAGARI LETTER MA
U+0950: "ॐ" DEVANAGARI OM -->
<Key
latin:keyLabel="&#x092E;"
- latin:moreKeys="&#x0950;" />
+ latin:moreKeys="&#x0950;"
+ 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="&#x0928;"
- latin:moreKeys="&#x091E;,&#x0919;,&#x0928;&#x093C;" />
+ latin:moreKeys="&#x091E;,&#x0919;,&#x0928;&#x093C;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0935: "व" DEVANAGARI LETTER VA -->
<Key
- latin:keyLabel="&#x0935;" />
+ latin:keyLabel="&#x0935;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0932: "ल" DEVANAGARI LETTER LA
U+090C: "ऌ" DEVANAGARI LETTER VOCALIC L
U+0961: "ॡ" DEVANAGARI LETTER VOCALIC LL -->
<Key
latin:keyLabel="&#x0932;"
- latin:moreKeys="&#x090C;,&#x0961;" />
+ latin:moreKeys="&#x090C;,&#x0961;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0938: "स" DEVANAGARI LETTER SA -->
<Key
- latin:keyLabel="&#x0938;" />
+ latin:keyLabel="&#x0938;"
+ latin:keyLabelFlags="fontNormal" />
<!-- U+092F: "य" DEVANAGARI LETTER YA
U+095F: "य़" DEVANAGARI LETTER YYA -->
<Key
latin:keyLabel="&#x092F;"
- latin:moreKeys="&#x095F;" />
+ latin:moreKeys="&#x095F;"
+ 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="&#x093C;"
- latin:moreKeys="&#x097D;,&#x0970;,&#x093D;" />
+ latin:moreKeys="&#x097D;,&#x0970;,&#x093D;"
+ latin:keyLabelFlags="fontNormal" />
</default>
</switch>
</merge>
diff --git a/java/res/xml/rowkeys_qwerty1.xml b/java/res/xml/rowkeys_qwerty1.xml
index 84d613460..e7c9b590b 100644
--- a/java/res/xml/rowkeys_qwerty1.xml
+++ b/java/res/xml/rowkeys_qwerty1.xml
@@ -22,11 +22,12 @@
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
>
<Key
- latin:keyLabel="q"
+ latin:keyLabel="!text/keylabel_for_q"
latin:keyHintLabel="1"
- latin:additionalMoreKeys="1" />
+ latin:additionalMoreKeys="1"
+ latin:moreKeys="!text/more_keys_for_q" />
<Key
- latin:keyLabel="w"
+ latin:keyLabel="!text/keylabel_for_w"
latin:keyHintLabel="2"
latin:additionalMoreKeys="2"
latin:moreKeys="!text/more_keys_for_w" />
@@ -46,7 +47,7 @@
latin:additionalMoreKeys="5"
latin:moreKeys="!text/more_keys_for_t" />
<Key
- latin:keyLabel="y"
+ latin:keyLabel="!text/keylabel_for_y"
latin:keyHintLabel="6"
latin:additionalMoreKeys="6"
latin:moreKeys="!text/more_keys_for_y" />
diff --git a/java/res/xml/rowkeys_qwerty3.xml b/java/res/xml/rowkeys_qwerty3.xml
index a74aeb842..b70fd729f 100644
--- a/java/res/xml/rowkeys_qwerty3.xml
+++ b/java/res/xml/rowkeys_qwerty3.xml
@@ -25,7 +25,8 @@
latin:keyLabel="z"
latin:moreKeys="!text/more_keys_for_z" />
<Key
- latin:keyLabel="x" />
+ latin:keyLabel="!text/keylabel_for_x"
+ latin:moreKeys="!text/more_keys_for_x" />
<Key
latin:keyLabel="c"
latin:moreKeys="!text/more_keys_for_c" />
diff --git a/java/res/xml/rowkeys_spanish2.xml b/java/res/xml/rowkeys_spanish2.xml
index 4c7e57997..335dff33c 100644
--- a/java/res/xml/rowkeys_spanish2.xml
+++ b/java/res/xml/rowkeys_spanish2.xml
@@ -25,5 +25,5 @@
latin:keyboardLayout="@xml/rowkeys_qwerty2" />
<!-- U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE -->
<Key
- latin:keyLabel="&#x00F1;" />
+ latin:keyLabel="!text/keylabel_for_spanish_row2_10" />
</merge>
diff --git a/java/res/xml/rowkeys_symbols3.xml b/java/res/xml/rowkeys_symbols3.xml
index c89716bc7..7722ca9ae 100644
--- a/java/res/xml/rowkeys_symbols3.xml
+++ b/java/res/xml/rowkeys_symbols3.xml
@@ -22,7 +22,7 @@
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
>
<Key
- latin:keyLabel="!text/keylabel_for_symbols_exclamation"
+ latin:keyLabel="!"
latin:moreKeys="!text/more_keys_for_symbols_exclamation" />
<switch>
<case
diff --git a/java/res/xml/rowkeys_thai1.xml b/java/res/xml/rowkeys_thai1.xml
index 4b49da171..950d2a456 100644
--- a/java/res/xml/rowkeys_thai1.xml
+++ b/java/res/xml/rowkeys_thai1.xml
@@ -25,100 +25,110 @@
<case
latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted"
>
- <!-- U+0E0E: "ฎ" THAI CHARACTER DO CHADA -->
<Key
- latin:keyLabel="&#x0E0E;" />
- <!-- U+0E11: "ฑ" THAI CHARACTER THO NANGMONTHO -->
+ latin:keyLabel="+" />
+ <!-- U+0E51: "๑" THAI DIGIT ONE -->
<Key
- latin:keyLabel="&#x0E11;" />
- <!-- U+0E18: "ธ" THAI CHARACTER THO THONG -->
- <Key
- latin:keyLabel="&#x0E18;" />
- <!-- U+0E13: "ณ" THAI CHARACTER NO NEN -->
+ latin:keyLabel="&#x0E51;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0E52: "๒" THAI DIGIT TWO -->
<Key
- latin:keyLabel="&#x0E13;" />
- <!-- U+0E0D: "ญ" THAI CHARACTER YO YING -->
+ latin:keyLabel="&#x0E52;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0E53: "๓" THAI DIGIT THREE -->
<Key
- latin:keyLabel="&#x0E0D;" />
- <!-- U+0E10: "ฐ" THAI CHARACTER THO THAN -->
+ latin:keyLabel="&#x0E53;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0E54: "๔" THAI DIGIT FOUR -->
<Key
- latin:keyLabel="&#x0E10;" />
- <!-- U+0E03: "ฃ" THAI CHARACTER KHO KHUAT -->
+ latin:keyLabel="&#x0E54;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0020: " " SPACE
+ U+0E39: " ู" THAI CHARACTER SARA UU -->
+ <!-- Note: The space character is needed as a preceding letter to draw some Thai
+ composing characters correctly. -->
<Key
- latin:keyLabel="&#x0E03;" />
- <!-- U+0E05: "ฅ" THAI CHARACTER KHO KHON -->
+ latin:keyLabel="&#x20;&#x0E39;"
+ latin:code="0x0E39"
+ latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+ <!-- U+0E3F: "฿" THAI CURRENCY SYMBOL BAHT -->
<Key
- latin:keyLabel="&#x0E05;" />
- <!-- U+0E51: "๑" THAI DIGIT ONE
- U+0E52: "๒" THAI DIGIT TWO
- U+0E53: "๓" THAI DIGIT THREE
- U+0E54: "๔" THAI DIGIT FOUR
- U+0E55: "๕" THAI DIGIT FIVE -->
+ latin:keyLabel="&#x0E3F;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0E55: "๕" THAI DIGIT FIVE -->
<Key
- latin:keyLabel="&#x0E51;"
- latin:moreKeys="!fixedColumnOrder!4,&#x0E52;,&#x0E53;,&#x0E54;,&#x0E55;" />
- <!-- U+0E56: "๖" THAI DIGIT SIX
- U+0E57: "๗" THAI DIGIT SEVEN
- U+0E58: "๘" THAI DIGIT EIGHT
- U+0E59: "๙" THAI DIGIT NINE
- U+0E50: "๐" THAI DIGIT ZERO -->
+ latin:keyLabel="&#x0E55;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0E56: "๖" THAI DIGIT SIX -->
<Key
latin:keyLabel="&#x0E56;"
- latin:moreKeys="!fixedColumnOrder!4,&#x0E57;,&#x0E58;,&#x0E59;,&#x0E50;" />
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0E57: "๗" THAI DIGIT SEVEN -->
+ <Key
+ latin:keyLabel="&#x0E57;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0E58: "๘" THAI DIGIT EIGHT -->
+ <Key
+ latin:keyLabel="&#x0E58;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0E59: "๙" THAI DIGIT NINE -->
+ <Key
+ latin:keyLabel="&#x0E59;"
+ latin:keyLabelFlags="fontNormal" />
</case>
<default>
+ <!-- U+0E45: "ๅ" THAI CHARACTER LAKKHANGYAO -->
+ <Key
+ latin:keyLabel="&#x0E45;"
+ latin:keyLabelFlags="fontNormal" />
+ <Key
+ latin:keyLabel="/" />
+ <Key
+ latin:keyLabel="_" />
<!-- U+0E20: "ภ" THAI CHARACTER PHO SAMPHAO -->
<Key
latin:keyLabel="&#x0E20;"
- latin:keyHintLabel="1"
- latin:additionalMoreKeys="1,&#x0E51;" />
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0E16: "ถ" THAI CHARACTER THO THUNG -->
<Key
latin:keyLabel="&#x0E16;"
- latin:keyHintLabel="2"
- latin:additionalMoreKeys="2,&#x0E52;" />
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0020: " " SPACE
+ U+0E38: " ุ" THAI CHARACTER SARA U -->
+ <!-- Note: The space character is needed as a preceding letter to draw some Thai
+ composing characters correctly. -->
+ <Key
+ latin:keyLabel="&#x20;&#x0E38;"
+ latin:code="0x0E38"
+ latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+ <!-- U+0020: " " SPACE
+ U+0E36: " ึ" THAI CHARACTER SARA UE -->
+ <!-- Note: The space character is needed as a preceding letter to draw some Thai
+ composing characters correctly. -->
+ <Key
+ latin:keyLabel="&#x20;&#x0E36;"
+ latin:code="0x0E36"
+ latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
<!-- U+0E04: "ค" THAI CHARACTER KHO KHWAI -->
<Key
latin:keyLabel="&#x0E04;"
- latin:keyHintLabel="3"
- latin:additionalMoreKeys="3,&#x0E53;" />
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0E15: "ต" THAI CHARACTER TO TAO -->
<Key
latin:keyLabel="&#x0E15;"
- latin:keyHintLabel="4"
- latin:additionalMoreKeys="4,&#x0E54;" />
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0E08: "จ" THAI CHARACTER CHO CHAN -->
<Key
latin:keyLabel="&#x0E08;"
- latin:keyHintLabel="5"
- latin:additionalMoreKeys="5,&#x0E55;" />
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0E02: "ข" THAI CHARACTER KHO KHAI -->
<Key
latin:keyLabel="&#x0E02;"
- latin:keyHintLabel="6"
- latin:additionalMoreKeys="6,&#x0E56;" />
+ latin:keyLabelFlags="fontNormal" />
<!-- U+0E0A: "ช" THAI CHARACTER CHO CHANG -->
<Key
latin:keyLabel="&#x0E0A;"
- latin:keyHintLabel="7"
- latin:additionalMoreKeys="7,&#x0E57;" />
- <!-- U+0E23: "ร" THAI CHARACTER RO RUA
- U+0E25: "ล" THAI CHARACTER LO LING -->
- <Key
- latin:keyLabel="&#x0E23;"
- latin:moreKeys="&#x0E25;"
- latin:keyHintLabel="8"
- latin:additionalMoreKeys="8,&#x0E58;" />
- <!-- U+0E19: "น" THAI CHARACTER NO NU -->
- <Key
- latin:keyLabel="&#x0E19;"
- latin:keyHintLabel="9"
- latin:additionalMoreKeys="9,&#x0E59;" />
- <!-- U+0E22: "ย" THAI CHARACTER YO YAK -->
- <Key
- latin:keyLabel="&#x0E22;"
- latin:keyHintLabel="0"
- latin:additionalMoreKeys="0,&#x0E50;" />
+ latin:keyLabelFlags="fontNormal" />
</default>
</switch>
</merge>
diff --git a/java/res/xml/rowkeys_thai2.xml b/java/res/xml/rowkeys_thai2.xml
index 80e3563f8..f602994b9 100644
--- a/java/res/xml/rowkeys_thai2.xml
+++ b/java/res/xml/rowkeys_thai2.xml
@@ -25,83 +25,116 @@
<case
latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted"
>
- <!-- U+0E24: "ฤ" THAI CHARACTER RU -->
- <Key
- latin:keyLabel="&#x0E24;" />
- <!-- U+0E06: "ฆ" THAI CHARACTER KHO RAKHANG -->
- <Key
- latin:keyLabel="&#x0E06;" />
- <!-- U+0E0F: "ฏ" THAI CHARACTER TO PATAK -->
- <Key
- latin:keyLabel="&#x0E0F;" />
- <!-- U+0E0C: "ฌ" THAI CHARACTER CHO CHOE -->
- <Key
- latin:keyLabel="&#x0E0C;" />
- <!-- U+0E29: "ษ" THAI CHARACTER SO RUSI -->
- <Key
- latin:keyLabel="&#x0E29;" />
- <!-- U+0E28: "ศ" THAI CHARACTER SO SALA -->
- <Key
- latin:keyLabel="&#x0E28;" />
- <!-- U+0E0B: "ซ" THAI CHARACTER SO SO -->
- <Key
- latin:keyLabel="&#x0E0B;" />
- <!-- U+0E3F: "฿" THAI CURRENCY SYMBOL BAHT
- U+0E45: "ๅ" THAI CHARACTER LAKKHANGYAO -->
- <Key
- latin:keyLabel="&#x0E3F;"
- latin:moreKeys="&#x0E45;" />
- <!-- U+0E46: "ๆ" THAI CHARACTER MAIYAMOK
- U+0E2F: "ฯ" THAI CHARACTER PAIYANNOI -->
- <Key
- latin:keyLabel="&#x0E46;"
- latin:moreKeys="&#x0E2F;" />
+ <!-- U+0E50: "๐" THAI DIGIT ZERO -->
+ <Key
+ latin:keyLabel="&#x0E50;"
+ latin:keyLabelFlags="fontNormal" />
+ <Key
+ latin:keyLabel="&quot;" />
+ <!-- U+0E0E: "ฎ" THAI CHARACTER DO CHADA -->
+ <Key
+ latin:keyLabel="&#x0E0E;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0E11: "ฑ" THAI CHARACTER THO NANGMONTHO -->
+ <Key
+ latin:keyLabel="&#x0E11;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0E18: "ธ" THAI CHARACTER THO THONG -->
+ <Key
+ latin:keyLabel="&#x0E18;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0020: " " SPACE
+ U+0E4D: " ํ" THAI CHARACTER THANTHAKHAT -->
+ <!-- Note: The space character is needed as a preceding letter to draw some Thai
+ composing characters correctly. -->
+ <Key
+ latin:keyLabel="&#x20;&#x0E4D;"
+ latin:code="0x0E4D"
+ latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+ <!-- U+0020: " " SPACE
+ U+0E4A: " ๊" THAI CHARACTER MAI TRI -->
+ <!-- Note: The space character is needed as a preceding letter to draw some Thai
+ composing characters correctly. -->
+ <Key
+ latin:keyLabel="&#x20;&#x0E4A;"
+ latin:code="0x0E4A"
+ latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+ <!-- U+0E13: "ณ" THAI CHARACTER NO NEN -->
+ <Key
+ latin:keyLabel="&#x0E13;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0E2F: "ฯ" THAI CHARACTER PAIYANNOI -->
+ <Key
+ latin:keyLabel="&#x0E2F;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0E0D: "ญ" THAI CHARACTER YO YING -->
+ <Key
+ latin:keyLabel="&#x0E0D;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0E10: "ฐ" THAI CHARACTER THO THAN -->
+ <Key
+ latin:keyLabel="&#x0E10;"
+ latin:keyLabelFlags="fontNormal" />
+ <Key
+ latin:keyLabel="," />
</case>
<default>
- <!-- U+0E1F: "ฟ" THAI CHARACTER FO FAN
- U+0E1E: "พ" THAI CHARACTER PHO PHAN -->
- <Key
- latin:keyLabel="&#x0E1F;"
- latin:moreKeys="&#x0E1E;" />
- <!-- U+0E2B: "ห" THAI CHARACTER HO HIP -->
+ <!-- U+0E46: "ๆ" THAI CHARACTER MAIYAMOK -->
<Key
- latin:keyLabel="&#x0E2B;" />
- <!-- U+0E01: "ก" THAI CHARACTER KO KAI -->
- <Key
- latin:keyLabel="&#x0E01;" />
- <!-- U+0E14: "ด" THAI CHARACTER DO DEK -->
- <Key
- latin:keyLabel="&#x0E14;" />
- <!-- U+0E2A: "ส" THAI CHARACTER SO SUA -->
+ latin:keyLabel="&#x0E46;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0E44: "ไ" THAI CHARACTER SARA AI MAIMALAI -->
<Key
- latin:keyLabel="&#x0E2A;" />
- <!-- U+0E27: "ว" THAI CHARACTER WO WAEN -->
+ latin:keyLabel="&#x0E44;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0E33: "ำ" THAI CHARACTER SARA AM -->
<Key
- latin:keyLabel="&#x0E27;" />
- <!-- U+0E07: "ง" THAI CHARACTER NGO NGU -->
+ latin:keyLabel="&#x0E33;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0E1E: "พ" THAI CHARACTER PHO PHAN -->
<Key
- latin:keyLabel="&#x0E07;" />
- <!-- U+0E30: "ะ" THAI CHARACTER SARA A
- U+0E32: "า" THAI CHARACTER SARA AA
- U+0E33: " ำ" THAI CHARACTER SARA AM
- U+0E40: "เ" THAI CHARACTER SARA E
- U+0E41: "แ" THAI CHARACTER SARA AE
- U+0E43: "ใ" THAI CHARACTER SARA AI MAIMUAN
- U+0E44: "ไ" THAI CHARACTER SARA AI MAIMALAI
- U+0E42: "โ" THAI CHARACTER SARA O -->
+ latin:keyLabel="&#x0E1E;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0E30: "ะ" THAI CHARACTER SARA A -->
<Key
latin:keyLabel="&#x0E30;"
- latin:moreKeys="&#x0E32;,&#x0E33;,&#x0E40;,&#x0E41;,&#x0E43;,&#x0E44;,&#x0E42;" />
- <!-- U+0E31: " ั" THAI CHARACTER MAI HAN-AKAT
- U+0E34: " ิ" THAI CHARACTER SARA I
- U+0E35: " ี" THAI CHARACTER SARA II
- U+0E36: " ึ" THAI CHARACTER SARA UE
- U+0E37: " ื" THAI CHARACTER SARA UEE
- U+0E38: " ุ" THAI CHARACTER SARA U
- U+0E39: " ู" THAI CHARACTER SARA UU -->
- <Key
- latin:keyLabel="&#x0E31;"
- latin:moreKeys="&#x0E34;,&#x0E35;,&#x0E36;,&#x0E37;,&#x0E38;,&#x0E39;" />
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0020: " " SPACE
+ U+0E31: " ั" THAI CHARACTER MAI HAN-AKAT -->
+ <!-- Note: The space character is needed as a preceding letter to draw some Thai
+ composing characters correctly. -->
+ <Key
+ latin:keyLabel="&#x20;&#x0E31;"
+ latin:code="0x0E31"
+ latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+ <!-- U+0020: " " SPACE
+ U+0E35: " ี" HAI CHARACTER SARA II -->
+ <!-- Note: The space character is needed as a preceding letter to draw some Thai
+ composing characters correctly. -->
+ <Key
+ latin:keyLabel="&#x20;&#x0E35;"
+ latin:code="0x0E35"
+ latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+ <!-- U+0E23: "ร" THAI CHARACTER RO RUA -->
+ <Key
+ latin:keyLabel="&#x0E23;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0E19: "น" THAI CHARACTER NO NU -->
+ <Key
+ latin:keyLabel="&#x0E19;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0E22: "ย" THAI CHARACTER YO YAK -->
+ <Key
+ latin:keyLabel="&#x0E22;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0E1A: "บ" THAI CHARACTER BO BAIMAI -->
+ <Key
+ latin:keyLabel="&#x0E1A;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0E25: "ล" THAI CHARACTER LO LING -->
+ <Key
+ latin:keyLabel="&#x0E25;"
+ latin:keyLabelFlags="fontNormal" />
</default>
</switch>
</merge>
diff --git a/java/res/xml/rowkeys_thai3.xml b/java/res/xml/rowkeys_thai3.xml
index b8338073c..7b6e6372e 100644
--- a/java/res/xml/rowkeys_thai3.xml
+++ b/java/res/xml/rowkeys_thai3.xml
@@ -25,59 +25,110 @@
<case
latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted"
>
- <!-- U+0E09: "ฉ" THAI CHARACTER CHO CHING -->
+ <!-- U+0E24: "ฤ" THAI CHARACTER RU -->
<Key
- latin:keyLabel="&#x0E09;" />
- <!-- U+0E2E: "ฮ" THAI CHARACTER HO NOKHUK -->
+ latin:keyLabel="&#x0E24;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0E06: "ฆ" THAI CHARACTER KHO RAKHANG -->
<Key
- latin:keyLabel="&#x0E2E;" />
- <!-- U+0E12: "ฒ" THAI CHARACTER THO PHUTHAO -->
+ latin:keyLabel="&#x0E06;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0E0F: "ฏ" THAI CHARACTER TO PATAK -->
<Key
- latin:keyLabel="&#x0E12;" />
- <!-- U+0E2C: "ฬ" THAI CHARACTER LO CHULA -->
+ latin:keyLabel="&#x0E0F;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0E42: "โ" THAI CHARACTER SARA O -->
<Key
- latin:keyLabel="&#x0E2C;" />
- <!-- U+0E26: "ฦ" THAI CHARACTER LU -->
+ latin:keyLabel="&#x0E42;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0E0C: "ฌ" THAI CHARACTER CHO CHOE -->
<Key
- latin:keyLabel="&#x0E26;" />
- <!-- U+0E4C: " ์" THAI CHARACTER THANTHAKHAT
- U+0E4D: " ํ" THAI CHARACTER NIKHAHIT
- U+0E3A: " ฺ" THAI CHARACTER PHINTHU -->
+ latin:keyLabel="&#x0E0C;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0020: " " SPACE
+ U+0E47: " ็" THAI CHARACTER MAITAIKHU -->
+ <!-- Note: The space character is needed as a preceding letter to draw some Thai
+ composing characters correctly. -->
<Key
- latin:keyLabel="&#x0E4C;"
- latin:moreKeys="&#x0E4D;,&#x0E3A;" />
- <!-- U+0E47: " ็" THAI CHARACTER MAITAIKHU -->
+ latin:keyLabel="&#x20;&#x0E47;"
+ latin:code="0x0E47"
+ latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+ <!-- U+0020: " " SPACE
+ U+0E4B: " ๋" THAI CHARACTER MAI CHATTAWA -->
+ <!-- Note: The space character is needed as a preceding letter to draw some Thai
+ composing characters correctly. -->
+ <Key
+ latin:keyLabel="&#x20;&#x0E4B;"
+ latin:code="0x0E4B"
+ latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+ <!-- U+0E29: "ษ" THAI CHARACTER SO RUSI -->
+ <Key
+ latin:keyLabel="&#x0E29;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0E28: "ศ" THAI CHARACTER SO SALA -->
+ <Key
+ latin:keyLabel="&#x0E28;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0E0B: "ซ" THAI CHARACTER SO SO -->
+ <Key
+ latin:keyLabel="&#x0E0B;"
+ latin:keyLabelFlags="fontNormal" />
<Key
- latin:keyLabel="&#x0E47;" />
+ latin:keyLabel="." />
</case>
<default>
- <!-- U+0E1C: "ผ" THAI CHARACTER PHO PHUNG -->
+ <!-- U+0E1F: "ฟ" THAI CHARACTER FO FAN -->
<Key
- latin:keyLabel="&#x0E1C;" />
- <!-- U+0E1B: "ป" THAI CHARACTER PO PLA
- U+0E1A: "บ" THAI CHARACTER BO BAIMAI -->
+ latin:keyLabel="&#x0E1F;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0E2B: "ห" THAI CHARACTER HO HIP -->
<Key
- latin:keyLabel="&#x0E1B;"
- latin:moreKeys="&#x0E1A;" />
- <!-- U+0E2D: "อ" THAI CHARACTER O ANG -->
+ latin:keyLabel="&#x0E2B;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0E01: "ก" THAI CHARACTER KO KAI -->
<Key
- latin:keyLabel="&#x0E2D;" />
- <!-- U+0E17: "ท" THAI CHARACTER THO THAHAN -->
+ latin:keyLabel="&#x0E01;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0E14: "ด" THAI CHARACTER DO DEK -->
<Key
- latin:keyLabel="&#x0E17;" />
- <!-- U+0E21: "ม" THAI CHARACTER MO MA -->
+ latin:keyLabel="&#x0E14;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0E40: "เ" THAI CHARACTER SARA E -->
<Key
- latin:keyLabel="&#x0E21;" />
- <!-- U+0E1D: "ฝ" THAI CHARACTER FO FA -->
+ latin:keyLabel="&#x0E40;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0020: " " SPACE
+ U+0E49: " ้" THAI CHARACTER MAI THO -->
+ <!-- Note: The space character is needed as a preceding letter to draw some Thai
+ composing characters correctly. -->
<Key
- latin:keyLabel="&#x0E1D;" />
- <!-- U+0E48: " ่" THAI CHARACTER MAI EK
- U+0E49: " ้" THAI CHARACTER MAI THO
- U+0E4A: " ๊" THAI CHARACTER MAI TRI
- U+0E4B: " ๋" THAI CHARACTER MAI CHATTAWA -->
+ latin:keyLabel="&#x20;&#x0E49;"
+ latin:code="0x0E49"
+ latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+ <!-- U+0020: " " SPACE
+ U+0E48: " ่" THAI CHARACTER MAI EK -->
+ <!-- Note: The space character is needed as a preceding letter to draw some Thai
+ composing characters correctly. -->
+ <Key
+ latin:keyLabel="&#x20;&#x0E48;"
+ latin:code="0x0E48"
+ latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+ <!-- U+0E32: "า" THAI CHARACTER SARA AA -->
+ <Key
+ latin:keyLabel="&#x0E32;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0E2A: "ส" THAI CHARACTER SO SUA -->
+ <Key
+ latin:keyLabel="&#x0E2A;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0E27: "ว" THAI CHARACTER WO WAEN -->
+ <Key
+ latin:keyLabel="&#x0E27;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0E07: "ง" THAI CHARACTER NGO NGU -->
<Key
- latin:keyLabel="&#x0E48;"
- latin:moreKeys="&#x0E49;,&#x0E4A;,&#x0E4B;" />
+ latin:keyLabel="&#x0E07;"
+ latin:keyLabelFlags="fontNormal" />
</default>
</switch>
</merge>
diff --git a/java/res/xml/rowkeys_thai4.xml b/java/res/xml/rowkeys_thai4.xml
new file mode 100644
index 000000000..8a784242c
--- /dev/null
+++ b/java/res/xml/rowkeys_thai4.xml
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+
+<merge
+ xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+ <switch>
+ <case
+ latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLocked|alphabetShiftLockShifted"
+ >
+ <Key
+ latin:keyLabel="(" />
+ <Key
+ latin:keyLabel=")" />
+ <!-- U+0E09: "ฉ" THAI CHARACTER CHO CHING -->
+ <Key
+ latin:keyLabel="&#x0E09;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0E2E: "ฮ" THAI CHARACTER HO NOKHUK -->
+ <Key
+ latin:keyLabel="&#x0E2E;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0020: " " SPACE
+ U+0E3A: " ฺ" THAI CHARACTER PHINTHU -->
+ <!-- Note: The space character is needed as a preceding letter to draw some Thai
+ composing characters correctly. -->
+ <Key
+ latin:keyLabel="&#x20;&#x0E3A;"
+ latin:code="0x0E3A"
+ latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+ <!-- U+0020: " " SPACE
+ U+0E4C: " ์" THAI CHARACTER THANTHAKHAT -->
+ <!-- Note: The space character is needed as a preceding letter to draw some Thai
+ composing characters correctly. -->
+ <Key
+ latin:keyLabel="&#x20;&#x0E4C;"
+ latin:code="0x0E4C"
+ latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+ <Key
+ latin:keyLabel="\?" />
+ <!-- U+0E12: "ฒ" THAI CHARACTER THO PHUTHAO -->
+ <Key
+ latin:keyLabel="&#x0E12;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0E2C: "ฬ" THAI CHARACTER LO CHULA -->
+ <Key
+ latin:keyLabel="&#x0E2C;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0E26: "ฦ" THAI CHARACTER LU -->
+ <Key
+ latin:keyLabel="&#x0E26;"
+ latin:keyLabelFlags="fontNormal" />
+ </case>
+ <default>
+ <!-- U+0E1C: "ผ" THAI CHARACTER PHO PHUNG -->
+ <Key
+ latin:keyLabel="&#x0E1C;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0E1B: "ป" THAI CHARACTER PO PLA -->
+ <Key
+ latin:keyLabel="&#x0E1B;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0E41: "แ" THAI CHARACTER SARA AE -->
+ <Key
+ latin:keyLabel="&#x0E41;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0E2D: "อ" THAI CHARACTER O ANG -->
+ <Key
+ latin:keyLabel="&#x0E2D;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0020: " " SPACE
+ U+0E34: " ิ" THAI CHARACTER SARA I -->
+ <!-- Note: The space character is needed as a preceding letter to draw some Thai
+ composing characters correctly. -->
+ <Key
+ latin:keyLabel="&#x20;&#x0E34;"
+ latin:code="0x0E34"
+ latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+ <!-- U+0020: " " SPACE
+ U+0E37: " ื" THAI CHARACTER SARA UEE -->
+ <!-- Note: The space character is needed as a preceding letter to draw some Thai
+ composing characters correctly. -->
+ <Key
+ latin:keyLabel="&#x20;&#x0E37;"
+ latin:code="0x0E37"
+ latin:keyLabelFlags="fontNormal|followKeyLetterRatio" />
+ <!-- U+0E17: "ท" THAI CHARACTER THO THAHAN -->
+ <Key
+ latin:keyLabel="&#x0E17;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0E21: "ม" THAI CHARACTER MO MA -->
+ <Key
+ latin:keyLabel="&#x0E21;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0E43: "ใ" THAI CHARACTER SARA AI MAIMUAN -->
+ <Key
+ latin:keyLabel="&#x0E43;"
+ latin:keyLabelFlags="fontNormal" />
+ <!-- U+0E1D: "ฝ" THAI CHARACTER FO FA -->
+ <Key
+ latin:keyLabel="&#x0E1D;"
+ latin:keyLabelFlags="fontNormal" />
+ </default>
+ </switch>
+</merge>
diff --git a/java/res/xml/rows_esperanto.xml b/java/res/xml/rows_esperanto.xml
deleted file mode 100644
index c5f626e9b..000000000
--- a/java/res/xml/rows_esperanto.xml
+++ /dev/null
@@ -1,54 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 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.
-*/
--->
-
-<merge
- xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
->
- <include
- latin:keyboardLayout="@xml/key_styles_common" />
- <Row
- latin:keyWidth="10%p"
- >
- <include
- latin:keyboardLayout="@xml/rowkeys_esperanto1" />
- </Row>
- <Row
- latin:keyWidth="10%p"
- >
- <include
- latin:keyboardLayout="@xml/rowkeys_esperanto2" />
- </Row>
- <Row
- latin:keyWidth="10%p"
- >
- <Key
- latin:keyStyle="shiftKeyStyle"
- latin:keyWidth="15%p"
- latin:visualInsetsRight="1%p" />
- <include
- latin:keyboardLayout="@xml/rowkeys_esperanto3" />
- <Key
- latin:keyStyle="deleteKeyStyle"
- latin:keyWidth="fillRight"
- latin:visualInsetsLeft="1%p" />
- </Row>
- <include
- latin:keyboardLayout="@xml/row_qwerty4" />
-</merge>
diff --git a/java/res/xml/rows_thai.xml b/java/res/xml/rows_thai.xml
index 6b80df640..108b7e1fc 100644
--- a/java/res/xml/rows_thai.xml
+++ b/java/res/xml/rows_thai.xml
@@ -24,31 +24,34 @@
<include
latin:keyboardLayout="@xml/key_styles_common" />
<Row
- latin:keyWidth="10%p"
+ latin:keyWidth="8.3333%p"
>
<include
latin:keyboardLayout="@xml/rowkeys_thai1" />
</Row>
<Row
- latin:keyWidth="10%p"
+ latin:keyWidth="8.3333%p"
>
<include
- latin:keyboardLayout="@xml/rowkeys_thai2"
- latin:keyXPos="5%p" />
+ latin:keyboardLayout="@xml/rowkeys_thai2" />
</Row>
<Row
- latin:keyWidth="10%p"
+ latin:keyWidth="8.3333%p"
>
- <Key
- latin:keyStyle="shiftKeyStyle"
- latin:keyWidth="15%p"
- latin:visualInsetsRight="1%p" />
<include
latin:keyboardLayout="@xml/rowkeys_thai3" />
+ <include
+ latin:keyboardLayout="@xml/key_thai_kho_khuat" />
+ </Row>
+ <Row
+ latin:keyWidth="8.3333%p"
+ >
+ <Key
+ latin:keyStyle="shiftKeyStyle" />
+ <include
+ latin:keyboardLayout="@xml/rowkeys_thai4" />
<Key
- latin:keyStyle="deleteKeyStyle"
- latin:keyWidth="fillRight"
- latin:visualInsetsLeft="1%p" />
+ latin:keyStyle="deleteKeyStyle" />
</Row>
<include
latin:keyboardLayout="@xml/row_qwerty4" />
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java b/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java
index 70e38fdb0..b9b6362fc 100644
--- a/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java
+++ b/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java
@@ -35,6 +35,7 @@ import android.view.inputmethod.EditorInfo;
import com.android.inputmethod.keyboard.Key;
import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.KeyboardView;
+import com.android.inputmethod.latin.CollectionUtils;
/**
* Exposes a virtual view sub-tree for {@link KeyboardView} and generates
@@ -46,7 +47,7 @@ import com.android.inputmethod.keyboard.KeyboardView;
* virtual views, thus conveying their logical structure.
* </p>
*/
-public class AccessibilityEntityProvider extends AccessibilityNodeProviderCompat {
+public final class AccessibilityEntityProvider extends AccessibilityNodeProviderCompat {
private static final String TAG = AccessibilityEntityProvider.class.getSimpleName();
private static final int UNDEFINED = Integer.MIN_VALUE;
@@ -55,7 +56,7 @@ public class AccessibilityEntityProvider extends AccessibilityNodeProviderCompat
private final AccessibilityUtils mAccessibilityUtils;
/** A map of integer IDs to {@link Key}s. */
- private final SparseArray<Key> mVirtualViewIdToKey = new SparseArray<Key>();
+ private final SparseArray<Key> mVirtualViewIdToKey = CollectionUtils.newSparseArray();
/** Temporary rect used to calculate in-screen bounds. */
private final Rect mTempBoundsInScreen = new Rect();
@@ -195,8 +196,7 @@ public class AccessibilityEntityProvider extends AccessibilityNodeProviderCompat
info.setSource(mKeyboardView, virtualViewId);
info.setBoundsInScreen(boundsInScreen);
info.setEnabled(true);
- info.setClickable(true);
- info.addAction(AccessibilityNodeInfoCompat.ACTION_CLICK);
+ info.setVisibleToUser(true);
if (mAccessibilityFocusedView == virtualViewId) {
info.addAction(AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
@@ -225,6 +225,9 @@ public class AccessibilityEntityProvider extends AccessibilityNodeProviderCompat
mKeyboardView.onTouchEvent(downEvent);
mKeyboardView.onTouchEvent(upEvent);
+
+ downEvent.recycle();
+ upEvent.recycle();
}
@Override
@@ -251,9 +254,6 @@ public class AccessibilityEntityProvider extends AccessibilityNodeProviderCompat
final int virtualViewId = generateVirtualViewIdForKey(key);
switch (action) {
- case AccessibilityNodeInfoCompat.ACTION_CLICK:
- simulateKeyPress(key);
- return true;
case AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS:
if (mAccessibilityFocusedView == virtualViewId) {
return false;
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java b/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java
index 616b1c6d7..1eee1df87 100644
--- a/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java
+++ b/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java
@@ -19,10 +19,15 @@ package com.android.inputmethod.accessibility;
import android.content.Context;
import android.inputmethodservice.InputMethodService;
import android.media.AudioManager;
+import android.os.Build;
import android.os.SystemClock;
import android.provider.Settings;
+import android.support.v4.view.accessibility.AccessibilityEventCompat;
import android.util.Log;
import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.inputmethod.EditorInfo;
@@ -32,7 +37,7 @@ import com.android.inputmethod.compat.SettingsSecureCompatUtils;
import com.android.inputmethod.latin.InputTypeUtils;
import com.android.inputmethod.latin.R;
-public class AccessibilityUtils {
+public final class AccessibilityUtils {
private static final String TAG = AccessibilityUtils.class.getSimpleName();
private static final String CLASS = AccessibilityUtils.class.getClass().getName();
private static final String PACKAGE = AccessibilityUtils.class.getClass().getPackage()
@@ -138,9 +143,10 @@ public class AccessibilityUtils {
* Sends the specified text to the {@link AccessibilityManager} to be
* spoken.
*
- * @param text the text to speak
+ * @param view The source view.
+ * @param text The text to speak.
*/
- public void speak(CharSequence text) {
+ public void announceForAccessibility(View view, CharSequence text) {
if (!mAccessibilityManager.isEnabled()) {
Log.e(TAG, "Attempted to speak when accessibility was disabled!");
return;
@@ -149,8 +155,7 @@ public class AccessibilityUtils {
// The following is a hack to avoid using the heavy-weight TextToSpeech
// class. Instead, we're just forcing a fake AccessibilityEvent into
// the screen reader to make it speak.
- final AccessibilityEvent event = AccessibilityEvent
- .obtain(AccessibilityEvent.TYPE_VIEW_FOCUSED);
+ final AccessibilityEvent event = AccessibilityEvent.obtain();
event.setPackageName(PACKAGE);
event.setClassName(CLASS);
@@ -158,20 +163,34 @@ public class AccessibilityUtils {
event.setEnabled(true);
event.getText().add(text);
- mAccessibilityManager.sendAccessibilityEvent(event);
+ // Platforms starting at SDK 16 should use announce events.
+ if (Build.VERSION.SDK_INT >= 16) {
+ event.setEventType(AccessibilityEventCompat.TYPE_ANNOUNCEMENT);
+ } else {
+ event.setEventType(AccessibilityEvent.TYPE_VIEW_FOCUSED);
+ }
+
+ final ViewParent viewParent = view.getParent();
+ if ((viewParent == null) || !(viewParent instanceof ViewGroup)) {
+ Log.e(TAG, "Failed to obtain ViewParent in announceForAccessibility");
+ return;
+ }
+
+ viewParent.requestSendAccessibilityEvent(view, event);
}
/**
* Handles speaking the "connect a headset to hear passwords" notification
* when connecting to a password field.
*
+ * @param view The source view.
* @param editorInfo The input connection's editor info attribute.
* @param restarting Whether the connection is being restarted.
*/
- public void onStartInputViewInternal(EditorInfo editorInfo, boolean restarting) {
+ public void onStartInputViewInternal(View view, EditorInfo editorInfo, boolean restarting) {
if (shouldObscureInput(editorInfo)) {
final CharSequence text = mContext.getText(R.string.spoken_use_headphones);
- speak(text);
+ announceForAccessibility(view, text);
}
}
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java
index f6376d5f4..fcfa6d4e4 100644
--- a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java
+++ b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java
@@ -24,27 +24,26 @@ import android.support.v4.view.accessibility.AccessibilityEventCompat;
import android.support.v4.view.accessibility.AccessibilityNodeInfoCompat;
import android.view.MotionEvent;
import android.view.View;
-import android.view.ViewConfiguration;
import com.android.inputmethod.keyboard.Key;
import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.KeyboardId;
-import com.android.inputmethod.keyboard.LatinKeyboardView;
+import com.android.inputmethod.keyboard.MainKeyboardView;
import com.android.inputmethod.keyboard.PointerTracker;
import com.android.inputmethod.latin.R;
-public class AccessibleKeyboardViewProxy extends AccessibilityDelegateCompat {
+public final class AccessibleKeyboardViewProxy extends AccessibilityDelegateCompat {
private static final AccessibleKeyboardViewProxy sInstance = new AccessibleKeyboardViewProxy();
private InputMethodService mInputMethod;
- private LatinKeyboardView mView;
+ private MainKeyboardView mView;
private AccessibilityEntityProvider mAccessibilityNodeProvider;
private Key mLastHoverKey = null;
/**
* Inset in pixels to look for keys when the user's finger exits the
- * keyboard area. See {@link ViewConfiguration#getScaledEdgeSlop()}.
+ * keyboard area.
*/
private int mEdgeSlop;
@@ -62,7 +61,8 @@ public class AccessibleKeyboardViewProxy extends AccessibilityDelegateCompat {
private void initInternal(InputMethodService inputMethod) {
mInputMethod = inputMethod;
- mEdgeSlop = ViewConfiguration.get(inputMethod).getScaledEdgeSlop();
+ mEdgeSlop = inputMethod.getResources().getDimensionPixelSize(
+ R.dimen.accessibility_edge_slop);
}
/**
@@ -70,7 +70,7 @@ public class AccessibleKeyboardViewProxy extends AccessibilityDelegateCompat {
*
* @param view The view to wrap.
*/
- public void setView(LatinKeyboardView view) {
+ public void setView(MainKeyboardView view) {
if (view == null) {
// Ignore null views.
return;
@@ -105,8 +105,21 @@ public class AccessibleKeyboardViewProxy extends AccessibilityDelegateCompat {
}
/**
- * Receives hover events when accessibility is turned on in SDK versions ICS
- * and higher.
+ * Intercepts touch events before dispatch when touch exploration is turned
+ * on in ICS and higher.
+ *
+ * @param event The motion event being dispatched.
+ * @return {@code true} if the event is handled
+ */
+ public boolean dispatchTouchEvent(MotionEvent event) {
+ // To avoid accidental key presses during touch exploration, always drop
+ // touch events generated by the user.
+ return false;
+ }
+
+ /**
+ * Receives hover events when touch exploration is turned on in SDK versions
+ * ICS and higher.
*
* @param event The hover event.
* @return {@code true} if the event is handled
@@ -114,8 +127,14 @@ public class AccessibleKeyboardViewProxy extends AccessibilityDelegateCompat {
public boolean dispatchHoverEvent(MotionEvent event, PointerTracker tracker) {
final int x = (int) event.getX();
final int y = (int) event.getY();
- final Key key = tracker.getKeyOn(x, y);
final Key previousKey = mLastHoverKey;
+ final Key key;
+
+ if (pointInView(x, y)) {
+ key = tracker.getKeyOn(x, y);
+ } else {
+ key = null;
+ }
mLastHoverKey = key;
@@ -123,7 +142,7 @@ public class AccessibleKeyboardViewProxy extends AccessibilityDelegateCompat {
case MotionEvent.ACTION_HOVER_EXIT:
// Make sure we're not getting an EXIT event because the user slid
// off the keyboard area, then force a key press.
- if (pointInView(x, y) && (key != null)) {
+ if (key != null) {
getAccessibilityNodeProvider().simulateKeyPress(key);
}
//$FALL-THROUGH$
@@ -250,7 +269,7 @@ public class AccessibleKeyboardViewProxy extends AccessibilityDelegateCompat {
text = context.getText(R.string.spoken_description_shiftmode_off);
}
- AccessibilityUtils.getInstance().speak(text);
+ AccessibilityUtils.getInstance().announceForAccessibility(mView, text);
}
/**
@@ -290,6 +309,6 @@ public class AccessibleKeyboardViewProxy extends AccessibilityDelegateCompat {
}
final String text = context.getString(resId);
- AccessibilityUtils.getInstance().speak(text);
+ AccessibilityUtils.getInstance().announceForAccessibility(mView, text);
}
}
diff --git a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
index 23acb8b74..32618ad85 100644
--- a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
+++ b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java
@@ -19,16 +19,18 @@ package com.android.inputmethod.accessibility;
import android.content.Context;
import android.text.TextUtils;
import android.util.Log;
+import android.util.SparseIntArray;
import android.view.inputmethod.EditorInfo;
import com.android.inputmethod.keyboard.Key;
import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.latin.CollectionUtils;
import com.android.inputmethod.latin.R;
import java.util.HashMap;
-public class KeyCodeDescriptionMapper {
+public final class KeyCodeDescriptionMapper {
private static final String TAG = KeyCodeDescriptionMapper.class.getSimpleName();
// The resource ID of the string spoken for obscured keys
@@ -37,10 +39,10 @@ public class KeyCodeDescriptionMapper {
private static KeyCodeDescriptionMapper sInstance = new KeyCodeDescriptionMapper();
// Map of key labels to spoken description resource IDs
- private final HashMap<CharSequence, Integer> mKeyLabelMap;
+ private final HashMap<CharSequence, Integer> mKeyLabelMap = CollectionUtils.newHashMap();
- // Map of key codes to spoken description resource IDs
- private final HashMap<Integer, Integer> mKeyCodeMap;
+ // Sparse array of spoken description resource IDs indexed by key codes
+ private final SparseIntArray mKeyCodeMap;
public static void init() {
sInstance.initInternal();
@@ -51,18 +53,15 @@ public class KeyCodeDescriptionMapper {
}
private KeyCodeDescriptionMapper() {
- mKeyLabelMap = new HashMap<CharSequence, Integer>();
- mKeyCodeMap = new HashMap<Integer, Integer>();
+ mKeyCodeMap = new SparseIntArray();
}
private void initInternal() {
// Manual label substitutions for key labels with no string resource
mKeyLabelMap.put(":-)", R.string.spoken_description_smiley);
- // Symbols that most TTS engines can't speak
- mKeyCodeMap.put((int) ' ', R.string.spoken_description_space);
-
// Special non-character codes defined in Keyboard
+ mKeyCodeMap.put(Keyboard.CODE_SPACE, R.string.spoken_description_space);
mKeyCodeMap.put(Keyboard.CODE_DELETE, R.string.spoken_description_delete);
mKeyCodeMap.put(Keyboard.CODE_ENTER, R.string.spoken_description_return);
mKeyCodeMap.put(Keyboard.CODE_SETTINGS, R.string.spoken_description_settings);
@@ -70,6 +69,9 @@ public class KeyCodeDescriptionMapper {
mKeyCodeMap.put(Keyboard.CODE_SHORTCUT, R.string.spoken_description_mic);
mKeyCodeMap.put(Keyboard.CODE_SWITCH_ALPHA_SYMBOL, R.string.spoken_description_to_symbol);
mKeyCodeMap.put(Keyboard.CODE_TAB, R.string.spoken_description_tab);
+ mKeyCodeMap.put(Keyboard.CODE_LANGUAGE_SWITCH, R.string.spoken_description_language_switch);
+ mKeyCodeMap.put(Keyboard.CODE_ACTION_NEXT, R.string.spoken_description_action_next);
+ mKeyCodeMap.put(Keyboard.CODE_ACTION_PREVIOUS, R.string.spoken_description_action_previous);
}
/**
@@ -273,7 +275,7 @@ public class KeyCodeDescriptionMapper {
return context.getString(OBSCURED_KEY_RES_ID);
}
- if (mKeyCodeMap.containsKey(code)) {
+ if (mKeyCodeMap.indexOfKey(code) >= 0) {
return context.getString(mKeyCodeMap.get(code));
} else if (isDefinedNonCtrl) {
return Character.toString((char) code);
diff --git a/java/src/com/android/inputmethod/compat/AudioManagerCompatWrapper.java b/java/src/com/android/inputmethod/compat/AudioManagerCompatWrapper.java
index b6c3e2a88..40eed91f2 100644
--- a/java/src/com/android/inputmethod/compat/AudioManagerCompatWrapper.java
+++ b/java/src/com/android/inputmethod/compat/AudioManagerCompatWrapper.java
@@ -20,7 +20,7 @@ import android.media.AudioManager;
import java.lang.reflect.Method;
-public class AudioManagerCompatWrapper {
+public final class AudioManagerCompatWrapper {
private static final Method METHOD_isWiredHeadsetOn = CompatUtils.getMethod(
AudioManager.class, "isWiredHeadsetOn");
private static final Method METHOD_isBluetoothA2dpOn = CompatUtils.getMethod(
diff --git a/java/src/com/android/inputmethod/compat/CompatUtils.java b/java/src/com/android/inputmethod/compat/CompatUtils.java
index ce427e9c9..ffed6ecb1 100644
--- a/java/src/com/android/inputmethod/compat/CompatUtils.java
+++ b/java/src/com/android/inputmethod/compat/CompatUtils.java
@@ -24,7 +24,7 @@ import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
-public class CompatUtils {
+public final class CompatUtils {
private static final String TAG = CompatUtils.class.getSimpleName();
private static final String EXTRA_INPUT_METHOD_ID = "input_method_id";
// TODO: Can these be constants instead of literal String constants?
diff --git a/java/src/com/android/inputmethod/compat/EditorInfoCompatUtils.java b/java/src/com/android/inputmethod/compat/EditorInfoCompatUtils.java
index 08c246f8b..210058bec 100644
--- a/java/src/com/android/inputmethod/compat/EditorInfoCompatUtils.java
+++ b/java/src/com/android/inputmethod/compat/EditorInfoCompatUtils.java
@@ -20,7 +20,7 @@ import android.view.inputmethod.EditorInfo;
import java.lang.reflect.Field;
-public class EditorInfoCompatUtils {
+public final class EditorInfoCompatUtils {
// EditorInfo.IME_FLAG_FORCE_ASCII has been introduced since API#16 (JellyBean).
private static final Field FIELD_IME_FLAG_FORCE_ASCII = CompatUtils.getField(
EditorInfo.class, "IME_FLAG_FORCE_ASCII");
diff --git a/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java b/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java
index cc10a4ed2..a01c301ee 100644
--- a/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java
+++ b/java/src/com/android/inputmethod/compat/InputMethodManagerCompatWrapper.java
@@ -27,7 +27,7 @@ import java.lang.reflect.Method;
// TODO: Override this class with the concrete implementation if we need to take care of the
// performance.
-public class InputMethodManagerCompatWrapper {
+public final class InputMethodManagerCompatWrapper {
private static final String TAG = InputMethodManagerCompatWrapper.class.getSimpleName();
private static final Method METHOD_switchToNextInputMethod = CompatUtils.getMethod(
InputMethodManager.class, "switchToNextInputMethod", IBinder.class, Boolean.TYPE);
diff --git a/java/src/com/android/inputmethod/compat/InputMethodServiceCompatUtils.java b/java/src/com/android/inputmethod/compat/InputMethodServiceCompatUtils.java
new file mode 100644
index 000000000..8eea31ed2
--- /dev/null
+++ b/java/src/com/android/inputmethod/compat/InputMethodServiceCompatUtils.java
@@ -0,0 +1,34 @@
+/*
+ * 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.compat;
+
+import android.inputmethodservice.InputMethodService;
+
+import java.lang.reflect.Method;
+
+public final class InputMethodServiceCompatUtils {
+ private static final Method METHOD_enableHardwareAcceleration =
+ CompatUtils.getMethod(InputMethodService.class, "enableHardwareAcceleration");
+
+ private InputMethodServiceCompatUtils() {
+ // This utility class is not publicly instantiable.
+ }
+
+ public static boolean enableHardwareAcceleration(InputMethodService ims) {
+ return (Boolean)CompatUtils.invoke(ims, false, METHOD_enableHardwareAcceleration);
+ }
+}
diff --git a/java/src/com/android/inputmethod/compat/SettingsSecureCompatUtils.java b/java/src/com/android/inputmethod/compat/SettingsSecureCompatUtils.java
index 1b79992f0..db5abd0fe 100644
--- a/java/src/com/android/inputmethod/compat/SettingsSecureCompatUtils.java
+++ b/java/src/com/android/inputmethod/compat/SettingsSecureCompatUtils.java
@@ -18,7 +18,7 @@ package com.android.inputmethod.compat;
import java.lang.reflect.Field;
-public class SettingsSecureCompatUtils {
+public final class SettingsSecureCompatUtils {
private static final Field FIELD_ACCESSIBILITY_SPEAK_PASSWORD = CompatUtils.getField(
android.provider.Settings.Secure.class, "ACCESSIBILITY_SPEAK_PASSWORD");
diff --git a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
index a0f48d24c..159f43650 100644
--- a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
+++ b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
@@ -16,10 +16,6 @@
package com.android.inputmethod.compat;
-import com.android.inputmethod.latin.LatinImeLogger;
-import com.android.inputmethod.latin.SuggestedWords;
-import com.android.inputmethod.latin.SuggestionSpanPickedNotificationReceiver;
-
import android.content.Context;
import android.text.Spannable;
import android.text.SpannableString;
@@ -27,12 +23,17 @@ import android.text.Spanned;
import android.text.TextUtils;
import android.util.Log;
+import com.android.inputmethod.latin.CollectionUtils;
+import com.android.inputmethod.latin.LatinImeLogger;
+import com.android.inputmethod.latin.SuggestedWords;
+import com.android.inputmethod.latin.SuggestionSpanPickedNotificationReceiver;
+
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Locale;
-public class SuggestionSpanUtils {
+public final class SuggestionSpanUtils {
private static final String TAG = SuggestionSpanUtils.class.getSimpleName();
// TODO: Use reflection to get field values
public static final String ACTION_SUGGESTION_PICKED =
@@ -119,8 +120,7 @@ public class SuggestionSpanUtils {
} else {
spannable = new SpannableString(pickedWord);
}
- final ArrayList<String> suggestionsList = new ArrayList<String>();
- boolean sameAsTyped = false;
+ final ArrayList<String> suggestionsList = CollectionUtils.newArrayList();
for (int i = 0; i < suggestedWords.size(); ++i) {
if (suggestionsList.size() >= OBJ_SUGGESTIONS_MAX_SIZE) {
break;
@@ -128,8 +128,6 @@ public class SuggestionSpanUtils {
final CharSequence word = suggestedWords.getWord(i);
if (!TextUtils.equals(pickedWord, word)) {
suggestionsList.add(word.toString());
- } else if (i == 0) {
- sameAsTyped = true;
}
}
diff --git a/java/src/com/android/inputmethod/compat/SuggestionsInfoCompatUtils.java b/java/src/com/android/inputmethod/compat/SuggestionsInfoCompatUtils.java
index e5f9db27c..8314212c9 100644
--- a/java/src/com/android/inputmethod/compat/SuggestionsInfoCompatUtils.java
+++ b/java/src/com/android/inputmethod/compat/SuggestionsInfoCompatUtils.java
@@ -20,7 +20,7 @@ import android.view.textservice.SuggestionsInfo;
import java.lang.reflect.Field;
-public class SuggestionsInfoCompatUtils {
+public final class SuggestionsInfoCompatUtils {
private static final Field FIELD_RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS = CompatUtils.getField(
SuggestionsInfo.class, "RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS");
private static final Integer OBJ_RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS = (Integer) CompatUtils
diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java
index e1e1ca9cf..30812e8c3 100644
--- a/java/src/com/android/inputmethod/keyboard/Key.java
+++ b/java/src/com/android/inputmethod/keyboard/Key.java
@@ -31,11 +31,16 @@ import android.text.TextUtils;
import android.util.Log;
import android.util.Xml;
+import com.android.inputmethod.keyboard.internal.KeyDrawParams;
import com.android.inputmethod.keyboard.internal.KeySpecParser;
-import com.android.inputmethod.keyboard.internal.KeySpecParser.MoreKeySpec;
-import com.android.inputmethod.keyboard.internal.KeyStyles.KeyStyle;
+import com.android.inputmethod.keyboard.internal.KeyStyle;
+import com.android.inputmethod.keyboard.internal.KeyVisualAttributes;
import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
+import com.android.inputmethod.keyboard.internal.KeyboardParams;
+import com.android.inputmethod.keyboard.internal.KeyboardRow;
+import com.android.inputmethod.keyboard.internal.MoreKeySpec;
import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.ResourceUtils;
import com.android.inputmethod.latin.StringUtils;
import org.xmlpull.v1.XmlPullParser;
@@ -47,14 +52,13 @@ import java.util.Locale;
/**
* Class for describing the position and characteristics of a single key in the keyboard.
*/
-public class Key {
+public class Key implements Comparable<Key> {
private static final String TAG = Key.class.getSimpleName();
/**
* The key code (unicode or custom code) that this key generates.
*/
public final int mCode;
- public final int mAltCode;
/** Label to display */
public final String mLabel;
@@ -89,22 +93,11 @@ public class Key {
/** Icon to display instead of a label. Icon takes precedence over a label */
private final int mIconId;
- /** Icon for disabled state */
- private final int mDisabledIconId;
- /** Preview version of the icon, for the preview popup */
- private final int mPreviewIconId;
/** Width of the key, not including the gap */
public final int mWidth;
/** Height of the key, not including the gap */
public final int mHeight;
- /** The horizontal gap around this key */
- public final int mHorizontalGap;
- /** The vertical gap below this key */
- public final int mVerticalGap;
- /** The visual insets */
- public final int mVisualInsetsLeft;
- public final int mVisualInsetsRight;
/** X coordinate of the key in the keyboard layout */
public final int mX;
/** Y coordinate of the key in the keyboard layout */
@@ -112,8 +105,6 @@ public class Key {
/** Hit bounding box of the key */
public final Rect mHitBox = new Rect();
- /** Text to output when pressed. This can be multiple characters, like ".com" */
- public final CharSequence mOutputText;
/** More keys */
public final MoreKeySpec[] mMoreKeys;
/** More keys column number and flags */
@@ -143,6 +134,34 @@ public class Key {
private static final int ACTION_FLAGS_ALT_CODE_WHILE_TYPING = 0x04;
private static final int ACTION_FLAGS_ENABLE_LONG_PRESS = 0x08;
+ public final KeyVisualAttributes mKeyVisualAttributes;
+
+ private final OptionalAttributes mOptionalAttributes;
+
+ private static final class OptionalAttributes {
+ /** Text to output when pressed. This can be multiple characters, like ".com" */
+ public final String mOutputText;
+ public final int mAltCode;
+ /** Icon for disabled state */
+ public final int mDisabledIconId;
+ /** Preview version of the icon, for the preview popup */
+ public final int mPreviewIconId;
+ /** The visual insets */
+ public final int mVisualInsetsLeft;
+ public final int mVisualInsetsRight;
+
+ public OptionalAttributes(final String outputText, final int altCode,
+ final int disabledIconId, final int previewIconId,
+ final int visualInsetsLeft, final int visualInsetsRight) {
+ mOutputText = outputText;
+ mAltCode = altCode;
+ mDisabledIconId = disabledIconId;
+ mPreviewIconId = previewIconId;
+ mVisualInsetsLeft = visualInsetsLeft;
+ mVisualInsetsRight = visualInsetsRight;
+ }
+ }
+
private final int mHashCode;
/** The current pressed state of this key */
@@ -153,8 +172,8 @@ public class Key {
/**
* This constructor is being used only for keys in more keys keyboard.
*/
- public Key(Keyboard.Params params, MoreKeySpec moreKeySpec, int x, int y, int width, int height,
- int labelFlags) {
+ public Key(final KeyboardParams params, final MoreKeySpec moreKeySpec, final int x, final int y,
+ final int width, final int height, final int labelFlags) {
this(params, moreKeySpec.mLabel, null, moreKeySpec.mIconId, moreKeySpec.mCode,
moreKeySpec.mOutputText, x, y, width, height, labelFlags);
}
@@ -162,13 +181,11 @@ public class Key {
/**
* This constructor is being used only for key in popup suggestions pane.
*/
- public Key(Keyboard.Params params, String label, String hintLabel, int iconId,
- int code, String outputText, int x, int y, int width, int height, int labelFlags) {
+ public Key(final KeyboardParams params, final String label, final String hintLabel,
+ final int iconId, final int code, final String outputText, final int x, final int y,
+ final int width, final int height, final int labelFlags) {
mHeight = height - params.mVerticalGap;
- mHorizontalGap = params.mHorizontalGap;
- mVerticalGap = params.mVerticalGap;
- mVisualInsetsLeft = mVisualInsetsRight = 0;
- mWidth = width - mHorizontalGap;
+ mWidth = width - params.mHorizontalGap;
mHintLabel = hintLabel;
mLabelFlags = labelFlags;
mBackgroundType = BACKGROUND_TYPE_NORMAL;
@@ -176,17 +193,20 @@ public class Key {
mMoreKeys = null;
mMoreKeysColumnAndFlags = 0;
mLabel = label;
- mOutputText = outputText;
+ if (outputText == null) {
+ mOptionalAttributes = null;
+ } else {
+ mOptionalAttributes = new OptionalAttributes(outputText, CODE_UNSPECIFIED,
+ ICON_UNDEFINED, ICON_UNDEFINED, 0, 0);
+ }
mCode = code;
mEnabled = (code != CODE_UNSPECIFIED);
- mAltCode = CODE_UNSPECIFIED;
mIconId = iconId;
- mDisabledIconId = ICON_UNDEFINED;
- mPreviewIconId = ICON_UNDEFINED;
// Horizontal gap is divided equally to both sides of the key.
- mX = x + mHorizontalGap / 2;
+ mX = x + params.mHorizontalGap / 2;
mY = y;
mHitBox.set(x, y, x + width + 1, y + height);
+ mKeyVisualAttributes = null;
mHashCode = computeHashCode(this);
}
@@ -201,12 +221,11 @@ public class Key {
* @param parser the XML parser containing the attributes for this key
* @throws XmlPullParserException
*/
- public Key(Resources res, Keyboard.Params params, Keyboard.Builder.Row row,
- XmlPullParser parser) throws XmlPullParserException {
+ public Key(final Resources res, final KeyboardParams params, final KeyboardRow row,
+ final XmlPullParser parser) throws XmlPullParserException {
final float horizontalGap = isSpacer() ? 0 : params.mHorizontalGap;
final int keyHeight = row.mRowHeight;
- mVerticalGap = params.mVerticalGap;
- mHeight = keyHeight - mVerticalGap;
+ mHeight = keyHeight - params.mVerticalGap;
final TypedArray keyAttr = res.obtainAttributes(Xml.asAttributeSet(parser),
R.styleable.Keyboard_Key);
@@ -220,7 +239,6 @@ public class Key {
mX = Math.round(keyXPos + horizontalGap / 2);
mY = keyYPos;
mWidth = Math.round(keyWidth - horizontalGap);
- mHorizontalGap = Math.round(horizontalGap);
mHitBox.set(Math.round(keyXPos), keyYPos, Math.round(keyXPos + keyWidth) + 1,
keyYPos + keyHeight);
// Update row to have current x coordinate.
@@ -229,15 +247,15 @@ public class Key {
mBackgroundType = style.getInt(keyAttr,
R.styleable.Keyboard_Key_backgroundType, row.getDefaultBackgroundType());
- mVisualInsetsLeft = Math.round(Keyboard.Builder.getDimensionOrFraction(keyAttr,
+ final int visualInsetsLeft = Math.round(ResourceUtils.getDimensionOrFraction(keyAttr,
R.styleable.Keyboard_Key_visualInsetsLeft, params.mBaseWidth, 0));
- mVisualInsetsRight = Math.round(Keyboard.Builder.getDimensionOrFraction(keyAttr,
+ final int visualInsetsRight = Math.round(ResourceUtils.getDimensionOrFraction(keyAttr,
R.styleable.Keyboard_Key_visualInsetsRight, params.mBaseWidth, 0));
mIconId = KeySpecParser.getIconId(style.getString(keyAttr,
R.styleable.Keyboard_Key_keyIcon));
- mDisabledIconId = KeySpecParser.getIconId(style.getString(keyAttr,
+ final int disabledIconId = KeySpecParser.getIconId(style.getString(keyAttr,
R.styleable.Keyboard_Key_keyIconDisabled));
- mPreviewIconId = KeySpecParser.getIconId(style.getString(keyAttr,
+ final int previewIconId = KeySpecParser.getIconId(style.getString(keyAttr,
R.styleable.Keyboard_Key_keyIconPreview));
mLabelFlags = style.getFlag(keyAttr, R.styleable.Keyboard_Key_keyLabelFlags)
@@ -331,21 +349,28 @@ public class Key {
} else {
mCode = KeySpecParser.toUpperCaseOfCodeForLocale(code, needsToUpperCase, locale);
}
- mOutputText = outputText;
- mAltCode = KeySpecParser.toUpperCaseOfCodeForLocale(
+ final int altCode = KeySpecParser.toUpperCaseOfCodeForLocale(
KeySpecParser.parseCode(style.getString(keyAttr,
R.styleable.Keyboard_Key_altCode), params.mCodesSet, CODE_UNSPECIFIED),
needsToUpperCase, locale);
- mHashCode = computeHashCode(this);
-
+ if (outputText == null && altCode == CODE_UNSPECIFIED
+ && disabledIconId == ICON_UNDEFINED && previewIconId == ICON_UNDEFINED
+ && visualInsetsLeft == 0 && visualInsetsRight == 0) {
+ mOptionalAttributes = null;
+ } else {
+ mOptionalAttributes = new OptionalAttributes(outputText, altCode,
+ disabledIconId, previewIconId,
+ visualInsetsLeft, visualInsetsRight);
+ }
+ mKeyVisualAttributes = KeyVisualAttributes.newInstance(keyAttr);
keyAttr.recycle();
-
+ mHashCode = computeHashCode(this);
if (hasShiftedLetterHint() && TextUtils.isEmpty(mHintLabel)) {
Log.w(TAG, "hasShiftedLetterHint specified without keyHintLabel: " + this);
}
}
- private static boolean needsToUpperCase(int labelFlags, int keyboardElementId) {
+ private static boolean needsToUpperCase(final int labelFlags, final int keyboardElementId) {
if ((labelFlags & LABEL_FLAGS_PRESERVE_CASE) != 0) return false;
switch (keyboardElementId) {
case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED:
@@ -358,7 +383,7 @@ public class Key {
}
}
- private static int computeHashCode(Key key) {
+ private static int computeHashCode(final Key key) {
return Arrays.hashCode(new Object[] {
key.mX,
key.mY,
@@ -370,22 +395,22 @@ public class Key {
key.mIconId,
key.mBackgroundType,
Arrays.hashCode(key.mMoreKeys),
- key.mOutputText,
+ key.getOutputText(),
key.mActionFlags,
key.mLabelFlags,
// Key can be distinguishable without the following members.
- // key.mAltCode,
- // key.mDisabledIconId,
- // key.mPreviewIconId,
+ // key.mOptionalAttributes.mAltCode,
+ // key.mOptionalAttributes.mDisabledIconId,
+ // key.mOptionalAttributes.mPreviewIconId,
// key.mHorizontalGap,
// key.mVerticalGap,
- // key.mVisualInsetLeft,
- // key.mVisualInsetRight,
+ // key.mOptionalAttributes.mVisualInsetLeft,
+ // key.mOptionalAttributes.mVisualInsetRight,
// key.mMaxMoreKeysColumn,
});
}
- private boolean equals(Key o) {
+ private boolean equalsInternal(final Key o) {
if (this == o) return true;
return o.mX == mX
&& o.mY == mY
@@ -397,29 +422,42 @@ public class Key {
&& o.mIconId == mIconId
&& o.mBackgroundType == mBackgroundType
&& Arrays.equals(o.mMoreKeys, mMoreKeys)
- && TextUtils.equals(o.mOutputText, mOutputText)
+ && TextUtils.equals(o.getOutputText(), getOutputText())
&& o.mActionFlags == mActionFlags
&& o.mLabelFlags == mLabelFlags;
}
@Override
+ public int compareTo(Key o) {
+ if (equalsInternal(o)) return 0;
+ if (mHashCode > o.mHashCode) return 1;
+ return -1;
+ }
+
+ @Override
public int hashCode() {
return mHashCode;
}
@Override
- public boolean equals(Object o) {
- return o instanceof Key && equals((Key)o);
+ public boolean equals(final Object o) {
+ return o instanceof Key && equalsInternal((Key)o);
}
@Override
public String toString() {
- return String.format("%s/%s %d,%d %dx%d %s/%s/%s",
- Keyboard.printableCode(mCode), mLabel, mX, mY, mWidth, mHeight, mHintLabel,
+ final String label;
+ if (StringUtils.codePointCount(mLabel) == 1 && mLabel.codePointAt(0) == mCode) {
+ label = "";
+ } else {
+ label = "/" + mLabel;
+ }
+ return String.format("%s%s %d,%d %dx%d %s/%s/%s",
+ Keyboard.printableCode(mCode), label, mX, mY, mWidth, mHeight, mHintLabel,
KeyboardIconsSet.getIconName(mIconId), backgroundName(mBackgroundType));
}
- private static String backgroundName(int backgroundType) {
+ private static String backgroundName(final int backgroundType) {
switch (backgroundType) {
case BACKGROUND_TYPE_NORMAL: return "normal";
case BACKGROUND_TYPE_FUNCTIONAL: return "functional";
@@ -430,19 +468,19 @@ public class Key {
}
}
- public void markAsLeftEdge(Keyboard.Params params) {
+ public void markAsLeftEdge(final KeyboardParams params) {
mHitBox.left = params.mHorizontalEdgesPadding;
}
- public void markAsRightEdge(Keyboard.Params params) {
+ public void markAsRightEdge(final KeyboardParams params) {
mHitBox.right = params.mOccupiedWidth - params.mHorizontalEdgesPadding;
}
- public void markAsTopEdge(Keyboard.Params params) {
+ public void markAsTopEdge(final KeyboardParams params) {
mHitBox.top = params.mTopPadding;
}
- public void markAsBottomEdge(Keyboard.Params params) {
+ public void markAsBottomEdge(final KeyboardParams params) {
mHitBox.bottom = params.mOccupiedHeight + params.mBottomPadding;
}
@@ -450,129 +488,169 @@ public class Key {
return this instanceof Spacer;
}
- public boolean isShift() {
+ public final boolean isShift() {
return mCode == CODE_SHIFT;
}
- public boolean isModifier() {
+ public final boolean isModifier() {
return mCode == CODE_SHIFT || mCode == CODE_SWITCH_ALPHA_SYMBOL;
}
- public boolean isRepeatable() {
+ public final boolean isRepeatable() {
return (mActionFlags & ACTION_FLAGS_IS_REPEATABLE) != 0;
}
- public boolean noKeyPreview() {
+ public final boolean noKeyPreview() {
return (mActionFlags & ACTION_FLAGS_NO_KEY_PREVIEW) != 0;
}
- public boolean altCodeWhileTyping() {
+ public final boolean altCodeWhileTyping() {
return (mActionFlags & ACTION_FLAGS_ALT_CODE_WHILE_TYPING) != 0;
}
- public boolean isLongPressEnabled() {
+ public final boolean isLongPressEnabled() {
// We need not start long press timer on the key which has activated shifted letter.
return (mActionFlags & ACTION_FLAGS_ENABLE_LONG_PRESS) != 0
&& (mLabelFlags & LABEL_FLAGS_SHIFTED_LETTER_ACTIVATED) == 0;
}
- public Typeface selectTypeface(Typeface defaultTypeface) {
+ public final Typeface selectTypeface(final KeyDrawParams params) {
// TODO: Handle "bold" here too?
if ((mLabelFlags & LABEL_FLAGS_FONT_NORMAL) != 0) {
return Typeface.DEFAULT;
} else if ((mLabelFlags & LABEL_FLAGS_FONT_MONO_SPACE) != 0) {
return Typeface.MONOSPACE;
} else {
- return defaultTypeface;
+ return params.mTypeface;
}
}
- public int selectTextSize(int letterSize, int largeLetterSize, int labelSize,
- int largeLabelSize, int hintLabelSize) {
+ public final int selectTextSize(final KeyDrawParams params) {
switch (mLabelFlags & LABEL_FLAGS_FOLLOW_KEY_TEXT_RATIO_MASK) {
case LABEL_FLAGS_FOLLOW_KEY_LETTER_RATIO:
- return letterSize;
+ return params.mLetterSize;
case LABEL_FLAGS_FOLLOW_KEY_LARGE_LETTER_RATIO:
- return largeLetterSize;
+ return params.mLargeLetterSize;
case LABEL_FLAGS_FOLLOW_KEY_LABEL_RATIO:
- return labelSize;
+ return params.mLabelSize;
case LABEL_FLAGS_FOLLOW_KEY_LARGE_LABEL_RATIO:
- return largeLabelSize;
+ return params.mLargeLabelSize;
case LABEL_FLAGS_FOLLOW_KEY_HINT_LABEL_RATIO:
- return hintLabelSize;
+ return params.mHintLabelSize;
default: // No follow key ratio flag specified.
- return StringUtils.codePointCount(mLabel) == 1 ? letterSize : labelSize;
+ return StringUtils.codePointCount(mLabel) == 1 ? params.mLetterSize : params.mLabelSize;
+ }
+ }
+
+ public final int selectTextColor(final KeyDrawParams params) {
+ return isShiftedLetterActivated() ? params.mTextInactivatedColor : params.mTextColor;
+ }
+
+ public final int selectHintTextSize(final KeyDrawParams params) {
+ if (hasHintLabel()) {
+ return params.mHintLabelSize;
+ } else if (hasShiftedLetterHint()) {
+ return params.mShiftedLetterHintSize;
+ } else {
+ return params.mHintLetterSize;
}
}
- public boolean isAlignLeft() {
+ public final int selectHintTextColor(final KeyDrawParams params) {
+ if (hasHintLabel()) {
+ return params.mHintLabelColor;
+ } else if (hasShiftedLetterHint()) {
+ return isShiftedLetterActivated() ? params.mShiftedLetterHintActivatedColor
+ : params.mShiftedLetterHintInactivatedColor;
+ } else {
+ return params.mHintLetterColor;
+ }
+ }
+
+ public final int selectMoreKeyTextSize(final KeyDrawParams params) {
+ return hasLabelsInMoreKeys() ? params.mLabelSize : params.mLetterSize;
+ }
+
+ public final boolean isAlignLeft() {
return (mLabelFlags & LABEL_FLAGS_ALIGN_LEFT) != 0;
}
- public boolean isAlignRight() {
+ public final boolean isAlignRight() {
return (mLabelFlags & LABEL_FLAGS_ALIGN_RIGHT) != 0;
}
- public boolean isAlignLeftOfCenter() {
+ public final boolean isAlignLeftOfCenter() {
return (mLabelFlags & LABEL_FLAGS_ALIGN_LEFT_OF_CENTER) != 0;
}
- public boolean hasPopupHint() {
+ public final boolean hasPopupHint() {
return (mLabelFlags & LABEL_FLAGS_HAS_POPUP_HINT) != 0;
}
- public boolean hasShiftedLetterHint() {
+ public final boolean hasShiftedLetterHint() {
return (mLabelFlags & LABEL_FLAGS_HAS_SHIFTED_LETTER_HINT) != 0;
}
- public boolean hasHintLabel() {
+ public final boolean hasHintLabel() {
return (mLabelFlags & LABEL_FLAGS_HAS_HINT_LABEL) != 0;
}
- public boolean hasLabelWithIconLeft() {
+ public final boolean hasLabelWithIconLeft() {
return (mLabelFlags & LABEL_FLAGS_WITH_ICON_LEFT) != 0;
}
- public boolean hasLabelWithIconRight() {
+ public final boolean hasLabelWithIconRight() {
return (mLabelFlags & LABEL_FLAGS_WITH_ICON_RIGHT) != 0;
}
- public boolean needsXScale() {
+ public final boolean needsXScale() {
return (mLabelFlags & LABEL_FLAGS_AUTO_X_SCALE) != 0;
}
- public boolean isShiftedLetterActivated() {
+ public final boolean isShiftedLetterActivated() {
return (mLabelFlags & LABEL_FLAGS_SHIFTED_LETTER_ACTIVATED) != 0;
}
- public int getMoreKeysColumn() {
+ public final int getMoreKeysColumn() {
return mMoreKeysColumnAndFlags & MORE_KEYS_COLUMN_MASK;
}
- public boolean isFixedColumnOrderMoreKeys() {
+ public final boolean isFixedColumnOrderMoreKeys() {
return (mMoreKeysColumnAndFlags & MORE_KEYS_FLAGS_FIXED_COLUMN_ORDER) != 0;
}
- public boolean hasLabelsInMoreKeys() {
+ public final boolean hasLabelsInMoreKeys() {
return (mMoreKeysColumnAndFlags & MORE_KEYS_FLAGS_HAS_LABELS) != 0;
}
- public int getMoreKeyLabelFlags() {
+ public final int getMoreKeyLabelFlags() {
return hasLabelsInMoreKeys()
? LABEL_FLAGS_FOLLOW_KEY_LABEL_RATIO
: LABEL_FLAGS_FOLLOW_KEY_LETTER_RATIO;
}
- public boolean needsDividersInMoreKeys() {
+ public final boolean needsDividersInMoreKeys() {
return (mMoreKeysColumnAndFlags & MORE_KEYS_FLAGS_NEEDS_DIVIDERS) != 0;
}
- public boolean hasEmbeddedMoreKey() {
+ public final boolean hasEmbeddedMoreKey() {
return (mMoreKeysColumnAndFlags & MORE_KEYS_FLAGS_EMBEDDED_MORE_KEY) != 0;
}
- public Drawable getIcon(KeyboardIconsSet iconSet, int alpha) {
- final int iconId = mEnabled ? mIconId : mDisabledIconId;
+ public final String getOutputText() {
+ final OptionalAttributes attrs = mOptionalAttributes;
+ return (attrs != null) ? attrs.mOutputText : null;
+ }
+
+ public final int getAltCode() {
+ final OptionalAttributes attrs = mOptionalAttributes;
+ return (attrs != null) ? attrs.mAltCode : CODE_UNSPECIFIED;
+ }
+
+ public Drawable getIcon(final KeyboardIconsSet iconSet, final int alpha) {
+ final OptionalAttributes attrs = mOptionalAttributes;
+ final int disabledIconId = (attrs != null) ? attrs.mDisabledIconId : ICON_UNDEFINED;
+ final int iconId = mEnabled ? mIconId : disabledIconId;
final Drawable icon = iconSet.getIconDrawable(iconId);
if (icon != null) {
icon.setAlpha(alpha);
@@ -580,10 +658,22 @@ public class Key {
return icon;
}
- public Drawable getPreviewIcon(KeyboardIconsSet iconSet) {
- return mPreviewIconId != ICON_UNDEFINED
- ? iconSet.getIconDrawable(mPreviewIconId)
- : iconSet.getIconDrawable(mIconId);
+ public Drawable getPreviewIcon(final KeyboardIconsSet iconSet) {
+ final OptionalAttributes attrs = mOptionalAttributes;
+ final int previewIconId = (attrs != null) ? attrs.mPreviewIconId : ICON_UNDEFINED;
+ return previewIconId != ICON_UNDEFINED
+ ? iconSet.getIconDrawable(previewIconId) : iconSet.getIconDrawable(mIconId);
+ }
+
+ public final int getDrawX() {
+ final OptionalAttributes attrs = mOptionalAttributes;
+ return (attrs == null) ? mX : mX + attrs.mVisualInsetsLeft;
+ }
+
+ public final int getDrawWidth() {
+ final OptionalAttributes attrs = mOptionalAttributes;
+ return (attrs == null) ? mWidth
+ : mWidth - attrs.mVisualInsetsLeft - attrs.mVisualInsetsRight;
}
/**
@@ -604,11 +694,11 @@ public class Key {
mPressed = false;
}
- public boolean isEnabled() {
+ public final boolean isEnabled() {
return mEnabled;
}
- public void setEnabled(boolean enabled) {
+ public void setEnabled(final boolean enabled) {
mEnabled = enabled;
}
@@ -618,9 +708,9 @@ public class Key {
* @param y the y-coordinate of the point
* @return whether or not the point falls on the key. If the key is attached to an edge, it
* will assume that all points between the key and the edge are considered to be on the key.
- * @see #markAsLeftEdge(Keyboard.Params) etc.
+ * @see #markAsLeftEdge(KeyboardParams) etc.
*/
- public boolean isOnKey(int x, int y) {
+ public boolean isOnKey(final int x, final int y) {
return mHitBox.contains(x, y);
}
@@ -630,7 +720,7 @@ public class Key {
* @param y the y-coordinate of the point
* @return the square of the distance of the point from the nearest edge of the key
*/
- public int squaredDistanceToEdge(int x, int y) {
+ public int squaredDistanceToEdge(final int x, final int y) {
final int left = mX;
final int right = left + mWidth;
final int top = mY;
@@ -696,7 +786,7 @@ public class Key {
* @return the drawable state of the key.
* @see android.graphics.drawable.StateListDrawable#setState(int[])
*/
- public int[] getCurrentDrawableState() {
+ public final int[] getCurrentDrawableState() {
switch (mBackgroundType) {
case BACKGROUND_TYPE_FUNCTIONAL:
return mPressed ? KEY_STATE_FUNCTIONAL_PRESSED : KEY_STATE_FUNCTIONAL_NORMAL;
@@ -712,15 +802,16 @@ public class Key {
}
public static class Spacer extends Key {
- public Spacer(Resources res, Keyboard.Params params, Keyboard.Builder.Row row,
- XmlPullParser parser) throws XmlPullParserException {
+ public Spacer(final Resources res, final KeyboardParams params, final KeyboardRow row,
+ final XmlPullParser parser) throws XmlPullParserException {
super(res, params, row, parser);
}
/**
* This constructor is being used only for divider in more keys keyboard.
*/
- protected Spacer(Keyboard.Params params, int x, int y, int width, int height) {
+ protected Spacer(final KeyboardParams params, final int x, final int y, final int width,
+ final int height) {
super(params, null, null, ICON_UNDEFINED, CODE_UNSPECIFIED,
null, x, y, width, height, 0);
}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyDetector.java b/java/src/com/android/inputmethod/keyboard/KeyDetector.java
index 13e909c7e..aa683c1d7 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyDetector.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyDetector.java
@@ -16,61 +16,71 @@
package com.android.inputmethod.keyboard;
+import com.android.inputmethod.latin.Constants;
-public class KeyDetector {
- public static final int NOT_A_CODE = -1;
+public class KeyDetector {
private final int mKeyHysteresisDistanceSquared;
+ private final int mKeyHysteresisDistanceForSlidingModifierSquared;
private Keyboard mKeyboard;
private int mCorrectionX;
private int mCorrectionY;
- private boolean mProximityCorrectOn;
/**
* This class handles key detection.
*
* @param keyHysteresisDistance if the pointer movement distance is smaller than this, the
- * movement will not been handled as meaningful movement. The unit is pixel.
+ * movement will not be handled as meaningful movement. The unit is pixel.
*/
public KeyDetector(float keyHysteresisDistance) {
+ this(keyHysteresisDistance, keyHysteresisDistance);
+ }
+
+ /**
+ * This class handles key detection.
+ *
+ * @param keyHysteresisDistance if the pointer movement distance is smaller than this, the
+ * movement will not be handled as meaningful movement. The unit is pixel.
+ * @param keyHysteresisDistanceForSlidingModifier the same parameter for sliding input that
+ * starts from a modifier key such as shift and symbols key.
+ */
+ public KeyDetector(float keyHysteresisDistance, float keyHysteresisDistanceForSlidingModifier) {
mKeyHysteresisDistanceSquared = (int)(keyHysteresisDistance * keyHysteresisDistance);
+ mKeyHysteresisDistanceForSlidingModifierSquared = (int)(
+ keyHysteresisDistanceForSlidingModifier * keyHysteresisDistanceForSlidingModifier);
}
public void setKeyboard(Keyboard keyboard, float correctionX, float correctionY) {
- if (keyboard == null)
+ if (keyboard == null) {
throw new NullPointerException();
+ }
mCorrectionX = (int)correctionX;
mCorrectionY = (int)correctionY;
mKeyboard = keyboard;
}
- public int getKeyHysteresisDistanceSquared() {
- return mKeyHysteresisDistanceSquared;
+ public int getKeyHysteresisDistanceSquared(boolean isSlidingFromModifier) {
+ return isSlidingFromModifier
+ ? mKeyHysteresisDistanceForSlidingModifierSquared : mKeyHysteresisDistanceSquared;
}
public int getTouchX(int x) {
return x + mCorrectionX;
}
+ // TODO: Remove vertical correction.
public int getTouchY(int y) {
return y + mCorrectionY;
}
public Keyboard getKeyboard() {
- if (mKeyboard == null)
+ if (mKeyboard == null) {
throw new IllegalStateException("keyboard isn't set");
+ }
return mKeyboard;
}
- public void setProximityCorrectionEnabled(boolean enabled) {
- mProximityCorrectOn = enabled;
- }
-
- public boolean isProximityCorrectionEnabled() {
- return mProximityCorrectOn;
- }
-
public boolean alwaysAllowsSlidingInput() {
return false;
}
@@ -89,11 +99,17 @@ public class KeyDetector {
int minDistance = Integer.MAX_VALUE;
Key primaryKey = null;
for (final Key key: mKeyboard.getNearestKeys(touchX, touchY)) {
- final boolean isOnKey = key.isOnKey(touchX, touchY);
+ // An edge key always has its enlarged hitbox to respond to an event that occurred in
+ // the empty area around the key. (@see Key#markAsLeftEdge(KeyboardParams)} etc.)
+ if (!key.isOnKey(touchX, touchY)) {
+ continue;
+ }
final int distance = key.squaredDistanceToEdge(touchX, touchY);
+ if (distance > minDistance) {
+ continue;
+ }
// To take care of hitbox overlaps, we compare mCode here too.
- if (primaryKey == null || distance < minDistance
- || (distance == minDistance && isOnKey && key.mCode > primaryKey.mCode)) {
+ if (primaryKey == null || distance < minDistance || key.mCode > primaryKey.mCode) {
minDistance = distance;
primaryKey = key;
}
@@ -109,7 +125,7 @@ public class KeyDetector {
final StringBuilder sb = new StringBuilder();
boolean addDelimiter = false;
for (final int code : codes) {
- if (code == NOT_A_CODE) break;
+ if (code == Constants.NOT_A_CODE) break;
if (addDelimiter) sb.append(", ");
sb.append(Keyboard.printableCode(code));
addDelimiter = true;
diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java
index 21f175d7d..b7c7f415d 100644
--- a/java/src/com/android/inputmethod/keyboard/Keyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java
@@ -16,37 +16,15 @@
package com.android.inputmethod.keyboard;
-import android.content.Context;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
-import android.util.AttributeSet;
-import android.util.DisplayMetrics;
import android.util.Log;
-import android.util.TypedValue;
-import android.util.Xml;
-import android.view.InflateException;
+import android.util.SparseArray;
-import com.android.inputmethod.keyboard.internal.KeyStyles;
-import com.android.inputmethod.keyboard.internal.KeyboardCodesSet;
+import com.android.inputmethod.keyboard.internal.KeyVisualAttributes;
import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
-import com.android.inputmethod.keyboard.internal.KeyboardTextsSet;
-import com.android.inputmethod.latin.LatinImeLogger;
-import com.android.inputmethod.latin.LocaleUtils.RunInLocale;
-import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.SubtypeLocale;
-import com.android.inputmethod.latin.Utils;
-import com.android.inputmethod.latin.XmlParseUtils;
+import com.android.inputmethod.keyboard.internal.KeyboardParams;
+import com.android.inputmethod.latin.CollectionUtils;
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Locale;
/**
* Loads an XML description of a keyboard and stores the attributes of the keys. A keyboard
@@ -79,6 +57,8 @@ public class Keyboard {
public static final int CODE_DASH = '-';
public static final int CODE_SINGLE_QUOTE = '\'';
public static final int CODE_DOUBLE_QUOTE = '"';
+ public static final int CODE_QUESTION_MARK = '?';
+ public static final int CODE_EXCLAMATION_MARK = '!';
// TODO: Check how this should work for right-to-left languages. It seems to stand
// that for rtl languages, a closing parenthesis is a left parenthesis. Is this
// managed by the font? Or is it a different char?
@@ -86,10 +66,10 @@ public class Keyboard {
public static final int CODE_CLOSING_SQUARE_BRACKET = ']';
public static final int CODE_CLOSING_CURLY_BRACKET = '}';
public static final int CODE_CLOSING_ANGLE_BRACKET = '>';
- private static final int MINIMUM_LETTER_CODE = CODE_TAB;
/** Special keys code. Must be negative.
- * These should be aligned with values/keycodes.xml
+ * These should be aligned with KeyboardCodesSet.ID_TO_NAME[],
+ * KeyboardCodesSet.DEFAULT[] and KeyboardCodesSet.RTL[]
*/
public static final int CODE_SHIFT = -1;
public static final int CODE_SWITCH_ALPHA_SYMBOL = -2;
@@ -101,8 +81,9 @@ public class Keyboard {
public static final int CODE_ACTION_NEXT = -8;
public static final int CODE_ACTION_PREVIOUS = -9;
public static final int CODE_LANGUAGE_SWITCH = -10;
+ public static final int CODE_RESEARCH = -11;
// Code value representing the code is not specified.
- public static final int CODE_UNSPECIFIED = -11;
+ public static final int CODE_UNSPECIFIED = -12;
public final KeyboardId mId;
public final int mThemeId;
@@ -117,6 +98,9 @@ public class Keyboard {
/** Default gap between rows */
public final int mVerticalGap;
+ /** Per keyboard key visual parameters */
+ public final KeyVisualAttributes mKeyVisualAttributes;
+
public final int mMostCommonKeyHeight;
public final int mMostCommonKeyWidth;
@@ -132,12 +116,12 @@ public class Keyboard {
public final Key[] mAltCodeKeysWhileTyping;
public final KeyboardIconsSet mIconsSet;
- private final HashMap<Integer, Key> mKeyCache = new HashMap<Integer, Key>();
+ private final SparseArray<Key> mKeyCache = CollectionUtils.newSparseArray();
private final ProximityInfo mProximityInfo;
private final boolean mProximityCharsCorrectionEnabled;
- public Keyboard(Params params) {
+ public Keyboard(final KeyboardParams params) {
mId = params.mId;
mThemeId = params.mThemeId;
mOccupiedHeight = params.mOccupiedHeight;
@@ -146,7 +130,7 @@ public class Keyboard {
mMostCommonKeyWidth = params.mMostCommonKeyWidth;
mMoreKeysTemplate = params.mMoreKeysTemplate;
mMaxMoreKeysKeyboardColumn = params.mMaxMoreKeysKeyboardColumn;
-
+ mKeyVisualAttributes = params.mKeyVisualAttributes;
mTopPadding = params.mTopPadding;
mVerticalGap = params.mVerticalGap;
@@ -162,7 +146,7 @@ public class Keyboard {
mProximityCharsCorrectionEnabled = params.mProximityCharsCorrectionEnabled;
}
- public boolean hasProximityCharsCorrection(int code) {
+ public boolean hasProximityCharsCorrection(final int code) {
if (!mProximityCharsCorrectionEnabled) {
return false;
}
@@ -178,27 +162,29 @@ public class Keyboard {
return mProximityInfo;
}
- public Key getKey(int code) {
+ public Key getKey(final int code) {
if (code == CODE_UNSPECIFIED) {
return null;
}
- final Integer keyCode = code;
- if (mKeyCache.containsKey(keyCode)) {
- return mKeyCache.get(keyCode);
- }
+ synchronized (mKeyCache) {
+ final int index = mKeyCache.indexOfKey(code);
+ if (index >= 0) {
+ return mKeyCache.valueAt(index);
+ }
- for (final Key key : mKeys) {
- if (key.mCode == code) {
- mKeyCache.put(keyCode, key);
- return key;
+ for (final Key key : mKeys) {
+ if (key.mCode == code) {
+ mKeyCache.put(code, key);
+ return key;
+ }
}
+ mKeyCache.put(code, null);
+ return null;
}
- mKeyCache.put(keyCode, null);
- return null;
}
- public boolean hasKey(Key aKey) {
- if (mKeyCache.containsKey(aKey)) {
+ public boolean hasKey(final Key aKey) {
+ if (mKeyCache.indexOfValue(aKey) >= 0) {
return true;
}
@@ -211,172 +197,13 @@ public class Keyboard {
return false;
}
- public static boolean isLetterCode(int code) {
- return code >= MINIMUM_LETTER_CODE;
+ public static boolean isLetterCode(final int code) {
+ return code >= CODE_SPACE;
}
- public static class Params {
- public KeyboardId mId;
- public int mThemeId;
-
- /** Total height and width of the keyboard, including the paddings and keys */
- public int mOccupiedHeight;
- public int mOccupiedWidth;
-
- /** Base height and width of the keyboard used to calculate rows' or keys' heights and
- * widths
- */
- public int mBaseHeight;
- public int mBaseWidth;
-
- public int mTopPadding;
- public int mBottomPadding;
- public int mHorizontalEdgesPadding;
- public int mHorizontalCenterPadding;
-
- public int mDefaultRowHeight;
- public int mDefaultKeyWidth;
- public int mHorizontalGap;
- public int mVerticalGap;
-
- public int mMoreKeysTemplate;
- public int mMaxMoreKeysKeyboardColumn;
-
- public int GRID_WIDTH;
- public int GRID_HEIGHT;
-
- public final HashSet<Key> mKeys = new HashSet<Key>();
- public final ArrayList<Key> mShiftKeys = new ArrayList<Key>();
- public final ArrayList<Key> mAltCodeKeysWhileTyping = new ArrayList<Key>();
- public final KeyboardIconsSet mIconsSet = new KeyboardIconsSet();
- public final KeyboardCodesSet mCodesSet = new KeyboardCodesSet();
- public final KeyboardTextsSet mTextsSet = new KeyboardTextsSet();
- public final KeyStyles mKeyStyles = new KeyStyles(mTextsSet);
-
- public KeyboardLayoutSet.KeysCache mKeysCache;
-
- public int mMostCommonKeyHeight = 0;
- public int mMostCommonKeyWidth = 0;
-
- public boolean mProximityCharsCorrectionEnabled;
-
- public final TouchPositionCorrection mTouchPositionCorrection =
- new TouchPositionCorrection();
-
- public static class TouchPositionCorrection {
- private static final int TOUCH_POSITION_CORRECTION_RECORD_SIZE = 3;
-
- public boolean mEnabled;
- public float[] mXs;
- public float[] mYs;
- public float[] mRadii;
-
- public void load(String[] data) {
- final int dataLength = data.length;
- if (dataLength % TOUCH_POSITION_CORRECTION_RECORD_SIZE != 0) {
- if (LatinImeLogger.sDBG)
- throw new RuntimeException(
- "the size of touch position correction data is invalid");
- return;
- }
-
- final int length = dataLength / TOUCH_POSITION_CORRECTION_RECORD_SIZE;
- mXs = new float[length];
- mYs = new float[length];
- mRadii = new float[length];
- try {
- for (int i = 0; i < dataLength; ++i) {
- final int type = i % TOUCH_POSITION_CORRECTION_RECORD_SIZE;
- final int index = i / TOUCH_POSITION_CORRECTION_RECORD_SIZE;
- final float value = Float.parseFloat(data[i]);
- if (type == 0) {
- mXs[index] = value;
- } else if (type == 1) {
- mYs[index] = value;
- } else {
- mRadii[index] = value;
- }
- }
- } catch (NumberFormatException e) {
- if (LatinImeLogger.sDBG) {
- throw new RuntimeException(
- "the number format for touch position correction data is invalid");
- }
- mXs = null;
- mYs = null;
- mRadii = null;
- }
- }
-
- // TODO: Remove this method.
- public void setEnabled(boolean enabled) {
- mEnabled = enabled;
- }
-
- public boolean isValid() {
- return mEnabled && mXs != null && mYs != null && mRadii != null
- && mXs.length > 0 && mYs.length > 0 && mRadii.length > 0;
- }
- }
-
- protected void clearKeys() {
- mKeys.clear();
- mShiftKeys.clear();
- clearHistogram();
- }
-
- public void onAddKey(Key newKey) {
- final Key key = (mKeysCache != null) ? mKeysCache.get(newKey) : newKey;
- final boolean zeroWidthSpacer = key.isSpacer() && key.mWidth == 0;
- if (!zeroWidthSpacer) {
- mKeys.add(key);
- updateHistogram(key);
- }
- if (key.mCode == Keyboard.CODE_SHIFT) {
- mShiftKeys.add(key);
- }
- if (key.altCodeWhileTyping()) {
- mAltCodeKeysWhileTyping.add(key);
- }
- }
-
- private int mMaxHeightCount = 0;
- private int mMaxWidthCount = 0;
- private final HashMap<Integer, Integer> mHeightHistogram = new HashMap<Integer, Integer>();
- private final HashMap<Integer, Integer> mWidthHistogram = new HashMap<Integer, Integer>();
-
- private void clearHistogram() {
- mMostCommonKeyHeight = 0;
- mMaxHeightCount = 0;
- mHeightHistogram.clear();
-
- mMaxWidthCount = 0;
- mMostCommonKeyWidth = 0;
- mWidthHistogram.clear();
- }
-
- private static int updateHistogramCounter(HashMap<Integer, Integer> histogram,
- Integer key) {
- final int count = (histogram.containsKey(key) ? histogram.get(key) : 0) + 1;
- histogram.put(key, count);
- return count;
- }
-
- private void updateHistogram(Key key) {
- final Integer height = key.mHeight + key.mVerticalGap;
- final int heightCount = updateHistogramCounter(mHeightHistogram, height);
- if (heightCount > mMaxHeightCount) {
- mMaxHeightCount = heightCount;
- mMostCommonKeyHeight = height;
- }
-
- final Integer width = key.mWidth + key.mHorizontalGap;
- final int widthCount = updateHistogramCounter(mWidthHistogram, width);
- if (widthCount > mMaxWidthCount) {
- mMaxWidthCount = widthCount;
- mMostCommonKeyWidth = width;
- }
- }
+ @Override
+ public String toString() {
+ return mId.toString();
}
/**
@@ -386,14 +213,14 @@ public class Keyboard {
* @return the array of the nearest keys to the given point. If the given
* point is out of range, then an array of size zero is returned.
*/
- public Key[] getNearestKeys(int x, int y) {
+ public Key[] getNearestKeys(final int x, final int y) {
// Avoid dead pixels at edges of the keyboard
final int adjustedX = Math.max(0, Math.min(x, mOccupiedWidth - 1));
final int adjustedY = Math.max(0, Math.min(y, mOccupiedHeight - 1));
return mProximityInfo.getNearestKeys(adjustedX, adjustedY);
}
- public static String printableCode(int code) {
+ public static String printableCode(final int code) {
switch (code) {
case CODE_SHIFT: return "shift";
case CODE_SWITCH_ALPHA_SYMBOL: return "symbol";
@@ -415,937 +242,4 @@ public class Keyboard {
return String.format("'\\u%04x'", code);
}
}
-
- /**
- * Keyboard Building helper.
- *
- * This class parses Keyboard XML file and eventually build a Keyboard.
- * The Keyboard XML file looks like:
- * <pre>
- * &gt;!-- xml/keyboard.xml --&lt;
- * &gt;Keyboard keyboard_attributes*&lt;
- * &gt;!-- Keyboard Content --&lt;
- * &gt;Row row_attributes*&lt;
- * &gt;!-- Row Content --&lt;
- * &gt;Key key_attributes* /&lt;
- * &gt;Spacer horizontalGap="32.0dp" /&lt;
- * &gt;include keyboardLayout="@xml/other_keys"&lt;
- * ...
- * &gt;/Row&lt;
- * &gt;include keyboardLayout="@xml/other_rows"&lt;
- * ...
- * &gt;/Keyboard&lt;
- * </pre>
- * The XML file which is included in other file must have &gt;merge&lt; as root element,
- * such as:
- * <pre>
- * &gt;!-- xml/other_keys.xml --&lt;
- * &gt;merge&lt;
- * &gt;Key key_attributes* /&lt;
- * ...
- * &gt;/merge&lt;
- * </pre>
- * and
- * <pre>
- * &gt;!-- xml/other_rows.xml --&lt;
- * &gt;merge&lt;
- * &gt;Row row_attributes*&lt;
- * &gt;Key key_attributes* /&lt;
- * &gt;/Row&lt;
- * ...
- * &gt;/merge&lt;
- * </pre>
- * You can also use switch-case-default tags to select Rows and Keys.
- * <pre>
- * &gt;switch&lt;
- * &gt;case case_attribute*&lt;
- * &gt;!-- Any valid tags at switch position --&lt;
- * &gt;/case&lt;
- * ...
- * &gt;default&lt;
- * &gt;!-- Any valid tags at switch position --&lt;
- * &gt;/default&lt;
- * &gt;/switch&lt;
- * </pre>
- * You can declare Key style and specify styles within Key tags.
- * <pre>
- * &gt;switch&lt;
- * &gt;case mode="email"&lt;
- * &gt;key-style styleName="f1-key" parentStyle="modifier-key"
- * keyLabel=".com"
- * /&lt;
- * &gt;/case&lt;
- * &gt;case mode="url"&lt;
- * &gt;key-style styleName="f1-key" parentStyle="modifier-key"
- * keyLabel="http://"
- * /&lt;
- * &gt;/case&lt;
- * &gt;/switch&lt;
- * ...
- * &gt;Key keyStyle="shift-key" ... /&lt;
- * </pre>
- */
-
- public static class Builder<KP extends Params> {
- private static final String BUILDER_TAG = "Keyboard.Builder";
- private static final boolean DEBUG = false;
-
- // Keyboard XML Tags
- private static final String TAG_KEYBOARD = "Keyboard";
- private static final String TAG_ROW = "Row";
- private static final String TAG_KEY = "Key";
- private static final String TAG_SPACER = "Spacer";
- private static final String TAG_INCLUDE = "include";
- private static final String TAG_MERGE = "merge";
- private static final String TAG_SWITCH = "switch";
- private static final String TAG_CASE = "case";
- private static final String TAG_DEFAULT = "default";
- public static final String TAG_KEY_STYLE = "key-style";
-
- private static final int DEFAULT_KEYBOARD_COLUMNS = 10;
- private static final int DEFAULT_KEYBOARD_ROWS = 4;
-
- protected final KP mParams;
- protected final Context mContext;
- protected final Resources mResources;
- private final DisplayMetrics mDisplayMetrics;
-
- private int mCurrentY = 0;
- private Row mCurrentRow = null;
- private boolean mLeftEdge;
- private boolean mTopEdge;
- private Key mRightEdgeKey = null;
-
- /**
- * Container for keys in the keyboard. All keys in a row are at the same Y-coordinate.
- * Some of the key size defaults can be overridden per row from what the {@link Keyboard}
- * defines.
- */
- public static class Row {
- // keyWidth enum constants
- private static final int KEYWIDTH_NOT_ENUM = 0;
- private static final int KEYWIDTH_FILL_RIGHT = -1;
-
- private final Params mParams;
- /** Default width of a key in this row. */
- private float mDefaultKeyWidth;
- /** Default height of a key in this row. */
- public final int mRowHeight;
- /** Default keyLabelFlags in this row. */
- private int mDefaultKeyLabelFlags;
- /** Default backgroundType for this row */
- private int mDefaultBackgroundType;
-
- private final int mCurrentY;
- // Will be updated by {@link Key}'s constructor.
- private float mCurrentX;
-
- public Row(Resources res, Params params, XmlPullParser parser, int y) {
- mParams = params;
- TypedArray keyboardAttr = res.obtainAttributes(Xml.asAttributeSet(parser),
- R.styleable.Keyboard);
- mRowHeight = (int)Builder.getDimensionOrFraction(keyboardAttr,
- R.styleable.Keyboard_rowHeight,
- params.mBaseHeight, params.mDefaultRowHeight);
- keyboardAttr.recycle();
- TypedArray keyAttr = res.obtainAttributes(Xml.asAttributeSet(parser),
- R.styleable.Keyboard_Key);
- mDefaultKeyWidth = Builder.getDimensionOrFraction(keyAttr,
- R.styleable.Keyboard_Key_keyWidth,
- params.mBaseWidth, params.mDefaultKeyWidth);
- mDefaultBackgroundType = keyAttr.getInt(R.styleable.Keyboard_Key_backgroundType,
- Key.BACKGROUND_TYPE_NORMAL);
- keyAttr.recycle();
-
- // TODO: Initialize this with <Row> attribute as backgroundType is done.
- mDefaultKeyLabelFlags = 0;
- mCurrentY = y;
- mCurrentX = 0.0f;
- }
-
- public float getDefaultKeyWidth() {
- return mDefaultKeyWidth;
- }
-
- public void setDefaultKeyWidth(float defaultKeyWidth) {
- mDefaultKeyWidth = defaultKeyWidth;
- }
-
- public int getDefaultKeyLabelFlags() {
- return mDefaultKeyLabelFlags;
- }
-
- public void setDefaultKeyLabelFlags(int keyLabelFlags) {
- mDefaultKeyLabelFlags = keyLabelFlags;
- }
-
- public int getDefaultBackgroundType() {
- return mDefaultBackgroundType;
- }
-
- public void setDefaultBackgroundType(int backgroundType) {
- mDefaultBackgroundType = backgroundType;
- }
-
- public void setXPos(float keyXPos) {
- mCurrentX = keyXPos;
- }
-
- public void advanceXPos(float width) {
- mCurrentX += width;
- }
-
- public int getKeyY() {
- return mCurrentY;
- }
-
- public float getKeyX(TypedArray keyAttr) {
- final int widthType = Builder.getEnumValue(keyAttr,
- R.styleable.Keyboard_Key_keyWidth, KEYWIDTH_NOT_ENUM);
-
- final int keyboardRightEdge = mParams.mOccupiedWidth
- - mParams.mHorizontalEdgesPadding;
- if (keyAttr.hasValue(R.styleable.Keyboard_Key_keyXPos)) {
- final float keyXPos = Builder.getDimensionOrFraction(keyAttr,
- R.styleable.Keyboard_Key_keyXPos, mParams.mBaseWidth, 0);
- if (keyXPos < 0) {
- // If keyXPos is negative, the actual x-coordinate will be
- // keyboardWidth + keyXPos.
- // keyXPos shouldn't be less than mCurrentX because drawable area for this
- // key starts at mCurrentX. Or, this key will overlaps the adjacent key on
- // its left hand side.
- return Math.max(keyXPos + keyboardRightEdge, mCurrentX);
- } else {
- return keyXPos + mParams.mHorizontalEdgesPadding;
- }
- }
- return mCurrentX;
- }
-
- public float getKeyWidth(TypedArray keyAttr) {
- return getKeyWidth(keyAttr, mCurrentX);
- }
-
- public float getKeyWidth(TypedArray keyAttr, float keyXPos) {
- final int widthType = Builder.getEnumValue(keyAttr,
- R.styleable.Keyboard_Key_keyWidth, KEYWIDTH_NOT_ENUM);
- switch (widthType) {
- case KEYWIDTH_FILL_RIGHT:
- final int keyboardRightEdge =
- mParams.mOccupiedWidth - mParams.mHorizontalEdgesPadding;
- // If keyWidth is fillRight, the actual key width will be determined to fill
- // out the area up to the right edge of the keyboard.
- return keyboardRightEdge - keyXPos;
- default: // KEYWIDTH_NOT_ENUM
- return Builder.getDimensionOrFraction(keyAttr,
- R.styleable.Keyboard_Key_keyWidth,
- mParams.mBaseWidth, mDefaultKeyWidth);
- }
- }
- }
-
- public Builder(Context context, KP params) {
- mContext = context;
- final Resources res = context.getResources();
- mResources = res;
- mDisplayMetrics = res.getDisplayMetrics();
-
- mParams = params;
-
- params.GRID_WIDTH = res.getInteger(R.integer.config_keyboard_grid_width);
- params.GRID_HEIGHT = res.getInteger(R.integer.config_keyboard_grid_height);
- }
-
- public void setAutoGenerate(KeyboardLayoutSet.KeysCache keysCache) {
- mParams.mKeysCache = keysCache;
- }
-
- public Builder<KP> load(int xmlId, KeyboardId id) {
- mParams.mId = id;
- final XmlResourceParser parser = mResources.getXml(xmlId);
- try {
- parseKeyboard(parser);
- } catch (XmlPullParserException e) {
- Log.w(BUILDER_TAG, "keyboard XML parse error: " + e);
- throw new IllegalArgumentException(e);
- } catch (IOException e) {
- Log.w(BUILDER_TAG, "keyboard XML parse error: " + e);
- throw new RuntimeException(e);
- } finally {
- parser.close();
- }
- return this;
- }
-
- // TODO: Remove this method.
- public void setTouchPositionCorrectionEnabled(boolean enabled) {
- mParams.mTouchPositionCorrection.setEnabled(enabled);
- }
-
- public void setProximityCharsCorrectionEnabled(boolean enabled) {
- mParams.mProximityCharsCorrectionEnabled = enabled;
- }
-
- public Keyboard build() {
- return new Keyboard(mParams);
- }
-
- private int mIndent;
- private static final String SPACES = " ";
-
- private static String spaces(int count) {
- return (count < SPACES.length()) ? SPACES.substring(0, count) : SPACES;
- }
-
- private void startTag(String format, Object ... args) {
- Log.d(BUILDER_TAG, String.format(spaces(++mIndent * 2) + format, args));
- }
-
- private void endTag(String format, Object ... args) {
- Log.d(BUILDER_TAG, String.format(spaces(mIndent-- * 2) + format, args));
- }
-
- private void startEndTag(String format, Object ... args) {
- Log.d(BUILDER_TAG, String.format(spaces(++mIndent * 2) + format, args));
- mIndent--;
- }
-
- private void parseKeyboard(XmlPullParser parser)
- throws XmlPullParserException, IOException {
- if (DEBUG) startTag("<%s> %s", TAG_KEYBOARD, mParams.mId);
- int event;
- while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
- if (event == XmlPullParser.START_TAG) {
- final String tag = parser.getName();
- if (TAG_KEYBOARD.equals(tag)) {
- parseKeyboardAttributes(parser);
- startKeyboard();
- parseKeyboardContent(parser, false);
- break;
- } else {
- throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEYBOARD);
- }
- }
- }
- }
-
- private void parseKeyboardAttributes(XmlPullParser parser) {
- final int displayWidth = mDisplayMetrics.widthPixels;
- final TypedArray keyboardAttr = mContext.obtainStyledAttributes(
- Xml.asAttributeSet(parser), R.styleable.Keyboard, R.attr.keyboardStyle,
- R.style.Keyboard);
- final TypedArray keyAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser),
- R.styleable.Keyboard_Key);
- try {
- final int displayHeight = mDisplayMetrics.heightPixels;
- final String keyboardHeightString = Utils.getDeviceOverrideValue(
- mResources, R.array.keyboard_heights, null);
- final float keyboardHeight;
- if (keyboardHeightString != null) {
- keyboardHeight = Float.parseFloat(keyboardHeightString)
- * mDisplayMetrics.density;
- } else {
- keyboardHeight = keyboardAttr.getDimension(
- R.styleable.Keyboard_keyboardHeight, displayHeight / 2);
- }
- final float maxKeyboardHeight = getDimensionOrFraction(keyboardAttr,
- R.styleable.Keyboard_maxKeyboardHeight, displayHeight, displayHeight / 2);
- float minKeyboardHeight = getDimensionOrFraction(keyboardAttr,
- R.styleable.Keyboard_minKeyboardHeight, displayHeight, displayHeight / 2);
- if (minKeyboardHeight < 0) {
- // Specified fraction was negative, so it should be calculated against display
- // width.
- minKeyboardHeight = -getDimensionOrFraction(keyboardAttr,
- R.styleable.Keyboard_minKeyboardHeight, displayWidth, displayWidth / 2);
- }
- final Params params = mParams;
- // Keyboard height will not exceed maxKeyboardHeight and will not be less than
- // minKeyboardHeight.
- params.mOccupiedHeight = (int)Math.max(
- Math.min(keyboardHeight, maxKeyboardHeight), minKeyboardHeight);
- params.mOccupiedWidth = params.mId.mWidth;
- params.mTopPadding = (int)getDimensionOrFraction(keyboardAttr,
- R.styleable.Keyboard_keyboardTopPadding, params.mOccupiedHeight, 0);
- params.mBottomPadding = (int)getDimensionOrFraction(keyboardAttr,
- R.styleable.Keyboard_keyboardBottomPadding, params.mOccupiedHeight, 0);
- params.mHorizontalEdgesPadding = (int)getDimensionOrFraction(keyboardAttr,
- R.styleable.Keyboard_keyboardHorizontalEdgesPadding,
- mParams.mOccupiedWidth, 0);
-
- params.mBaseWidth = params.mOccupiedWidth - params.mHorizontalEdgesPadding * 2
- - params.mHorizontalCenterPadding;
- params.mDefaultKeyWidth = (int)getDimensionOrFraction(keyAttr,
- R.styleable.Keyboard_Key_keyWidth, params.mBaseWidth,
- params.mBaseWidth / DEFAULT_KEYBOARD_COLUMNS);
- params.mHorizontalGap = (int)getDimensionOrFraction(keyboardAttr,
- R.styleable.Keyboard_horizontalGap, params.mBaseWidth, 0);
- params.mVerticalGap = (int)getDimensionOrFraction(keyboardAttr,
- R.styleable.Keyboard_verticalGap, params.mOccupiedHeight, 0);
- params.mBaseHeight = params.mOccupiedHeight - params.mTopPadding
- - params.mBottomPadding + params.mVerticalGap;
- params.mDefaultRowHeight = (int)getDimensionOrFraction(keyboardAttr,
- R.styleable.Keyboard_rowHeight, params.mBaseHeight,
- params.mBaseHeight / DEFAULT_KEYBOARD_ROWS);
-
- params.mMoreKeysTemplate = keyboardAttr.getResourceId(
- R.styleable.Keyboard_moreKeysTemplate, 0);
- params.mMaxMoreKeysKeyboardColumn = keyAttr.getInt(
- R.styleable.Keyboard_Key_maxMoreKeysColumn, 5);
-
- params.mThemeId = keyboardAttr.getInt(R.styleable.Keyboard_themeId, 0);
- params.mIconsSet.loadIcons(keyboardAttr);
- final String language = params.mId.mLocale.getLanguage();
- params.mCodesSet.setLanguage(language);
- params.mTextsSet.setLanguage(language);
- final RunInLocale<Void> job = new RunInLocale<Void>() {
- @Override
- protected Void job(Resources res) {
- params.mTextsSet.loadStringResources(mContext);
- return null;
- }
- };
- // Null means the current system locale.
- final Locale locale = SubtypeLocale.isNoLanguage(params.mId.mSubtype)
- ? null : params.mId.mLocale;
- job.runInLocale(mResources, locale);
-
- final int resourceId = keyboardAttr.getResourceId(
- R.styleable.Keyboard_touchPositionCorrectionData, 0);
- params.mTouchPositionCorrection.setEnabled(resourceId != 0);
- if (resourceId != 0) {
- final String[] data = mResources.getStringArray(resourceId);
- params.mTouchPositionCorrection.load(data);
- }
- } finally {
- keyAttr.recycle();
- keyboardAttr.recycle();
- }
- }
-
- private void parseKeyboardContent(XmlPullParser parser, boolean skip)
- throws XmlPullParserException, IOException {
- int event;
- while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
- if (event == XmlPullParser.START_TAG) {
- final String tag = parser.getName();
- if (TAG_ROW.equals(tag)) {
- Row row = parseRowAttributes(parser);
- if (DEBUG) startTag("<%s>%s", TAG_ROW, skip ? " skipped" : "");
- if (!skip) {
- startRow(row);
- }
- parseRowContent(parser, row, skip);
- } else if (TAG_INCLUDE.equals(tag)) {
- parseIncludeKeyboardContent(parser, skip);
- } else if (TAG_SWITCH.equals(tag)) {
- parseSwitchKeyboardContent(parser, skip);
- } else if (TAG_KEY_STYLE.equals(tag)) {
- parseKeyStyle(parser, skip);
- } else {
- throw new XmlParseUtils.IllegalStartTag(parser, TAG_ROW);
- }
- } else if (event == XmlPullParser.END_TAG) {
- final String tag = parser.getName();
- if (DEBUG) endTag("</%s>", tag);
- if (TAG_KEYBOARD.equals(tag)) {
- endKeyboard();
- break;
- } else if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag)
- || TAG_MERGE.equals(tag)) {
- break;
- } else {
- throw new XmlParseUtils.IllegalEndTag(parser, TAG_ROW);
- }
- }
- }
- }
-
- private Row parseRowAttributes(XmlPullParser parser) throws XmlPullParserException {
- final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
- R.styleable.Keyboard);
- try {
- if (a.hasValue(R.styleable.Keyboard_horizontalGap))
- throw new XmlParseUtils.IllegalAttribute(parser, "horizontalGap");
- if (a.hasValue(R.styleable.Keyboard_verticalGap))
- throw new XmlParseUtils.IllegalAttribute(parser, "verticalGap");
- return new Row(mResources, mParams, parser, mCurrentY);
- } finally {
- a.recycle();
- }
- }
-
- private void parseRowContent(XmlPullParser parser, Row row, boolean skip)
- throws XmlPullParserException, IOException {
- int event;
- while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
- if (event == XmlPullParser.START_TAG) {
- final String tag = parser.getName();
- if (TAG_KEY.equals(tag)) {
- parseKey(parser, row, skip);
- } else if (TAG_SPACER.equals(tag)) {
- parseSpacer(parser, row, skip);
- } else if (TAG_INCLUDE.equals(tag)) {
- parseIncludeRowContent(parser, row, skip);
- } else if (TAG_SWITCH.equals(tag)) {
- parseSwitchRowContent(parser, row, skip);
- } else if (TAG_KEY_STYLE.equals(tag)) {
- parseKeyStyle(parser, skip);
- } else {
- throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEY);
- }
- } else if (event == XmlPullParser.END_TAG) {
- final String tag = parser.getName();
- if (DEBUG) endTag("</%s>", tag);
- if (TAG_ROW.equals(tag)) {
- if (!skip) {
- endRow(row);
- }
- break;
- } else if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag)
- || TAG_MERGE.equals(tag)) {
- break;
- } else {
- throw new XmlParseUtils.IllegalEndTag(parser, TAG_KEY);
- }
- }
- }
- }
-
- private void parseKey(XmlPullParser parser, Row row, boolean skip)
- throws XmlPullParserException, IOException {
- if (skip) {
- XmlParseUtils.checkEndTag(TAG_KEY, parser);
- if (DEBUG) startEndTag("<%s /> skipped", TAG_KEY);
- } else {
- final Key key = new Key(mResources, mParams, row, parser);
- if (DEBUG) {
- startEndTag("<%s%s %s moreKeys=%s />", TAG_KEY,
- (key.isEnabled() ? "" : " disabled"), key,
- Arrays.toString(key.mMoreKeys));
- }
- XmlParseUtils.checkEndTag(TAG_KEY, parser);
- endKey(key);
- }
- }
-
- private void parseSpacer(XmlPullParser parser, Row row, boolean skip)
- throws XmlPullParserException, IOException {
- if (skip) {
- XmlParseUtils.checkEndTag(TAG_SPACER, parser);
- if (DEBUG) startEndTag("<%s /> skipped", TAG_SPACER);
- } else {
- final Key.Spacer spacer = new Key.Spacer(mResources, mParams, row, parser);
- if (DEBUG) startEndTag("<%s />", TAG_SPACER);
- XmlParseUtils.checkEndTag(TAG_SPACER, parser);
- endKey(spacer);
- }
- }
-
- private void parseIncludeKeyboardContent(XmlPullParser parser, boolean skip)
- throws XmlPullParserException, IOException {
- parseIncludeInternal(parser, null, skip);
- }
-
- private void parseIncludeRowContent(XmlPullParser parser, Row row, boolean skip)
- throws XmlPullParserException, IOException {
- parseIncludeInternal(parser, row, skip);
- }
-
- private void parseIncludeInternal(XmlPullParser parser, Row row, boolean skip)
- throws XmlPullParserException, IOException {
- if (skip) {
- XmlParseUtils.checkEndTag(TAG_INCLUDE, parser);
- if (DEBUG) startEndTag("</%s> skipped", TAG_INCLUDE);
- } else {
- final AttributeSet attr = Xml.asAttributeSet(parser);
- final TypedArray keyboardAttr = mResources.obtainAttributes(attr,
- R.styleable.Keyboard_Include);
- final TypedArray keyAttr = mResources.obtainAttributes(attr,
- R.styleable.Keyboard_Key);
- int keyboardLayout = 0;
- float savedDefaultKeyWidth = 0;
- int savedDefaultKeyLabelFlags = 0;
- int savedDefaultBackgroundType = Key.BACKGROUND_TYPE_NORMAL;
- try {
- XmlParseUtils.checkAttributeExists(keyboardAttr,
- R.styleable.Keyboard_Include_keyboardLayout, "keyboardLayout",
- TAG_INCLUDE, parser);
- keyboardLayout = keyboardAttr.getResourceId(
- R.styleable.Keyboard_Include_keyboardLayout, 0);
- if (row != null) {
- if (keyAttr.hasValue(R.styleable.Keyboard_Key_keyXPos)) {
- // Override current x coordinate.
- row.setXPos(row.getKeyX(keyAttr));
- }
- // TODO: Remove this if-clause and do the same as backgroundType below.
- savedDefaultKeyWidth = row.getDefaultKeyWidth();
- if (keyAttr.hasValue(R.styleable.Keyboard_Key_keyWidth)) {
- // Override default key width.
- row.setDefaultKeyWidth(row.getKeyWidth(keyAttr));
- }
- savedDefaultKeyLabelFlags = row.getDefaultKeyLabelFlags();
- // Bitwise-or default keyLabelFlag if exists.
- row.setDefaultKeyLabelFlags(keyAttr.getInt(
- R.styleable.Keyboard_Key_keyLabelFlags, 0)
- | savedDefaultKeyLabelFlags);
- savedDefaultBackgroundType = row.getDefaultBackgroundType();
- // Override default backgroundType if exists.
- row.setDefaultBackgroundType(keyAttr.getInt(
- R.styleable.Keyboard_Key_backgroundType,
- savedDefaultBackgroundType));
- }
- } finally {
- keyboardAttr.recycle();
- keyAttr.recycle();
- }
-
- XmlParseUtils.checkEndTag(TAG_INCLUDE, parser);
- if (DEBUG) {
- startEndTag("<%s keyboardLayout=%s />",TAG_INCLUDE,
- mResources.getResourceEntryName(keyboardLayout));
- }
- final XmlResourceParser parserForInclude = mResources.getXml(keyboardLayout);
- try {
- parseMerge(parserForInclude, row, skip);
- } finally {
- if (row != null) {
- // Restore default keyWidth, keyLabelFlags, and backgroundType.
- row.setDefaultKeyWidth(savedDefaultKeyWidth);
- row.setDefaultKeyLabelFlags(savedDefaultKeyLabelFlags);
- row.setDefaultBackgroundType(savedDefaultBackgroundType);
- }
- parserForInclude.close();
- }
- }
- }
-
- private void parseMerge(XmlPullParser parser, Row row, boolean skip)
- throws XmlPullParserException, IOException {
- if (DEBUG) startTag("<%s>", TAG_MERGE);
- int event;
- while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
- if (event == XmlPullParser.START_TAG) {
- final String tag = parser.getName();
- if (TAG_MERGE.equals(tag)) {
- if (row == null) {
- parseKeyboardContent(parser, skip);
- } else {
- parseRowContent(parser, row, skip);
- }
- break;
- } else {
- throw new XmlParseUtils.ParseException(
- "Included keyboard layout must have <merge> root element", parser);
- }
- }
- }
- }
-
- private void parseSwitchKeyboardContent(XmlPullParser parser, boolean skip)
- throws XmlPullParserException, IOException {
- parseSwitchInternal(parser, null, skip);
- }
-
- private void parseSwitchRowContent(XmlPullParser parser, Row row, boolean skip)
- throws XmlPullParserException, IOException {
- parseSwitchInternal(parser, row, skip);
- }
-
- private void parseSwitchInternal(XmlPullParser parser, Row row, boolean skip)
- throws XmlPullParserException, IOException {
- if (DEBUG) startTag("<%s> %s", TAG_SWITCH, mParams.mId);
- boolean selected = false;
- int event;
- while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
- if (event == XmlPullParser.START_TAG) {
- final String tag = parser.getName();
- if (TAG_CASE.equals(tag)) {
- selected |= parseCase(parser, row, selected ? true : skip);
- } else if (TAG_DEFAULT.equals(tag)) {
- selected |= parseDefault(parser, row, selected ? true : skip);
- } else {
- throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEY);
- }
- } else if (event == XmlPullParser.END_TAG) {
- final String tag = parser.getName();
- if (TAG_SWITCH.equals(tag)) {
- if (DEBUG) endTag("</%s>", TAG_SWITCH);
- break;
- } else {
- throw new XmlParseUtils.IllegalEndTag(parser, TAG_KEY);
- }
- }
- }
- }
-
- private boolean parseCase(XmlPullParser parser, Row row, boolean skip)
- throws XmlPullParserException, IOException {
- final boolean selected = parseCaseCondition(parser);
- if (row == null) {
- // Processing Rows.
- parseKeyboardContent(parser, selected ? skip : true);
- } else {
- // Processing Keys.
- parseRowContent(parser, row, selected ? skip : true);
- }
- return selected;
- }
-
- private boolean parseCaseCondition(XmlPullParser parser) {
- final KeyboardId id = mParams.mId;
- if (id == null)
- return true;
-
- final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
- R.styleable.Keyboard_Case);
- try {
- final boolean keyboardLayoutSetElementMatched = matchTypedValue(a,
- R.styleable.Keyboard_Case_keyboardLayoutSetElement, id.mElementId,
- KeyboardId.elementIdToName(id.mElementId));
- final boolean modeMatched = matchTypedValue(a,
- R.styleable.Keyboard_Case_mode, id.mMode, KeyboardId.modeName(id.mMode));
- final boolean navigateNextMatched = matchBoolean(a,
- R.styleable.Keyboard_Case_navigateNext, id.navigateNext());
- final boolean navigatePreviousMatched = matchBoolean(a,
- R.styleable.Keyboard_Case_navigatePrevious, id.navigatePrevious());
- final boolean passwordInputMatched = matchBoolean(a,
- R.styleable.Keyboard_Case_passwordInput, id.passwordInput());
- final boolean clobberSettingsKeyMatched = matchBoolean(a,
- R.styleable.Keyboard_Case_clobberSettingsKey, id.mClobberSettingsKey);
- final boolean shortcutKeyEnabledMatched = matchBoolean(a,
- R.styleable.Keyboard_Case_shortcutKeyEnabled, id.mShortcutKeyEnabled);
- final boolean hasShortcutKeyMatched = matchBoolean(a,
- R.styleable.Keyboard_Case_hasShortcutKey, id.mHasShortcutKey);
- final boolean languageSwitchKeyEnabledMatched = matchBoolean(a,
- R.styleable.Keyboard_Case_languageSwitchKeyEnabled,
- id.mLanguageSwitchKeyEnabled);
- final boolean isMultiLineMatched = matchBoolean(a,
- R.styleable.Keyboard_Case_isMultiLine, id.isMultiLine());
- final boolean imeActionMatched = matchInteger(a,
- R.styleable.Keyboard_Case_imeAction, id.imeAction());
- final boolean localeCodeMatched = matchString(a,
- R.styleable.Keyboard_Case_localeCode, id.mLocale.toString());
- final boolean languageCodeMatched = matchString(a,
- R.styleable.Keyboard_Case_languageCode, id.mLocale.getLanguage());
- final boolean countryCodeMatched = matchString(a,
- R.styleable.Keyboard_Case_countryCode, id.mLocale.getCountry());
- final boolean selected = keyboardLayoutSetElementMatched && modeMatched
- && navigateNextMatched && navigatePreviousMatched && passwordInputMatched
- && clobberSettingsKeyMatched && shortcutKeyEnabledMatched
- && hasShortcutKeyMatched && languageSwitchKeyEnabledMatched
- && isMultiLineMatched && imeActionMatched && localeCodeMatched
- && languageCodeMatched && countryCodeMatched;
-
- if (DEBUG) {
- startTag("<%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s>%s", TAG_CASE,
- textAttr(a.getString(
- R.styleable.Keyboard_Case_keyboardLayoutSetElement),
- "keyboardLayoutSetElement"),
- textAttr(a.getString(R.styleable.Keyboard_Case_mode), "mode"),
- textAttr(a.getString(R.styleable.Keyboard_Case_imeAction),
- "imeAction"),
- booleanAttr(a, R.styleable.Keyboard_Case_navigateNext,
- "navigateNext"),
- booleanAttr(a, R.styleable.Keyboard_Case_navigatePrevious,
- "navigatePrevious"),
- booleanAttr(a, R.styleable.Keyboard_Case_clobberSettingsKey,
- "clobberSettingsKey"),
- booleanAttr(a, R.styleable.Keyboard_Case_passwordInput,
- "passwordInput"),
- booleanAttr(a, R.styleable.Keyboard_Case_shortcutKeyEnabled,
- "shortcutKeyEnabled"),
- booleanAttr(a, R.styleable.Keyboard_Case_hasShortcutKey,
- "hasShortcutKey"),
- booleanAttr(a, R.styleable.Keyboard_Case_languageSwitchKeyEnabled,
- "languageSwitchKeyEnabled"),
- booleanAttr(a, R.styleable.Keyboard_Case_isMultiLine,
- "isMultiLine"),
- textAttr(a.getString(R.styleable.Keyboard_Case_localeCode),
- "localeCode"),
- textAttr(a.getString(R.styleable.Keyboard_Case_languageCode),
- "languageCode"),
- textAttr(a.getString(R.styleable.Keyboard_Case_countryCode),
- "countryCode"),
- selected ? "" : " skipped");
- }
-
- return selected;
- } finally {
- a.recycle();
- }
- }
-
- private static boolean matchInteger(TypedArray a, int index, int value) {
- // If <case> does not have "index" attribute, that means this <case> is wild-card for
- // the attribute.
- return !a.hasValue(index) || a.getInt(index, 0) == value;
- }
-
- private static boolean matchBoolean(TypedArray a, int index, boolean value) {
- // If <case> does not have "index" attribute, that means this <case> is wild-card for
- // the attribute.
- return !a.hasValue(index) || a.getBoolean(index, false) == value;
- }
-
- private static boolean matchString(TypedArray a, int index, String value) {
- // If <case> does not have "index" attribute, that means this <case> is wild-card for
- // the attribute.
- return !a.hasValue(index)
- || stringArrayContains(a.getString(index).split("\\|"), value);
- }
-
- private static boolean matchTypedValue(TypedArray a, int index, int intValue,
- String strValue) {
- // If <case> does not have "index" attribute, that means this <case> is wild-card for
- // the attribute.
- final TypedValue v = a.peekValue(index);
- if (v == null)
- return true;
-
- if (isIntegerValue(v)) {
- return intValue == a.getInt(index, 0);
- } else if (isStringValue(v)) {
- return stringArrayContains(a.getString(index).split("\\|"), strValue);
- }
- return false;
- }
-
- private static boolean stringArrayContains(String[] array, String value) {
- for (final String elem : array) {
- if (elem.equals(value))
- return true;
- }
- return false;
- }
-
- private boolean parseDefault(XmlPullParser parser, Row row, boolean skip)
- throws XmlPullParserException, IOException {
- if (DEBUG) startTag("<%s>", TAG_DEFAULT);
- if (row == null) {
- parseKeyboardContent(parser, skip);
- } else {
- parseRowContent(parser, row, skip);
- }
- return true;
- }
-
- private void parseKeyStyle(XmlPullParser parser, boolean skip)
- throws XmlPullParserException, IOException {
- TypedArray keyStyleAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser),
- R.styleable.Keyboard_KeyStyle);
- TypedArray keyAttrs = mResources.obtainAttributes(Xml.asAttributeSet(parser),
- R.styleable.Keyboard_Key);
- try {
- if (!keyStyleAttr.hasValue(R.styleable.Keyboard_KeyStyle_styleName))
- throw new XmlParseUtils.ParseException("<" + TAG_KEY_STYLE
- + "/> needs styleName attribute", parser);
- if (DEBUG) {
- startEndTag("<%s styleName=%s />%s", TAG_KEY_STYLE,
- keyStyleAttr.getString(R.styleable.Keyboard_KeyStyle_styleName),
- skip ? " skipped" : "");
- }
- if (!skip)
- mParams.mKeyStyles.parseKeyStyleAttributes(keyStyleAttr, keyAttrs, parser);
- } finally {
- keyStyleAttr.recycle();
- keyAttrs.recycle();
- }
- XmlParseUtils.checkEndTag(TAG_KEY_STYLE, parser);
- }
-
- private void startKeyboard() {
- mCurrentY += mParams.mTopPadding;
- mTopEdge = true;
- }
-
- private void startRow(Row row) {
- addEdgeSpace(mParams.mHorizontalEdgesPadding, row);
- mCurrentRow = row;
- mLeftEdge = true;
- mRightEdgeKey = null;
- }
-
- private void endRow(Row row) {
- if (mCurrentRow == null)
- throw new InflateException("orphan end row tag");
- if (mRightEdgeKey != null) {
- mRightEdgeKey.markAsRightEdge(mParams);
- mRightEdgeKey = null;
- }
- addEdgeSpace(mParams.mHorizontalEdgesPadding, row);
- mCurrentY += row.mRowHeight;
- mCurrentRow = null;
- mTopEdge = false;
- }
-
- private void endKey(Key key) {
- mParams.onAddKey(key);
- if (mLeftEdge) {
- key.markAsLeftEdge(mParams);
- mLeftEdge = false;
- }
- if (mTopEdge) {
- key.markAsTopEdge(mParams);
- }
- mRightEdgeKey = key;
- }
-
- private void endKeyboard() {
- // nothing to do here.
- }
-
- private void addEdgeSpace(float width, Row row) {
- row.advanceXPos(width);
- mLeftEdge = false;
- mRightEdgeKey = null;
- }
-
- public static float getDimensionOrFraction(TypedArray a, int index, int base,
- float defValue) {
- final TypedValue value = a.peekValue(index);
- if (value == null)
- return defValue;
- if (isFractionValue(value)) {
- return a.getFraction(index, base, base, defValue);
- } else if (isDimensionValue(value)) {
- return a.getDimension(index, defValue);
- }
- return defValue;
- }
-
- public static int getEnumValue(TypedArray a, int index, int defValue) {
- final TypedValue value = a.peekValue(index);
- if (value == null)
- return defValue;
- if (isIntegerValue(value)) {
- return a.getInt(index, defValue);
- }
- return defValue;
- }
-
- private static boolean isFractionValue(TypedValue v) {
- return v.type == TypedValue.TYPE_FRACTION;
- }
-
- private static boolean isDimensionValue(TypedValue v) {
- return v.type == TypedValue.TYPE_DIMENSION;
- }
-
- private static boolean isIntegerValue(TypedValue v) {
- return v.type >= TypedValue.TYPE_FIRST_INT && v.type <= TypedValue.TYPE_LAST_INT;
- }
-
- private static boolean isStringValue(TypedValue v) {
- return v.type == TypedValue.TYPE_STRING;
- }
-
- private static String textAttr(String value, String name) {
- return value != null ? String.format(" %s=%s", name, value) : "";
- }
-
- private static String booleanAttr(TypedArray a, int index, String name) {
- return a.hasValue(index)
- ? String.format(" %s=%s", name, a.getBoolean(index, false)) : "";
- }
- }
}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java
index 275aacf36..5c8f78f5e 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java
@@ -16,6 +16,9 @@
package com.android.inputmethod.keyboard;
+import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.latin.InputPointers;
+
public interface KeyboardActionListener {
/**
@@ -42,20 +45,16 @@ public interface KeyboardActionListener {
*
* @param primaryCode this is the code of the key that was pressed
* @param x x-coordinate pixel of touched event. If {@link #onCodeInput} is not called by
- * {@link PointerTracker} or so, the value should be {@link #NOT_A_TOUCH_COORDINATE}.
- * If it's called on insertion from the suggestion strip, it should be
- * {@link #SUGGESTION_STRIP_COORDINATE}.
+ * {@link PointerTracker} or so, the value should be
+ * {@link Constants#NOT_A_COORDINATE}. If it's called on insertion from the
+ * suggestion strip, it should be {@link Constants#SUGGESTION_STRIP_COORDINATE}.
* @param y y-coordinate pixel of touched event. If {@link #onCodeInput} is not called by
- * {@link PointerTracker} or so, the value should be {@link #NOT_A_TOUCH_COORDINATE}.
- * If it's called on insertion from the suggestion strip, it should be
- * {@link #SUGGESTION_STRIP_COORDINATE}.
+ * {@link PointerTracker} or so, the value should be
+ * {@link Constants#NOT_A_COORDINATE}.If it's called on insertion from the
+ * suggestion strip, it should be {@link Constants#SUGGESTION_STRIP_COORDINATE}.
*/
public void onCodeInput(int primaryCode, int x, int y);
- public static final int NOT_A_TOUCH_COORDINATE = -1;
- public static final int SUGGESTION_STRIP_COORDINATE = -2;
- public static final int SPELL_CHECKER_COORDINATE = -3;
-
/**
* Sends a sequence of characters to the listener.
*
@@ -64,6 +63,24 @@ public interface KeyboardActionListener {
public void onTextInput(CharSequence text);
/**
+ * Called when user started batch input.
+ */
+ public void onStartBatchInput();
+
+ /**
+ * Sends the ongoing batch input points data.
+ * @param batchPointers the batch input points representing the user input
+ */
+ public void onUpdateBatchInput(InputPointers batchPointers);
+
+ /**
+ * Sends the final batch input points data.
+ *
+ * @param batchPointers the batch input points representing the user input
+ */
+ public void onEndBatchInput(InputPointers batchPointers);
+
+ /**
* Called when user released a finger outside any key.
*/
public void onCancelInput();
@@ -84,10 +101,24 @@ public interface KeyboardActionListener {
@Override
public void onTextInput(CharSequence text) {}
@Override
+ public void onStartBatchInput() {}
+ @Override
+ public void onUpdateBatchInput(InputPointers batchPointers) {}
+ @Override
+ public void onEndBatchInput(InputPointers batchPointers) {}
+ @Override
public void onCancelInput() {}
@Override
public boolean onCustomRequest(int requestCode) {
return false;
}
+
+ // TODO: Remove this method when the vertical correction is removed.
+ public static boolean isInvalidCoordinate(int coordinate) {
+ // Detect {@link Constants#NOT_A_COORDINATE},
+ // {@link Constants#SUGGESTION_STRIP_COORDINATE}, and
+ // {@link Constants#SPELL_CHECKER_COORDINATE}.
+ return coordinate < 0;
+ }
}
}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardId.java b/java/src/com/android/inputmethod/keyboard/KeyboardId.java
index 233716acf..5e8a8f6bb 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardId.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardId.java
@@ -33,7 +33,7 @@ import java.util.Locale;
/**
* Unique identifier for each keyboard type.
*/
-public class KeyboardId {
+public final class KeyboardId {
public static final int MODE_TEXT = 0;
public static final int MODE_URL = 1;
public static final int MODE_EMAIL = 2;
@@ -55,10 +55,15 @@ public class KeyboardId {
public static final int ELEMENT_PHONE_SYMBOLS = 8;
public static final int ELEMENT_NUMBER = 9;
+ public static final int FORM_FACTOR_PHONE = 0;
+ public static final int FORM_FACTOR_TABLET7 = 1;
+ public static final int FORM_FACTOR_TABLET10 = 2;
+
private static final int IME_ACTION_CUSTOM_LABEL = EditorInfo.IME_MASK_ACTION + 1;
public final InputMethodSubtype mSubtype;
public final Locale mLocale;
+ public final int mDeviceFormFactor;
public final int mOrientation;
public final int mWidth;
public final int mMode;
@@ -72,11 +77,12 @@ public class KeyboardId {
private final int mHashCode;
- public KeyboardId(int elementId, InputMethodSubtype subtype, int orientation, int width,
- int mode, EditorInfo editorInfo, boolean clobberSettingsKey, boolean shortcutKeyEnabled,
- boolean hasShortcutKey, boolean languageSwitchKeyEnabled) {
+ public KeyboardId(int elementId, InputMethodSubtype subtype, int deviceFormFactor,
+ int orientation, int width, int mode, EditorInfo editorInfo, boolean clobberSettingsKey,
+ boolean shortcutKeyEnabled, boolean hasShortcutKey, boolean languageSwitchKeyEnabled) {
mSubtype = subtype;
mLocale = SubtypeLocale.getSubtypeLocale(subtype);
+ mDeviceFormFactor = deviceFormFactor;
mOrientation = orientation;
mWidth = width;
mMode = mode;
@@ -94,6 +100,7 @@ public class KeyboardId {
private static int computeHashCode(KeyboardId id) {
return Arrays.hashCode(new Object[] {
+ id.mDeviceFormFactor,
id.mOrientation,
id.mElementId,
id.mMode,
@@ -115,7 +122,8 @@ public class KeyboardId {
private boolean equals(KeyboardId other) {
if (other == this)
return true;
- return other.mOrientation == mOrientation
+ return other.mDeviceFormFactor == mDeviceFormFactor
+ && other.mOrientation == mOrientation
&& other.mElementId == mElementId
&& other.mMode == mMode
&& other.mWidth == mWidth
@@ -137,11 +145,13 @@ public class KeyboardId {
}
public boolean navigateNext() {
- return (mEditorInfo.imeOptions & EditorInfo.IME_FLAG_NAVIGATE_NEXT) != 0;
+ return (mEditorInfo.imeOptions & EditorInfo.IME_FLAG_NAVIGATE_NEXT) != 0
+ || imeAction() == EditorInfo.IME_ACTION_NEXT;
}
public boolean navigatePrevious() {
- return (mEditorInfo.imeOptions & EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS) != 0;
+ return (mEditorInfo.imeOptions & EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS) != 0
+ || imeAction() == EditorInfo.IME_ACTION_PREVIOUS;
}
public boolean passwordInput() {
@@ -182,11 +192,11 @@ public class KeyboardId {
@Override
public String toString() {
- return String.format("[%s %s:%s %s%d %s %s %s%s%s%s%s%s%s%s]",
+ return String.format("[%s %s:%s %s-%s:%d %s %s %s%s%s%s%s%s%s%s]",
elementIdToName(mElementId),
mLocale,
mSubtype.getExtraValueOf(KEYBOARD_LAYOUT_SET),
- (mOrientation == 1 ? "port" : "land"), mWidth,
+ deviceFormFactor(mDeviceFormFactor), (mOrientation == 1 ? "port" : "land"), mWidth,
modeName(mMode),
imeAction(),
(navigateNext() ? "navigateNext" : ""),
@@ -224,6 +234,15 @@ public class KeyboardId {
}
}
+ public static String deviceFormFactor(int devoceFormFactor) {
+ switch (devoceFormFactor) {
+ case FORM_FACTOR_PHONE: return "phone";
+ case FORM_FACTOR_TABLET7: return "tablet7";
+ case FORM_FACTOR_TABLET10: return "tablet10";
+ default: return null;
+ }
+ }
+
public static String modeName(int mode) {
switch (mode) {
case MODE_TEXT: return "text";
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
index 8c7246855..c7813ab02 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
@@ -29,12 +29,16 @@ import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.text.InputType;
import android.util.Log;
+import android.util.SparseArray;
import android.util.Xml;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodSubtype;
import com.android.inputmethod.compat.EditorInfoCompatUtils;
-import com.android.inputmethod.keyboard.KeyboardLayoutSet.Params.ElementParams;
+import com.android.inputmethod.keyboard.internal.KeyboardBuilder;
+import com.android.inputmethod.keyboard.internal.KeyboardParams;
+import com.android.inputmethod.keyboard.internal.KeysCache;
+import com.android.inputmethod.latin.CollectionUtils;
import com.android.inputmethod.latin.InputAttributes;
import com.android.inputmethod.latin.InputTypeUtils;
import com.android.inputmethod.latin.LatinImeLogger;
@@ -57,7 +61,7 @@ import java.util.HashMap;
* A {@link KeyboardLayoutSet} needs to be created for each
* {@link android.view.inputmethod.EditorInfo}.
*/
-public class KeyboardLayoutSet {
+public final class KeyboardLayoutSet {
private static final String TAG = KeyboardLayoutSet.class.getSimpleName();
private static final boolean DEBUG_CACHE = LatinImeLogger.sDBG;
@@ -70,60 +74,41 @@ public class KeyboardLayoutSet {
private final Params mParams;
private static final HashMap<KeyboardId, SoftReference<Keyboard>> sKeyboardCache =
- new HashMap<KeyboardId, SoftReference<Keyboard>>();
+ CollectionUtils.newHashMap();
private static final KeysCache sKeysCache = new KeysCache();
- public static class KeyboardLayoutSetException extends RuntimeException {
+ public static final class KeyboardLayoutSetException extends RuntimeException {
public final KeyboardId mKeyboardId;
- public KeyboardLayoutSetException(Throwable cause, KeyboardId keyboardId) {
+ public KeyboardLayoutSetException(final Throwable cause, final KeyboardId keyboardId) {
super(cause);
mKeyboardId = keyboardId;
}
}
- public static class KeysCache {
- private final HashMap<Key, Key> mMap;
-
- public KeysCache() {
- mMap = new HashMap<Key, Key>();
- }
-
- public void clear() {
- mMap.clear();
- }
-
- public Key get(Key key) {
- final Key existingKey = mMap.get(key);
- if (existingKey != null) {
- // Reuse the existing element that equals to "key" without adding "key" to the map.
- return existingKey;
- }
- mMap.put(key, key);
- return key;
- }
+ private static final class ElementParams {
+ int mKeyboardXmlId;
+ boolean mProximityCharsCorrectionEnabled;
+ public ElementParams() {}
}
- static class Params {
+ private static final class Params {
String mKeyboardLayoutSetName;
int mMode;
EditorInfo mEditorInfo;
- boolean mTouchPositionCorrectionEnabled;
+ boolean mDisableTouchPositionCorrectionDataForTest;
boolean mVoiceKeyEnabled;
boolean mVoiceKeyOnMain;
boolean mNoSettingsKey;
boolean mLanguageSwitchKeyEnabled;
InputMethodSubtype mSubtype;
+ int mDeviceFormFactor;
int mOrientation;
int mWidth;
- // KeyboardLayoutSet element id to element's parameters map.
- final HashMap<Integer, ElementParams> mKeyboardLayoutSetElementIdToParamsMap =
- new HashMap<Integer, ElementParams>();
-
- static class ElementParams {
- int mKeyboardXmlId;
- boolean mProximityCharsCorrectionEnabled;
- }
+ // Sparse array of KeyboardLayoutSet element parameters indexed by element's id.
+ final SparseArray<ElementParams> mKeyboardLayoutSetElementIdToParamsMap =
+ CollectionUtils.newSparseArray();
+ public Params() {}
}
public static void clearKeyboardCache() {
@@ -131,12 +116,12 @@ public class KeyboardLayoutSet {
sKeysCache.clear();
}
- private KeyboardLayoutSet(Context context, Params params) {
+ KeyboardLayoutSet(final Context context, final Params params) {
mContext = context;
mParams = params;
}
- public Keyboard getKeyboard(int baseKeyboardLayoutSetElementId) {
+ public Keyboard getKeyboard(final int baseKeyboardLayoutSetElementId) {
final int keyboardLayoutSetElementId;
switch (mParams.mMode) {
case KeyboardId.MODE_PHONE:
@@ -171,18 +156,20 @@ public class KeyboardLayoutSet {
}
}
- private Keyboard getKeyboard(ElementParams elementParams, final KeyboardId id) {
+ private Keyboard getKeyboard(final ElementParams elementParams, final KeyboardId id) {
final SoftReference<Keyboard> ref = sKeyboardCache.get(id);
Keyboard keyboard = (ref == null) ? null : ref.get();
if (keyboard == null) {
- final Keyboard.Builder<Keyboard.Params> builder =
- new Keyboard.Builder<Keyboard.Params>(mContext, new Keyboard.Params());
+ final KeyboardBuilder<KeyboardParams> builder =
+ new KeyboardBuilder<KeyboardParams>(mContext, new KeyboardParams());
if (id.isAlphabetKeyboard()) {
builder.setAutoGenerate(sKeysCache);
}
final int keyboardXmlId = elementParams.mKeyboardXmlId;
builder.load(keyboardXmlId, id);
- builder.setTouchPositionCorrectionEnabled(mParams.mTouchPositionCorrectionEnabled);
+ if (mParams.mDisableTouchPositionCorrectionDataForTest) {
+ builder.disableTouchPositionCorrectionDataForTest();
+ }
builder.setProximityCharsCorrectionEnabled(
elementParams.mProximityCharsCorrectionEnabled);
keyboard = builder.build();
@@ -203,19 +190,20 @@ public class KeyboardLayoutSet {
// KeyboardLayoutSet element id that is a key in keyboard_set.xml. Also that file specifies
// which XML layout should be used for each keyboard. The KeyboardId is an internal key for
// Keyboard object.
- private KeyboardId getKeyboardId(int keyboardLayoutSetElementId) {
+ private KeyboardId getKeyboardId(final int keyboardLayoutSetElementId) {
final Params params = mParams;
final boolean isSymbols = (keyboardLayoutSetElementId == KeyboardId.ELEMENT_SYMBOLS
|| keyboardLayoutSetElementId == KeyboardId.ELEMENT_SYMBOLS_SHIFTED);
final boolean noLanguage = SubtypeLocale.isNoLanguage(params.mSubtype);
final boolean voiceKeyEnabled = params.mVoiceKeyEnabled && !noLanguage;
final boolean hasShortcutKey = voiceKeyEnabled && (isSymbols != params.mVoiceKeyOnMain);
- return new KeyboardId(keyboardLayoutSetElementId, params.mSubtype, params.mOrientation,
- params.mWidth, params.mMode, params.mEditorInfo, params.mNoSettingsKey,
- voiceKeyEnabled, hasShortcutKey, params.mLanguageSwitchKeyEnabled);
+ return new KeyboardId(keyboardLayoutSetElementId, params.mSubtype, params.mDeviceFormFactor,
+ params.mOrientation, params.mWidth, params.mMode, params.mEditorInfo,
+ params.mNoSettingsKey, voiceKeyEnabled, hasShortcutKey,
+ params.mLanguageSwitchKeyEnabled);
}
- public static class Builder {
+ public static final class Builder {
private final Context mContext;
private final String mPackageName;
private final Resources mResources;
@@ -225,7 +213,7 @@ public class KeyboardLayoutSet {
private static final EditorInfo EMPTY_EDITOR_INFO = new EditorInfo();
- public Builder(Context context, EditorInfo editorInfo) {
+ public Builder(final Context context, final EditorInfo editorInfo) {
mContext = context;
mPackageName = context.getPackageName();
mResources = context.getResources();
@@ -238,13 +226,16 @@ public class KeyboardLayoutSet {
mPackageName, NO_SETTINGS_KEY, mEditorInfo);
}
- public Builder setScreenGeometry(int orientation, int widthPixels) {
- mParams.mOrientation = orientation;
- mParams.mWidth = widthPixels;
+ public Builder setScreenGeometry(final int deviceFormFactor, final int orientation,
+ final int widthPixels) {
+ final Params params = mParams;
+ params.mDeviceFormFactor = deviceFormFactor;
+ params.mOrientation = orientation;
+ params.mWidth = widthPixels;
return this;
}
- public Builder setSubtype(InputMethodSubtype subtype) {
+ public Builder setSubtype(final InputMethodSubtype subtype) {
final boolean asciiCapable = subtype.containsExtraValueKey(ASCII_CAPABLE);
@SuppressWarnings("deprecation")
final boolean deprecatedForceAscii = InputAttributes.inPrivateImeOptions(
@@ -261,8 +252,8 @@ public class KeyboardLayoutSet {
return this;
}
- public Builder setOptions(boolean voiceKeyEnabled, boolean voiceKeyOnMain,
- boolean languageSwitchKeyEnabled) {
+ public Builder setOptions(final boolean voiceKeyEnabled, final boolean voiceKeyOnMain,
+ final boolean languageSwitchKeyEnabled) {
@SuppressWarnings("deprecation")
final boolean deprecatedNoMicrophone = InputAttributes.inPrivateImeOptions(
null, NO_MICROPHONE_COMPAT, mEditorInfo);
@@ -275,8 +266,9 @@ public class KeyboardLayoutSet {
return this;
}
- public void setTouchPositionCorrectionEnabled(boolean enabled) {
- mParams.mTouchPositionCorrectionEnabled = enabled;
+ // For test only
+ public void disableTouchPositionCorrectionDataForTest() {
+ mParams.mDisableTouchPositionCorrectionDataForTest = true;
}
public KeyboardLayoutSet build() {
@@ -296,7 +288,7 @@ public class KeyboardLayoutSet {
return new KeyboardLayoutSet(mContext, mParams);
}
- private void parseKeyboardLayoutSet(Resources res, int resId)
+ private void parseKeyboardLayoutSet(final Resources res, final int resId)
throws XmlPullParserException, IOException {
final XmlResourceParser parser = res.getXml(resId);
try {
@@ -316,7 +308,7 @@ public class KeyboardLayoutSet {
}
}
- private void parseKeyboardLayoutSetContent(XmlPullParser parser)
+ private void parseKeyboardLayoutSetContent(final XmlPullParser parser)
throws XmlPullParserException, IOException {
int event;
while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
@@ -338,7 +330,7 @@ public class KeyboardLayoutSet {
}
}
- private void parseKeyboardLayoutSetElement(XmlPullParser parser)
+ private void parseKeyboardLayoutSetElement(final XmlPullParser parser)
throws XmlPullParserException, IOException {
final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
R.styleable.KeyboardLayoutSet_Element);
@@ -365,7 +357,7 @@ public class KeyboardLayoutSet {
}
}
- private static int getKeyboardMode(EditorInfo editorInfo) {
+ private static int getKeyboardMode(final EditorInfo editorInfo) {
if (editorInfo == null)
return KeyboardId.MODE_TEXT;
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index 2e4ce199e..38025e8e4 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -21,7 +21,6 @@ import android.content.SharedPreferences;
import android.content.res.Resources;
import android.util.Log;
import android.view.ContextThemeWrapper;
-import android.view.InflateException;
import android.view.LayoutInflater;
import android.view.View;
import android.view.inputmethod.EditorInfo;
@@ -38,32 +37,32 @@ import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.SettingsValues;
import com.android.inputmethod.latin.SubtypeSwitcher;
-import com.android.inputmethod.latin.Utils;
+import com.android.inputmethod.latin.WordComposer;
-public class KeyboardSwitcher implements KeyboardState.SwitchActions {
+public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
private static final String TAG = KeyboardSwitcher.class.getSimpleName();
public static final String PREF_KEYBOARD_LAYOUT = "pref_keyboard_layout_20110916";
- static class KeyboardTheme {
- public final String mName;
+ static final class KeyboardTheme {
public final int mThemeId;
public final int mStyleId;
- public KeyboardTheme(String name, int themeId, int styleId) {
- mName = name;
+ // Note: The themeId should be aligned with "themeId" attribute of Keyboard style
+ // in values/style.xml.
+ public KeyboardTheme(int themeId, int styleId) {
mThemeId = themeId;
mStyleId = styleId;
}
}
private static final KeyboardTheme[] KEYBOARD_THEMES = {
- new KeyboardTheme("Basic", 0, R.style.KeyboardTheme),
- new KeyboardTheme("HighContrast", 1, R.style.KeyboardTheme_HighContrast),
- new KeyboardTheme("Stone", 6, R.style.KeyboardTheme_Stone),
- new KeyboardTheme("Stne.Bold", 7, R.style.KeyboardTheme_Stone_Bold),
- new KeyboardTheme("GingerBread", 8, R.style.KeyboardTheme_Gingerbread),
- new KeyboardTheme("IceCreamSandwich", 5, R.style.KeyboardTheme_IceCreamSandwich),
+ new KeyboardTheme(0, R.style.KeyboardTheme),
+ new KeyboardTheme(1, R.style.KeyboardTheme_HighContrast),
+ new KeyboardTheme(6, R.style.KeyboardTheme_Stone),
+ new KeyboardTheme(7, R.style.KeyboardTheme_Stone_Bold),
+ new KeyboardTheme(8, R.style.KeyboardTheme_Gingerbread),
+ new KeyboardTheme(5, R.style.KeyboardTheme_IceCreamSandwich),
};
private SubtypeSwitcher mSubtypeSwitcher;
@@ -71,7 +70,7 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions {
private boolean mForceNonDistinctMultitouch;
private InputView mCurrentInputView;
- private LatinKeyboardView mKeyboardView;
+ private MainKeyboardView mKeyboardView;
private LatinIME mLatinIME;
private Resources mResources;
@@ -137,8 +136,9 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions {
public void loadKeyboard(EditorInfo editorInfo, SettingsValues settingsValues) {
final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder(
mThemeContext, editorInfo);
- builder.setScreenGeometry(mThemeContext.getResources().getConfiguration().orientation,
- mThemeContext.getResources().getDisplayMetrics().widthPixels);
+ final Resources res = mThemeContext.getResources();
+ builder.setScreenGeometry(res.getInteger(R.integer.config_device_form_factor),
+ res.getConfiguration().orientation, res.getDisplayMetrics().widthPixels);
builder.setSubtype(mSubtypeSwitcher.getCurrentSubtype());
builder.setOptions(
settingsValues.isVoiceKeyEnabled(editorInfo),
@@ -169,19 +169,20 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions {
}
private void setKeyboard(final Keyboard keyboard) {
- final Keyboard oldKeyboard = mKeyboardView.getKeyboard();
- mKeyboardView.setKeyboard(keyboard);
+ final MainKeyboardView keyboardView = mKeyboardView;
+ final Keyboard oldKeyboard = keyboardView.getKeyboard();
+ keyboardView.setKeyboard(keyboard);
mCurrentInputView.setKeyboardGeometry(keyboard.mTopPadding);
- mKeyboardView.setKeyPreviewPopupEnabled(
+ keyboardView.setKeyPreviewPopupEnabled(
SettingsValues.isKeyPreviewPopupEnabled(mPrefs, mResources),
SettingsValues.getKeyPreviewPopupDismissDelay(mPrefs, mResources));
- mKeyboardView.updateAutoCorrectionState(mIsAutoCorrectionActive);
- mKeyboardView.updateShortcutKey(mSubtypeSwitcher.isShortcutImeReady());
+ keyboardView.updateAutoCorrectionState(mIsAutoCorrectionActive);
+ keyboardView.updateShortcutKey(mSubtypeSwitcher.isShortcutImeReady());
final boolean subtypeChanged = (oldKeyboard == null)
|| !keyboard.mId.mLocale.equals(oldKeyboard.mId.mLocale);
final boolean needsToDisplayLanguage = mSubtypeSwitcher.needsToDisplayLanguage(
keyboard.mId.mLocale);
- mKeyboardView.startDisplayLanguageOnSpacebar(subtypeChanged, needsToDisplayLanguage,
+ keyboardView.startDisplayLanguageOnSpacebar(subtypeChanged, needsToDisplayLanguage,
ImfUtils.hasMultipleEnabledIMEsOrSubtypes(mLatinIME, true));
}
@@ -265,7 +266,7 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions {
// Implements {@link KeyboardState.SwitchActions}.
@Override
public void startDoubleTapTimer() {
- final LatinKeyboardView keyboardView = getKeyboardView();
+ final MainKeyboardView keyboardView = getMainKeyboardView();
if (keyboardView != null) {
final TimerProxy timer = keyboardView.getTimerProxy();
timer.startDoubleTapTimer();
@@ -275,7 +276,7 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions {
// Implements {@link KeyboardState.SwitchActions}.
@Override
public void cancelDoubleTapTimer() {
- final LatinKeyboardView keyboardView = getKeyboardView();
+ final MainKeyboardView keyboardView = getMainKeyboardView();
if (keyboardView != null) {
final TimerProxy timer = keyboardView.getTimerProxy();
timer.cancelDoubleTapTimer();
@@ -285,7 +286,7 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions {
// Implements {@link KeyboardState.SwitchActions}.
@Override
public boolean isInDoubleTapTimeout() {
- final LatinKeyboardView keyboardView = getKeyboardView();
+ final MainKeyboardView keyboardView = getMainKeyboardView();
return (keyboardView != null)
? keyboardView.getTimerProxy().isInDoubleTapTimeout() : false;
}
@@ -293,7 +294,7 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions {
// Implements {@link KeyboardState.SwitchActions}.
@Override
public void startLongPressTimer(int code) {
- final LatinKeyboardView keyboardView = getKeyboardView();
+ final MainKeyboardView keyboardView = getMainKeyboardView();
if (keyboardView != null) {
final TimerProxy timer = keyboardView.getTimerProxy();
timer.startLongPressTimer(code);
@@ -303,7 +304,7 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions {
// Implements {@link KeyboardState.SwitchActions}.
@Override
public void cancelLongPressTimer() {
- final LatinKeyboardView keyboardView = getKeyboardView();
+ final MainKeyboardView keyboardView = getMainKeyboardView();
if (keyboardView != null) {
final TimerProxy timer = keyboardView.getTimerProxy();
timer.cancelLongPressTimer();
@@ -343,33 +344,24 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions {
mState.onCodeInput(code, isSinglePointer(), mLatinIME.getCurrentAutoCapsState());
}
- public LatinKeyboardView getKeyboardView() {
+ public MainKeyboardView getMainKeyboardView() {
return mKeyboardView;
}
- public View onCreateInputView() {
+ public View onCreateInputView(boolean isHardwareAcceleratedDrawingEnabled) {
if (mKeyboardView != null) {
mKeyboardView.closing();
}
- Utils.GCUtils.getInstance().reset();
- boolean tryGC = true;
- for (int i = 0; i < Utils.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) {
- try {
- setContextThemeWrapper(mLatinIME, mKeyboardTheme);
- mCurrentInputView = (InputView)LayoutInflater.from(mThemeContext).inflate(
- R.layout.input_view, null);
- tryGC = false;
- } catch (OutOfMemoryError e) {
- Log.w(TAG, "load keyboard failed: " + e);
- tryGC = Utils.GCUtils.getInstance().tryGCOrWait(mKeyboardTheme.mName, e);
- } catch (InflateException e) {
- Log.w(TAG, "load keyboard failed: " + e);
- tryGC = Utils.GCUtils.getInstance().tryGCOrWait(mKeyboardTheme.mName, e);
- }
- }
+ setContextThemeWrapper(mLatinIME, mKeyboardTheme);
+ mCurrentInputView = (InputView)LayoutInflater.from(mThemeContext).inflate(
+ R.layout.input_view, null);
- mKeyboardView = (LatinKeyboardView) mCurrentInputView.findViewById(R.id.keyboard_view);
+ mKeyboardView = (MainKeyboardView) mCurrentInputView.findViewById(R.id.keyboard_view);
+ if (isHardwareAcceleratedDrawingEnabled) {
+ mKeyboardView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ // TODO: Should use LAYER_TYPE_SOFTWARE when hardware acceleration is off?
+ }
mKeyboardView.setKeyboardActionListener(mLatinIME);
if (mForceNonDistinctMultitouch) {
mKeyboardView.setDistinctMultitouch(false);
@@ -396,4 +388,22 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions {
}
}
}
+
+ public int getKeyboardShiftMode() {
+ final Keyboard keyboard = getKeyboard();
+ if (keyboard == null) {
+ return WordComposer.CAPS_MODE_OFF;
+ }
+ switch (keyboard.mId.mElementId) {
+ case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED:
+ case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED:
+ return WordComposer.CAPS_MODE_MANUAL_SHIFT_LOCKED;
+ case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED:
+ return WordComposer.CAPS_MODE_MANUAL_SHIFTED;
+ case KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED:
+ return WordComposer.CAPS_MODE_AUTO_SHIFTED;
+ default:
+ return WordComposer.CAPS_MODE_OFF;
+ }
+ }
}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index 51a0f537f..472f74b12 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -25,62 +25,96 @@ import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.PorterDuff;
import android.graphics.Rect;
-import android.graphics.Region.Op;
+import android.graphics.Region;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.os.Message;
import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.SparseArray;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.RelativeLayout;
import android.widget.TextView;
+import com.android.inputmethod.keyboard.internal.KeyDrawParams;
+import com.android.inputmethod.keyboard.internal.KeyPreviewDrawParams;
+import com.android.inputmethod.keyboard.internal.KeyVisualAttributes;
+import com.android.inputmethod.keyboard.internal.PreviewPlacerView;
+import com.android.inputmethod.latin.CollectionUtils;
+import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.StaticInnerHandlerWrapper;
import com.android.inputmethod.latin.StringUtils;
+import com.android.inputmethod.latin.define.ProductionFlag;
+import com.android.inputmethod.research.ResearchLogger;
-import java.util.HashMap;
import java.util.HashSet;
/**
* A view that renders a virtual {@link Keyboard}.
*
- * @attr ref R.styleable#KeyboardView_backgroundDimAlpha
* @attr ref R.styleable#KeyboardView_keyBackground
- * @attr ref R.styleable#KeyboardView_keyLetterRatio
- * @attr ref R.styleable#KeyboardView_keyLargeLetterRatio
- * @attr ref R.styleable#KeyboardView_keyLabelRatio
- * @attr ref R.styleable#KeyboardView_keyHintLetterRatio
- * @attr ref R.styleable#KeyboardView_keyShiftedLetterHintRatio
- * @attr ref R.styleable#KeyboardView_keyHintLabelRatio
+ * @attr ref R.styleable#KeyboardView_moreKeysLayout
+ * @attr ref R.styleable#KeyboardView_keyPreviewLayout
+ * @attr ref R.styleable#KeyboardView_keyPreviewOffset
+ * @attr ref R.styleable#KeyboardView_keyPreviewHeight
+ * @attr ref R.styleable#KeyboardView_keyPreviewLingerTimeout
* @attr ref R.styleable#KeyboardView_keyLabelHorizontalPadding
* @attr ref R.styleable#KeyboardView_keyHintLetterPadding
* @attr ref R.styleable#KeyboardView_keyPopupHintLetterPadding
* @attr ref R.styleable#KeyboardView_keyShiftedLetterHintPadding
- * @attr ref R.styleable#KeyboardView_keyTextStyle
- * @attr ref R.styleable#KeyboardView_keyPreviewLayout
- * @attr ref R.styleable#KeyboardView_keyPreviewTextRatio
- * @attr ref R.styleable#KeyboardView_keyPreviewOffset
- * @attr ref R.styleable#KeyboardView_keyPreviewHeight
- * @attr ref R.styleable#KeyboardView_keyTextColor
- * @attr ref R.styleable#KeyboardView_keyTextColorDisabled
- * @attr ref R.styleable#KeyboardView_keyHintLetterColor
- * @attr ref R.styleable#KeyboardView_keyHintLabelColor
- * @attr ref R.styleable#KeyboardView_keyShiftedLetterHintInactivatedColor
- * @attr ref R.styleable#KeyboardView_keyShiftedLetterHintActivatedColor
- * @attr ref R.styleable#KeyboardView_shadowColor
- * @attr ref R.styleable#KeyboardView_shadowRadius
+ * @attr ref R.styleable#KeyboardView_keyTextShadowRadius
+ * @attr ref R.styleable#KeyboardView_backgroundDimAlpha
+ * @attr ref R.styleable#KeyboardView_gestureFloatingPreviewTextSize
+ * @attr ref R.styleable#KeyboardView_gestureFloatingPreviewTextColor
+ * @attr ref R.styleable#KeyboardView_gestureFloatingPreviewTextOffset
+ * @attr ref R.styleable#KeyboardView_gestureFloatingPreviewColor
+ * @attr ref R.styleable#KeyboardView_gestureFloatingPreviewHorizontalPadding
+ * @attr ref R.styleable#KeyboardView_gestureFloatingPreviewVerticalPadding
+ * @attr ref R.styleable#KeyboardView_gestureFloatingPreviewRoundRadius
+ * @attr ref R.styleable#KeyboardView_gestureFloatingPreviewTextLingerTimeout
+ * @attr ref R.styleable#KeyboardView_gesturePreviewTrailFadeoutStartDelay
+ * @attr ref R.styleable#KeyboardView_gesturePreviewTrailFadeoutDuration
+ * @attr ref R.styleable#KeyboardView_gesturePreviewTrailUpdateInterval
+ * @attr ref R.styleable#KeyboardView_gesturePreviewTrailColor
+ * @attr ref R.styleable#KeyboardView_gesturePreviewTrailWidth
+ * @attr ref R.styleable#KeyboardView_verticalCorrection
+ * @attr ref R.styleable#Keyboard_Key_keyTypeface
+ * @attr ref R.styleable#Keyboard_Key_keyLetterSize
+ * @attr ref R.styleable#Keyboard_Key_keyLabelSize
+ * @attr ref R.styleable#Keyboard_Key_keyLargeLetterRatio
+ * @attr ref R.styleable#Keyboard_Key_keyLargeLabelRatio
+ * @attr ref R.styleable#Keyboard_Key_keyHintLetterRatio
+ * @attr ref R.styleable#Keyboard_Key_keyShiftedLetterHintRatio
+ * @attr ref R.styleable#Keyboard_Key_keyHintLabelRatio
+ * @attr ref R.styleable#Keyboard_Key_keyPreviewTextRatio
+ * @attr ref R.styleable#Keyboard_Key_keyTextColor
+ * @attr ref R.styleable#Keyboard_Key_keyTextColorDisabled
+ * @attr ref R.styleable#Keyboard_Key_keyTextShadowColor
+ * @attr ref R.styleable#Keyboard_Key_keyHintLetterColor
+ * @attr ref R.styleable#Keyboard_Key_keyHintLabelColor
+ * @attr ref R.styleable#Keyboard_Key_keyShiftedLetterHintInactivatedColor
+ * @attr ref R.styleable#Keyboard_Key_keyShiftedLetterHintActivatedColor
+ * @attr ref R.styleable#Keyboard_Key_keyPreviewTextColor
*/
public class KeyboardView extends View implements PointerTracker.DrawingProxy {
- // Miscellaneous constants
- private static final int[] LONG_PRESSABLE_STATE_SET = { android.R.attr.state_long_pressable };
+ private static final String TAG = KeyboardView.class.getSimpleName();
// XML attributes
+ protected final KeyVisualAttributes mKeyVisualAttributes;
+ private final int mKeyLabelHorizontalPadding;
+ private final float mKeyHintLetterPadding;
+ private final float mKeyPopupHintLetterPadding;
+ private final float mKeyShiftedLetterHintPadding;
+ private final float mKeyTextShadowRadius;
protected final float mVerticalCorrection;
protected final int mMoreKeysLayout;
+ protected final Drawable mKeyBackground;
+ protected final Rect mKeyBackgroundPadding = new Rect();
private final int mBackgroundDimAlpha;
// HORIZONTAL ELLIPSIS "...", character for popup hint.
@@ -94,75 +128,104 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
// The maximum key label width in the proportion to the key width.
private static final float MAX_LABEL_RATIO = 0.90f;
- private final static int ALPHA_OPAQUE = 255;
-
// Main keyboard
private Keyboard mKeyboard;
- protected final KeyDrawParams mKeyDrawParams;
+ protected final KeyDrawParams mKeyDrawParams = new KeyDrawParams();
+
+ // Preview placer view
+ private final PreviewPlacerView mPreviewPlacerView;
+ private final int[] mCoordinates = new int[2];
// Key preview
+ private static final int PREVIEW_ALPHA = 240;
private final int mKeyPreviewLayoutId;
- protected final KeyPreviewDrawParams mKeyPreviewDrawParams;
+ private final int mPreviewOffset;
+ private final int mPreviewHeight;
+ private final int mPreviewLingerTimeout;
+ private final SparseArray<TextView> mKeyPreviewTexts = CollectionUtils.newSparseArray();
+ protected final KeyPreviewDrawParams mKeyPreviewDrawParams = new KeyPreviewDrawParams();
private boolean mShowKeyPreviewPopup = true;
private int mDelayAfterPreview;
- private ViewGroup mPreviewPlacer;
+ // Background state set
+ private static final int[][][] KEY_PREVIEW_BACKGROUND_STATE_TABLE = {
+ { // STATE_MIDDLE
+ EMPTY_STATE_SET,
+ { R.attr.state_has_morekeys }
+ },
+ { // STATE_LEFT
+ { R.attr.state_left_edge },
+ { R.attr.state_left_edge, R.attr.state_has_morekeys }
+ },
+ { // STATE_RIGHT
+ { R.attr.state_right_edge },
+ { R.attr.state_right_edge, R.attr.state_has_morekeys }
+ }
+ };
+ private static final int STATE_MIDDLE = 0;
+ private static final int STATE_LEFT = 1;
+ private static final int STATE_RIGHT = 2;
+ private static final int STATE_NORMAL = 0;
+ private static final int STATE_HAS_MOREKEYS = 1;
+ private static final int[] KEY_PREVIEW_BACKGROUND_DEFAULT_STATE =
+ KEY_PREVIEW_BACKGROUND_STATE_TABLE[STATE_MIDDLE][STATE_NORMAL];
// Drawing
/** True if the entire keyboard needs to be dimmed. */
private boolean mNeedsToDimEntireKeyboard;
- /** Whether the keyboard bitmap buffer needs to be redrawn before it's blitted. **/
- private boolean mBufferNeedsUpdate;
/** True if all keys should be drawn */
private boolean mInvalidateAllKeys;
/** The keys that should be drawn */
- private final HashSet<Key> mInvalidatedKeys = new HashSet<Key>();
- /** The region of invalidated keys */
- private final Rect mInvalidatedKeysRect = new Rect();
+ private final HashSet<Key> mInvalidatedKeys = CollectionUtils.newHashSet();
+ /** The working rectangle variable */
+ private final Rect mWorkingRect = new Rect();
/** The keyboard bitmap buffer for faster updates */
- private Bitmap mBuffer;
+ /** The clip region to draw keys */
+ private final Region mClipRegion = new Region();
+ private Bitmap mOffscreenBuffer;
/** The canvas for the above mutable keyboard bitmap */
- private Canvas mCanvas;
+ private final Canvas mOffscreenCanvas = new Canvas();
private final Paint mPaint = new Paint();
private final Paint.FontMetrics mFontMetrics = new Paint.FontMetrics();
- // This map caches key label text height in pixel as value and key label text size as map key.
- private static final HashMap<Integer, Float> sTextHeightCache =
- new HashMap<Integer, Float>();
- // This map caches key label text width in pixel as value and key label text size as map key.
- private static final HashMap<Integer, Float> sTextWidthCache =
- new HashMap<Integer, Float>();
+ // This sparse array caches key label text height in pixel indexed by key label text size.
+ private static final SparseArray<Float> sTextHeightCache = CollectionUtils.newSparseArray();
+ // This sparse array caches key label text width in pixel indexed by key label text size.
+ private static final SparseArray<Float> sTextWidthCache = CollectionUtils.newSparseArray();
private static final char[] KEY_LABEL_REFERENCE_CHAR = { 'M' };
private static final char[] KEY_NUMERIC_HINT_LABEL_REFERENCE_CHAR = { '8' };
private final DrawingHandler mDrawingHandler = new DrawingHandler(this);
public static class DrawingHandler extends StaticInnerHandlerWrapper<KeyboardView> {
- private static final int MSG_DISMISS_KEY_PREVIEW = 1;
+ private static final int MSG_DISMISS_KEY_PREVIEW = 0;
- public DrawingHandler(KeyboardView outerInstance) {
+ public DrawingHandler(final KeyboardView outerInstance) {
super(outerInstance);
}
@Override
- public void handleMessage(Message msg) {
+ public void handleMessage(final Message msg) {
final KeyboardView keyboardView = getOuterInstance();
if (keyboardView == null) return;
final PointerTracker tracker = (PointerTracker) msg.obj;
switch (msg.what) {
case MSG_DISMISS_KEY_PREVIEW:
- tracker.getKeyPreviewText().setVisibility(View.INVISIBLE);
+ final TextView previewText = keyboardView.mKeyPreviewTexts.get(tracker.mPointerId);
+ if (previewText != null) {
+ previewText.setVisibility(INVISIBLE);
+ }
break;
}
}
- public void dismissKeyPreview(long delay, PointerTracker tracker) {
+ public void dismissKeyPreview(final long delay, final PointerTracker tracker) {
sendMessageDelayed(obtainMessage(MSG_DISMISS_KEY_PREVIEW, tracker), delay);
}
- public void cancelDismissKeyPreview(PointerTracker tracker) {
+ public void cancelDismissKeyPreview(final PointerTracker tracker) {
removeMessages(MSG_DISMISS_KEY_PREVIEW, tracker);
}
- public void cancelAllDismissKeyPreviews() {
+ private void cancelAllDismissKeyPreviews() {
removeMessages(MSG_DISMISS_KEY_PREVIEW);
}
@@ -171,214 +234,60 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
}
}
- protected static class KeyDrawParams {
- // XML attributes
- public final int mKeyTextColor;
- public final int mKeyTextInactivatedColor;
- public final Typeface mKeyTextStyle;
- public final float mKeyLabelHorizontalPadding;
- public final float mKeyHintLetterPadding;
- public final float mKeyPopupHintLetterPadding;
- public final float mKeyShiftedLetterHintPadding;
- public final int mShadowColor;
- public final float mShadowRadius;
- public final Drawable mKeyBackground;
- public final int mKeyHintLetterColor;
- public final int mKeyHintLabelColor;
- public final int mKeyShiftedLetterHintInactivatedColor;
- public final int mKeyShiftedLetterHintActivatedColor;
-
- /* package */ final float mKeyLetterRatio;
- private final float mKeyLargeLetterRatio;
- private final float mKeyLabelRatio;
- private final float mKeyLargeLabelRatio;
- private final float mKeyHintLetterRatio;
- private final float mKeyShiftedLetterHintRatio;
- private final float mKeyHintLabelRatio;
- private static final float UNDEFINED_RATIO = -1.0f;
-
- public final Rect mPadding = new Rect();
- public int mKeyLetterSize;
- public int mKeyLargeLetterSize;
- public int mKeyLabelSize;
- public int mKeyLargeLabelSize;
- public int mKeyHintLetterSize;
- public int mKeyShiftedLetterHintSize;
- public int mKeyHintLabelSize;
- public int mAnimAlpha;
-
- public KeyDrawParams(TypedArray a) {
- mKeyBackground = a.getDrawable(R.styleable.KeyboardView_keyBackground);
- if (a.hasValue(R.styleable.KeyboardView_keyLetterSize)) {
- mKeyLetterRatio = UNDEFINED_RATIO;
- mKeyLetterSize = a.getDimensionPixelSize(R.styleable.KeyboardView_keyLetterSize, 0);
- } else {
- mKeyLetterRatio = getRatio(a, R.styleable.KeyboardView_keyLetterRatio);
- }
- if (a.hasValue(R.styleable.KeyboardView_keyLabelSize)) {
- mKeyLabelRatio = UNDEFINED_RATIO;
- mKeyLabelSize = a.getDimensionPixelSize(R.styleable.KeyboardView_keyLabelSize, 0);
- } else {
- mKeyLabelRatio = getRatio(a, R.styleable.KeyboardView_keyLabelRatio);
- }
- mKeyLargeLabelRatio = getRatio(a, R.styleable.KeyboardView_keyLargeLabelRatio);
- mKeyLargeLetterRatio = getRatio(a, R.styleable.KeyboardView_keyLargeLetterRatio);
- mKeyHintLetterRatio = getRatio(a, R.styleable.KeyboardView_keyHintLetterRatio);
- mKeyShiftedLetterHintRatio = getRatio(a,
- R.styleable.KeyboardView_keyShiftedLetterHintRatio);
- mKeyHintLabelRatio = getRatio(a, R.styleable.KeyboardView_keyHintLabelRatio);
- mKeyLabelHorizontalPadding = a.getDimension(
- R.styleable.KeyboardView_keyLabelHorizontalPadding, 0);
- mKeyHintLetterPadding = a.getDimension(
- R.styleable.KeyboardView_keyHintLetterPadding, 0);
- mKeyPopupHintLetterPadding = a.getDimension(
- R.styleable.KeyboardView_keyPopupHintLetterPadding, 0);
- mKeyShiftedLetterHintPadding = a.getDimension(
- R.styleable.KeyboardView_keyShiftedLetterHintPadding, 0);
- mKeyTextColor = a.getColor(R.styleable.KeyboardView_keyTextColor, 0xFF000000);
- mKeyTextInactivatedColor = a.getColor(
- R.styleable.KeyboardView_keyTextInactivatedColor, 0xFF000000);
- mKeyHintLetterColor = a.getColor(R.styleable.KeyboardView_keyHintLetterColor, 0);
- mKeyHintLabelColor = a.getColor(R.styleable.KeyboardView_keyHintLabelColor, 0);
- mKeyShiftedLetterHintInactivatedColor = a.getColor(
- R.styleable.KeyboardView_keyShiftedLetterHintInactivatedColor, 0);
- mKeyShiftedLetterHintActivatedColor = a.getColor(
- R.styleable.KeyboardView_keyShiftedLetterHintActivatedColor, 0);
- mKeyTextStyle = Typeface.defaultFromStyle(
- a.getInt(R.styleable.KeyboardView_keyTextStyle, Typeface.NORMAL));
- mShadowColor = a.getColor(R.styleable.KeyboardView_shadowColor, 0);
- mShadowRadius = a.getFloat(R.styleable.KeyboardView_shadowRadius, 0f);
-
- mKeyBackground.getPadding(mPadding);
- }
-
- public void updateKeyHeight(int keyHeight) {
- if (mKeyLetterRatio >= 0.0f)
- mKeyLetterSize = (int)(keyHeight * mKeyLetterRatio);
- if (mKeyLabelRatio >= 0.0f)
- mKeyLabelSize = (int)(keyHeight * mKeyLabelRatio);
- mKeyLargeLabelSize = (int)(keyHeight * mKeyLargeLabelRatio);
- mKeyLargeLetterSize = (int)(keyHeight * mKeyLargeLetterRatio);
- mKeyHintLetterSize = (int)(keyHeight * mKeyHintLetterRatio);
- mKeyShiftedLetterHintSize = (int)(keyHeight * mKeyShiftedLetterHintRatio);
- mKeyHintLabelSize = (int)(keyHeight * mKeyHintLabelRatio);
- }
-
- public void blendAlpha(Paint paint) {
- final int color = paint.getColor();
- paint.setARGB((paint.getAlpha() * mAnimAlpha) / ALPHA_OPAQUE,
- Color.red(color), Color.green(color), Color.blue(color));
- }
- }
-
- /* package */ static class KeyPreviewDrawParams {
- // XML attributes.
- public final Drawable mPreviewBackground;
- public final Drawable mPreviewLeftBackground;
- public final Drawable mPreviewRightBackground;
- public final int mPreviewTextColor;
- public final int mPreviewOffset;
- public final int mPreviewHeight;
- public final Typeface mKeyTextStyle;
- public final int mLingerTimeout;
-
- private final float mPreviewTextRatio;
- private final float mKeyLetterRatio;
-
- // The graphical geometry of the key preview.
- // <-width->
- // +-------+ ^
- // | | |
- // |preview| height (visible)
- // | | |
- // + + ^ v
- // \ / |offset
- // +-\ /-+ v
- // | +-+ |
- // |parent |
- // | key|
- // +-------+
- // The background of a {@link TextView} being used for a key preview may have invisible
- // paddings. To align the more keys keyboard panel's visible part with the visible part of
- // the background, we need to record the width and height of key preview that don't include
- // invisible paddings.
- public int mPreviewVisibleWidth;
- public int mPreviewVisibleHeight;
- // The key preview may have an arbitrary offset and its background that may have a bottom
- // padding. To align the more keys keyboard and the key preview we also need to record the
- // offset between the top edge of parent key and the bottom of the visible part of key
- // preview background.
- public int mPreviewVisibleOffset;
-
- public int mPreviewTextSize;
- public int mKeyLetterSize;
- public final int[] mCoordinates = new int[2];
-
- private static final int PREVIEW_ALPHA = 240;
-
- public KeyPreviewDrawParams(TypedArray a, KeyDrawParams keyDrawParams) {
- mPreviewBackground = a.getDrawable(R.styleable.KeyboardView_keyPreviewBackground);
- mPreviewLeftBackground = a.getDrawable(
- R.styleable.KeyboardView_keyPreviewLeftBackground);
- mPreviewRightBackground = a.getDrawable(
- R.styleable.KeyboardView_keyPreviewRightBackground);
- setAlpha(mPreviewBackground, PREVIEW_ALPHA);
- setAlpha(mPreviewLeftBackground, PREVIEW_ALPHA);
- setAlpha(mPreviewRightBackground, PREVIEW_ALPHA);
- mPreviewOffset = a.getDimensionPixelOffset(
- R.styleable.KeyboardView_keyPreviewOffset, 0);
- mPreviewHeight = a.getDimensionPixelSize(
- R.styleable.KeyboardView_keyPreviewHeight, 80);
- mPreviewTextRatio = getRatio(a, R.styleable.KeyboardView_keyPreviewTextRatio);
- mPreviewTextColor = a.getColor(R.styleable.KeyboardView_keyPreviewTextColor, 0);
- mLingerTimeout = a.getInt(R.styleable.KeyboardView_keyPreviewLingerTimeout, 0);
-
- mKeyLetterRatio = keyDrawParams.mKeyLetterRatio;
- mKeyTextStyle = keyDrawParams.mKeyTextStyle;
- }
-
- public void updateKeyHeight(int keyHeight) {
- mPreviewTextSize = (int)(keyHeight * mPreviewTextRatio);
- mKeyLetterSize = (int)(keyHeight * mKeyLetterRatio);
- }
-
- private static void setAlpha(Drawable drawable, int alpha) {
- if (drawable == null)
- return;
- drawable.setAlpha(alpha);
- }
- }
-
- public KeyboardView(Context context, AttributeSet attrs) {
+ public KeyboardView(final Context context, final AttributeSet attrs) {
this(context, attrs, R.attr.keyboardViewStyle);
}
- public KeyboardView(Context context, AttributeSet attrs, int defStyle) {
+ public KeyboardView(final Context context, final AttributeSet attrs, final int defStyle) {
super(context, attrs, defStyle);
- final TypedArray a = context.obtainStyledAttributes(
- attrs, R.styleable.KeyboardView, defStyle, R.style.KeyboardView);
-
- mKeyDrawParams = new KeyDrawParams(a);
- mKeyPreviewDrawParams = new KeyPreviewDrawParams(a, mKeyDrawParams);
- mKeyPreviewLayoutId = a.getResourceId(R.styleable.KeyboardView_keyPreviewLayout, 0);
+ final TypedArray keyboardViewAttr = context.obtainStyledAttributes(attrs,
+ R.styleable.KeyboardView, defStyle, R.style.KeyboardView);
+ mKeyBackground = keyboardViewAttr.getDrawable(R.styleable.KeyboardView_keyBackground);
+ mKeyBackground.getPadding(mKeyBackgroundPadding);
+ mPreviewOffset = keyboardViewAttr.getDimensionPixelOffset(
+ R.styleable.KeyboardView_keyPreviewOffset, 0);
+ mPreviewHeight = keyboardViewAttr.getDimensionPixelSize(
+ R.styleable.KeyboardView_keyPreviewHeight, 80);
+ mPreviewLingerTimeout = keyboardViewAttr.getInt(
+ R.styleable.KeyboardView_keyPreviewLingerTimeout, 0);
+ mDelayAfterPreview = mPreviewLingerTimeout;
+ mKeyLabelHorizontalPadding = keyboardViewAttr.getDimensionPixelOffset(
+ R.styleable.KeyboardView_keyLabelHorizontalPadding, 0);
+ mKeyHintLetterPadding = keyboardViewAttr.getDimension(
+ R.styleable.KeyboardView_keyHintLetterPadding, 0);
+ mKeyPopupHintLetterPadding = keyboardViewAttr.getDimension(
+ R.styleable.KeyboardView_keyPopupHintLetterPadding, 0);
+ mKeyShiftedLetterHintPadding = keyboardViewAttr.getDimension(
+ R.styleable.KeyboardView_keyShiftedLetterHintPadding, 0);
+ mKeyTextShadowRadius = keyboardViewAttr.getFloat(
+ R.styleable.KeyboardView_keyTextShadowRadius, 0.0f);
+ mKeyPreviewLayoutId = keyboardViewAttr.getResourceId(
+ R.styleable.KeyboardView_keyPreviewLayout, 0);
if (mKeyPreviewLayoutId == 0) {
mShowKeyPreviewPopup = false;
}
- mVerticalCorrection = a.getDimensionPixelOffset(
+ mVerticalCorrection = keyboardViewAttr.getDimension(
R.styleable.KeyboardView_verticalCorrection, 0);
- mMoreKeysLayout = a.getResourceId(R.styleable.KeyboardView_moreKeysLayout, 0);
- mBackgroundDimAlpha = a.getInt(R.styleable.KeyboardView_backgroundDimAlpha, 0);
- a.recycle();
-
- mDelayAfterPreview = mKeyPreviewDrawParams.mLingerTimeout;
-
+ mMoreKeysLayout = keyboardViewAttr.getResourceId(
+ R.styleable.KeyboardView_moreKeysLayout, 0);
+ mBackgroundDimAlpha = keyboardViewAttr.getInt(
+ R.styleable.KeyboardView_backgroundDimAlpha, 0);
+ keyboardViewAttr.recycle();
+
+ final TypedArray keyAttr = context.obtainStyledAttributes(attrs,
+ R.styleable.Keyboard_Key, defStyle, R.style.KeyboardView);
+ mKeyVisualAttributes = KeyVisualAttributes.newInstance(keyAttr);
+ keyAttr.recycle();
+
+ mPreviewPlacerView = new PreviewPlacerView(context, attrs);
mPaint.setAntiAlias(true);
}
- // Read fraction value in TypedArray as float.
- /* package */ static float getRatio(TypedArray a, int index) {
- return a.getFraction(index, 1000, 1000, 1) / 1000.0f;
+ private static void blendAlpha(final Paint paint, final int alpha) {
+ final int color = paint.getColor();
+ paint.setARGB((paint.getAlpha() * alpha) / Constants.Color.ALPHA_OPAQUE,
+ Color.red(color), Color.green(color), Color.blue(color));
}
/**
@@ -388,14 +297,14 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
* @see #getKeyboard()
* @param keyboard the keyboard to display in this view
*/
- public void setKeyboard(Keyboard keyboard) {
+ public void setKeyboard(final Keyboard keyboard) {
mKeyboard = keyboard;
LatinImeLogger.onSetKeyboard(keyboard);
requestLayout();
invalidateAllKeys();
final int keyHeight = keyboard.mMostCommonKeyHeight - keyboard.mVerticalGap;
- mKeyDrawParams.updateKeyHeight(keyHeight);
- mKeyPreviewDrawParams.updateKeyHeight(keyHeight);
+ mKeyDrawParams.updateParams(keyHeight, mKeyVisualAttributes);
+ mKeyDrawParams.updateParams(keyHeight, keyboard.mKeyVisualAttributes);
}
/**
@@ -414,7 +323,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
* @param delay the delay after which the preview is dismissed
* @see #isKeyPreviewPopupEnabled()
*/
- public void setKeyPreviewPopupEnabled(boolean previewEnabled, int delay) {
+ public void setKeyPreviewPopupEnabled(final boolean previewEnabled, final int delay) {
mShowKeyPreviewPopup = previewEnabled;
mDelayAfterPreview = delay;
}
@@ -428,8 +337,14 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
return mShowKeyPreviewPopup;
}
+ public void setGesturePreviewMode(final boolean drawsGesturePreviewTrail,
+ final boolean drawsGestureFloatingPreviewText) {
+ mPreviewPlacerView.setGesturePreviewMode(
+ drawsGesturePreviewTrail, drawsGestureFloatingPreviewText);
+ }
+
@Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
if (mKeyboard != null) {
// The main keyboard expands to the display width.
final int height = mKeyboard.mOccupiedHeight + getPaddingTop() + getPaddingBottom();
@@ -440,73 +355,116 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
}
@Override
- public void onDraw(Canvas canvas) {
+ public void onDraw(final Canvas canvas) {
super.onDraw(canvas);
- if (mBufferNeedsUpdate || mBuffer == null) {
- mBufferNeedsUpdate = false;
- onBufferDraw();
+ if (canvas.isHardwareAccelerated()) {
+ onDrawKeyboard(canvas);
+ return;
+ }
+
+ final boolean bufferNeedsUpdates = mInvalidateAllKeys || !mInvalidatedKeys.isEmpty();
+ if (bufferNeedsUpdates || mOffscreenBuffer == null) {
+ if (maybeAllocateOffscreenBuffer()) {
+ mInvalidateAllKeys = true;
+ // TODO: Stop using the offscreen canvas even when in software rendering
+ mOffscreenCanvas.setBitmap(mOffscreenBuffer);
+ }
+ onDrawKeyboard(mOffscreenCanvas);
}
- canvas.drawBitmap(mBuffer, 0, 0, null);
+ canvas.drawBitmap(mOffscreenBuffer, 0, 0, null);
}
- private void onBufferDraw() {
+ private boolean maybeAllocateOffscreenBuffer() {
final int width = getWidth();
final int height = getHeight();
- if (width == 0 || height == 0)
- return;
- if (mBuffer == null || mBuffer.getWidth() != width || mBuffer.getHeight() != height) {
- if (mBuffer != null)
- mBuffer.recycle();
- mBuffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- mInvalidateAllKeys = true;
- if (mCanvas != null) {
- mCanvas.setBitmap(mBuffer);
- } else {
- mCanvas = new Canvas(mBuffer);
- }
+ if (width == 0 || height == 0) {
+ return false;
+ }
+ if (mOffscreenBuffer != null && mOffscreenBuffer.getWidth() == width
+ && mOffscreenBuffer.getHeight() == height) {
+ return false;
+ }
+ freeOffscreenBuffer();
+ mOffscreenBuffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ return true;
+ }
+
+ private void freeOffscreenBuffer() {
+ if (mOffscreenBuffer != null) {
+ mOffscreenBuffer.recycle();
+ mOffscreenBuffer = null;
}
+ }
+ private void onDrawKeyboard(final Canvas canvas) {
if (mKeyboard == null) return;
- final Canvas canvas = mCanvas;
+ final int width = getWidth();
+ final int height = getHeight();
final Paint paint = mPaint;
- final KeyDrawParams params = mKeyDrawParams;
- if (mInvalidateAllKeys || mInvalidatedKeys.isEmpty()) {
- mInvalidatedKeysRect.set(0, 0, width, height);
- canvas.clipRect(mInvalidatedKeysRect, Op.REPLACE);
+ // Calculate clip region and set.
+ final boolean drawAllKeys = mInvalidateAllKeys || mInvalidatedKeys.isEmpty();
+ final boolean isHardwareAccelerated = canvas.isHardwareAccelerated();
+ // TODO: Confirm if it's really required to draw all keys when hardware acceleration is on.
+ if (drawAllKeys || isHardwareAccelerated) {
+ mClipRegion.set(0, 0, width, height);
+ } else {
+ mClipRegion.setEmpty();
+ for (final Key key : mInvalidatedKeys) {
+ if (mKeyboard.hasKey(key)) {
+ final int x = key.mX + getPaddingLeft();
+ final int y = key.mY + getPaddingTop();
+ mWorkingRect.set(x, y, x + key.mWidth, y + key.mHeight);
+ mClipRegion.union(mWorkingRect);
+ }
+ }
+ }
+ if (!isHardwareAccelerated) {
+ canvas.clipRegion(mClipRegion, Region.Op.REPLACE);
+ // Draw keyboard background.
canvas.drawColor(Color.BLACK, PorterDuff.Mode.CLEAR);
+ final Drawable background = getBackground();
+ if (background != null) {
+ background.draw(canvas);
+ }
+ }
+
+ // TODO: Confirm if it's really required to draw all keys when hardware acceleration is on.
+ if (drawAllKeys || isHardwareAccelerated) {
// Draw all keys.
for (final Key key : mKeyboard.mKeys) {
- onDrawKey(key, canvas, paint, params);
- }
- if (mNeedsToDimEntireKeyboard) {
- drawDimRectangle(canvas, mInvalidatedKeysRect, mBackgroundDimAlpha, paint);
+ onDrawKey(key, canvas, paint);
}
} else {
// Draw invalidated keys.
for (final Key key : mInvalidatedKeys) {
- if (!mKeyboard.hasKey(key)) {
- continue;
- }
- final int x = key.mX + getPaddingLeft();
- final int y = key.mY + getPaddingTop();
- mInvalidatedKeysRect.set(x, y, x + key.mWidth, y + key.mHeight);
- canvas.clipRect(mInvalidatedKeysRect, Op.REPLACE);
- canvas.drawColor(Color.BLACK, PorterDuff.Mode.CLEAR);
- onDrawKey(key, canvas, paint, params);
- if (mNeedsToDimEntireKeyboard) {
- drawDimRectangle(canvas, mInvalidatedKeysRect, mBackgroundDimAlpha, paint);
+ if (mKeyboard.hasKey(key)) {
+ onDrawKey(key, canvas, paint);
}
}
}
+ // Overlay a dark rectangle to dim.
+ if (mNeedsToDimEntireKeyboard) {
+ paint.setColor(Color.BLACK);
+ paint.setAlpha(mBackgroundDimAlpha);
+ // Note: clipRegion() above is in effect if it was called.
+ canvas.drawRect(0, 0, width, height, paint);
+ }
+
+ // ResearchLogging indicator.
+ // TODO: Reimplement using a keyboard background image specific to the ResearchLogger,
+ // and remove this call.
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.getInstance().paintIndicator(this, paint, canvas, width, height);
+ }
+
mInvalidatedKeys.clear();
- mInvalidatedKeysRect.setEmpty();
mInvalidateAllKeys = false;
}
- public void dimEntireKeyboard(boolean dimmed) {
+ public void dimEntireKeyboard(final boolean dimmed) {
final boolean needsRedrawing = mNeedsToDimEntireKeyboard != dimmed;
mNeedsToDimEntireKeyboard = dimmed;
if (needsRedrawing) {
@@ -514,14 +472,18 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
}
}
- private void onDrawKey(Key key, Canvas canvas, Paint paint, KeyDrawParams params) {
- final int keyDrawX = key.mX + key.mVisualInsetsLeft + getPaddingLeft();
+ private void onDrawKey(final Key key, final Canvas canvas, final Paint paint) {
+ final int keyDrawX = key.getDrawX() + getPaddingLeft();
final int keyDrawY = key.mY + getPaddingTop();
canvas.translate(keyDrawX, keyDrawY);
- params.mAnimAlpha = ALPHA_OPAQUE;
+ final int keyHeight = mKeyboard.mMostCommonKeyHeight - mKeyboard.mVerticalGap;
+ final KeyVisualAttributes attr = key.mKeyVisualAttributes;
+ final KeyDrawParams params = mKeyDrawParams.mayCloneAndUpdateParams(keyHeight, attr);
+ params.mAnimAlpha = Constants.Color.ALPHA_OPAQUE;
+
if (!key.isSpacer()) {
- onDrawKeyBackground(key, canvas, params);
+ onDrawKeyBackground(key, canvas);
}
onDrawKeyTopVisuals(key, canvas, paint, params);
@@ -529,14 +491,14 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
}
// Draw key background.
- protected void onDrawKeyBackground(Key key, Canvas canvas, KeyDrawParams params) {
- final int bgWidth = key.mWidth - key.mVisualInsetsLeft - key.mVisualInsetsRight
- + params.mPadding.left + params.mPadding.right;
- final int bgHeight = key.mHeight + params.mPadding.top + params.mPadding.bottom;
- final int bgX = -params.mPadding.left;
- final int bgY = -params.mPadding.top;
+ protected void onDrawKeyBackground(final Key key, final Canvas canvas) {
+ final Rect padding = mKeyBackgroundPadding;
+ final int bgWidth = key.getDrawWidth() + padding.left + padding.right;
+ final int bgHeight = key.mHeight + padding.top + padding.bottom;
+ final int bgX = -padding.left;
+ final int bgY = -padding.top;
final int[] drawableState = key.getCurrentDrawableState();
- final Drawable background = params.mKeyBackground;
+ final Drawable background = mKeyBackground;
background.setState(drawableState);
final Rect bounds = background.getBounds();
if (bgWidth != bounds.right || bgHeight != bounds.bottom) {
@@ -551,8 +513,9 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
}
// Draw key top visuals.
- protected void onDrawKeyTopVisuals(Key key, Canvas canvas, Paint paint, KeyDrawParams params) {
- final int keyWidth = key.mWidth - key.mVisualInsetsLeft - key.mVisualInsetsRight;
+ protected void onDrawKeyTopVisuals(final Key key, final Canvas canvas, final Paint paint,
+ final KeyDrawParams params) {
+ final int keyWidth = key.getDrawWidth();
final int keyHeight = key.mHeight;
final float centerX = keyWidth * 0.5f;
final float centerY = keyHeight * 0.5f;
@@ -566,12 +529,8 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
float positionX = centerX;
if (key.mLabel != null) {
final String label = key.mLabel;
- // For characters, use large font. For labels like "Done", use smaller font.
- paint.setTypeface(key.selectTypeface(params.mKeyTextStyle));
- final int labelSize = key.selectTextSize(params.mKeyLetterSize,
- params.mKeyLargeLetterSize, params.mKeyLabelSize, params.mKeyLargeLabelSize,
- params.mKeyHintLabelSize);
- paint.setTextSize(labelSize);
+ paint.setTypeface(key.selectTypeface(params));
+ paint.setTextSize(key.selectTextSize(params));
final float labelCharHeight = getCharHeight(KEY_LABEL_REFERENCE_CHAR, paint);
final float labelCharWidth = getCharWidth(KEY_LABEL_REFERENCE_CHAR, paint);
@@ -581,10 +540,10 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
// Horizontal label text alignment
float labelWidth = 0;
if (key.isAlignLeft()) {
- positionX = (int)params.mKeyLabelHorizontalPadding;
+ positionX = mKeyLabelHorizontalPadding;
paint.setTextAlign(Align.LEFT);
} else if (key.isAlignRight()) {
- positionX = keyWidth - (int)params.mKeyLabelHorizontalPadding;
+ positionX = keyWidth - mKeyLabelHorizontalPadding;
paint.setTextAlign(Align.RIGHT);
} else if (key.isAlignLeftOfCenter()) {
// TODO: Parameterise this?
@@ -609,16 +568,15 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
Math.min(1.0f, (keyWidth * MAX_LABEL_RATIO) / getLabelWidth(label, paint)));
}
- paint.setColor(key.isShiftedLetterActivated()
- ? params.mKeyTextInactivatedColor : params.mKeyTextColor);
+ paint.setColor(key.selectTextColor(params));
if (key.isEnabled()) {
// Set a drop shadow for the text
- paint.setShadowLayer(params.mShadowRadius, 0, 0, params.mShadowColor);
+ paint.setShadowLayer(mKeyTextShadowRadius, 0, 0, params.mTextShadowColor);
} else {
// Make label invisible
paint.setColor(Color.TRANSPARENT);
}
- params.blendAlpha(paint);
+ blendAlpha(paint, params.mAnimAlpha);
canvas.drawText(label, 0, label.length(), positionX, baseline, paint);
// Turn off drop shadow and reset x-scale.
paint.setShadowLayer(0, 0, 0, 0);
@@ -646,25 +604,10 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
// Draw hint label.
if (key.mHintLabel != null) {
- final String hint = key.mHintLabel;
- final int hintColor;
- final int hintSize;
- if (key.hasHintLabel()) {
- hintColor = params.mKeyHintLabelColor;
- hintSize = params.mKeyHintLabelSize;
- paint.setTypeface(Typeface.DEFAULT);
- } else if (key.hasShiftedLetterHint()) {
- hintColor = key.isShiftedLetterActivated()
- ? params.mKeyShiftedLetterHintActivatedColor
- : params.mKeyShiftedLetterHintInactivatedColor;
- hintSize = params.mKeyShiftedLetterHintSize;
- } else { // key.hasHintLetter()
- hintColor = params.mKeyHintLetterColor;
- hintSize = params.mKeyHintLetterSize;
- }
- paint.setColor(hintColor);
- params.blendAlpha(paint);
- paint.setTextSize(hintSize);
+ final String hintLabel = key.mHintLabel;
+ paint.setTextSize(key.selectHintTextSize(params));
+ paint.setColor(key.selectHintTextColor(params));
+ blendAlpha(paint, params.mAnimAlpha);
final float hintX, hintY;
if (key.hasHintLabel()) {
// The hint label is placed just right of the key label. Used mainly on
@@ -675,19 +618,19 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
paint.setTextAlign(Align.LEFT);
} else if (key.hasShiftedLetterHint()) {
// The hint label is placed at top-right corner of the key. Used mainly on tablet.
- hintX = keyWidth - params.mKeyShiftedLetterHintPadding
+ hintX = keyWidth - mKeyShiftedLetterHintPadding
- getCharWidth(KEY_LABEL_REFERENCE_CHAR, paint) / 2;
paint.getFontMetrics(mFontMetrics);
hintY = -mFontMetrics.top;
paint.setTextAlign(Align.CENTER);
} else { // key.hasHintLetter()
// The hint letter is placed at top-right corner of the key. Used mainly on phone.
- hintX = keyWidth - params.mKeyHintLetterPadding
+ hintX = keyWidth - mKeyHintLetterPadding
- getCharWidth(KEY_NUMERIC_HINT_LABEL_REFERENCE_CHAR, paint) / 2;
hintY = -paint.ascent();
paint.setTextAlign(Align.CENTER);
}
- canvas.drawText(hint, 0, hint.length(), hintX, hintY, paint);
+ canvas.drawText(hintLabel, 0, hintLabel.length(), hintX, hintY, paint);
if (LatinImeLogger.sVISUALDEBUG) {
final Paint line = new Paint();
@@ -698,15 +641,15 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
// Draw key icon.
if (key.mLabel == null && icon != null) {
- final int iconWidth = icon.getIntrinsicWidth();
+ final int iconWidth = Math.min(icon.getIntrinsicWidth(), keyWidth);
final int iconHeight = icon.getIntrinsicHeight();
final int iconX, alignX;
final int iconY = (keyHeight - iconHeight) / 2;
if (key.isAlignLeft()) {
- iconX = (int)params.mKeyLabelHorizontalPadding;
+ iconX = mKeyLabelHorizontalPadding;
alignX = iconX;
} else if (key.isAlignRight()) {
- iconX = keyWidth - (int)params.mKeyLabelHorizontalPadding - iconWidth;
+ iconX = keyWidth - mKeyLabelHorizontalPadding - iconWidth;
alignX = iconX + iconWidth;
} else { // Align center
iconX = (keyWidth - iconWidth) / 2;
@@ -727,17 +670,18 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
}
// Draw popup hint "..." at the bottom right corner of the key.
- protected void drawKeyPopupHint(Key key, Canvas canvas, Paint paint, KeyDrawParams params) {
- final int keyWidth = key.mWidth - key.mVisualInsetsLeft - key.mVisualInsetsRight;
+ protected void drawKeyPopupHint(final Key key, final Canvas canvas, final Paint paint,
+ final KeyDrawParams params) {
+ final int keyWidth = key.getDrawWidth();
final int keyHeight = key.mHeight;
- paint.setTypeface(params.mKeyTextStyle);
- paint.setTextSize(params.mKeyHintLetterSize);
- paint.setColor(params.mKeyHintLabelColor);
+ paint.setTypeface(params.mTypeface);
+ paint.setTextSize(params.mHintLetterSize);
+ paint.setColor(params.mHintLabelColor);
paint.setTextAlign(Align.CENTER);
- final float hintX = keyWidth - params.mKeyHintLetterPadding
+ final float hintX = keyWidth - mKeyHintLetterPadding
- getCharWidth(KEY_LABEL_REFERENCE_CHAR, paint) / 2;
- final float hintY = keyHeight - params.mKeyPopupHintLetterPadding;
+ final float hintY = keyHeight - mKeyPopupHintLetterPadding;
canvas.drawText(POPUP_HINT_CHAR, hintX, hintY, paint);
if (LatinImeLogger.sVISUALDEBUG) {
@@ -747,7 +691,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
}
}
- private static int getCharGeometryCacheKey(char referenceChar, Paint paint) {
+ private static int getCharGeometryCacheKey(final char referenceChar, final Paint paint) {
final int labelSize = (int)paint.getTextSize();
final Typeface face = paint.getTypeface();
final int codePointOffset = referenceChar << 15;
@@ -765,8 +709,8 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
// Working variable for the following methods.
private final Rect mTextBounds = new Rect();
- private float getCharHeight(char[] referenceChar, Paint paint) {
- final Integer key = getCharGeometryCacheKey(referenceChar[0], paint);
+ private float getCharHeight(final char[] referenceChar, final Paint paint) {
+ final int key = getCharGeometryCacheKey(referenceChar[0], paint);
final Float cachedValue = sTextHeightCache.get(key);
if (cachedValue != null)
return cachedValue;
@@ -777,8 +721,8 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
return height;
}
- private float getCharWidth(char[] referenceChar, Paint paint) {
- final Integer key = getCharGeometryCacheKey(referenceChar[0], paint);
+ private float getCharWidth(final char[] referenceChar, final Paint paint) {
+ final int key = getCharGeometryCacheKey(referenceChar[0], paint);
final Float cachedValue = sTextWidthCache.get(key);
if (cachedValue != null)
return cachedValue;
@@ -789,36 +733,38 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
return width;
}
- public float getLabelWidth(String label, Paint paint) {
- paint.getTextBounds(label.toString(), 0, label.length(), mTextBounds);
+ // TODO: Remove this method.
+ public float getLabelWidth(final String label, final Paint paint) {
+ paint.getTextBounds(label, 0, label.length(), mTextBounds);
return mTextBounds.width();
}
- protected static void drawIcon(Canvas canvas, Drawable icon, int x, int y, int width,
- int height) {
+ protected static void drawIcon(final Canvas canvas, final Drawable icon, final int x,
+ final int y, final int width, final int height) {
canvas.translate(x, y);
icon.setBounds(0, 0, width, height);
icon.draw(canvas);
canvas.translate(-x, -y);
}
- private static void drawHorizontalLine(Canvas canvas, float y, float w, int color,
- Paint paint) {
+ private static void drawHorizontalLine(final Canvas canvas, final float y, final float w,
+ final int color, final Paint paint) {
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(1.0f);
paint.setColor(color);
canvas.drawLine(0, y, w, y, paint);
}
- private static void drawVerticalLine(Canvas canvas, float x, float h, int color, Paint paint) {
+ private static void drawVerticalLine(final Canvas canvas, final float x, final float h,
+ final int color, final Paint paint) {
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(1.0f);
paint.setColor(color);
canvas.drawLine(x, 0, x, h, paint);
}
- private static void drawRectangle(Canvas canvas, float x, float y, float w, float h, int color,
- Paint paint) {
+ private static void drawRectangle(final Canvas canvas, final float x, final float y,
+ final float w, final float h, final int color, final Paint paint) {
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(1.0f);
paint.setColor(color);
@@ -827,18 +773,11 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
canvas.translate(-x, -y);
}
- // Overlay a dark rectangle to dim.
- private static void drawDimRectangle(Canvas canvas, Rect rect, int alpha, Paint paint) {
- paint.setColor(Color.BLACK);
- paint.setAlpha(alpha);
- canvas.drawRect(rect, paint);
- }
-
public Paint newDefaultLabelPaint() {
final Paint paint = new Paint();
paint.setAntiAlias(true);
- paint.setTypeface(mKeyDrawParams.mKeyTextStyle);
- paint.setTextSize(mKeyDrawParams.mKeyLabelSize);
+ paint.setTypeface(mKeyDrawParams.mTypeface);
+ paint.setTextSize(mKeyDrawParams.mLabelSize);
return paint;
}
@@ -846,38 +785,101 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
mDrawingHandler.cancelAllMessages();
}
- // Called by {@link PointerTracker} constructor to create a TextView.
- @Override
- public TextView inflateKeyPreviewText() {
+ private TextView getKeyPreviewText(final int pointerId) {
+ TextView previewText = mKeyPreviewTexts.get(pointerId);
+ if (previewText != null) {
+ return previewText;
+ }
final Context context = getContext();
if (mKeyPreviewLayoutId != 0) {
- return (TextView)LayoutInflater.from(context).inflate(mKeyPreviewLayoutId, null);
+ previewText = (TextView)LayoutInflater.from(context).inflate(mKeyPreviewLayoutId, null);
} else {
- return new TextView(context);
+ previewText = new TextView(context);
}
+ mKeyPreviewTexts.put(pointerId, previewText);
+ return previewText;
+ }
+
+ private void dismissAllKeyPreviews() {
+ final int pointerCount = mKeyPreviewTexts.size();
+ for (int id = 0; id < pointerCount; id++) {
+ final TextView previewText = mKeyPreviewTexts.get(id);
+ if (previewText != null) {
+ previewText.setVisibility(INVISIBLE);
+ }
+ }
+ PointerTracker.setReleasedKeyGraphicsToAllKeys();
}
@Override
- public void dismissKeyPreview(PointerTracker tracker) {
+ public void dismissKeyPreview(final PointerTracker tracker) {
mDrawingHandler.dismissKeyPreview(mDelayAfterPreview, tracker);
}
- private void addKeyPreview(TextView keyPreview) {
- if (mPreviewPlacer == null) {
- mPreviewPlacer = new RelativeLayout(getContext());
- final ViewGroup windowContentView =
- (ViewGroup)getRootView().findViewById(android.R.id.content);
- windowContentView.addView(mPreviewPlacer);
+ private void addKeyPreview(final TextView keyPreview) {
+ locatePreviewPlacerView();
+ mPreviewPlacerView.addView(
+ keyPreview, ViewLayoutUtils.newLayoutParam(mPreviewPlacerView, 0, 0));
+ }
+
+ private void locatePreviewPlacerView() {
+ if (mPreviewPlacerView.getParent() != null) {
+ return;
}
- mPreviewPlacer.addView(
- keyPreview, ViewLayoutUtils.newLayoutParam(mPreviewPlacer, 0, 0));
+ final int width = getWidth();
+ final int height = getHeight();
+ if (width == 0 || height == 0) {
+ // In transient state.
+ return;
+ }
+ final int[] viewOrigin = new int[2];
+ getLocationInWindow(viewOrigin);
+ final DisplayMetrics dm = getResources().getDisplayMetrics();
+ if (viewOrigin[1] < dm.heightPixels / 4) {
+ // In transient state.
+ return;
+ }
+ final View rootView = getRootView();
+ if (rootView == null) {
+ Log.w(TAG, "Cannot find root view");
+ return;
+ }
+ final ViewGroup windowContentView = (ViewGroup)rootView.findViewById(android.R.id.content);
+ // Note: It'd be very weird if we get null by android.R.id.content.
+ if (windowContentView == null) {
+ Log.w(TAG, "Cannot find android.R.id.content view to add PreviewPlacerView");
+ } else {
+ windowContentView.addView(mPreviewPlacerView);
+ mPreviewPlacerView.setKeyboardViewGeometry(viewOrigin[0], viewOrigin[1], width, height);
+ }
+ }
+
+ public void showGestureFloatingPreviewText(final String gestureFloatingPreviewText) {
+ locatePreviewPlacerView();
+ mPreviewPlacerView.setGestureFloatingPreviewText(gestureFloatingPreviewText);
+ }
+
+ public void dismissGestureFloatingPreviewText() {
+ locatePreviewPlacerView();
+ mPreviewPlacerView.dismissGestureFloatingPreviewText();
+ }
+
+ @Override
+ public void showGesturePreviewTrail(final PointerTracker tracker,
+ final boolean isOldestTracker) {
+ locatePreviewPlacerView();
+ mPreviewPlacerView.invalidatePointer(tracker, isOldestTracker);
}
@Override
- public void showKeyPreview(PointerTracker tracker) {
- if (!mShowKeyPreviewPopup) return;
+ public void showKeyPreview(final PointerTracker tracker) {
+ final KeyPreviewDrawParams previewParams = mKeyPreviewDrawParams;
+ if (!mShowKeyPreviewPopup) {
+ previewParams.mPreviewVisibleOffset = -mKeyboard.mVerticalGap;
+ return;
+ }
- final TextView previewText = tracker.getKeyPreviewText();
+ final TextView previewText = getKeyPreviewText(tracker.mPointerId);
// If the key preview has no parent view yet, add it to the ViewGroup which can place
// key preview absolutely in SoftInputWindow.
if (previewText.getParent() == null) {
@@ -889,21 +891,28 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
// If key is invalid or IME is already closed, we must not show key preview.
// Trying to show key preview while root window is closed causes
// WindowManager.BadTokenException.
- if (key == null)
+ if (key == null) {
return;
+ }
- final KeyPreviewDrawParams params = mKeyPreviewDrawParams;
+ final KeyDrawParams drawParams = mKeyDrawParams;
+ previewText.setTextColor(drawParams.mPreviewTextColor);
+ final Drawable background = previewText.getBackground();
+ if (background != null) {
+ background.setState(KEY_PREVIEW_BACKGROUND_DEFAULT_STATE);
+ background.setAlpha(PREVIEW_ALPHA);
+ }
final String label = key.isShiftedLetterActivated() ? key.mHintLabel : key.mLabel;
- // What we show as preview should match what we show on a key top in onBufferDraw().
+ // What we show as preview should match what we show on a key top in onDraw().
if (label != null) {
// TODO Should take care of temporaryShiftLabel here.
previewText.setCompoundDrawables(null, null, null, null);
if (StringUtils.codePointCount(label) > 1) {
- previewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, params.mKeyLetterSize);
+ previewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, drawParams.mLetterSize);
previewText.setTypeface(Typeface.DEFAULT_BOLD);
} else {
- previewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, params.mPreviewTextSize);
- previewText.setTypeface(params.mKeyTextStyle);
+ previewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, drawParams.mPreviewTextSize);
+ previewText.setTypeface(key.selectTypeface(drawParams));
}
previewText.setText(label);
} else {
@@ -911,48 +920,44 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
key.getPreviewIcon(mKeyboard.mIconsSet));
previewText.setText(null);
}
- previewText.setBackgroundDrawable(params.mPreviewBackground);
previewText.measure(
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
- final int keyDrawWidth = key.mWidth - key.mVisualInsetsLeft - key.mVisualInsetsRight;
+ final int keyDrawWidth = key.getDrawWidth();
final int previewWidth = previewText.getMeasuredWidth();
- final int previewHeight = params.mPreviewHeight;
+ final int previewHeight = mPreviewHeight;
// The width and height of visible part of the key preview background. The content marker
// of the background 9-patch have to cover the visible part of the background.
- params.mPreviewVisibleWidth = previewWidth - previewText.getPaddingLeft()
+ previewParams.mPreviewVisibleWidth = previewWidth - previewText.getPaddingLeft()
- previewText.getPaddingRight();
- params.mPreviewVisibleHeight = previewHeight - previewText.getPaddingTop()
+ previewParams.mPreviewVisibleHeight = previewHeight - previewText.getPaddingTop()
- previewText.getPaddingBottom();
// The distance between the top edge of the parent key and the bottom of the visible part
// of the key preview background.
- params.mPreviewVisibleOffset = params.mPreviewOffset - previewText.getPaddingBottom();
- getLocationInWindow(params.mCoordinates);
+ previewParams.mPreviewVisibleOffset = mPreviewOffset - previewText.getPaddingBottom();
+ getLocationInWindow(mCoordinates);
// The key preview is horizontally aligned with the center of the visible part of the
// parent key. If it doesn't fit in this {@link KeyboardView}, it is moved inward to fit and
// the left/right background is used if such background is specified.
- int previewX = key.mX + key.mVisualInsetsLeft - (previewWidth - keyDrawWidth) / 2
- + params.mCoordinates[0];
+ final int statePosition;
+ int previewX = key.getDrawX() - (previewWidth - keyDrawWidth) / 2 + mCoordinates[0];
if (previewX < 0) {
previewX = 0;
- if (params.mPreviewLeftBackground != null) {
- previewText.setBackgroundDrawable(params.mPreviewLeftBackground);
- }
+ statePosition = STATE_LEFT;
} else if (previewX > getWidth() - previewWidth) {
previewX = getWidth() - previewWidth;
- if (params.mPreviewRightBackground != null) {
- previewText.setBackgroundDrawable(params.mPreviewRightBackground);
- }
+ statePosition = STATE_RIGHT;
+ } else {
+ statePosition = STATE_MIDDLE;
}
// The key preview is placed vertically above the top edge of the parent key with an
// arbitrary offset.
- final int previewY = key.mY - previewHeight + params.mPreviewOffset
- + params.mCoordinates[1];
+ final int previewY = key.mY - previewHeight + mPreviewOffset + mCoordinates[1];
- // Set the preview background state
- previewText.getBackground().setState(
- key.mMoreKeys != null ? LONG_PRESSABLE_STATE_SET : EMPTY_STATE_SET);
- previewText.setTextColor(params.mPreviewTextColor);
+ if (background != null) {
+ final int hasMoreKeys = (key.mMoreKeys != null) ? STATE_HAS_MOREKEYS : STATE_NORMAL;
+ background.setState(KEY_PREVIEW_BACKGROUND_STATE_TABLE[statePosition][hasMoreKeys]);
+ }
ViewLayoutUtils.placeViewAt(
previewText, previewX, previewY, previewWidth, previewHeight);
previewText.setVisibility(VISIBLE);
@@ -967,7 +972,6 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
public void invalidateAllKeys() {
mInvalidatedKeys.clear();
mInvalidateAllKeys = true;
- mBufferNeedsUpdate = true;
invalidate();
}
@@ -979,19 +983,17 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
* @see #invalidateAllKeys
*/
@Override
- public void invalidateKey(Key key) {
+ public void invalidateKey(final Key key) {
if (mInvalidateAllKeys) return;
if (key == null) return;
mInvalidatedKeys.add(key);
final int x = key.mX + getPaddingLeft();
final int y = key.mY + getPaddingTop();
- mInvalidatedKeysRect.union(x, y, x + key.mWidth, y + key.mHeight);
- mBufferNeedsUpdate = true;
- invalidate(mInvalidatedKeysRect);
+ invalidate(x, y, x + key.mWidth, y + key.mHeight);
}
public void closing() {
- PointerTracker.dismissAllKeyPreviews();
+ dismissAllKeyPreviews();
cancelAllMessages();
mInvalidateAllKeys = true;
@@ -1012,12 +1014,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy {
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
closing();
- if (mPreviewPlacer != null) {
- mPreviewPlacer.removeAllViews();
- }
- if (mBuffer != null) {
- mBuffer.recycle();
- mBuffer = null;
- }
+ mPreviewPlacerView.removeAllViews();
+ freeOffscreenBuffer();
}
}
diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
index 383298de9..3e6f92c2a 100644
--- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
@@ -43,16 +43,19 @@ import com.android.inputmethod.accessibility.AccessibilityUtils;
import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
import com.android.inputmethod.keyboard.PointerTracker.DrawingProxy;
import com.android.inputmethod.keyboard.PointerTracker.TimerProxy;
+import com.android.inputmethod.keyboard.internal.KeyDrawParams;
+import com.android.inputmethod.keyboard.internal.SuddenJumpingTouchEventHandler;
+import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.LatinIME;
import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.ResearchLogger;
+import com.android.inputmethod.latin.ResourceUtils;
import com.android.inputmethod.latin.StaticInnerHandlerWrapper;
import com.android.inputmethod.latin.StringUtils;
import com.android.inputmethod.latin.SubtypeLocale;
-import com.android.inputmethod.latin.Utils;
import com.android.inputmethod.latin.Utils.UsabilityStudyLogUtils;
import com.android.inputmethod.latin.define.ProductionFlag;
+import com.android.inputmethod.research.ResearchLogger;
import java.util.Locale;
import java.util.WeakHashMap;
@@ -60,13 +63,40 @@ import java.util.WeakHashMap;
/**
* A view that is responsible for detecting key presses and touch movements.
*
- * @attr ref R.styleable#KeyboardView_keyHysteresisDistance
- * @attr ref R.styleable#KeyboardView_verticalCorrection
- * @attr ref R.styleable#KeyboardView_popupLayout
+ * @attr ref R.styleable#MainKeyboardView_autoCorrectionSpacebarLedEnabled
+ * @attr ref R.styleable#MainKeyboardView_autoCorrectionSpacebarLedIcon
+ * @attr ref R.styleable#MainKeyboardView_spacebarTextRatio
+ * @attr ref R.styleable#MainKeyboardView_spacebarTextColor
+ * @attr ref R.styleable#MainKeyboardView_spacebarTextShadowColor
+ * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarFinalAlpha
+ * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarFadeoutAnimator
+ * @attr ref R.styleable#MainKeyboardView_altCodeKeyWhileTypingFadeoutAnimator
+ * @attr ref R.styleable#MainKeyboardView_altCodeKeyWhileTypingFadeinAnimator
+ * @attr ref R.styleable#MainKeyboardView_keyHysteresisDistance
+ * @attr ref R.styleable#MainKeyboardView_touchNoiseThresholdTime
+ * @attr ref R.styleable#MainKeyboardView_touchNoiseThresholdDistance
+ * @attr ref R.styleable#MainKeyboardView_slidingKeyInputEnable
+ * @attr ref R.styleable#MainKeyboardView_keyRepeatStartTimeout
+ * @attr ref R.styleable#MainKeyboardView_keyRepeatInterval
+ * @attr ref R.styleable#MainKeyboardView_longPressKeyTimeout
+ * @attr ref R.styleable#MainKeyboardView_longPressShiftKeyTimeout
+ * @attr ref R.styleable#MainKeyboardView_ignoreAltCodeKeyTimeout
+ * @attr ref R.styleable#MainKeyboardView_showMoreKeysKeyboardAtTouchPoint
+ * @attr ref R.styleable#MainKeyboardView_gestureStaticTimeThresholdAfterFastTyping
+ * @attr ref R.styleable#MainKeyboardView_gestureDetectFastMoveSpeedThreshold
+ * @attr ref R.styleable#MainKeyboardView_gestureDynamicThresholdDecayDuration
+ * @attr ref R.styleable#MainKeyboardView_gestureDynamicTimeThresholdFrom
+ * @attr ref R.styleable#MainKeyboardView_gestureDynamicTimeThresholdTo
+ * @attr ref R.styleable#MainKeyboardView_gestureDynamicDistanceThresholdFrom
+ * @attr ref R.styleable#MainKeyboardView_gestureDynamicDistanceThresholdTo
+ * @attr ref R.styleable#MainKeyboardView_gestureSamplingMinimumDistance
+ * @attr ref R.styleable#MainKeyboardView_gestureRecognitionMinimumTime
+ * @attr ref R.styleable#MainKeyboardView_gestureRecognitionSpeedThreshold
+ * @attr ref R.styleable#MainKeyboardView_suppressKeyPreviewAfterBatchInputDuration
*/
-public class LatinKeyboardView extends KeyboardView implements PointerTracker.KeyEventHandler,
+public final class MainKeyboardView extends KeyboardView implements PointerTracker.KeyEventHandler,
SuddenJumpingTouchEventHandler.ProcessMotionEvent {
- private static final String TAG = LatinKeyboardView.class.getSimpleName();
+ private static final String TAG = MainKeyboardView.class.getSimpleName();
// TODO: Kill process when the usability study mode was changed.
private static final boolean ENABLE_USABILITY_STUDY_LOG = LatinImeLogger.sUsabilityStudy;
@@ -80,10 +110,9 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
// Stuff to draw language name on spacebar.
private final int mLanguageOnSpacebarFinalAlpha;
private ObjectAnimator mLanguageOnSpacebarFadeoutAnimator;
- private static final int ALPHA_OPAQUE = 255;
private boolean mNeedsToDisplayLanguage;
private boolean mHasMultipleEnabledIMEsOrSubtypes;
- private int mLanguageOnSpacebarAnimAlpha = ALPHA_OPAQUE;
+ private int mLanguageOnSpacebarAnimAlpha = Constants.Color.ALPHA_OPAQUE;
private final float mSpacebarTextRatio;
private float mSpacebarTextSize;
private final int mSpacebarTextColor;
@@ -99,7 +128,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
// Stuff to draw altCodeWhileTyping keys.
private ObjectAnimator mAltCodeKeyWhileTypingFadeoutAnimator;
private ObjectAnimator mAltCodeKeyWhileTypingFadeinAnimator;
- private int mAltCodeKeyWhileTypingAnimAlpha = ALPHA_OPAQUE;
+ private int mAltCodeKeyWhileTypingAnimAlpha = Constants.Color.ALPHA_OPAQUE;
// More keys keyboard
private PopupWindow mMoreKeysWindow;
@@ -109,7 +138,6 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
new WeakHashMap<Key, MoreKeysPanel>();
private final boolean mConfigShowMoreKeysKeyboardAtTouchedPoint;
- private final PointerTrackerParams mPointerTrackerParams;
private final SuddenJumpingTouchEventHandler mTouchScreenRegulator;
protected KeyDetector mKeyDetector;
@@ -119,29 +147,49 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
private final KeyTimerHandler mKeyTimerHandler;
- private static class KeyTimerHandler extends StaticInnerHandlerWrapper<LatinKeyboardView>
+ private static final class KeyTimerHandler extends StaticInnerHandlerWrapper<MainKeyboardView>
implements TimerProxy {
+ private static final int MSG_TYPING_STATE_EXPIRED = 0;
private static final int MSG_REPEAT_KEY = 1;
private static final int MSG_LONGPRESS_KEY = 2;
private static final int MSG_DOUBLE_TAP = 3;
- private static final int MSG_TYPING_STATE_EXPIRED = 4;
- private final KeyTimerParams mParams;
- private boolean mInKeyRepeat;
+ private final int mKeyRepeatStartTimeout;
+ private final int mKeyRepeatInterval;
+ private final int mLongPressKeyTimeout;
+ private final int mLongPressShiftKeyTimeout;
+ private final int mIgnoreAltCodeKeyTimeout;
- public KeyTimerHandler(LatinKeyboardView outerInstance, KeyTimerParams params) {
+ public KeyTimerHandler(final MainKeyboardView outerInstance,
+ final TypedArray mainKeyboardViewAttr) {
super(outerInstance);
- mParams = params;
+
+ mKeyRepeatStartTimeout = mainKeyboardViewAttr.getInt(
+ R.styleable.MainKeyboardView_keyRepeatStartTimeout, 0);
+ mKeyRepeatInterval = mainKeyboardViewAttr.getInt(
+ R.styleable.MainKeyboardView_keyRepeatInterval, 0);
+ mLongPressKeyTimeout = mainKeyboardViewAttr.getInt(
+ R.styleable.MainKeyboardView_longPressKeyTimeout, 0);
+ mLongPressShiftKeyTimeout = mainKeyboardViewAttr.getInt(
+ R.styleable.MainKeyboardView_longPressShiftKeyTimeout, 0);
+ mIgnoreAltCodeKeyTimeout = mainKeyboardViewAttr.getInt(
+ R.styleable.MainKeyboardView_ignoreAltCodeKeyTimeout, 0);
}
@Override
- public void handleMessage(Message msg) {
- final LatinKeyboardView keyboardView = getOuterInstance();
+ public void handleMessage(final Message msg) {
+ final MainKeyboardView keyboardView = getOuterInstance();
final PointerTracker tracker = (PointerTracker) msg.obj;
switch (msg.what) {
+ case MSG_TYPING_STATE_EXPIRED:
+ startWhileTypingFadeinAnimation(keyboardView);
+ break;
case MSG_REPEAT_KEY:
- tracker.onRegisterKey(tracker.getKey());
- startKeyRepeatTimer(tracker, mParams.mKeyRepeatInterval);
+ final Key currentKey = tracker.getKey();
+ if (currentKey != null && currentKey.mCode == msg.arg1) {
+ tracker.onRegisterKey(currentKey);
+ startKeyRepeatTimer(tracker, mKeyRepeatInterval);
+ }
break;
case MSG_LONGPRESS_KEY:
if (tracker != null) {
@@ -150,39 +198,36 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
KeyboardSwitcher.getInstance().onLongPressTimeout(msg.arg1);
}
break;
- case MSG_TYPING_STATE_EXPIRED:
- cancelAndStartAnimators(keyboardView.mAltCodeKeyWhileTypingFadeoutAnimator,
- keyboardView.mAltCodeKeyWhileTypingFadeinAnimator);
- break;
}
}
- private void startKeyRepeatTimer(PointerTracker tracker, long delay) {
- sendMessageDelayed(obtainMessage(MSG_REPEAT_KEY, tracker), delay);
+ private void startKeyRepeatTimer(final PointerTracker tracker, final long delay) {
+ final Key key = tracker.getKey();
+ if (key == null) return;
+ sendMessageDelayed(obtainMessage(MSG_REPEAT_KEY, key.mCode, 0, tracker), delay);
}
@Override
- public void startKeyRepeatTimer(PointerTracker tracker) {
- mInKeyRepeat = true;
- startKeyRepeatTimer(tracker, mParams.mKeyRepeatStartTimeout);
+ public void startKeyRepeatTimer(final PointerTracker tracker) {
+ startKeyRepeatTimer(tracker, mKeyRepeatStartTimeout);
}
public void cancelKeyRepeatTimer() {
- mInKeyRepeat = false;
removeMessages(MSG_REPEAT_KEY);
}
+ // TODO: Suppress layout changes in key repeat mode
public boolean isInKeyRepeat() {
- return mInKeyRepeat;
+ return hasMessages(MSG_REPEAT_KEY);
}
@Override
- public void startLongPressTimer(int code) {
+ public void startLongPressTimer(final int code) {
cancelLongPressTimer();
final int delay;
switch (code) {
case Keyboard.CODE_SHIFT:
- delay = mParams.mLongPressShiftKeyTimeout;
+ delay = mLongPressShiftKeyTimeout;
break;
default:
delay = 0;
@@ -194,7 +239,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
}
@Override
- public void startLongPressTimer(PointerTracker tracker) {
+ public void startLongPressTimer(final PointerTracker tracker) {
cancelLongPressTimer();
if (tracker == null) {
return;
@@ -203,15 +248,15 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
final int delay;
switch (key.mCode) {
case Keyboard.CODE_SHIFT:
- delay = mParams.mLongPressShiftKeyTimeout;
+ delay = mLongPressShiftKeyTimeout;
break;
default:
if (KeyboardSwitcher.getInstance().isInMomentarySwitchState()) {
// We use longer timeout for sliding finger input started from the symbols
// mode key.
- delay = mParams.mLongPressKeyTimeout * 3;
+ delay = mLongPressKeyTimeout * 3;
} else {
- delay = mParams.mLongPressKeyTimeout;
+ delay = mLongPressKeyTimeout;
}
break;
}
@@ -225,7 +270,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
removeMessages(MSG_LONGPRESS_KEY);
}
- public static void cancelAndStartAnimators(final ObjectAnimator animatorToCancel,
+ private static void cancelAndStartAnimators(final ObjectAnimator animatorToCancel,
final ObjectAnimator animatorToStart) {
float startFraction = 0.0f;
if (animatorToCancel.isStarted()) {
@@ -237,18 +282,39 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
animatorToStart.setCurrentPlayTime(startTime);
}
+ private static void startWhileTypingFadeinAnimation(final MainKeyboardView keyboardView) {
+ cancelAndStartAnimators(keyboardView.mAltCodeKeyWhileTypingFadeoutAnimator,
+ keyboardView.mAltCodeKeyWhileTypingFadeinAnimator);
+ }
+
+ private static void startWhileTypingFadeoutAnimation(final MainKeyboardView keyboardView) {
+ cancelAndStartAnimators(keyboardView.mAltCodeKeyWhileTypingFadeinAnimator,
+ keyboardView.mAltCodeKeyWhileTypingFadeoutAnimator);
+ }
+
@Override
- public void startTypingStateTimer() {
+ public void startTypingStateTimer(final Key typedKey) {
+ if (typedKey.isModifier() || typedKey.altCodeWhileTyping()) {
+ return;
+ }
+
final boolean isTyping = isTypingState();
removeMessages(MSG_TYPING_STATE_EXPIRED);
+ final MainKeyboardView keyboardView = getOuterInstance();
+
+ // When user hits the space or the enter key, just cancel the while-typing timer.
+ final int typedCode = typedKey.mCode;
+ if (typedCode == Keyboard.CODE_SPACE || typedCode == Keyboard.CODE_ENTER) {
+ startWhileTypingFadeinAnimation(keyboardView);
+ return;
+ }
+
sendMessageDelayed(
- obtainMessage(MSG_TYPING_STATE_EXPIRED), mParams.mIgnoreAltCodeKeyTimeout);
+ obtainMessage(MSG_TYPING_STATE_EXPIRED), mIgnoreAltCodeKeyTimeout);
if (isTyping) {
return;
}
- final LatinKeyboardView keyboardView = getOuterInstance();
- cancelAndStartAnimators(keyboardView.mAltCodeKeyWhileTypingFadeinAnimator,
- keyboardView.mAltCodeKeyWhileTypingFadeoutAnimator);
+ startWhileTypingFadeoutAnimation(keyboardView);
}
@Override
@@ -283,99 +349,56 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
}
}
- public static class PointerTrackerParams {
- public final boolean mSlidingKeyInputEnabled;
- public final int mTouchNoiseThresholdTime;
- public final float mTouchNoiseThresholdDistance;
-
- public static final PointerTrackerParams DEFAULT = new PointerTrackerParams();
-
- private PointerTrackerParams() {
- mSlidingKeyInputEnabled = false;
- mTouchNoiseThresholdTime =0;
- mTouchNoiseThresholdDistance = 0;
- }
-
- public PointerTrackerParams(TypedArray latinKeyboardViewAttr) {
- mSlidingKeyInputEnabled = latinKeyboardViewAttr.getBoolean(
- R.styleable.LatinKeyboardView_slidingKeyInputEnable, false);
- mTouchNoiseThresholdTime = latinKeyboardViewAttr.getInt(
- R.styleable.LatinKeyboardView_touchNoiseThresholdTime, 0);
- mTouchNoiseThresholdDistance = latinKeyboardViewAttr.getDimension(
- R.styleable.LatinKeyboardView_touchNoiseThresholdDistance, 0);
- }
+ public MainKeyboardView(final Context context, final AttributeSet attrs) {
+ this(context, attrs, R.attr.mainKeyboardViewStyle);
}
- static class KeyTimerParams {
- public final int mKeyRepeatStartTimeout;
- public final int mKeyRepeatInterval;
- public final int mLongPressKeyTimeout;
- public final int mLongPressShiftKeyTimeout;
- public final int mIgnoreAltCodeKeyTimeout;
-
- public KeyTimerParams(TypedArray latinKeyboardViewAttr) {
- mKeyRepeatStartTimeout = latinKeyboardViewAttr.getInt(
- R.styleable.LatinKeyboardView_keyRepeatStartTimeout, 0);
- mKeyRepeatInterval = latinKeyboardViewAttr.getInt(
- R.styleable.LatinKeyboardView_keyRepeatInterval, 0);
- mLongPressKeyTimeout = latinKeyboardViewAttr.getInt(
- R.styleable.LatinKeyboardView_longPressKeyTimeout, 0);
- mLongPressShiftKeyTimeout = latinKeyboardViewAttr.getInt(
- R.styleable.LatinKeyboardView_longPressShiftKeyTimeout, 0);
- mIgnoreAltCodeKeyTimeout = latinKeyboardViewAttr.getInt(
- R.styleable.LatinKeyboardView_ignoreAltCodeKeyTimeout, 0);
- }
- }
-
- public LatinKeyboardView(Context context, AttributeSet attrs) {
- this(context, attrs, R.attr.latinKeyboardViewStyle);
- }
-
- public LatinKeyboardView(Context context, AttributeSet attrs, int defStyle) {
+ public MainKeyboardView(final Context context, final AttributeSet attrs, final int defStyle) {
super(context, attrs, defStyle);
mTouchScreenRegulator = new SuddenJumpingTouchEventHandler(getContext(), this);
mHasDistinctMultitouch = context.getPackageManager()
.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT);
+ final Resources res = getResources();
final boolean needsPhantomSuddenMoveEventHack = Boolean.parseBoolean(
- Utils.getDeviceOverrideValue(context.getResources(),
+ ResourceUtils.getDeviceOverrideValue(res,
R.array.phantom_sudden_move_event_device_list, "false"));
PointerTracker.init(mHasDistinctMultitouch, needsPhantomSuddenMoveEventHack);
final TypedArray a = context.obtainStyledAttributes(
- attrs, R.styleable.LatinKeyboardView, defStyle, R.style.LatinKeyboardView);
+ attrs, R.styleable.MainKeyboardView, defStyle, R.style.MainKeyboardView);
mAutoCorrectionSpacebarLedEnabled = a.getBoolean(
- R.styleable.LatinKeyboardView_autoCorrectionSpacebarLedEnabled, false);
+ R.styleable.MainKeyboardView_autoCorrectionSpacebarLedEnabled, false);
mAutoCorrectionSpacebarLedIcon = a.getDrawable(
- R.styleable.LatinKeyboardView_autoCorrectionSpacebarLedIcon);
- mSpacebarTextRatio = a.getFraction(R.styleable.LatinKeyboardView_spacebarTextRatio,
- 1000, 1000, 1) / 1000.0f;
- mSpacebarTextColor = a.getColor(R.styleable.LatinKeyboardView_spacebarTextColor, 0);
+ R.styleable.MainKeyboardView_autoCorrectionSpacebarLedIcon);
+ mSpacebarTextRatio = a.getFraction(
+ R.styleable.MainKeyboardView_spacebarTextRatio, 1, 1, 1.0f);
+ mSpacebarTextColor = a.getColor(R.styleable.MainKeyboardView_spacebarTextColor, 0);
mSpacebarTextShadowColor = a.getColor(
- R.styleable.LatinKeyboardView_spacebarTextShadowColor, 0);
+ R.styleable.MainKeyboardView_spacebarTextShadowColor, 0);
mLanguageOnSpacebarFinalAlpha = a.getInt(
- R.styleable.LatinKeyboardView_languageOnSpacebarFinalAlpha, ALPHA_OPAQUE);
+ R.styleable.MainKeyboardView_languageOnSpacebarFinalAlpha,
+ Constants.Color.ALPHA_OPAQUE);
final int languageOnSpacebarFadeoutAnimatorResId = a.getResourceId(
- R.styleable.LatinKeyboardView_languageOnSpacebarFadeoutAnimator, 0);
+ R.styleable.MainKeyboardView_languageOnSpacebarFadeoutAnimator, 0);
final int altCodeKeyWhileTypingFadeoutAnimatorResId = a.getResourceId(
- R.styleable.LatinKeyboardView_altCodeKeyWhileTypingFadeoutAnimator, 0);
+ R.styleable.MainKeyboardView_altCodeKeyWhileTypingFadeoutAnimator, 0);
final int altCodeKeyWhileTypingFadeinAnimatorResId = a.getResourceId(
- R.styleable.LatinKeyboardView_altCodeKeyWhileTypingFadeinAnimator, 0);
-
- final KeyTimerParams keyTimerParams = new KeyTimerParams(a);
- mPointerTrackerParams = new PointerTrackerParams(a);
+ R.styleable.MainKeyboardView_altCodeKeyWhileTypingFadeinAnimator, 0);
final float keyHysteresisDistance = a.getDimension(
- R.styleable.LatinKeyboardView_keyHysteresisDistance, 0);
- mKeyDetector = new KeyDetector(keyHysteresisDistance);
- mKeyTimerHandler = new KeyTimerHandler(this, keyTimerParams);
+ R.styleable.MainKeyboardView_keyHysteresisDistance, 0);
+ final float keyHysteresisDistanceForSlidingModifier = a.getDimension(
+ R.styleable.MainKeyboardView_keyHysteresisDistanceForSlidingModifier, 0);
+ mKeyDetector = new KeyDetector(
+ keyHysteresisDistance, keyHysteresisDistanceForSlidingModifier);
+ mKeyTimerHandler = new KeyTimerHandler(this, a);
mConfigShowMoreKeysKeyboardAtTouchedPoint = a.getBoolean(
- R.styleable.LatinKeyboardView_showMoreKeysKeyboardAtTouchedPoint, false);
+ R.styleable.MainKeyboardView_showMoreKeysKeyboardAtTouchedPoint, false);
+ PointerTracker.setParameters(a);
a.recycle();
- PointerTracker.setParameters(mPointerTrackerParams);
-
mLanguageOnSpacebarFadeoutAnimator = loadObjectAnimator(
languageOnSpacebarFadeoutAnimatorResId, this);
mAltCodeKeyWhileTypingFadeoutAnimator = loadObjectAnimator(
@@ -384,7 +407,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
altCodeKeyWhileTypingFadeinAnimatorResId, this);
}
- private ObjectAnimator loadObjectAnimator(int resId, Object target) {
+ private ObjectAnimator loadObjectAnimator(final int resId, final Object target) {
if (resId == 0) return null;
final ObjectAnimator animator = (ObjectAnimator)AnimatorInflater.loadAnimator(
getContext(), resId);
@@ -399,7 +422,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
return mLanguageOnSpacebarAnimAlpha;
}
- public void setLanguageOnSpacebarAnimAlpha(int alpha) {
+ public void setLanguageOnSpacebarAnimAlpha(final int alpha) {
mLanguageOnSpacebarAnimAlpha = alpha;
invalidateKey(mSpaceKey);
}
@@ -408,12 +431,12 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
return mAltCodeKeyWhileTypingAnimAlpha;
}
- public void setAltCodeKeyWhileTypingAnimAlpha(int alpha) {
+ public void setAltCodeKeyWhileTypingAnimAlpha(final int alpha) {
mAltCodeKeyWhileTypingAnimAlpha = alpha;
updateAltCodeKeyWhileTyping();
}
- public void setKeyboardActionListener(KeyboardActionListener listener) {
+ public void setKeyboardActionListener(final KeyboardActionListener listener) {
mKeyboardActionListener = listener;
PointerTracker.setKeyboardActionListener(listener);
}
@@ -450,9 +473,9 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
* @param keyboard the keyboard to display in this view
*/
@Override
- public void setKeyboard(Keyboard keyboard) {
- // Remove any pending messages, except dismissing preview
- mKeyTimerHandler.cancelKeyTimers();
+ public void setKeyboard(final Keyboard keyboard) {
+ // Remove any pending messages, except dismissing preview and key repeat.
+ mKeyTimerHandler.cancelLongPressTimer();
super.setKeyboard(keyboard);
mKeyDetector.setKeyboard(
keyboard, -getPaddingLeft(), -getPaddingTop() + mVerticalCorrection);
@@ -462,11 +485,11 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
mSpaceKey = keyboard.getKey(Keyboard.CODE_SPACE);
mSpaceIcon = (mSpaceKey != null)
- ? mSpaceKey.getIcon(keyboard.mIconsSet, ALPHA_OPAQUE) : null;
+ ? mSpaceKey.getIcon(keyboard.mIconsSet, Constants.Color.ALPHA_OPAQUE) : null;
final int keyHeight = keyboard.mMostCommonKeyHeight - keyboard.mVerticalGap;
mSpacebarTextSize = keyHeight * mSpacebarTextRatio;
if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinKeyboardView_setKeyboard(keyboard);
+ ResearchLogger.mainKeyboardView_setKeyboard(keyboard);
}
// This always needs to be set since the accessibility state can
@@ -474,6 +497,15 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
AccessibleKeyboardViewProxy.getInstance().setKeyboard(keyboard);
}
+ // Note that this method is called from a non-UI thread.
+ public void setMainDictionaryAvailability(final boolean mainDictionaryAvailable) {
+ PointerTracker.setMainDictionaryAvailability(mainDictionaryAvailable);
+ }
+
+ public void setGestureHandlingEnabledByUser(final boolean gestureHandlingEnabledByUser) {
+ PointerTracker.setGestureHandlingEnabledByUser(gestureHandlingEnabledByUser);
+ }
+
/**
* Returns whether the device has distinct multi-touch panel.
* @return true if the device has distinct multi-touch panel.
@@ -482,25 +514,29 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
return mHasDistinctMultitouch;
}
- public void setDistinctMultitouch(boolean hasDistinctMultitouch) {
+ public void setDistinctMultitouch(final boolean hasDistinctMultitouch) {
mHasDistinctMultitouch = hasDistinctMultitouch;
}
- /**
- * When enabled, calls to {@link KeyboardActionListener#onCodeInput} will include key
- * codes for adjacent keys. When disabled, only the primary key code will be
- * reported.
- * @param enabled whether or not the proximity correction is enabled
- */
- public void setProximityCorrectionEnabled(boolean enabled) {
- mKeyDetector.setProximityCorrectionEnabled(enabled);
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ // Notify the research logger that the keyboard view has been attached. This is needed
+ // to properly show the splash screen, which requires that the window token of the
+ // KeyboardView be non-null.
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.getInstance().mainKeyboardView_onAttachedToWindow(this);
+ }
}
- /**
- * Returns true if proximity correction is enabled.
- */
- public boolean isProximityCorrectionEnabled() {
- return mKeyDetector.isProximityCorrectionEnabled();
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ // Notify the research logger that the keyboard view has been detached. This is needed
+ // to invalidate the reference of {@link MainKeyboardView} to null.
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.getInstance().mainKeyboardView_onDetachedFromWindow();
+ }
}
@Override
@@ -509,7 +545,8 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
super.cancelAllMessages();
}
- private boolean openMoreKeysKeyboardIfRequired(Key parentKey, PointerTracker tracker) {
+ private boolean openMoreKeysKeyboardIfRequired(final Key parentKey,
+ final PointerTracker tracker) {
// Check if we have a popup layout specified first.
if (mMoreKeysLayout == 0) {
return false;
@@ -524,7 +561,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
}
// This default implementation returns a more keys panel.
- protected MoreKeysPanel onCreateMoreKeysPanel(Key parentKey) {
+ protected MoreKeysPanel onCreateMoreKeysPanel(final Key parentKey) {
if (parentKey.mMoreKeys == null)
return null;
@@ -550,9 +587,9 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
* @return true if the long press is handled, false otherwise. Subclasses should call the
* method on the base class if the subclass doesn't wish to handle the call.
*/
- protected boolean onLongPress(Key parentKey, PointerTracker tracker) {
+ protected boolean onLongPress(final Key parentKey, final PointerTracker tracker) {
if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinKeyboardView_onLongPress();
+ ResearchLogger.mainKeyboardView_onLongPress();
}
final int primaryCode = parentKey.mCode;
if (parentKey.hasEmbeddedMoreKey()) {
@@ -574,21 +611,20 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
return openMoreKeysPanel(parentKey, tracker);
}
- private boolean invokeCustomRequest(int code) {
+ private boolean invokeCustomRequest(final int code) {
return mKeyboardActionListener.onCustomRequest(code);
}
- private void invokeCodeInput(int primaryCode) {
- mKeyboardActionListener.onCodeInput(primaryCode,
- KeyboardActionListener.NOT_A_TOUCH_COORDINATE,
- KeyboardActionListener.NOT_A_TOUCH_COORDINATE);
+ private void invokeCodeInput(final int primaryCode) {
+ mKeyboardActionListener.onCodeInput(
+ primaryCode, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
}
- private void invokeReleaseKey(int primaryCode) {
+ private void invokeReleaseKey(final int primaryCode) {
mKeyboardActionListener.onReleaseKey(primaryCode, false);
}
- private boolean openMoreKeysPanel(Key parentKey, PointerTracker tracker) {
+ private boolean openMoreKeysPanel(final Key parentKey, final PointerTracker tracker) {
MoreKeysPanel moreKeysPanel = mMoreKeysPanelCache.get(parentKey);
if (moreKeysPanel == null) {
moreKeysPanel = onCreateMoreKeysPanel(parentKey);
@@ -614,9 +650,9 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
// The more keys keyboard is usually vertically aligned with the top edge of the parent key
// (plus vertical gap). If the key preview is enabled, the more keys keyboard is vertically
// aligned with the bottom edge of the visible part of the key preview.
- final int pointY = parentKey.mY + (keyPreviewEnabled
- ? mKeyPreviewDrawParams.mPreviewVisibleOffset
- : -parentKey.mVerticalGap);
+ // {@code mPreviewVisibleOffset} has been set appropriately in
+ // {@link KeyboardView#showKeyPreview(PointerTracker)}.
+ final int pointY = parentKey.mY + mKeyPreviewDrawParams.mPreviewVisibleOffset;
moreKeysPanel.showMoreKeysPanel(
this, this, pointX, pointY, mMoreKeysWindow, mKeyboardActionListener);
final int translatedX = moreKeysPanel.translateX(tracker.getLastX());
@@ -639,7 +675,15 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
}
@Override
- public boolean onTouchEvent(MotionEvent me) {
+ public boolean dispatchTouchEvent(MotionEvent event) {
+ if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
+ return AccessibleKeyboardViewProxy.getInstance().dispatchTouchEvent(event);
+ }
+ return super.dispatchTouchEvent(event);
+ }
+
+ @Override
+ public boolean onTouchEvent(final MotionEvent me) {
if (getKeyboard() == null) {
return false;
}
@@ -647,7 +691,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
}
@Override
- public boolean processMotionEvent(MotionEvent me) {
+ public boolean processMotionEvent(final MotionEvent me) {
final boolean nonDistinctMultitouch = !mHasDistinctMultitouch;
final int action = me.getActionMasked();
final int pointerCount = me.getPointerCount();
@@ -703,7 +747,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
}
}
if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinKeyboardView_processMotionEvent(me, action, eventTime, index, id,
+ ResearchLogger.mainKeyboardView_processMotionEvent(me, action, eventTime, index, id,
x, y);
}
@@ -755,15 +799,18 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
final PointerTracker tracker = PointerTracker.getPointerTracker(
pointerId, this);
final int px, py;
+ final MotionEvent motionEvent;
if (mMoreKeysPanel != null
&& tracker.mPointerId == mMoreKeysPanelPointerTrackerId) {
px = mMoreKeysPanel.translateX((int)me.getX(i));
py = mMoreKeysPanel.translateY((int)me.getY(i));
+ motionEvent = null;
} else {
px = (int)me.getX(i);
py = (int)me.getY(i);
+ motionEvent = me;
}
- tracker.onMoveEvent(px, py, eventTime);
+ tracker.onMoveEvent(px, py, eventTime, motionEvent);
if (ENABLE_USABILITY_STUDY_LOG) {
final float pointerSize = me.getSize(i);
final float pointerPressure = me.getPressure(i);
@@ -772,7 +819,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
+ pointerSize + "," + pointerPressure);
}
if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinKeyboardView_processMotionEvent(me, action, eventTime,
+ ResearchLogger.mainKeyboardView_processMotionEvent(me, action, eventTime,
i, pointerId, px, py);
}
}
@@ -803,20 +850,6 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
return false;
}
- @Override
- public void draw(Canvas c) {
- Utils.GCUtils.getInstance().reset();
- boolean tryGC = true;
- for (int i = 0; i < Utils.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) {
- try {
- super.draw(c);
- tryGC = false;
- } catch (OutOfMemoryError e) {
- tryGC = Utils.GCUtils.getInstance().tryGCOrWait(TAG, e);
- }
- }
- }
-
/**
* Receives hover events from the input framework.
*
@@ -825,7 +858,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
* otherwise
*/
@Override
- public boolean dispatchHoverEvent(MotionEvent event) {
+ public boolean dispatchHoverEvent(final MotionEvent event) {
if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()) {
final PointerTracker tracker = PointerTracker.getPointerTracker(0, this);
return AccessibleKeyboardViewProxy.getInstance().dispatchHoverEvent(event, tracker);
@@ -835,7 +868,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
return false;
}
- public void updateShortcutKey(boolean available) {
+ public void updateShortcutKey(final boolean available) {
final Keyboard keyboard = getKeyboard();
if (keyboard == null) return;
final Key shortcutKey = keyboard.getKey(Keyboard.CODE_SHORTCUT);
@@ -852,8 +885,8 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
}
}
- public void startDisplayLanguageOnSpacebar(boolean subtypeChanged,
- boolean needsToDisplayLanguage, boolean hasMultipleEnabledIMEsOrSubtypes) {
+ public void startDisplayLanguageOnSpacebar(final boolean subtypeChanged,
+ final boolean needsToDisplayLanguage, final boolean hasMultipleEnabledIMEsOrSubtypes) {
mNeedsToDisplayLanguage = needsToDisplayLanguage;
mHasMultipleEnabledIMEsOrSubtypes = hasMultipleEnabledIMEsOrSubtypes;
final ObjectAnimator animator = mLanguageOnSpacebarFadeoutAnimator;
@@ -861,7 +894,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
mNeedsToDisplayLanguage = false;
} else {
if (subtypeChanged && needsToDisplayLanguage) {
- setLanguageOnSpacebarAnimAlpha(ALPHA_OPAQUE);
+ setLanguageOnSpacebarAnimAlpha(Constants.Color.ALPHA_OPAQUE);
if (animator.isStarted()) {
animator.cancel();
}
@@ -875,14 +908,15 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
invalidateKey(mSpaceKey);
}
- public void updateAutoCorrectionState(boolean isAutoCorrection) {
+ public void updateAutoCorrectionState(final boolean isAutoCorrection) {
if (!mAutoCorrectionSpacebarLedEnabled) return;
mAutoCorrectionSpacebarLedOn = isAutoCorrection;
invalidateKey(mSpaceKey);
}
@Override
- protected void onDrawKeyTopVisuals(Key key, Canvas canvas, Paint paint, KeyDrawParams params) {
+ protected void onDrawKeyTopVisuals(final Key key, final Canvas canvas, final Paint paint,
+ final KeyDrawParams params) {
if (key.altCodeWhileTyping() && key.isEnabled()) {
params.mAnimAlpha = mAltCodeKeyWhileTypingAnimAlpha;
}
@@ -900,7 +934,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
}
}
- private boolean fitsTextIntoWidth(final int width, String text, Paint paint) {
+ private boolean fitsTextIntoWidth(final int width, final String text, final Paint paint) {
paint.setTextScaleX(1.0f);
final float textWidth = getLabelWidth(text, paint);
if (textWidth < width) return true;
@@ -913,7 +947,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
}
// Layout language name on spacebar.
- private String layoutLanguageOnSpacebar(Paint paint, InputMethodSubtype subtype,
+ private String layoutLanguageOnSpacebar(final Paint paint, final InputMethodSubtype subtype,
final int width) {
// Choose appropriate language name to fit into the width.
String text = getFullDisplayName(subtype, getResources());
@@ -934,7 +968,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
return "";
}
- private void drawSpacebar(Key key, Canvas canvas, Paint paint) {
+ private void drawSpacebar(final Key key, final Canvas canvas, final Paint paint) {
final int width = key.mWidth;
final int height = key.mHeight;
@@ -989,7 +1023,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
// zz azerty T AZERTY AZERTY
// Get InputMethodSubtype's full display name in its locale.
- static String getFullDisplayName(InputMethodSubtype subtype, Resources res) {
+ static String getFullDisplayName(final InputMethodSubtype subtype, final Resources res) {
if (SubtypeLocale.isNoLanguage(subtype)) {
return SubtypeLocale.getKeyboardLayoutSetDisplayName(subtype);
}
@@ -998,7 +1032,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
}
// Get InputMethodSubtype's short display name in its locale.
- static String getShortDisplayName(InputMethodSubtype subtype) {
+ static String getShortDisplayName(final InputMethodSubtype subtype) {
if (SubtypeLocale.isNoLanguage(subtype)) {
return "";
}
@@ -1007,7 +1041,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke
}
// Get InputMethodSubtype's middle display name in its locale.
- static String getMiddleDisplayName(InputMethodSubtype subtype) {
+ static String getMiddleDisplayName(final InputMethodSubtype subtype) {
if (SubtypeLocale.isNoLanguage(subtype)) {
return SubtypeLocale.getKeyboardLayoutSetDisplayName(subtype);
}
diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java b/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java
index cd4e3001e..a2001cb8f 100644
--- a/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java
+++ b/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java
@@ -16,7 +16,7 @@
package com.android.inputmethod.keyboard;
-public class MoreKeysDetector extends KeyDetector {
+public final class MoreKeysDetector extends KeyDetector {
private final int mSlideAllowanceSquare;
private final int mSlideAllowanceSquareTop;
diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java
index a3741a2d8..d7d4be40b 100644
--- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java
@@ -20,15 +20,17 @@ import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.view.View;
-import com.android.inputmethod.keyboard.internal.KeySpecParser.MoreKeySpec;
+import com.android.inputmethod.keyboard.internal.KeyboardBuilder;
import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
+import com.android.inputmethod.keyboard.internal.KeyboardParams;
+import com.android.inputmethod.keyboard.internal.MoreKeySpec;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.StringUtils;
-public class MoreKeysKeyboard extends Keyboard {
+public final class MoreKeysKeyboard extends Keyboard {
private final int mDefaultKeyCoordX;
- MoreKeysKeyboard(Builder.MoreKeysKeyboardParams params) {
+ MoreKeysKeyboard(final MoreKeysKeyboardParams params) {
super(params);
mDefaultKeyCoordX = params.getDefaultKeyCoordX() + params.mDefaultKeyWidth / 2;
}
@@ -37,228 +39,231 @@ public class MoreKeysKeyboard extends Keyboard {
return mDefaultKeyCoordX;
}
- public static class Builder extends Keyboard.Builder<Builder.MoreKeysKeyboardParams> {
- private final Key mParentKey;
- private final Drawable mDivider;
-
- private static final float LABEL_PADDING_RATIO = 0.2f;
- private static final float DIVIDER_RATIO = 0.2f;
+ /* package for test */
+ static class MoreKeysKeyboardParams extends KeyboardParams {
+ public boolean mIsFixedOrder;
+ /* package */int mTopRowAdjustment;
+ public int mNumRows;
+ public int mNumColumns;
+ public int mTopKeys;
+ public int mLeftKeys;
+ public int mRightKeys; // includes default key.
+ public int mDividerWidth;
+ public int mColumnWidth;
+
+ public MoreKeysKeyboardParams() {
+ super();
+ }
- public static class MoreKeysKeyboardParams extends Keyboard.Params {
- public boolean mIsFixedOrder;
- /* package */int mTopRowAdjustment;
- public int mNumRows;
- public int mNumColumns;
- public int mTopKeys;
- public int mLeftKeys;
- public int mRightKeys; // includes default key.
- public int mDividerWidth;
- public int mColumnWidth;
-
- public MoreKeysKeyboardParams() {
- super();
+ /**
+ * Set keyboard parameters of more keys keyboard.
+ *
+ * @param numKeys number of keys in this more keys keyboard.
+ * @param maxColumns number of maximum columns of this more keys keyboard.
+ * @param keyWidth more keys keyboard key width in pixel, including horizontal gap.
+ * @param rowHeight more keys keyboard row height in pixel, including vertical gap.
+ * @param coordXInParent coordinate x of the key preview in parent keyboard.
+ * @param parentKeyboardWidth parent keyboard width in pixel.
+ * @param isFixedColumnOrder if true, more keys should be laid out in fixed order.
+ * @param dividerWidth width of divider, zero for no dividers.
+ */
+ public void setParameters(final int numKeys, final int maxColumns, final int keyWidth,
+ final int rowHeight, final int coordXInParent, final int parentKeyboardWidth,
+ final boolean isFixedColumnOrder, final int dividerWidth) {
+ mIsFixedOrder = isFixedColumnOrder;
+ if (parentKeyboardWidth / keyWidth < maxColumns) {
+ throw new IllegalArgumentException(
+ "Keyboard is too small to hold more keys keyboard: "
+ + parentKeyboardWidth + " " + keyWidth + " " + maxColumns);
}
-
- /**
- * Set keyboard parameters of more keys keyboard.
- *
- * @param numKeys number of keys in this more keys keyboard.
- * @param maxColumns number of maximum columns of this more keys keyboard.
- * @param keyWidth more keys keyboard key width in pixel, including horizontal gap.
- * @param rowHeight more keys keyboard row height in pixel, including vertical gap.
- * @param coordXInParent coordinate x of the key preview in parent keyboard.
- * @param parentKeyboardWidth parent keyboard width in pixel.
- * @param isFixedColumnOrder if true, more keys should be laid out in fixed order.
- * @param dividerWidth width of divider, zero for no dividers.
- */
- public void setParameters(int numKeys, int maxColumns, int keyWidth, int rowHeight,
- int coordXInParent, int parentKeyboardWidth, boolean isFixedColumnOrder,
- int dividerWidth) {
- mIsFixedOrder = isFixedColumnOrder;
- if (parentKeyboardWidth / keyWidth < maxColumns) {
- throw new IllegalArgumentException(
- "Keyboard is too small to hold more keys keyboard: "
- + parentKeyboardWidth + " " + keyWidth + " " + maxColumns);
- }
- mDefaultKeyWidth = keyWidth;
- mDefaultRowHeight = rowHeight;
-
- final int numRows = (numKeys + maxColumns - 1) / maxColumns;
- mNumRows = numRows;
- final int numColumns = mIsFixedOrder ? Math.min(numKeys, maxColumns)
- : getOptimizedColumns(numKeys, maxColumns);
- mNumColumns = numColumns;
- final int topKeys = numKeys % numColumns;
- mTopKeys = topKeys == 0 ? numColumns : topKeys;
-
- final int numLeftKeys = (numColumns - 1) / 2;
- final int numRightKeys = numColumns - numLeftKeys; // including default key.
- // Maximum number of keys we can layout both side of the parent key
- final int maxLeftKeys = coordXInParent / keyWidth;
- final int maxRightKeys = (parentKeyboardWidth - coordXInParent) / keyWidth;
- int leftKeys, rightKeys;
- if (numLeftKeys > maxLeftKeys) {
- leftKeys = maxLeftKeys;
- rightKeys = numColumns - leftKeys;
- } else if (numRightKeys > maxRightKeys + 1) {
- rightKeys = maxRightKeys + 1; // include default key
- leftKeys = numColumns - rightKeys;
- } else {
- leftKeys = numLeftKeys;
- rightKeys = numRightKeys;
- }
- // If the left keys fill the left side of the parent key, entire more keys keyboard
- // should be shifted to the right unless the parent key is on the left edge.
- if (maxLeftKeys == leftKeys && leftKeys > 0) {
- leftKeys--;
- rightKeys++;
- }
- // If the right keys fill the right side of the parent key, entire more keys
- // should be shifted to the left unless the parent key is on the right edge.
- if (maxRightKeys == rightKeys - 1 && rightKeys > 1) {
- leftKeys++;
- rightKeys--;
- }
- mLeftKeys = leftKeys;
- mRightKeys = rightKeys;
-
- // Adjustment of the top row.
- mTopRowAdjustment = mIsFixedOrder ? getFixedOrderTopRowAdjustment()
- : getAutoOrderTopRowAdjustment();
- mDividerWidth = dividerWidth;
- mColumnWidth = mDefaultKeyWidth + mDividerWidth;
- mBaseWidth = mOccupiedWidth = mNumColumns * mColumnWidth - mDividerWidth;
- // Need to subtract the bottom row's gutter only.
- mBaseHeight = mOccupiedHeight = mNumRows * mDefaultRowHeight - mVerticalGap
- + mTopPadding + mBottomPadding;
+ mDefaultKeyWidth = keyWidth;
+ mDefaultRowHeight = rowHeight;
+
+ final int numRows = (numKeys + maxColumns - 1) / maxColumns;
+ mNumRows = numRows;
+ final int numColumns = mIsFixedOrder ? Math.min(numKeys, maxColumns)
+ : getOptimizedColumns(numKeys, maxColumns);
+ mNumColumns = numColumns;
+ final int topKeys = numKeys % numColumns;
+ mTopKeys = topKeys == 0 ? numColumns : topKeys;
+
+ final int numLeftKeys = (numColumns - 1) / 2;
+ final int numRightKeys = numColumns - numLeftKeys; // including default key.
+ // Maximum number of keys we can layout both side of the parent key
+ final int maxLeftKeys = coordXInParent / keyWidth;
+ final int maxRightKeys = (parentKeyboardWidth - coordXInParent) / keyWidth;
+ int leftKeys, rightKeys;
+ if (numLeftKeys > maxLeftKeys) {
+ leftKeys = maxLeftKeys;
+ rightKeys = numColumns - leftKeys;
+ } else if (numRightKeys > maxRightKeys + 1) {
+ rightKeys = maxRightKeys + 1; // include default key
+ leftKeys = numColumns - rightKeys;
+ } else {
+ leftKeys = numLeftKeys;
+ rightKeys = numRightKeys;
}
-
- private int getFixedOrderTopRowAdjustment() {
- if (mNumRows == 1 || mTopKeys % 2 == 1 || mTopKeys == mNumColumns
- || mLeftKeys == 0 || mRightKeys == 1) {
- return 0;
- }
- return -1;
+ // If the left keys fill the left side of the parent key, entire more keys keyboard
+ // should be shifted to the right unless the parent key is on the left edge.
+ if (maxLeftKeys == leftKeys && leftKeys > 0) {
+ leftKeys--;
+ rightKeys++;
}
-
- private int getAutoOrderTopRowAdjustment() {
- if (mNumRows == 1 || mTopKeys == 1 || mNumColumns % 2 == mTopKeys % 2
- || mLeftKeys == 0 || mRightKeys == 1) {
- return 0;
- }
- return -1;
+ // If the right keys fill the right side of the parent key, entire more keys
+ // should be shifted to the left unless the parent key is on the right edge.
+ if (maxRightKeys == rightKeys - 1 && rightKeys > 1) {
+ leftKeys++;
+ rightKeys--;
}
+ mLeftKeys = leftKeys;
+ mRightKeys = rightKeys;
+
+ // Adjustment of the top row.
+ mTopRowAdjustment = mIsFixedOrder ? getFixedOrderTopRowAdjustment()
+ : getAutoOrderTopRowAdjustment();
+ mDividerWidth = dividerWidth;
+ mColumnWidth = mDefaultKeyWidth + mDividerWidth;
+ mBaseWidth = mOccupiedWidth = mNumColumns * mColumnWidth - mDividerWidth;
+ // Need to subtract the bottom row's gutter only.
+ mBaseHeight = mOccupiedHeight = mNumRows * mDefaultRowHeight - mVerticalGap
+ + mTopPadding + mBottomPadding;
+ }
- // Return key position according to column count (0 is default).
- /* package */int getColumnPos(int n) {
- return mIsFixedOrder ? getFixedOrderColumnPos(n) : getAutomaticColumnPos(n);
+ private int getFixedOrderTopRowAdjustment() {
+ if (mNumRows == 1 || mTopKeys % 2 == 1 || mTopKeys == mNumColumns
+ || mLeftKeys == 0 || mRightKeys == 1) {
+ return 0;
}
+ return -1;
+ }
- private int getFixedOrderColumnPos(int n) {
- final int col = n % mNumColumns;
- final int row = n / mNumColumns;
- if (!isTopRow(row)) {
- return col - mLeftKeys;
- }
- final int rightSideKeys = mTopKeys / 2;
- final int leftSideKeys = mTopKeys - (rightSideKeys + 1);
- final int pos = col - leftSideKeys;
- final int numLeftKeys = mLeftKeys + mTopRowAdjustment;
- final int numRightKeys = mRightKeys - 1;
- if (numRightKeys >= rightSideKeys && numLeftKeys >= leftSideKeys) {
- return pos;
- } else if (numRightKeys < rightSideKeys) {
- return pos - (rightSideKeys - numRightKeys);
- } else { // numLeftKeys < leftSideKeys
- return pos + (leftSideKeys - numLeftKeys);
- }
+ private int getAutoOrderTopRowAdjustment() {
+ if (mNumRows == 1 || mTopKeys == 1 || mNumColumns % 2 == mTopKeys % 2
+ || mLeftKeys == 0 || mRightKeys == 1) {
+ return 0;
}
+ return -1;
+ }
- private int getAutomaticColumnPos(int n) {
- final int col = n % mNumColumns;
- final int row = n / mNumColumns;
- int leftKeys = mLeftKeys;
- if (isTopRow(row)) {
- leftKeys += mTopRowAdjustment;
- }
- if (col == 0) {
- // default position.
- return 0;
- }
+ // Return key position according to column count (0 is default).
+ /* package */int getColumnPos(final int n) {
+ return mIsFixedOrder ? getFixedOrderColumnPos(n) : getAutomaticColumnPos(n);
+ }
- int pos = 0;
- int right = 1; // include default position key.
- int left = 0;
- int i = 0;
- while (true) {
- // Assign right key if available.
- if (right < mRightKeys) {
- pos = right;
- right++;
- i++;
- }
- if (i >= col)
- break;
- // Assign left key if available.
- if (left < leftKeys) {
- left++;
- pos = -left;
- i++;
- }
- if (i >= col)
- break;
- }
+ private int getFixedOrderColumnPos(final int n) {
+ final int col = n % mNumColumns;
+ final int row = n / mNumColumns;
+ if (!isTopRow(row)) {
+ return col - mLeftKeys;
+ }
+ final int rightSideKeys = mTopKeys / 2;
+ final int leftSideKeys = mTopKeys - (rightSideKeys + 1);
+ final int pos = col - leftSideKeys;
+ final int numLeftKeys = mLeftKeys + mTopRowAdjustment;
+ final int numRightKeys = mRightKeys - 1;
+ if (numRightKeys >= rightSideKeys && numLeftKeys >= leftSideKeys) {
return pos;
+ } else if (numRightKeys < rightSideKeys) {
+ return pos - (rightSideKeys - numRightKeys);
+ } else { // numLeftKeys < leftSideKeys
+ return pos + (leftSideKeys - numLeftKeys);
}
+ }
- private static int getTopRowEmptySlots(int numKeys, int numColumns) {
- final int remainings = numKeys % numColumns;
- return remainings == 0 ? 0 : numColumns - remainings;
+ private int getAutomaticColumnPos(final int n) {
+ final int col = n % mNumColumns;
+ final int row = n / mNumColumns;
+ int leftKeys = mLeftKeys;
+ if (isTopRow(row)) {
+ leftKeys += mTopRowAdjustment;
+ }
+ if (col == 0) {
+ // default position.
+ return 0;
}
- private int getOptimizedColumns(int numKeys, int maxColumns) {
- int numColumns = Math.min(numKeys, maxColumns);
- while (getTopRowEmptySlots(numKeys, numColumns) >= mNumRows) {
- numColumns--;
+ int pos = 0;
+ int right = 1; // include default position key.
+ int left = 0;
+ int i = 0;
+ while (true) {
+ // Assign right key if available.
+ if (right < mRightKeys) {
+ pos = right;
+ right++;
+ i++;
}
- return numColumns;
+ if (i >= col)
+ break;
+ // Assign left key if available.
+ if (left < leftKeys) {
+ left++;
+ pos = -left;
+ i++;
+ }
+ if (i >= col)
+ break;
}
+ return pos;
+ }
- public int getDefaultKeyCoordX() {
- return mLeftKeys * mColumnWidth;
- }
+ private static int getTopRowEmptySlots(final int numKeys, final int numColumns) {
+ final int remainings = numKeys % numColumns;
+ return remainings == 0 ? 0 : numColumns - remainings;
+ }
- public int getX(int n, int row) {
- final int x = getColumnPos(n) * mColumnWidth + getDefaultKeyCoordX();
- if (isTopRow(row)) {
- return x + mTopRowAdjustment * (mColumnWidth / 2);
- }
- return x;
+ private int getOptimizedColumns(final int numKeys, final int maxColumns) {
+ int numColumns = Math.min(numKeys, maxColumns);
+ while (getTopRowEmptySlots(numKeys, numColumns) >= mNumRows) {
+ numColumns--;
}
+ return numColumns;
+ }
- public int getY(int row) {
- return (mNumRows - 1 - row) * mDefaultRowHeight + mTopPadding;
- }
+ public int getDefaultKeyCoordX() {
+ return mLeftKeys * mColumnWidth;
+ }
- public void markAsEdgeKey(Key key, int row) {
- if (row == 0)
- key.markAsTopEdge(this);
- if (isTopRow(row))
- key.markAsBottomEdge(this);
+ public int getX(final int n, final int row) {
+ final int x = getColumnPos(n) * mColumnWidth + getDefaultKeyCoordX();
+ if (isTopRow(row)) {
+ return x + mTopRowAdjustment * (mColumnWidth / 2);
}
+ return x;
+ }
- private boolean isTopRow(int rowCount) {
- return mNumRows > 1 && rowCount == mNumRows - 1;
- }
+ public int getY(final int row) {
+ return (mNumRows - 1 - row) * mDefaultRowHeight + mTopPadding;
}
+ public void markAsEdgeKey(final Key key, final int row) {
+ if (row == 0)
+ key.markAsTopEdge(this);
+ if (isTopRow(row))
+ key.markAsBottomEdge(this);
+ }
+
+ private boolean isTopRow(final int rowCount) {
+ return mNumRows > 1 && rowCount == mNumRows - 1;
+ }
+ }
+
+ public static class Builder extends KeyboardBuilder<MoreKeysKeyboardParams> {
+ private final Key mParentKey;
+ private final Drawable mDivider;
+
+ private static final float LABEL_PADDING_RATIO = 0.2f;
+ private static final float DIVIDER_RATIO = 0.2f;
+
+
/**
* The builder of MoreKeysKeyboard.
* @param containerView the container of {@link MoreKeysKeyboardView}.
* @param parentKey the {@link Key} that invokes more keys keyboard.
* @param parentKeyboardView the {@link KeyboardView} that contains the parentKey.
*/
- public Builder(View containerView, Key parentKey, KeyboardView parentKeyboardView) {
+ public Builder(final View containerView, final Key parentKey,
+ final KeyboardView parentKeyboardView) {
super(containerView.getContext(), new MoreKeysKeyboardParams());
final Keyboard parentKeyboard = parentKeyboardView.getKeyboard();
load(parentKeyboard.mMoreKeysTemplate, parentKeyboard.mId);
@@ -300,14 +305,14 @@ public class MoreKeysKeyboard extends Keyboard {
dividerWidth);
}
- private static int getMaxKeyWidth(KeyboardView view, Key parentKey, int minKeyWidth) {
+ private static int getMaxKeyWidth(final KeyboardView view, final Key parentKey,
+ final int minKeyWidth) {
final int padding = (int)(view.getResources()
.getDimension(R.dimen.more_keys_keyboard_key_horizontal_padding)
+ (parentKey.hasLabelsInMoreKeys() ? minKeyWidth * LABEL_PADDING_RATIO : 0));
final Paint paint = view.newDefaultLabelPaint();
- paint.setTextSize(parentKey.hasLabelsInMoreKeys()
- ? view.mKeyDrawParams.mKeyLabelSize
- : view.mKeyDrawParams.mKeyLetterSize);
+ paint.setTypeface(parentKey.selectTypeface(view.mKeyDrawParams));
+ paint.setTextSize(parentKey.selectMoreKeyTextSize(view.mKeyDrawParams));
int maxWidth = minKeyWidth;
for (final MoreKeySpec spec : parentKey.mMoreKeys) {
final String label = spec.mLabel;
@@ -322,24 +327,6 @@ public class MoreKeysKeyboard extends Keyboard {
return maxWidth;
}
- private static class MoreKeyDivider extends Key.Spacer {
- private final Drawable mIcon;
-
- public MoreKeyDivider(MoreKeysKeyboardParams params, Drawable icon, int x, int y) {
- super(params, x, y, params.mDividerWidth, params.mDefaultRowHeight);
- mIcon = icon;
- }
-
- @Override
- public Drawable getIcon(KeyboardIconsSet iconSet, int alpha) {
- // KeyboardIconsSet and alpha are unused. Use the icon that has been passed to the
- // constructor.
- // TODO: Drawable itself should have an alpha value.
- mIcon.setAlpha(128);
- return mIcon;
- }
- }
-
@Override
public MoreKeysKeyboard build() {
final MoreKeysKeyboardParams params = mParams;
@@ -368,4 +355,23 @@ public class MoreKeysKeyboard extends Keyboard {
return new MoreKeysKeyboard(params);
}
}
+
+ private static class MoreKeyDivider extends Key.Spacer {
+ private final Drawable mIcon;
+
+ public MoreKeyDivider(final MoreKeysKeyboardParams params, final Drawable icon,
+ final int x, final int y) {
+ super(params, x, y, params.mDividerWidth, params.mDefaultRowHeight);
+ mIcon = icon;
+ }
+
+ @Override
+ public Drawable getIcon(final KeyboardIconsSet iconSet, final int alpha) {
+ // KeyboardIconsSet and alpha are unused. Use the icon that has been passed to the
+ // constructor.
+ // TODO: Drawable itself should have an alpha value.
+ mIcon.setAlpha(128);
+ return mIcon;
+ }
+ }
}
diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
index be7644fb5..a50617693 100644
--- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
@@ -25,13 +25,15 @@ import android.widget.PopupWindow;
import com.android.inputmethod.keyboard.PointerTracker.DrawingProxy;
import com.android.inputmethod.keyboard.PointerTracker.TimerProxy;
+import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.latin.InputPointers;
import com.android.inputmethod.latin.R;
/**
* A view that renders a virtual {@link MoreKeysKeyboard}. It handles rendering of keys and
* detecting key presses and touch movements.
*/
-public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel {
+public final class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel {
private final int[] mCoordinates = new int[2];
private final KeyDetector mKeyDetector;
@@ -49,7 +51,8 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel
public void onCodeInput(int primaryCode, int x, int y) {
// Because a more keys keyboard doesn't need proximity characters correction, we don't
// send touch event coordinates.
- mListener.onCodeInput(primaryCode, NOT_A_TOUCH_COORDINATE, NOT_A_TOUCH_COORDINATE);
+ mListener.onCodeInput(
+ primaryCode, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
}
@Override
@@ -58,6 +61,21 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel
}
@Override
+ public void onStartBatchInput() {
+ mListener.onStartBatchInput();
+ }
+
+ @Override
+ public void onUpdateBatchInput(InputPointers batchPointers) {
+ mListener.onUpdateBatchInput(batchPointers);
+ }
+
+ @Override
+ public void onEndBatchInput(InputPointers batchPointers) {
+ mListener.onEndBatchInput(batchPointers);
+ }
+
+ @Override
public void onCancelInput() {
mListener.onCancelInput();
}
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index babf6ec99..d0f7cb276 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -16,25 +16,37 @@
package com.android.inputmethod.keyboard;
+import android.content.res.TypedArray;
import android.os.SystemClock;
import android.util.Log;
import android.view.MotionEvent;
-import android.view.View;
-import android.widget.TextView;
+import com.android.inputmethod.accessibility.AccessibilityUtils;
+import com.android.inputmethod.keyboard.internal.GestureStroke;
+import com.android.inputmethod.keyboard.internal.GestureStroke.GestureStrokeParams;
+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;
import com.android.inputmethod.latin.LatinImeLogger;
-import com.android.inputmethod.latin.ResearchLogger;
+import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.define.ProductionFlag;
+import com.android.inputmethod.research.ResearchLogger;
import java.util.ArrayList;
-public class PointerTracker {
+public final class PointerTracker implements PointerTrackerQueue.Element {
private static final String TAG = PointerTracker.class.getSimpleName();
private static final boolean DEBUG_EVENT = false;
private static final boolean DEBUG_MOVE_EVENT = false;
private static final boolean DEBUG_LISTENER = false;
- private static boolean DEBUG_MODE = LatinImeLogger.sDBG;
+ private static boolean DEBUG_MODE = LatinImeLogger.sDBG || DEBUG_EVENT;
+
+ /** True if {@link PointerTracker}s should handle gesture events. */
+ private static boolean sShouldHandleGesture = false;
+ private static boolean sMainDictionaryAvailable = false;
+ private static boolean sGestureHandlingEnabledByInputField = false;
+ private static boolean sGestureHandlingEnabledByUser = false;
public interface KeyEventHandler {
/**
@@ -65,13 +77,13 @@ public class PointerTracker {
public interface DrawingProxy extends MoreKeysPanel.Controller {
public void invalidateKey(Key key);
- public TextView inflateKeyPreviewText();
public void showKeyPreview(PointerTracker tracker);
public void dismissKeyPreview(PointerTracker tracker);
+ public void showGesturePreviewTrail(PointerTracker tracker, boolean isOldestTracker);
}
public interface TimerProxy {
- public void startTypingStateTimer();
+ public void startTypingStateTimer(Key typedKey);
public boolean isTypingState();
public void startKeyRepeatTimer(PointerTracker tracker);
public void startLongPressTimer(PointerTracker tracker);
@@ -84,7 +96,7 @@ public class PointerTracker {
public static class Adapter implements TimerProxy {
@Override
- public void startTypingStateTimer() {}
+ public void startTypingStateTimer(Key typedKey) {}
@Override
public boolean isTypingState() { return false; }
@Override
@@ -106,12 +118,44 @@ public class PointerTracker {
}
}
+ static final class PointerTrackerParams {
+ public final boolean mSlidingKeyInputEnabled;
+ public final int mTouchNoiseThresholdTime;
+ public final int mTouchNoiseThresholdDistance;
+ public final int mSuppressKeyPreviewAfterBatchInputDuration;
+
+ public static final PointerTrackerParams DEFAULT = new PointerTrackerParams();
+
+ private PointerTrackerParams() {
+ mSlidingKeyInputEnabled = false;
+ mTouchNoiseThresholdTime = 0;
+ mTouchNoiseThresholdDistance = 0;
+ mSuppressKeyPreviewAfterBatchInputDuration = 0;
+ }
+
+ public PointerTrackerParams(final TypedArray mainKeyboardViewAttr) {
+ mSlidingKeyInputEnabled = mainKeyboardViewAttr.getBoolean(
+ R.styleable.MainKeyboardView_slidingKeyInputEnable, false);
+ mTouchNoiseThresholdTime = mainKeyboardViewAttr.getInt(
+ R.styleable.MainKeyboardView_touchNoiseThresholdTime, 0);
+ mTouchNoiseThresholdDistance = mainKeyboardViewAttr.getDimensionPixelSize(
+ R.styleable.MainKeyboardView_touchNoiseThresholdDistance, 0);
+ mSuppressKeyPreviewAfterBatchInputDuration = mainKeyboardViewAttr.getInt(
+ R.styleable.MainKeyboardView_suppressKeyPreviewAfterBatchInputDuration, 0);
+ }
+ }
+
// Parameters for pointer handling.
- private static LatinKeyboardView.PointerTrackerParams sParams;
- private static int sTouchNoiseThresholdDistanceSquared;
+ private static PointerTrackerParams sParams;
+ private static GestureStrokeParams sGestureStrokeParams;
private static boolean sNeedsPhantomSuddenMoveEventHack;
+ // Move this threshold to resource.
+ // TODO: Device specific parameter would be better for device specific hack?
+ private static final float PHANTOM_SUDDEN_MOVE_THRESHOLD = 0.25f; // in keyWidth
+ // This hack might be device specific.
+ private static final boolean sNeedsProximateBogusDownMoveUpEventHack = true;
- private static final ArrayList<PointerTracker> sTrackers = new ArrayList<PointerTracker>();
+ private static final ArrayList<PointerTracker> sTrackers = CollectionUtils.newArrayList();
private static PointerTrackerQueue sPointerTrackerQueue;
public final int mPointerId;
@@ -122,8 +166,126 @@ public class PointerTracker {
private KeyboardActionListener mListener = EMPTY_LISTENER;
private Keyboard mKeyboard;
- private int mKeyQuarterWidthSquared;
- private final TextView mKeyPreviewText;
+ private int mPhantonSuddenMoveThreshold;
+ private final BogusMoveEventDetector mBogusMoveEventDetector = new BogusMoveEventDetector();
+
+ private boolean mIsDetectingGesture = false; // per PointerTracker.
+ private static boolean sInGesture = false;
+ private static long sGestureFirstDownTime;
+ private static TimeRecorder sTimeRecorder;
+ private static final InputPointers sAggregratedPointers = new InputPointers(
+ GestureStroke.DEFAULT_CAPACITY);
+ private static int sLastRecognitionPointSize = 0; // synchronized using sAggregratedPointers
+ private static long sLastRecognitionTime = 0; // synchronized using sAggregratedPointers
+
+ static final class BogusMoveEventDetector {
+ // Move these thresholds to resource.
+ // These thresholds' unit is a diagonal length of a key.
+ private static final float BOGUS_MOVE_ACCUMULATED_DISTANCE_THRESHOLD = 0.53f;
+ private static final float BOGUS_MOVE_RADIUS_THRESHOLD = 1.14f;
+
+ private int mAccumulatedDistanceThreshold;
+ private int mRadiusThreshold;
+
+ // Accumulated distance from actual and artificial down keys.
+ /* package */ int mAccumulatedDistanceFromDownKey;
+ private int mActualDownX;
+ private int mActualDownY;
+
+ public void setKeyboardGeometry(final int keyWidth, final int keyHeight) {
+ final float keyDiagonal = (float)Math.hypot(keyWidth, keyHeight);
+ mAccumulatedDistanceThreshold = (int)(
+ keyDiagonal * BOGUS_MOVE_ACCUMULATED_DISTANCE_THRESHOLD);
+ mRadiusThreshold = (int)(keyDiagonal * BOGUS_MOVE_RADIUS_THRESHOLD);
+ }
+
+ public void onActualDownEvent(final int x, final int y) {
+ mActualDownX = x;
+ mActualDownY = y;
+ }
+
+ public void onDownKey() {
+ mAccumulatedDistanceFromDownKey = 0;
+ }
+
+ public void onMoveKey(final int distance) {
+ mAccumulatedDistanceFromDownKey += distance;
+ }
+
+ public boolean hasTraveledLongDistance(final int x, final int y) {
+ final int dx = Math.abs(x - mActualDownX);
+ final int dy = Math.abs(y - mActualDownY);
+ // A bogus move event should be a horizontal movement. A vertical movement might be
+ // a sloppy typing and should be ignored.
+ return dx >= dy && mAccumulatedDistanceFromDownKey >= mAccumulatedDistanceThreshold;
+ }
+
+ /* package */ int getDistanceFromDownEvent(final int x, final int y) {
+ return getDistance(x, y, mActualDownX, mActualDownY);
+ }
+
+ public boolean isCloseToActualDownEvent(final int x, final int y) {
+ return getDistanceFromDownEvent(x, y) < mRadiusThreshold;
+ }
+ }
+
+ static final class TimeRecorder {
+ private final int mSuppressKeyPreviewAfterBatchInputDuration;
+ private final int mStaticTimeThresholdAfterFastTyping; // msec
+ private long mLastTypingTime;
+ private long mLastLetterTypingTime;
+ private long mLastBatchInputTime;
+
+ public TimeRecorder(final PointerTrackerParams pointerTrackerParams,
+ final GestureStrokeParams gestureStrokeParams) {
+ mSuppressKeyPreviewAfterBatchInputDuration =
+ pointerTrackerParams.mSuppressKeyPreviewAfterBatchInputDuration;
+ mStaticTimeThresholdAfterFastTyping =
+ gestureStrokeParams.mStaticTimeThresholdAfterFastTyping;
+ }
+
+ public boolean isInFastTyping(final long eventTime) {
+ final long elapsedTimeSinceLastLetterTyping = eventTime - mLastLetterTypingTime;
+ return elapsedTimeSinceLastLetterTyping < mStaticTimeThresholdAfterFastTyping;
+ }
+
+ private boolean wasLastInputTyping() {
+ return mLastTypingTime >= mLastBatchInputTime;
+ }
+
+ public void onCodeInput(final int code, final long eventTime) {
+ // Record the letter typing time when
+ // 1. Letter keys are typed successively without any batch input in between.
+ // 2. A letter key is typed within the threshold time since the last any key typing.
+ // 3. A non-letter key is typed within the threshold time since the last letter key
+ // typing.
+ if (Character.isLetter(code)) {
+ if (wasLastInputTyping()
+ || eventTime - mLastTypingTime < mStaticTimeThresholdAfterFastTyping) {
+ mLastLetterTypingTime = eventTime;
+ }
+ } else {
+ if (eventTime - mLastLetterTypingTime < mStaticTimeThresholdAfterFastTyping) {
+ // This non-letter typing should be treated as a part of fast typing.
+ mLastLetterTypingTime = eventTime;
+ }
+ }
+ mLastTypingTime = eventTime;
+ }
+
+ public void onEndBatchInput(final long eventTime) {
+ mLastBatchInputTime = eventTime;
+ }
+
+ public long getLastLetterTypingTime() {
+ return mLastLetterTypingTime;
+ }
+
+ public boolean needsToSuppressKeyPreviewPopup(final long eventTime) {
+ return !wasLastInputTyping()
+ && eventTime - mLastBatchInputTime < mSuppressKeyPreviewAfterBatchInputDuration;
+ }
+ }
// The position and time at which first down event occurred.
private long mDownTime;
@@ -148,22 +310,21 @@ public class PointerTracker {
// true if this pointer has been long-pressed and is showing a more keys panel.
private boolean mIsShowingMoreKeysPanel;
- // true if this pointer is repeatable key
- private boolean mIsRepeatableKey;
-
- // true if this pointer is in sliding key input
+ // true if this pointer is in a sliding key input.
boolean mIsInSlidingKeyInput;
+ // true if this pointer is in a sliding key input from a modifier key,
+ // so that further modifier keys should be ignored.
+ boolean mIsInSlidingKeyInputFromModifier;
- // true if sliding key is allowed.
+ // true if a sliding key input is allowed.
private boolean mIsAllowedSlidingKeyInput;
- // ignore modifier key if true
- private boolean mIgnoreModifierKey;
-
// Empty {@link KeyboardActionListener}
private static final KeyboardActionListener EMPTY_LISTENER =
new KeyboardActionListener.Adapter();
+ private final GestureStrokeWithPreviewPoints mGestureStrokeWithPreviewPoints;
+
public static void init(boolean hasDistinctMultitouch,
boolean needsPhantomSuddenMoveEventHack) {
if (hasDistinctMultitouch) {
@@ -172,17 +333,36 @@ public class PointerTracker {
sPointerTrackerQueue = null;
}
sNeedsPhantomSuddenMoveEventHack = needsPhantomSuddenMoveEventHack;
+ sParams = PointerTrackerParams.DEFAULT;
+ sGestureStrokeParams = GestureStrokeParams.DEFAULT;
+ sTimeRecorder = new TimeRecorder(sParams, sGestureStrokeParams);
+ }
+
+ public static void setParameters(final TypedArray mainKeyboardViewAttr) {
+ sParams = new PointerTrackerParams(mainKeyboardViewAttr);
+ sGestureStrokeParams = new GestureStrokeParams(mainKeyboardViewAttr);
+ sTimeRecorder = new TimeRecorder(sParams, sGestureStrokeParams);
+ }
+
+ private static void updateGestureHandlingMode() {
+ sShouldHandleGesture = sMainDictionaryAvailable
+ && sGestureHandlingEnabledByInputField
+ && sGestureHandlingEnabledByUser
+ && !AccessibilityUtils.getInstance().isTouchExplorationEnabled();
+ }
- setParameters(LatinKeyboardView.PointerTrackerParams.DEFAULT);
+ // Note that this method is called from a non-UI thread.
+ public static void setMainDictionaryAvailability(final boolean mainDictionaryAvailable) {
+ sMainDictionaryAvailable = mainDictionaryAvailable;
+ updateGestureHandlingMode();
}
- public static void setParameters(LatinKeyboardView.PointerTrackerParams params) {
- sParams = params;
- sTouchNoiseThresholdDistanceSquared = (int)(
- params.mTouchNoiseThresholdDistance * params.mTouchNoiseThresholdDistance);
+ public static void setGestureHandlingEnabledByUser(final boolean gestureHandlingEnabledByUser) {
+ sGestureHandlingEnabledByUser = gestureHandlingEnabledByUser;
+ updateGestureHandlingMode();
}
- public static PointerTracker getPointerTracker(final int id, KeyEventHandler handler) {
+ public static PointerTracker getPointerTracker(final int id, final KeyEventHandler handler) {
final ArrayList<PointerTracker> trackers = sTrackers;
// Create pointer trackers until we can get 'id+1'-th tracker, if needed.
@@ -198,53 +378,59 @@ public class PointerTracker {
return sPointerTrackerQueue != null ? sPointerTrackerQueue.isAnyInSlidingKeyInput() : false;
}
- public static void setKeyboardActionListener(KeyboardActionListener listener) {
- for (final PointerTracker tracker : sTrackers) {
+ public static void setKeyboardActionListener(final KeyboardActionListener listener) {
+ final int trackersSize = sTrackers.size();
+ for (int i = 0; i < trackersSize; ++i) {
+ final PointerTracker tracker = sTrackers.get(i);
tracker.mListener = listener;
}
}
- public static void setKeyDetector(KeyDetector keyDetector) {
- for (final PointerTracker tracker : sTrackers) {
+ public static void setKeyDetector(final KeyDetector keyDetector) {
+ final int trackersSize = sTrackers.size();
+ for (int i = 0; i < trackersSize; ++i) {
+ final PointerTracker tracker = sTrackers.get(i);
tracker.setKeyDetectorInner(keyDetector);
// Mark that keyboard layout has been changed.
tracker.mKeyboardLayoutHasBeenChanged = true;
}
+ final Keyboard keyboard = keyDetector.getKeyboard();
+ sGestureHandlingEnabledByInputField = !keyboard.mId.passwordInput();
+ updateGestureHandlingMode();
}
- public static void dismissAllKeyPreviews() {
- for (final PointerTracker tracker : sTrackers) {
- tracker.getKeyPreviewText().setVisibility(View.INVISIBLE);
+ public static void setReleasedKeyGraphicsToAllKeys() {
+ final int trackersSize = sTrackers.size();
+ for (int i = 0; i < trackersSize; ++i) {
+ final PointerTracker tracker = sTrackers.get(i);
tracker.setReleasedKeyGraphics(tracker.mCurrentKey);
}
}
- public PointerTracker(int id, KeyEventHandler handler) {
- if (handler == null)
+ private PointerTracker(final int id, final KeyEventHandler handler) {
+ if (handler == null) {
throw new NullPointerException();
+ }
mPointerId = id;
+ mGestureStrokeWithPreviewPoints = new GestureStrokeWithPreviewPoints(
+ id, sGestureStrokeParams);
setKeyDetectorInner(handler.getKeyDetector());
mListener = handler.getKeyboardActionListener();
mDrawingProxy = handler.getDrawingProxy();
mTimerProxy = handler.getTimerProxy();
- mKeyPreviewText = mDrawingProxy.inflateKeyPreviewText();
- }
-
- public TextView getKeyPreviewText() {
- return mKeyPreviewText;
}
// Returns true if keyboard has been changed by this callback.
- private boolean callListenerOnPressAndCheckKeyboardLayoutChange(Key key) {
- final boolean ignoreModifierKey = mIgnoreModifierKey && key.isModifier();
- if (DEBUG_LISTENER) {
- Log.d(TAG, "onPress : " + KeyDetector.printableCode(key)
- + " ignoreModifier=" + ignoreModifierKey
- + " enabled=" + key.isEnabled());
+ private boolean callListenerOnPressAndCheckKeyboardLayoutChange(final Key key) {
+ if (sInGesture) {
+ return false;
}
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.pointerTracker_callListenerOnPressAndCheckKeyboardLayoutChange(key,
- ignoreModifierKey);
+ final boolean ignoreModifierKey = mIsInSlidingKeyInputFromModifier && key.isModifier();
+ if (DEBUG_LISTENER) {
+ Log.d(TAG, String.format("[%d] onPress : %s%s%s", mPointerId,
+ KeyDetector.printableCode(key),
+ ignoreModifierKey ? " ignoreModifier" : "",
+ key.isEnabled() ? "" : " disabled"));
}
if (ignoreModifierKey) {
return false;
@@ -253,9 +439,7 @@ public class PointerTracker {
mListener.onPressKey(key.mCode);
final boolean keyboardLayoutHasBeenChanged = mKeyboardLayoutHasBeenChanged;
mKeyboardLayoutHasBeenChanged = false;
- if (!key.altCodeWhileTyping() && !key.isModifier()) {
- mTimerProxy.startTypingStateTimer();
- }
+ mTimerProxy.startTypingStateTimer(key);
return keyboardLayoutHasBeenChanged;
}
return false;
@@ -263,15 +447,17 @@ public class PointerTracker {
// Note that we need primaryCode argument because the keyboard may in shifted state and the
// primaryCode is different from {@link Key#mCode}.
- private void callListenerOnCodeInput(Key key, int primaryCode, int x, int y) {
- final boolean ignoreModifierKey = mIgnoreModifierKey && key.isModifier();
+ private void callListenerOnCodeInput(final Key key, final int primaryCode, final int x,
+ final int y, final long eventTime) {
+ final boolean ignoreModifierKey = mIsInSlidingKeyInputFromModifier && key.isModifier();
final boolean altersCode = key.altCodeWhileTyping() && mTimerProxy.isTypingState();
- final int code = altersCode ? key.mAltCode : primaryCode;
+ final int code = altersCode ? key.getAltCode() : primaryCode;
if (DEBUG_LISTENER) {
- Log.d(TAG, "onCodeInput: " + Keyboard.printableCode(code) + " text=" + key.mOutputText
- + " x=" + x + " y=" + y
- + " ignoreModifier=" + ignoreModifierKey + " altersCode=" + altersCode
- + " enabled=" + key.isEnabled());
+ final String output = code == Keyboard.CODE_OUTPUT_TEXT
+ ? key.getOutputText() : Keyboard.printableCode(code);
+ Log.d(TAG, String.format("[%d] onCodeInput: %4d %4d %s%s%s", mPointerId, x, y,
+ output, ignoreModifierKey ? " ignoreModifier" : "",
+ altersCode ? " altersCode" : "", key.isEnabled() ? "" : " disabled"));
}
if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.pointerTracker_callListenerOnCodeInput(key, x, y, ignoreModifierKey,
@@ -282,22 +468,28 @@ public class PointerTracker {
}
// Even if the key is disabled, it should respond if it is in the altCodeWhileTyping state.
if (key.isEnabled() || altersCode) {
+ sTimeRecorder.onCodeInput(code, eventTime);
if (code == Keyboard.CODE_OUTPUT_TEXT) {
- mListener.onTextInput(key.mOutputText);
+ mListener.onTextInput(key.getOutputText());
} else if (code != Keyboard.CODE_UNSPECIFIED) {
mListener.onCodeInput(code, x, y);
}
}
}
- // Note that we need primaryCode argument because the keyboard may in shifted state and the
+ // Note that we need primaryCode argument because the keyboard may be in shifted state and the
// primaryCode is different from {@link Key#mCode}.
- private void callListenerOnRelease(Key key, int primaryCode, boolean withSliding) {
- final boolean ignoreModifierKey = mIgnoreModifierKey && key.isModifier();
+ private void callListenerOnRelease(final Key key, final int primaryCode,
+ final boolean withSliding) {
+ if (sInGesture) {
+ return;
+ }
+ final boolean ignoreModifierKey = mIsInSlidingKeyInputFromModifier && key.isModifier();
if (DEBUG_LISTENER) {
- Log.d(TAG, "onRelease : " + Keyboard.printableCode(primaryCode)
- + " sliding=" + withSliding + " ignoreModifier=" + ignoreModifierKey
- + " enabled="+ key.isEnabled());
+ Log.d(TAG, String.format("[%d] onRelease : %s%s%s%s", mPointerId,
+ Keyboard.printableCode(primaryCode),
+ withSliding ? " sliding" : "", ignoreModifierKey ? " ignoreModifier" : "",
+ key.isEnabled() ? "": " disabled"));
}
if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.pointerTracker_callListenerOnRelease(key, primaryCode, withSliding,
@@ -312,21 +504,37 @@ public class PointerTracker {
}
private void callListenerOnCancelInput() {
- if (DEBUG_LISTENER)
- Log.d(TAG, "onCancelInput");
+ if (DEBUG_LISTENER) {
+ Log.d(TAG, String.format("[%d] onCancelInput", mPointerId));
+ }
if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.pointerTracker_callListenerOnCancelInput();
}
mListener.onCancelInput();
}
- private void setKeyDetectorInner(KeyDetector keyDetector) {
+ private void setKeyDetectorInner(final KeyDetector keyDetector) {
+ final Keyboard keyboard = keyDetector.getKeyboard();
+ if (keyDetector == mKeyDetector && keyboard == mKeyboard) {
+ return;
+ }
mKeyDetector = keyDetector;
mKeyboard = keyDetector.getKeyboard();
- final int keyQuarterWidth = mKeyboard.mMostCommonKeyWidth / 4;
- mKeyQuarterWidthSquared = keyQuarterWidth * keyQuarterWidth;
+ final int keyWidth = mKeyboard.mMostCommonKeyWidth;
+ final int keyHeight = mKeyboard.mMostCommonKeyHeight;
+ mGestureStrokeWithPreviewPoints.setKeyboardGeometry(keyWidth);
+ final Key newKey = mKeyDetector.detectHitKey(mKeyX, mKeyY);
+ if (newKey != mCurrentKey) {
+ if (mDrawingProxy != null) {
+ setReleasedKeyGraphics(mCurrentKey);
+ }
+ // Keep {@link #mCurrentKey} that comes from previous keyboard.
+ }
+ mPhantonSuddenMoveThreshold = (int)(keyWidth * PHANTOM_SUDDEN_MOVE_THRESHOLD);
+ mBogusMoveEventDetector.setKeyboardGeometry(keyWidth, keyHeight);
}
+ @Override
public boolean isInSlidingKeyInput() {
return mIsInSlidingKeyInput;
}
@@ -335,15 +543,16 @@ public class PointerTracker {
return mCurrentKey;
}
+ @Override
public boolean isModifier() {
return mCurrentKey != null && mCurrentKey.isModifier();
}
- public Key getKeyOn(int x, int y) {
+ public Key getKeyOn(final int x, final int y) {
return mKeyDetector.detectHitKey(x, y);
}
- private void setReleasedKeyGraphics(Key key) {
+ private void setReleasedKeyGraphics(final Key key) {
mDrawingProxy.dismissKeyPreview(this);
if (key == null) {
return;
@@ -361,20 +570,25 @@ public class PointerTracker {
}
if (key.altCodeWhileTyping()) {
- final int altCode = key.mAltCode;
+ final int altCode = key.getAltCode();
final Key altKey = mKeyboard.getKey(altCode);
if (altKey != null) {
updateReleaseKeyGraphics(altKey);
}
for (final Key k : mKeyboard.mAltCodeKeysWhileTyping) {
- if (k != key && k.mAltCode == altCode) {
+ if (k != key && k.getAltCode() == altCode) {
updateReleaseKeyGraphics(k);
}
}
}
}
- private void setPressedKeyGraphics(Key key) {
+ private static boolean needsToSuppressKeyPreviewPopup(final long eventTime) {
+ if (!sShouldHandleGesture) return false;
+ return sTimeRecorder.needsToSuppressKeyPreviewPopup(eventTime);
+ }
+
+ private void setPressedKeyGraphics(final Key key, final long eventTime) {
if (key == null) {
return;
}
@@ -386,7 +600,7 @@ public class PointerTracker {
return;
}
- if (!key.noKeyPreview()) {
+ if (!key.noKeyPreview() && !sInGesture && !needsToSuppressKeyPreviewPopup(eventTime)) {
mDrawingProxy.showKeyPreview(this);
}
updatePressKeyGraphics(key);
@@ -400,29 +614,33 @@ public class PointerTracker {
}
if (key.altCodeWhileTyping() && mTimerProxy.isTypingState()) {
- final int altCode = key.mAltCode;
+ final int altCode = key.getAltCode();
final Key altKey = mKeyboard.getKey(altCode);
if (altKey != null) {
updatePressKeyGraphics(altKey);
}
for (final Key k : mKeyboard.mAltCodeKeysWhileTyping) {
- if (k != key && k.mAltCode == altCode) {
+ if (k != key && k.getAltCode() == altCode) {
updatePressKeyGraphics(k);
}
}
}
}
- private void updateReleaseKeyGraphics(Key key) {
+ private void updateReleaseKeyGraphics(final Key key) {
key.onReleased();
mDrawingProxy.invalidateKey(key);
}
- private void updatePressKeyGraphics(Key key) {
+ private void updatePressKeyGraphics(final Key key) {
key.onPressed();
mDrawingProxy.invalidateKey(key);
}
+ public GestureStrokeWithPreviewPoints getGestureStrokeWithPreviewPoints() {
+ return mGestureStrokeWithPreviewPoints;
+ }
+
public int getLastX() {
return mLastX;
}
@@ -435,30 +653,100 @@ public class PointerTracker {
return mDownTime;
}
- private Key onDownKey(int x, int y, long eventTime) {
+ private Key onDownKey(final int x, final int y, final long eventTime) {
mDownTime = eventTime;
+ mBogusMoveEventDetector.onDownKey();
return onMoveToNewKey(onMoveKeyInternal(x, y), x, y);
}
- private Key onMoveKeyInternal(int x, int y) {
+ static int getDistance(final int x1, final int y1, final int x2, final int y2) {
+ return (int)Math.hypot(x1 - x2, y1 - y2);
+ }
+
+ private Key onMoveKeyInternal(final int x, final int y) {
+ mBogusMoveEventDetector.onMoveKey(getDistance(x, y, mLastX, mLastY));
mLastX = x;
mLastY = y;
return mKeyDetector.detectHitKey(x, y);
}
- private Key onMoveKey(int x, int y) {
+ private Key onMoveKey(final int x, final int y) {
return onMoveKeyInternal(x, y);
}
- private Key onMoveToNewKey(Key newKey, int x, int y) {
+ private Key onMoveToNewKey(final Key newKey, final int x, final int y) {
mCurrentKey = newKey;
mKeyX = x;
mKeyY = y;
return newKey;
}
- public void processMotionEvent(int action, int x, int y, long eventTime,
- KeyEventHandler handler) {
+ private static int getActivePointerTrackerCount() {
+ return (sPointerTrackerQueue == null) ? 1 : sPointerTrackerQueue.size();
+ }
+
+ private void mayStartBatchInput(final Key key) {
+ if (sInGesture || !mGestureStrokeWithPreviewPoints.isStartOfAGesture()) {
+ return;
+ }
+ if (key == null || !Character.isLetter(key.mCode)) {
+ return;
+ }
+ if (DEBUG_LISTENER) {
+ Log.d(TAG, String.format("[%d] onStartBatchInput", mPointerId));
+ }
+ sInGesture = true;
+ synchronized (sAggregratedPointers) {
+ sAggregratedPointers.reset();
+ sLastRecognitionPointSize = 0;
+ sLastRecognitionTime = 0;
+ mListener.onStartBatchInput();
+ }
+ final boolean isOldestTracker = sPointerTrackerQueue.getOldestElement() == this;
+ mDrawingProxy.showGesturePreviewTrail(this, isOldestTracker);
+ }
+
+ private void mayUpdateBatchInput(final long eventTime, final Key key) {
+ if (key != null) {
+ synchronized (sAggregratedPointers) {
+ final GestureStroke stroke = mGestureStrokeWithPreviewPoints;
+ stroke.appendIncrementalBatchPoints(sAggregratedPointers);
+ final int size = sAggregratedPointers.getPointerSize();
+ if (size > sLastRecognitionPointSize
+ && stroke.hasRecognitionTimePast(eventTime, sLastRecognitionTime)) {
+ sLastRecognitionPointSize = size;
+ sLastRecognitionTime = eventTime;
+ if (DEBUG_LISTENER) {
+ Log.d(TAG, String.format("[%d] onUpdateBatchInput: batchPoints=%d",
+ mPointerId, size));
+ }
+ mListener.onUpdateBatchInput(sAggregratedPointers);
+ }
+ }
+ }
+ final boolean isOldestTracker = sPointerTrackerQueue.getOldestElement() == this;
+ mDrawingProxy.showGesturePreviewTrail(this, isOldestTracker);
+ }
+
+ private void mayEndBatchInput(final long eventTime) {
+ synchronized (sAggregratedPointers) {
+ mGestureStrokeWithPreviewPoints.appendAllBatchPoints(sAggregratedPointers);
+ if (getActivePointerTrackerCount() == 1) {
+ if (DEBUG_LISTENER) {
+ Log.d(TAG, String.format("[%d] onEndBatchInput : batchPoints=%d",
+ mPointerId, sAggregratedPointers.getPointerSize()));
+ }
+ sInGesture = false;
+ sTimeRecorder.onEndBatchInput(eventTime);
+ mListener.onEndBatchInput(sAggregratedPointers);
+ }
+ }
+ final boolean isOldestTracker = sPointerTrackerQueue.getOldestElement() == this;
+ mDrawingProxy.showGesturePreviewTrail(this, isOldestTracker);
+ }
+
+ public void processMotionEvent(final int action, final int x, final int y, final long eventTime,
+ final KeyEventHandler handler) {
switch (action) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
@@ -469,7 +757,7 @@ public class PointerTracker {
onUpEvent(x, y, eventTime);
break;
case MotionEvent.ACTION_MOVE:
- onMoveEvent(x, y, eventTime);
+ onMoveEvent(x, y, eventTime, null);
break;
case MotionEvent.ACTION_CANCEL:
onCancelEvent(x, y, eventTime);
@@ -477,9 +765,11 @@ public class PointerTracker {
}
}
- public void onDownEvent(int x, int y, long eventTime, KeyEventHandler handler) {
- if (DEBUG_EVENT)
+ public void onDownEvent(final int x, final int y, final long eventTime,
+ final KeyEventHandler handler) {
+ if (DEBUG_EVENT) {
printTouchEvent("onDownEvent:", x, y, eventTime);
+ }
mDrawingProxy = handler.getDrawingProxy();
mTimerProxy = handler.getTimerProxy();
@@ -488,24 +778,24 @@ public class PointerTracker {
// Naive up-to-down noise filter.
final long deltaT = eventTime - mUpTime;
if (deltaT < sParams.mTouchNoiseThresholdTime) {
- final int dx = x - mLastX;
- final int dy = y - mLastY;
- final int distanceSquared = (dx * dx + dy * dy);
- if (distanceSquared < sTouchNoiseThresholdDistanceSquared) {
+ final int distance = getDistance(x, y, mLastX, mLastY);
+ if (distance < sParams.mTouchNoiseThresholdDistance) {
if (DEBUG_MODE)
- Log.w(TAG, "onDownEvent: ignore potential noise: time=" + deltaT
- + " distance=" + distanceSquared);
+ Log.w(TAG, String.format("[%d] onDownEvent:"
+ + " ignore potential noise: time=%d distance=%d",
+ mPointerId, deltaT, distance));
if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.pointerTracker_onDownEvent(deltaT, distanceSquared);
+ ResearchLogger.pointerTracker_onDownEvent(deltaT, distance * distance);
}
mKeyAlreadyProcessed = true;
return;
}
}
+ final Key key = getKeyOn(x, y);
+ mBogusMoveEventDetector.onActualDownEvent(x, y);
final PointerTrackerQueue queue = sPointerTrackerQueue;
if (queue != null) {
- final Key key = getKeyOn(x, y);
if (key != null && key.isModifier()) {
// Before processing a down event of modifier key, all pointers already being
// tracked should be released.
@@ -514,9 +804,22 @@ public class PointerTracker {
queue.add(this);
}
onDownEventInternal(x, y, eventTime);
+ if (!sShouldHandleGesture) {
+ return;
+ }
+ // A gesture should start only from the letter key.
+ mIsDetectingGesture = (mKeyboard != null) && mKeyboard.mId.isAlphabetKeyboard()
+ && !mIsShowingMoreKeysPanel && key != null && Keyboard.isLetterCode(key.mCode);
+ if (mIsDetectingGesture) {
+ if (getActivePointerTrackerCount() == 1) {
+ sGestureFirstDownTime = eventTime;
+ }
+ mGestureStrokeWithPreviewPoints.onDownEvent(x, y, eventTime, sGestureFirstDownTime,
+ sTimeRecorder.getLastLetterTypingTime());
+ }
}
- private void onDownEventInternal(int x, int y, long eventTime) {
+ 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
// from modifier key, or 3) this pointer's KeyDetector always allows sliding input.
@@ -525,9 +828,7 @@ public class PointerTracker {
|| mKeyDetector.alwaysAllowsSlidingInput();
mKeyboardLayoutHasBeenChanged = false;
mKeyAlreadyProcessed = false;
- mIsRepeatableKey = false;
- mIsInSlidingKeyInput = false;
- mIgnoreModifierKey = false;
+ resetSlidingKeyInput();
if (key != null) {
// This onPress call may have changed keyboard layout. Those cases are detected at
// {@link #setKeyboard}. In those cases, we should update key according to the new
@@ -538,27 +839,75 @@ public class PointerTracker {
startRepeatKey(key);
startLongPressTimer(key);
- setPressedKeyGraphics(key);
+ setPressedKeyGraphics(key, eventTime);
}
}
- private void startSlidingKeyInput(Key key) {
+ private void startSlidingKeyInput(final Key key) {
if (!mIsInSlidingKeyInput) {
- mIgnoreModifierKey = key.isModifier();
+ mIsInSlidingKeyInputFromModifier = key.isModifier();
}
mIsInSlidingKeyInput = true;
}
- public void onMoveEvent(int x, int y, long eventTime) {
- if (DEBUG_MOVE_EVENT)
+ private void resetSlidingKeyInput() {
+ mIsInSlidingKeyInput = false;
+ mIsInSlidingKeyInputFromModifier = false;
+ }
+
+ private void onGestureMoveEvent(final int x, final int y, final long eventTime,
+ final boolean isMajorEvent, final Key key) {
+ final int gestureTime = (int)(eventTime - sGestureFirstDownTime);
+ if (mIsDetectingGesture) {
+ mGestureStrokeWithPreviewPoints.addPoint(x, y, gestureTime, isMajorEvent);
+ mayStartBatchInput(key);
+ if (sInGesture) {
+ mayUpdateBatchInput(eventTime, key);
+ }
+ }
+ }
+
+ public void onMoveEvent(final int x, final int y, final long eventTime, final MotionEvent me) {
+ if (DEBUG_MOVE_EVENT) {
printTouchEvent("onMoveEvent:", x, y, eventTime);
- if (mKeyAlreadyProcessed)
+ }
+ if (mKeyAlreadyProcessed) {
return;
+ }
+
+ if (sShouldHandleGesture && me != null) {
+ // Add historical points to gesture path.
+ final int pointerIndex = me.findPointerIndex(mPointerId);
+ final int historicalSize = me.getHistorySize();
+ for (int h = 0; h < historicalSize; h++) {
+ final int historicalX = (int)me.getHistoricalX(pointerIndex, h);
+ final int historicalY = (int)me.getHistoricalY(pointerIndex, h);
+ final long historicalTime = me.getHistoricalEventTime(h);
+ onGestureMoveEvent(historicalX, historicalY, historicalTime,
+ false /* isMajorEvent */, null);
+ }
+ }
+ onMoveEventInternal(x, y, eventTime);
+ }
+
+ private void onMoveEventInternal(final int x, final int y, final long eventTime) {
final int lastX = mLastX;
final int lastY = mLastY;
final Key oldKey = mCurrentKey;
Key key = onMoveKey(x, y);
+
+ if (sShouldHandleGesture) {
+ // Register move event on gesture tracker.
+ onGestureMoveEvent(x, y, eventTime, true /* isMajorEvent */, key);
+ if (sInGesture) {
+ mTimerProxy.cancelLongPressTimer();
+ mCurrentKey = null;
+ setReleasedKeyGraphics(oldKey);
+ return;
+ }
+ }
+
if (key != null) {
if (oldKey == null) {
// The pointer has been slid in to the new key, but the finger was not on any keys.
@@ -571,8 +920,8 @@ public class PointerTracker {
}
onMoveToNewKey(key, x, y);
startLongPressTimer(key);
- setPressedKeyGraphics(key);
- } else if (isMajorEnoughMoveToBeOnNewKey(x, y, key)) {
+ setPressedKeyGraphics(key, eventTime);
+ } else if (isMajorEnoughMoveToBeOnNewKey(x, y, eventTime, key)) {
// The pointer has been slid in to the new key from the previous key, we must call
// onRelease() first to notify that the previous key has been released, then call
// onPress() to notify that the new key is being pressed.
@@ -590,34 +939,75 @@ public class PointerTracker {
}
onMoveToNewKey(key, x, y);
startLongPressTimer(key);
- setPressedKeyGraphics(key);
+ setPressedKeyGraphics(key, eventTime);
} else {
- // HACK: On some devices, quick successive touches may be translated to sudden
- // move by touch panel firmware. This hack detects the case and translates the
+ // HACK: On some devices, quick successive touches may be reported as a sudden
+ // move by touch panel firmware. This hack detects such cases and translates the
// move event to successive up and down events.
- final int dx = x - lastX;
- final int dy = y - lastY;
- final int lastMoveSquared = dx * dx + dy * dy;
+ // TODO: Should find a way to balance gesture detection and this hack.
if (sNeedsPhantomSuddenMoveEventHack
- && lastMoveSquared >= mKeyQuarterWidthSquared) {
+ && getDistance(x, y, lastX, lastY) >= mPhantonSuddenMoveThreshold) {
if (DEBUG_MODE) {
- Log.w(TAG, String.format("onMoveEvent:"
- + " phantom sudden move event is translated to "
- + "up[%d,%d]/down[%d,%d] events", lastX, lastY, x, y));
+ Log.w(TAG, String.format("[%d] onMoveEvent:"
+ + " phantom sudden move event (distance=%d) is translated to "
+ + "up[%d,%d,%s]/down[%d,%d,%s] events", mPointerId,
+ getDistance(x, y, lastX, lastY),
+ lastX, lastY, Keyboard.printableCode(oldKey.mCode),
+ x, y, Keyboard.printableCode(key.mCode)));
}
+ // TODO: This should be moved to outside of this nested if-clause?
if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.pointerTracker_onMoveEvent(x, y, lastX, lastY);
}
- onUpEventInternal();
+ onUpEventInternal(eventTime);
+ onDownEventInternal(x, y, eventTime);
+ }
+ // HACK: On some devices, quick successive proximate touches may be reported as
+ // a bogus down-move-up event by touch panel firmware. This hack detects such
+ // cases and breaks these events into separate up and down events.
+ else if (sNeedsProximateBogusDownMoveUpEventHack
+ && sTimeRecorder.isInFastTyping(eventTime)
+ && mBogusMoveEventDetector.isCloseToActualDownEvent(x, y)) {
+ if (DEBUG_MODE) {
+ final float keyDiagonal = (float)Math.hypot(
+ mKeyboard.mMostCommonKeyWidth, mKeyboard.mMostCommonKeyHeight);
+ final float radiusRatio =
+ mBogusMoveEventDetector.getDistanceFromDownEvent(x, y)
+ / keyDiagonal;
+ Log.w(TAG, String.format("[%d] onMoveEvent:"
+ + " bogus down-move-up event (raidus=%.2f key diagonal) is "
+ + " translated to up[%d,%d,%s]/down[%d,%d,%s] events",
+ mPointerId, radiusRatio,
+ lastX, lastY, Keyboard.printableCode(oldKey.mCode),
+ x, y, Keyboard.printableCode(key.mCode)));
+ }
+ onUpEventInternal(eventTime);
onDownEventInternal(x, y, eventTime);
} else {
- mKeyAlreadyProcessed = true;
+ // HACK: If there are currently multiple touches, register the key even if
+ // the finger slides off the key. This defends against noise from some
+ // touch panels when there are close multiple touches.
+ // Caveat: When in chording input mode with a modifier key, we don't use
+ // this hack.
+ if (getActivePointerTrackerCount() > 1 && sPointerTrackerQueue != null
+ && !sPointerTrackerQueue.hasModifierKeyOlderThan(this)) {
+ if (DEBUG_MODE) {
+ Log.w(TAG, String.format("[%d] onMoveEvent:"
+ + " detected sliding finger while multi touching",
+ mPointerId));
+ }
+ onUpEvent(x, y, eventTime);
+ mKeyAlreadyProcessed = true;
+ }
+ if (!mIsDetectingGesture) {
+ mKeyAlreadyProcessed = true;
+ }
setReleasedKeyGraphics(oldKey);
}
}
}
} else {
- if (oldKey != null && isMajorEnoughMoveToBeOnNewKey(x, y, key)) {
+ if (oldKey != null && isMajorEnoughMoveToBeOnNewKey(x, y, eventTime, key)) {
// The pointer has been slid out from the previous key, we must call onRelease() to
// notify that the previous key has been released.
setReleasedKeyGraphics(oldKey);
@@ -627,60 +1017,82 @@ public class PointerTracker {
if (mIsAllowedSlidingKeyInput) {
onMoveToNewKey(key, x, y);
} else {
- mKeyAlreadyProcessed = true;
+ if (!mIsDetectingGesture) {
+ mKeyAlreadyProcessed = true;
+ }
}
}
}
}
- public void onUpEvent(int x, int y, long eventTime) {
- if (DEBUG_EVENT)
+ public void onUpEvent(final int x, final int y, final long eventTime) {
+ if (DEBUG_EVENT) {
printTouchEvent("onUpEvent :", x, y, eventTime);
+ }
final PointerTrackerQueue queue = sPointerTrackerQueue;
if (queue != null) {
- if (mCurrentKey != null && mCurrentKey.isModifier()) {
- // Before processing an up event of modifier key, all pointers already being
- // tracked should be released.
- queue.releaseAllPointersExcept(this, eventTime);
- } else {
- queue.releaseAllPointersOlderThan(this, eventTime);
+ if (!sInGesture) {
+ if (mCurrentKey != null && mCurrentKey.isModifier()) {
+ // Before processing an up event of modifier key, all pointers already being
+ // tracked should be released.
+ queue.releaseAllPointersExcept(this, eventTime);
+ } else {
+ queue.releaseAllPointersOlderThan(this, eventTime);
+ }
}
+ }
+ onUpEventInternal(eventTime);
+ if (queue != null) {
queue.remove(this);
}
- onUpEventInternal();
}
// Let this pointer tracker know that one of newer-than-this pointer trackers got an up event.
// This pointer tracker needs to keep the key top graphics "pressed", but needs to get a
// "virtual" up event.
- public void onPhantomUpEvent(int x, int y, long eventTime) {
- if (DEBUG_EVENT)
- printTouchEvent("onPhntEvent:", x, y, eventTime);
- onUpEventInternal();
+ @Override
+ public void onPhantomUpEvent(final long eventTime) {
+ if (DEBUG_EVENT) {
+ printTouchEvent("onPhntEvent:", getLastX(), getLastY(), eventTime);
+ }
+ onUpEventInternal(eventTime);
mKeyAlreadyProcessed = true;
}
- private void onUpEventInternal() {
+ private void onUpEventInternal(final long eventTime) {
mTimerProxy.cancelKeyTimers();
- mIsInSlidingKeyInput = false;
+ resetSlidingKeyInput();
+ mIsDetectingGesture = false;
+ final Key currentKey = mCurrentKey;
+ mCurrentKey = null;
// Release the last pressed key.
- setReleasedKeyGraphics(mCurrentKey);
+ setReleasedKeyGraphics(currentKey);
if (mIsShowingMoreKeysPanel) {
mDrawingProxy.dismissMoreKeysPanel();
mIsShowingMoreKeysPanel = false;
}
- if (mKeyAlreadyProcessed)
+
+ if (sInGesture) {
+ if (currentKey != null) {
+ callListenerOnRelease(currentKey, currentKey.mCode, true);
+ }
+ mayEndBatchInput(eventTime);
+ return;
+ }
+
+ if (mKeyAlreadyProcessed) {
return;
- if (!mIsRepeatableKey) {
- detectAndSendKey(mCurrentKey, mKeyX, mKeyY);
+ }
+ if (currentKey != null && !currentKey.isRepeatable()) {
+ detectAndSendKey(currentKey, mKeyX, mKeyY, eventTime);
}
}
- public void onShowMoreKeysPanel(int x, int y, KeyEventHandler handler) {
+ public void onShowMoreKeysPanel(final int x, final int y, final KeyEventHandler handler) {
onLongPressed();
- onDownEvent(x, y, SystemClock.uptimeMillis(), handler);
mIsShowingMoreKeysPanel = true;
+ onDownEvent(x, y, SystemClock.uptimeMillis(), handler);
}
public void onLongPressed() {
@@ -692,9 +1104,10 @@ public class PointerTracker {
}
}
- public void onCancelEvent(int x, int y, long eventTime) {
- if (DEBUG_EVENT)
+ public void onCancelEvent(final int x, final int y, final long eventTime) {
+ if (DEBUG_EVENT) {
printTouchEvent("onCancelEvt:", x, y, eventTime);
+ }
final PointerTrackerQueue queue = sPointerTrackerQueue;
if (queue != null) {
@@ -707,71 +1120,91 @@ public class PointerTracker {
private void onCancelEventInternal() {
mTimerProxy.cancelKeyTimers();
setReleasedKeyGraphics(mCurrentKey);
- mIsInSlidingKeyInput = false;
+ resetSlidingKeyInput();
if (mIsShowingMoreKeysPanel) {
mDrawingProxy.dismissMoreKeysPanel();
mIsShowingMoreKeysPanel = false;
}
}
- private void startRepeatKey(Key key) {
- if (key != null && key.isRepeatable()) {
+ private void startRepeatKey(final Key key) {
+ if (key != null && key.isRepeatable() && !sInGesture) {
onRegisterKey(key);
mTimerProxy.startKeyRepeatTimer(this);
- mIsRepeatableKey = true;
- } else {
- mIsRepeatableKey = false;
}
}
- public void onRegisterKey(Key key) {
+ public void onRegisterKey(final Key key) {
if (key != null) {
- detectAndSendKey(key, key.mX, key.mY);
- if (!key.altCodeWhileTyping() && !key.isModifier()) {
- mTimerProxy.startTypingStateTimer();
- }
+ detectAndSendKey(key, key.mX, key.mY, SystemClock.uptimeMillis());
+ mTimerProxy.startTypingStateTimer(key);
}
}
- private boolean isMajorEnoughMoveToBeOnNewKey(int x, int y, Key newKey) {
- if (mKeyDetector == null)
+ private boolean isMajorEnoughMoveToBeOnNewKey(final int x, final int y, final long eventTime,
+ final Key newKey) {
+ if (mKeyDetector == null) {
throw new NullPointerException("keyboard and/or key detector not set");
- Key curKey = mCurrentKey;
+ }
+ final Key curKey = mCurrentKey;
if (newKey == curKey) {
return false;
} else if (curKey != null) {
- return curKey.squaredDistanceToEdge(x, y)
- >= mKeyDetector.getKeyHysteresisDistanceSquared();
- } else {
+ final int keyHysteresisDistanceSquared = mKeyDetector.getKeyHysteresisDistanceSquared(
+ mIsInSlidingKeyInputFromModifier);
+ final int distanceFromKeyEdgeSquared = curKey.squaredDistanceToEdge(x, y);
+ if (distanceFromKeyEdgeSquared >= keyHysteresisDistanceSquared) {
+ if (DEBUG_MODE) {
+ final float distanceToEdgeRatio = (float)Math.sqrt(distanceFromKeyEdgeSquared)
+ / mKeyboard.mMostCommonKeyWidth;
+ Log.d(TAG, String.format("[%d] isMajorEnoughMoveToBeOnNewKey:"
+ +" %.2f key width from key edge",
+ mPointerId, distanceToEdgeRatio));
+ }
+ return true;
+ }
+ if (sNeedsProximateBogusDownMoveUpEventHack && !mIsAllowedSlidingKeyInput
+ && sTimeRecorder.isInFastTyping(eventTime)
+ && mBogusMoveEventDetector.hasTraveledLongDistance(x, y)) {
+ if (DEBUG_MODE) {
+ final float keyDiagonal = (float)Math.hypot(
+ mKeyboard.mMostCommonKeyWidth, mKeyboard.mMostCommonKeyHeight);
+ final float lengthFromDownRatio =
+ mBogusMoveEventDetector.mAccumulatedDistanceFromDownKey / keyDiagonal;
+ Log.d(TAG, String.format("[%d] isMajorEnoughMoveToBeOnNewKey:"
+ + " %.2f key diagonal from virtual down point",
+ mPointerId, lengthFromDownRatio));
+ }
+ return true;
+ }
+ return false;
+ } else { // curKey == null && newKey != null
return true;
}
}
- private void startLongPressTimer(Key key) {
- if (key != null && key.isLongPressEnabled()) {
+ private void startLongPressTimer(final Key key) {
+ if (key != null && key.isLongPressEnabled() && !sInGesture) {
mTimerProxy.startLongPressTimer(this);
}
}
- private void detectAndSendKey(Key key, int x, int y) {
+ private void detectAndSendKey(final Key key, final int x, final int y, final long eventTime) {
if (key == null) {
callListenerOnCancelInput();
return;
}
- int code = key.mCode;
- callListenerOnCodeInput(key, code, x, y);
+ final int code = key.mCode;
+ callListenerOnCodeInput(key, code, x, y, eventTime);
callListenerOnRelease(key, code, false);
}
- private long mPreviousEventTime;
-
- private void printTouchEvent(String title, int x, int y, long eventTime) {
+ private void printTouchEvent(final String title, final int x, final int y,
+ final long eventTime) {
final Key key = mKeyDetector.detectHitKey(x, y);
final String code = KeyDetector.printableCode(key);
- final long delta = eventTime - mPreviousEventTime;
- Log.d(TAG, String.format("%s%s[%d] %4d %4d %5d %s", title,
- (mKeyAlreadyProcessed ? "-" : " "), mPointerId, x, y, delta, code));
- mPreviousEventTime = eventTime;
+ Log.d(TAG, String.format("[%d]%s%s %4d %4d %5d %s", mPointerId,
+ (mKeyAlreadyProcessed ? "-" : " "), title, x, y, eventTime, code));
}
}
diff --git a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
index 1207c3fcd..06a9e9252 100644
--- a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
+++ b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
@@ -18,21 +18,22 @@ package com.android.inputmethod.keyboard;
import android.graphics.Rect;
import android.text.TextUtils;
-import android.util.FloatMath;
-import com.android.inputmethod.keyboard.Keyboard.Params.TouchPositionCorrection;
+import com.android.inputmethod.keyboard.internal.TouchPositionCorrection;
+import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.JniUtils;
import java.util.Arrays;
-import java.util.HashMap;
-public class ProximityInfo {
+public final class ProximityInfo {
+ /** MAX_PROXIMITY_CHARS_SIZE must be the same as MAX_PROXIMITY_CHARS_SIZE_INTERNAL
+ * in defines.h */
public static final int MAX_PROXIMITY_CHARS_SIZE = 16;
/** Number of key widths from current touch point to search for nearest keys. */
private static float SEARCH_DISTANCE = 1.2f;
private static final Key[] EMPTY_KEY_ARRAY = new Key[0];
+ private static final float DEFAULT_TOUCH_POSITION_CORRECTION_RADIUS = 0.15f;
- private final int mKeyHeight;
private final int mGridWidth;
private final int mGridHeight;
private final int mGridSize;
@@ -42,14 +43,15 @@ public class ProximityInfo {
private final int mKeyboardMinWidth;
private final int mKeyboardHeight;
private final int mMostCommonKeyWidth;
+ private final int mMostCommonKeyHeight;
private final Key[] mKeys;
- private final TouchPositionCorrection mTouchPositionCorrection;
private final Key[][] mGridNeighbors;
private final String mLocaleStr;
- ProximityInfo(String localeStr, int gridWidth, int gridHeight, int minWidth, int height,
- int mostCommonKeyWidth, int mostCommonKeyHeight, final Key[] keys,
- TouchPositionCorrection touchPositionCorrection) {
+ ProximityInfo(final String localeStr, final int gridWidth, final int gridHeight,
+ final int minWidth, final int height, final int mostCommonKeyWidth,
+ final int mostCommonKeyHeight, final Key[] keys,
+ final TouchPositionCorrection touchPositionCorrection) {
if (TextUtils.isEmpty(localeStr)) {
mLocaleStr = "";
} else {
@@ -62,38 +64,16 @@ public class ProximityInfo {
mCellHeight = (height + mGridHeight - 1) / mGridHeight;
mKeyboardMinWidth = minWidth;
mKeyboardHeight = height;
- mKeyHeight = mostCommonKeyHeight;
+ mMostCommonKeyHeight = mostCommonKeyHeight;
mMostCommonKeyWidth = mostCommonKeyWidth;
mKeys = keys;
- mTouchPositionCorrection = touchPositionCorrection;
mGridNeighbors = new Key[mGridSize][];
if (minWidth == 0 || height == 0) {
// No proximity required. Keyboard might be more keys keyboard.
return;
}
computeNearestNeighbors();
- mNativeProximityInfo = createNativeProximityInfo();
- }
-
- // TODO: Remove this public constructor when the native part of the ProximityInfo becomes
- // immutable.
- // This public constructor aims only for test purpose.
- public ProximityInfo(ProximityInfo o) {
- mLocaleStr = o.mLocaleStr;
- mGridWidth = o.mGridWidth;
- mGridHeight = o.mGridHeight;
- mGridSize = o.mGridSize;
- mCellWidth = o.mCellWidth;
- mCellHeight = o.mCellHeight;
- mKeyboardMinWidth = o.mKeyboardMinWidth;
- mKeyboardHeight = o.mKeyboardHeight;
- mKeyHeight = o.mKeyHeight;
- mMostCommonKeyWidth = o.mMostCommonKeyWidth;
- mKeys = o.mKeys;
- mTouchPositionCorrection = o.mTouchPositionCorrection;
- mGridNeighbors = new Key[mGridSize][];
- computeNearestNeighbors();
- mNativeProximityInfo = createNativeProximityInfo();
+ mNativeProximityInfo = createNativeProximityInfo(touchPositionCorrection);
}
public static ProximityInfo createDummyProximityInfo() {
@@ -101,7 +81,7 @@ public class ProximityInfo {
}
public static ProximityInfo createSpellCheckerProximityInfo(final int[] proximity,
- int rowSize, int gridWidth, int gridHeight) {
+ final int rowSize, final int gridWidth, final int gridHeight) {
final ProximityInfo spellCheckerProximityInfo = createDummyProximityInfo();
spellCheckerProximityInfo.mNativeProximityInfo =
spellCheckerProximityInfo.setProximityInfoNative("",
@@ -125,14 +105,14 @@ public class ProximityInfo {
private native void releaseProximityInfoNative(long nativeProximityInfo);
- private final long createNativeProximityInfo() {
+ private final long createNativeProximityInfo(
+ final TouchPositionCorrection touchPositionCorrection) {
final Key[][] gridNeighborKeys = mGridNeighbors;
final int keyboardWidth = mKeyboardMinWidth;
final int keyboardHeight = mKeyboardHeight;
final Key[] keys = mKeys;
- final TouchPositionCorrection touchPositionCorrection = mTouchPositionCorrection;
final int[] proximityCharsArray = new int[mGridSize * MAX_PROXIMITY_CHARS_SIZE];
- Arrays.fill(proximityCharsArray, KeyDetector.NOT_A_CODE);
+ Arrays.fill(proximityCharsArray, Constants.NOT_A_CODE);
for (int i = 0; i < mGridSize; ++i) {
final int proximityCharsLength = gridNeighborKeys[i].length;
for (int j = 0; j < proximityCharsLength; ++j) {
@@ -163,20 +143,22 @@ public class ProximityInfo {
sweetSpotCenterXs = new float[keyCount];
sweetSpotCenterYs = new float[keyCount];
sweetSpotRadii = new float[keyCount];
+ final float defaultRadius = DEFAULT_TOUCH_POSITION_CORRECTION_RADIUS
+ * (float)Math.hypot(mMostCommonKeyWidth, mMostCommonKeyHeight);
for (int i = 0; i < keyCount; i++) {
final Key key = keys[i];
final Rect hitBox = key.mHitBox;
- final int row = hitBox.top / mKeyHeight;
- if (row < touchPositionCorrection.mRadii.length) {
+ sweetSpotCenterXs[i] = hitBox.exactCenterX();
+ sweetSpotCenterYs[i] = hitBox.exactCenterY();
+ sweetSpotRadii[i] = defaultRadius;
+ final int row = hitBox.top / mMostCommonKeyHeight;
+ if (row < touchPositionCorrection.getRows()) {
final int hitBoxWidth = hitBox.width();
final int hitBoxHeight = hitBox.height();
- final float x = touchPositionCorrection.mXs[row];
- final float y = touchPositionCorrection.mYs[row];
- final float radius = touchPositionCorrection.mRadii[row];
- sweetSpotCenterXs[i] = hitBox.exactCenterX() + x * hitBoxWidth;
- sweetSpotCenterYs[i] = hitBox.exactCenterY() + y * hitBoxHeight;
- sweetSpotRadii[i] = radius * FloatMath.sqrt(
- hitBoxWidth * hitBoxWidth + hitBoxHeight * hitBoxHeight);
+ final float hitBoxDiagonal = (float)Math.hypot(hitBoxWidth, hitBoxHeight);
+ sweetSpotCenterXs[i] += touchPositionCorrection.getX(row) * hitBoxWidth;
+ sweetSpotCenterYs[i] += touchPositionCorrection.getY(row) * hitBoxHeight;
+ sweetSpotRadii[i] = touchPositionCorrection.getRadius(row) * hitBoxDiagonal;
}
}
} else {
@@ -209,10 +191,6 @@ public class ProximityInfo {
private void computeNearestNeighbors() {
final int defaultWidth = mMostCommonKeyWidth;
final Key[] keys = mKeys;
- final HashMap<Integer, Key> keyCodeMap = new HashMap<Integer, Key>();
- for (final Key key : keys) {
- keyCodeMap.put(key.mCode, key);
- }
final int thresholdBase = (int) (defaultWidth * SEARCH_DISTANCE);
final int threshold = thresholdBase * thresholdBase;
// Round-up so we don't have any pixels outside the grid
@@ -236,7 +214,8 @@ public class ProximityInfo {
}
}
- public void fillArrayWithNearestKeyCodes(int x, int y, int primaryKeyCode, int[] dest) {
+ public void fillArrayWithNearestKeyCodes(final int x, final int y, final int primaryKeyCode,
+ final int[] dest) {
final int destLength = dest.length;
if (destLength < 1) {
return;
@@ -257,11 +236,11 @@ public class ProximityInfo {
dest[index++] = code;
}
if (index < destLength) {
- dest[index] = KeyDetector.NOT_A_CODE;
+ dest[index] = Constants.NOT_A_CODE;
}
}
- public Key[] getNearestKeys(int x, int y) {
+ public Key[] getNearestKeys(final int x, final int y) {
if (mGridNeighbors == null) {
return EMPTY_KEY_ARRAY;
}
diff --git a/java/src/com/android/inputmethod/keyboard/ViewLayoutUtils.java b/java/src/com/android/inputmethod/keyboard/ViewLayoutUtils.java
index ee5047083..dc12fa468 100644
--- a/java/src/com/android/inputmethod/keyboard/ViewLayoutUtils.java
+++ b/java/src/com/android/inputmethod/keyboard/ViewLayoutUtils.java
@@ -22,7 +22,7 @@ import android.view.ViewGroup.MarginLayoutParams;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
-public class ViewLayoutUtils {
+public final class ViewLayoutUtils {
private ViewLayoutUtils() {
// This utility class is not publicly instantiable.
}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/AlphabetShiftState.java b/java/src/com/android/inputmethod/keyboard/internal/AlphabetShiftState.java
index 5712df1fc..44aa72a0a 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/AlphabetShiftState.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/AlphabetShiftState.java
@@ -18,7 +18,7 @@ package com.android.inputmethod.keyboard.internal;
import android.util.Log;
-public class AlphabetShiftState {
+public final class AlphabetShiftState {
private static final String TAG = AlphabetShiftState.class.getSimpleName();
private static final boolean DEBUG = false;
diff --git a/java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java b/java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java
new file mode 100644
index 000000000..699aaeaef
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/internal/GesturePreviewTrail.java
@@ -0,0 +1,297 @@
+/*
+ * 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.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;
+import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.ResizableIntArray;
+
+final class GesturePreviewTrail {
+ 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);
+ private final ResizableIntArray mEventTimes = new ResizableIntArray(DEFAULT_CAPACITY);
+ private int mCurrentStrokeId = -1;
+ // The wall time of the zero value in {@link #mEventTimes}
+ private long mCurrentTimeBase;
+ private int mTrailStartIndex;
+
+ static final class Params {
+ public final int mTrailColor;
+ public final float mTrailStartWidth;
+ public final float mTrailEndWidth;
+ public final int mFadeoutStartDelay;
+ public final int mFadeoutDuration;
+ public final int mUpdateInterval;
+
+ public final int mTrailLingerDuration;
+
+ public Params(final TypedArray keyboardViewAttr) {
+ mTrailColor = keyboardViewAttr.getColor(
+ R.styleable.KeyboardView_gesturePreviewTrailColor, 0);
+ mTrailStartWidth = keyboardViewAttr.getDimension(
+ R.styleable.KeyboardView_gesturePreviewTrailStartWidth, 0.0f);
+ mTrailEndWidth = keyboardViewAttr.getDimension(
+ R.styleable.KeyboardView_gesturePreviewTrailEndWidth, 0.0f);
+ mFadeoutStartDelay = keyboardViewAttr.getInt(
+ R.styleable.KeyboardView_gesturePreviewTrailFadeoutStartDelay, 0);
+ mFadeoutDuration = keyboardViewAttr.getInt(
+ R.styleable.KeyboardView_gesturePreviewTrailFadeoutDuration, 0);
+ mTrailLingerDuration = mFadeoutStartDelay + mFadeoutDuration;
+ mUpdateInterval = keyboardViewAttr.getInt(
+ R.styleable.KeyboardView_gesturePreviewTrailUpdateInterval, 0);
+ }
+ }
+
+ // Use this value as imaginary zero because x-coordinates may be zero.
+ private static final int DOWN_EVENT_MARKER = -128;
+
+ private static int markAsDownEvent(final int xCoord) {
+ return DOWN_EVENT_MARKER - xCoord;
+ }
+
+ private static boolean isDownEventXCoord(final int xCoordOrMark) {
+ return xCoordOrMark <= DOWN_EVENT_MARKER;
+ }
+
+ private static int getXCoordValue(final int xCoordOrMark) {
+ return isDownEventXCoord(xCoordOrMark)
+ ? DOWN_EVENT_MARKER - xCoordOrMark : xCoordOrMark;
+ }
+
+ public void addStroke(final GestureStrokeWithPreviewPoints stroke, final long downTime) {
+ final int trailSize = mEventTimes.getLength();
+ stroke.appendPreviewStroke(mEventTimes, mXCoordinates, mYCoordinates);
+ if (mEventTimes.getLength() == trailSize) {
+ return;
+ }
+ final int[] eventTimes = mEventTimes.getPrimitiveArray();
+ final int strokeId = stroke.getGestureStrokeId();
+ if (strokeId != mCurrentStrokeId) {
+ final int elapsedTime = (int)(downTime - mCurrentTimeBase);
+ for (int i = mTrailStartIndex; i < trailSize; i++) {
+ // Decay the previous strokes' event times.
+ eventTimes[i] -= elapsedTime;
+ }
+ final int[] xCoords = mXCoordinates.getPrimitiveArray();
+ final int downIndex = trailSize;
+ xCoords[downIndex] = markAsDownEvent(xCoords[downIndex]);
+ mCurrentTimeBase = downTime - eventTimes[downIndex];
+ mCurrentStrokeId = strokeId;
+ }
+ }
+
+ private static int getAlpha(final int elapsedTime, final Params params) {
+ if (elapsedTime < params.mFadeoutStartDelay) {
+ return Constants.Color.ALPHA_OPAQUE;
+ }
+ final int decreasingAlpha = Constants.Color.ALPHA_OPAQUE
+ * (elapsedTime - params.mFadeoutStartDelay)
+ / params.mFadeoutDuration;
+ return Constants.Color.ALPHA_OPAQUE - decreasingAlpha;
+ }
+
+ private static float getWidth(final int elapsedTime, final Params params) {
+ return Math.max((params.mTrailLingerDuration - elapsedTime)
+ * (params.mTrailStartWidth - params.mTrailEndWidth)
+ / 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
+ * @param paint The paint object to be used to draw the gesture preview trail
+ * @param outBoundsRect the bounding box of this gesture trail drawing
+ * @param params The drawing parameters of gesture preview trail
+ * @return true if some gesture preview trails remain to be drawn
+ */
+ public boolean drawGestureTrail(final Canvas canvas, final Paint paint,
+ final Rect outBoundsRect, final Params params) {
+ final int trailSize = mEventTimes.getLength();
+ if (trailSize == 0) {
+ return false;
+ }
+
+ final int[] eventTimes = mEventTimes.getPrimitiveArray();
+ final int[] xCoords = mXCoordinates.getPrimitiveArray();
+ final int[] yCoords = mYCoordinates.getPrimitiveArray();
+ final int sinceDown = (int)(SystemClock.uptimeMillis() - mCurrentTimeBase);
+ int startIndex;
+ for (startIndex = mTrailStartIndex; startIndex < trailSize; startIndex++) {
+ final int elapsedTime = sinceDown - eventTimes[startIndex];
+ // Skip too old trail points.
+ if (elapsedTime < params.mTrailLingerDuration) {
+ break;
+ }
+ }
+ mTrailStartIndex = startIndex;
+
+ if (startIndex < trailSize) {
+ paint.setColor(params.mTrailColor);
+ 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((int)w.p1x, (int)w.p1y, (int)w.p1x, (int)w.p1y);
+ for (int i = startIndex + 1; i < trailSize - 1; 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(xCoords[i])) {
+ final int alpha = getAlpha(elapsedTime, params);
+ paint.setAlpha(alpha);
+ final float width = getWidth(elapsedTime, params);
+ 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.
+ maxWidth = Math.max(maxWidth, width);
+ }
+ 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);
+ outBoundsRect.inset(inset, inset);
+ }
+
+ final int newSize = trailSize - startIndex;
+ if (newSize < startIndex) {
+ mTrailStartIndex = 0;
+ if (newSize > 0) {
+ System.arraycopy(eventTimes, startIndex, eventTimes, 0, newSize);
+ System.arraycopy(xCoords, startIndex, xCoords, 0, newSize);
+ System.arraycopy(yCoords, startIndex, yCoords, 0, newSize);
+ }
+ mEventTimes.setLength(newSize);
+ mXCoordinates.setLength(newSize);
+ mYCoordinates.setLength(newSize);
+ }
+ return newSize > 0;
+ }
+}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java
new file mode 100644
index 000000000..f8244dd5b
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/internal/GestureStroke.java
@@ -0,0 +1,341 @@
+/*
+ * 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.keyboard.internal;
+
+import android.content.res.TypedArray;
+import android.util.Log;
+
+import com.android.inputmethod.latin.InputPointers;
+import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.ResizableIntArray;
+import com.android.inputmethod.latin.ResourceUtils;
+
+public class GestureStroke {
+ private static final String TAG = GestureStroke.class.getSimpleName();
+ private static final boolean DEBUG = false;
+ private static final boolean DEBUG_SPEED = false;
+
+ public static final int DEFAULT_CAPACITY = 128;
+
+ private final int mPointerId;
+ private final ResizableIntArray mEventTimes = new ResizableIntArray(DEFAULT_CAPACITY);
+ private final ResizableIntArray mXCoordinates = new ResizableIntArray(DEFAULT_CAPACITY);
+ private final ResizableIntArray mYCoordinates = new ResizableIntArray(DEFAULT_CAPACITY);
+
+ private final GestureStrokeParams mParams;
+
+ private int mKeyWidth; // pixel
+ // Static threshold for starting gesture detection
+ private int mDetectFastMoveSpeedThreshold; // pixel /sec
+ private int mDetectFastMoveTime;
+ private int mDetectFastMoveX;
+ private int mDetectFastMoveY;
+ // Dynamic threshold for gesture after fast typing
+ private boolean mAfterFastTyping;
+ private int mGestureDynamicDistanceThresholdFrom; // pixel
+ private int mGestureDynamicDistanceThresholdTo; // pixel
+ // Variables for gesture sampling
+ private int mGestureSamplingMinimumDistance; // pixel
+ private long mLastMajorEventTime;
+ private int mLastMajorEventX;
+ private int mLastMajorEventY;
+ // Variables for gesture recognition
+ private int mGestureRecognitionSpeedThreshold; // pixel / sec
+ private int mIncrementalRecognitionSize;
+ private int mLastIncrementalBatchSize;
+
+ public static final class GestureStrokeParams {
+ // Static threshold for gesture after fast typing
+ public final int mStaticTimeThresholdAfterFastTyping; // msec
+ // Static threshold for starting gesture detection
+ public final float mDetectFastMoveSpeedThreshold; // keyWidth/sec
+ // Dynamic threshold for gesture after fast typing
+ public final int mDynamicThresholdDecayDuration; // msec
+ // Time based threshold values
+ public final int mDynamicTimeThresholdFrom; // msec
+ public final int mDynamicTimeThresholdTo; // msec
+ // Distance based threshold values
+ public final float mDynamicDistanceThresholdFrom; // keyWidth
+ public final float mDynamicDistanceThresholdTo; // keyWidth
+ // Parameters for gesture sampling
+ public final float mSamplingMinimumDistance; // keyWidth
+ // Parameters for gesture recognition
+ public final int mRecognitionMinimumTime; // msec
+ public final float mRecognitionSpeedThreshold; // keyWidth/sec
+
+ // Default GestureStroke parameters for test.
+ public static final GestureStrokeParams FOR_TEST = new GestureStrokeParams();
+ public static final GestureStrokeParams DEFAULT = FOR_TEST;
+
+ private GestureStrokeParams() {
+ // These parameter values are default and intended for testing.
+ mStaticTimeThresholdAfterFastTyping = 350; // msec
+ mDetectFastMoveSpeedThreshold = 1.5f; // keyWidth / sec
+ mDynamicThresholdDecayDuration = 450; // msec
+ mDynamicTimeThresholdFrom = 300; // msec
+ mDynamicTimeThresholdTo = 20; // msec
+ mDynamicDistanceThresholdFrom = 6.0f; // keyWidth
+ mDynamicDistanceThresholdTo = 0.35f; // keyWidth
+ // The following parameters' change will affect the result of regression test.
+ mSamplingMinimumDistance = 1.0f / 6.0f; // keyWidth
+ mRecognitionMinimumTime = 100; // msec
+ mRecognitionSpeedThreshold = 5.5f; // keyWidth / sec
+ }
+
+ public GestureStrokeParams(final TypedArray mainKeyboardViewAttr) {
+ mStaticTimeThresholdAfterFastTyping = mainKeyboardViewAttr.getInt(
+ R.styleable.MainKeyboardView_gestureStaticTimeThresholdAfterFastTyping,
+ DEFAULT.mStaticTimeThresholdAfterFastTyping);
+ mDetectFastMoveSpeedThreshold = ResourceUtils.getFraction(mainKeyboardViewAttr,
+ R.styleable.MainKeyboardView_gestureDetectFastMoveSpeedThreshold,
+ DEFAULT.mDetectFastMoveSpeedThreshold);
+ mDynamicThresholdDecayDuration = mainKeyboardViewAttr.getInt(
+ R.styleable.MainKeyboardView_gestureDynamicThresholdDecayDuration,
+ DEFAULT.mDynamicThresholdDecayDuration);
+ mDynamicTimeThresholdFrom = mainKeyboardViewAttr.getInt(
+ R.styleable.MainKeyboardView_gestureDynamicTimeThresholdFrom,
+ DEFAULT.mDynamicTimeThresholdFrom);
+ mDynamicTimeThresholdTo = mainKeyboardViewAttr.getInt(
+ R.styleable.MainKeyboardView_gestureDynamicTimeThresholdTo,
+ DEFAULT.mDynamicTimeThresholdTo);
+ mDynamicDistanceThresholdFrom = ResourceUtils.getFraction(mainKeyboardViewAttr,
+ R.styleable.MainKeyboardView_gestureDynamicDistanceThresholdFrom,
+ DEFAULT.mDynamicDistanceThresholdFrom);
+ mDynamicDistanceThresholdTo = ResourceUtils.getFraction(mainKeyboardViewAttr,
+ R.styleable.MainKeyboardView_gestureDynamicDistanceThresholdTo,
+ DEFAULT.mDynamicDistanceThresholdTo);
+ mSamplingMinimumDistance = ResourceUtils.getFraction(mainKeyboardViewAttr,
+ R.styleable.MainKeyboardView_gestureSamplingMinimumDistance,
+ DEFAULT.mSamplingMinimumDistance);
+ mRecognitionMinimumTime = mainKeyboardViewAttr.getInt(
+ R.styleable.MainKeyboardView_gestureRecognitionMinimumTime,
+ DEFAULT.mRecognitionMinimumTime);
+ mRecognitionSpeedThreshold = ResourceUtils.getFraction(mainKeyboardViewAttr,
+ R.styleable.MainKeyboardView_gestureRecognitionSpeedThreshold,
+ DEFAULT.mRecognitionSpeedThreshold);
+ }
+ }
+
+ private static final int MSEC_PER_SEC = 1000;
+
+ public GestureStroke(final int pointerId, final GestureStrokeParams params) {
+ mPointerId = pointerId;
+ mParams = params;
+ }
+
+ public void setKeyboardGeometry(final int keyWidth) {
+ mKeyWidth = keyWidth;
+ // TODO: Find an appropriate base metric for these length. Maybe diagonal length of the key?
+ mDetectFastMoveSpeedThreshold = (int)(keyWidth * mParams.mDetectFastMoveSpeedThreshold);
+ mGestureDynamicDistanceThresholdFrom =
+ (int)(keyWidth * mParams.mDynamicDistanceThresholdFrom);
+ mGestureDynamicDistanceThresholdTo = (int)(keyWidth * mParams.mDynamicDistanceThresholdTo);
+ mGestureSamplingMinimumDistance = (int)(keyWidth * mParams.mSamplingMinimumDistance);
+ mGestureRecognitionSpeedThreshold = (int)(keyWidth * mParams.mRecognitionSpeedThreshold);
+ if (DEBUG) {
+ Log.d(TAG, String.format(
+ "[%d] setKeyboardGeometry: keyWidth=%3d tT=%3d >> %3d tD=%3d >> %3d",
+ mPointerId, keyWidth,
+ mParams.mDynamicTimeThresholdFrom,
+ mParams.mDynamicTimeThresholdTo,
+ mGestureDynamicDistanceThresholdFrom,
+ mGestureDynamicDistanceThresholdTo));
+ }
+ }
+
+ public void onDownEvent(final int x, final int y, final long downTime,
+ final long gestureFirstDownTime, final long lastTypingTime) {
+ reset();
+ final long elapsedTimeAfterTyping = downTime - lastTypingTime;
+ if (elapsedTimeAfterTyping < mParams.mStaticTimeThresholdAfterFastTyping) {
+ mAfterFastTyping = true;
+ }
+ if (DEBUG) {
+ Log.d(TAG, String.format("[%d] onDownEvent: dT=%3d%s", mPointerId,
+ elapsedTimeAfterTyping, mAfterFastTyping ? " afterFastTyping" : ""));
+ }
+ final int elapsedTimeFromFirstDown = (int)(downTime - gestureFirstDownTime);
+ addPoint(x, y, elapsedTimeFromFirstDown, true /* isMajorEvent */);
+ }
+
+ private int getGestureDynamicDistanceThreshold(final int deltaTime) {
+ if (!mAfterFastTyping || deltaTime >= mParams.mDynamicThresholdDecayDuration) {
+ return mGestureDynamicDistanceThresholdTo;
+ }
+ final int decayedThreshold =
+ (mGestureDynamicDistanceThresholdFrom - mGestureDynamicDistanceThresholdTo)
+ * deltaTime / mParams.mDynamicThresholdDecayDuration;
+ return mGestureDynamicDistanceThresholdFrom - decayedThreshold;
+ }
+
+ private int getGestureDynamicTimeThreshold(final int deltaTime) {
+ if (!mAfterFastTyping || deltaTime >= mParams.mDynamicThresholdDecayDuration) {
+ return mParams.mDynamicTimeThresholdTo;
+ }
+ final int decayedThreshold =
+ (mParams.mDynamicTimeThresholdFrom - mParams.mDynamicTimeThresholdTo)
+ * deltaTime / mParams.mDynamicThresholdDecayDuration;
+ return mParams.mDynamicTimeThresholdFrom - decayedThreshold;
+ }
+
+ public final boolean isStartOfAGesture() {
+ if (!hasDetectedFastMove()) {
+ return false;
+ }
+ final int size = mEventTimes.getLength();
+ if (size <= 0) {
+ return false;
+ }
+ final int lastIndex = size - 1;
+ final int deltaTime = mEventTimes.get(lastIndex) - mDetectFastMoveTime;
+ if (deltaTime < 0) {
+ return false;
+ }
+ final int deltaDistance = getDistance(
+ mXCoordinates.get(lastIndex), mYCoordinates.get(lastIndex),
+ mDetectFastMoveX, mDetectFastMoveY);
+ final int distanceThreshold = getGestureDynamicDistanceThreshold(deltaTime);
+ final int timeThreshold = getGestureDynamicTimeThreshold(deltaTime);
+ final boolean isStartOfAGesture = deltaTime >= timeThreshold
+ && deltaDistance >= distanceThreshold;
+ if (DEBUG) {
+ Log.d(TAG, String.format("[%d] isStartOfAGesture: dT=%3d tT=%3d dD=%3d tD=%3d%s%s",
+ mPointerId, deltaTime, timeThreshold,
+ deltaDistance, distanceThreshold,
+ mAfterFastTyping ? " afterFastTyping" : "",
+ isStartOfAGesture ? " startOfAGesture" : ""));
+ }
+ return isStartOfAGesture;
+ }
+
+ protected void reset() {
+ mIncrementalRecognitionSize = 0;
+ mLastIncrementalBatchSize = 0;
+ mEventTimes.setLength(0);
+ mXCoordinates.setLength(0);
+ mYCoordinates.setLength(0);
+ mLastMajorEventTime = 0;
+ mDetectFastMoveTime = 0;
+ mAfterFastTyping = false;
+ }
+
+ private void appendPoint(final int x, final int y, final int time) {
+ mEventTimes.add(time);
+ mXCoordinates.add(x);
+ mYCoordinates.add(y);
+ }
+
+ private void updateMajorEvent(final int x, final int y, final int time) {
+ mLastMajorEventTime = time;
+ mLastMajorEventX = x;
+ mLastMajorEventY = y;
+ }
+
+ private final boolean hasDetectedFastMove() {
+ return mDetectFastMoveTime > 0;
+ }
+
+ private int detectFastMove(final int x, final int y, final int time) {
+ final int size = mEventTimes.getLength();
+ final int lastIndex = size - 1;
+ final int lastX = mXCoordinates.get(lastIndex);
+ final int lastY = mYCoordinates.get(lastIndex);
+ final int dist = getDistance(lastX, lastY, x, y);
+ final int msecs = time - mEventTimes.get(lastIndex);
+ if (msecs > 0) {
+ final int pixels = getDistance(lastX, lastY, x, y);
+ final int pixelsPerSec = pixels * MSEC_PER_SEC;
+ if (DEBUG_SPEED) {
+ final float speed = (float)pixelsPerSec / msecs / mKeyWidth;
+ Log.d(TAG, String.format("[%d] detectFastMove: speed=%5.2f", mPointerId, speed));
+ }
+ // Equivalent to (pixels / msecs < mStartSpeedThreshold / MSEC_PER_SEC)
+ if (!hasDetectedFastMove() && pixelsPerSec > mDetectFastMoveSpeedThreshold * msecs) {
+ if (DEBUG) {
+ final float speed = (float)pixelsPerSec / msecs / mKeyWidth;
+ Log.d(TAG, String.format(
+ "[%d] detectFastMove: speed=%5.2f T=%3d points=%3d fastMove",
+ mPointerId, speed, time, size));
+ }
+ mDetectFastMoveTime = time;
+ mDetectFastMoveX = x;
+ mDetectFastMoveY = y;
+ }
+ }
+ return dist;
+ }
+
+ public void addPoint(final int x, final int y, final int time, final boolean isMajorEvent) {
+ final int size = mEventTimes.getLength();
+ if (size <= 0) {
+ // Down event
+ appendPoint(x, y, time);
+ updateMajorEvent(x, y, time);
+ } else {
+ final int distance = detectFastMove(x, y, time);
+ if (distance > mGestureSamplingMinimumDistance) {
+ appendPoint(x, y, time);
+ }
+ }
+ if (isMajorEvent) {
+ updateIncrementalRecognitionSize(x, y, time);
+ updateMajorEvent(x, y, time);
+ }
+ }
+
+ private void updateIncrementalRecognitionSize(final int x, final int y, final int time) {
+ final int msecs = (int)(time - mLastMajorEventTime);
+ if (msecs <= 0) {
+ return;
+ }
+ final int pixels = getDistance(mLastMajorEventX, mLastMajorEventY, x, y);
+ final int pixelsPerSec = pixels * MSEC_PER_SEC;
+ // Equivalent to (pixels / msecs < mGestureRecognitionThreshold / MSEC_PER_SEC)
+ if (pixelsPerSec < mGestureRecognitionSpeedThreshold * msecs) {
+ mIncrementalRecognitionSize = mEventTimes.getLength();
+ }
+ }
+
+ public final boolean hasRecognitionTimePast(
+ final long currentTime, final long lastRecognitionTime) {
+ return currentTime > lastRecognitionTime + mParams.mRecognitionMinimumTime;
+ }
+
+ public final void appendAllBatchPoints(final InputPointers out) {
+ appendBatchPoints(out, mEventTimes.getLength());
+ }
+
+ public final void appendIncrementalBatchPoints(final InputPointers out) {
+ appendBatchPoints(out, mIncrementalRecognitionSize);
+ }
+
+ private void appendBatchPoints(final InputPointers out, final int size) {
+ final int length = size - mLastIncrementalBatchSize;
+ if (length <= 0) {
+ return;
+ }
+ out.append(mPointerId, mEventTimes, mXCoordinates, mYCoordinates,
+ mLastIncrementalBatchSize, length);
+ mLastIncrementalBatchSize = size;
+ }
+
+ private static int getDistance(final int x1, final int y1, final int x2, final int y2) {
+ final int dx = x1 - x2;
+ final int dy = y1 - y2;
+ // Note that, in recent versions of Android, FloatMath is actually slower than
+ // java.lang.Math due to the way the JIT optimizes java.lang.Math.
+ return (int)Math.sqrt(dx * dx + dy * dy);
+ }
+}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeWithPreviewPoints.java b/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeWithPreviewPoints.java
new file mode 100644
index 000000000..8192c9076
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/internal/GestureStrokeWithPreviewPoints.java
@@ -0,0 +1,94 @@
+/*
+ * 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.keyboard.internal;
+
+import com.android.inputmethod.latin.ResizableIntArray;
+
+public final class GestureStrokeWithPreviewPoints extends GestureStroke {
+ public static final int PREVIEW_CAPACITY = 256;
+
+ private final ResizableIntArray mPreviewEventTimes = new ResizableIntArray(PREVIEW_CAPACITY);
+ private final ResizableIntArray mPreviewXCoordinates = new ResizableIntArray(PREVIEW_CAPACITY);
+ private final ResizableIntArray mPreviewYCoordinates = new ResizableIntArray(PREVIEW_CAPACITY);
+
+ private int mStrokeId;
+ private int mLastPreviewSize;
+
+ 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, final GestureStrokeParams params) {
+ super(pointerId, params);
+ }
+
+ @Override
+ protected void reset() {
+ super.reset();
+ mStrokeId++;
+ mLastPreviewSize = 0;
+ mPreviewEventTimes.setLength(0);
+ mPreviewXCoordinates.setLength(0);
+ mPreviewYCoordinates.setLength(0);
+ }
+
+ public int getGestureStrokeId() {
+ return mStrokeId;
+ }
+
+ public int getGestureStrokePreviewSize() {
+ 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;
+ return dx * dx + dy * dy >= mMinPreviewSampleLengthSquare;
+ }
+
+ @Override
+ public void addPoint(final int x, final int y, final int time, final boolean isMajorEvent) {
+ super.addPoint(x, y, time, isMajorEvent);
+ if (isMajorEvent || needsSampling(x, y)) {
+ mPreviewEventTimes.add(time);
+ mPreviewXCoordinates.add(x);
+ mPreviewYCoordinates.add(y);
+ mLastX = x;
+ mLastY = y;
+ }
+ }
+
+ public void appendPreviewStroke(final ResizableIntArray eventTimes,
+ final ResizableIntArray xCoords, final ResizableIntArray yCoords) {
+ final int length = mPreviewEventTimes.getLength() - mLastPreviewSize;
+ if (length <= 0) {
+ return;
+ }
+ eventTimes.append(mPreviewEventTimes, mLastPreviewSize, length);
+ xCoords.append(mPreviewXCoordinates, mLastPreviewSize, length);
+ yCoords.append(mPreviewYCoordinates, mLastPreviewSize, length);
+ mLastPreviewSize = mPreviewEventTimes.getLength();
+ }
+}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyDrawParams.java b/java/src/com/android/inputmethod/keyboard/internal/KeyDrawParams.java
new file mode 100644
index 000000000..5dcd842f7
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyDrawParams.java
@@ -0,0 +1,140 @@
+/*
+ * 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.keyboard.internal;
+
+import android.graphics.Typeface;
+
+import com.android.inputmethod.latin.ResourceUtils;
+
+public final class KeyDrawParams {
+ public Typeface mTypeface;
+
+ public int mLetterSize;
+ public int mLabelSize;
+ public int mLargeLetterSize;
+ public int mLargeLabelSize;
+ public int mHintLetterSize;
+ public int mShiftedLetterHintSize;
+ public int mHintLabelSize;
+ public int mPreviewTextSize;
+
+ public int mTextColor;
+ public int mTextInactivatedColor;
+ public int mTextShadowColor;
+ public int mHintLetterColor;
+ public int mHintLabelColor;
+ public int mShiftedLetterHintInactivatedColor;
+ public int mShiftedLetterHintActivatedColor;
+ public int mPreviewTextColor;
+
+ public int mAnimAlpha;
+
+ public KeyDrawParams() {}
+
+ private KeyDrawParams(final KeyDrawParams copyFrom) {
+ mTypeface = copyFrom.mTypeface;
+
+ mLetterSize = copyFrom.mLetterSize;
+ mLabelSize = copyFrom.mLabelSize;
+ mLargeLetterSize = copyFrom.mLargeLetterSize;
+ mLargeLabelSize = copyFrom.mLargeLabelSize;
+ mHintLetterSize = copyFrom.mHintLetterSize;
+ mShiftedLetterHintSize = copyFrom.mShiftedLetterHintSize;
+ mHintLabelSize = copyFrom.mHintLabelSize;
+ mPreviewTextSize = copyFrom.mPreviewTextSize;
+
+ mTextColor = copyFrom.mTextColor;
+ mTextInactivatedColor = copyFrom.mTextInactivatedColor;
+ mTextShadowColor = copyFrom.mTextShadowColor;
+ mHintLetterColor = copyFrom.mHintLetterColor;
+ mHintLabelColor = copyFrom.mHintLabelColor;
+ mShiftedLetterHintInactivatedColor = copyFrom.mShiftedLetterHintInactivatedColor;
+ mShiftedLetterHintActivatedColor = copyFrom.mShiftedLetterHintActivatedColor;
+ mPreviewTextColor = copyFrom.mPreviewTextColor;
+
+ mAnimAlpha = copyFrom.mAnimAlpha;
+ }
+
+ public void updateParams(final int keyHeight, final KeyVisualAttributes attr) {
+ if (attr == null) {
+ return;
+ }
+
+ if (attr.mTypeface != null) {
+ mTypeface = attr.mTypeface;
+ }
+
+ mLetterSize = selectTextSizeFromDimensionOrRatio(keyHeight,
+ attr.mLetterSize, attr.mLetterRatio, mLetterSize);
+ mLabelSize = selectTextSizeFromDimensionOrRatio(keyHeight,
+ attr.mLabelSize, attr.mLabelRatio, mLabelSize);
+ mLargeLabelSize = selectTextSize(keyHeight, attr.mLargeLabelRatio, mLargeLabelSize);
+ mLargeLetterSize = selectTextSize(keyHeight, attr.mLargeLetterRatio, mLargeLetterSize);
+ mHintLetterSize = selectTextSize(keyHeight, attr.mHintLetterRatio, mHintLetterSize);
+ mShiftedLetterHintSize = selectTextSize(keyHeight,
+ attr.mShiftedLetterHintRatio, mShiftedLetterHintSize);
+ mHintLabelSize = selectTextSize(keyHeight, attr.mHintLabelRatio, mHintLabelSize);
+ mPreviewTextSize = selectTextSize(keyHeight, attr.mPreviewTextRatio, mPreviewTextSize);
+
+ mTextColor = selectColor(attr.mTextColor, mTextColor);
+ mTextInactivatedColor = selectColor(attr.mTextInactivatedColor, mTextInactivatedColor);
+ mTextShadowColor = selectColor(attr.mTextShadowColor, mTextShadowColor);
+ mHintLetterColor = selectColor(attr.mHintLetterColor, mHintLetterColor);
+ mHintLabelColor = selectColor(attr.mHintLabelColor, mHintLabelColor);
+ mShiftedLetterHintInactivatedColor = selectColor(
+ attr.mShiftedLetterHintInactivatedColor, mShiftedLetterHintInactivatedColor);
+ mShiftedLetterHintActivatedColor = selectColor(
+ attr.mShiftedLetterHintActivatedColor, mShiftedLetterHintActivatedColor);
+ mPreviewTextColor = selectColor(attr.mPreviewTextColor, mPreviewTextColor);
+ }
+
+ public KeyDrawParams mayCloneAndUpdateParams(final int keyHeight,
+ final KeyVisualAttributes attr) {
+ if (attr == null) {
+ return this;
+ }
+ final KeyDrawParams newParams = new KeyDrawParams(this);
+ newParams.updateParams(keyHeight, attr);
+ return newParams;
+ }
+
+ private static final int selectTextSizeFromDimensionOrRatio(final int keyHeight,
+ final int dimens, final float ratio, final int defaultDimens) {
+ if (ResourceUtils.isValidDimensionPixelSize(dimens)) {
+ return dimens;
+ }
+ if (ResourceUtils.isValidFraction(ratio)) {
+ return (int)(keyHeight * ratio);
+ }
+ return defaultDimens;
+ }
+
+ private static final int selectTextSize(final int keyHeight, final float ratio,
+ final int defaultSize) {
+ if (ResourceUtils.isValidFraction(ratio)) {
+ return (int)(keyHeight * ratio);
+ }
+ return defaultSize;
+ }
+
+ private static final int selectColor(final int attrColor, final int defaultColor) {
+ if (attrColor != 0) {
+ return attrColor;
+ }
+ return defaultColor;
+ }
+}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewDrawParams.java b/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewDrawParams.java
new file mode 100644
index 000000000..609d1a57f
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewDrawParams.java
@@ -0,0 +1,44 @@
+/*
+ * 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.keyboard.internal;
+
+public final class KeyPreviewDrawParams {
+ // The graphical geometry of the key preview.
+ // <-width->
+ // +-------+ ^
+ // | | |
+ // |preview| height (visible)
+ // | | |
+ // + + ^ v
+ // \ / |offset
+ // +-\ /-+ v
+ // | +-+ |
+ // |parent |
+ // | key|
+ // +-------+
+ // The background of a {@link TextView} being used for a key preview may have invisible
+ // paddings. To align the more keys keyboard panel's visible part with the visible part of
+ // the background, we need to record the width and height of key preview that don't include
+ // invisible paddings.
+ public int mPreviewVisibleWidth;
+ public int mPreviewVisibleHeight;
+ // The key preview may have an arbitrary offset and its background that may have a bottom
+ // padding. To align the more keys keyboard and the key preview we also need to record the
+ // offset between the top edge of parent key and the bottom of the visible part of key
+ // preview background.
+ public int mPreviewVisibleOffset;
+}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java
index c4452a5f5..2caa5eb02 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java
@@ -21,6 +21,7 @@ import static com.android.inputmethod.keyboard.Keyboard.CODE_UNSPECIFIED;
import android.text.TextUtils;
import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.latin.CollectionUtils;
import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.StringUtils;
@@ -45,7 +46,7 @@ import java.util.Locale;
* Note that the '\' is also parsed by XML parser and CSV parser as well.
* See {@link KeyboardIconsSet} about icon_name.
*/
-public class KeySpecParser {
+public final class KeySpecParser {
private static final boolean DEBUG = LatinImeLogger.sDBG;
private static final int MAX_STRING_REFERENCE_INDIRECTION = 10;
@@ -55,38 +56,20 @@ public class KeySpecParser {
private static final char ESCAPE_CHAR = '\\';
private static final char LABEL_END = '|';
private static final String PREFIX_TEXT = "!text/";
- private static final String PREFIX_ICON = "!icon/";
+ static final String PREFIX_ICON = "!icon/";
private static final String PREFIX_CODE = "!code/";
private static final String PREFIX_HEX = "0x";
private static final String ADDITIONAL_MORE_KEY_MARKER = "%";
- public static class MoreKeySpec {
- public final int mCode;
- public final String mLabel;
- public final String mOutputText;
- public final int mIconId;
-
- public MoreKeySpec(final String moreKeySpec, boolean needsToUpperCase, Locale locale,
- final KeyboardCodesSet codesSet) {
- mCode = toUpperCaseOfCodeForLocale(getCode(moreKeySpec, codesSet),
- needsToUpperCase, locale);
- mLabel = toUpperCaseOfStringForLocale(getLabel(moreKeySpec),
- needsToUpperCase, locale);
- mOutputText = toUpperCaseOfStringForLocale(getOutputText(moreKeySpec),
- needsToUpperCase, locale);
- mIconId = getIconId(moreKeySpec);
- }
- }
-
private KeySpecParser() {
// Intentional empty constructor for utility class.
}
- private static boolean hasIcon(String moreKeySpec) {
+ private static boolean hasIcon(final String moreKeySpec) {
return moreKeySpec.startsWith(PREFIX_ICON);
}
- private static boolean hasCode(String moreKeySpec) {
+ private static boolean hasCode(final String moreKeySpec) {
final int end = indexOfLabelEnd(moreKeySpec, 0);
if (end > 0 && end + 1 < moreKeySpec.length() && moreKeySpec.startsWith(
PREFIX_CODE, end + 1)) {
@@ -95,7 +78,7 @@ public class KeySpecParser {
return false;
}
- private static String parseEscape(String text) {
+ private static String parseEscape(final String text) {
if (text.indexOf(ESCAPE_CHAR) < 0) {
return text;
}
@@ -114,7 +97,7 @@ public class KeySpecParser {
return sb.toString();
}
- private static int indexOfLabelEnd(String moreKeySpec, int start) {
+ private static int indexOfLabelEnd(final String moreKeySpec, final int start) {
if (moreKeySpec.indexOf(ESCAPE_CHAR, start) < 0) {
final int end = moreKeySpec.indexOf(LABEL_END, start);
if (end == 0) {
@@ -135,7 +118,7 @@ public class KeySpecParser {
return -1;
}
- public static String getLabel(String moreKeySpec) {
+ public static String getLabel(final String moreKeySpec) {
if (hasIcon(moreKeySpec)) {
return null;
}
@@ -148,7 +131,7 @@ public class KeySpecParser {
return label;
}
- private static String getOutputTextInternal(String moreKeySpec) {
+ private static String getOutputTextInternal(final String moreKeySpec) {
final int end = indexOfLabelEnd(moreKeySpec, 0);
if (end <= 0) {
return null;
@@ -159,7 +142,7 @@ public class KeySpecParser {
return parseEscape(moreKeySpec.substring(end + /* LABEL_END */1));
}
- static String getOutputText(String moreKeySpec) {
+ static String getOutputText(final String moreKeySpec) {
if (hasCode(moreKeySpec)) {
return null;
}
@@ -183,7 +166,7 @@ public class KeySpecParser {
return (StringUtils.codePointCount(label) == 1) ? null : label;
}
- static int getCode(String moreKeySpec, KeyboardCodesSet codesSet) {
+ static int getCode(final String moreKeySpec, final KeyboardCodesSet codesSet) {
if (hasCode(moreKeySpec)) {
final int end = indexOfLabelEnd(moreKeySpec, 0);
if (indexOfLabelEnd(moreKeySpec, end + 1) >= 0) {
@@ -208,7 +191,8 @@ public class KeySpecParser {
return Keyboard.CODE_OUTPUT_TEXT;
}
- public static int parseCode(String text, KeyboardCodesSet codesSet, int defCode) {
+ public static int parseCode(final String text, final KeyboardCodesSet codesSet,
+ final int defCode) {
if (text == null) return defCode;
if (text.startsWith(PREFIX_CODE)) {
return codesSet.getCode(text.substring(PREFIX_CODE.length()));
@@ -219,7 +203,7 @@ public class KeySpecParser {
}
}
- public static int getIconId(String moreKeySpec) {
+ public static int getIconId(final String moreKeySpec) {
if (moreKeySpec != null && hasIcon(moreKeySpec)) {
final int end = moreKeySpec.indexOf(LABEL_END, PREFIX_ICON.length());
final String name = (end < 0) ? moreKeySpec.substring(PREFIX_ICON.length())
@@ -229,7 +213,7 @@ public class KeySpecParser {
return KeyboardIconsSet.ICON_UNDEFINED;
}
- private static <T> ArrayList<T> arrayAsList(T[] array, int start, int end) {
+ private static <T> ArrayList<T> arrayAsList(final T[] array, final int start, final int end) {
if (array == null) {
throw new NullPointerException();
}
@@ -237,7 +221,7 @@ public class KeySpecParser {
throw new IllegalArgumentException();
}
- final ArrayList<T> list = new ArrayList<T>(end - start);
+ final ArrayList<T> list = CollectionUtils.newArrayList(end - start);
for (int i = start; i < end; i++) {
list.add(array[i]);
}
@@ -246,7 +230,7 @@ public class KeySpecParser {
private static final String[] EMPTY_STRING_ARRAY = new String[0];
- private static String[] filterOutEmptyString(String[] array) {
+ private static String[] filterOutEmptyString(final String[] array) {
if (array == null) {
return EMPTY_STRING_ARRAY;
}
@@ -267,8 +251,8 @@ public class KeySpecParser {
return out.toArray(new String[out.size()]);
}
- public static String[] insertAdditionalMoreKeys(String[] moreKeySpecs,
- String[] additionalMoreKeySpecs) {
+ public static String[] insertAdditionalMoreKeys(final String[] moreKeySpecs,
+ final String[] additionalMoreKeySpecs) {
final String[] moreKeys = filterOutEmptyString(moreKeySpecs);
final String[] additionalMoreKeys = filterOutEmptyString(additionalMoreKeySpecs);
final int moreKeysCount = moreKeys.length;
@@ -334,13 +318,14 @@ public class KeySpecParser {
}
@SuppressWarnings("serial")
- public static class KeySpecParserError extends RuntimeException {
- public KeySpecParserError(String message) {
+ public static final class KeySpecParserError extends RuntimeException {
+ public KeySpecParserError(final String message) {
super(message);
}
}
- public static String resolveTextReference(String rawText, KeyboardTextsSet textsSet) {
+ public static String resolveTextReference(final String rawText,
+ final KeyboardTextsSet textsSet) {
int level = 0;
String text = rawText;
StringBuilder sb;
@@ -386,7 +371,7 @@ public class KeySpecParser {
return text;
}
- private static int searchTextNameEnd(String text, int start) {
+ private static int searchTextNameEnd(final String text, final int start) {
final int size = text.length();
for (int pos = start; pos < size; pos++) {
final char c = text.charAt(pos);
@@ -399,7 +384,7 @@ public class KeySpecParser {
return size;
}
- public static String[] parseCsvString(String rawText, KeyboardTextsSet textsSet) {
+ public static String[] parseCsvString(final String rawText, final KeyboardTextsSet textsSet) {
final String text = resolveTextReference(rawText, textsSet);
final int size = text.length();
if (size == 0) {
@@ -417,7 +402,7 @@ public class KeySpecParser {
// Skip empty entry.
if (pos - start > 0) {
if (list == null) {
- list = new ArrayList<String>();
+ list = CollectionUtils.newArrayList();
}
list.add(text.substring(start, pos));
}
@@ -438,7 +423,8 @@ public class KeySpecParser {
return list.toArray(new String[list.size()]);
}
- public static int getIntValue(String[] moreKeys, String key, int defaultValue) {
+ public static int getIntValue(final String[] moreKeys, final String key,
+ final int defaultValue) {
if (moreKeys == null) {
return defaultValue;
}
@@ -464,7 +450,7 @@ public class KeySpecParser {
return value;
}
- public static boolean getBooleanValue(String[] moreKeys, String key) {
+ public static boolean getBooleanValue(final String[] moreKeys, final String key) {
if (moreKeys == null) {
return false;
}
@@ -480,8 +466,8 @@ public class KeySpecParser {
return value;
}
- public static int toUpperCaseOfCodeForLocale(int code, boolean needsToUpperCase,
- Locale locale) {
+ public static int toUpperCaseOfCodeForLocale(final int code, final boolean needsToUpperCase,
+ final Locale locale) {
if (!Keyboard.isLetterCode(code) || !needsToUpperCase) return code;
final String text = new String(new int[] { code } , 0, 1);
final String casedText = KeySpecParser.toUpperCaseOfStringForLocale(
@@ -490,8 +476,8 @@ public class KeySpecParser {
? casedText.codePointAt(0) : CODE_UNSPECIFIED;
}
- public static String toUpperCaseOfStringForLocale(String text, boolean needsToUpperCase,
- Locale locale) {
+ public static String toUpperCaseOfStringForLocale(final String text,
+ final boolean needsToUpperCase, final Locale locale) {
if (text == null || !needsToUpperCase) return text;
return text.toUpperCase(locale);
}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyStyle.java b/java/src/com/android/inputmethod/keyboard/internal/KeyStyle.java
new file mode 100644
index 000000000..e8cacf9e7
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyStyle.java
@@ -0,0 +1,46 @@
+/*
+ * 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.keyboard.internal;
+
+import android.content.res.TypedArray;
+
+public abstract class KeyStyle {
+ private final KeyboardTextsSet mTextsSet;
+
+ public abstract String[] getStringArray(TypedArray a, int index);
+ public abstract String getString(TypedArray a, int index);
+ public abstract int getInt(TypedArray a, int index, int defaultValue);
+ public abstract int getFlag(TypedArray a, int index);
+
+ protected KeyStyle(final KeyboardTextsSet textsSet) {
+ mTextsSet = textsSet;
+ }
+
+ protected String parseString(final TypedArray a, final int index) {
+ if (a.hasValue(index)) {
+ return KeySpecParser.resolveTextReference(a.getString(index), mTextsSet);
+ }
+ return null;
+ }
+
+ protected String[] parseStringArray(final TypedArray a, final int index) {
+ if (a.hasValue(index)) {
+ return KeySpecParser.parseCsvString(a.getString(index), mTextsSet);
+ }
+ return null;
+ }
+}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java b/java/src/com/android/inputmethod/keyboard/internal/KeyStylesSet.java
index 80f4f259b..563d22414 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyStylesSet.java
@@ -18,8 +18,9 @@ package com.android.inputmethod.keyboard.internal;
import android.content.res.TypedArray;
import android.util.Log;
+import android.util.SparseArray;
-import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.latin.CollectionUtils;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.XmlParseUtils;
@@ -28,120 +29,111 @@ import org.xmlpull.v1.XmlPullParserException;
import java.util.HashMap;
-public class KeyStyles {
- private static final String TAG = KeyStyles.class.getSimpleName();
+public final class KeyStylesSet {
+ private static final String TAG = KeyStylesSet.class.getSimpleName();
private static final boolean DEBUG = false;
- final HashMap<String, KeyStyle> mStyles = new HashMap<String, KeyStyle>();
+ private final HashMap<String, KeyStyle> mStyles = CollectionUtils.newHashMap();
- final KeyboardTextsSet mTextsSet;
+ private final KeyboardTextsSet mTextsSet;
private final KeyStyle mEmptyKeyStyle;
private static final String EMPTY_STYLE_NAME = "<empty>";
- public KeyStyles(KeyboardTextsSet textsSet) {
+ public KeyStylesSet(final KeyboardTextsSet textsSet) {
mTextsSet = textsSet;
- mEmptyKeyStyle = new EmptyKeyStyle();
+ mEmptyKeyStyle = new EmptyKeyStyle(textsSet);
mStyles.put(EMPTY_STYLE_NAME, mEmptyKeyStyle);
}
- public abstract class KeyStyle {
- public abstract String[] getStringArray(TypedArray a, int index);
- public abstract String getString(TypedArray a, int index);
- public abstract int getInt(TypedArray a, int index, int defaultValue);
- public abstract int getFlag(TypedArray a, int index);
-
- protected String parseString(TypedArray a, int index) {
- if (a.hasValue(index)) {
- return KeySpecParser.resolveTextReference(a.getString(index), mTextsSet);
- }
- return null;
- }
-
- protected String[] parseStringArray(TypedArray a, int index) {
- if (a.hasValue(index)) {
- return KeySpecParser.parseCsvString(a.getString(index), mTextsSet);
- }
- return null;
+ private static final class EmptyKeyStyle extends KeyStyle {
+ EmptyKeyStyle(final KeyboardTextsSet textsSet) {
+ super(textsSet);
}
- }
- class EmptyKeyStyle extends KeyStyle {
@Override
- public String[] getStringArray(TypedArray a, int index) {
+ public String[] getStringArray(final TypedArray a, final int index) {
return parseStringArray(a, index);
}
@Override
- public String getString(TypedArray a, int index) {
+ public String getString(final TypedArray a, final int index) {
return parseString(a, index);
}
@Override
- public int getInt(TypedArray a, int index, int defaultValue) {
+ public int getInt(final TypedArray a, final int index, final int defaultValue) {
return a.getInt(index, defaultValue);
}
@Override
- public int getFlag(TypedArray a, int index) {
+ public int getFlag(final TypedArray a, final int index) {
return a.getInt(index, 0);
}
}
- private class DeclaredKeyStyle extends KeyStyle {
+ private static final class DeclaredKeyStyle extends KeyStyle {
+ private final HashMap<String, KeyStyle> mStyles;
private final String mParentStyleName;
- private final HashMap<Integer, Object> mStyleAttributes = new HashMap<Integer, Object>();
+ private final SparseArray<Object> mStyleAttributes = CollectionUtils.newSparseArray();
- public DeclaredKeyStyle(String parentStyleName) {
+ public DeclaredKeyStyle(final String parentStyleName, final KeyboardTextsSet textsSet,
+ final HashMap<String, KeyStyle> styles) {
+ super(textsSet);
mParentStyleName = parentStyleName;
+ mStyles = styles;
}
@Override
- public String[] getStringArray(TypedArray a, int index) {
+ public String[] getStringArray(final TypedArray a, final int index) {
if (a.hasValue(index)) {
return parseStringArray(a, index);
}
- if (mStyleAttributes.containsKey(index)) {
- return (String[])mStyleAttributes.get(index);
+ final Object value = mStyleAttributes.get(index);
+ if (value != null) {
+ return (String[])value;
}
final KeyStyle parentStyle = mStyles.get(mParentStyleName);
return parentStyle.getStringArray(a, index);
}
@Override
- public String getString(TypedArray a, int index) {
+ public String getString(final TypedArray a, final int index) {
if (a.hasValue(index)) {
return parseString(a, index);
}
- if (mStyleAttributes.containsKey(index)) {
- return (String)mStyleAttributes.get(index);
+ final Object value = mStyleAttributes.get(index);
+ if (value != null) {
+ return (String)value;
}
final KeyStyle parentStyle = mStyles.get(mParentStyleName);
return parentStyle.getString(a, index);
}
@Override
- public int getInt(TypedArray a, int index, int defaultValue) {
+ public int getInt(final TypedArray a, final int index, final int defaultValue) {
if (a.hasValue(index)) {
return a.getInt(index, defaultValue);
}
- if (mStyleAttributes.containsKey(index)) {
- return (Integer)mStyleAttributes.get(index);
+ final Object value = mStyleAttributes.get(index);
+ if (value != null) {
+ return (Integer)value;
}
final KeyStyle parentStyle = mStyles.get(mParentStyleName);
return parentStyle.getInt(a, index, defaultValue);
}
@Override
- public int getFlag(TypedArray a, int index) {
- int value = a.getInt(index, 0);
- if (mStyleAttributes.containsKey(index)) {
- value |= (Integer)mStyleAttributes.get(index);
+ public int getFlag(final TypedArray a, final int index) {
+ int flags = a.getInt(index, 0);
+ final Object value = mStyleAttributes.get(index);
+ if (value != null) {
+ flags |= (Integer)value;
}
final KeyStyle parentStyle = mStyles.get(mParentStyleName);
- return value | parentStyle.getFlag(a, index);
+ return flags | parentStyle.getFlag(a, index);
}
- void readKeyAttributes(TypedArray keyAttr) {
+ public void readKeyAttributes(final TypedArray keyAttr) {
// TODO: Currently not all Key attributes can be declared as style.
readString(keyAttr, R.styleable.Keyboard_Key_code);
readString(keyAttr, R.styleable.Keyboard_Key_altCode);
@@ -159,38 +151,38 @@ public class KeyStyles {
readFlag(keyAttr, R.styleable.Keyboard_Key_keyActionFlags);
}
- private void readString(TypedArray a, int index) {
+ private void readString(final TypedArray a, final int index) {
if (a.hasValue(index)) {
mStyleAttributes.put(index, parseString(a, index));
}
}
- private void readInt(TypedArray a, int index) {
+ private void readInt(final TypedArray a, final int index) {
if (a.hasValue(index)) {
mStyleAttributes.put(index, a.getInt(index, 0));
}
}
- private void readFlag(TypedArray a, int index) {
+ private void readFlag(final TypedArray a, final int index) {
if (a.hasValue(index)) {
final Integer value = (Integer)mStyleAttributes.get(index);
mStyleAttributes.put(index, a.getInt(index, 0) | (value != null ? value : 0));
}
}
- private void readStringArray(TypedArray a, int index) {
+ private void readStringArray(final TypedArray a, final int index) {
if (a.hasValue(index)) {
mStyleAttributes.put(index, parseStringArray(a, index));
}
}
}
- public void parseKeyStyleAttributes(TypedArray keyStyleAttr, TypedArray keyAttrs,
- XmlPullParser parser) throws XmlPullParserException {
+ public void parseKeyStyleAttributes(final TypedArray keyStyleAttr, final TypedArray keyAttrs,
+ final XmlPullParser parser) throws XmlPullParserException {
final String styleName = keyStyleAttr.getString(R.styleable.Keyboard_KeyStyle_styleName);
if (DEBUG) {
Log.d(TAG, String.format("<%s styleName=%s />",
- Keyboard.Builder.TAG_KEY_STYLE, styleName));
+ KeyboardBuilder.TAG_KEY_STYLE, styleName));
if (mStyles.containsKey(styleName)) {
Log.d(TAG, "key-style " + styleName + " is overridden at "
+ parser.getPositionDescription());
@@ -205,12 +197,12 @@ public class KeyStyles {
"Unknown parentStyle " + parentStyleName, parser);
}
}
- final DeclaredKeyStyle style = new DeclaredKeyStyle(parentStyleName);
+ final DeclaredKeyStyle style = new DeclaredKeyStyle(parentStyleName, mTextsSet, mStyles);
style.readKeyAttributes(keyAttrs);
mStyles.put(styleName, style);
}
- public KeyStyle getKeyStyle(TypedArray keyAttr, XmlPullParser parser)
+ public KeyStyle getKeyStyle(final TypedArray keyAttr, final XmlPullParser parser)
throws XmlParseUtils.ParseException {
if (!keyAttr.hasValue(R.styleable.Keyboard_Key_keyStyle)) {
return mEmptyKeyStyle;
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyVisualAttributes.java b/java/src/com/android/inputmethod/keyboard/internal/KeyVisualAttributes.java
new file mode 100644
index 000000000..6ddd2a6fb
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyVisualAttributes.java
@@ -0,0 +1,130 @@
+/*
+ * 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.keyboard.internal;
+
+import android.content.res.TypedArray;
+import android.graphics.Typeface;
+import android.util.SparseIntArray;
+
+import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.ResourceUtils;
+
+public final class KeyVisualAttributes {
+ public final Typeface mTypeface;
+
+ public final float mLetterRatio;
+ public final int mLetterSize;
+ public final float mLabelRatio;
+ public final int mLabelSize;
+ public final float mLargeLetterRatio;
+ public final float mLargeLabelRatio;
+ public final float mHintLetterRatio;
+ public final float mShiftedLetterHintRatio;
+ public final float mHintLabelRatio;
+ public final float mPreviewTextRatio;
+
+ public final int mTextColor;
+ public final int mTextInactivatedColor;
+ public final int mTextShadowColor;
+ public final int mHintLetterColor;
+ public final int mHintLabelColor;
+ public final int mShiftedLetterHintInactivatedColor;
+ public final int mShiftedLetterHintActivatedColor;
+ public final int mPreviewTextColor;
+
+ private static final int[] VISUAL_ATTRIBUTE_IDS = {
+ R.styleable.Keyboard_Key_keyTypeface,
+ R.styleable.Keyboard_Key_keyLetterSize,
+ R.styleable.Keyboard_Key_keyLabelSize,
+ R.styleable.Keyboard_Key_keyLargeLetterRatio,
+ R.styleable.Keyboard_Key_keyLargeLabelRatio,
+ R.styleable.Keyboard_Key_keyHintLetterRatio,
+ R.styleable.Keyboard_Key_keyShiftedLetterHintRatio,
+ R.styleable.Keyboard_Key_keyHintLabelRatio,
+ R.styleable.Keyboard_Key_keyPreviewTextRatio,
+ R.styleable.Keyboard_Key_keyTextColor,
+ R.styleable.Keyboard_Key_keyTextInactivatedColor,
+ R.styleable.Keyboard_Key_keyTextShadowColor,
+ R.styleable.Keyboard_Key_keyHintLetterColor,
+ R.styleable.Keyboard_Key_keyHintLabelColor,
+ R.styleable.Keyboard_Key_keyShiftedLetterHintInactivatedColor,
+ R.styleable.Keyboard_Key_keyShiftedLetterHintActivatedColor,
+ R.styleable.Keyboard_Key_keyPreviewTextColor,
+ };
+ private static final SparseIntArray sVisualAttributeIds = new SparseIntArray();
+ private static final int ATTR_DEFINED = 1;
+ private static final int ATTR_NOT_FOUND = 0;
+ static {
+ for (final int attrId : VISUAL_ATTRIBUTE_IDS) {
+ sVisualAttributeIds.put(attrId, ATTR_DEFINED);
+ }
+ }
+
+ public static KeyVisualAttributes newInstance(final TypedArray keyAttr) {
+ final int indexCount = keyAttr.getIndexCount();
+ for (int i = 0; i < indexCount; i++) {
+ final int attrId = keyAttr.getIndex(i);
+ if (sVisualAttributeIds.get(attrId, ATTR_NOT_FOUND) == ATTR_NOT_FOUND) {
+ continue;
+ }
+ return new KeyVisualAttributes(keyAttr);
+ }
+ return null;
+ }
+
+ private KeyVisualAttributes(final TypedArray keyAttr) {
+ if (keyAttr.hasValue(R.styleable.Keyboard_Key_keyTypeface)) {
+ mTypeface = Typeface.defaultFromStyle(
+ keyAttr.getInt(R.styleable.Keyboard_Key_keyTypeface, Typeface.NORMAL));
+ } else {
+ mTypeface = null;
+ }
+
+ mLetterRatio = ResourceUtils.getFraction(keyAttr,
+ R.styleable.Keyboard_Key_keyLetterSize);
+ mLetterSize = ResourceUtils.getDimensionPixelSize(keyAttr,
+ R.styleable.Keyboard_Key_keyLetterSize);
+ mLabelRatio = ResourceUtils.getFraction(keyAttr,
+ R.styleable.Keyboard_Key_keyLabelSize);
+ mLabelSize = ResourceUtils.getDimensionPixelSize(keyAttr,
+ R.styleable.Keyboard_Key_keyLabelSize);
+ mLargeLetterRatio = ResourceUtils.getFraction(keyAttr,
+ R.styleable.Keyboard_Key_keyLargeLetterRatio);
+ mLargeLabelRatio = ResourceUtils.getFraction(keyAttr,
+ R.styleable.Keyboard_Key_keyLargeLabelRatio);
+ mHintLetterRatio = ResourceUtils.getFraction(keyAttr,
+ R.styleable.Keyboard_Key_keyHintLetterRatio);
+ mShiftedLetterHintRatio = ResourceUtils.getFraction(keyAttr,
+ R.styleable.Keyboard_Key_keyShiftedLetterHintRatio);
+ mHintLabelRatio = ResourceUtils.getFraction(keyAttr,
+ R.styleable.Keyboard_Key_keyHintLabelRatio);
+ mPreviewTextRatio = ResourceUtils.getFraction(keyAttr,
+ R.styleable.Keyboard_Key_keyPreviewTextRatio);
+
+ mTextColor = keyAttr.getColor(R.styleable.Keyboard_Key_keyTextColor, 0);
+ mTextInactivatedColor = keyAttr.getColor(
+ R.styleable.Keyboard_Key_keyTextInactivatedColor, 0);
+ mTextShadowColor = keyAttr.getColor(R.styleable.Keyboard_Key_keyTextShadowColor, 0);
+ mHintLetterColor = keyAttr.getColor(R.styleable.Keyboard_Key_keyHintLetterColor, 0);
+ mHintLabelColor = keyAttr.getColor(R.styleable.Keyboard_Key_keyHintLabelColor, 0);
+ mShiftedLetterHintInactivatedColor = keyAttr.getColor(
+ R.styleable.Keyboard_Key_keyShiftedLetterHintInactivatedColor, 0);
+ mShiftedLetterHintActivatedColor = keyAttr.getColor(
+ R.styleable.Keyboard_Key_keyShiftedLetterHintActivatedColor, 0);
+ mPreviewTextColor = keyAttr.getColor(R.styleable.Keyboard_Key_keyPreviewTextColor, 0);
+ }
+}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
new file mode 100644
index 000000000..b314a3795
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
@@ -0,0 +1,813 @@
+/*
+ * 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.keyboard.internal;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.TypedValue;
+import android.util.Xml;
+import android.view.InflateException;
+
+import com.android.inputmethod.keyboard.Key;
+import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.latin.LocaleUtils.RunInLocale;
+import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.ResourceUtils;
+import com.android.inputmethod.latin.StringUtils;
+import com.android.inputmethod.latin.SubtypeLocale;
+import com.android.inputmethod.latin.XmlParseUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Locale;
+
+/**
+ * Keyboard Building helper.
+ *
+ * This class parses Keyboard XML file and eventually build a Keyboard.
+ * The Keyboard XML file looks like:
+ * <pre>
+ * &lt;!-- xml/keyboard.xml --&gt;
+ * &lt;Keyboard keyboard_attributes*&gt;
+ * &lt;!-- Keyboard Content --&gt;
+ * &lt;Row row_attributes*&gt;
+ * &lt;!-- Row Content --&gt;
+ * &lt;Key key_attributes* /&gt;
+ * &lt;Spacer horizontalGap="32.0dp" /&gt;
+ * &lt;include keyboardLayout="@xml/other_keys"&gt;
+ * ...
+ * &lt;/Row&gt;
+ * &lt;include keyboardLayout="@xml/other_rows"&gt;
+ * ...
+ * &lt;/Keyboard&gt;
+ * </pre>
+ * The XML file which is included in other file must have &lt;merge&gt; as root element,
+ * such as:
+ * <pre>
+ * &lt;!-- xml/other_keys.xml --&gt;
+ * &lt;merge&gt;
+ * &lt;Key key_attributes* /&gt;
+ * ...
+ * &lt;/merge&gt;
+ * </pre>
+ * and
+ * <pre>
+ * &lt;!-- xml/other_rows.xml --&gt;
+ * &lt;merge&gt;
+ * &lt;Row row_attributes*&gt;
+ * &lt;Key key_attributes* /&gt;
+ * &lt;/Row&gt;
+ * ...
+ * &lt;/merge&gt;
+ * </pre>
+ * You can also use switch-case-default tags to select Rows and Keys.
+ * <pre>
+ * &lt;switch&gt;
+ * &lt;case case_attribute*&gt;
+ * &lt;!-- Any valid tags at switch position --&gt;
+ * &lt;/case&gt;
+ * ...
+ * &lt;default&gt;
+ * &lt;!-- Any valid tags at switch position --&gt;
+ * &lt;/default&gt;
+ * &lt;/switch&gt;
+ * </pre>
+ * You can declare Key style and specify styles within Key tags.
+ * <pre>
+ * &lt;switch&gt;
+ * &lt;case mode="email"&gt;
+ * &lt;key-style styleName="f1-key" parentStyle="modifier-key"
+ * keyLabel=".com"
+ * /&gt;
+ * &lt;/case&gt;
+ * &lt;case mode="url"&gt;
+ * &lt;key-style styleName="f1-key" parentStyle="modifier-key"
+ * keyLabel="http://"
+ * /&gt;
+ * &lt;/case&gt;
+ * &lt;/switch&gt;
+ * ...
+ * &lt;Key keyStyle="shift-key" ... /&gt;
+ * </pre>
+ */
+
+public class KeyboardBuilder<KP extends KeyboardParams> {
+ private static final String BUILDER_TAG = "Keyboard.Builder";
+ private static final boolean DEBUG = false;
+
+ // Keyboard XML Tags
+ private static final String TAG_KEYBOARD = "Keyboard";
+ private static final String TAG_ROW = "Row";
+ private static final String TAG_KEY = "Key";
+ private static final String TAG_SPACER = "Spacer";
+ private static final String TAG_INCLUDE = "include";
+ private static final String TAG_MERGE = "merge";
+ private static final String TAG_SWITCH = "switch";
+ private static final String TAG_CASE = "case";
+ private static final String TAG_DEFAULT = "default";
+ public static final String TAG_KEY_STYLE = "key-style";
+
+ private static final int DEFAULT_KEYBOARD_COLUMNS = 10;
+ private static final int DEFAULT_KEYBOARD_ROWS = 4;
+
+ protected final KP mParams;
+ protected final Context mContext;
+ protected final Resources mResources;
+ private final DisplayMetrics mDisplayMetrics;
+
+ private int mCurrentY = 0;
+ private KeyboardRow mCurrentRow = null;
+ private boolean mLeftEdge;
+ private boolean mTopEdge;
+ private Key mRightEdgeKey = null;
+
+ public KeyboardBuilder(final Context context, final KP params) {
+ mContext = context;
+ final Resources res = context.getResources();
+ mResources = res;
+ mDisplayMetrics = res.getDisplayMetrics();
+
+ mParams = params;
+
+ params.GRID_WIDTH = res.getInteger(R.integer.config_keyboard_grid_width);
+ params.GRID_HEIGHT = res.getInteger(R.integer.config_keyboard_grid_height);
+ }
+
+ public void setAutoGenerate(final KeysCache keysCache) {
+ mParams.mKeysCache = keysCache;
+ }
+
+ public KeyboardBuilder<KP> load(final int xmlId, final KeyboardId id) {
+ mParams.mId = id;
+ final XmlResourceParser parser = mResources.getXml(xmlId);
+ try {
+ parseKeyboard(parser);
+ } catch (XmlPullParserException e) {
+ Log.w(BUILDER_TAG, "keyboard XML parse error: " + e);
+ throw new IllegalArgumentException(e);
+ } catch (IOException e) {
+ Log.w(BUILDER_TAG, "keyboard XML parse error: " + e);
+ throw new RuntimeException(e);
+ } finally {
+ parser.close();
+ }
+ return this;
+ }
+
+ // For test only
+ public void disableTouchPositionCorrectionDataForTest() {
+ mParams.mTouchPositionCorrection.setEnabled(false);
+ }
+
+ public void setProximityCharsCorrectionEnabled(final boolean enabled) {
+ mParams.mProximityCharsCorrectionEnabled = enabled;
+ }
+
+ public Keyboard build() {
+ return new Keyboard(mParams);
+ }
+
+ private int mIndent;
+ private static final String SPACES = " ";
+
+ private static String spaces(final int count) {
+ return (count < SPACES.length()) ? SPACES.substring(0, count) : SPACES;
+ }
+
+ private void startTag(final String format, final Object ... args) {
+ Log.d(BUILDER_TAG, String.format(spaces(++mIndent * 2) + format, args));
+ }
+
+ private void endTag(final String format, final Object ... args) {
+ Log.d(BUILDER_TAG, String.format(spaces(mIndent-- * 2) + format, args));
+ }
+
+ private void startEndTag(final String format, final Object ... args) {
+ Log.d(BUILDER_TAG, String.format(spaces(++mIndent * 2) + format, args));
+ mIndent--;
+ }
+
+ private void parseKeyboard(final XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ if (DEBUG) startTag("<%s> %s", TAG_KEYBOARD, mParams.mId);
+ int event;
+ while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
+ if (event == XmlPullParser.START_TAG) {
+ final String tag = parser.getName();
+ if (TAG_KEYBOARD.equals(tag)) {
+ parseKeyboardAttributes(parser);
+ startKeyboard();
+ parseKeyboardContent(parser, false);
+ break;
+ } else {
+ throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEYBOARD);
+ }
+ }
+ }
+ }
+
+ private void parseKeyboardAttributes(final XmlPullParser parser) {
+ final int displayWidth = mDisplayMetrics.widthPixels;
+ final TypedArray keyboardAttr = mContext.obtainStyledAttributes(
+ Xml.asAttributeSet(parser), R.styleable.Keyboard, R.attr.keyboardStyle,
+ R.style.Keyboard);
+ final TypedArray keyAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser),
+ R.styleable.Keyboard_Key);
+ try {
+ final int displayHeight = mDisplayMetrics.heightPixels;
+ final String keyboardHeightString = ResourceUtils.getDeviceOverrideValue(
+ mResources, R.array.keyboard_heights, null);
+ final float keyboardHeight;
+ if (keyboardHeightString != null) {
+ keyboardHeight = Float.parseFloat(keyboardHeightString)
+ * mDisplayMetrics.density;
+ } else {
+ keyboardHeight = keyboardAttr.getDimension(
+ R.styleable.Keyboard_keyboardHeight, displayHeight / 2);
+ }
+ final float maxKeyboardHeight = ResourceUtils.getDimensionOrFraction(keyboardAttr,
+ R.styleable.Keyboard_maxKeyboardHeight, displayHeight, displayHeight / 2);
+ float minKeyboardHeight = ResourceUtils.getDimensionOrFraction(keyboardAttr,
+ R.styleable.Keyboard_minKeyboardHeight, displayHeight, displayHeight / 2);
+ if (minKeyboardHeight < 0) {
+ // Specified fraction was negative, so it should be calculated against display
+ // width.
+ minKeyboardHeight = -ResourceUtils.getDimensionOrFraction(keyboardAttr,
+ R.styleable.Keyboard_minKeyboardHeight, displayWidth, displayWidth / 2);
+ }
+ final KeyboardParams params = mParams;
+ // Keyboard height will not exceed maxKeyboardHeight and will not be less than
+ // minKeyboardHeight.
+ params.mOccupiedHeight = (int)Math.max(
+ Math.min(keyboardHeight, maxKeyboardHeight), minKeyboardHeight);
+ params.mOccupiedWidth = params.mId.mWidth;
+ params.mTopPadding = (int)ResourceUtils.getDimensionOrFraction(keyboardAttr,
+ R.styleable.Keyboard_keyboardTopPadding, params.mOccupiedHeight, 0);
+ params.mBottomPadding = (int)ResourceUtils.getDimensionOrFraction(keyboardAttr,
+ R.styleable.Keyboard_keyboardBottomPadding, params.mOccupiedHeight, 0);
+ params.mHorizontalEdgesPadding = (int)ResourceUtils.getDimensionOrFraction(
+ keyboardAttr,
+ R.styleable.Keyboard_keyboardHorizontalEdgesPadding,
+ mParams.mOccupiedWidth, 0);
+
+ params.mBaseWidth = params.mOccupiedWidth - params.mHorizontalEdgesPadding * 2
+ - params.mHorizontalCenterPadding;
+ params.mDefaultKeyWidth = (int)ResourceUtils.getDimensionOrFraction(keyAttr,
+ R.styleable.Keyboard_Key_keyWidth, params.mBaseWidth,
+ params.mBaseWidth / DEFAULT_KEYBOARD_COLUMNS);
+ params.mHorizontalGap = (int)ResourceUtils.getDimensionOrFraction(keyboardAttr,
+ R.styleable.Keyboard_horizontalGap, params.mBaseWidth, 0);
+ params.mVerticalGap = (int)ResourceUtils.getDimensionOrFraction(keyboardAttr,
+ R.styleable.Keyboard_verticalGap, params.mOccupiedHeight, 0);
+ params.mBaseHeight = params.mOccupiedHeight - params.mTopPadding
+ - params.mBottomPadding + params.mVerticalGap;
+ params.mDefaultRowHeight = (int)ResourceUtils.getDimensionOrFraction(keyboardAttr,
+ R.styleable.Keyboard_rowHeight, params.mBaseHeight,
+ params.mBaseHeight / DEFAULT_KEYBOARD_ROWS);
+
+ params.mKeyVisualAttributes = KeyVisualAttributes.newInstance(keyAttr);
+
+ params.mMoreKeysTemplate = keyboardAttr.getResourceId(
+ R.styleable.Keyboard_moreKeysTemplate, 0);
+ params.mMaxMoreKeysKeyboardColumn = keyAttr.getInt(
+ R.styleable.Keyboard_Key_maxMoreKeysColumn, 5);
+
+ params.mThemeId = keyboardAttr.getInt(R.styleable.Keyboard_themeId, 0);
+ params.mIconsSet.loadIcons(keyboardAttr);
+ final String language = params.mId.mLocale.getLanguage();
+ params.mCodesSet.setLanguage(language);
+ params.mTextsSet.setLanguage(language);
+ final RunInLocale<Void> job = new RunInLocale<Void>() {
+ @Override
+ protected Void job(Resources res) {
+ params.mTextsSet.loadStringResources(mContext);
+ return null;
+ }
+ };
+ // Null means the current system locale.
+ final Locale locale = SubtypeLocale.isNoLanguage(params.mId.mSubtype)
+ ? null : params.mId.mLocale;
+ job.runInLocale(mResources, locale);
+
+ final int resourceId = keyboardAttr.getResourceId(
+ R.styleable.Keyboard_touchPositionCorrectionData, 0);
+ if (resourceId != 0) {
+ final String[] data = mResources.getStringArray(resourceId);
+ params.mTouchPositionCorrection.load(data);
+ }
+ } finally {
+ keyAttr.recycle();
+ keyboardAttr.recycle();
+ }
+ }
+
+ private void parseKeyboardContent(final XmlPullParser parser, final boolean skip)
+ throws XmlPullParserException, IOException {
+ int event;
+ while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
+ if (event == XmlPullParser.START_TAG) {
+ final String tag = parser.getName();
+ if (TAG_ROW.equals(tag)) {
+ final KeyboardRow row = parseRowAttributes(parser);
+ if (DEBUG) startTag("<%s>%s", TAG_ROW, skip ? " skipped" : "");
+ if (!skip) {
+ startRow(row);
+ }
+ parseRowContent(parser, row, skip);
+ } else if (TAG_INCLUDE.equals(tag)) {
+ parseIncludeKeyboardContent(parser, skip);
+ } else if (TAG_SWITCH.equals(tag)) {
+ parseSwitchKeyboardContent(parser, skip);
+ } else if (TAG_KEY_STYLE.equals(tag)) {
+ parseKeyStyle(parser, skip);
+ } else {
+ throw new XmlParseUtils.IllegalStartTag(parser, TAG_ROW);
+ }
+ } else if (event == XmlPullParser.END_TAG) {
+ final String tag = parser.getName();
+ if (DEBUG) endTag("</%s>", tag);
+ if (TAG_KEYBOARD.equals(tag)) {
+ endKeyboard();
+ break;
+ } else if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag)
+ || TAG_MERGE.equals(tag)) {
+ break;
+ } else {
+ throw new XmlParseUtils.IllegalEndTag(parser, TAG_ROW);
+ }
+ }
+ }
+ }
+
+ private KeyboardRow parseRowAttributes(final XmlPullParser parser)
+ throws XmlPullParserException {
+ final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
+ R.styleable.Keyboard);
+ try {
+ if (a.hasValue(R.styleable.Keyboard_horizontalGap)) {
+ throw new XmlParseUtils.IllegalAttribute(parser, "horizontalGap");
+ }
+ if (a.hasValue(R.styleable.Keyboard_verticalGap)) {
+ throw new XmlParseUtils.IllegalAttribute(parser, "verticalGap");
+ }
+ return new KeyboardRow(mResources, mParams, parser, mCurrentY);
+ } finally {
+ a.recycle();
+ }
+ }
+
+ private void parseRowContent(final XmlPullParser parser, final KeyboardRow row,
+ final boolean skip) throws XmlPullParserException, IOException {
+ int event;
+ while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
+ if (event == XmlPullParser.START_TAG) {
+ final String tag = parser.getName();
+ if (TAG_KEY.equals(tag)) {
+ parseKey(parser, row, skip);
+ } else if (TAG_SPACER.equals(tag)) {
+ parseSpacer(parser, row, skip);
+ } else if (TAG_INCLUDE.equals(tag)) {
+ parseIncludeRowContent(parser, row, skip);
+ } else if (TAG_SWITCH.equals(tag)) {
+ parseSwitchRowContent(parser, row, skip);
+ } else if (TAG_KEY_STYLE.equals(tag)) {
+ parseKeyStyle(parser, skip);
+ } else {
+ throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEY);
+ }
+ } else if (event == XmlPullParser.END_TAG) {
+ final String tag = parser.getName();
+ if (DEBUG) endTag("</%s>", tag);
+ if (TAG_ROW.equals(tag)) {
+ if (!skip) {
+ endRow(row);
+ }
+ break;
+ } else if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag)
+ || TAG_MERGE.equals(tag)) {
+ break;
+ } else {
+ throw new XmlParseUtils.IllegalEndTag(parser, TAG_KEY);
+ }
+ }
+ }
+ }
+
+ private void parseKey(final XmlPullParser parser, final KeyboardRow row, final boolean skip)
+ throws XmlPullParserException, IOException {
+ if (skip) {
+ XmlParseUtils.checkEndTag(TAG_KEY, parser);
+ if (DEBUG) {
+ startEndTag("<%s /> skipped", TAG_KEY);
+ }
+ } else {
+ final Key key = new Key(mResources, mParams, row, parser);
+ if (DEBUG) {
+ startEndTag("<%s%s %s moreKeys=%s />", TAG_KEY,
+ (key.isEnabled() ? "" : " disabled"), key,
+ Arrays.toString(key.mMoreKeys));
+ }
+ XmlParseUtils.checkEndTag(TAG_KEY, parser);
+ endKey(key);
+ }
+ }
+
+ private void parseSpacer(final XmlPullParser parser, final KeyboardRow row, final boolean skip)
+ throws XmlPullParserException, IOException {
+ if (skip) {
+ XmlParseUtils.checkEndTag(TAG_SPACER, parser);
+ if (DEBUG) startEndTag("<%s /> skipped", TAG_SPACER);
+ } else {
+ final Key.Spacer spacer = new Key.Spacer(mResources, mParams, row, parser);
+ if (DEBUG) startEndTag("<%s />", TAG_SPACER);
+ XmlParseUtils.checkEndTag(TAG_SPACER, parser);
+ endKey(spacer);
+ }
+ }
+
+ private void parseIncludeKeyboardContent(final XmlPullParser parser, final boolean skip)
+ throws XmlPullParserException, IOException {
+ parseIncludeInternal(parser, null, skip);
+ }
+
+ private void parseIncludeRowContent(final XmlPullParser parser, final KeyboardRow row,
+ final boolean skip) throws XmlPullParserException, IOException {
+ parseIncludeInternal(parser, row, skip);
+ }
+
+ private void parseIncludeInternal(final XmlPullParser parser, final KeyboardRow row,
+ final boolean skip) throws XmlPullParserException, IOException {
+ if (skip) {
+ XmlParseUtils.checkEndTag(TAG_INCLUDE, parser);
+ if (DEBUG) startEndTag("</%s> skipped", TAG_INCLUDE);
+ } else {
+ final AttributeSet attr = Xml.asAttributeSet(parser);
+ final TypedArray keyboardAttr = mResources.obtainAttributes(attr,
+ R.styleable.Keyboard_Include);
+ final TypedArray keyAttr = mResources.obtainAttributes(attr,
+ R.styleable.Keyboard_Key);
+ int keyboardLayout = 0;
+ float savedDefaultKeyWidth = 0;
+ int savedDefaultKeyLabelFlags = 0;
+ int savedDefaultBackgroundType = Key.BACKGROUND_TYPE_NORMAL;
+ try {
+ XmlParseUtils.checkAttributeExists(keyboardAttr,
+ R.styleable.Keyboard_Include_keyboardLayout, "keyboardLayout",
+ TAG_INCLUDE, parser);
+ keyboardLayout = keyboardAttr.getResourceId(
+ R.styleable.Keyboard_Include_keyboardLayout, 0);
+ if (row != null) {
+ if (keyAttr.hasValue(R.styleable.Keyboard_Key_keyXPos)) {
+ // Override current x coordinate.
+ row.setXPos(row.getKeyX(keyAttr));
+ }
+ // TODO: Remove this if-clause and do the same as backgroundType below.
+ savedDefaultKeyWidth = row.getDefaultKeyWidth();
+ if (keyAttr.hasValue(R.styleable.Keyboard_Key_keyWidth)) {
+ // Override default key width.
+ row.setDefaultKeyWidth(row.getKeyWidth(keyAttr));
+ }
+ savedDefaultKeyLabelFlags = row.getDefaultKeyLabelFlags();
+ // Bitwise-or default keyLabelFlag if exists.
+ row.setDefaultKeyLabelFlags(keyAttr.getInt(
+ R.styleable.Keyboard_Key_keyLabelFlags, 0)
+ | savedDefaultKeyLabelFlags);
+ savedDefaultBackgroundType = row.getDefaultBackgroundType();
+ // Override default backgroundType if exists.
+ row.setDefaultBackgroundType(keyAttr.getInt(
+ R.styleable.Keyboard_Key_backgroundType,
+ savedDefaultBackgroundType));
+ }
+ } finally {
+ keyboardAttr.recycle();
+ keyAttr.recycle();
+ }
+
+ XmlParseUtils.checkEndTag(TAG_INCLUDE, parser);
+ if (DEBUG) {
+ startEndTag("<%s keyboardLayout=%s />",TAG_INCLUDE,
+ mResources.getResourceEntryName(keyboardLayout));
+ }
+ final XmlResourceParser parserForInclude = mResources.getXml(keyboardLayout);
+ try {
+ parseMerge(parserForInclude, row, skip);
+ } finally {
+ if (row != null) {
+ // Restore default keyWidth, keyLabelFlags, and backgroundType.
+ row.setDefaultKeyWidth(savedDefaultKeyWidth);
+ row.setDefaultKeyLabelFlags(savedDefaultKeyLabelFlags);
+ row.setDefaultBackgroundType(savedDefaultBackgroundType);
+ }
+ parserForInclude.close();
+ }
+ }
+ }
+
+ private void parseMerge(final XmlPullParser parser, final KeyboardRow row, final boolean skip)
+ throws XmlPullParserException, IOException {
+ if (DEBUG) startTag("<%s>", TAG_MERGE);
+ int event;
+ while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
+ if (event == XmlPullParser.START_TAG) {
+ final String tag = parser.getName();
+ if (TAG_MERGE.equals(tag)) {
+ if (row == null) {
+ parseKeyboardContent(parser, skip);
+ } else {
+ parseRowContent(parser, row, skip);
+ }
+ break;
+ } else {
+ throw new XmlParseUtils.ParseException(
+ "Included keyboard layout must have <merge> root element", parser);
+ }
+ }
+ }
+ }
+
+ private void parseSwitchKeyboardContent(final XmlPullParser parser, final boolean skip)
+ throws XmlPullParserException, IOException {
+ parseSwitchInternal(parser, null, skip);
+ }
+
+ private void parseSwitchRowContent(final XmlPullParser parser, final KeyboardRow row,
+ final boolean skip) throws XmlPullParserException, IOException {
+ parseSwitchInternal(parser, row, skip);
+ }
+
+ private void parseSwitchInternal(final XmlPullParser parser, final KeyboardRow row,
+ final boolean skip) throws XmlPullParserException, IOException {
+ if (DEBUG) startTag("<%s> %s", TAG_SWITCH, mParams.mId);
+ boolean selected = false;
+ int event;
+ while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) {
+ if (event == XmlPullParser.START_TAG) {
+ final String tag = parser.getName();
+ if (TAG_CASE.equals(tag)) {
+ selected |= parseCase(parser, row, selected ? true : skip);
+ } else if (TAG_DEFAULT.equals(tag)) {
+ selected |= parseDefault(parser, row, selected ? true : skip);
+ } else {
+ throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEY);
+ }
+ } else if (event == XmlPullParser.END_TAG) {
+ final String tag = parser.getName();
+ if (TAG_SWITCH.equals(tag)) {
+ if (DEBUG) endTag("</%s>", TAG_SWITCH);
+ break;
+ } else {
+ throw new XmlParseUtils.IllegalEndTag(parser, TAG_KEY);
+ }
+ }
+ }
+ }
+
+ private boolean parseCase(final XmlPullParser parser, final KeyboardRow row, final boolean skip)
+ throws XmlPullParserException, IOException {
+ final boolean selected = parseCaseCondition(parser);
+ if (row == null) {
+ // Processing Rows.
+ parseKeyboardContent(parser, selected ? skip : true);
+ } else {
+ // Processing Keys.
+ parseRowContent(parser, row, selected ? skip : true);
+ }
+ return selected;
+ }
+
+ private boolean parseCaseCondition(final XmlPullParser parser) {
+ final KeyboardId id = mParams.mId;
+ if (id == null) {
+ return true;
+ }
+ final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser),
+ R.styleable.Keyboard_Case);
+ try {
+ final boolean keyboardLayoutSetElementMatched = matchTypedValue(a,
+ R.styleable.Keyboard_Case_keyboardLayoutSetElement, id.mElementId,
+ KeyboardId.elementIdToName(id.mElementId));
+ final boolean modeMatched = matchTypedValue(a,
+ R.styleable.Keyboard_Case_mode, id.mMode, KeyboardId.modeName(id.mMode));
+ final boolean navigateNextMatched = matchBoolean(a,
+ R.styleable.Keyboard_Case_navigateNext, id.navigateNext());
+ final boolean navigatePreviousMatched = matchBoolean(a,
+ R.styleable.Keyboard_Case_navigatePrevious, id.navigatePrevious());
+ final boolean passwordInputMatched = matchBoolean(a,
+ R.styleable.Keyboard_Case_passwordInput, id.passwordInput());
+ final boolean clobberSettingsKeyMatched = matchBoolean(a,
+ R.styleable.Keyboard_Case_clobberSettingsKey, id.mClobberSettingsKey);
+ final boolean shortcutKeyEnabledMatched = matchBoolean(a,
+ R.styleable.Keyboard_Case_shortcutKeyEnabled, id.mShortcutKeyEnabled);
+ final boolean hasShortcutKeyMatched = matchBoolean(a,
+ R.styleable.Keyboard_Case_hasShortcutKey, id.mHasShortcutKey);
+ final boolean languageSwitchKeyEnabledMatched = matchBoolean(a,
+ R.styleable.Keyboard_Case_languageSwitchKeyEnabled,
+ id.mLanguageSwitchKeyEnabled);
+ final boolean isMultiLineMatched = matchBoolean(a,
+ R.styleable.Keyboard_Case_isMultiLine, id.isMultiLine());
+ final boolean imeActionMatched = matchInteger(a,
+ R.styleable.Keyboard_Case_imeAction, id.imeAction());
+ final boolean localeCodeMatched = matchString(a,
+ R.styleable.Keyboard_Case_localeCode, id.mLocale.toString());
+ final boolean languageCodeMatched = matchString(a,
+ R.styleable.Keyboard_Case_languageCode, id.mLocale.getLanguage());
+ final boolean countryCodeMatched = matchString(a,
+ R.styleable.Keyboard_Case_countryCode, id.mLocale.getCountry());
+ final boolean selected = keyboardLayoutSetElementMatched && modeMatched
+ && navigateNextMatched && navigatePreviousMatched && passwordInputMatched
+ && clobberSettingsKeyMatched && shortcutKeyEnabledMatched
+ && hasShortcutKeyMatched && languageSwitchKeyEnabledMatched
+ && isMultiLineMatched && imeActionMatched && localeCodeMatched
+ && languageCodeMatched && countryCodeMatched;
+
+ if (DEBUG) {
+ startTag("<%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s>%s", TAG_CASE,
+ textAttr(a.getString(
+ R.styleable.Keyboard_Case_keyboardLayoutSetElement),
+ "keyboardLayoutSetElement"),
+ textAttr(a.getString(R.styleable.Keyboard_Case_mode), "mode"),
+ textAttr(a.getString(R.styleable.Keyboard_Case_imeAction),
+ "imeAction"),
+ booleanAttr(a, R.styleable.Keyboard_Case_navigateNext,
+ "navigateNext"),
+ booleanAttr(a, R.styleable.Keyboard_Case_navigatePrevious,
+ "navigatePrevious"),
+ booleanAttr(a, R.styleable.Keyboard_Case_clobberSettingsKey,
+ "clobberSettingsKey"),
+ booleanAttr(a, R.styleable.Keyboard_Case_passwordInput,
+ "passwordInput"),
+ booleanAttr(a, R.styleable.Keyboard_Case_shortcutKeyEnabled,
+ "shortcutKeyEnabled"),
+ booleanAttr(a, R.styleable.Keyboard_Case_hasShortcutKey,
+ "hasShortcutKey"),
+ booleanAttr(a, R.styleable.Keyboard_Case_languageSwitchKeyEnabled,
+ "languageSwitchKeyEnabled"),
+ booleanAttr(a, R.styleable.Keyboard_Case_isMultiLine,
+ "isMultiLine"),
+ textAttr(a.getString(R.styleable.Keyboard_Case_localeCode),
+ "localeCode"),
+ textAttr(a.getString(R.styleable.Keyboard_Case_languageCode),
+ "languageCode"),
+ textAttr(a.getString(R.styleable.Keyboard_Case_countryCode),
+ "countryCode"),
+ selected ? "" : " skipped");
+ }
+
+ return selected;
+ } finally {
+ a.recycle();
+ }
+ }
+
+ private static boolean matchInteger(final TypedArray a, final int index, final int value) {
+ // If <case> does not have "index" attribute, that means this <case> is wild-card for
+ // the attribute.
+ return !a.hasValue(index) || a.getInt(index, 0) == value;
+ }
+
+ private static boolean matchBoolean(final TypedArray a, final int index, final boolean value) {
+ // If <case> does not have "index" attribute, that means this <case> is wild-card for
+ // the attribute.
+ return !a.hasValue(index) || a.getBoolean(index, false) == value;
+ }
+
+ private static boolean matchString(final TypedArray a, final int index, final String value) {
+ // If <case> does not have "index" attribute, that means this <case> is wild-card for
+ // the attribute.
+ return !a.hasValue(index)
+ || StringUtils.containsInArray(value, a.getString(index).split("\\|"));
+ }
+
+ private static boolean matchTypedValue(final TypedArray a, final int index, final int intValue,
+ final String strValue) {
+ // If <case> does not have "index" attribute, that means this <case> is wild-card for
+ // the attribute.
+ final TypedValue v = a.peekValue(index);
+ if (v == null) {
+ return true;
+ }
+ if (ResourceUtils.isIntegerValue(v)) {
+ return intValue == a.getInt(index, 0);
+ } else if (ResourceUtils.isStringValue(v)) {
+ return StringUtils.containsInArray(strValue, a.getString(index).split("\\|"));
+ }
+ return false;
+ }
+
+ private boolean parseDefault(final XmlPullParser parser, final KeyboardRow row,
+ final boolean skip) throws XmlPullParserException, IOException {
+ if (DEBUG) startTag("<%s>", TAG_DEFAULT);
+ if (row == null) {
+ parseKeyboardContent(parser, skip);
+ } else {
+ parseRowContent(parser, row, skip);
+ }
+ return true;
+ }
+
+ private void parseKeyStyle(final XmlPullParser parser, final boolean skip)
+ throws XmlPullParserException, IOException {
+ TypedArray keyStyleAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser),
+ R.styleable.Keyboard_KeyStyle);
+ TypedArray keyAttrs = mResources.obtainAttributes(Xml.asAttributeSet(parser),
+ R.styleable.Keyboard_Key);
+ try {
+ if (!keyStyleAttr.hasValue(R.styleable.Keyboard_KeyStyle_styleName)) {
+ throw new XmlParseUtils.ParseException("<" + TAG_KEY_STYLE
+ + "/> needs styleName attribute", parser);
+ }
+ if (DEBUG) {
+ startEndTag("<%s styleName=%s />%s", TAG_KEY_STYLE,
+ keyStyleAttr.getString(R.styleable.Keyboard_KeyStyle_styleName),
+ skip ? " skipped" : "");
+ }
+ if (!skip) {
+ mParams.mKeyStyles.parseKeyStyleAttributes(keyStyleAttr, keyAttrs, parser);
+ }
+ } finally {
+ keyStyleAttr.recycle();
+ keyAttrs.recycle();
+ }
+ XmlParseUtils.checkEndTag(TAG_KEY_STYLE, parser);
+ }
+
+ private void startKeyboard() {
+ mCurrentY += mParams.mTopPadding;
+ mTopEdge = true;
+ }
+
+ private void startRow(final KeyboardRow row) {
+ addEdgeSpace(mParams.mHorizontalEdgesPadding, row);
+ mCurrentRow = row;
+ mLeftEdge = true;
+ mRightEdgeKey = null;
+ }
+
+ private void endRow(final KeyboardRow row) {
+ if (mCurrentRow == null) {
+ throw new InflateException("orphan end row tag");
+ }
+ if (mRightEdgeKey != null) {
+ mRightEdgeKey.markAsRightEdge(mParams);
+ mRightEdgeKey = null;
+ }
+ addEdgeSpace(mParams.mHorizontalEdgesPadding, row);
+ mCurrentY += row.mRowHeight;
+ mCurrentRow = null;
+ mTopEdge = false;
+ }
+
+ private void endKey(final Key key) {
+ mParams.onAddKey(key);
+ if (mLeftEdge) {
+ key.markAsLeftEdge(mParams);
+ mLeftEdge = false;
+ }
+ if (mTopEdge) {
+ key.markAsTopEdge(mParams);
+ }
+ mRightEdgeKey = key;
+ }
+
+ private void endKeyboard() {
+ // nothing to do here.
+ }
+
+ private void addEdgeSpace(final float width, final KeyboardRow row) {
+ row.advanceXPos(width);
+ mLeftEdge = false;
+ mRightEdgeKey = null;
+ }
+
+ private static String textAttr(final String value, final String name) {
+ return value != null ? String.format(" %s=%s", name, value) : "";
+ }
+
+ private static String booleanAttr(final TypedArray a, final int index, final String name) {
+ return a.hasValue(index)
+ ? String.format(" %s=%s", name, a.getBoolean(index, false)) : "";
+ }
+}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java
index 67cb74f4d..840d7133d 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java
@@ -17,13 +17,13 @@
package com.android.inputmethod.keyboard.internal;
import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.latin.CollectionUtils;
import java.util.HashMap;
-public class KeyboardCodesSet {
- private static final HashMap<String, int[]> sLanguageToCodesMap =
- new HashMap<String, int[]>();
- private static final HashMap<String, Integer> sNameToIdMap = new HashMap<String, Integer>();
+public final class KeyboardCodesSet {
+ private static final HashMap<String, int[]> sLanguageToCodesMap = CollectionUtils.newHashMap();
+ private static final HashMap<String, Integer> sNameToIdMap = CollectionUtils.newHashMap();
private int[] mCodes = DEFAULT;
@@ -52,6 +52,7 @@ public class KeyboardCodesSet {
"key_action_next",
"key_action_previous",
"key_language_switch",
+ "key_research",
"key_unspecified",
"key_left_parenthesis",
"key_right_parenthesis",
@@ -86,6 +87,7 @@ public class KeyboardCodesSet {
Keyboard.CODE_ACTION_NEXT,
Keyboard.CODE_ACTION_PREVIOUS,
Keyboard.CODE_LANGUAGE_SWITCH,
+ Keyboard.CODE_RESEARCH,
Keyboard.CODE_UNSPECIFIED,
CODE_LEFT_PARENTHESIS,
CODE_RIGHT_PARENTHESIS,
@@ -112,6 +114,7 @@ public class KeyboardCodesSet {
DEFAULT[11],
DEFAULT[12],
DEFAULT[13],
+ DEFAULT[14],
CODE_RIGHT_PARENTHESIS,
CODE_LEFT_PARENTHESIS,
CODE_GREATER_THAN_SIGN,
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java
index 540e63b3f..7292e8e19 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java
@@ -20,22 +20,23 @@ import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.util.Log;
+import android.util.SparseIntArray;
+import com.android.inputmethod.latin.CollectionUtils;
import com.android.inputmethod.latin.R;
import java.util.HashMap;
-public class KeyboardIconsSet {
+public final class KeyboardIconsSet {
private static final String TAG = KeyboardIconsSet.class.getSimpleName();
public static final int ICON_UNDEFINED = 0;
private static final int ATTR_UNDEFINED = 0;
- private static final HashMap<Integer, Integer> ATTR_ID_TO_ICON_ID
- = new HashMap<Integer, Integer>();
+ private static final SparseIntArray ATTR_ID_TO_ICON_ID = new SparseIntArray();
// Icon name to icon id map.
- private static final HashMap<String, Integer> sNameToIdsMap = new HashMap<String, Integer>();
+ private static final HashMap<String, Integer> sNameToIdsMap = CollectionUtils.newHashMap();
private static final Object[] NAMES_AND_ATTR_IDS = {
"undefined", ATTR_UNDEFINED,
@@ -76,7 +77,9 @@ public class KeyboardIconsSet {
}
public void loadIcons(final TypedArray keyboardAttrs) {
- for (final Integer attrId : ATTR_ID_TO_ICON_ID.keySet()) {
+ final int size = ATTR_ID_TO_ICON_ID.size();
+ for (int index = 0; index < size; index++) {
+ final int attrId = ATTR_ID_TO_ICON_ID.keyAt(index);
try {
final Drawable icon = keyboardAttrs.getDrawable(attrId);
setDefaultBounds(icon);
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java
new file mode 100644
index 000000000..e6fe50e02
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java
@@ -0,0 +1,137 @@
+/*
+ * 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.keyboard.internal;
+
+import android.util.SparseIntArray;
+
+import com.android.inputmethod.keyboard.Key;
+import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.latin.CollectionUtils;
+
+import java.util.ArrayList;
+import java.util.TreeSet;
+
+public class KeyboardParams {
+ public KeyboardId mId;
+ public int mThemeId;
+
+ /** Total height and width of the keyboard, including the paddings and keys */
+ public int mOccupiedHeight;
+ public int mOccupiedWidth;
+
+ /** Base height and width of the keyboard used to calculate rows' or keys' heights and
+ * widths
+ */
+ public int mBaseHeight;
+ public int mBaseWidth;
+
+ public int mTopPadding;
+ public int mBottomPadding;
+ public int mHorizontalEdgesPadding;
+ public int mHorizontalCenterPadding;
+
+ public KeyVisualAttributes mKeyVisualAttributes;
+
+ public int mDefaultRowHeight;
+ public int mDefaultKeyWidth;
+ public int mHorizontalGap;
+ public int mVerticalGap;
+
+ public int mMoreKeysTemplate;
+ public int mMaxMoreKeysKeyboardColumn;
+
+ public int GRID_WIDTH;
+ public int GRID_HEIGHT;
+
+ public final TreeSet<Key> mKeys = CollectionUtils.newTreeSet(); // ordered set
+ public final ArrayList<Key> mShiftKeys = CollectionUtils.newArrayList();
+ public final ArrayList<Key> mAltCodeKeysWhileTyping = CollectionUtils.newArrayList();
+ public final KeyboardIconsSet mIconsSet = new KeyboardIconsSet();
+ public final KeyboardCodesSet mCodesSet = new KeyboardCodesSet();
+ public final KeyboardTextsSet mTextsSet = new KeyboardTextsSet();
+ public final KeyStylesSet mKeyStyles = new KeyStylesSet(mTextsSet);
+
+ public KeysCache mKeysCache;
+
+ public int mMostCommonKeyHeight = 0;
+ public int mMostCommonKeyWidth = 0;
+
+ public boolean mProximityCharsCorrectionEnabled;
+
+ public final TouchPositionCorrection mTouchPositionCorrection =
+ new TouchPositionCorrection();
+
+ protected void clearKeys() {
+ mKeys.clear();
+ mShiftKeys.clear();
+ clearHistogram();
+ }
+
+ public void onAddKey(final Key newKey) {
+ final Key key = (mKeysCache != null) ? mKeysCache.get(newKey) : newKey;
+ final boolean zeroWidthSpacer = key.isSpacer() && key.mWidth == 0;
+ if (!zeroWidthSpacer) {
+ mKeys.add(key);
+ updateHistogram(key);
+ }
+ if (key.mCode == Keyboard.CODE_SHIFT) {
+ mShiftKeys.add(key);
+ }
+ if (key.altCodeWhileTyping()) {
+ mAltCodeKeysWhileTyping.add(key);
+ }
+ }
+
+ private int mMaxHeightCount = 0;
+ private int mMaxWidthCount = 0;
+ private final SparseIntArray mHeightHistogram = new SparseIntArray();
+ private final SparseIntArray mWidthHistogram = new SparseIntArray();
+
+ private void clearHistogram() {
+ mMostCommonKeyHeight = 0;
+ mMaxHeightCount = 0;
+ mHeightHistogram.clear();
+
+ mMaxWidthCount = 0;
+ mMostCommonKeyWidth = 0;
+ mWidthHistogram.clear();
+ }
+
+ private static int updateHistogramCounter(final SparseIntArray histogram, final int key) {
+ final int index = histogram.indexOfKey(key);
+ final int count = (index >= 0 ? histogram.get(key) : 0) + 1;
+ histogram.put(key, count);
+ return count;
+ }
+
+ private void updateHistogram(final Key key) {
+ final int height = key.mHeight + mVerticalGap;
+ final int heightCount = updateHistogramCounter(mHeightHistogram, height);
+ if (heightCount > mMaxHeightCount) {
+ mMaxHeightCount = heightCount;
+ mMostCommonKeyHeight = height;
+ }
+
+ final int width = key.mWidth + mHorizontalGap;
+ final int widthCount = updateHistogramCounter(mWidthHistogram, width);
+ if (widthCount > mMaxWidthCount) {
+ mMaxWidthCount = widthCount;
+ mMostCommonKeyWidth = width;
+ }
+ }
+}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardRow.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardRow.java
new file mode 100644
index 000000000..b986262d7
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardRow.java
@@ -0,0 +1,154 @@
+/*
+ * 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.keyboard.internal;
+
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.util.Xml;
+
+import com.android.inputmethod.keyboard.Key;
+import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.ResourceUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+
+/**
+ * Container for keys in the keyboard. All keys in a row are at the same Y-coordinate.
+ * Some of the key size defaults can be overridden per row from what the {@link Keyboard}
+ * defines.
+ */
+public final class KeyboardRow {
+ // keyWidth enum constants
+ private static final int KEYWIDTH_NOT_ENUM = 0;
+ private static final int KEYWIDTH_FILL_RIGHT = -1;
+
+ private final KeyboardParams mParams;
+ /** Default width of a key in this row. */
+ private float mDefaultKeyWidth;
+ /** Default height of a key in this row. */
+ public final int mRowHeight;
+ /** Default keyLabelFlags in this row. */
+ private int mDefaultKeyLabelFlags;
+ /** Default backgroundType for this row */
+ private int mDefaultBackgroundType;
+
+ private final int mCurrentY;
+ // Will be updated by {@link Key}'s constructor.
+ private float mCurrentX;
+
+ public KeyboardRow(final Resources res, final KeyboardParams params, final XmlPullParser parser,
+ final int y) {
+ mParams = params;
+ TypedArray keyboardAttr = res.obtainAttributes(Xml.asAttributeSet(parser),
+ R.styleable.Keyboard);
+ mRowHeight = (int)ResourceUtils.getDimensionOrFraction(keyboardAttr,
+ R.styleable.Keyboard_rowHeight,
+ params.mBaseHeight, params.mDefaultRowHeight);
+ keyboardAttr.recycle();
+ TypedArray keyAttr = res.obtainAttributes(Xml.asAttributeSet(parser),
+ R.styleable.Keyboard_Key);
+ mDefaultKeyWidth = ResourceUtils.getDimensionOrFraction(keyAttr,
+ R.styleable.Keyboard_Key_keyWidth,
+ params.mBaseWidth, params.mDefaultKeyWidth);
+ mDefaultBackgroundType = keyAttr.getInt(R.styleable.Keyboard_Key_backgroundType,
+ Key.BACKGROUND_TYPE_NORMAL);
+ keyAttr.recycle();
+
+ // TODO: Initialize this with <Row> attribute as backgroundType is done.
+ mDefaultKeyLabelFlags = 0;
+ mCurrentY = y;
+ mCurrentX = 0.0f;
+ }
+
+ public float getDefaultKeyWidth() {
+ return mDefaultKeyWidth;
+ }
+
+ public void setDefaultKeyWidth(final float defaultKeyWidth) {
+ mDefaultKeyWidth = defaultKeyWidth;
+ }
+
+ public int getDefaultKeyLabelFlags() {
+ return mDefaultKeyLabelFlags;
+ }
+
+ public void setDefaultKeyLabelFlags(final int keyLabelFlags) {
+ mDefaultKeyLabelFlags = keyLabelFlags;
+ }
+
+ public int getDefaultBackgroundType() {
+ return mDefaultBackgroundType;
+ }
+
+ public void setDefaultBackgroundType(final int backgroundType) {
+ mDefaultBackgroundType = backgroundType;
+ }
+
+ public void setXPos(final float keyXPos) {
+ mCurrentX = keyXPos;
+ }
+
+ public void advanceXPos(final float width) {
+ mCurrentX += width;
+ }
+
+ public int getKeyY() {
+ return mCurrentY;
+ }
+
+ public float getKeyX(final TypedArray keyAttr) {
+ final int keyboardRightEdge = mParams.mOccupiedWidth
+ - mParams.mHorizontalEdgesPadding;
+ if (keyAttr.hasValue(R.styleable.Keyboard_Key_keyXPos)) {
+ final float keyXPos = ResourceUtils.getDimensionOrFraction(keyAttr,
+ R.styleable.Keyboard_Key_keyXPos, mParams.mBaseWidth, 0);
+ if (keyXPos < 0) {
+ // If keyXPos is negative, the actual x-coordinate will be
+ // keyboardWidth + keyXPos.
+ // keyXPos shouldn't be less than mCurrentX because drawable area for this
+ // key starts at mCurrentX. Or, this key will overlaps the adjacent key on
+ // its left hand side.
+ return Math.max(keyXPos + keyboardRightEdge, mCurrentX);
+ } else {
+ return keyXPos + mParams.mHorizontalEdgesPadding;
+ }
+ }
+ return mCurrentX;
+ }
+
+ public float getKeyWidth(final TypedArray keyAttr) {
+ return getKeyWidth(keyAttr, mCurrentX);
+ }
+
+ public float getKeyWidth(final TypedArray keyAttr, final float keyXPos) {
+ final int widthType = ResourceUtils.getEnumValue(keyAttr,
+ R.styleable.Keyboard_Key_keyWidth, KEYWIDTH_NOT_ENUM);
+ switch (widthType) {
+ case KEYWIDTH_FILL_RIGHT:
+ final int keyboardRightEdge =
+ mParams.mOccupiedWidth - mParams.mHorizontalEdgesPadding;
+ // If keyWidth is fillRight, the actual key width will be determined to fill
+ // out the area up to the right edge of the keyboard.
+ return keyboardRightEdge - keyXPos;
+ default: // KEYWIDTH_NOT_ENUM
+ return ResourceUtils.getDimensionOrFraction(keyAttr,
+ R.styleable.Keyboard_Key_keyWidth,
+ mParams.mBaseWidth, mDefaultKeyWidth);
+ }
+ }
+}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java
index 43ffb85f7..58cc8972a 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java
@@ -21,8 +21,6 @@ import android.util.Log;
import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.latin.Constants;
-import com.android.inputmethod.latin.ResearchLogger;
-import com.android.inputmethod.latin.define.ProductionFlag;
/**
* Keyboard state machine.
@@ -36,7 +34,7 @@ import com.android.inputmethod.latin.define.ProductionFlag;
*
* The actions are {@link SwitchActions}'s methods.
*/
-public class KeyboardState {
+public final class KeyboardState {
private static final String TAG = KeyboardState.class.getSimpleName();
private static final boolean DEBUG_EVENT = false;
private static final boolean DEBUG_ACTION = false;
@@ -94,7 +92,7 @@ public class KeyboardState {
private final SavedKeyboardState mSavedKeyboardState = new SavedKeyboardState();
- static class SavedKeyboardState {
+ static final class SavedKeyboardState {
public boolean mIsValid;
public boolean mIsAlphabetMode;
public boolean mIsAlphabetShiftLocked;
@@ -305,9 +303,6 @@ public class KeyboardState {
Log.d(TAG, "onPressKey: code=" + Keyboard.printableCode(code)
+ " single=" + isSinglePointer + " autoCaps=" + autoCaps + " " + this);
}
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.keyboardState_onPressKey(code, this);
- }
if (code == Keyboard.CODE_SHIFT) {
onPressShift();
} else if (code == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) {
@@ -341,9 +336,6 @@ public class KeyboardState {
Log.d(TAG, "onReleaseKey: code=" + Keyboard.printableCode(code)
+ " sliding=" + withSliding + " " + this);
}
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.keyboardState_onReleaseKey(this, code, withSliding);
- }
if (code == Keyboard.CODE_SHIFT) {
onReleaseShift(withSliding);
} else if (code == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) {
@@ -375,9 +367,6 @@ public class KeyboardState {
if (DEBUG_EVENT) {
Log.d(TAG, "onLongPressTimeout: code=" + Keyboard.printableCode(code) + " " + this);
}
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.keyboardState_onLongPressTimeout(code, this);
- }
if (mIsAlphabetMode && code == Keyboard.CODE_SHIFT) {
mLongPressShiftLockFired = true;
mSwitchActions.hapticAndAudioFeedback(code);
@@ -509,9 +498,6 @@ public class KeyboardState {
if (DEBUG_EVENT) {
Log.d(TAG, "onCancelInput: single=" + isSinglePointer + " " + this);
}
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.keyboardState_onCancelInput(isSinglePointer, this);
- }
// Switch back to the previous keyboard mode if the user cancels sliding input.
if (isSinglePointer) {
if (mSwitchState == SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL) {
@@ -543,9 +529,6 @@ public class KeyboardState {
+ " single=" + isSinglePointer
+ " autoCaps=" + autoCaps + " " + this);
}
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.keyboardState_onCodeInput(code, isSinglePointer, autoCaps, this);
- }
switch (mSwitchState) {
case SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL:
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
index d762797dd..3bb272f8c 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
@@ -19,6 +19,7 @@ package com.android.inputmethod.keyboard.internal;
import android.content.Context;
import android.content.res.Resources;
+import com.android.inputmethod.latin.CollectionUtils;
import com.android.inputmethod.latin.R;
import java.util.HashMap;
@@ -45,14 +46,12 @@ import java.util.HashMap;
*/
public final class KeyboardTextsSet {
// Language to texts map.
- private static final HashMap<String, String[]> sLocaleToTextsMap =
- new HashMap<String, String[]>();
- private static final HashMap<String, Integer> sNameToIdsMap =
- new HashMap<String, Integer>();
+ private static final HashMap<String, String[]> sLocaleToTextsMap = CollectionUtils.newHashMap();
+ private static final HashMap<String, Integer> sNameToIdsMap = CollectionUtils.newHashMap();
private String[] mTexts;
// Resource name to text map.
- private HashMap<String, String> mResourceNameToTextsMap = new HashMap<String, String>();
+ private HashMap<String, String> mResourceNameToTextsMap = CollectionUtils.newHashMap();
public void setLanguage(final String language) {
mTexts = sLocaleToTextsMap.get(language);
@@ -131,71 +130,71 @@ public final class KeyboardTextsSet {
/* 23 */ "more_keys_for_nordic_row2_10",
/* 24 */ "more_keys_for_nordic_row2_11",
/* 25 */ "keylabel_for_east_slavic_row1_9",
- /* 26 */ "keylabel_for_east_slavic_row2_1",
- /* 27 */ "keylabel_for_east_slavic_row3_5",
- /* 28 */ "more_keys_for_cyrillic_u",
- /* 29 */ "more_keys_for_cyrillic_ye",
- /* 30 */ "more_keys_for_cyrillic_en",
- /* 31 */ "more_keys_for_cyrillic_ha",
- /* 32 */ "more_keys_for_east_slavic_row2_1",
- /* 33 */ "more_keys_for_cyrillic_o",
- /* 34 */ "more_keys_for_cyrillic_soft_sign",
- /* 35 */ "keylabel_for_south_slavic_row1_6",
- /* 36 */ "keylabel_for_south_slavic_row2_11",
- /* 37 */ "keylabel_for_south_slavic_row3_1",
- /* 38 */ "keylabel_for_south_slavic_row3_8",
- /* 39 */ "more_keys_for_cyrillic_ie",
- /* 40 */ "more_keys_for_cyrillic_i",
- /* 41 */ "more_keys_for_single_quote",
- /* 42 */ "more_keys_for_double_quote",
- /* 43 */ "more_keys_for_tablet_double_quote",
- /* 44 */ "more_keys_for_currency_dollar",
- /* 45 */ "more_keys_for_currency_euro",
- /* 46 */ "more_keys_for_currency_pound",
- /* 47 */ "more_keys_for_currency_general",
- /* 48 */ "more_keys_for_punctuation",
- /* 49 */ "more_keys_for_star",
- /* 50 */ "more_keys_for_bullet",
- /* 51 */ "more_keys_for_plus",
- /* 52 */ "more_keys_for_left_parenthesis",
- /* 53 */ "more_keys_for_right_parenthesis",
- /* 54 */ "more_keys_for_less_than",
- /* 55 */ "more_keys_for_greater_than",
- /* 56 */ "more_keys_for_arabic_diacritics",
- /* 57 */ "keyhintlabel_for_arabic_diacritics",
- /* 58 */ "keylabel_for_symbols_1",
- /* 59 */ "keylabel_for_symbols_2",
- /* 60 */ "keylabel_for_symbols_3",
- /* 61 */ "keylabel_for_symbols_4",
- /* 62 */ "keylabel_for_symbols_5",
- /* 63 */ "keylabel_for_symbols_6",
- /* 64 */ "keylabel_for_symbols_7",
- /* 65 */ "keylabel_for_symbols_8",
- /* 66 */ "keylabel_for_symbols_9",
- /* 67 */ "keylabel_for_symbols_0",
- /* 68 */ "additional_more_keys_for_symbols_1",
- /* 69 */ "additional_more_keys_for_symbols_2",
- /* 70 */ "additional_more_keys_for_symbols_3",
- /* 71 */ "additional_more_keys_for_symbols_4",
- /* 72 */ "additional_more_keys_for_symbols_5",
- /* 73 */ "additional_more_keys_for_symbols_6",
- /* 74 */ "additional_more_keys_for_symbols_7",
- /* 75 */ "additional_more_keys_for_symbols_8",
- /* 76 */ "additional_more_keys_for_symbols_9",
- /* 77 */ "additional_more_keys_for_symbols_0",
- /* 78 */ "more_keys_for_symbols_1",
- /* 79 */ "more_keys_for_symbols_2",
- /* 80 */ "more_keys_for_symbols_3",
- /* 81 */ "more_keys_for_symbols_4",
- /* 82 */ "more_keys_for_symbols_5",
- /* 83 */ "more_keys_for_symbols_6",
- /* 84 */ "more_keys_for_symbols_7",
- /* 85 */ "more_keys_for_symbols_8",
- /* 86 */ "more_keys_for_symbols_9",
- /* 87 */ "more_keys_for_symbols_0",
- /* 88 */ "keylabel_for_comma",
- /* 89 */ "more_keys_for_comma",
- /* 90 */ "keylabel_for_symbols_exclamation",
+ /* 26 */ "keylabel_for_east_slavic_row1_12",
+ /* 27 */ "keylabel_for_east_slavic_row2_1",
+ /* 28 */ "keylabel_for_east_slavic_row2_11",
+ /* 29 */ "keylabel_for_east_slavic_row3_5",
+ /* 30 */ "more_keys_for_cyrillic_u",
+ /* 31 */ "more_keys_for_cyrillic_en",
+ /* 32 */ "more_keys_for_cyrillic_ghe",
+ /* 33 */ "more_keys_for_east_slavic_row2_1",
+ /* 34 */ "more_keys_for_cyrillic_o",
+ /* 35 */ "more_keys_for_cyrillic_soft_sign",
+ /* 36 */ "keylabel_for_south_slavic_row1_6",
+ /* 37 */ "keylabel_for_south_slavic_row2_11",
+ /* 38 */ "keylabel_for_south_slavic_row3_1",
+ /* 39 */ "keylabel_for_south_slavic_row3_8",
+ /* 40 */ "more_keys_for_cyrillic_ie",
+ /* 41 */ "more_keys_for_cyrillic_i",
+ /* 42 */ "more_keys_for_single_quote",
+ /* 43 */ "more_keys_for_double_quote",
+ /* 44 */ "more_keys_for_tablet_double_quote",
+ /* 45 */ "more_keys_for_currency_dollar",
+ /* 46 */ "more_keys_for_currency_euro",
+ /* 47 */ "more_keys_for_currency_pound",
+ /* 48 */ "more_keys_for_currency_general",
+ /* 49 */ "more_keys_for_punctuation",
+ /* 50 */ "more_keys_for_star",
+ /* 51 */ "more_keys_for_bullet",
+ /* 52 */ "more_keys_for_plus",
+ /* 53 */ "more_keys_for_left_parenthesis",
+ /* 54 */ "more_keys_for_right_parenthesis",
+ /* 55 */ "more_keys_for_less_than",
+ /* 56 */ "more_keys_for_greater_than",
+ /* 57 */ "more_keys_for_arabic_diacritics",
+ /* 58 */ "keyhintlabel_for_arabic_diacritics",
+ /* 59 */ "keylabel_for_symbols_1",
+ /* 60 */ "keylabel_for_symbols_2",
+ /* 61 */ "keylabel_for_symbols_3",
+ /* 62 */ "keylabel_for_symbols_4",
+ /* 63 */ "keylabel_for_symbols_5",
+ /* 64 */ "keylabel_for_symbols_6",
+ /* 65 */ "keylabel_for_symbols_7",
+ /* 66 */ "keylabel_for_symbols_8",
+ /* 67 */ "keylabel_for_symbols_9",
+ /* 68 */ "keylabel_for_symbols_0",
+ /* 69 */ "additional_more_keys_for_symbols_1",
+ /* 70 */ "additional_more_keys_for_symbols_2",
+ /* 71 */ "additional_more_keys_for_symbols_3",
+ /* 72 */ "additional_more_keys_for_symbols_4",
+ /* 73 */ "additional_more_keys_for_symbols_5",
+ /* 74 */ "additional_more_keys_for_symbols_6",
+ /* 75 */ "additional_more_keys_for_symbols_7",
+ /* 76 */ "additional_more_keys_for_symbols_8",
+ /* 77 */ "additional_more_keys_for_symbols_9",
+ /* 78 */ "additional_more_keys_for_symbols_0",
+ /* 79 */ "more_keys_for_symbols_1",
+ /* 80 */ "more_keys_for_symbols_2",
+ /* 81 */ "more_keys_for_symbols_3",
+ /* 82 */ "more_keys_for_symbols_4",
+ /* 83 */ "more_keys_for_symbols_5",
+ /* 84 */ "more_keys_for_symbols_6",
+ /* 85 */ "more_keys_for_symbols_7",
+ /* 86 */ "more_keys_for_symbols_8",
+ /* 87 */ "more_keys_for_symbols_9",
+ /* 88 */ "more_keys_for_symbols_0",
+ /* 89 */ "keylabel_for_comma",
+ /* 90 */ "more_keys_for_comma",
/* 91 */ "keylabel_for_symbols_question",
/* 92 */ "keylabel_for_symbols_semicolon",
/* 93 */ "keylabel_for_symbols_percent",
@@ -211,22 +210,29 @@ public final class KeyboardTextsSet {
/* 103 */ "keylabel_for_apostrophe",
/* 104 */ "keyhintlabel_for_apostrophe",
/* 105 */ "more_keys_for_apostrophe",
- /* 106 */ "more_keys_for_am_pm",
- /* 107 */ "settings_as_more_key",
- /* 108 */ "shortcut_as_more_key",
- /* 109 */ "action_next_as_more_key",
- /* 110 */ "action_previous_as_more_key",
- /* 111 */ "label_to_more_symbol_key",
- /* 112 */ "label_to_more_symbol_for_tablet_key",
- /* 113 */ "label_tab_key",
- /* 114 */ "label_to_phone_numeric_key",
- /* 115 */ "label_to_phone_symbols_key",
- /* 116 */ "label_time_am",
- /* 117 */ "label_time_pm",
- /* 118 */ "label_to_symbol_key_pcqwerty",
- /* 119 */ "keylabel_for_popular_domain",
- /* 120 */ "more_keys_for_popular_domain",
- /* 121 */ "more_keys_for_smiley",
+ /* 106 */ "more_keys_for_q",
+ /* 107 */ "more_keys_for_x",
+ /* 108 */ "keylabel_for_q",
+ /* 109 */ "keylabel_for_w",
+ /* 110 */ "keylabel_for_y",
+ /* 111 */ "keylabel_for_x",
+ /* 112 */ "keylabel_for_spanish_row2_10",
+ /* 113 */ "more_keys_for_am_pm",
+ /* 114 */ "settings_as_more_key",
+ /* 115 */ "shortcut_as_more_key",
+ /* 116 */ "action_next_as_more_key",
+ /* 117 */ "action_previous_as_more_key",
+ /* 118 */ "label_to_more_symbol_key",
+ /* 119 */ "label_to_more_symbol_for_tablet_key",
+ /* 120 */ "label_tab_key",
+ /* 121 */ "label_to_phone_numeric_key",
+ /* 122 */ "label_to_phone_symbols_key",
+ /* 123 */ "label_time_am",
+ /* 124 */ "label_time_pm",
+ /* 125 */ "label_to_symbol_key_pcqwerty",
+ /* 126 */ "keylabel_for_popular_domain",
+ /* 127 */ "more_keys_for_popular_domain",
+ /* 128 */ "more_keys_for_smiley",
};
private static final String EMPTY = "";
@@ -237,41 +243,41 @@ public final class KeyboardTextsSet {
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
- EMPTY, EMPTY,
- /* ~40 */
- /* 41 */ "!fixedColumnOrder!4,\u2018,\u2019,\u201A,\u201B",
+ EMPTY, EMPTY, EMPTY,
+ /* ~41 */
+ /* 42 */ "!fixedColumnOrder!4,\u2018,\u2019,\u201A,\u201B",
// TODO: Neither DroidSans nor Roboto have the glyph for U+201F DOUBLE HIGH-REVERSED-9 QUOTATION MARK.
// <string name="more_keys_for_double_quote">!fixedColumnOrder!6,&#x201C;,&#x201D;,&#x201E;,&#x201F;,&#x00AB;,&#x00BB;</string>
- /* 42 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB,\u00BB",
+ /* 43 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB,\u00BB",
// TODO: Neither DroidSans nor Roboto have the glyph for U+201F DOUBLE HIGH-REVERSED-9 QUOTATION MARK.
// <string name="more_keys_for_tablet_double_quote">!fixedColumnOrder!6,&#x201C;,&#x201D;,&#x201E;,&#x201F;,&#x00AB;,&#x00BB;,&#x2018;,&#x2019;,&#x201A;,&#x201B;</string>
- /* 43 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB,\u00BB,\u2018,\u2019,\u201A,\u201B",
+ /* 44 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB,\u00BB,\u2018,\u2019,\u201A,\u201B",
// U+00A2: "¢" CENT SIGN
// U+00A3: "£" POUND SIGN
// U+20AC: "€" EURO SIGN
// U+00A5: "¥" YEN SIGN
// U+20B1: "₱" PESO SIGN
- /* 44 */ "\u00A2,\u00A3,\u20AC,\u00A5,\u20B1",
- /* 45 */ "\u00A2,\u00A3,$,\u00A5,\u20B1",
- /* 46 */ "\u00A2,$,\u20AC,\u00A5,\u20B1",
- /* 47 */ "\u00A2,$,\u20AC,\u00A3,\u00A5,\u20B1",
- /* 48 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\\,,?,@,&,\\%,+,;,/,(,)",
+ /* 45 */ "\u00A2,\u00A3,\u20AC,\u00A5,\u20B1",
+ /* 46 */ "\u00A2,\u00A3,$,\u00A5,\u20B1",
+ /* 47 */ "\u00A2,$,\u20AC,\u00A5,\u20B1",
+ /* 48 */ "\u00A2,$,\u20AC,\u00A3,\u00A5,\u20B1",
+ /* 49 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\\,,?,@,&,\\%,+,;,/,(,)",
// U+2020: "†" DAGGER
// U+2021: "‡" DOUBLE DAGGER
// U+2605: "★" BLACK STAR
- /* 49 */ "\u2020,\u2021,\u2605",
+ /* 50 */ "\u2020,\u2021,\u2605",
// U+266A: "♪" EIGHTH NOTE
// U+2665: "♥" BLACK HEART SUIT
// U+2660: "♠" BLACK SPADE SUIT
// U+2666: "♦" BLACK DIAMOND SUIT
// U+2663: "♣" BLACK CLUB SUIT
- /* 50 */ "\u266A,\u2665,\u2660,\u2666,\u2663",
+ /* 51 */ "\u266A,\u2665,\u2660,\u2666,\u2663",
// U+00B1: "±" PLUS-MINUS SIGN
- /* 51 */ "\u00B1",
+ /* 52 */ "\u00B1",
// The all letters need to be mirrored are found at
// http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt
- /* 52 */ "!fixedColumnOrder!3,<,{,[",
- /* 53 */ "!fixedColumnOrder!3,>,},]",
+ /* 53 */ "!fixedColumnOrder!3,<,{,[",
+ /* 54 */ "!fixedColumnOrder!3,>,},]",
// U+2039: "‹" SINGLE LEFT-POINTING ANGLE QUOTATION MARK
// U+203A: "›" SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
// U+2264: "≤" LESS-THAN OR EQUAL TO
@@ -287,51 +293,50 @@ public final class KeyboardTextsSet {
// U+201D: "”" RIGHT DOUBLE QUOTATION MARK
// U+201E: "„" DOUBLE LOW-9 QUOTATION MARK
// U+201F: "‟" DOUBLE HIGH-REVERSED-9 QUOTATION MARK
- /* 54 */ "!fixedColumnOrder!3,\u2039,\u2264,\u00AB",
- /* 55 */ "!fixedColumnOrder!3,\u203A,\u2265,\u00BB",
- /* 56 */ EMPTY,
+ /* 55 */ "!fixedColumnOrder!3,\u2039,\u2264,\u00AB",
+ /* 56 */ "!fixedColumnOrder!3,\u203A,\u2265,\u00BB",
/* 57 */ EMPTY,
- /* 58 */ "1",
- /* 59 */ "2",
- /* 60 */ "3",
- /* 61 */ "4",
- /* 62 */ "5",
- /* 63 */ "6",
- /* 64 */ "7",
- /* 65 */ "8",
- /* 66 */ "9",
- /* 67 */ "0",
- /* 68~ */
+ /* 58 */ EMPTY,
+ /* 59 */ "1",
+ /* 60 */ "2",
+ /* 61 */ "3",
+ /* 62 */ "4",
+ /* 63 */ "5",
+ /* 64 */ "6",
+ /* 65 */ "7",
+ /* 66 */ "8",
+ /* 67 */ "9",
+ /* 68 */ "0",
+ /* 69~ */
EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY, EMPTY,
- /* ~77 */
+ /* ~78 */
// U+00B9: "¹" SUPERSCRIPT ONE
// U+00BD: "½" VULGAR FRACTION ONE HALF
// U+2153: "⅓" VULGAR FRACTION ONE THIRD
// U+00BC: "¼" VULGAR FRACTION ONE QUARTER
// U+215B: "⅛" VULGAR FRACTION ONE EIGHTH
- /* 78 */ "\u00B9,\u00BD,\u2153,\u00BC,\u215B",
+ /* 79 */ "\u00B9,\u00BD,\u2153,\u00BC,\u215B",
// U+00B2: "²" SUPERSCRIPT TWO
// U+2154: "⅔" VULGAR FRACTION TWO THIRDS
- /* 79 */ "\u00B2,\u2154",
+ /* 80 */ "\u00B2,\u2154",
// U+00B3: "³" SUPERSCRIPT THREE
// U+00BE: "¾" VULGAR FRACTION THREE QUARTERS
// U+215C: "⅜" VULGAR FRACTION THREE EIGHTHS
- /* 80 */ "\u00B3,\u00BE,\u215C",
+ /* 81 */ "\u00B3,\u00BE,\u215C",
// U+2074: "⁴" SUPERSCRIPT FOUR
- /* 81 */ "\u2074",
+ /* 82 */ "\u2074",
// U+215D: "⅝" VULGAR FRACTION FIVE EIGHTHS
- /* 82 */ "\u215D",
- /* 83 */ EMPTY,
+ /* 83 */ "\u215D",
+ /* 84 */ EMPTY,
// U+215E: "⅞" VULGAR FRACTION SEVEN EIGHTHS
- /* 84 */ "\u215E",
- /* 85 */ EMPTY,
+ /* 85 */ "\u215E",
/* 86 */ EMPTY,
+ /* 87 */ EMPTY,
// U+207F: "ⁿ" SUPERSCRIPT LATIN SMALL LETTER N
// U+2205: "∅" EMPTY SET
- /* 87 */ "\u207F,\u2205",
- /* 88 */ ",",
- /* 89 */ EMPTY,
- /* 90 */ "!",
+ /* 88 */ "\u207F,\u2205",
+ /* 89 */ ",",
+ /* 90 */ EMPTY,
/* 91 */ "?",
/* 92 */ ";",
/* 93 */ "%",
@@ -350,33 +355,94 @@ public final class KeyboardTextsSet {
/* 103 */ "\'",
/* 104 */ "\"",
/* 105 */ "\"",
- /* 106 */ "!fixedColumnOrder!2,!hasLabels!,!text/label_time_am,!text/label_time_pm",
- /* 107 */ "!icon/settings_key|!code/key_settings",
- /* 108 */ "!icon/shortcut_key|!code/key_shortcut",
- /* 109 */ "!hasLabels!,!text/label_next_key|!code/key_action_next",
- /* 110 */ "!hasLabels!,!text/label_previous_key|!code/key_action_previous",
+ /* 106 */ EMPTY,
+ /* 107 */ EMPTY,
+ /* 108 */ "q",
+ /* 109 */ "w",
+ /* 110 */ "y",
+ /* 111 */ "x",
+ // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
+ /* 112 */ "\u00F1",
+ /* 113 */ "!fixedColumnOrder!2,!hasLabels!,!text/label_time_am,!text/label_time_pm",
+ /* 114 */ "!icon/settings_key|!code/key_settings",
+ /* 115 */ "!icon/shortcut_key|!code/key_shortcut",
+ /* 116 */ "!hasLabels!,!text/label_next_key|!code/key_action_next",
+ /* 117 */ "!hasLabels!,!text/label_previous_key|!code/key_action_previous",
// Label for "switch to more symbol" modifier key. Must be short to fit on key!
- /* 111 */ "= \\ <",
+ /* 118 */ "= \\ <",
// Label for "switch to more symbol" modifier key on tablets. Must be short to fit on key!
- /* 112 */ "~ \\ {",
+ /* 119 */ "~ \\ {",
// Label for "Tab" key. Must be short to fit on key!
- /* 113 */ "Tab",
+ /* 120 */ "Tab",
// Label for "switch to phone numeric" key. Must be short to fit on key!
- /* 114 */ "123",
+ /* 121 */ "123",
// Label for "switch to phone symbols" key. Must be short to fit on key!
// U+FF0A: "*" FULLWIDTH ASTERISK
// U+FF03: "#" FULLWIDTH NUMBER SIGN
- /* 115 */ "\uFF0A\uFF03",
+ /* 122 */ "\uFF0A\uFF03",
// Key label for "ante meridiem"
- /* 116 */ "AM",
+ /* 123 */ "AM",
// Key label for "post meridiem"
- /* 117 */ "PM",
+ /* 124 */ "PM",
// Label for "switch to symbols" key on PC QWERTY layout
- /* 118 */ "Sym",
- /* 119 */ ".com",
+ /* 125 */ "Sym",
+ /* 126 */ ".com",
// popular web domains for the locale - most popular, displayed on the keyboard
- /* 120 */ "!hasLabels!,.net,.org,.gov,.edu",
- /* 121 */ "!fixedColumnOrder!5,!hasLabels!,=-O|=-O ,:-P|:-P ,;-)|;-) ,:-(|:-( ,:-)|:-) ,:-!|:-! ,:-$|:-$ ,B-)|B-) ,:O|:O ,:-*|:-* ,:-D|:-D ,:\'(|:\'( ,:-\\\\|:-\\\\ ,O:-)|O:-) ,:-[|:-[ ",
+ /* 127 */ "!hasLabels!,.net,.org,.gov,.edu",
+ /* 128 */ "!fixedColumnOrder!5,!hasLabels!,=-O|=-O ,:-P|:-P ,;-)|;-) ,:-(|:-( ,:-)|:-) ,:-!|:-! ,:-$|:-$ ,B-)|B-) ,:O|:O ,:-*|:-* ,:-D|:-D ,:\'(|:\'( ,:-\\\\|:-\\\\ ,O:-)|O:-) ,:-[|:-[ ",
+ };
+
+ /* Language af: Afrikaans */
+ private static final String[] LANGUAGE_af = {
+ // This is the same as Dutch except more keys of y and demoting vowels with diaeresis.
+ // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
+ // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
+ // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
+ // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
+ // U+00E6: "æ" LATIN SMALL LETTER AE
+ // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
+ // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
+ // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
+ /* 0 */ "\u00E1,\u00E2,\u00E4,\u00E0,\u00E6,\u00E3,\u00E5,\u0101",
+ // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
+ // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
+ // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
+ // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
+ // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
+ // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
+ // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
+ /* 1 */ "\u00E9,\u00E8,\u00EA,\u00EB,\u0119,\u0117,\u0113",
+ // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
+ // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
+ // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
+ // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
+ // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
+ // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
+ // U+0133: "ij" LATIN SMALL LIGATURE IJ
+ /* 2 */ "\u00ED,\u00EC,\u00EF,\u00EE,\u012F,\u012B,\u0133",
+ // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
+ // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
+ // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
+ // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
+ // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
+ // U+0153: "œ" LATIN SMALL LIGATURE OE
+ // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
+ // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
+ /* 3 */ "\u00F3,\u00F4,\u00F6,\u00F2,\u00F5,\u0153,\u00F8,\u014D",
+ // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
+ // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
+ // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
+ // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
+ // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
+ /* 4 */ "\u00FA,\u00FB,\u00FC,\u00F9,\u016B",
+ /* 5 */ null,
+ // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
+ // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
+ /* 6 */ "\u00F1,\u0144",
+ /* 7 */ null,
+ // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
+ // U+0133: "ij" LATIN SMALL LIGATURE IJ
+ /* 8 */ "\u00FD,\u0133",
};
/* Language ar: Arabic */
@@ -384,33 +450,33 @@ public final class KeyboardTextsSet {
/* 0~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null,
- /* ~41 */
+ null, null, null, null, null, null, null, null, null, null, null, null, null,
+ /* ~42 */
// TODO: Neither DroidSans nor Roboto have the glyph for U+201F DOUBLE HIGH-REVERSED-9 QUOTATION MARK
// <string name="more_keys_for_double_quote">&#x201C;,&#x201D;,&#x201E;,&#x201F;,&#x00AB;|&#x00BB;,&#x00BB;|&#x00AB;</string>
- /* 42 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB|\u00BB,\u00BB|\u00AB",
+ /* 43 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB|\u00BB,\u00BB|\u00AB",
// TODO: Neither DroidSans nor Roboto have the glyph for U+201F DOUBLE HIGH-REVERSED-9 QUOTATION MARK
// <string name="more_keys_for_tablet_double_quote">!fixedColumnOrder!6,&#x201C;,&#x201D;,&#x201E;,&#x201F;,&#x00AB;|&#x00BB;,&#x00BB|&#x00AB;;,&#x2018;,&#x2019;,&#x201A;,&#x201B;</string>
- /* 43 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB|\u00BB,\u00BB|\u00AB,\u2018,\u2019,\u201A,\u201B",
- /* 44~ */
+ /* 44 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB|\u00BB,\u00BB|\u00AB,\u2018,\u2019,\u201A,\u201B",
+ /* 45~ */
null, null, null, null,
- /* ~47 */
+ /* ~48 */
// U+061F: "؟" ARABIC QUESTION MARK
// U+060C: "،" ARABIC COMMA
// U+061B: "؛" ARABIC SEMICOLON
- /* 48 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\u060C,\u061F,@,&,\\%,+,\u061B,/,(,)",
+ /* 49 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\u060C,\u061F,@,&,\\%,+,\u061B,/,(,)",
// U+2605: "★" BLACK STAR
// U+066D: "٭" ARABIC FIVE POINTED STAR
- /* 49 */ "\u2605,\u066D",
+ /* 50 */ "\u2605,\u066D",
// U+266A: "♪" EIGHTH NOTE
- /* 50 */ "\u266A",
- /* 51 */ null,
+ /* 51 */ "\u266A",
+ /* 52 */ null,
// The all letters need to be mirrored are found at
// http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt
// U+FD3E: "﴾" ORNATE LEFT PARENTHESIS
// U+FD3F: "﴿" ORNATE RIGHT PARENTHESIS
- /* 52 */ "!fixedColumnOrder!4,\uFD3E|\uFD3F,<|>,{|},[|]",
- /* 53 */ "!fixedColumnOrder!4,\uFD3F|\uFD3E,>|<,}|{,]|[",
+ /* 53 */ "!fixedColumnOrder!4,\uFD3E|\uFD3F,<|>,{|},[|]",
+ /* 54 */ "!fixedColumnOrder!4,\uFD3F|\uFD3E,>|<,}|{,]|[",
// U+2264: "≤" LESS-THAN OR EQUAL TO
// U+2265: "≥" GREATER-THAN EQUAL TO
// U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
@@ -426,8 +492,8 @@ public final class KeyboardTextsSet {
// U+201D: "”" RIGHT DOUBLE QUOTATION MARK
// U+201E: "„" DOUBLE LOW-9 QUOTATION MARK
// U+201F: "‟" DOUBLE HIGH-REVERSED-9 QUOTATION MARK
- /* 54 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,\u00AB|\u00BB",
- /* 55 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,\u00BB|\u00AB",
+ /* 55 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,\u00AB|\u00BB",
+ /* 56 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,\u00BB|\u00AB",
// U+0655: "ٕ" ARABIC HAMZA BELOW
// U+0654: "ٔ" ARABIC HAMZA ABOVE
// U+0652: "ْ" ARABIC SUKUN
@@ -443,47 +509,46 @@ public final class KeyboardTextsSet {
// U+064E: "َ" ARABIC FATHA
// U+0640: "ـ" ARABIC TATWEEL
// In order to make Tatweel easily distinguishable from other punctuations, we use consecutive Tatweels only for its displayed label.
- /* 56 */ "!fixedColumnOrder!7,\u0655,\u0654,\u0652,\u064D,\u064C,\u064B,\u0651,\u0656,\u0670,\u0653,\u0650,\u064F,\u064E,\u0640\u0640\u0640|\u0640",
- /* 57 */ "\u0651",
+ /* 57 */ "!fixedColumnOrder!7,\u0655,\u0654,\u0652,\u064D,\u064C,\u064B,\u0651,\u0656,\u0670,\u0653,\u0650,\u064F,\u064E,\u0640\u0640\u0640|\u0640",
+ /* 58 */ "\u0651",
// U+0661: "١" ARABIC-INDIC DIGIT ONE
- /* 58 */ "\u0661",
+ /* 59 */ "\u0661",
// U+0662: "٢" ARABIC-INDIC DIGIT TWO
- /* 59 */ "\u0662",
+ /* 60 */ "\u0662",
// U+0663: "٣" ARABIC-INDIC DIGIT THREE
- /* 60 */ "\u0663",
+ /* 61 */ "\u0663",
// U+0664: "٤" ARABIC-INDIC DIGIT FOUR
- /* 61 */ "\u0664",
+ /* 62 */ "\u0664",
// U+0665: "٥" ARABIC-INDIC DIGIT FIVE
- /* 62 */ "\u0665",
+ /* 63 */ "\u0665",
// U+0666: "٦" ARABIC-INDIC DIGIT SIX
- /* 63 */ "\u0666",
+ /* 64 */ "\u0666",
// U+0667: "٧" ARABIC-INDIC DIGIT SEVEN
- /* 64 */ "\u0667",
+ /* 65 */ "\u0667",
// U+0668: "٨" ARABIC-INDIC DIGIT EIGHT
- /* 65 */ "\u0668",
+ /* 66 */ "\u0668",
// U+0669: "٩" ARABIC-INDIC DIGIT NINE
- /* 66 */ "\u0669",
+ /* 67 */ "\u0669",
// U+0660: "٠" ARABIC-INDIC DIGIT ZERO
- /* 67 */ "\u0660",
- /* 68 */ "1",
- /* 69 */ "2",
- /* 70 */ "3",
- /* 71 */ "4",
- /* 72 */ "5",
- /* 73 */ "6",
- /* 74 */ "7",
- /* 75 */ "8",
- /* 76 */ "9",
+ /* 68 */ "\u0660",
+ /* 69 */ "1",
+ /* 70 */ "2",
+ /* 71 */ "3",
+ /* 72 */ "4",
+ /* 73 */ "5",
+ /* 74 */ "6",
+ /* 75 */ "7",
+ /* 76 */ "8",
+ /* 77 */ "9",
// U+066B: "٫" ARABIC DECIMAL SEPARATOR
// U+066C: "٬" ARABIC THOUSANDS SEPARATOR
- /* 77 */ "0,\u066B,\u066C",
- /* 78~ */
+ /* 78 */ "0,\u066B,\u066C",
+ /* 79~ */
null, null, null, null, null, null, null, null, null, null,
- /* ~87 */
+ /* ~88 */
// U+060C: "،" ARABIC COMMA
- /* 88 */ "\u060C",
- /* 89 */ "\\,",
- /* 90 */ null,
+ /* 89 */ "\u060C",
+ /* 90 */ "\\,",
/* 91 */ "\u061F",
/* 92 */ "\u061B",
// U+066A: "٪" ARABIC PERCENT SIGN
@@ -512,19 +577,24 @@ public final class KeyboardTextsSet {
/* ~24 */
// U+045E: "ў" CYRILLIC SMALL LETTER SHORT U
/* 25 */ "\u045E",
+ // U+0451: "ё" CYRILLIC SMALL LETTER IO
+ /* 26 */ "\u0451",
// U+044B: "ы" CYRILLIC SMALL LETTER YERU
- /* 26 */ "\u044B",
+ /* 27 */ "\u044B",
+ // U+044D: "э" CYRILLIC SMALL LETTER E
+ /* 28 */ "\u044D",
// U+0456: "і" CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I
- /* 27 */ "\u0456",
- /* 28~ */
- null, null, null,
- /* ~30 */
- // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN
- /* 31 */ "\u044A",
- /* 32 */ null,
- /* 33 */ null,
+ /* 29 */ "\u0456",
+ /* 30~ */
+ null, null, null, null, null,
+ /* ~34 */
// U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN
- /* 34 */ "\u044A",
+ /* 35 */ "\u044A",
+ /* 36~ */
+ null, null, null, null,
+ /* ~39 */
+ // U+0451: "ё" CYRILLIC SMALL LETTER IO
+ /* 40 */ "\u0451",
};
/* Language ca: Catalan */
@@ -806,6 +876,144 @@ public final class KeyboardTextsSet {
/* 7 */ "\u00E7",
};
+ /* Language eo: Esperanto */
+ private static final String[] LANGUAGE_eo = {
+ // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
+ // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
+ // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
+ // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
+ // U+00E6: "æ" LATIN SMALL LETTER AE
+ // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
+ // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
+ // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
+ // U+0103: "ă" LATIN SMALL LETTER A WITH BREVE
+ // U+0105: "ą" LATIN SMALL LETTER A WITH OGONEK
+ // U+00AA: "ª" FEMININE ORDINAL INDICATOR
+ /* 0 */ "\u00E1,\u00E0,\u00E2,\u00E4,\u00E6,\u00E3,\u00E5,\u0101,\u0103,\u0105,\u00AA",
+ // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
+ // U+011B: "ě" LATIN SMALL LETTER E WITH CARON
+ // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
+ // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
+ // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
+ // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
+ // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
+ // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
+ /* 1 */ "\u00E9,\u011B,\u00E8,\u00EA,\u00EB,\u0119,\u0117,\u0113",
+ // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
+ // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
+ // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
+ // U+0129: "ĩ" LATIN SMALL LETTER I WITH TILDE
+ // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
+ // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
+ // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
+ // U+0131: "ı" LATIN SMALL LETTER DOTLESS I
+ // U+0133: "ij" LATIN SMALL LIGATURE IJ
+ /* 2 */ "\u00ED,\u00EE,\u00EF,\u0129,\u00EC,\u012F,\u012B,\u0131,\u0133",
+ // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
+ // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
+ // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
+ // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
+ // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
+ // U+0153: "œ" LATIN SMALL LIGATURE OE
+ // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
+ // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
+ // U+0151: "ő" LATIN SMALL LETTER O WITH DOUBLE ACUTE
+ // U+00BA: "º" MASCULINE ORDINAL INDICATOR
+ /* 3 */ "\u00F3,\u00F6,\u00F4,\u00F2,\u00F5,\u0153,\u00F8,\u014D,\u0151,\u00BA",
+ // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
+ // U+016F: "ů" LATIN SMALL LETTER U WITH RING ABOVE
+ // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
+ // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
+ // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
+ // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
+ // U+0169: "ũ" LATIN SMALL LETTER U WITH TILDE
+ // U+0171: "ű" LATIN SMALL LETTER U WITH DOUBLE ACUTE
+ // U+0173: "ų" LATIN SMALL LETTER U WITH OGONEK
+ // U+00B5: "µ" MICRO SIGN
+ /* 4 */ "\u00FA,\u016F,\u00FB,\u00FC,\u00F9,\u016B,\u0169,\u0171,\u0173,\u00B5",
+ // U+00DF: "ß" LATIN SMALL LETTER SHARP S
+ // U+0161: "š" LATIN SMALL LETTER S WITH CARON
+ // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
+ // U+0219: "ș" LATIN SMALL LETTER S WITH COMMA BELOW
+ // U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA
+ /* 5 */ "\u00DF,\u0161,\u015B,\u0219,\u015F",
+ // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
+ // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
+ // U+0146: "ņ" LATIN SMALL LETTER N WITH CEDILLA
+ // U+0148: "ň" LATIN SMALL LETTER N WITH CARON
+ // U+0149: "ʼn" LATIN SMALL LETTER N PRECEDED BY APOSTROPHE
+ // U+014B: "ŋ" LATIN SMALL LETTER ENG
+ /* 6 */ "\u00F1,\u0144,\u0146,\u0148,\u0149,\u014B",
+ // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
+ // U+010D: "č" LATIN SMALL LETTER C WITH CARON
+ // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
+ // U+010B: "ċ" LATIN SMALL LETTER C WITH DOT ABOVE
+ /* 7 */ "\u0107,\u010D,\u00E7,\u010B",
+ // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
+ // U+0177: "ŷ" LATIN SMALL LETTER Y WITH CIRCUMFLEX
+ // U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS
+ // U+00FE: "þ" LATIN SMALL LETTER THORN
+ /* 8 */ "y,\u00FD,\u0177,\u00FF,\u00FE",
+ // U+00F0: "ð" LATIN SMALL LETTER ETH
+ // U+010F: "ď" LATIN SMALL LETTER D WITH CARON
+ // U+0111: "đ" LATIN SMALL LETTER D WITH STROKE
+ /* 9 */ "\u00F0,\u010F,\u0111",
+ // U+0159: "ř" LATIN SMALL LETTER R WITH CARON
+ // U+0155: "ŕ" LATIN SMALL LETTER R WITH ACUTE
+ // U+0157: "ŗ" LATIN SMALL LETTER R WITH CEDILLA
+ /* 10 */ "\u0159,\u0155,\u0157",
+ // U+0165: "ť" LATIN SMALL LETTER T WITH CARON
+ // U+021B: "ț" LATIN SMALL LETTER T WITH COMMA BELOW
+ // U+0163: "ţ" LATIN SMALL LETTER T WITH CEDILLA
+ // U+0167: "ŧ" LATIN SMALL LETTER T WITH STROKE
+ /* 11 */ "\u0165,\u021B,\u0163,\u0167",
+ // U+017A: "ź" LATIN SMALL LETTER Z WITH ACUTE
+ // U+017C: "ż" LATIN SMALL LETTER Z WITH DOT ABOVE
+ // U+017E: "ž" LATIN SMALL LETTER Z WITH CARON
+ /* 12 */ "\u017A,\u017C,\u017E",
+ // U+0137: "ķ" LATIN SMALL LETTER K WITH CEDILLA
+ // U+0138: "ĸ" LATIN SMALL LETTER KRA
+ /* 13 */ "\u0137,\u0138",
+ // U+013A: "ĺ" LATIN SMALL LETTER L WITH ACUTE
+ // U+013C: "ļ" LATIN SMALL LETTER L WITH CEDILLA
+ // U+013E: "ľ" LATIN SMALL LETTER L WITH CARON
+ // U+0140: "ŀ" LATIN SMALL LETTER L WITH MIDDLE DOT
+ // U+0142: "ł" LATIN SMALL LETTER L WITH STROKE
+ /* 14 */ "\u013A,\u013C,\u013E,\u0140,\u0142",
+ // U+011F: "ğ" LATIN SMALL LETTER G WITH BREVE
+ // U+0121: "ġ" LATIN SMALL LETTER G WITH DOT ABOVE
+ // U+0123: "ģ" LATIN SMALL LETTER G WITH CEDILLA
+ /* 15 */ "\u011F,\u0121,\u0123",
+ // U+0175: "ŵ" LATIN SMALL LETTER W WITH CIRCUMFLEX
+ /* 16 */ "w,\u0175",
+ // U+0125: "ĥ" LATIN SMALL LETTER H WITH CIRCUMFLEX
+ // U+0127: "ħ" LATIN SMALL LETTER H WITH STROKE
+ /* 17 */ "\u0125,\u0127",
+ /* 18 */ null,
+ // U+0175: "ŵ" LATIN SMALL LETTER W WITH CIRCUMFLEX
+ /* 19 */ "w,\u0175",
+ /* 20~ */
+ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null, null, null, null, null, null, null,
+ /* ~105 */
+ /* 106 */ "q",
+ /* 107 */ "x",
+ // U+015D: "ŝ" LATIN SMALL LETTER S WITH CIRCUMFLEX
+ /* 108 */ "\u015D",
+ // U+011D: "ĝ" LATIN SMALL LETTER G WITH CIRCUMFLEX
+ /* 109 */ "\u011D",
+ // U+016D: "ŭ" LATIN SMALL LETTER U WITH BREVE
+ /* 110 */ "\u016D",
+ // U+0109: "ĉ" LATIN SMALL LETTER C WITH CIRCUMFLEX
+ /* 111 */ "\u0109",
+ // U+0135: "ĵ" LATIN SMALL LETTER J WITH CIRCUMFLEX
+ /* 112 */ "\u0135",
+ };
+
/* Language es: Spanish */
private static final String[] LANGUAGE_es = {
// U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
@@ -861,31 +1069,22 @@ public final class KeyboardTextsSet {
/* 8~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null,
- /* ~47 */
+ null, null, null, null, null, null, null, null, null, null, null,
+ /* ~48 */
// U+00A1: "¡" INVERTED EXCLAMATION MARK
// U+00BF: "¿" INVERTED QUESTION MARK
- /* 48 */ "!fixedColumnOrder!9,\",\',#,-,\u00A1,!,\u00BF,\\,,?,@,&,\\%,+,;,:,/,(,)",
- /* 49~ */
+ /* 49 */ "!fixedColumnOrder!9,\u00A1,\",\',#,-,:,!,\\,,?,\u00BF,@,&,\\%,+,;,/,(,)",
+ /* 50~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null,
- /* ~89 */
+ null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+ null, null, null, null, null,
+ /* ~99 */
// U+00A1: "¡" INVERTED EXCLAMATION MARK
- /* 90 */ "\u00A1",
+ /* 100 */ "!,\u00A1",
+ /* 101 */ null,
// U+00BF: "¿" INVERTED QUESTION MARK
- /* 91 */ "\u00BF",
- /* 92 */ null,
- /* 93 */ null,
- /* 94 */ "!",
- /* 95 */ "?",
- /* 96~ */
- null, null, null,
- /* ~98 */
- /* 99 */ "\u00A1",
- /* 100 */ "\u00A1,!",
- /* 101 */ "\u00BF",
- /* 102 */ "\u00BF,?",
+ /* 102 */ "?,\u00BF",
};
/* Language et: Estonian */
@@ -993,33 +1192,33 @@ public final class KeyboardTextsSet {
/* 0~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null,
- /* ~41 */
+ null, null, null, null, null, null, null, null, null, null, null, null, null,
+ /* ~42 */
// TODO: Neither DroidSans nor Roboto have the glyph for U+201F DOUBLE HIGH-REVERSED-9 QUOTATION MARK
// <string name="more_keys_for_double_quote">&#x201C;,&#x201D;,&#x201E;,&#x201F;,&#x00AB;|&#x00BB;,&#x00BB;|&#x00AB;</string>
- /* 42 */ "!fixedColumnOrder!4,\u201C,\u201D,\",\'",
+ /* 43 */ "!fixedColumnOrder!4,\u201C,\u201D,\",\'",
// TODO: Neither DroidSans nor Roboto have the glyph for U+201F DOUBLE HIGH-REVERSED-9 QUOTATION MARK
// <string name="more_keys_for_tablet_double_quote">!fixedColumnOrder!6,&#x201C;,&#x201D;,&#x201E;,&#x201F;,&#x00AB;|&#x00BB;,&#x00BB|&#x00AB;;,&#x2018;,&#x2019;,&#x201A;,&#x201B;</string>
- /* 43 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB|\u00BB,\u00BB|\u00AB,\u2018,\u2019,\u201A,\u201B",
- /* 44~ */
+ /* 44 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB|\u00BB,\u00BB|\u00AB,\u2018,\u2019,\u201A,\u201B",
+ /* 45~ */
null, null, null, null,
- /* ~47 */
+ /* ~48 */
// U+061F: "؟" ARABIC QUESTION MARK
// U+060C: "،" ARABIC COMMA
// U+061B: "؛" ARABIC SEMICOLON
- /* 48 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\u060C,\u061F,@,&,\\%,+,\u061B,/,(,)",
+ /* 49 */ "!fixedColumnOrder!8,\",\',#,-,:,!,\u060C,\u061F,@,&,\\%,+,\u061B,/,(,)",
// U+2605: "★" BLACK STAR
// U+066D: "٭" ARABIC FIVE POINTED STAR
- /* 49 */ "\u2605,\u066D",
+ /* 50 */ "\u2605,\u066D",
// U+266A: "♪" EIGHTH NOTE
- /* 50 */ "\u266A",
- /* 51 */ null,
+ /* 51 */ "\u266A",
+ /* 52 */ null,
// The all letters need to be mirrored are found at
// http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt
// U+FD3E: "﴾" ORNATE LEFT PARENTHESIS
// U+FD3F: "﴿" ORNATE RIGHT PARENTHESIS
- /* 52 */ "!fixedColumnOrder!4,\uFD3E|\uFD3F,<|>,{|},[|]",
- /* 53 */ "!fixedColumnOrder!4,\uFD3F|\uFD3E,>|<,}|{,]|[",
+ /* 53 */ "!fixedColumnOrder!4,\uFD3E|\uFD3F,<|>,{|},[|]",
+ /* 54 */ "!fixedColumnOrder!4,\uFD3F|\uFD3E,>|<,}|{,]|[",
// U+2264: "≤" LESS-THAN OR EQUAL TO
// U+2265: "≥" GREATER-THAN EQUAL TO
// U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
@@ -1035,8 +1234,8 @@ public final class KeyboardTextsSet {
// U+201D: "”" RIGHT DOUBLE QUOTATION MARK
// U+201E: "„" DOUBLE LOW-9 QUOTATION MARK
// U+201F: "‟" DOUBLE HIGH-REVERSED-9 QUOTATION MARK
- /* 54 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,<|>",
- /* 55 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,>|<",
+ /* 55 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,<|>",
+ /* 56 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,>|<",
// U+0655: "ٕ" ARABIC HAMZA BELOW
// U+0652: "ْ" ARABIC SUKUN
// U+0651: "ّ" ARABIC SHADDA
@@ -1052,47 +1251,46 @@ public final class KeyboardTextsSet {
// U+064E: "َ" ARABIC FATHA
// U+0640: "ـ" ARABIC TATWEEL
// In order to make Tatweel easily distinguishable from other punctuations, we use consecutive Tatweels only for its displayed label.
- /* 56 */ "!fixedColumnOrder!7,\u0655,\u0652,\u0651,\u064C,\u064D,\u064B,\u0654,\u0656,\u0670,\u0653,\u064F,\u0650,\u064E,\u0640\u0640\u0640|\u0640",
- /* 57 */ "\u064B",
+ /* 57 */ "!fixedColumnOrder!7,\u0655,\u0652,\u0651,\u064C,\u064D,\u064B,\u0654,\u0656,\u0670,\u0653,\u064F,\u0650,\u064E,\u0640\u0640\u0640|\u0640",
+ /* 58 */ "\u064B",
// U+06F1: "۱" EXTENDED ARABIC-INDIC DIGIT ONE
- /* 58 */ "\u06F1",
+ /* 59 */ "\u06F1",
// U+06F2: "۲" EXTENDED ARABIC-INDIC DIGIT TWO
- /* 59 */ "\u06F2",
+ /* 60 */ "\u06F2",
// U+06F3: "۳" EXTENDED ARABIC-INDIC DIGIT THREE
- /* 60 */ "\u06F3",
+ /* 61 */ "\u06F3",
// U+06F4: "۴" EXTENDED ARABIC-INDIC DIGIT FOUR
- /* 61 */ "\u06F4",
+ /* 62 */ "\u06F4",
// U+06F5: "۵" EXTENDED ARABIC-INDIC DIGIT FIVE
- /* 62 */ "\u06F5",
+ /* 63 */ "\u06F5",
// U+06F6: "۶" EXTENDED ARABIC-INDIC DIGIT SIX
- /* 63 */ "\u06F6",
+ /* 64 */ "\u06F6",
// U+06F7: "۷" EXTENDED ARABIC-INDIC DIGIT SEVEN
- /* 64 */ "\u06F7",
+ /* 65 */ "\u06F7",
// U+06F8: "۸" EXTENDED ARABIC-INDIC DIGIT EIGHT
- /* 65 */ "\u06F8",
+ /* 66 */ "\u06F8",
// U+06F9: "۹" EXTENDED ARABIC-INDIC DIGIT NINE
- /* 66 */ "\u06F9",
+ /* 67 */ "\u06F9",
// U+06F0: "۰" EXTENDED ARABIC-INDIC DIGIT ZERO
- /* 67 */ "\u06F0",
- /* 68 */ "1",
- /* 69 */ "2",
- /* 70 */ "3",
- /* 71 */ "4",
- /* 72 */ "5",
- /* 73 */ "6",
- /* 74 */ "7",
- /* 75 */ "8",
- /* 76 */ "9",
+ /* 68 */ "\u06F0",
+ /* 69 */ "1",
+ /* 70 */ "2",
+ /* 71 */ "3",
+ /* 72 */ "4",
+ /* 73 */ "5",
+ /* 74 */ "6",
+ /* 75 */ "7",
+ /* 76 */ "8",
+ /* 77 */ "9",
// U+066B: "٫" ARABIC DECIMAL SEPARATOR
// U+066C: "٬" ARABIC THOUSANDS SEPARATOR
- /* 77 */ "0,\u066B,\u066C",
- /* 78~ */
+ /* 78 */ "0,\u066B,\u066C",
+ /* 79~ */
null, null, null, null, null, null, null, null, null, null,
- /* ~87 */
+ /* ~88 */
// U+060C: "،" ARABIC COMMA
- /* 88 */ "\u060C",
- /* 89 */ "\\,",
- /* 90 */ null,
+ /* 89 */ "\u060C",
+ /* 90 */ "\\,",
/* 91 */ "\u061F",
/* 92 */ "\u061B",
// U+066A: "٪" ARABIC PERCENT SIGN
@@ -1223,38 +1421,38 @@ public final class KeyboardTextsSet {
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null, null,
- /* ~57 */
+ null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+ /* ~58 */
// U+0967: "१" DEVANAGARI DIGIT ONE
- /* 58 */ "\u0967",
+ /* 59 */ "\u0967",
// U+0968: "२" DEVANAGARI DIGIT TWO
- /* 59 */ "\u0968",
+ /* 60 */ "\u0968",
// U+0969: "३" DEVANAGARI DIGIT THREE
- /* 60 */ "\u0969",
+ /* 61 */ "\u0969",
// U+096A: "४" DEVANAGARI DIGIT FOUR
- /* 61 */ "\u096A",
+ /* 62 */ "\u096A",
// U+096B: "५" DEVANAGARI DIGIT FIVE
- /* 62 */ "\u096B",
+ /* 63 */ "\u096B",
// U+096C: "६" DEVANAGARI DIGIT SIX
- /* 63 */ "\u096C",
+ /* 64 */ "\u096C",
// U+096D: "७" DEVANAGARI DIGIT SEVEN
- /* 64 */ "\u096D",
+ /* 65 */ "\u096D",
// U+096E: "८" DEVANAGARI DIGIT EIGHT
- /* 65 */ "\u096E",
+ /* 66 */ "\u096E",
// U+096F: "९" DEVANAGARI DIGIT NINE
- /* 66 */ "\u096F",
+ /* 67 */ "\u096F",
// U+0966: "०" DEVANAGARI DIGIT ZERO
- /* 67 */ "\u0966",
- /* 68 */ "1",
- /* 69 */ "2",
- /* 70 */ "3",
- /* 71 */ "4",
- /* 72 */ "5",
- /* 73 */ "6",
- /* 74 */ "7",
- /* 75 */ "8",
- /* 76 */ "9",
- /* 77 */ "0",
+ /* 68 */ "\u0966",
+ /* 69 */ "1",
+ /* 70 */ "2",
+ /* 71 */ "3",
+ /* 72 */ "4",
+ /* 73 */ "5",
+ /* 74 */ "6",
+ /* 75 */ "7",
+ /* 76 */ "8",
+ /* 77 */ "9",
+ /* 78 */ "0",
};
/* Language hr: Croatian */
@@ -1442,27 +1640,27 @@ public final class KeyboardTextsSet {
/* 0~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null, null, null, null, null, null, null, null,
- /* ~41 */
+ null, null, null, null, null, null, null, null, null, null, null, null, null,
+ /* ~42 */
// TODO: Neither DroidSans nor Roboto have the glyph for U+201F DOUBLE HIGH-REVERSED-9 QUOTATION MARK
// <string name="more_keys_for_double_quote">&#x201C;,&#x201D;,&#x201E;,&#x201F;,&#x00AB;|&#x00BB;,&#x00BB;|&#x00AB;</string>
- /* 42 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB|\u00BB,\u00BB|\u00AB",
+ /* 43 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB|\u00BB,\u00BB|\u00AB",
// TODO: Neither DroidSans nor Roboto have the glyph for U+201F DOUBLE HIGH-REVERSED-9 QUOTATION MARK
// <string name="more_keys_for_tablet_double_quote">!fixedColumnOrder!6,&#x201C;,&#x201D;,&#x201E;,&#x201F;,&#x00AB;|&#x00BB;,&#x00BB|&#x00AB;;,&#x2018;,&#x2019;,&#x201A;,&#x201B;</string>
- /* 43 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB|\u00BB,\u00BB|\u00AB,\u2018,\u2019,\u201A,\u201B",
- /* 44~ */
+ /* 44 */ "!fixedColumnOrder!4,\u201C,\u201D,\u00AB|\u00BB,\u00BB|\u00AB,\u2018,\u2019,\u201A,\u201B",
+ /* 45~ */
null, null, null, null, null,
- /* ~48 */
+ /* ~49 */
// U+2605: "★" BLACK STAR
- /* 49 */ "\u2605",
- /* 50 */ null,
+ /* 50 */ "\u2605",
+ /* 51 */ null,
// U+00B1: "±" PLUS-MINUS SIGN
// U+FB29: "﬩" HEBREW LETTER ALTERNATIVE PLUS SIGN
- /* 51 */ "\u00B1,\uFB29",
+ /* 52 */ "\u00B1,\uFB29",
// The all letters need to be mirrored are found at
// http://www.unicode.org/Public/6.1.0/ucd/BidiMirroring.txt
- /* 52 */ "!fixedColumnOrder!3,<|>,{|},[|]",
- /* 53 */ "!fixedColumnOrder!3,>|<,}|{,]|[",
+ /* 53 */ "!fixedColumnOrder!3,<|>,{|},[|]",
+ /* 54 */ "!fixedColumnOrder!3,>|<,}|{,]|[",
// U+2264: "≤" LESS-THAN OR EQUAL TO
// U+2265: "≥" GREATER-THAN EQUAL TO
// U+00AB: "«" LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
@@ -1478,8 +1676,8 @@ public final class KeyboardTextsSet {
// U+201D: "”" RIGHT DOUBLE QUOTATION MARK
// U+201E: "„" DOUBLE LOW-9 QUOTATION MARK
// U+201F: "‟" DOUBLE HIGH-REVERSED-9 QUOTATION MARK
- /* 54 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,\u00AB|\u00BB",
- /* 55 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,\u00BB|\u00AB",
+ /* 55 */ "!fixedColumnOrder!3,\u2039|\u203A,\u2264|\u2265,\u00AB|\u00BB",
+ /* 56 */ "!fixedColumnOrder!3,\u203A|\u2039,\u2265|\u2264,\u00BB|\u00AB",
};
/* Language ky: Kirghiz */
@@ -1490,22 +1688,29 @@ public final class KeyboardTextsSet {
/* ~24 */
// U+0449: "щ" CYRILLIC SMALL LETTER SHCHA
/* 25 */ "\u0449",
+ // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN
+ /* 26 */ "\u044A",
// U+044B: "ы" CYRILLIC SMALL LETTER YERU
- /* 26 */ "\u044B",
+ /* 27 */ "\u044B",
+ // U+044D: "э" CYRILLIC SMALL LETTER E
+ /* 28 */ "\u044D",
// U+0438: "и" CYRILLIC SMALL LETTER I
- /* 27 */ "\u0438",
+ /* 29 */ "\u0438",
// U+04AF: "ү" CYRILLIC SMALL LETTER STRAIGHT U
- /* 28 */ "\u04AF",
- /* 29 */ null,
+ /* 30 */ "\u04AF",
// U+04A3: "ң" CYRILLIC SMALL LETTER EN WITH DESCENDER
- /* 30 */ "\u04A3",
- // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN
- /* 31 */ "\u044A",
+ /* 31 */ "\u04A3",
/* 32 */ null,
+ /* 33 */ null,
// U+04E9: "ө" CYRILLIC SMALL LETTER BARRED O
- /* 33 */ "\u04E9",
+ /* 34 */ "\u04E9",
// U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN
- /* 34 */ "\u044A",
+ /* 35 */ "\u044A",
+ /* 36~ */
+ null, null, null, null,
+ /* ~39 */
+ // U+0451: "ё" CYRILLIC SMALL LETTER IO
+ /* 40 */ "\u0451",
};
/* Language lt: Lithuanian */
@@ -1692,21 +1897,21 @@ public final class KeyboardTextsSet {
/* 0~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null,
- /* ~34 */
+ null, null, null, null, null, null,
+ /* ~35 */
// U+0455: "ѕ" CYRILLIC SMALL LETTER DZE
- /* 35 */ "\u0455",
+ /* 36 */ "\u0455",
// U+045C: "ќ" CYRILLIC SMALL LETTER KJE
- /* 36 */ "\u045C",
+ /* 37 */ "\u045C",
// U+0437: "з" CYRILLIC SMALL LETTER ZE
- /* 37 */ "\u0437",
+ /* 38 */ "\u0437",
// U+0453: "ѓ" CYRILLIC SMALL LETTER GJE
- /* 38 */ "\u0453",
+ /* 39 */ "\u0453",
// U+0450: "ѐ" CYRILLIC SMALL LETTER IE WITH GRAVE
- /* 39 */ "\u0450",
+ /* 40 */ "\u0450",
// U+045D: "ѝ" CYRILLIC SMALL LETTER I WITH GRAVE
- /* 40 */ "\u045D",
- /* 41 */ null,
+ /* 41 */ "\u045D",
+ /* 42 */ null,
// U+2018: "‘" LEFT SINGLE QUOTATION MARK
// U+2019: "’" RIGHT SINGLE QUOTATION MARK
// U+201A: "‚" SINGLE LOW-9 QUOTATION MARK
@@ -1717,10 +1922,10 @@ public final class KeyboardTextsSet {
// U+201F: "‟" DOUBLE HIGH-REVERSED-9 QUOTATION MARK
// TODO: Neither DroidSans nor Roboto have the glyph for U+201F DOUBLE HIGH-REVERSED-9 QUOTATION MARK.
// <string name="more_keys_for_double_quote">!fixedColumnOrder!6,&#x201E;,&#x201C;,&#x201D;,&#x201F;,&#x00AB;,&#x00BB;</string>
- /* 42 */ "!fixedColumnOrder!5,\u201E,\u201C,\u201D,\u00AB,\u00BB",
+ /* 43 */ "!fixedColumnOrder!5,\u201E,\u201C,\u201D,\u00AB,\u00BB",
// TODO: Neither DroidSans nor Roboto have the glyph for U+201F DOUBLE HIGH-REVERSED-9 QUOTATION MARK.
// <string name="more_keys_for_tablet_double_quote">!fixedColumnOrder!6,&#x201C;,&#x201D;,&#x201E;,&#x201F;,&#x00AB;,&#x00BB;,&#x2018;,&#x2019;,&#x201A;,&#x201B;</string>
- /* 43 */ "!fixedColumnOrder!5,\u201E,\u201C,\u201D,\u00AB,\u00BB,\u2018,\u2019,\u201A,\u201B",
+ /* 44 */ "!fixedColumnOrder!5,\u201E,\u201C,\u201D,\u00AB,\u00BB,\u2018,\u2019,\u201A,\u201B",
};
/* Language nb: Norwegian Bokmål */
@@ -1982,20 +2187,24 @@ public final class KeyboardTextsSet {
/* ~24 */
// U+0449: "щ" CYRILLIC SMALL LETTER SHCHA
/* 25 */ "\u0449",
+ // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN
+ /* 26 */ "\u044A",
// U+044B: "ы" CYRILLIC SMALL LETTER YERU
- /* 26 */ "\u044B",
+ /* 27 */ "\u044B",
+ // U+044D: "э" CYRILLIC SMALL LETTER E
+ /* 28 */ "\u044D",
// U+0438: "и" CYRILLIC SMALL LETTER I
- /* 27 */ "\u0438",
- /* 28 */ null,
- // U+0451: "ё" CYRILLIC SMALL LETTER IO
- /* 29 */ "\u0451",
- /* 30 */ null,
- // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN
- /* 31 */ "\u044A",
- /* 32 */ null,
- /* 33 */ null,
+ /* 29 */ "\u0438",
+ /* 30~ */
+ null, null, null, null, null,
+ /* ~34 */
// U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN
- /* 34 */ "\u044A",
+ /* 35 */ "\u044A",
+ /* 36~ */
+ null, null, null, null,
+ /* ~39 */
+ // U+0451: "ё" CYRILLIC SMALL LETTER IO
+ /* 40 */ "\u0451",
};
/* Language sk: Slovak */
@@ -2113,21 +2322,40 @@ public final class KeyboardTextsSet {
/* 0~ */
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null, null, null, null,
- null, null, null, null, null,
- /* ~34 */
+ null, null, null, null, null, null,
+ /* ~35 */
+ // TODO: Move these to sr-Latn once we can handle IETF language tag with script name specified.
+ // BEGIN: More keys definitions for Serbian (Latin)
+ // U+0161: "š" LATIN SMALL LETTER S WITH CARON
+ // U+00DF: "ß" LATIN SMALL LETTER SHARP S
+ // U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
+ // <string name="more_keys_for_s">&#x0161;,&#x00DF;,&#x015B;</string>
+ // U+010D: "č" LATIN SMALL LETTER C WITH CARON
+ // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
+ // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
+ // <string name="more_keys_for_c">&#x010D;,&#x00E7;,&#x0107;</string>
+ // U+010F: "ď" LATIN SMALL LETTER D WITH CARON
+ // <string name="more_keys_for_d">&#x010F;</string>
+ // U+017E: "ž" LATIN SMALL LETTER Z WITH CARON
+ // U+017A: "ź" LATIN SMALL LETTER Z WITH ACUTE
+ // U+017C: "ż" LATIN SMALL LETTER Z WITH DOT ABOVE
+ // <string name="more_keys_for_z">&#x017E;,&#x017A;,&#x017C;</string>
+ // END: More keys definitions for Serbian (Latin)
+ // BEGIN: More keys definitions for Serbian (Cyrillic)
// U+0437: "з" CYRILLIC SMALL LETTER ZE
- /* 35 */ "\u0437",
+ /* 36 */ "\u0437",
// U+045B: "ћ" CYRILLIC SMALL LETTER TSHE
- /* 36 */ "\u045B",
+ /* 37 */ "\u045B",
// U+0455: "ѕ" CYRILLIC SMALL LETTER DZE
- /* 37 */ "\u0455",
+ /* 38 */ "\u0455",
// U+0452: "ђ" CYRILLIC SMALL LETTER DJE
- /* 38 */ "\u0452",
+ /* 39 */ "\u0452",
// U+0450: "ѐ" CYRILLIC SMALL LETTER IE WITH GRAVE
- /* 39 */ "\u0450",
+ /* 40 */ "\u0450",
// U+045D: "ѝ" CYRILLIC SMALL LETTER I WITH GRAVE
- /* 40 */ "\u045D",
- /* 41 */ null,
+ /* 41 */ "\u045D",
+ /* 42 */ null,
+ // END: More keys definitions for Serbian (Cyrillic)
// U+2018: "‘" LEFT SINGLE QUOTATION MARK
// U+2019: "’" RIGHT SINGLE QUOTATION MARK
// U+201A: "‚" SINGLE LOW-9 QUOTATION MARK
@@ -2138,10 +2366,10 @@ public final class KeyboardTextsSet {
// U+201F: "‟" DOUBLE HIGH-REVERSED-9 QUOTATION MARK
// TODO: Neither DroidSans nor Roboto have the glyph for U+201F DOUBLE HIGH-REVERSED-9 QUOTATION MARK.
// <string name="more_keys_for_double_quote">!fixedColumnOrder!6,&#x201E;,&#x201C;,&#x201D;,&#x201F;,&#x00AB;,&#x00BB;</string>
- /* 42 */ "!fixedColumnOrder!5,\u201E,\u201C,\u201D,\u00AB,\u00BB",
+ /* 43 */ "!fixedColumnOrder!5,\u201E,\u201C,\u201D,\u00AB,\u00BB",
// TODO: Neither DroidSans nor Roboto have the glyph for U+201F DOUBLE HIGH-REVERSED-9 QUOTATION MARK.
// <string name="more_keys_for_tablet_double_quote">!fixedColumnOrder!6,&#x201C;,&#x201D;,&#x201E;,&#x201F;,&#x00AB;,&#x00BB;,&#x2018;,&#x2019;,&#x201A;,&#x201B;</string>
- /* 43 */ "!fixedColumnOrder!5,\u201E,\u201C,\u201D,\u00AB,\u00BB,\u2018,\u2019,\u201A,\u201B",
+ /* 44 */ "!fixedColumnOrder!5,\u201E,\u201C,\u201D,\u00AB,\u00BB,\u2018,\u2019,\u201A,\u201B",
};
/* Language sv: Swedish */
@@ -2186,6 +2414,111 @@ public final class KeyboardTextsSet {
/* 24 */ "\u00E6",
};
+ /* Language sw: Swahili */
+ private static final String[] LANGUAGE_sw = {
+ // This is the same as English except more_keys_for_g.
+ // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
+ // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
+ // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
+ // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
+ // U+00E6: "æ" LATIN SMALL LETTER AE
+ // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
+ // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
+ // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
+ /* 0 */ "\u00E0,\u00E1,\u00E2,\u00E4,\u00E6,\u00E3,\u00E5,\u0101",
+ // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
+ // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
+ // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
+ // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
+ // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
+ /* 1 */ "\u00E8,\u00E9,\u00EA,\u00EB,\u0113",
+ // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
+ // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
+ // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
+ // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
+ // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
+ /* 2 */ "\u00EE,\u00EF,\u00ED,\u012B,\u00EC",
+ // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
+ // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
+ // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
+ // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
+ // U+0153: "œ" LATIN SMALL LIGATURE OE
+ // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
+ // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
+ // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
+ /* 3 */ "\u00F4,\u00F6,\u00F2,\u00F3,\u0153,\u00F8,\u014D,\u00F5",
+ // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
+ // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
+ // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
+ // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
+ // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
+ /* 4 */ "\u00FB,\u00FC,\u00F9,\u00FA,\u016B",
+ // U+00DF: "ß" LATIN SMALL LETTER SHARP S
+ /* 5 */ "\u00DF",
+ // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
+ /* 6 */ "\u00F1",
+ // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
+ /* 7 */ "\u00E7",
+ /* 8~ */
+ null, null, null, null, null, null, null,
+ /* ~14 */
+ /* 15 */ "g\'",
+ };
+
+ /* Language tl: Tagalog */
+ private static final String[] LANGUAGE_tl = {
+ // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
+ // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
+ // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
+ // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
+ // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
+ // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
+ // U+0105: "ą" LATIN SMALL LETTER A WITH OGONEK
+ // U+00E6: "æ" LATIN SMALL LETTER AE
+ // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
+ // U+00AA: "ª" FEMININE ORDINAL INDICATOR
+ /* 0 */ "\u00E1,\u00E0,\u00E4,\u00E2,\u00E3,\u00E5,\u0105,\u00E6,\u0101,\u00AA",
+ // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
+ // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
+ // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
+ // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
+ // U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
+ // U+0117: "ė" LATIN SMALL LETTER E WITH DOT ABOVE
+ // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
+ /* 1 */ "\u00E9,\u00E8,\u00EB,\u00EA,\u0119,\u0117,\u0113",
+ // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
+ // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
+ // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
+ // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
+ // U+012F: "į" LATIN SMALL LETTER I WITH OGONEK
+ // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
+ /* 2 */ "\u00ED,\u00EF,\u00EC,\u00EE,\u012F,\u012B",
+ // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
+ // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
+ // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
+ // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
+ // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
+ // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
+ // U+0153: "œ" LATIN SMALL LIGATURE OE
+ // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
+ // U+00BA: "º" MASCULINE ORDINAL INDICATOR
+ /* 3 */ "\u00F3,\u00F2,\u00F6,\u00F4,\u00F5,\u00F8,\u0153,\u014D,\u00BA",
+ // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
+ // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
+ // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
+ // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
+ // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
+ /* 4 */ "\u00FA,\u00FC,\u00F9,\u00FB,\u016B",
+ /* 5 */ null,
+ // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
+ // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
+ /* 6 */ "\u00F1,\u0144",
+ // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
+ // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
+ // U+010D: "č" LATIN SMALL LETTER C WITH CARON
+ /* 7 */ "\u00E7,\u0107,\u010D",
+ };
+
/* Language tr: Turkish */
private static final String[] LANGUAGE_tr = {
// U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
@@ -2239,20 +2572,23 @@ public final class KeyboardTextsSet {
/* ~24 */
// U+0449: "щ" CYRILLIC SMALL LETTER SHCHA
/* 25 */ "\u0449",
+ // U+0457: "ї" CYRILLIC SMALL LETTER YI
+ /* 26 */ "\u0457",
// U+0456: "і" CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I
- /* 26 */ "\u0456",
+ /* 27 */ "\u0456",
+ // U+0454: "є" CYRILLIC SMALL LETTER UKRAINIAN IE
+ /* 28 */ "\u0454",
// U+0438: "и" CYRILLIC SMALL LETTER I
- /* 27 */ "\u0438",
- /* 28~ */
- null, null, null,
- /* ~30 */
- // U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN
- /* 31 */ "\u044A",
+ /* 29 */ "\u0438",
+ /* 30 */ null,
+ /* 31 */ null,
+ // U+0491: "ґ" CYRILLIC SMALL LETTER GHE WITH UPTURN
+ /* 32 */ "\u0491",
// U+0457: "ї" CYRILLIC SMALL LETTER YI
- /* 32 */ "\u0457",
- /* 33 */ null,
+ /* 33 */ "\u0457",
+ /* 34 */ null,
// U+044A: "ъ" CYRILLIC SMALL LETTER HARD SIGN
- /* 34 */ "\u044A",
+ /* 35 */ "\u044A",
};
/* Language vi: Vietnamese */
@@ -2336,6 +2672,53 @@ public final class KeyboardTextsSet {
/* 9 */ "\u0111",
};
+ /* Language zu: Zulu */
+ private static final String[] LANGUAGE_zu = {
+ // This is the same as English
+ // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
+ // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
+ // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
+ // U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
+ // U+00E6: "æ" LATIN SMALL LETTER AE
+ // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
+ // U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
+ // U+0101: "ā" LATIN SMALL LETTER A WITH MACRON
+ /* 0 */ "\u00E0,\u00E1,\u00E2,\u00E4,\u00E6,\u00E3,\u00E5,\u0101",
+ // U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
+ // U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
+ // U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
+ // U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
+ // U+0113: "ē" LATIN SMALL LETTER E WITH MACRON
+ /* 1 */ "\u00E8,\u00E9,\u00EA,\u00EB,\u0113",
+ // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
+ // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
+ // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
+ // U+012B: "ī" LATIN SMALL LETTER I WITH MACRON
+ // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
+ /* 2 */ "\u00EE,\u00EF,\u00ED,\u012B,\u00EC",
+ // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
+ // U+00F6: "ö" LATIN SMALL LETTER O WITH DIAERESIS
+ // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
+ // U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
+ // U+0153: "œ" LATIN SMALL LIGATURE OE
+ // U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
+ // U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
+ // U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
+ /* 3 */ "\u00F4,\u00F6,\u00F2,\u00F3,\u0153,\u00F8,\u014D,\u00F5",
+ // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
+ // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
+ // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
+ // U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
+ // U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
+ /* 4 */ "\u00FB,\u00FC,\u00F9,\u00FA,\u016B",
+ // U+00DF: "ß" LATIN SMALL LETTER SHARP S
+ /* 5 */ "\u00DF",
+ // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
+ /* 6 */ "\u00F1",
+ // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
+ /* 7 */ "\u00E7",
+ };
+
/* Language zz: No language */
private static final String[] LANGUAGE_zz = {
// U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
@@ -2461,6 +2844,7 @@ public final class KeyboardTextsSet {
private static final Object[] LANGUAGES_AND_TEXTS = {
"DEFAULT", LANGUAGE_DEFAULT, /* default */
+ "af", LANGUAGE_af, /* Afrikaans */
"ar", LANGUAGE_ar, /* Arabic */
"be", LANGUAGE_be, /* Belarusian */
"ca", LANGUAGE_ca, /* Catalan */
@@ -2468,6 +2852,7 @@ public final class KeyboardTextsSet {
"da", LANGUAGE_da, /* Danish */
"de", LANGUAGE_de, /* German */
"en", LANGUAGE_en, /* English */
+ "eo", LANGUAGE_eo, /* Esperanto */
"es", LANGUAGE_es, /* Spanish */
"et", LANGUAGE_et, /* Estonian */
"fa", LANGUAGE_fa, /* Persian */
@@ -2494,9 +2879,12 @@ public final class KeyboardTextsSet {
"sl", LANGUAGE_sl, /* Slovenian */
"sr", LANGUAGE_sr, /* Serbian */
"sv", LANGUAGE_sv, /* Swedish */
+ "sw", LANGUAGE_sw, /* Swahili */
+ "tl", LANGUAGE_tl, /* Tagalog */
"tr", LANGUAGE_tr, /* Turkish */
"uk", LANGUAGE_uk, /* Ukrainian */
"vi", LANGUAGE_vi, /* Vietnamese */
+ "zu", LANGUAGE_zu, /* Zulu */
"zz", LANGUAGE_zz, /* No language */
};
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeysCache.java b/java/src/com/android/inputmethod/keyboard/internal/KeysCache.java
new file mode 100644
index 000000000..d1b4c8524
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeysCache.java
@@ -0,0 +1,40 @@
+/*
+ * 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.keyboard.internal;
+
+import com.android.inputmethod.keyboard.Key;
+import com.android.inputmethod.latin.CollectionUtils;
+
+import java.util.HashMap;
+
+public final class KeysCache {
+ private final HashMap<Key, Key> mMap = CollectionUtils.newHashMap();
+
+ public void clear() {
+ mMap.clear();
+ }
+
+ public Key get(final Key key) {
+ final Key existingKey = mMap.get(key);
+ if (existingKey != null) {
+ // Reuse the existing element that equals to "key" without adding "key" to the map.
+ return existingKey;
+ }
+ mMap.put(key, key);
+ return key;
+ }
+}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java b/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java
new file mode 100644
index 000000000..550391b49
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java
@@ -0,0 +1,86 @@
+/*
+ * 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.keyboard.internal;
+
+import android.text.TextUtils;
+
+import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.latin.StringUtils;
+
+import java.util.Locale;
+
+public final class MoreKeySpec {
+ public final int mCode;
+ public final String mLabel;
+ public final String mOutputText;
+ public final int mIconId;
+
+ public MoreKeySpec(final String moreKeySpec, boolean needsToUpperCase, final Locale locale,
+ final KeyboardCodesSet codesSet) {
+ mLabel = KeySpecParser.toUpperCaseOfStringForLocale(
+ KeySpecParser.getLabel(moreKeySpec), needsToUpperCase, locale);
+ final int code = KeySpecParser.toUpperCaseOfCodeForLocale(
+ KeySpecParser.getCode(moreKeySpec, codesSet), needsToUpperCase, locale);
+ if (code == Keyboard.CODE_UNSPECIFIED) {
+ // Some letter, for example German Eszett (U+00DF: "ß"), has multiple characters
+ // upper case representation ("SS").
+ mCode = Keyboard.CODE_OUTPUT_TEXT;
+ mOutputText = mLabel;
+ } else {
+ mCode = code;
+ mOutputText = KeySpecParser.toUpperCaseOfStringForLocale(
+ KeySpecParser.getOutputText(moreKeySpec), needsToUpperCase, locale);
+ }
+ mIconId = KeySpecParser.getIconId(moreKeySpec);
+ }
+
+ @Override
+ public int hashCode() {
+ int hashCode = 1;
+ hashCode = 31 + mCode;
+ hashCode = hashCode * 31 + mIconId;
+ hashCode = hashCode * 31 + (mLabel == null ? 0 : mLabel.hashCode());
+ hashCode = hashCode * 31 + (mOutputText == null ? 0 : mOutputText.hashCode());
+ return hashCode;
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) return true;
+ if (o instanceof MoreKeySpec) {
+ final MoreKeySpec other = (MoreKeySpec)o;
+ return mCode == other.mCode
+ && mIconId == other.mIconId
+ && TextUtils.equals(mLabel, other.mLabel)
+ && TextUtils.equals(mOutputText, other.mOutputText);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ final String label = (mIconId == KeyboardIconsSet.ICON_UNDEFINED ? mLabel
+ : KeySpecParser.PREFIX_ICON + KeyboardIconsSet.getIconName(mIconId));
+ final String output = (mCode == Keyboard.CODE_OUTPUT_TEXT ? mOutputText
+ : Keyboard.printableCode(mCode));
+ if (StringUtils.codePointCount(label) == 1 && label.codePointAt(0) == mCode) {
+ return output;
+ } else {
+ return label + "|" + output;
+ }
+ }
+}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java b/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java
index 5db65c660..a52f202aa 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/PointerTrackerQueue.java
@@ -18,72 +18,164 @@ package com.android.inputmethod.keyboard.internal;
import android.util.Log;
-import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.keyboard.PointerTracker;
+import com.android.inputmethod.latin.CollectionUtils;
-import java.util.Iterator;
-import java.util.LinkedList;
+import java.util.ArrayList;
-public class PointerTrackerQueue {
+public final class PointerTrackerQueue {
private static final String TAG = PointerTrackerQueue.class.getSimpleName();
private static final boolean DEBUG = false;
- private final LinkedList<PointerTracker> mQueue = new LinkedList<PointerTracker>();
+ public interface Element {
+ public boolean isModifier();
+ public boolean isInSlidingKeyInput();
+ public void onPhantomUpEvent(long eventTime);
+ }
+
+ private static final int INITIAL_CAPACITY = 10;
+ private final ArrayList<Element> mExpandableArrayOfActivePointers =
+ CollectionUtils.newArrayList(INITIAL_CAPACITY);
+ private int mArraySize = 0;
- public synchronized void add(PointerTracker tracker) {
- mQueue.add(tracker);
+ public synchronized int size() {
+ return mArraySize;
}
- public synchronized void remove(PointerTracker tracker) {
- mQueue.remove(tracker);
+ public synchronized void add(final Element pointer) {
+ final ArrayList<Element> expandableArray = mExpandableArrayOfActivePointers;
+ final int arraySize = mArraySize;
+ if (arraySize < expandableArray.size()) {
+ expandableArray.set(arraySize, pointer);
+ } else {
+ expandableArray.add(pointer);
+ }
+ mArraySize = arraySize + 1;
}
- public synchronized void releaseAllPointersOlderThan(PointerTracker tracker,
- long eventTime) {
+ public synchronized void remove(final Element pointer) {
+ final ArrayList<Element> expandableArray = mExpandableArrayOfActivePointers;
+ final int arraySize = mArraySize;
+ int newSize = 0;
+ for (int index = 0; index < arraySize; index++) {
+ final Element element = expandableArray.get(index);
+ if (element == pointer) {
+ if (newSize != index) {
+ Log.w(TAG, "Found duplicated element in remove: " + pointer);
+ }
+ continue; // Remove this element from the expandableArray.
+ }
+ if (newSize != index) {
+ // Shift this element toward the beginning of the expandableArray.
+ expandableArray.set(newSize, element);
+ }
+ newSize++;
+ }
+ mArraySize = newSize;
+ }
+
+ public synchronized Element getOldestElement() {
+ return (mArraySize == 0) ? null : mExpandableArrayOfActivePointers.get(0);
+ }
+
+ public synchronized void releaseAllPointersOlderThan(final Element pointer,
+ final long eventTime) {
if (DEBUG) {
- Log.d(TAG, "releaseAllPoniterOlderThan: [" + tracker.mPointerId + "] " + this);
+ Log.d(TAG, "releaseAllPoniterOlderThan: " + pointer + " " + this);
}
- if (!mQueue.contains(tracker)) {
- return;
+ final ArrayList<Element> expandableArray = mExpandableArrayOfActivePointers;
+ final int arraySize = mArraySize;
+ int newSize, index;
+ for (newSize = index = 0; index < arraySize; index++) {
+ final Element element = expandableArray.get(index);
+ if (element == pointer) {
+ break; // Stop releasing elements.
+ }
+ if (!element.isModifier()) {
+ element.onPhantomUpEvent(eventTime);
+ continue; // Remove this element from the expandableArray.
+ }
+ if (newSize != index) {
+ // Shift this element toward the beginning of the expandableArray.
+ expandableArray.set(newSize, element);
+ }
+ newSize++;
}
- final Iterator<PointerTracker> it = mQueue.iterator();
- while (it.hasNext()) {
- final PointerTracker t = it.next();
- if (t == tracker) {
- break;
+ // Shift rest of the expandableArray.
+ int count = 0;
+ for (; index < arraySize; index++) {
+ final Element element = expandableArray.get(index);
+ if (element == pointer) {
+ if (count > 0) {
+ Log.w(TAG, "Found duplicated element in releaseAllPointersOlderThan: "
+ + pointer);
+ }
+ count++;
}
- if (!t.isModifier()) {
- t.onPhantomUpEvent(t.getLastX(), t.getLastY(), eventTime);
- it.remove();
+ if (newSize != index) {
+ expandableArray.set(newSize, expandableArray.get(index));
+ newSize++;
}
}
+ mArraySize = newSize;
}
- public void releaseAllPointers(long eventTime) {
+ public void releaseAllPointers(final long eventTime) {
releaseAllPointersExcept(null, eventTime);
}
- public synchronized void releaseAllPointersExcept(PointerTracker tracker, long eventTime) {
+ public synchronized void releaseAllPointersExcept(final Element pointer,
+ final long eventTime) {
if (DEBUG) {
- if (tracker == null) {
+ if (pointer == null) {
Log.d(TAG, "releaseAllPoniters: " + this);
} else {
- Log.d(TAG, "releaseAllPoniterExcept: [" + tracker.mPointerId + "] " + this);
+ Log.d(TAG, "releaseAllPoniterExcept: " + pointer + " " + this);
}
}
- final Iterator<PointerTracker> it = mQueue.iterator();
- while (it.hasNext()) {
- final PointerTracker t = it.next();
- if (t != tracker) {
- t.onPhantomUpEvent(t.getLastX(), t.getLastY(), eventTime);
- it.remove();
+ final ArrayList<Element> expandableArray = mExpandableArrayOfActivePointers;
+ final int arraySize = mArraySize;
+ int newSize = 0, count = 0;
+ for (int index = 0; index < arraySize; index++) {
+ final Element element = expandableArray.get(index);
+ if (element == pointer) {
+ if (count > 0) {
+ Log.w(TAG, "Found duplicated element in releaseAllPointersExcept: " + pointer);
+ }
+ count++;
+ } else {
+ element.onPhantomUpEvent(eventTime);
+ continue; // Remove this element from the expandableArray.
}
+ if (newSize != index) {
+ // Shift this element toward the beginning of the expandableArray.
+ expandableArray.set(newSize, element);
+ }
+ newSize++;
}
+ mArraySize = newSize;
+ }
+
+ public synchronized boolean hasModifierKeyOlderThan(final Element pointer) {
+ final ArrayList<Element> expandableArray = mExpandableArrayOfActivePointers;
+ final int arraySize = mArraySize;
+ for (int index = 0; index < arraySize; index++) {
+ final Element element = expandableArray.get(index);
+ if (element == pointer) {
+ return false; // Stop searching modifier key.
+ }
+ if (element.isModifier()) {
+ return true;
+ }
+ }
+ return false;
}
public synchronized boolean isAnyInSlidingKeyInput() {
- for (final PointerTracker tracker : mQueue) {
- if (tracker.isInSlidingKeyInput()) {
+ final ArrayList<Element> expandableArray = mExpandableArrayOfActivePointers;
+ final int arraySize = mArraySize;
+ for (int index = 0; index < arraySize; index++) {
+ final Element element = expandableArray.get(index);
+ if (element.isInSlidingKeyInput()) {
return true;
}
}
@@ -91,14 +183,16 @@ public class PointerTrackerQueue {
}
@Override
- public String toString() {
+ public synchronized String toString() {
final StringBuilder sb = new StringBuilder();
- for (final PointerTracker tracker : mQueue) {
+ final ArrayList<Element> expandableArray = mExpandableArrayOfActivePointers;
+ final int arraySize = mArraySize;
+ for (int index = 0; index < arraySize; index++) {
+ final Element element = expandableArray.get(index);
if (sb.length() > 0)
sb.append(" ");
- sb.append("[" + tracker.mPointerId + " "
- + Keyboard.printableCode(tracker.getKey().mCode) + "]");
+ sb.append(element.toString());
}
- return sb.toString();
+ return "[" + sb.toString() + "]";
}
}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java b/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java
new file mode 100644
index 000000000..776ac0204
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/internal/PreviewPlacerView.java
@@ -0,0 +1,348 @@
+/*
+ * 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.keyboard.internal;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Paint.Align;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.os.Message;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.SparseArray;
+import android.widget.RelativeLayout;
+
+import com.android.inputmethod.keyboard.PointerTracker;
+import com.android.inputmethod.keyboard.internal.GesturePreviewTrail.Params;
+import com.android.inputmethod.latin.CollectionUtils;
+import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.StaticInnerHandlerWrapper;
+
+public final class PreviewPlacerView extends RelativeLayout {
+ // The height of extra area above the keyboard to draw gesture trails.
+ // Proportional to the keyboard height.
+ private static final float EXTRA_GESTURE_TRAIL_AREA_ABOVE_KEYBOARD_RATIO = 0.25f;
+
+ private final int mGestureFloatingPreviewTextColor;
+ private final int mGestureFloatingPreviewTextOffset;
+ private final int mGestureFloatingPreviewColor;
+ private final float mGestureFloatingPreviewHorizontalPadding;
+ private final float mGestureFloatingPreviewVerticalPadding;
+ private final float mGestureFloatingPreviewRoundRadius;
+
+ private int mKeyboardViewOriginX;
+ private int mKeyboardViewOriginY;
+
+ private final SparseArray<GesturePreviewTrail> mGesturePreviewTrails =
+ CollectionUtils.newSparseArray();
+ private final Params mGesturePreviewTrailParams;
+ private final Paint mGesturePaint;
+ private boolean mDrawsGesturePreviewTrail;
+ private int mOffscreenWidth;
+ private int mOffscreenHeight;
+ private int mOffscreenOffsetY;
+ private Bitmap mOffscreenBuffer;
+ private final Canvas mOffscreenCanvas = new Canvas();
+ private final Rect mOffscreenDirtyRect = new Rect();
+ private final Rect mGesturePreviewTrailBoundsRect = new Rect(); // per trail
+
+ private final Paint mTextPaint;
+ private String mGestureFloatingPreviewText;
+ private final int mGestureFloatingPreviewTextHeight;
+ // {@link RectF} is needed for {@link Canvas#drawRoundRect(RectF, float, float, Paint)}.
+ private final RectF mGestureFloatingPreviewRectangle = new RectF();
+ private int mLastPointerX;
+ private int mLastPointerY;
+ private static final char[] TEXT_HEIGHT_REFERENCE_CHAR = { 'M' };
+ private boolean mDrawsGestureFloatingPreviewText;
+
+ private final DrawingHandler mDrawingHandler;
+
+ private static final class DrawingHandler extends StaticInnerHandlerWrapper<PreviewPlacerView> {
+ private static final int MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT = 0;
+ 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 int getstureFloatinPreviewTextLinerTimeout) {
+ super(outerInstance);
+ mGesturePreviewTrailParams = gesturePreviewTrailParams;
+ mGestureFloatingPreviewTextLingerTimeout = getstureFloatinPreviewTextLinerTimeout;
+ }
+
+ @Override
+ public void handleMessage(final Message msg) {
+ final PreviewPlacerView placerView = getOuterInstance();
+ if (placerView == null) return;
+ switch (msg.what) {
+ case MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT:
+ placerView.setGestureFloatingPreviewText(null);
+ break;
+ case MSG_UPDATE_GESTURE_PREVIEW_TRAIL:
+ placerView.invalidate();
+ break;
+ }
+ }
+
+ public void dismissGestureFloatingPreviewText() {
+ removeMessages(MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT);
+ sendMessageDelayed(obtainMessage(MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT),
+ mGestureFloatingPreviewTextLingerTimeout);
+ }
+
+ public void postUpdateGestureTrailPreview() {
+ removeMessages(MSG_UPDATE_GESTURE_PREVIEW_TRAIL);
+ sendMessageDelayed(obtainMessage(MSG_UPDATE_GESTURE_PREVIEW_TRAIL),
+ mGesturePreviewTrailParams.mUpdateInterval);
+ }
+ }
+
+ public PreviewPlacerView(final Context context, final AttributeSet attrs) {
+ this(context, attrs, R.attr.keyboardViewStyle);
+ }
+
+ public PreviewPlacerView(final Context context, final AttributeSet attrs, final int defStyle) {
+ super(context);
+ setWillNotDraw(false);
+
+ final TypedArray keyboardViewAttr = context.obtainStyledAttributes(
+ attrs, R.styleable.KeyboardView, defStyle, R.style.KeyboardView);
+ final int gestureFloatingPreviewTextSize = keyboardViewAttr.getDimensionPixelSize(
+ R.styleable.KeyboardView_gestureFloatingPreviewTextSize, 0);
+ mGestureFloatingPreviewTextColor = keyboardViewAttr.getColor(
+ R.styleable.KeyboardView_gestureFloatingPreviewTextColor, 0);
+ mGestureFloatingPreviewTextOffset = keyboardViewAttr.getDimensionPixelOffset(
+ R.styleable.KeyboardView_gestureFloatingPreviewTextOffset, 0);
+ mGestureFloatingPreviewColor = keyboardViewAttr.getColor(
+ R.styleable.KeyboardView_gestureFloatingPreviewColor, 0);
+ mGestureFloatingPreviewHorizontalPadding = keyboardViewAttr.getDimension(
+ R.styleable.KeyboardView_gestureFloatingPreviewHorizontalPadding, 0.0f);
+ mGestureFloatingPreviewVerticalPadding = keyboardViewAttr.getDimension(
+ R.styleable.KeyboardView_gestureFloatingPreviewVerticalPadding, 0.0f);
+ mGestureFloatingPreviewRoundRadius = keyboardViewAttr.getDimension(
+ R.styleable.KeyboardView_gestureFloatingPreviewRoundRadius, 0.0f);
+ final int gestureFloatingPreviewTextLingerTimeout = keyboardViewAttr.getInt(
+ R.styleable.KeyboardView_gestureFloatingPreviewTextLingerTimeout, 0);
+ mGesturePreviewTrailParams = new Params(keyboardViewAttr);
+ keyboardViewAttr.recycle();
+
+ mDrawingHandler = new DrawingHandler(this, mGesturePreviewTrailParams,
+ gestureFloatingPreviewTextLingerTimeout);
+
+ final Paint gesturePaint = new Paint();
+ gesturePaint.setAntiAlias(true);
+ gesturePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
+ mGesturePaint = gesturePaint;
+
+ final Paint textPaint = new Paint();
+ textPaint.setAntiAlias(true);
+ textPaint.setTextAlign(Align.CENTER);
+ textPaint.setTextSize(gestureFloatingPreviewTextSize);
+ mTextPaint = textPaint;
+ final Rect textRect = new Rect();
+ textPaint.getTextBounds(TEXT_HEIGHT_REFERENCE_CHAR, 0, 1, textRect);
+ mGestureFloatingPreviewTextHeight = textRect.height();
+
+ final Paint layerPaint = new Paint();
+ layerPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER));
+ setLayerType(LAYER_TYPE_HARDWARE, layerPaint);
+ }
+
+ public void setKeyboardViewGeometry(final int x, final int y, final int w, final int h) {
+ mKeyboardViewOriginX = x;
+ mKeyboardViewOriginY = y;
+ mOffscreenOffsetY = (int)(h * EXTRA_GESTURE_TRAIL_AREA_ABOVE_KEYBOARD_RATIO);
+ mOffscreenWidth = w;
+ mOffscreenHeight = mOffscreenOffsetY + h;
+ }
+
+ public void setGesturePreviewMode(final boolean drawsGesturePreviewTrail,
+ final boolean drawsGestureFloatingPreviewText) {
+ mDrawsGesturePreviewTrail = drawsGesturePreviewTrail;
+ mDrawsGestureFloatingPreviewText = drawsGestureFloatingPreviewText;
+ }
+
+ public void invalidatePointer(final PointerTracker tracker, final boolean 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.
+ if (mDrawsGesturePreviewTrail || needsToUpdateLastPointer) {
+ invalidate();
+ }
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ freeOffscreenBuffer();
+ }
+
+ private void freeOffscreenBuffer() {
+ if (mOffscreenBuffer != null) {
+ mOffscreenBuffer.recycle();
+ mOffscreenBuffer = null;
+ }
+ }
+
+ private void mayAllocateOffscreenBuffer() {
+ if (mOffscreenBuffer != null && mOffscreenBuffer.getWidth() == mOffscreenWidth
+ && mOffscreenBuffer.getHeight() == mOffscreenHeight) {
+ return;
+ }
+ freeOffscreenBuffer();
+ mOffscreenBuffer = Bitmap.createBitmap(
+ mOffscreenWidth, mOffscreenHeight, Bitmap.Config.ARGB_8888);
+ mOffscreenCanvas.setBitmap(mOffscreenBuffer);
+ }
+
+ @Override
+ public void onDraw(final Canvas canvas) {
+ super.onDraw(canvas);
+ if (mDrawsGesturePreviewTrail) {
+ mayAllocateOffscreenBuffer();
+ // Draw gesture trails to offscreen buffer.
+ final boolean needsUpdatingGesturePreviewTrail = drawGestureTrails(
+ mOffscreenCanvas, mGesturePaint, mOffscreenDirtyRect);
+ // Transfer offscreen buffer to screen.
+ if (!mOffscreenDirtyRect.isEmpty()) {
+ final int offsetY = mKeyboardViewOriginY - mOffscreenOffsetY;
+ canvas.translate(mKeyboardViewOriginX, offsetY);
+ canvas.drawBitmap(mOffscreenBuffer, mOffscreenDirtyRect, mOffscreenDirtyRect,
+ mGesturePaint);
+ canvas.translate(-mKeyboardViewOriginX, -offsetY);
+ // Note: Defer clearing the dirty rectangle here because we will get cleared
+ // rectangle on the canvas.
+ }
+ if (needsUpdatingGesturePreviewTrail) {
+ mDrawingHandler.postUpdateGestureTrailPreview();
+ }
+ }
+ if (mDrawsGestureFloatingPreviewText) {
+ canvas.translate(mKeyboardViewOriginX, mKeyboardViewOriginY);
+ drawGestureFloatingPreviewText(canvas, mGestureFloatingPreviewText);
+ canvas.translate(-mKeyboardViewOriginX, -mKeyboardViewOriginY);
+ }
+ }
+
+ private boolean drawGestureTrails(final Canvas offscreenCanvas, final Paint paint,
+ final Rect dirtyRect) {
+ // Clear previous dirty rectangle.
+ if (!dirtyRect.isEmpty()) {
+ paint.setColor(Color.TRANSPARENT);
+ paint.setStyle(Paint.Style.FILL);
+ offscreenCanvas.drawRect(dirtyRect, paint);
+ }
+ dirtyRect.setEmpty();
+
+ // Draw gesture trails to offscreen buffer.
+ offscreenCanvas.translate(0, mOffscreenOffsetY);
+ boolean needsUpdatingGesturePreviewTrail = false;
+ synchronized (mGesturePreviewTrails) {
+ // Trails count == fingers count that have ever been active.
+ final int trailsCount = mGesturePreviewTrails.size();
+ for (int index = 0; index < trailsCount; index++) {
+ final GesturePreviewTrail trail = mGesturePreviewTrails.valueAt(index);
+ needsUpdatingGesturePreviewTrail |=
+ trail.drawGestureTrail(offscreenCanvas, paint,
+ mGesturePreviewTrailBoundsRect, mGesturePreviewTrailParams);
+ // {@link #mGesturePreviewTrailBoundsRect} has bounding box of the trail.
+ dirtyRect.union(mGesturePreviewTrailBoundsRect);
+ }
+ }
+ offscreenCanvas.translate(0, -mOffscreenOffsetY);
+
+ // Clip dirty rectangle with offscreen buffer width/height.
+ dirtyRect.offset(0, mOffscreenOffsetY);
+ clipRect(dirtyRect, 0, 0, mOffscreenWidth, mOffscreenHeight);
+ return needsUpdatingGesturePreviewTrail;
+ }
+
+ private static void clipRect(final Rect out, final int left, final int top, final int right,
+ final int bottom) {
+ out.set(Math.max(out.left, left), Math.max(out.top, top), Math.min(out.right, right),
+ Math.min(out.bottom, bottom));
+ }
+
+ public void setGestureFloatingPreviewText(final String gestureFloatingPreviewText) {
+ if (!mDrawsGestureFloatingPreviewText) return;
+ mGestureFloatingPreviewText = gestureFloatingPreviewText;
+ invalidate();
+ }
+
+ public void dismissGestureFloatingPreviewText() {
+ mDrawingHandler.dismissGestureFloatingPreviewText();
+ }
+
+ private void drawGestureFloatingPreviewText(final Canvas canvas,
+ final String gestureFloatingPreviewText) {
+ if (TextUtils.isEmpty(gestureFloatingPreviewText)) {
+ return;
+ }
+
+ final Paint paint = mTextPaint;
+ final RectF rectangle = mGestureFloatingPreviewRectangle;
+ // TODO: Figure out how we should deal with the floating preview text with multiple moving
+ // fingers.
+
+ // Paint the round rectangle background.
+ final int textHeight = mGestureFloatingPreviewTextHeight;
+ final float textWidth = paint.measureText(gestureFloatingPreviewText);
+ final float hPad = mGestureFloatingPreviewHorizontalPadding;
+ final float vPad = mGestureFloatingPreviewVerticalPadding;
+ final float rectWidth = textWidth + hPad * 2.0f;
+ final float rectHeight = textHeight + vPad * 2.0f;
+ final int canvasWidth = canvas.getWidth();
+ final float rectX = Math.min(Math.max(mLastPointerX - rectWidth / 2.0f, 0.0f),
+ canvasWidth - rectWidth);
+ final float rectY = mLastPointerY - mGestureFloatingPreviewTextOffset - rectHeight;
+ rectangle.set(rectX, rectY, rectX + rectWidth, rectY + rectHeight);
+ final float round = mGestureFloatingPreviewRoundRadius;
+ paint.setColor(mGestureFloatingPreviewColor);
+ canvas.drawRoundRect(rectangle, round, round, paint);
+ // Paint the text preview
+ paint.setColor(mGestureFloatingPreviewTextColor);
+ final float textX = rectX + hPad + textWidth / 2.0f;
+ final float textY = rectY + vPad + textHeight;
+ canvas.drawText(gestureFloatingPreviewText, textX, textY, paint);
+ }
+}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/ShiftKeyState.java b/java/src/com/android/inputmethod/keyboard/internal/ShiftKeyState.java
index edb40c8e7..90db73dee 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/ShiftKeyState.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/ShiftKeyState.java
@@ -18,7 +18,7 @@ package com.android.inputmethod.keyboard.internal;
import android.util.Log;
-/* package */ class ShiftKeyState extends ModifierKeyState {
+/* package */ final class ShiftKeyState extends ModifierKeyState {
private static final int PRESSING_ON_SHIFTED = 3; // both temporary shifted & shift locked
private static final int IGNORING = 4;
diff --git a/java/src/com/android/inputmethod/keyboard/SuddenJumpingTouchEventHandler.java b/java/src/com/android/inputmethod/keyboard/internal/SuddenJumpingTouchEventHandler.java
index 107138395..c53428fe5 100644
--- a/java/src/com/android/inputmethod/keyboard/SuddenJumpingTouchEventHandler.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/SuddenJumpingTouchEventHandler.java
@@ -14,19 +14,21 @@
* the License.
*/
-package com.android.inputmethod.keyboard;
+package com.android.inputmethod.keyboard.internal;
import android.content.Context;
import android.util.Log;
import android.view.MotionEvent;
+import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.keyboard.MainKeyboardView;
import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.ResearchLogger;
-import com.android.inputmethod.latin.Utils;
+import com.android.inputmethod.latin.ResourceUtils;
import com.android.inputmethod.latin.define.ProductionFlag;
+import com.android.inputmethod.research.ResearchLogger;
-public class SuddenJumpingTouchEventHandler {
+public final class SuddenJumpingTouchEventHandler {
private static final String TAG = SuddenJumpingTouchEventHandler.class.getSimpleName();
private static boolean DEBUG_MODE = LatinImeLogger.sDBG;
@@ -51,7 +53,7 @@ public class SuddenJumpingTouchEventHandler {
public SuddenJumpingTouchEventHandler(Context context, ProcessMotionEvent view) {
mView = view;
- mNeedsSuddenJumpingHack = Boolean.parseBoolean(Utils.getDeviceOverrideValue(
+ mNeedsSuddenJumpingHack = Boolean.parseBoolean(ResourceUtils.getDeviceOverrideValue(
context.getResources(), R.array.sudden_jumping_touch_event_device_list, "false"));
}
@@ -70,7 +72,7 @@ public class SuddenJumpingTouchEventHandler {
* the sudden moves subside, a DOWN event is simulated for the second key.
* @param me the motion event
* @return true if the event was consumed, so that it doesn't continue to be handled by
- * {@link LatinKeyboardView}.
+ * {@link MainKeyboardView}.
*/
private boolean handleSuddenJumping(MotionEvent me) {
if (!mNeedsSuddenJumpingHack)
diff --git a/java/src/com/android/inputmethod/keyboard/internal/TouchPositionCorrection.java b/java/src/com/android/inputmethod/keyboard/internal/TouchPositionCorrection.java
new file mode 100644
index 000000000..d8950a713
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/internal/TouchPositionCorrection.java
@@ -0,0 +1,95 @@
+/*
+ * 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.keyboard.internal;
+
+import com.android.inputmethod.latin.LatinImeLogger;
+
+public final class TouchPositionCorrection {
+ private static final int TOUCH_POSITION_CORRECTION_RECORD_SIZE = 3;
+
+ private boolean mEnabled;
+ private float[] mXs;
+ private float[] mYs;
+ private float[] mRadii;
+
+ public void load(final String[] data) {
+ final int dataLength = data.length;
+ if (dataLength % TOUCH_POSITION_CORRECTION_RECORD_SIZE != 0) {
+ if (LatinImeLogger.sDBG) {
+ throw new RuntimeException(
+ "the size of touch position correction data is invalid");
+ }
+ return;
+ }
+
+ final int length = dataLength / TOUCH_POSITION_CORRECTION_RECORD_SIZE;
+ mXs = new float[length];
+ mYs = new float[length];
+ mRadii = new float[length];
+ try {
+ for (int i = 0; i < dataLength; ++i) {
+ final int type = i % TOUCH_POSITION_CORRECTION_RECORD_SIZE;
+ final int index = i / TOUCH_POSITION_CORRECTION_RECORD_SIZE;
+ final float value = Float.parseFloat(data[i]);
+ if (type == 0) {
+ mXs[index] = value;
+ } else if (type == 1) {
+ mYs[index] = value;
+ } else {
+ mRadii[index] = value;
+ }
+ }
+ mEnabled = dataLength > 0;
+ } catch (NumberFormatException e) {
+ if (LatinImeLogger.sDBG) {
+ throw new RuntimeException(
+ "the number format for touch position correction data is invalid");
+ }
+ mEnabled = false;
+ mXs = null;
+ mYs = null;
+ mRadii = null;
+ }
+ }
+
+ // For test only
+ public void setEnabled(final boolean enabled) {
+ mEnabled = enabled;
+ }
+
+ public boolean isValid() {
+ return mEnabled;
+ }
+
+ public int getRows() {
+ return mRadii.length;
+ }
+
+ public float getX(final int row) {
+ return 0.0f;
+ // Touch position correction data for X coordinate is obsolete.
+ // return mXs[row];
+ }
+
+ public float getY(final int row) {
+ return mYs[row];
+ }
+
+ public float getRadius(final int row) {
+ return mRadii[row];
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/AdditionalSubtype.java b/java/src/com/android/inputmethod/latin/AdditionalSubtype.java
index f8f1395b3..509fc1ba3 100644
--- a/java/src/com/android/inputmethod/latin/AdditionalSubtype.java
+++ b/java/src/com/android/inputmethod/latin/AdditionalSubtype.java
@@ -27,22 +27,22 @@ import android.view.inputmethod.InputMethodSubtype;
import java.util.ArrayList;
-public class AdditionalSubtype {
+public final class AdditionalSubtype {
private static final InputMethodSubtype[] EMPTY_SUBTYPE_ARRAY = new InputMethodSubtype[0];
private AdditionalSubtype() {
// This utility class is not publicly instantiable.
}
- public static boolean isAdditionalSubtype(InputMethodSubtype subtype) {
+ public static boolean isAdditionalSubtype(final InputMethodSubtype subtype) {
return subtype.containsExtraValueKey(IS_ADDITIONAL_SUBTYPE);
}
private static final String LOCALE_AND_LAYOUT_SEPARATOR = ":";
- public static final String PREF_SUBTYPE_SEPARATOR = ";";
+ private static final String PREF_SUBTYPE_SEPARATOR = ";";
- public static InputMethodSubtype createAdditionalSubtype(
- String localeString, String keyboardLayoutSetName, String extraValue) {
+ public static InputMethodSubtype createAdditionalSubtype(final String localeString,
+ final String keyboardLayoutSetName, final String extraValue) {
final String layoutExtraValue = KEYBOARD_LAYOUT_SET + "=" + keyboardLayoutSetName;
final String layoutDisplayNameExtraValue;
if (Build.VERSION.SDK_INT >= /* JELLY_BEAN */ 15
@@ -62,7 +62,7 @@ public class AdditionalSubtype {
layoutExtraValue + "," + additionalSubtypeExtraValue, false, false);
}
- public static String getPrefSubtype(InputMethodSubtype subtype) {
+ public static String getPrefSubtype(final InputMethodSubtype subtype) {
final String localeString = subtype.getLocale();
final String keyboardLayoutSetName = SubtypeLocale.getKeyboardLayoutSetName(subtype);
final String layoutExtraValue = KEYBOARD_LAYOUT_SET + "=" + keyboardLayoutSetName;
@@ -74,7 +74,7 @@ public class AdditionalSubtype {
: basePrefSubtype + LOCALE_AND_LAYOUT_SEPARATOR + extraValue;
}
- public static InputMethodSubtype createAdditionalSubtype(String prefSubtype) {
+ public static InputMethodSubtype createAdditionalSubtype(final String prefSubtype) {
final String elems[] = prefSubtype.split(LOCALE_AND_LAYOUT_SEPARATOR);
if (elems.length < 2 || elems.length > 3) {
throw new RuntimeException("Unknown additional subtype specified: " + prefSubtype);
@@ -85,13 +85,13 @@ public class AdditionalSubtype {
return createAdditionalSubtype(localeString, keyboardLayoutSetName, extraValue);
}
- public static InputMethodSubtype[] createAdditionalSubtypesArray(String prefSubtypes) {
+ public static InputMethodSubtype[] createAdditionalSubtypesArray(final String prefSubtypes) {
if (TextUtils.isEmpty(prefSubtypes)) {
return EMPTY_SUBTYPE_ARRAY;
}
final String[] prefSubtypeArray = prefSubtypes.split(PREF_SUBTYPE_SEPARATOR);
final ArrayList<InputMethodSubtype> subtypesList =
- new ArrayList<InputMethodSubtype>(prefSubtypeArray.length);
+ CollectionUtils.newArrayList(prefSubtypeArray.length);
for (final String prefSubtype : prefSubtypeArray) {
final InputMethodSubtype subtype = createAdditionalSubtype(prefSubtype);
if (subtype.getNameResId() == SubtypeLocale.UNKNOWN_KEYBOARD_LAYOUT) {
@@ -103,4 +103,32 @@ public class AdditionalSubtype {
}
return subtypesList.toArray(new InputMethodSubtype[subtypesList.size()]);
}
+
+ public static String createPrefSubtypes(final InputMethodSubtype[] subtypes) {
+ if (subtypes == null || subtypes.length == 0) {
+ return "";
+ }
+ final StringBuilder sb = new StringBuilder();
+ for (final InputMethodSubtype subtype : subtypes) {
+ if (sb.length() > 0) {
+ sb.append(PREF_SUBTYPE_SEPARATOR);
+ }
+ sb.append(getPrefSubtype(subtype));
+ }
+ return sb.toString();
+ }
+
+ public static String createPrefSubtypes(final String[] prefSubtypes) {
+ if (prefSubtypes == null || prefSubtypes.length == 0) {
+ return "";
+ }
+ final StringBuilder sb = new StringBuilder();
+ for (final String prefSubtype : prefSubtypes) {
+ if (sb.length() > 0) {
+ sb.append(PREF_SUBTYPE_SEPARATOR);
+ }
+ sb.append(prefSubtype);
+ }
+ return sb.toString();
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java b/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java
index 779a38823..d12607721 100644
--- a/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java
+++ b/java/src/com/android/inputmethod/latin/AdditionalSubtypeSettings.java
@@ -49,7 +49,7 @@ import com.android.inputmethod.compat.CompatUtils;
import java.util.ArrayList;
import java.util.TreeSet;
-public class AdditionalSubtypeSettings extends PreferenceFragment {
+public final class AdditionalSubtypeSettings extends PreferenceFragment {
private SharedPreferences mPrefs;
private SubtypeLocaleAdapter mSubtypeLocaleAdapter;
private KeyboardLayoutSetAdapter mKeyboardLayoutSetAdapter;
@@ -63,13 +63,13 @@ public class AdditionalSubtypeSettings extends PreferenceFragment {
private static final String KEY_IS_SUBTYPE_ENABLER_NOTIFICATION_DIALOG_OPEN =
"is_subtype_enabler_notification_dialog_open";
private static final String KEY_SUBTYPE_FOR_SUBTYPE_ENABLER = "subtype_for_subtype_enabler";
- static class SubtypeLocaleItem extends Pair<String, String>
+ static final class SubtypeLocaleItem extends Pair<String, String>
implements Comparable<SubtypeLocaleItem> {
- public SubtypeLocaleItem(String localeString, String displayName) {
+ public SubtypeLocaleItem(final String localeString, final String displayName) {
super(localeString, displayName);
}
- public SubtypeLocaleItem(String localeString) {
+ public SubtypeLocaleItem(final String localeString) {
this(localeString, SubtypeLocale.getSubtypeLocaleDisplayName(localeString));
}
@@ -79,17 +79,17 @@ public class AdditionalSubtypeSettings extends PreferenceFragment {
}
@Override
- public int compareTo(SubtypeLocaleItem o) {
+ public int compareTo(final SubtypeLocaleItem o) {
return first.compareTo(o.first);
}
}
- static class SubtypeLocaleAdapter extends ArrayAdapter<SubtypeLocaleItem> {
- public SubtypeLocaleAdapter(Context context) {
+ static final class SubtypeLocaleAdapter extends ArrayAdapter<SubtypeLocaleItem> {
+ public SubtypeLocaleAdapter(final Context context) {
super(context, android.R.layout.simple_spinner_item);
setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- final TreeSet<SubtypeLocaleItem> items = new TreeSet<SubtypeLocaleItem>();
+ final TreeSet<SubtypeLocaleItem> items = CollectionUtils.newTreeSet();
final InputMethodInfo imi = ImfUtils.getInputMethodInfoOfThisIme(context);
final int count = imi.getSubtypeCount();
for (int i = 0; i < count; i++) {
@@ -102,7 +102,8 @@ public class AdditionalSubtypeSettings extends PreferenceFragment {
addAll(items);
}
- public static SubtypeLocaleItem createItem(Context context, String localeString) {
+ public static SubtypeLocaleItem createItem(final Context context,
+ final String localeString) {
if (localeString.equals(SubtypeLocale.NO_LANGUAGE)) {
final String displayName = context.getString(R.string.subtype_no_language);
return new SubtypeLocaleItem(localeString, displayName);
@@ -112,8 +113,8 @@ public class AdditionalSubtypeSettings extends PreferenceFragment {
}
}
- static class KeyboardLayoutSetItem extends Pair<String, String> {
- public KeyboardLayoutSetItem(InputMethodSubtype subtype) {
+ static final class KeyboardLayoutSetItem extends Pair<String, String> {
+ public KeyboardLayoutSetItem(final InputMethodSubtype subtype) {
super(SubtypeLocale.getKeyboardLayoutSetName(subtype),
SubtypeLocale.getKeyboardLayoutSetDisplayName(subtype));
}
@@ -124,8 +125,8 @@ public class AdditionalSubtypeSettings extends PreferenceFragment {
}
}
- static class KeyboardLayoutSetAdapter extends ArrayAdapter<KeyboardLayoutSetItem> {
- public KeyboardLayoutSetAdapter(Context context) {
+ static final class KeyboardLayoutSetAdapter extends ArrayAdapter<KeyboardLayoutSetItem> {
+ public KeyboardLayoutSetAdapter(final Context context) {
super(context, android.R.layout.simple_spinner_item);
setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
@@ -147,7 +148,7 @@ public class AdditionalSubtypeSettings extends PreferenceFragment {
public KeyboardLayoutSetAdapter getKeyboardLayoutSetAdapter();
}
- static class SubtypePreference extends DialogPreference
+ static final class SubtypePreference extends DialogPreference
implements DialogInterface.OnCancelListener {
private static final String KEY_PREFIX = "subtype_pref_";
private static final String KEY_NEW_SUBTYPE = KEY_PREFIX + "new";
@@ -159,13 +160,13 @@ public class AdditionalSubtypeSettings extends PreferenceFragment {
private Spinner mSubtypeLocaleSpinner;
private Spinner mKeyboardLayoutSetSpinner;
- public static SubtypePreference newIncompleteSubtypePreference(
- Context context, SubtypeDialogProxy proxy) {
+ public static SubtypePreference newIncompleteSubtypePreference(final Context context,
+ final SubtypeDialogProxy proxy) {
return new SubtypePreference(context, null, proxy);
}
- public SubtypePreference(Context context, InputMethodSubtype subtype,
- SubtypeDialogProxy proxy) {
+ public SubtypePreference(final Context context, final InputMethodSubtype subtype,
+ final SubtypeDialogProxy proxy) {
super(context, null);
setDialogLayoutResource(R.layout.additional_subtype_dialog);
setPersistent(false);
@@ -185,7 +186,7 @@ public class AdditionalSubtypeSettings extends PreferenceFragment {
return mSubtype;
}
- public void setSubtype(InputMethodSubtype subtype) {
+ public void setSubtype(final InputMethodSubtype subtype) {
mPreviousSubtype = mSubtype;
mSubtype = subtype;
if (isIncomplete()) {
@@ -221,7 +222,7 @@ public class AdditionalSubtypeSettings extends PreferenceFragment {
}
@Override
- protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
+ protected void onPrepareDialogBuilder(final AlertDialog.Builder builder) {
final Context context = builder.getContext();
builder.setCancelable(true).setOnCancelListener(this);
if (isIncomplete()) {
@@ -239,7 +240,7 @@ public class AdditionalSubtypeSettings extends PreferenceFragment {
}
}
- private static void setSpinnerPosition(Spinner spinner, Object itemToSelect) {
+ private static void setSpinnerPosition(final Spinner spinner, final Object itemToSelect) {
final SpinnerAdapter adapter = spinner.getAdapter();
final int count = adapter.getCount();
for (int i = 0; i < count; i++) {
@@ -252,14 +253,14 @@ public class AdditionalSubtypeSettings extends PreferenceFragment {
}
@Override
- public void onCancel(DialogInterface dialog) {
+ public void onCancel(final DialogInterface dialog) {
if (isIncomplete()) {
mProxy.onRemovePressed(this);
}
}
@Override
- public void onClick(DialogInterface dialog, int which) {
+ public void onClick(final DialogInterface dialog, final int which) {
super.onClick(dialog, which);
switch (which) {
case DialogInterface.BUTTON_POSITIVE:
@@ -287,12 +288,12 @@ public class AdditionalSubtypeSettings extends PreferenceFragment {
}
}
- private static int getSpinnerPosition(Spinner spinner) {
+ private static int getSpinnerPosition(final Spinner spinner) {
if (spinner == null) return -1;
return spinner.getSelectedItemPosition();
}
- private static void setSpinnerPosition(Spinner spinner, int position) {
+ private static void setSpinnerPosition(final Spinner spinner, final int position) {
if (spinner == null || position < 0) return;
spinner.setSelection(position);
}
@@ -313,7 +314,7 @@ public class AdditionalSubtypeSettings extends PreferenceFragment {
}
@Override
- protected void onRestoreInstanceState(Parcelable state) {
+ protected void onRestoreInstanceState(final Parcelable state) {
if (!(state instanceof SavedState)) {
super.onRestoreInstanceState(state);
return;
@@ -326,24 +327,24 @@ public class AdditionalSubtypeSettings extends PreferenceFragment {
setSubtype(myState.mSubtype);
}
- static class SavedState extends Preference.BaseSavedState {
+ static final class SavedState extends Preference.BaseSavedState {
InputMethodSubtype mSubtype;
int mSubtypeLocaleSelectedPos;
int mKeyboardLayoutSetSelectedPos;
- public SavedState(Parcelable superState) {
+ public SavedState(final Parcelable superState) {
super(superState);
}
@Override
- public void writeToParcel(Parcel dest, int flags) {
+ public void writeToParcel(final Parcel dest, final int flags) {
super.writeToParcel(dest, flags);
dest.writeInt(mSubtypeLocaleSelectedPos);
dest.writeInt(mKeyboardLayoutSetSelectedPos);
dest.writeParcelable(mSubtype, 0);
}
- public SavedState(Parcel source) {
+ public SavedState(final Parcel source) {
super(source);
mSubtypeLocaleSelectedPos = source.readInt();
mKeyboardLayoutSetSelectedPos = source.readInt();
@@ -354,12 +355,12 @@ public class AdditionalSubtypeSettings extends PreferenceFragment {
public static final Parcelable.Creator<SavedState> CREATOR =
new Parcelable.Creator<SavedState>() {
@Override
- public SavedState createFromParcel(Parcel source) {
+ public SavedState createFromParcel(final Parcel source) {
return new SavedState(source);
}
@Override
- public SavedState[] newArray(int size) {
+ public SavedState[] newArray(final int size) {
return new SavedState[size];
}
};
@@ -371,7 +372,7 @@ public class AdditionalSubtypeSettings extends PreferenceFragment {
}
@Override
- public void onCreate(Bundle savedInstanceState) {
+ public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.additional_subtype_settings);
@@ -381,7 +382,7 @@ public class AdditionalSubtypeSettings extends PreferenceFragment {
}
@Override
- public void onActivityCreated(Bundle savedInstanceState) {
+ public void onActivityCreated(final Bundle savedInstanceState) {
final Context context = getActivity();
mSubtypeLocaleAdapter = new SubtypeLocaleAdapter(context);
mKeyboardLayoutSetAdapter = new KeyboardLayoutSetAdapter(context);
@@ -411,7 +412,7 @@ public class AdditionalSubtypeSettings extends PreferenceFragment {
}
@Override
- public void onSaveInstanceState(Bundle outState) {
+ public void onSaveInstanceState(final Bundle outState) {
super.onSaveInstanceState(outState);
if (mIsAddingNewSubtype) {
outState.putBoolean(KEY_IS_ADDING_NEW_SUBTYPE, true);
@@ -426,7 +427,7 @@ public class AdditionalSubtypeSettings extends PreferenceFragment {
private final SubtypeDialogProxy mSubtypeProxy = new SubtypeDialogProxy() {
@Override
- public void onRemovePressed(SubtypePreference subtypePref) {
+ public void onRemovePressed(final SubtypePreference subtypePref) {
mIsAddingNewSubtype = false;
final PreferenceGroup group = getPreferenceScreen();
group.removePreference(subtypePref);
@@ -434,7 +435,7 @@ public class AdditionalSubtypeSettings extends PreferenceFragment {
}
@Override
- public void onSavePressed(SubtypePreference subtypePref) {
+ public void onSavePressed(final SubtypePreference subtypePref) {
final InputMethodSubtype subtype = subtypePref.getSubtype();
if (!subtypePref.hasBeenModified()) {
return;
@@ -453,7 +454,7 @@ public class AdditionalSubtypeSettings extends PreferenceFragment {
}
@Override
- public void onAddPressed(SubtypePreference subtypePref) {
+ public void onAddPressed(final SubtypePreference subtypePref) {
mIsAddingNewSubtype = false;
final InputMethodSubtype subtype = subtypePref.getSubtype();
if (findDuplicatedSubtype(subtype) == null) {
@@ -481,7 +482,7 @@ public class AdditionalSubtypeSettings extends PreferenceFragment {
}
};
- private void showSubtypeAlreadyExistsToast(InputMethodSubtype subtype) {
+ private void showSubtypeAlreadyExistsToast(final InputMethodSubtype subtype) {
final Context context = getActivity();
final Resources res = context.getResources();
final String message = res.getString(R.string.custom_input_style_already_exists,
@@ -489,14 +490,15 @@ public class AdditionalSubtypeSettings extends PreferenceFragment {
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
}
- private InputMethodSubtype findDuplicatedSubtype(InputMethodSubtype subtype) {
+ private InputMethodSubtype findDuplicatedSubtype(final InputMethodSubtype subtype) {
final String localeString = subtype.getLocale();
final String keyboardLayoutSetName = SubtypeLocale.getKeyboardLayoutSetName(subtype);
return ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet(
getActivity(), localeString, keyboardLayoutSetName);
}
- private AlertDialog createDialog(SubtypePreference subtypePref) {
+ private AlertDialog createDialog(
+ @SuppressWarnings("unused") final SubtypePreference subtypePref) {
final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle(R.string.custom_input_styles_title)
.setMessage(R.string.custom_input_style_note_message)
@@ -519,7 +521,7 @@ public class AdditionalSubtypeSettings extends PreferenceFragment {
return builder.create();
}
- private void setPrefSubtypes(String prefSubtypes, Context context) {
+ private void setPrefSubtypes(final String prefSubtypes, final Context context) {
final PreferenceGroup group = getPreferenceScreen();
group.removeAll();
final InputMethodSubtype[] subtypesArray =
@@ -533,7 +535,7 @@ public class AdditionalSubtypeSettings extends PreferenceFragment {
private InputMethodSubtype[] getSubtypes() {
final PreferenceGroup group = getPreferenceScreen();
- final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
+ final ArrayList<InputMethodSubtype> subtypes = CollectionUtils.newArrayList();
final int count = group.getPreferenceCount();
for (int i = 0; i < count; i++) {
final Preference pref = group.getPreference(i);
@@ -547,23 +549,12 @@ public class AdditionalSubtypeSettings extends PreferenceFragment {
return subtypes.toArray(new InputMethodSubtype[subtypes.size()]);
}
- private String getPrefSubtypes(InputMethodSubtype[] subtypes) {
- final StringBuilder sb = new StringBuilder();
- for (final InputMethodSubtype subtype : subtypes) {
- if (sb.length() > 0) {
- sb.append(AdditionalSubtype.PREF_SUBTYPE_SEPARATOR);
- }
- sb.append(AdditionalSubtype.getPrefSubtype(subtype));
- }
- return sb.toString();
- }
-
@Override
public void onPause() {
super.onPause();
final String oldSubtypes = SettingsValues.getPrefAdditionalSubtypes(mPrefs, getResources());
final InputMethodSubtype[] subtypes = getSubtypes();
- final String prefSubtypes = getPrefSubtypes(subtypes);
+ final String prefSubtypes = AdditionalSubtype.createPrefSubtypes(subtypes);
if (prefSubtypes.equals(oldSubtypes)) {
return;
}
@@ -578,13 +569,13 @@ public class AdditionalSubtypeSettings extends PreferenceFragment {
}
@Override
- public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
final MenuItem addSubtypeMenu = menu.add(0, MENU_ADD_SUBTYPE, 0, R.string.add_style);
addSubtypeMenu.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
}
@Override
- public boolean onOptionsItemSelected(MenuItem item) {
+ public boolean onOptionsItemSelected(final MenuItem item) {
final int itemId = item.getItemId();
if (itemId == MENU_ADD_SUBTYPE) {
final SubtypePreference newSubtype =
diff --git a/java/src/com/android/inputmethod/latin/AssetFileAddress.java b/java/src/com/android/inputmethod/latin/AssetFileAddress.java
index 3549a1561..29c733ba6 100644
--- a/java/src/com/android/inputmethod/latin/AssetFileAddress.java
+++ b/java/src/com/android/inputmethod/latin/AssetFileAddress.java
@@ -24,7 +24,7 @@ import java.io.File;
* the package file. Open it correctly thus requires the name of the package it is in, but
* also the offset in the file and the length of this data. This class encapsulates these three.
*/
-class AssetFileAddress {
+final class AssetFileAddress {
public final String mFilename;
public final long mOffset;
public final long mLength;
diff --git a/java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java b/java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java
index 55664d411..59ef5e09f 100644
--- a/java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java
+++ b/java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java
@@ -30,7 +30,7 @@ import com.android.inputmethod.latin.VibratorUtils;
* It offers a consistent and simple interface that allows LatinIME to forget about the
* complexity of settings and the like.
*/
-public class AudioAndHapticFeedbackManager {
+public final class AudioAndHapticFeedbackManager {
final private SettingsValues mSettingsValues;
final private AudioManager mAudioManager;
final private VibratorUtils mVibratorUtils;
diff --git a/java/src/com/android/inputmethod/latin/AutoCorrection.java b/java/src/com/android/inputmethod/latin/AutoCorrection.java
index e0452483c..84fad158f 100644
--- a/java/src/com/android/inputmethod/latin/AutoCorrection.java
+++ b/java/src/com/android/inputmethod/latin/AutoCorrection.java
@@ -21,34 +21,17 @@ import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import android.text.TextUtils;
import android.util.Log;
-import java.util.ArrayList;
import java.util.concurrent.ConcurrentHashMap;
-public class AutoCorrection {
+public final class AutoCorrection {
private static final boolean DBG = LatinImeLogger.sDBG;
private static final String TAG = AutoCorrection.class.getSimpleName();
+ private static final int MINIMUM_SAFETY_NET_CHAR_LENGTH = 4;
private AutoCorrection() {
// Purely static class: can't instantiate.
}
- public static CharSequence computeAutoCorrectionWord(
- final ConcurrentHashMap<String, Dictionary> dictionaries,
- final WordComposer wordComposer, final ArrayList<SuggestedWordInfo> suggestions,
- final CharSequence consideredWord, final float autoCorrectionThreshold,
- final CharSequence whitelistedWord) {
- if (hasAutoCorrectionForWhitelistedWord(whitelistedWord)) {
- return whitelistedWord;
- } else if (hasAutoCorrectionForConsideredWord(
- dictionaries, wordComposer, suggestions, consideredWord)) {
- return consideredWord;
- } else if (hasAutoCorrectionForBinaryDictionary(wordComposer, suggestions,
- consideredWord, autoCorrectionThreshold)) {
- return suggestions.get(0).mWord;
- }
- return null;
- }
-
public static boolean isValidWord(final ConcurrentHashMap<String, Dictionary> dictionaries,
CharSequence word, boolean ignoreCase) {
if (TextUtils.isEmpty(word)) {
@@ -56,7 +39,6 @@ public class AutoCorrection {
}
final CharSequence lowerCasedWord = word.toString().toLowerCase();
for (final String key : dictionaries.keySet()) {
- if (key.equals(Suggest.DICT_KEY_WHITELIST)) continue;
final Dictionary dictionary = dictionaries.get(key);
// It's unclear how realistically 'dictionary' can be null, but the monkey is somehow
// managing to get null in here. Presumably the language is changing to a language with
@@ -81,7 +63,6 @@ public class AutoCorrection {
}
int maxFreq = -1;
for (final String key : dictionaries.keySet()) {
- if (key.equals(Suggest.DICT_KEY_WHITELIST)) continue;
final Dictionary dictionary = dictionaries.get(key);
if (null == dictionary) continue;
final int tempFreq = dictionary.getFrequency(word);
@@ -92,46 +73,26 @@ public class AutoCorrection {
return maxFreq;
}
- public static boolean allowsToBeAutoCorrected(
+ // Returns true if this is in any of the dictionaries.
+ public static boolean isInTheDictionary(
final ConcurrentHashMap<String, Dictionary> dictionaries,
final CharSequence word, final boolean ignoreCase) {
- final WhitelistDictionary whitelistDictionary =
- (WhitelistDictionary)dictionaries.get(Suggest.DICT_KEY_WHITELIST);
- // If "word" is in the whitelist dictionary, it should not be auto corrected.
- if (whitelistDictionary != null
- && whitelistDictionary.shouldForciblyAutoCorrectFrom(word)) {
- return true;
- }
- return !isValidWord(dictionaries, word, ignoreCase);
- }
-
- private static boolean hasAutoCorrectionForWhitelistedWord(CharSequence whiteListedWord) {
- return whiteListedWord != null;
- }
-
- private static boolean hasAutoCorrectionForConsideredWord(
- final ConcurrentHashMap<String, Dictionary> dictionaries,
- final WordComposer wordComposer, final ArrayList<SuggestedWordInfo> suggestions,
- final CharSequence consideredWord) {
- if (TextUtils.isEmpty(consideredWord)) return false;
- return wordComposer.size() > 1 && suggestions.size() > 0
- && !allowsToBeAutoCorrected(dictionaries, consideredWord, false);
+ return isValidWord(dictionaries, word, ignoreCase);
}
- private static boolean hasAutoCorrectionForBinaryDictionary(WordComposer wordComposer,
- ArrayList<SuggestedWordInfo> suggestions,
+ public static boolean suggestionExceedsAutoCorrectionThreshold(SuggestedWordInfo suggestion,
CharSequence consideredWord, float autoCorrectionThreshold) {
- if (wordComposer.size() > 1 && suggestions.size() > 0) {
- final SuggestedWordInfo autoCorrectionSuggestion = suggestions.get(0);
- //final int autoCorrectionSuggestionScore = sortedScores[0];
- final int autoCorrectionSuggestionScore = autoCorrectionSuggestion.mScore;
+ if (null != suggestion) {
+ // Shortlist a whitelisted word
+ if (suggestion.mKind == SuggestedWordInfo.KIND_WHITELIST) return true;
+ final int autoCorrectionSuggestionScore = suggestion.mScore;
// TODO: when the normalized score of the first suggestion is nearly equals to
// the normalized score of the second suggestion, behave less aggressive.
final float normalizedScore = BinaryDictionary.calcNormalizedScore(
- consideredWord.toString(), autoCorrectionSuggestion.mWord.toString(),
+ consideredWord.toString(), suggestion.mWord.toString(),
autoCorrectionSuggestionScore);
if (DBG) {
- Log.d(TAG, "Normalized " + consideredWord + "," + autoCorrectionSuggestion + ","
+ Log.d(TAG, "Normalized " + consideredWord + "," + suggestion + ","
+ autoCorrectionSuggestionScore + ", " + normalizedScore
+ "(" + autoCorrectionThreshold + ")");
}
@@ -139,10 +100,43 @@ public class AutoCorrection {
if (DBG) {
Log.d(TAG, "Auto corrected by S-threshold.");
}
- return true;
+ return !shouldBlockAutoCorrectionBySafetyNet(consideredWord.toString(),
+ suggestion.mWord);
}
}
return false;
}
+ // TODO: Resolve the inconsistencies between the native auto correction algorithms and
+ // this safety net
+ public static boolean shouldBlockAutoCorrectionBySafetyNet(final String typedWord,
+ final CharSequence suggestion) {
+ // Safety net for auto correction.
+ // Actually if we hit this safety net, it's a bug.
+ // If user selected aggressive auto correction mode, there is no need to use the safety
+ // net.
+ // If the length of typed word is less than MINIMUM_SAFETY_NET_CHAR_LENGTH,
+ // we should not use net because relatively edit distance can be big.
+ final int typedWordLength = typedWord.length();
+ if (typedWordLength < MINIMUM_SAFETY_NET_CHAR_LENGTH) {
+ return false;
+ }
+ final int maxEditDistanceOfNativeDictionary =
+ (typedWordLength < 5 ? 2 : typedWordLength / 2) + 1;
+ final int distance = BinaryDictionary.editDistance(typedWord, suggestion.toString());
+ if (DBG) {
+ Log.d(TAG, "Autocorrected edit distance = " + distance
+ + ", " + maxEditDistanceOfNativeDictionary);
+ }
+ if (distance > maxEditDistanceOfNativeDictionary) {
+ if (DBG) {
+ Log.e(TAG, "Safety net: before = " + typedWord + ", after = " + suggestion);
+ Log.e(TAG, "(Error) The edit distance of this correction exceeds limit. "
+ + "Turning off auto-correction.");
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/BackupAgent.java b/java/src/com/android/inputmethod/latin/BackupAgent.java
index ee070af75..0beb088ac 100644
--- a/java/src/com/android/inputmethod/latin/BackupAgent.java
+++ b/java/src/com/android/inputmethod/latin/BackupAgent.java
@@ -22,7 +22,7 @@ import android.app.backup.SharedPreferencesBackupHelper;
/**
* Backs up the Latin IME shared preferences.
*/
-public class BackupAgent extends BackupAgentHelper {
+public final class BackupAgent extends BackupAgentHelper {
@Override
public void onCreate() {
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index d0613bd72..7184f1d8a 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -18,16 +18,19 @@ package com.android.inputmethod.latin;
import android.content.Context;
import android.text.TextUtils;
+import android.util.SparseArray;
import com.android.inputmethod.keyboard.ProximityInfo;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Locale;
/**
* Implements a static, compacted, binary dictionary of standard words.
*/
-public class BinaryDictionary extends Dictionary {
+public final class BinaryDictionary extends Dictionary {
public static final String DICTIONARY_PACK_AUTHORITY =
"com.android.inputmethod.latin.dictionarypack";
@@ -38,24 +41,46 @@ public class BinaryDictionary extends Dictionary {
* It is necessary to keep it at this value because some languages e.g. German have
* really long words.
*/
- public static final int MAX_WORD_LENGTH = 48;
+ public static final int MAX_WORD_LENGTH = Constants.Dictionary.MAX_WORD_LENGTH;
public static final int MAX_WORDS = 18;
+ public static final int MAX_SPACES = 16;
- private static final String TAG = "BinaryDictionary";
- private static final int MAX_BIGRAMS = 60;
+ private static final String TAG = BinaryDictionary.class.getSimpleName();
+ private static final int MAX_PREDICTIONS = 60;
+ private static final int MAX_RESULTS = Math.max(MAX_PREDICTIONS, MAX_WORDS);
private static final int TYPED_LETTER_MULTIPLIER = 2;
- private int mDicTypeId;
private long mNativeDict;
- private final int[] mInputCodes = new int[MAX_WORD_LENGTH];
- private final char[] mOutputChars = new char[MAX_WORD_LENGTH * MAX_WORDS];
- private final char[] mOutputChars_bigrams = new char[MAX_WORD_LENGTH * MAX_BIGRAMS];
- private final int[] mScores = new int[MAX_WORDS];
- private final int[] mBigramScores = new int[MAX_BIGRAMS];
+ private final Locale mLocale;
+ private final int[] mInputCodePoints = new int[MAX_WORD_LENGTH];
+ // TODO: The below should be int[] mOutputCodePoints
+ private final char[] mOutputChars = new char[MAX_WORD_LENGTH * MAX_RESULTS];
+ private final int[] mSpaceIndices = new int[MAX_SPACES];
+ private final int[] mOutputScores = new int[MAX_RESULTS];
+ private final int[] mOutputTypes = new int[MAX_RESULTS];
private final boolean mUseFullEditDistance;
+ private final SparseArray<DicTraverseSession> mDicTraverseSessions =
+ CollectionUtils.newSparseArray();
+
+ // TODO: There should be a way to remove used DicTraverseSession objects from
+ // {@code mDicTraverseSessions}.
+ private DicTraverseSession getTraverseSession(int traverseSessionId) {
+ synchronized(mDicTraverseSessions) {
+ DicTraverseSession traverseSession = mDicTraverseSessions.get(traverseSessionId);
+ if (traverseSession == null) {
+ traverseSession = mDicTraverseSessions.get(traverseSessionId);
+ if (traverseSession == null) {
+ traverseSession = new DicTraverseSession(mLocale, mNativeDict);
+ mDicTraverseSessions.put(traverseSessionId, traverseSession);
+ }
+ }
+ return traverseSession;
+ }
+ }
+
/**
* Constructor for the binary dictionary. This is supposed to be called from the
* dictionary factory.
@@ -65,14 +90,13 @@ public class BinaryDictionary extends Dictionary {
* @param offset the offset of the dictionary data within the file.
* @param length the length of the binary data.
* @param useFullEditDistance whether to use the full edit distance in suggestions
+ * @param dictType the dictionary type, as a human-readable string
*/
public BinaryDictionary(final Context context,
final String filename, final long offset, final long length,
- final boolean useFullEditDistance, final Locale locale) {
- // Note: at the moment a binary dictionary is always of the "main" type.
- // Initializing this here will help transitioning out of the scheme where
- // the Suggest class knows everything about every single dictionary.
- mDicTypeId = Suggest.DIC_MAIN;
+ final boolean useFullEditDistance, final Locale locale, final String dictType) {
+ super(dictType);
+ mLocale = locale;
mUseFullEditDistance = useFullEditDistance;
loadDictionary(filename, offset, length);
}
@@ -82,121 +106,91 @@ public class BinaryDictionary extends Dictionary {
}
private native long openNative(String sourceDir, long dictOffset, long dictSize,
- int typedLetterMultiplier, int fullWordMultiplier, int maxWordLength, int maxWords);
+ int typedLetterMultiplier, int fullWordMultiplier, int maxWordLength, int maxWords,
+ int maxPredictions);
private native void closeNative(long dict);
- private native int getFrequencyNative(long dict, int[] word, int wordLength);
+ private native int getFrequencyNative(long dict, int[] word);
private native boolean isValidBigramNative(long dict, int[] word1, int[] word2);
- private native int getSuggestionsNative(long dict, long proximityInfo, int[] xCoordinates,
- int[] yCoordinates, int[] inputCodes, int codesSize, int[] prevWordForBigrams,
- boolean useFullEditDistance, char[] outputChars, int[] scores);
- private native int getBigramsNative(long dict, int[] prevWord, int prevWordLength,
- int[] inputCodes, int inputCodesLength, char[] outputChars, int[] scores,
- int maxWordLength, int maxBigrams);
- private static native float calcNormalizedScoreNative(
- char[] before, int beforeLength, char[] after, int afterLength, int score);
- private static native int editDistanceNative(
- char[] before, int beforeLength, char[] after, int afterLength);
-
+ private native int getSuggestionsNative(long dict, long proximityInfo, long traverseSession,
+ int[] xCoordinates, int[] yCoordinates, int[] times, int[] pointerIds,
+ int[] inputCodePoints, int codesSize, int commitPoint, boolean isGesture,
+ int[] prevWordCodePointArray, boolean useFullEditDistance, char[] outputChars,
+ int[] outputScores, int[] outputIndices, int[] outputTypes);
+ private static native float calcNormalizedScoreNative(char[] before, char[] after, int score);
+ private static native int editDistanceNative(char[] before, char[] after);
+
+ // TODO: Move native dict into session
private final void loadDictionary(String path, long startOffset, long length) {
- mNativeDict = openNative(path, startOffset, length,
- TYPED_LETTER_MULTIPLIER, FULL_WORD_SCORE_MULTIPLIER, MAX_WORD_LENGTH, MAX_WORDS);
+ mNativeDict = openNative(path, startOffset, length, TYPED_LETTER_MULTIPLIER,
+ FULL_WORD_SCORE_MULTIPLIER, MAX_WORD_LENGTH, MAX_WORDS, MAX_PREDICTIONS);
}
@Override
- public void getBigrams(final WordComposer codes, final CharSequence previousWord,
- final WordCallback callback) {
- if (mNativeDict == 0) return;
-
- int[] codePoints = StringUtils.toCodePointArray(previousWord.toString());
- Arrays.fill(mOutputChars_bigrams, (char) 0);
- Arrays.fill(mBigramScores, 0);
-
- int codesSize = codes.size();
- Arrays.fill(mInputCodes, -1);
- if (codesSize > 0) {
- mInputCodes[0] = codes.getCodeAt(0);
- }
-
- int count = getBigramsNative(mNativeDict, codePoints, codePoints.length, mInputCodes,
- codesSize, mOutputChars_bigrams, mBigramScores, MAX_WORD_LENGTH, MAX_BIGRAMS);
- if (count > MAX_BIGRAMS) {
- count = MAX_BIGRAMS;
- }
-
- for (int j = 0; j < count; ++j) {
- if (codesSize > 0 && mBigramScores[j] < 1) break;
- final int start = j * MAX_WORD_LENGTH;
- int len = 0;
- while (len < MAX_WORD_LENGTH && mOutputChars_bigrams[start + len] != 0) {
- ++len;
- }
- if (len > 0) {
- callback.addWord(mOutputChars_bigrams, start, len, mBigramScores[j],
- mDicTypeId, Dictionary.BIGRAM);
- }
- }
+ public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
+ final CharSequence prevWord, final ProximityInfo proximityInfo) {
+ return getSuggestionsWithSessionId(composer, prevWord, proximityInfo, 0);
}
- // proximityInfo and/or prevWordForBigrams may not be null.
@Override
- public void getWords(final WordComposer codes, final CharSequence prevWordForBigrams,
- final WordCallback callback, final ProximityInfo proximityInfo) {
- final int count = getSuggestions(codes, prevWordForBigrams, proximityInfo, mOutputChars,
- mScores);
+ public ArrayList<SuggestedWordInfo> getSuggestionsWithSessionId(final WordComposer composer,
+ final CharSequence prevWord, final ProximityInfo proximityInfo, int sessionId) {
+ if (!isValidDictionary()) return null;
+
+ Arrays.fill(mInputCodePoints, Constants.NOT_A_CODE);
+ // TODO: toLowerCase in the native code
+ final int[] prevWordCodePointArray = (null == prevWord)
+ ? null : StringUtils.toCodePointArray(prevWord.toString());
+ final int composerSize = composer.size();
+
+ final boolean isGesture = composer.isBatchMode();
+ if (composerSize <= 1 || !isGesture) {
+ if (composerSize > MAX_WORD_LENGTH - 1) return null;
+ for (int i = 0; i < composerSize; i++) {
+ mInputCodePoints[i] = composer.getCodeAt(i);
+ }
+ }
+ final InputPointers ips = composer.getInputPointers();
+ final int codesSize = isGesture ? ips.getPointerSize() : composerSize;
+ // proximityInfo and/or prevWordForBigrams may not be null.
+ final int tmpCount = getSuggestionsNative(mNativeDict,
+ proximityInfo.getNativeProximityInfo(), getTraverseSession(sessionId).getSession(),
+ ips.getXCoordinates(), ips.getYCoordinates(), ips.getTimes(), ips.getPointerIds(),
+ mInputCodePoints, codesSize, 0 /* commitPoint */, isGesture, prevWordCodePointArray,
+ mUseFullEditDistance, mOutputChars, mOutputScores, mSpaceIndices, mOutputTypes);
+ final int count = Math.min(tmpCount, MAX_PREDICTIONS);
+
+ final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList();
for (int j = 0; j < count; ++j) {
- if (mScores[j] < 1) break;
+ if (composerSize > 0 && mOutputScores[j] < 1) break;
final int start = j * MAX_WORD_LENGTH;
int len = 0;
while (len < MAX_WORD_LENGTH && mOutputChars[start + len] != 0) {
++len;
}
if (len > 0) {
- callback.addWord(mOutputChars, start, len, mScores[j], mDicTypeId,
- Dictionary.UNIGRAM);
+ final int score = SuggestedWordInfo.KIND_WHITELIST == mOutputTypes[j]
+ ? SuggestedWordInfo.MAX_SCORE : mOutputScores[j];
+ suggestions.add(new SuggestedWordInfo(
+ new String(mOutputChars, start, len), score, mOutputTypes[j], mDictType));
}
}
+ return suggestions;
}
/* package for test */ boolean isValidDictionary() {
return mNativeDict != 0;
}
- // proximityInfo may not be null.
- /* package for test */ int getSuggestions(final WordComposer codes,
- final CharSequence prevWordForBigrams, final ProximityInfo proximityInfo,
- char[] outputChars, int[] scores) {
- if (!isValidDictionary()) return -1;
-
- final int codesSize = codes.size();
- // Won't deal with really long words.
- if (codesSize > MAX_WORD_LENGTH - 1) return -1;
-
- Arrays.fill(mInputCodes, WordComposer.NOT_A_CODE);
- for (int i = 0; i < codesSize; i++) {
- mInputCodes[i] = codes.getCodeAt(i);
- }
- Arrays.fill(outputChars, (char) 0);
- Arrays.fill(scores, 0);
-
- final int[] prevWordCodePointArray = null == prevWordForBigrams
- ? null : StringUtils.toCodePointArray(prevWordForBigrams.toString());
-
- // TODO: pass the previous word to native code
- return getSuggestionsNative(
- mNativeDict, proximityInfo.getNativeProximityInfo(),
- codes.getXCoordinates(), codes.getYCoordinates(), mInputCodes, codesSize,
- prevWordCodePointArray, mUseFullEditDistance, outputChars, scores);
- }
-
public static float calcNormalizedScore(String before, String after, int score) {
- return calcNormalizedScoreNative(before.toCharArray(), before.length(),
- after.toCharArray(), after.length(), score);
+ return calcNormalizedScoreNative(before.toCharArray(), after.toCharArray(), score);
}
public static int editDistance(String before, String after) {
- return editDistanceNative(
- before.toCharArray(), before.length(), after.toCharArray(), after.length());
+ if (before == null || after == null) {
+ throw new IllegalArgumentException();
+ }
+ return editDistanceNative(before.toCharArray(), after.toCharArray());
}
@Override
@@ -207,8 +201,8 @@ public class BinaryDictionary extends Dictionary {
@Override
public int getFrequency(CharSequence word) {
if (word == null) return -1;
- int[] chars = StringUtils.toCodePointArray(word.toString());
- return getFrequencyNative(mNativeDict, chars, chars.length);
+ int[] codePoints = StringUtils.toCodePointArray(word.toString());
+ return getFrequencyNative(mNativeDict, codePoints);
}
// TODO: Add a batch process version (isValidBigramMultiple?) to avoid excessive numbers of jni
@@ -221,11 +215,20 @@ public class BinaryDictionary extends Dictionary {
}
@Override
- public synchronized void close() {
+ public void close() {
+ synchronized (mDicTraverseSessions) {
+ final int sessionsSize = mDicTraverseSessions.size();
+ for (int index = 0; index < sessionsSize; ++index) {
+ final DicTraverseSession traverseSession = mDicTraverseSessions.valueAt(index);
+ if (traverseSession != null) {
+ traverseSession.close();
+ }
+ }
+ }
closeInternal();
}
- private void closeInternal() {
+ private synchronized void closeInternal() {
if (mNativeDict != 0) {
closeNative(mNativeDict);
mNativeDict = 0;
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
index 236c198ad..b0b65edb6 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
@@ -30,7 +30,6 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -40,14 +39,14 @@ import java.util.Locale;
* Group class for static methods to help with creation and getting of the binary dictionary
* file from the dictionary provider
*/
-public class BinaryDictionaryFileDumper {
+public final class BinaryDictionaryFileDumper {
private static final String TAG = BinaryDictionaryFileDumper.class.getSimpleName();
private static final boolean DEBUG = false;
/**
* The size of the temporary buffer to copy files.
*/
- private static final int FILE_READ_BUFFER_SIZE = 1024;
+ private static final int FILE_READ_BUFFER_SIZE = 8192;
// TODO: make the following data common with the native code
private static final byte[] MAGIC_NUMBER_VERSION_1 =
new byte[] { (byte)0x78, (byte)0xB1, (byte)0x00, (byte)0x00 };
@@ -99,7 +98,7 @@ public class BinaryDictionaryFileDumper {
}
try {
- final List<WordListInfo> list = new ArrayList<WordListInfo>();
+ final List<WordListInfo> list = CollectionUtils.newArrayList();
do {
final String wordListId = c.getString(0);
final String wordListLocale = c.getString(1);
@@ -150,7 +149,7 @@ public class BinaryDictionaryFileDumper {
final Uri.Builder wordListUriBuilder = getProviderUriBuilder(id);
final String finalFileName = BinaryDictionaryGetter.getCacheFileName(id, locale, context);
- final String tempFileName = finalFileName + ".tmp";
+ final String tempFileName = BinaryDictionaryGetter.getTempFileName(id, context);
for (int mode = MODE_MIN; mode <= MODE_MAX; ++mode) {
InputStream originalSourceStream = null;
@@ -201,6 +200,7 @@ public class BinaryDictionaryFileDumper {
outputStream.flush();
outputStream.close();
final File finalFile = new File(finalFileName);
+ finalFile.delete();
if (!outputFile.renameTo(finalFile)) {
throw new IOException("Can't move the file to its final name");
}
@@ -267,7 +267,7 @@ public class BinaryDictionaryFileDumper {
final ContentResolver resolver = context.getContentResolver();
final List<WordListInfo> idList = getWordListWordListInfos(locale, context,
hasDefaultWordList);
- final List<AssetFileAddress> fileAddressList = new ArrayList<AssetFileAddress>();
+ final List<AssetFileAddress> fileAddressList = CollectionUtils.newArrayList();
for (WordListInfo id : idList) {
final AssetFileAddress afd = cacheWordList(id.mId, id.mLocale, resolver, context);
if (null != afd) {
@@ -287,6 +287,7 @@ public class BinaryDictionaryFileDumper {
* @param input the stream to be copied.
* @param output an output stream to copy the data to.
*/
+ // TODO: make output a BufferedOutputStream
private static void checkMagicAndCopyFileTo(final BufferedInputStream input,
final FileOutputStream output) throws FileNotFoundException, IOException {
// Check the magic number
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
index 063243e1b..c747dc673 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
@@ -16,6 +16,9 @@
package com.android.inputmethod.latin;
+import com.android.inputmethod.latin.makedict.BinaryDictInputOutput;
+import com.android.inputmethod.latin.makedict.FormatSpec;
+
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.PackageManager.NameNotFoundException;
@@ -23,6 +26,10 @@ import android.content.res.AssetFileDescriptor;
import android.util.Log;
import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.BufferUnderflowException;
+import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Locale;
@@ -30,7 +37,7 @@ import java.util.Locale;
/**
* Helper class to get the address of a mmap'able dictionary file.
*/
-class BinaryDictionaryGetter {
+final class BinaryDictionaryGetter {
/**
* Used for Log actions from this class
@@ -51,6 +58,9 @@ class BinaryDictionaryGetter {
private static final String MAIN_DICTIONARY_CATEGORY = "main";
public static final String ID_CATEGORY_SEPARATOR = ":";
+ // The key considered to read the version attribute in a dictionary file.
+ private static String VERSION_KEY = "version";
+
// Prevents this from being instantiated
private BinaryDictionaryGetter() {}
@@ -154,6 +164,18 @@ class BinaryDictionaryGetter {
}
/**
+ * Generates a unique temporary file name in the app cache directory.
+ *
+ * This is unique as long as it doesn't get called twice in the same millisecond by the same
+ * thread, which should be more than enough for our purposes.
+ */
+ public static String getTempFileName(String id, Context context) {
+ final String fileName = replaceFileNameDangerousCharacters(id);
+ return context.getCacheDir() + File.separator + fileName + "."
+ + Thread.currentThread().getId() + "." + System.currentTimeMillis();
+ }
+
+ /**
* Returns a file address from a resource, or null if it cannot be opened.
*/
private static AssetFileAddress loadFallbackResource(final Context context,
@@ -168,7 +190,7 @@ class BinaryDictionaryGetter {
context.getApplicationInfo().sourceDir, afd.getStartOffset(), afd.getLength());
}
- static private class DictPackSettings {
+ private static final class DictPackSettings {
final SharedPreferences mDictPreferences;
public DictPackSettings(final Context context) {
Context dictPackContext = null;
@@ -227,7 +249,7 @@ class BinaryDictionaryGetter {
/**
* Utility class for the {@link #getCachedWordLists} method
*/
- private static class FileAndMatchLevel {
+ private static final class FileAndMatchLevel {
final File mFile;
final int mMatchLevel;
public FileAndMatchLevel(final File file, final int matchLevel) {
@@ -254,8 +276,7 @@ class BinaryDictionaryGetter {
final Context context) {
final File[] directoryList = getCachedDirectoryList(context);
if (null == directoryList) return EMPTY_FILE_ARRAY;
- final HashMap<String, FileAndMatchLevel> cacheFiles =
- new HashMap<String, FileAndMatchLevel>();
+ final HashMap<String, FileAndMatchLevel> cacheFiles = CollectionUtils.newHashMap();
for (File directory : directoryList) {
if (!directory.isDirectory()) continue;
final String dirLocale = getWordListIdFromFileName(directory.getName());
@@ -336,6 +357,57 @@ class BinaryDictionaryGetter {
return MAIN_DICTIONARY_CATEGORY.equals(idArray[0]);
}
+ // ## HACK ## we prevent usage of a dictionary before version 18 for English only. The reason
+ // for this is, since those do not include whitelist entries, the new code with an old version
+ // of the dictionary would lose whitelist functionality.
+ private static boolean hackCanUseDictionaryFile(final Locale locale, final File f) {
+ // Only for English - other languages didn't have a whitelist, hence this
+ // ad-hoc ## HACK ##
+ if (!Locale.ENGLISH.getLanguage().equals(locale.getLanguage())) return true;
+
+ FileInputStream inStream = null;
+ try {
+ // Read the version of the file
+ inStream = new FileInputStream(f);
+ 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.readInt();
+ final int headerSize = buffer.readInt();
+ final HashMap<String, String> options = CollectionUtils.newHashMap();
+ BinaryDictInputOutput.populateOptions(buffer, headerSize, options);
+
+ final String version = options.get(VERSION_KEY);
+ if (null == version) {
+ // No version in the options : the format is unexpected
+ return false;
+ }
+ // Version 18 is the first one to include the whitelist
+ // Obviously this is a big ## HACK ##
+ return Integer.parseInt(version) >= 18;
+ } catch (java.io.FileNotFoundException e) {
+ return false;
+ } catch (java.io.IOException e) {
+ return false;
+ } catch (NumberFormatException e) {
+ return false;
+ } catch (BufferUnderflowException e) {
+ return false;
+ } finally {
+ if (inStream != null) {
+ try {
+ inStream.close();
+ } catch (IOException e) {
+ // do nothing
+ }
+ }
+ }
+ }
+
/**
* Returns a list of file addresses for a given locale, trying relevant methods in order.
*
@@ -362,18 +434,19 @@ class BinaryDictionaryGetter {
final DictPackSettings dictPackSettings = new DictPackSettings(context);
boolean foundMainDict = false;
- final ArrayList<AssetFileAddress> fileList = new ArrayList<AssetFileAddress>();
+ final ArrayList<AssetFileAddress> fileList = CollectionUtils.newArrayList();
// cachedWordLists may not be null, see doc for getCachedDictionaryList
for (final File f : cachedWordLists) {
final String wordListId = getWordListIdFromFileName(f.getName());
- if (isMainWordListId(wordListId)) {
+ final boolean canUse = f.canRead() && hackCanUseDictionaryFile(locale, f);
+ if (canUse && isMainWordListId(wordListId)) {
foundMainDict = true;
}
if (!dictPackSettings.isWordListActive(wordListId)) continue;
- if (f.canRead()) {
+ if (canUse) {
fileList.add(AssetFileAddress.makeFromFileName(f.getPath()));
} else {
- Log.e(TAG, "Found a cached dictionary file but cannot read it");
+ Log.e(TAG, "Found a cached dictionary file but cannot read or use it");
}
}
diff --git a/java/src/com/android/inputmethod/latin/BoundedTreeSet.java b/java/src/com/android/inputmethod/latin/BoundedTreeSet.java
new file mode 100644
index 000000000..7f7ff31c8
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/BoundedTreeSet.java
@@ -0,0 +1,49 @@
+/*
+ * 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;
+
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.TreeSet;
+
+/**
+ * A TreeSet that is bounded in size and throws everything that's smaller than its limit
+ */
+public final class BoundedTreeSet extends TreeSet<SuggestedWordInfo> {
+ private final int mCapacity;
+ public BoundedTreeSet(final Comparator<SuggestedWordInfo> comparator, final int capacity) {
+ super(comparator);
+ mCapacity = capacity;
+ }
+
+ @Override
+ public boolean add(final SuggestedWordInfo e) {
+ if (size() < mCapacity) return super.add(e);
+ if (comparator().compare(e, last()) > 0) return false;
+ super.add(e);
+ pollLast(); // removes the last element
+ return true;
+ }
+
+ @Override
+ public boolean addAll(final Collection<? extends SuggestedWordInfo> e) {
+ if (null == e) return false;
+ return super.addAll(e);
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/CollectionUtils.java b/java/src/com/android/inputmethod/latin/CollectionUtils.java
new file mode 100644
index 000000000..c75f2df5c
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/CollectionUtils.java
@@ -0,0 +1,95 @@
+/*
+ * 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;
+
+import android.util.SparseArray;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+public final class CollectionUtils {
+ private CollectionUtils() {
+ // This utility class is not publicly instantiable.
+ }
+
+ public static <K,V> HashMap<K,V> newHashMap() {
+ return new HashMap<K,V>();
+ }
+
+ public static <K,V> TreeMap<K,V> newTreeMap() {
+ return new TreeMap<K,V>();
+ }
+
+ public static <K, V> Map<K,V> newSynchronizedTreeMap() {
+ final TreeMap<K,V> treeMap = newTreeMap();
+ return Collections.synchronizedMap(treeMap);
+ }
+
+ public static <K,V> ConcurrentHashMap<K,V> newConcurrentHashMap() {
+ return new ConcurrentHashMap<K,V>();
+ }
+
+ public static <E> HashSet<E> newHashSet() {
+ return new HashSet<E>();
+ }
+
+ public static <E> TreeSet<E> newTreeSet() {
+ return new TreeSet<E>();
+ }
+
+ public static <E> ArrayList<E> newArrayList() {
+ return new ArrayList<E>();
+ }
+
+ public static <E> ArrayList<E> newArrayList(final int initialCapacity) {
+ return new ArrayList<E>(initialCapacity);
+ }
+
+ public static <E> ArrayList<E> newArrayList(final Collection<E> collection) {
+ return new ArrayList<E>(collection);
+ }
+
+ public static <E> LinkedList<E> newLinkedList() {
+ return new LinkedList<E>();
+ }
+
+ public static <E> CopyOnWriteArrayList<E> newCopyOnWriteArrayList() {
+ return new CopyOnWriteArrayList<E>();
+ }
+
+ public static <E> CopyOnWriteArrayList<E> newCopyOnWriteArrayList(
+ final Collection<E> collection) {
+ return new CopyOnWriteArrayList<E>(collection);
+ }
+
+ public static <E> CopyOnWriteArrayList<E> newCopyOnWriteArrayList(final E[] array) {
+ return new CopyOnWriteArrayList<E>(array);
+ }
+
+ public static <E> SparseArray<E> newSparseArray() {
+ return new SparseArray<E>();
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/Constants.java b/java/src/com/android/inputmethod/latin/Constants.java
index e79db367c..57e12a64f 100644
--- a/java/src/com/android/inputmethod/latin/Constants.java
+++ b/java/src/com/android/inputmethod/latin/Constants.java
@@ -16,9 +16,14 @@
package com.android.inputmethod.latin;
-import android.view.inputmethod.EditorInfo;
-
public final class Constants {
+ public static final class Color {
+ /**
+ * The alpha value for fully opaque.
+ */
+ public final static int ALPHA_OPAQUE = 255;
+ }
+
public static final class ImeOption {
/**
* The private IME option used to indicate that no microphone should be shown for a given
@@ -47,7 +52,7 @@ public final class Constants {
* The private IME option used to indicate that the given text field needs ASCII code points
* input.
*
- * @deprecated Use {@link EditorInfo#IME_FLAG_FORCE_ASCII}.
+ * @deprecated Use EditorInfo#IME_FLAG_FORCE_ASCII.
*/
@SuppressWarnings("dep-ann")
public static final String FORCE_ASCII = "forceAscii";
@@ -121,6 +126,21 @@ public final class Constants {
}
}
+ public static class Dictionary {
+ public static final int MAX_WORD_LENGTH = 48;
+
+ private Dictionary() {
+ // This utility class is no publicly instantiable.
+ }
+ }
+
+ public static final int NOT_A_CODE = -1;
+
+ // See {@link KeyboardActionListener.Adapter#isInvalidCoordinate(int)}.
+ public static final int NOT_A_COORDINATE = -1;
+ public static final int SUGGESTION_STRIP_COORDINATE = -2;
+ public static final int SPELL_CHECKER_COORDINATE = -3;
+
private Constants() {
// This utility class is not publicly instantiable.
}
diff --git a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
index 34308dfb3..5edc4314f 100644
--- a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
@@ -52,6 +52,9 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
/** The number of contacts in the most recent dictionary rebuild. */
static private int sContactCountAtLastRebuild = 0;
+ /** The locale for this contacts dictionary. Controls name bigram predictions. */
+ public final Locale mLocale;
+
private ContentObserver mObserver;
/**
@@ -59,8 +62,9 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
*/
private final boolean mUseFirstLastBigrams;
- public ContactsBinaryDictionary(final Context context, final int dicTypeId, Locale locale) {
- super(context, getFilenameWithLocale(NAME, locale.toString()), dicTypeId);
+ public ContactsBinaryDictionary(final Context context, Locale locale) {
+ super(context, getFilenameWithLocale(NAME, locale.toString()), Dictionary.TYPE_CONTACTS);
+ mLocale = locale;
mUseFirstLastBigrams = useFirstLastBigramsForLocale(locale);
registerObserver(context);
@@ -116,12 +120,6 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
}
}
- @Override
- public void getBigrams(final WordComposer codes, final CharSequence previousWord,
- final WordCallback callback) {
- super.getBigrams(codes, previousWord, callback);
- }
-
private boolean useFirstLastBigramsForLocale(Locale locale) {
// TODO: Add firstname/lastname bigram rules for other languages.
if (locale != null && locale.getLanguage().equals(Locale.ENGLISH.getLanguage())) {
@@ -163,7 +161,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
* bigrams depending on locale.
*/
private void addName(String name) {
- int len = name.codePointCount(0, name.length());
+ int len = StringUtils.codePointCount(name);
String prevWord = null;
// TODO: Better tokenization for non-Latin writing systems
for (int i = 0; i < len; i++) {
@@ -173,7 +171,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
i = end - 1;
// Don't add single letter words, possibly confuses
// capitalization of i.
- final int wordLen = word.codePointCount(0, word.length());
+ final int wordLen = StringUtils.codePointCount(word);
if (wordLen < MAX_WORD_LENGTH && wordLen > 1) {
super.addWord(word, null /* shortcut */, FREQUENCY_FOR_CONTACTS);
if (!TextUtils.isEmpty(prevWord)) {
@@ -262,14 +260,14 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
* Checks if the words in a name are in the current binary dictionary.
*/
private boolean isNameInDictionary(String name) {
- int len = name.codePointCount(0, name.length());
+ int len = StringUtils.codePointCount(name);
String prevWord = null;
for (int i = 0; i < len; i++) {
if (Character.isLetter(name.codePointAt(i))) {
int end = getWordEndPosition(name, len, i);
String word = name.substring(i, end);
i = end - 1;
- final int wordLen = word.codePointCount(0, word.length());
+ final int wordLen = StringUtils.codePointCount(word);
if (wordLen < MAX_WORD_LENGTH && wordLen > 1) {
if (!TextUtils.isEmpty(prevWord) && mUseFirstLastBigrams) {
if (!super.isValidBigramLocked(prevWord, word)) {
diff --git a/java/src/com/android/inputmethod/latin/ContactsDictionary.java b/java/src/com/android/inputmethod/latin/ContactsDictionary.java
deleted file mode 100644
index cbfbd0ec8..000000000
--- a/java/src/com/android/inputmethod/latin/ContactsDictionary.java
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * Copyright (C) 2009 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;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.database.ContentObserver;
-import android.database.Cursor;
-import android.os.SystemClock;
-import android.provider.BaseColumns;
-import android.provider.ContactsContract.Contacts;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.inputmethod.keyboard.Keyboard;
-
-// TODO: This class is superseded by {@link ContactsBinaryDictionary}. Should be cleaned up.
-/**
- * An expandable dictionary that stores the words from Contacts provider.
- *
- * @deprecated Use {@link ContactsBinaryDictionary}.
- */
-@Deprecated
-public class ContactsDictionary extends ExpandableDictionary {
-
- private static final String[] PROJECTION = {
- BaseColumns._ID,
- Contacts.DISPLAY_NAME,
- };
-
- private static final String TAG = "ContactsDictionary";
-
- /**
- * Frequency for contacts information into the dictionary
- */
- private static final int FREQUENCY_FOR_CONTACTS = 40;
- private static final int FREQUENCY_FOR_CONTACTS_BIGRAM = 90;
-
- private static final int INDEX_NAME = 1;
-
- private ContentObserver mObserver;
-
- private long mLastLoadedContacts;
-
- public ContactsDictionary(final Context context, final int dicTypeId) {
- super(context, dicTypeId);
- registerObserver(context);
- loadDictionary();
- }
-
- private synchronized void registerObserver(final Context context) {
- // Perform a managed query. The Activity will handle closing and requerying the cursor
- // when needed.
- if (mObserver != null) return;
- ContentResolver cres = context.getContentResolver();
- cres.registerContentObserver(
- Contacts.CONTENT_URI, true, mObserver = new ContentObserver(null) {
- @Override
- public void onChange(boolean self) {
- setRequiresReload(true);
- }
- });
- }
-
- public void reopen(final Context context) {
- registerObserver(context);
- }
-
- @Override
- public synchronized void close() {
- if (mObserver != null) {
- getContext().getContentResolver().unregisterContentObserver(mObserver);
- mObserver = null;
- }
- super.close();
- }
-
- @Override
- public void startDictionaryLoadingTaskLocked() {
- long now = SystemClock.uptimeMillis();
- if (mLastLoadedContacts == 0
- || now - mLastLoadedContacts > 30 * 60 * 1000 /* 30 minutes */) {
- super.startDictionaryLoadingTaskLocked();
- }
- }
-
- @Override
- public void loadDictionaryAsync() {
- try {
- Cursor cursor = getContext().getContentResolver()
- .query(Contacts.CONTENT_URI, PROJECTION, null, null, null);
- if (cursor != null) {
- addWords(cursor);
- }
- } catch(IllegalStateException e) {
- Log.e(TAG, "Contacts DB is having problems");
- }
- mLastLoadedContacts = SystemClock.uptimeMillis();
- }
-
- @Override
- public void getBigrams(final WordComposer codes, final CharSequence previousWord,
- final WordCallback callback) {
- // Do not return bigrams from Contacts when nothing was typed.
- if (codes.size() <= 0) return;
- super.getBigrams(codes, previousWord, callback);
- }
-
- private void addWords(Cursor cursor) {
- clearDictionary();
-
- final int maxWordLength = getMaxWordLength();
- try {
- if (cursor.moveToFirst()) {
- while (!cursor.isAfterLast()) {
- String name = cursor.getString(INDEX_NAME);
-
- if (name != null && -1 == name.indexOf('@')) {
- int len = name.length();
- String prevWord = null;
-
- // TODO: Better tokenization for non-Latin writing systems
- for (int i = 0; i < len; i++) {
- if (Character.isLetter(name.charAt(i))) {
- int j;
- for (j = i + 1; j < len; j++) {
- char c = name.charAt(j);
-
- if (!(c == Keyboard.CODE_DASH
- || c == Keyboard.CODE_SINGLE_QUOTE
- || Character.isLetter(c))) {
- break;
- }
- }
-
- String word = name.substring(i, j);
- i = j - 1;
-
- // Safeguard against adding really long words. Stack
- // may overflow due to recursion
- // Also don't add single letter words, possibly confuses
- // capitalization of i.
- final int wordLen = word.length();
- if (wordLen < maxWordLength && wordLen > 1) {
- super.addWord(word, null /* shortcut */,
- FREQUENCY_FOR_CONTACTS);
- if (!TextUtils.isEmpty(prevWord)) {
- super.setBigramAndGetFrequency(prevWord, word,
- FREQUENCY_FOR_CONTACTS_BIGRAM);
- }
- prevWord = word;
- }
- }
- }
- }
- cursor.moveToNext();
- }
- }
- cursor.close();
- } catch(IllegalStateException e) {
- Log.e(TAG, "Contacts DB is having problems");
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/DebugSettings.java b/java/src/com/android/inputmethod/latin/DebugSettings.java
index af7649863..3af3cab2c 100644
--- a/java/src/com/android/inputmethod/latin/DebugSettings.java
+++ b/java/src/com/android/inputmethod/latin/DebugSettings.java
@@ -23,17 +23,20 @@ import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;
import android.os.Process;
import android.preference.CheckBoxPreference;
+import android.preference.Preference;
import android.preference.PreferenceFragment;
import android.util.Log;
import com.android.inputmethod.keyboard.KeyboardSwitcher;
+import com.android.inputmethod.research.ResearchLogger;
-public class DebugSettings extends PreferenceFragment
+public final class DebugSettings extends PreferenceFragment
implements SharedPreferences.OnSharedPreferenceChangeListener {
private static final String TAG = DebugSettings.class.getSimpleName();
private static final String DEBUG_MODE_KEY = "debug_mode";
public static final String FORCE_NON_DISTINCT_MULTITOUCH_KEY = "force_non_distinct_multitouch";
+ public static final String PREF_USABILITY_STUDY_MODE = "usability_study_mode";
private boolean mServiceNeedsRestart = false;
private CheckBoxPreference mDebugMode;
@@ -45,6 +48,14 @@ public class DebugSettings extends PreferenceFragment
SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
prefs.registerOnSharedPreferenceChangeListener(this);
+ final Preference usabilityStudyPref = findPreference(PREF_USABILITY_STUDY_MODE);
+ if (usabilityStudyPref instanceof CheckBoxPreference) {
+ final CheckBoxPreference checkbox = (CheckBoxPreference)usabilityStudyPref;
+ checkbox.setChecked(prefs.getBoolean(PREF_USABILITY_STUDY_MODE,
+ ResearchLogger.DEFAULT_USABILITY_STUDY_MODE));
+ checkbox.setSummary(R.string.settings_warning_researcher_mode);
+ }
+
mServiceNeedsRestart = false;
mDebugMode = (CheckBoxPreference) findPreference(DEBUG_MODE_KEY);
updateDebugMode();
diff --git a/java/src/com/android/inputmethod/latin/DebugSettingsActivity.java b/java/src/com/android/inputmethod/latin/DebugSettingsActivity.java
index cde20606a..6ef19ee82 100644
--- a/java/src/com/android/inputmethod/latin/DebugSettingsActivity.java
+++ b/java/src/com/android/inputmethod/latin/DebugSettingsActivity.java
@@ -20,7 +20,7 @@ import android.content.Intent;
import android.os.Bundle;
import android.preference.PreferenceActivity;
-public class DebugSettingsActivity extends PreferenceActivity {
+public final class DebugSettingsActivity extends PreferenceActivity {
@Override
public Intent getIntent() {
final Intent modIntent = new Intent(super.getIntent());
diff --git a/java/src/com/android/inputmethod/latin/DicTraverseSession.java b/java/src/com/android/inputmethod/latin/DicTraverseSession.java
new file mode 100644
index 000000000..ce1b64660
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/DicTraverseSession.java
@@ -0,0 +1,75 @@
+/*
+ * 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;
+
+import java.util.Locale;
+
+public final class DicTraverseSession {
+ static {
+ JniUtils.loadNativeLibrary();
+ }
+
+ private native long setDicTraverseSessionNative(String locale);
+ private native void initDicTraverseSessionNative(long nativeDicTraverseSession,
+ long dictionary, int[] previousWord, int previousWordLength);
+ private native void releaseDicTraverseSessionNative(long nativeDicTraverseSession);
+
+ private long mNativeDicTraverseSession;
+
+ public DicTraverseSession(Locale locale, long dictionary) {
+ mNativeDicTraverseSession = createNativeDicTraverseSession(
+ locale != null ? locale.toString() : "");
+ initSession(dictionary);
+ }
+
+ public long getSession() {
+ return mNativeDicTraverseSession;
+ }
+
+ public void initSession(long dictionary) {
+ initSession(dictionary, null, 0);
+ }
+
+ public void initSession(long dictionary, int[] previousWord, int previousWordLength) {
+ initDicTraverseSessionNative(
+ mNativeDicTraverseSession, dictionary, previousWord, previousWordLength);
+ }
+
+ private final long createNativeDicTraverseSession(String locale) {
+ return setDicTraverseSessionNative(locale);
+ }
+
+ private void closeInternal() {
+ if (mNativeDicTraverseSession != 0) {
+ releaseDicTraverseSessionNative(mNativeDicTraverseSession);
+ mNativeDicTraverseSession = 0;
+ }
+ }
+
+ public void close() {
+ closeInternal();
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ closeInternal();
+ } finally {
+ super.finalize();
+ }
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/Dictionary.java b/java/src/com/android/inputmethod/latin/Dictionary.java
index 7cd9bc2a8..88d0c09dd 100644
--- a/java/src/com/android/inputmethod/latin/Dictionary.java
+++ b/java/src/com/android/inputmethod/latin/Dictionary.java
@@ -17,6 +17,9 @@
package com.android.inputmethod.latin;
import com.android.inputmethod.keyboard.ProximityInfo;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+
+import java.util.ArrayList;
/**
* Abstract base class for a dictionary that can do a fuzzy search for words based on a set of key
@@ -28,54 +31,41 @@ public abstract class Dictionary {
*/
protected static final int FULL_WORD_SCORE_MULTIPLIER = 2;
- public static final int UNIGRAM = 0;
- public static final int BIGRAM = 1;
-
public static final int NOT_A_PROBABILITY = -1;
- /**
- * Interface to be implemented by classes requesting words to be fetched from the dictionary.
- * @see #getWords(WordComposer, CharSequence, WordCallback, ProximityInfo)
- */
- public interface WordCallback {
- /**
- * Adds a word to a list of suggestions. The word is expected to be ordered based on
- * the provided score.
- * @param word the character array containing the word
- * @param wordOffset starting offset of the word in the character array
- * @param wordLength length of valid characters in the character array
- * @param score the score of occurrence. This is normalized between 1 and 255, but
- * can exceed those limits
- * @param dicTypeId of the dictionary where word was from
- * @param dataType tells type of this data, either UNIGRAM or BIGRAM
- * @return true if the word was added, false if no more words are required
- */
- boolean addWord(char[] word, int wordOffset, int wordLength, int score, int dicTypeId,
- int dataType);
+
+ public static final String TYPE_USER_TYPED = "user_typed";
+ public static final String TYPE_APPLICATION_DEFINED = "application_defined";
+ public static final String TYPE_HARDCODED = "hardcoded"; // punctuation signs and such
+ public static final String TYPE_MAIN = "main";
+ public static final String TYPE_CONTACTS = "contacts";
+ // User dictionary, the system-managed one.
+ public static final String TYPE_USER = "user";
+ // User history dictionary internal to LatinIME.
+ public static final String TYPE_USER_HISTORY = "history";
+ protected final String mDictType;
+
+ public Dictionary(final String dictType) {
+ mDictType = dictType;
}
/**
- * Searches for words in the dictionary that match the characters in the composer. Matched
- * words are added through the callback object.
- * @param composer the key sequence to match
- * @param prevWordForBigrams the previous word, or null if none
- * @param callback the callback object to send matched words to as possible candidates
+ * Searches for suggestions for a given context. For the moment the context is only the
+ * previous word.
+ * @param composer the key sequence to match with coordinate info, as a WordComposer
+ * @param prevWord the previous word, or null if none
* @param proximityInfo the object for key proximity. May be ignored by some implementations.
- * @see WordCallback#addWord(char[], int, int, int, int, int)
+ * @return the list of suggestions (possibly null if none)
*/
- abstract public void getWords(final WordComposer composer,
- final CharSequence prevWordForBigrams, final WordCallback callback,
- final ProximityInfo proximityInfo);
+ // TODO: pass more context than just the previous word, to enable better suggestions (n-gram
+ // and more)
+ abstract public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
+ final CharSequence prevWord, final ProximityInfo proximityInfo);
- /**
- * Searches for pairs in the bigram dictionary that matches the previous word and all the
- * possible words following are added through the callback object.
- * @param composer the key sequence to match
- * @param previousWord the word before
- * @param callback the callback object to send possible word following previous word
- */
- public void getBigrams(final WordComposer composer, final CharSequence previousWord,
- final WordCallback callback) {
- // empty base implementation
+ // The default implementation of this method ignores sessionId.
+ // Subclasses that want to use sessionId need to override this method.
+ public ArrayList<SuggestedWordInfo> getSuggestionsWithSessionId(final WordComposer composer,
+ final CharSequence prevWord, final ProximityInfo proximityInfo, int sessionId) {
+ return getSuggestions(composer, prevWord, proximityInfo);
}
/**
@@ -115,4 +105,12 @@ public abstract class Dictionary {
public void close() {
// empty base implementation
}
+
+ /**
+ * Subclasses may override to indicate that this Dictionary is not yet properly initialized.
+ */
+
+ public boolean isInitialized() {
+ return true;
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/DictionaryCollection.java b/java/src/com/android/inputmethod/latin/DictionaryCollection.java
index 1a05fcd86..d3b120989 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryCollection.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryCollection.java
@@ -17,9 +17,11 @@
package com.android.inputmethod.latin;
import com.android.inputmethod.keyboard.ProximityInfo;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import android.util.Log;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -27,40 +29,48 @@ import java.util.concurrent.CopyOnWriteArrayList;
/**
* Class for a collection of dictionaries that behave like one dictionary.
*/
-public class DictionaryCollection extends Dictionary {
+public final class DictionaryCollection extends Dictionary {
private final String TAG = DictionaryCollection.class.getSimpleName();
protected final CopyOnWriteArrayList<Dictionary> mDictionaries;
- public DictionaryCollection() {
- mDictionaries = new CopyOnWriteArrayList<Dictionary>();
+ public DictionaryCollection(final String dictType) {
+ super(dictType);
+ mDictionaries = CollectionUtils.newCopyOnWriteArrayList();
}
- public DictionaryCollection(Dictionary... dictionaries) {
+ public DictionaryCollection(final String dictType, Dictionary... dictionaries) {
+ super(dictType);
if (null == dictionaries) {
- mDictionaries = new CopyOnWriteArrayList<Dictionary>();
+ mDictionaries = CollectionUtils.newCopyOnWriteArrayList();
} else {
- mDictionaries = new CopyOnWriteArrayList<Dictionary>(dictionaries);
+ mDictionaries = CollectionUtils.newCopyOnWriteArrayList(dictionaries);
mDictionaries.removeAll(Collections.singleton(null));
}
}
- public DictionaryCollection(Collection<Dictionary> dictionaries) {
- mDictionaries = new CopyOnWriteArrayList<Dictionary>(dictionaries);
+ public DictionaryCollection(final String dictType, Collection<Dictionary> dictionaries) {
+ super(dictType);
+ mDictionaries = CollectionUtils.newCopyOnWriteArrayList(dictionaries);
mDictionaries.removeAll(Collections.singleton(null));
}
@Override
- public void getWords(final WordComposer composer, final CharSequence prevWordForBigrams,
- final WordCallback callback, final ProximityInfo proximityInfo) {
- for (final Dictionary dict : mDictionaries)
- dict.getWords(composer, prevWordForBigrams, callback, proximityInfo);
- }
-
- @Override
- public void getBigrams(final WordComposer composer, final CharSequence previousWord,
- final WordCallback callback) {
- for (final Dictionary dict : mDictionaries)
- dict.getBigrams(composer, previousWord, callback);
+ public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
+ final CharSequence prevWord, final ProximityInfo proximityInfo) {
+ final CopyOnWriteArrayList<Dictionary> dictionaries = mDictionaries;
+ if (dictionaries.isEmpty()) return null;
+ // To avoid creating unnecessary objects, we get the list out of the first
+ // dictionary and add the rest to it if not null, hence the get(0)
+ ArrayList<SuggestedWordInfo> suggestions = dictionaries.get(0).getSuggestions(composer,
+ prevWord, proximityInfo);
+ if (null == suggestions) suggestions = CollectionUtils.newArrayList();
+ final int length = dictionaries.size();
+ for (int i = 1; i < length; ++ i) {
+ final ArrayList<SuggestedWordInfo> sugg = dictionaries.get(i).getSuggestions(composer,
+ prevWord, proximityInfo);
+ if (null != sugg) suggestions.addAll(sugg);
+ }
+ return suggestions;
}
@Override
@@ -82,8 +92,9 @@ public class DictionaryCollection extends Dictionary {
return maxFreq;
}
- public boolean isEmpty() {
- return mDictionaries.isEmpty();
+ @Override
+ public boolean isInitialized() {
+ return !mDictionaries.isEmpty();
}
@Override
diff --git a/java/src/com/android/inputmethod/latin/DictionaryFactory.java b/java/src/com/android/inputmethod/latin/DictionaryFactory.java
index a22d73af7..f381973ae 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryFactory.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryFactory.java
@@ -29,7 +29,7 @@ import java.util.Locale;
/**
* Factory for dictionary instances.
*/
-public class DictionaryFactory {
+public final class DictionaryFactory {
private static final String TAG = DictionaryFactory.class.getSimpleName();
// This class must be located in the same package as LatinIME.java.
private static final String RESOURCE_PACKAGE_NAME =
@@ -49,17 +49,18 @@ public class DictionaryFactory {
final Locale locale, final boolean useFullEditDistance) {
if (null == locale) {
Log.e(TAG, "No locale defined for dictionary");
- return new DictionaryCollection(createBinaryDictionary(context, locale));
+ return new DictionaryCollection(Dictionary.TYPE_MAIN,
+ createBinaryDictionary(context, locale));
}
- final LinkedList<Dictionary> dictList = new LinkedList<Dictionary>();
+ final LinkedList<Dictionary> dictList = CollectionUtils.newLinkedList();
final ArrayList<AssetFileAddress> assetFileList =
BinaryDictionaryGetter.getDictionaryFiles(locale, context);
if (null != assetFileList) {
for (final AssetFileAddress f : assetFileList) {
final BinaryDictionary binaryDictionary =
new BinaryDictionary(context, f.mFilename, f.mOffset, f.mLength,
- useFullEditDistance, locale);
+ useFullEditDistance, locale, Dictionary.TYPE_MAIN);
if (binaryDictionary.isValidDictionary()) {
dictList.add(binaryDictionary);
}
@@ -69,7 +70,7 @@ public class DictionaryFactory {
// If the list is empty, that means we should not use any dictionary (for example, the user
// explicitly disabled the main dictionary), so the following is okay. dictList is never
// null, but if for some reason it is, DictionaryCollection handles it gracefully.
- return new DictionaryCollection(dictList);
+ return new DictionaryCollection(Dictionary.TYPE_MAIN, dictList);
}
/**
@@ -112,7 +113,7 @@ public class DictionaryFactory {
return null;
}
return new BinaryDictionary(context, sourceDir, afd.getStartOffset(), afd.getLength(),
- false /* useFullEditDistance */, locale);
+ false /* useFullEditDistance */, locale, Dictionary.TYPE_MAIN);
} catch (android.content.res.Resources.NotFoundException e) {
Log.e(TAG, "Could not find the resource");
return null;
@@ -140,7 +141,7 @@ public class DictionaryFactory {
long startOffset, long length, final boolean useFullEditDistance, Locale locale) {
if (dictionary.isFile()) {
return new BinaryDictionary(context, dictionary.getAbsolutePath(), startOffset, length,
- useFullEditDistance, locale);
+ useFullEditDistance, locale, Dictionary.TYPE_MAIN);
} else {
Log.e(TAG, "Could not find the file. path=" + dictionary.getAbsolutePath());
return null;
diff --git a/java/src/com/android/inputmethod/latin/DictionaryPackInstallBroadcastReceiver.java b/java/src/com/android/inputmethod/latin/DictionaryPackInstallBroadcastReceiver.java
index 9c37d7673..f2f3fbded 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryPackInstallBroadcastReceiver.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryPackInstallBroadcastReceiver.java
@@ -27,7 +27,7 @@ import android.net.Uri;
/**
* Takes action to reload the necessary data when a dictionary pack was added/removed.
*/
-public class DictionaryPackInstallBroadcastReceiver extends BroadcastReceiver {
+public final class DictionaryPackInstallBroadcastReceiver extends BroadcastReceiver {
final LatinIME mService;
/**
diff --git a/java/src/com/android/inputmethod/latin/EditingUtils.java b/java/src/com/android/inputmethod/latin/EditingUtils.java
deleted file mode 100644
index 0f34d50bb..000000000
--- a/java/src/com/android/inputmethod/latin/EditingUtils.java
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * Copyright (C) 2009 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;
-
-import android.view.inputmethod.ExtractedText;
-import android.view.inputmethod.ExtractedTextRequest;
-import android.view.inputmethod.InputConnection;
-
-import java.util.regex.Pattern;
-
-/**
- * Utility methods to deal with editing text through an InputConnection.
- */
-public class EditingUtils {
- /**
- * Number of characters we want to look back in order to identify the previous word
- */
- // Provision for a long word pair and a separator
- private static final int LOOKBACK_CHARACTER_NUM = BinaryDictionary.MAX_WORD_LENGTH * 2 + 1;
- private static final int INVALID_CURSOR_POSITION = -1;
-
- private EditingUtils() {
- // Unintentional empty constructor for singleton.
- }
-
- private static int getCursorPosition(InputConnection connection) {
- if (null == connection) return INVALID_CURSOR_POSITION;
- final ExtractedText extracted = connection.getExtractedText(new ExtractedTextRequest(), 0);
- if (extracted == null) {
- return INVALID_CURSOR_POSITION;
- }
- return extracted.startOffset + extracted.selectionStart;
- }
-
- /**
- * @param connection connection to the current text field.
- * @param separators characters which may separate words
- * @return the word that surrounds the cursor, including up to one trailing
- * separator. For example, if the field contains "he|llo world", where |
- * represents the cursor, then "hello " will be returned.
- */
- public static String getWordAtCursor(InputConnection connection, String separators) {
- // getWordRangeAtCursor returns null if the connection is null
- Range r = getWordRangeAtCursor(connection, separators);
- return (r == null) ? null : r.mWord;
- }
-
- /**
- * Represents a range of text, relative to the current cursor position.
- */
- public static class Range {
- /** Characters before selection start */
- public final int mCharsBefore;
-
- /**
- * Characters after selection start, including one trailing word
- * separator.
- */
- public final int mCharsAfter;
-
- /** The actual characters that make up a word */
- public final String mWord;
-
- public Range(int charsBefore, int charsAfter, String word) {
- if (charsBefore < 0 || charsAfter < 0) {
- throw new IndexOutOfBoundsException();
- }
- this.mCharsBefore = charsBefore;
- this.mCharsAfter = charsAfter;
- this.mWord = word;
- }
- }
-
- private static Range getWordRangeAtCursor(InputConnection connection, String sep) {
- if (connection == null || sep == null) {
- return null;
- }
- CharSequence before = connection.getTextBeforeCursor(1000, 0);
- CharSequence after = connection.getTextAfterCursor(1000, 0);
- if (before == null || after == null) {
- return null;
- }
-
- // Find first word separator before the cursor
- int start = before.length();
- while (start > 0 && !isWhitespace(before.charAt(start - 1), sep)) start--;
-
- // Find last word separator after the cursor
- int end = -1;
- while (++end < after.length() && !isWhitespace(after.charAt(end), sep)) {
- // Nothing to do here.
- }
-
- int cursor = getCursorPosition(connection);
- if (start >= 0 && cursor + end <= after.length() + before.length()) {
- String word = before.toString().substring(start, before.length())
- + after.toString().substring(0, end);
- return new Range(before.length() - start, end, word);
- }
-
- return null;
- }
-
- private static boolean isWhitespace(int code, String whitespace) {
- return whitespace.contains(String.valueOf((char) code));
- }
-
- private static final Pattern spaceRegex = Pattern.compile("\\s+");
-
- public static CharSequence getPreviousWord(InputConnection connection,
- String sentenceSeperators) {
- //TODO: Should fix this. This could be slow!
- if (null == connection) return null;
- CharSequence prev = connection.getTextBeforeCursor(LOOKBACK_CHARACTER_NUM, 0);
- return getPreviousWord(prev, sentenceSeperators);
- }
-
- // Get the word before the whitespace preceding the non-whitespace preceding the cursor.
- // Also, it won't return words that end in a separator.
- // Example :
- // "abc def|" -> abc
- // "abc def |" -> abc
- // "abc def. |" -> abc
- // "abc def . |" -> def
- // "abc|" -> null
- // "abc |" -> null
- // "abc. def|" -> null
- public static CharSequence getPreviousWord(CharSequence prev, String sentenceSeperators) {
- if (prev == null) return null;
- String[] w = spaceRegex.split(prev);
-
- // If we can't find two words, or we found an empty word, return null.
- if (w.length < 2 || w[w.length - 2].length() <= 0) return null;
-
- // If ends in a separator, return null
- char lastChar = w[w.length - 2].charAt(w[w.length - 2].length() - 1);
- if (sentenceSeperators.contains(String.valueOf(lastChar))) return null;
-
- return w[w.length - 2];
- }
-
- public static CharSequence getThisWord(InputConnection connection, String sentenceSeperators) {
- if (null == connection) return null;
- final CharSequence prev = connection.getTextBeforeCursor(LOOKBACK_CHARACTER_NUM, 0);
- return getThisWord(prev, sentenceSeperators);
- }
-
- // Get the word immediately before the cursor, even if there is whitespace between it and
- // the cursor - but not if there is punctuation.
- // Example :
- // "abc def|" -> def
- // "abc def |" -> def
- // "abc def. |" -> null
- // "abc def . |" -> null
- public static CharSequence getThisWord(CharSequence prev, String sentenceSeperators) {
- if (prev == null) return null;
- String[] w = spaceRegex.split(prev);
-
- // No word : return null
- if (w.length < 1 || w[w.length - 1].length() <= 0) return null;
-
- // If ends in a separator, return null
- char lastChar = w[w.length - 1].charAt(w[w.length - 1].length() - 1);
- if (sentenceSeperators.contains(String.valueOf(lastChar))) return null;
-
- return w[w.length - 1];
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
index c65404cbc..b93c17f11 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
@@ -19,7 +19,9 @@ import android.os.SystemClock;
import android.util.Log;
import com.android.inputmethod.keyboard.ProximityInfo;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.makedict.BinaryDictInputOutput;
+import com.android.inputmethod.latin.makedict.FormatSpec;
import com.android.inputmethod.latin.makedict.FusionDictionary;
import com.android.inputmethod.latin.makedict.FusionDictionary.Node;
import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
@@ -61,7 +63,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
* that filename.
*/
private static final HashMap<String, DictionaryController> sSharedDictionaryControllers =
- new HashMap<String, DictionaryController>();
+ CollectionUtils.newHashMap();
/** The application context. */
protected final Context mContext;
@@ -75,9 +77,6 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
/** The expandable fusion dictionary used to generate the binary dictionary. */
private FusionDictionary mFusionDictionary;
- /** The dictionary type id. */
- public final int mDicTypeId;
-
/**
* The name of this dictionary, used as the filename for storing the binary dictionary. Multiple
* dictionary instances with the same filename is supported, with access controlled by
@@ -91,6 +90,10 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
/** Controls access to the local binary dictionary for this instance. */
private final DictionaryController mLocalDictionaryController = new DictionaryController();
+ private static final int BINARY_DICT_VERSION = 1;
+ private static final FormatSpec.FormatOptions FORMAT_OPTIONS =
+ new FormatSpec.FormatOptions(BINARY_DICT_VERSION);
+
/**
* Abstract method for loading the unigrams and bigrams of a given dictionary in a background
* thread.
@@ -123,11 +126,11 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
* @param context The application context of the parent.
* @param filename The filename for this binary dictionary. Multiple dictionaries with the same
* filename is supported.
- * @param dictType The type of this dictionary.
+ * @param dictType the dictionary type, as a human-readable string
*/
public ExpandableBinaryDictionary(
- final Context context, final String filename, final int dictType) {
- mDicTypeId = dictType;
+ final Context context, final String filename, final String dictType) {
+ super(dictType);
mFilename = filename;
mContext = context;
mBinaryDictionary = null;
@@ -161,9 +164,9 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
* the native side.
*/
public void clearFusionDictionary() {
+ final HashMap<String, String> attributes = CollectionUtils.newHashMap();
mFusionDictionary = new FusionDictionary(new Node(),
- new FusionDictionary.DictionaryOptions(new HashMap<String, String>(), false,
- false));
+ new FusionDictionary.DictionaryOptions(attributes, false, false));
}
/**
@@ -174,12 +177,12 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
// considering performance regression.
protected void addWord(final String word, final String shortcutTarget, final int frequency) {
if (shortcutTarget == null) {
- mFusionDictionary.add(word, frequency, null);
+ mFusionDictionary.add(word, frequency, null, false /* isNotAWord */);
} else {
// TODO: Do this in the subclass, with this class taking an arraylist.
- final ArrayList<WeightedString> shortcutTargets = new ArrayList<WeightedString>();
+ final ArrayList<WeightedString> shortcutTargets = CollectionUtils.newArrayList();
shortcutTargets.add(new WeightedString(shortcutTarget, frequency));
- mFusionDictionary.add(word, frequency, shortcutTargets);
+ mFusionDictionary.add(word, frequency, shortcutTargets, false /* isNotAWord */);
}
}
@@ -194,46 +197,19 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
}
@Override
- public void getWords(final WordComposer codes, final CharSequence prevWordForBigrams,
- final WordCallback callback, final ProximityInfo proximityInfo) {
- asyncReloadDictionaryIfRequired();
- getWordsInner(codes, prevWordForBigrams, callback, proximityInfo);
- }
-
- protected final void getWordsInner(final WordComposer codes,
- final CharSequence prevWordForBigrams, final WordCallback callback,
- final ProximityInfo proximityInfo) {
- // Ensure that there are no concurrent calls to getWords. If there are, do nothing and
- // return.
- if (mLocalDictionaryController.tryLock()) {
- try {
- if (mBinaryDictionary != null) {
- mBinaryDictionary.getWords(codes, prevWordForBigrams, callback, proximityInfo);
- }
- } finally {
- mLocalDictionaryController.unlock();
- }
- }
- }
-
- @Override
- public void getBigrams(final WordComposer codes, final CharSequence previousWord,
- final WordCallback callback) {
+ public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
+ final CharSequence prevWord, final ProximityInfo proximityInfo) {
asyncReloadDictionaryIfRequired();
- getBigramsInner(codes, previousWord, callback);
- }
-
- protected void getBigramsInner(final WordComposer codes, final CharSequence previousWord,
- final WordCallback callback) {
if (mLocalDictionaryController.tryLock()) {
try {
if (mBinaryDictionary != null) {
- mBinaryDictionary.getBigrams(codes, previousWord, callback);
+ return mBinaryDictionary.getSuggestions(composer, prevWord, proximityInfo);
}
} finally {
mLocalDictionaryController.unlock();
}
}
+ return null;
}
@Override
@@ -306,7 +282,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
// Build the new binary dictionary
final BinaryDictionary newBinaryDictionary =
new BinaryDictionary(mContext, filename, 0, length, true /* useFullEditDistance */,
- null);
+ null, mDictType);
if (mBinaryDictionary != null) {
// Ensure all threads accessing the current dictionary have finished before swapping in
@@ -339,7 +315,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
FileOutputStream out = null;
try {
out = new FileOutputStream(tempFile);
- BinaryDictInputOutput.writeDictionaryBinary(out, mFusionDictionary, 1);
+ BinaryDictInputOutput.writeDictionaryBinary(out, mFusionDictionary, FORMAT_OPTIONS);
out.flush();
out.close();
tempFile.renameTo(file);
diff --git a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
index 34a92fd30..8cdc2a0af 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
@@ -17,10 +17,11 @@
package com.android.inputmethod.latin;
import android.content.Context;
+import android.text.TextUtils;
-import com.android.inputmethod.keyboard.KeyDetector;
import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.ProximityInfo;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.UserHistoryForgettingCurveUtils.ForgettingCurveParams;
import java.util.ArrayList;
@@ -37,7 +38,6 @@ public class ExpandableDictionary extends Dictionary {
private Context mContext;
private char[] mWordBuilder = new char[BinaryDictionary.MAX_WORD_LENGTH];
- private int mDicTypeId;
private int mMaxDepth;
private int mInputLength;
@@ -48,7 +48,7 @@ public class ExpandableDictionary extends Dictionary {
// Use this lock before touching mUpdatingDictionary & mRequiresDownload
private Object mUpdatingLock = new Object();
- private static class Node {
+ private static final class Node {
Node() {}
char mCode;
int mFrequency;
@@ -60,7 +60,7 @@ public class ExpandableDictionary extends Dictionary {
LinkedList<NextWord> mNGrams; // Supports ngram
}
- private static class NodeArray {
+ private static final class NodeArray {
Node[] mData;
int mLength = 0;
private static final int INCREMENT = 2;
@@ -88,7 +88,7 @@ public class ExpandableDictionary extends Dictionary {
public int notifyTypedAgainAndGetFrequency();
}
- private static class NextStaticWord implements NextWord {
+ private static final class NextStaticWord implements NextWord {
public final Node mWord;
private final int mFrequency;
public NextStaticWord(Node word, int frequency) {
@@ -117,7 +117,7 @@ public class ExpandableDictionary extends Dictionary {
}
}
- private static class NextHistoryWord implements NextWord {
+ private static final class NextHistoryWord implements NextWord {
public final Node mWord;
public final ForgettingCurveParams mFcp;
@@ -151,11 +151,11 @@ public class ExpandableDictionary extends Dictionary {
private int[][] mCodes;
- public ExpandableDictionary(Context context, int dicTypeId) {
+ public ExpandableDictionary(final Context context, final String dictType) {
+ super(dictType);
mContext = context;
clearDictionary();
mCodes = new int[BinaryDictionary.MAX_WORD_LENGTH][];
- mDicTypeId = dicTypeId;
}
public void loadDictionary() {
@@ -230,7 +230,7 @@ public class ExpandableDictionary extends Dictionary {
childNode.mTerminal = true;
if (isShortcutOnly) {
if (null == childNode.mShortcutTargets) {
- childNode.mShortcutTargets = new ArrayList<char[]>();
+ childNode.mShortcutTargets = CollectionUtils.newArrayList();
}
childNode.mShortcutTargets.add(shortcutTarget.toCharArray());
} else {
@@ -247,27 +247,43 @@ public class ExpandableDictionary extends Dictionary {
}
@Override
- public void getWords(final WordComposer codes, final CharSequence prevWordForBigrams,
- final WordCallback callback, final ProximityInfo proximityInfo) {
+ public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
+ final CharSequence prevWord, final ProximityInfo proximityInfo) {
+ if (reloadDictionaryIfRequired()) return null;
+ if (composer.size() > 1) {
+ if (composer.size() >= BinaryDictionary.MAX_WORD_LENGTH) {
+ return null;
+ }
+ final ArrayList<SuggestedWordInfo> suggestions =
+ getWordsInner(composer, prevWord, proximityInfo);
+ return suggestions;
+ } else {
+ if (TextUtils.isEmpty(prevWord)) return null;
+ final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList();
+ runBigramReverseLookUp(prevWord, suggestions);
+ return suggestions;
+ }
+ }
+
+ // This reloads the dictionary if required, and returns whether it's currently updating its
+ // contents or not.
+ // @VisibleForTesting
+ boolean reloadDictionaryIfRequired() {
synchronized (mUpdatingLock) {
// If we need to update, start off a background task
if (mRequiresReload) startDictionaryLoadingTaskLocked();
- // Currently updating contacts, don't return any results.
- if (mUpdatingDictionary) return;
- }
- if (codes.size() >= BinaryDictionary.MAX_WORD_LENGTH) {
- return;
+ return mUpdatingDictionary;
}
- getWordsInner(codes, prevWordForBigrams, callback, proximityInfo);
}
- protected final void getWordsInner(final WordComposer codes,
- final CharSequence prevWordForBigrams, final WordCallback callback,
- final ProximityInfo proximityInfo) {
+ protected ArrayList<SuggestedWordInfo> getWordsInner(final WordComposer codes,
+ final CharSequence prevWordForBigrams, final ProximityInfo proximityInfo) {
+ final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList();
mInputLength = codes.size();
if (mCodes.length < mInputLength) mCodes = new int[mInputLength][];
- final int[] xCoordinates = codes.getXCoordinates();
- final int[] yCoordinates = codes.getYCoordinates();
+ final InputPointers ips = codes.getInputPointers();
+ final int[] xCoordinates = ips.getXCoordinates();
+ final int[] yCoordinates = ips.getYCoordinates();
// Cache the codes so that we don't have to lookup an array list
for (int i = 0; i < mInputLength; i++) {
// TODO: Calculate proximity info here.
@@ -275,16 +291,17 @@ public class ExpandableDictionary extends Dictionary {
mCodes[i] = new int[ProximityInfo.MAX_PROXIMITY_CHARS_SIZE];
}
final int x = xCoordinates != null && i < xCoordinates.length ?
- xCoordinates[i] : WordComposer.NOT_A_COORDINATE;
+ xCoordinates[i] : Constants.NOT_A_COORDINATE;
final int y = xCoordinates != null && i < yCoordinates.length ?
- yCoordinates[i] : WordComposer.NOT_A_COORDINATE;
+ yCoordinates[i] : Constants.NOT_A_COORDINATE;
proximityInfo.fillArrayWithNearestKeyCodes(x, y, codes.getCodeAt(i), mCodes[i]);
}
mMaxDepth = mInputLength * 3;
- getWordsRec(mRoots, codes, mWordBuilder, 0, false, 1, 0, -1, callback);
+ getWordsRec(mRoots, codes, mWordBuilder, 0, false, 1, 0, -1, suggestions);
for (int i = 0; i < mInputLength; i++) {
- getWordsRec(mRoots, codes, mWordBuilder, 0, false, 1, 0, i, callback);
+ getWordsRec(mRoots, codes, mWordBuilder, 0, false, 1, 0, i, suggestions);
}
+ return suggestions;
}
@Override
@@ -368,24 +385,27 @@ public class ExpandableDictionary extends Dictionary {
* @param word the word to insert, as an array of code points
* @param depth the depth of the node in the tree
* @param finalFreq the frequency for this word
+ * @param suggestions the suggestion collection to add the suggestions to
* @return whether there is still space for more words.
- * @see Dictionary.WordCallback#addWord(char[], int, int, int, int, int)
*/
private boolean addWordAndShortcutsFromNode(final Node node, final char[] word, final int depth,
- final int finalFreq, final WordCallback callback) {
+ final int finalFreq, final ArrayList<SuggestedWordInfo> suggestions) {
if (finalFreq > 0 && !node.mShortcutOnly) {
- if (!callback.addWord(word, 0, depth + 1, finalFreq, mDicTypeId, Dictionary.UNIGRAM)) {
- return false;
- }
+ // Use KIND_CORRECTION always. This dictionary does not really have a notion of
+ // COMPLETION against CORRECTION; we could artificially add one by looking at
+ // the respective size of the typed word and the suggestion if it matters sometime
+ // in the future.
+ suggestions.add(new SuggestedWordInfo(new String(word, 0, depth + 1), finalFreq,
+ SuggestedWordInfo.KIND_CORRECTION, mDictType));
+ if (suggestions.size() >= Suggest.MAX_SUGGESTIONS) return false;
}
if (null != node.mShortcutTargets) {
final int length = node.mShortcutTargets.size();
for (int shortcutIndex = 0; shortcutIndex < length; ++shortcutIndex) {
final char[] shortcut = node.mShortcutTargets.get(shortcutIndex);
- if (!callback.addWord(shortcut, 0, shortcut.length, finalFreq, mDicTypeId,
- Dictionary.UNIGRAM)) {
- return false;
- }
+ suggestions.add(new SuggestedWordInfo(new String(shortcut, 0, shortcut.length),
+ finalFreq, SuggestedWordInfo.KIND_SHORTCUT, mDictType));
+ if (suggestions.size() > Suggest.MAX_SUGGESTIONS) return false;
}
}
return true;
@@ -408,12 +428,12 @@ public class ExpandableDictionary extends Dictionary {
* case we skip over some punctuations such as apostrophe in the traversal. That is, if you type
* "wouldve", it could be matching "would've", so the depth will be one more than the
* inputIndex
- * @param callback the callback class for adding a word
+ * @param suggestions the list in which to add suggestions
*/
// TODO: Share this routine with the native code for BinaryDictionary
protected void getWordsRec(NodeArray roots, final WordComposer codes, final char[] word,
final int depth, final boolean completion, int snr, int inputIndex, int skipPos,
- WordCallback callback) {
+ final ArrayList<SuggestedWordInfo> suggestions) {
final int count = roots.mLength;
final int codeSize = mInputLength;
// Optimization: Prune out words that are too long compared to how much was typed.
@@ -443,14 +463,14 @@ public class ExpandableDictionary extends Dictionary {
} else {
finalFreq = computeSkippedWordFinalFreq(freq, snr, mInputLength);
}
- if (!addWordAndShortcutsFromNode(node, word, depth, finalFreq, callback)) {
+ if (!addWordAndShortcutsFromNode(node, word, depth, finalFreq, suggestions)) {
// No space left in the queue, bail out
return;
}
}
if (children != null) {
getWordsRec(children, codes, word, depth + 1, true, snr, inputIndex,
- skipPos, callback);
+ skipPos, suggestions);
}
} else if ((c == Keyboard.CODE_SINGLE_QUOTE
&& currentChars[0] != Keyboard.CODE_SINGLE_QUOTE) || depth == skipPos) {
@@ -458,7 +478,7 @@ public class ExpandableDictionary extends Dictionary {
word[depth] = c;
if (children != null) {
getWordsRec(children, codes, word, depth + 1, completion, snr, inputIndex,
- skipPos, callback);
+ skipPos, suggestions);
}
} else {
// Don't use alternatives if we're looking for missing characters
@@ -466,7 +486,7 @@ public class ExpandableDictionary extends Dictionary {
for (int j = 0; j < alternativesSize; j++) {
final int addedAttenuation = (j > 0 ? 1 : 2);
final int currentChar = currentChars[j];
- if (currentChar == KeyDetector.NOT_A_CODE) {
+ if (currentChar == Constants.NOT_A_CODE) {
break;
}
if (currentChar == lowerC || currentChar == c) {
@@ -483,7 +503,7 @@ public class ExpandableDictionary extends Dictionary {
snr * addedAttenuation, mInputLength);
}
if (!addWordAndShortcutsFromNode(node, word, depth, finalFreq,
- callback)) {
+ suggestions)) {
// No space left in the queue, bail out
return;
}
@@ -491,12 +511,12 @@ public class ExpandableDictionary extends Dictionary {
if (children != null) {
getWordsRec(children, codes, word, depth + 1,
true, snr * addedAttenuation, inputIndex + 1,
- skipPos, callback);
+ skipPos, suggestions);
}
} else if (children != null) {
getWordsRec(children, codes, word, depth + 1,
false, snr * addedAttenuation, inputIndex + 1,
- skipPos, callback);
+ skipPos, suggestions);
}
}
}
@@ -514,8 +534,10 @@ public class ExpandableDictionary extends Dictionary {
/**
* Adds bigrams to the in-memory trie structure that is being used to retrieve any word
+ * @param word1 the first word of this bigram
+ * @param word2 the second word of this bigram
* @param frequency frequency for this bigram
- * @param addFrequency if true, it adds to current frequency, else it overwrites the old value
+ * @param fcp an instance of ForgettingCurveParams to use for decay policy
* @return returns the final bigram frequency
*/
private int setBigramAndGetFrequency(
@@ -528,7 +550,7 @@ public class ExpandableDictionary extends Dictionary {
Node secondWord = searchWord(mRoots, word2, 0, null);
LinkedList<NextWord> bigrams = firstWord.mNGrams;
if (bigrams == null || bigrams.size() == 0) {
- firstWord.mNGrams = new LinkedList<NextWord>();
+ firstWord.mNGrams = CollectionUtils.newLinkedList();
bigrams = firstWord.mNGrams;
} else {
for (NextWord nw : bigrams) {
@@ -580,32 +602,14 @@ public class ExpandableDictionary extends Dictionary {
return searchWord(childNode.mChildren, word, depth + 1, childNode);
}
- // @VisibleForTesting
- boolean reloadDictionaryIfRequired() {
- synchronized (mUpdatingLock) {
- // If we need to update, start off a background task
- if (mRequiresReload) startDictionaryLoadingTaskLocked();
- // Currently updating contacts, don't return any results.
- return mUpdatingDictionary;
- }
- }
-
private void runBigramReverseLookUp(final CharSequence previousWord,
- final WordCallback callback) {
+ final ArrayList<SuggestedWordInfo> suggestions) {
// Search for the lowercase version of the word only, because that's where bigrams
// store their sons.
Node prevWord = searchNode(mRoots, previousWord.toString().toLowerCase(), 0,
previousWord.length());
if (prevWord != null && prevWord.mNGrams != null) {
- reverseLookUp(prevWord.mNGrams, callback);
- }
- }
-
- @Override
- public void getBigrams(final WordComposer codes, final CharSequence previousWord,
- final WordCallback callback) {
- if (!reloadDictionaryIfRequired()) {
- runBigramReverseLookUp(previousWord, callback);
+ reverseLookUp(prevWord.mNGrams, suggestions);
}
}
@@ -633,11 +637,12 @@ public class ExpandableDictionary extends Dictionary {
/**
* reverseLookUp retrieves the full word given a list of terminal nodes and adds those words
- * through callback.
+ * to the suggestions list passed as an argument.
* @param terminalNodes list of terminal nodes we want to add
+ * @param suggestions the suggestion collection to add the word to
*/
private void reverseLookUp(LinkedList<NextWord> terminalNodes,
- final WordCallback callback) {
+ final ArrayList<SuggestedWordInfo> suggestions) {
Node node;
int freq;
for (NextWord nextWord : terminalNodes) {
@@ -648,11 +653,15 @@ public class ExpandableDictionary extends Dictionary {
--index;
mLookedUpString[index] = node.mCode;
node = node.mParent;
- } while (node != null);
-
- if (freq >= 0) {
- callback.addWord(mLookedUpString, index, BinaryDictionary.MAX_WORD_LENGTH - index,
- freq, mDicTypeId, Dictionary.BIGRAM);
+ } while (node != null && index > 0);
+
+ // If node is null, we have a word longer than MAX_WORD_LENGTH in the dictionary.
+ // It's a little unclear how this can happen, but just in case it does it's safer
+ // to ignore the word in this case.
+ if (freq >= 0 && node == null) {
+ suggestions.add(new SuggestedWordInfo(new String(mLookedUpString, index,
+ BinaryDictionary.MAX_WORD_LENGTH - index),
+ freq, SuggestedWordInfo.KIND_CORRECTION, mDictType));
}
}
}
@@ -694,7 +703,7 @@ public class ExpandableDictionary extends Dictionary {
mRoots = new NodeArray();
}
- private class LoadDictionaryTask extends Thread {
+ private final class LoadDictionaryTask extends Thread {
LoadDictionaryTask() {}
@Override
public void run() {
diff --git a/java/src/com/android/inputmethod/latin/FileTransforms.java b/java/src/com/android/inputmethod/latin/FileTransforms.java
index 80159521c..09cf23a9b 100644
--- a/java/src/com/android/inputmethod/latin/FileTransforms.java
+++ b/java/src/com/android/inputmethod/latin/FileTransforms.java
@@ -21,7 +21,7 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.GZIPInputStream;
-public class FileTransforms {
+public final class FileTransforms {
public static OutputStream getCryptedStream(OutputStream out) {
// Crypt the stream.
return out;
diff --git a/java/src/com/android/inputmethod/latin/ImfUtils.java b/java/src/com/android/inputmethod/latin/ImfUtils.java
index b882a4860..2674e4575 100644
--- a/java/src/com/android/inputmethod/latin/ImfUtils.java
+++ b/java/src/com/android/inputmethod/latin/ImfUtils.java
@@ -29,7 +29,7 @@ import java.util.List;
/**
* Utility class for Input Method Framework
*/
-public class ImfUtils {
+public final class ImfUtils {
private ImfUtils() {
// This utility class is not publicly instantiable.
}
@@ -90,6 +90,13 @@ public class ImfUtils {
return false;
}
+ public static InputMethodSubtype getCurrentInputMethodSubtype(Context context,
+ InputMethodSubtype defaultSubtype) {
+ final InputMethodManager imm = getInputMethodManager(context);
+ final InputMethodSubtype currentSubtype = imm.getCurrentInputMethodSubtype();
+ return (currentSubtype != null) ? currentSubtype : defaultSubtype;
+ }
+
public static boolean hasMultipleEnabledIMEsOrSubtypes(Context context,
final boolean shouldIncludeAuxiliarySubtypes) {
final InputMethodManager imm = getInputMethodManager(context);
diff --git a/java/src/com/android/inputmethod/latin/InputAttributes.java b/java/src/com/android/inputmethod/latin/InputAttributes.java
index 229ae2f3c..2f7608a03 100644
--- a/java/src/com/android/inputmethod/latin/InputAttributes.java
+++ b/java/src/com/android/inputmethod/latin/InputAttributes.java
@@ -23,17 +23,18 @@ import android.view.inputmethod.EditorInfo;
/**
* Class to hold attributes of the input field.
*/
-public class InputAttributes {
+public final class InputAttributes {
private final String TAG = InputAttributes.class.getSimpleName();
final public boolean mInputTypeNoAutoCorrect;
final public boolean mIsSettingsSuggestionStripOn;
final public boolean mApplicationSpecifiedCompletionOn;
- final public int mEditorAction;
+ final private int mInputType;
public InputAttributes(final EditorInfo editorInfo, final boolean isFullscreenMode) {
final int inputType = null != editorInfo ? editorInfo.inputType : 0;
final int inputClass = inputType & InputType.TYPE_MASK_CLASS;
+ mInputType = inputType;
if (inputClass != InputType.TYPE_CLASS_TEXT) {
// If we are not looking at a TYPE_CLASS_TEXT field, the following strange
// cases may arise, so we do a couple sanity checks for them. If it's a
@@ -64,7 +65,7 @@ public class InputAttributes {
final boolean flagAutoComplete =
0 != (inputType & InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE);
- // Make sure that passwords are not displayed in {@link SuggestionsView}.
+ // Make sure that passwords are not displayed in {@link SuggestionStripView}.
if (InputTypeUtils.isPasswordInputType(inputType)
|| InputTypeUtils.isVisiblePasswordInputType(inputType)
|| InputTypeUtils.isEmailVariation(variation)
@@ -92,8 +93,10 @@ public class InputAttributes {
mApplicationSpecifiedCompletionOn = flagAutoComplete && isFullscreenMode;
}
- mEditorAction = (editorInfo == null) ? EditorInfo.IME_ACTION_UNSPECIFIED
- : editorInfo.imeOptions & EditorInfo.IME_MASK_ACTION;
+ }
+
+ public boolean isSameInputType(final EditorInfo editorInfo) {
+ return editorInfo.inputType == mInputType;
}
@SuppressWarnings("unused")
diff --git a/java/src/com/android/inputmethod/latin/InputPointers.java b/java/src/com/android/inputmethod/latin/InputPointers.java
new file mode 100644
index 000000000..6b48aabb3
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/InputPointers.java
@@ -0,0 +1,133 @@
+/*
+ * 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;
+
+// TODO: This class is not thread-safe.
+public final class InputPointers {
+ private final int mDefaultCapacity;
+ private final ResizableIntArray mXCoordinates;
+ private final ResizableIntArray mYCoordinates;
+ private final ResizableIntArray mPointerIds;
+ private final ResizableIntArray mTimes;
+
+ public InputPointers(int defaultCapacity) {
+ mDefaultCapacity = defaultCapacity;
+ mXCoordinates = new ResizableIntArray(defaultCapacity);
+ mYCoordinates = new ResizableIntArray(defaultCapacity);
+ mPointerIds = new ResizableIntArray(defaultCapacity);
+ mTimes = new ResizableIntArray(defaultCapacity);
+ }
+
+ public void addPointer(int index, int x, int y, int pointerId, int time) {
+ mXCoordinates.add(index, x);
+ mYCoordinates.add(index, y);
+ mPointerIds.add(index, pointerId);
+ mTimes.add(index, time);
+ }
+
+ public void addPointer(int x, int y, int pointerId, int time) {
+ mXCoordinates.add(x);
+ mYCoordinates.add(y);
+ mPointerIds.add(pointerId);
+ mTimes.add(time);
+ }
+
+ public void set(InputPointers ip) {
+ mXCoordinates.set(ip.mXCoordinates);
+ mYCoordinates.set(ip.mYCoordinates);
+ mPointerIds.set(ip.mPointerIds);
+ mTimes.set(ip.mTimes);
+ }
+
+ public void copy(InputPointers ip) {
+ mXCoordinates.copy(ip.mXCoordinates);
+ mYCoordinates.copy(ip.mYCoordinates);
+ mPointerIds.copy(ip.mPointerIds);
+ mTimes.copy(ip.mTimes);
+ }
+
+ /**
+ * Append the pointers in the specified {@link InputPointers} to the end of this.
+ * @param src the source {@link InputPointers} to read the data from.
+ * @param startPos the starting index of the pointers in {@code src}.
+ * @param length the number of pointers to be appended.
+ */
+ public void append(InputPointers src, int startPos, int length) {
+ if (length == 0) {
+ return;
+ }
+ mXCoordinates.append(src.mXCoordinates, startPos, length);
+ mYCoordinates.append(src.mYCoordinates, startPos, length);
+ mPointerIds.append(src.mPointerIds, startPos, length);
+ mTimes.append(src.mTimes, startPos, length);
+ }
+
+ /**
+ * Append the times, x-coordinates and y-coordinates in the specified {@link ResizableIntArray}
+ * to the end of this.
+ * @param pointerId the pointer id of the source.
+ * @param times the source {@link ResizableIntArray} to read the event times from.
+ * @param xCoordinates the source {@link ResizableIntArray} to read the x-coordinates from.
+ * @param yCoordinates the source {@link ResizableIntArray} to read the y-coordinates from.
+ * @param startPos the starting index of the data in {@code times} and etc.
+ * @param length the number of data to be appended.
+ */
+ public void append(int pointerId, ResizableIntArray times, ResizableIntArray xCoordinates,
+ ResizableIntArray yCoordinates, int startPos, int length) {
+ if (length == 0) {
+ return;
+ }
+ mXCoordinates.append(xCoordinates, startPos, length);
+ mYCoordinates.append(yCoordinates, startPos, length);
+ mPointerIds.fill(pointerId, mPointerIds.getLength(), length);
+ mTimes.append(times, startPos, length);
+ }
+
+ public void reset() {
+ final int defaultCapacity = mDefaultCapacity;
+ mXCoordinates.reset(defaultCapacity);
+ mYCoordinates.reset(defaultCapacity);
+ mPointerIds.reset(defaultCapacity);
+ mTimes.reset(defaultCapacity);
+ }
+
+ public int getPointerSize() {
+ return mXCoordinates.getLength();
+ }
+
+ public int[] getXCoordinates() {
+ return mXCoordinates.getPrimitiveArray();
+ }
+
+ public int[] getYCoordinates() {
+ return mYCoordinates.getPrimitiveArray();
+ }
+
+ public int[] getPointerIds() {
+ return mPointerIds.getPrimitiveArray();
+ }
+
+ public int[] getTimes() {
+ return mTimes.getPrimitiveArray();
+ }
+
+ @Override
+ public String toString() {
+ return "size=" + getPointerSize() + " id=" + mPointerIds + " time=" + mTimes
+ + " x=" + mXCoordinates + " y=" + mYCoordinates;
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/InputTypeUtils.java b/java/src/com/android/inputmethod/latin/InputTypeUtils.java
index 40c3b765e..500866a13 100644
--- a/java/src/com/android/inputmethod/latin/InputTypeUtils.java
+++ b/java/src/com/android/inputmethod/latin/InputTypeUtils.java
@@ -18,7 +18,7 @@ package com.android.inputmethod.latin;
import android.text.InputType;
-public class InputTypeUtils implements InputType {
+public final class InputTypeUtils implements InputType {
private static final int WEB_TEXT_PASSWORD_INPUT_TYPE =
TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_WEB_PASSWORD;
private static final int WEB_TEXT_EMAIL_ADDRESS_INPUT_TYPE =
diff --git a/java/src/com/android/inputmethod/latin/InputView.java b/java/src/com/android/inputmethod/latin/InputView.java
index 0dcb811b5..d7595bfbe 100644
--- a/java/src/com/android/inputmethod/latin/InputView.java
+++ b/java/src/com/android/inputmethod/latin/InputView.java
@@ -23,8 +23,8 @@ import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;
-public class InputView extends LinearLayout {
- private View mSuggestionsContainer;
+public final class InputView extends LinearLayout {
+ private View mSuggestionStripContainer;
private View mKeyboardView;
private int mKeyboardTopPadding;
@@ -43,13 +43,13 @@ public class InputView extends LinearLayout {
@Override
protected void onFinishInflate() {
- mSuggestionsContainer = findViewById(R.id.suggestions_container);
+ mSuggestionStripContainer = findViewById(R.id.suggestions_container);
mKeyboardView = findViewById(R.id.keyboard_view);
}
@Override
public boolean dispatchTouchEvent(MotionEvent me) {
- if (mSuggestionsContainer.getVisibility() == VISIBLE
+ if (mSuggestionStripContainer.getVisibility() == VISIBLE
&& mKeyboardView.getVisibility() == VISIBLE
&& forwardTouchEvent(me)) {
return true;
@@ -57,7 +57,8 @@ public class InputView extends LinearLayout {
return super.dispatchTouchEvent(me);
}
- // The touch events that hit the top padding of keyboard should be forwarded to SuggestionsView.
+ // The touch events that hit the top padding of keyboard should be forwarded to
+ // {@link SuggestionStripView}.
private boolean forwardTouchEvent(MotionEvent me) {
final Rect rect = mInputViewRect;
this.getGlobalVisibleRect(rect);
@@ -96,7 +97,7 @@ public class InputView extends LinearLayout {
}
final Rect receivingRect = mEventReceivingRect;
- mSuggestionsContainer.getGlobalVisibleRect(receivingRect);
+ mSuggestionStripContainer.getGlobalVisibleRect(receivingRect);
final int translatedX = x - receivingRect.left;
final int translatedY;
if (y < forwardingLimitY) {
@@ -106,7 +107,7 @@ public class InputView extends LinearLayout {
translatedY = y - receivingRect.top;
}
me.setLocation(translatedX, translatedY);
- mSuggestionsContainer.dispatchTouchEvent(me);
+ mSuggestionStripContainer.dispatchTouchEvent(me);
return true;
}
}
diff --git a/java/src/com/android/inputmethod/latin/JniUtils.java b/java/src/com/android/inputmethod/latin/JniUtils.java
index 4808b867a..f9305991a 100644
--- a/java/src/com/android/inputmethod/latin/JniUtils.java
+++ b/java/src/com/android/inputmethod/latin/JniUtils.java
@@ -20,7 +20,7 @@ import android.util.Log;
import com.android.inputmethod.latin.define.JniLibName;
-public class JniUtils {
+public final class JniUtils {
private static final String TAG = JniUtils.class.getSimpleName();
private JniUtils() {
@@ -31,11 +31,7 @@ public class JniUtils {
try {
System.loadLibrary(JniLibName.JNI_LIB_NAME);
} catch (UnsatisfiedLinkError ule) {
- Log.e(TAG, "Could not load native library " + JniLibName.JNI_LIB_NAME);
- if (LatinImeLogger.sDBG) {
- throw new RuntimeException(
- "Could not load native library " + JniLibName.JNI_LIB_NAME);
- }
+ Log.e(TAG, "Could not load native library " + JniLibName.JNI_LIB_NAME, ule);
}
}
}
diff --git a/java/src/com/android/inputmethod/latin/LastComposedWord.java b/java/src/com/android/inputmethod/latin/LastComposedWord.java
index 4e1f5fe92..94cdc9b85 100644
--- a/java/src/com/android/inputmethod/latin/LastComposedWord.java
+++ b/java/src/com/android/inputmethod/latin/LastComposedWord.java
@@ -22,7 +22,7 @@ import android.text.TextUtils;
* This class encapsulates data about a word previously composed, but that has been
* committed already. This is used for resuming suggestion, and cancel auto-correction.
*/
-public class LastComposedWord {
+public final class LastComposedWord {
// COMMIT_TYPE_USER_TYPED_WORD is used when the word committed is the exact typed word, with
// no hinting from the IME. It happens when some external event happens (rotating the device,
// for example) or when auto-correction is off by settings or editor attributes.
@@ -38,32 +38,32 @@ public class LastComposedWord {
// an auto-correction.
public static final int COMMIT_TYPE_CANCEL_AUTO_CORRECT = 3;
- public static final int NOT_A_SEPARATOR = -1;
+ public static final String NOT_A_SEPARATOR = "";
public final int[] mPrimaryKeyCodes;
- public final int[] mXCoordinates;
- public final int[] mYCoordinates;
public final String mTypedWord;
public final String mCommittedWord;
- public final int mSeparatorCode;
+ public final String mSeparatorString;
public final CharSequence mPrevWord;
+ public final InputPointers mInputPointers = new InputPointers(BinaryDictionary.MAX_WORD_LENGTH);
private boolean mActive;
public static final LastComposedWord NOT_A_COMPOSED_WORD =
- new LastComposedWord(null, null, null, "", "", NOT_A_SEPARATOR, null);
+ new LastComposedWord(null, null, "", "", NOT_A_SEPARATOR, null);
// Warning: this is using the passed objects as is and fully expects them to be
// immutable. Do not fiddle with their contents after you passed them to this constructor.
- public LastComposedWord(final int[] primaryKeyCodes, final int[] xCoordinates,
- final int[] yCoordinates, final String typedWord, final String committedWord,
- final int separatorCode, final CharSequence prevWord) {
+ public LastComposedWord(final int[] primaryKeyCodes, final InputPointers inputPointers,
+ final String typedWord, final String committedWord,
+ final String separatorString, final CharSequence prevWord) {
mPrimaryKeyCodes = primaryKeyCodes;
- mXCoordinates = xCoordinates;
- mYCoordinates = yCoordinates;
+ if (inputPointers != null) {
+ mInputPointers.copy(inputPointers);
+ }
mTypedWord = typedWord;
mCommittedWord = committedWord;
- mSeparatorCode = separatorCode;
+ mSeparatorString = separatorString;
mActive = true;
mPrevWord = prevWord;
}
@@ -73,14 +73,14 @@ public class LastComposedWord {
}
public boolean canRevertCommit() {
- return mActive && !TextUtils.isEmpty(mCommittedWord);
+ return mActive && !TextUtils.isEmpty(mCommittedWord) && !didCommitTypedWord();
}
- public boolean didCommitTypedWord() {
+ private boolean didCommitTypedWord() {
return TextUtils.equals(mTypedWord, mCommittedWord);
}
- public static int getSeparatorLength(final int separatorCode) {
- return NOT_A_SEPARATOR == separatorCode ? 0 : 1;
+ public static int getSeparatorLength(final String separatorString) {
+ return StringUtils.codePointCount(separatorString);
}
}
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 97e898af9..8ac0cd4ca 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -20,6 +20,7 @@ import static com.android.inputmethod.latin.Constants.ImeOption.FORCE_ASCII;
import static com.android.inputmethod.latin.Constants.ImeOption.NO_MICROPHONE;
import static com.android.inputmethod.latin.Constants.ImeOption.NO_MICROPHONE_COMPAT;
+import android.app.Activity;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -35,10 +36,11 @@ import android.inputmethodservice.InputMethodService;
import android.media.AudioManager;
import android.net.ConnectivityManager;
import android.os.Debug;
+import android.os.Handler;
+import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Message;
import android.os.SystemClock;
-import android.preference.PreferenceActivity;
import android.preference.PreferenceManager;
import android.text.InputType;
import android.text.TextUtils;
@@ -48,31 +50,32 @@ import android.util.Printer;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.View;
-import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
-import android.view.ViewParent;
import android.view.Window;
import android.view.WindowManager;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CorrectionInfo;
import android.view.inputmethod.EditorInfo;
-import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodSubtype;
import com.android.inputmethod.accessibility.AccessibilityUtils;
import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
import com.android.inputmethod.compat.CompatUtils;
import com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
+import com.android.inputmethod.compat.InputMethodServiceCompatUtils;
import com.android.inputmethod.compat.SuggestionSpanUtils;
+import com.android.inputmethod.keyboard.KeyDetector;
import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.KeyboardActionListener;
import com.android.inputmethod.keyboard.KeyboardId;
import com.android.inputmethod.keyboard.KeyboardSwitcher;
import com.android.inputmethod.keyboard.KeyboardView;
-import com.android.inputmethod.keyboard.LatinKeyboardView;
+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.SuggestionsView;
+import com.android.inputmethod.latin.suggestions.SuggestionStripView;
+import com.android.inputmethod.research.ResearchLogger;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -82,8 +85,9 @@ import java.util.Locale;
/**
* Input method implementation for Qwerty'ish keyboard.
*/
-public class LatinIME extends InputMethodService implements KeyboardActionListener,
- SuggestionsView.Listener, TargetApplicationGetter.OnTargetApplicationKnownListener {
+public final class LatinIME extends InputMethodService implements KeyboardActionListener,
+ SuggestionStripView.Listener, TargetApplicationGetter.OnTargetApplicationKnownListener,
+ Suggest.SuggestInitializationListener {
private static final String TAG = LatinIME.class.getSimpleName();
private static final boolean TRACE = false;
private static boolean DEBUG;
@@ -103,27 +107,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
*/
private static final String SCHEME_PACKAGE = "package";
- /** Whether to use the binary version of the contacts dictionary */
- public static final boolean USE_BINARY_CONTACTS_DICTIONARY = true;
-
- /** Whether to use the binary version of the user dictionary */
- public static final boolean USE_BINARY_USER_DICTIONARY = true;
-
- // TODO: migrate this to SettingsValues
- private int mSuggestionVisibility;
- private static final int SUGGESTION_VISIBILILTY_SHOW_VALUE
- = R.string.prefs_suggestion_visibility_show_value;
- private static final int SUGGESTION_VISIBILILTY_SHOW_ONLY_PORTRAIT_VALUE
- = R.string.prefs_suggestion_visibility_show_only_portrait_value;
- private static final int SUGGESTION_VISIBILILTY_HIDE_VALUE
- = R.string.prefs_suggestion_visibility_hide_value;
-
- private static final int[] SUGGESTION_VISIBILITY_VALUE_ARRAY = new int[] {
- SUGGESTION_VISIBILILTY_SHOW_VALUE,
- SUGGESTION_VISIBILILTY_SHOW_ONLY_PORTRAIT_VALUE,
- SUGGESTION_VISIBILILTY_HIDE_VALUE
- };
-
private static final int SPACE_STATE_NONE = 0;
// Double space: the state where the user pressed space twice quickly, which LatinIME
// resolved as period-space. Undoing this converts the period to a space.
@@ -143,13 +126,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// Current space state of the input method. This can be any of the above constants.
private int mSpaceState;
- private SettingsValues mSettingsValues;
- private InputAttributes mInputAttributes;
+ private SettingsValues mCurrentSettings;
private View mExtractArea;
private View mKeyPreviewBackingView;
private View mSuggestionsContainer;
- private SuggestionsView mSuggestionsView;
+ private SuggestionStripView mSuggestionStripView;
/* package for tests */ Suggest mSuggest;
private CompletionInfo[] mApplicationSpecifiedCompletions;
private ApplicationInfo mTargetApplicationInfo;
@@ -162,15 +144,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private boolean mShouldSwitchToLastSubtype = true;
private boolean mIsMainDictionaryAvailable;
- // TODO: revert this back to the concrete class after transition.
- private Dictionary mUserDictionary;
+ private UserBinaryDictionary mUserDictionary;
private UserHistoryDictionary mUserHistoryDictionary;
private boolean mIsUserDictionaryAvailable;
private LastComposedWord mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
- private WordComposer mWordComposer = new WordComposer();
-
- private int mCorrectionMode;
+ 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
private static final int NOT_A_CURSOR_POSITION = -1;
@@ -199,20 +179,24 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private AlertDialog mOptionsDialog;
+ private final boolean mIsHardwareAcceleratedDrawingEnabled;
+
public final UIHandler mHandler = new UIHandler(this);
- public static class UIHandler extends StaticInnerHandlerWrapper<LatinIME> {
- private static final int MSG_UPDATE_SHIFT_STATE = 1;
- private static final int MSG_SPACE_TYPED = 4;
- private static final int MSG_SET_BIGRAM_PREDICTIONS = 5;
- private static final int MSG_PENDING_IMS_CALLBACK = 6;
- private static final int MSG_UPDATE_SUGGESTIONS = 7;
+ public static final class UIHandler extends StaticInnerHandlerWrapper<LatinIME> {
+ private static final int MSG_UPDATE_SHIFT_STATE = 0;
+ private static final int MSG_PENDING_IMS_CALLBACK = 1;
+ private static final int MSG_UPDATE_SUGGESTION_STRIP = 2;
+ private static final int MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP = 3;
+
+ private static final int ARG1_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT = 1;
private int mDelayUpdateSuggestions;
private int mDelayUpdateShiftState;
private long mDoubleSpacesTurnIntoPeriodTimeout;
+ private long mDoubleSpaceTimerStart;
- public UIHandler(LatinIME outerInstance) {
+ public UIHandler(final LatinIME outerInstance) {
super(outerInstance);
}
@@ -227,33 +211,33 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
@Override
- public void handleMessage(Message msg) {
+ public void handleMessage(final Message msg) {
final LatinIME latinIme = getOuterInstance();
final KeyboardSwitcher switcher = latinIme.mKeyboardSwitcher;
switch (msg.what) {
- case MSG_UPDATE_SUGGESTIONS:
- latinIme.updateSuggestions();
+ case MSG_UPDATE_SUGGESTION_STRIP:
+ latinIme.updateSuggestionStrip();
break;
case MSG_UPDATE_SHIFT_STATE:
switcher.updateShiftState();
break;
- case MSG_SET_BIGRAM_PREDICTIONS:
- latinIme.updateBigramPredictions();
+ case MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP:
+ latinIme.showGesturePreviewAndSuggestionStrip((SuggestedWords)msg.obj,
+ msg.arg1 == ARG1_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT);
break;
}
}
- public void postUpdateSuggestions() {
- removeMessages(MSG_UPDATE_SUGGESTIONS);
- sendMessageDelayed(obtainMessage(MSG_UPDATE_SUGGESTIONS), mDelayUpdateSuggestions);
+ public void postUpdateSuggestionStrip() {
+ sendMessageDelayed(obtainMessage(MSG_UPDATE_SUGGESTION_STRIP), mDelayUpdateSuggestions);
}
- public void cancelUpdateSuggestions() {
- removeMessages(MSG_UPDATE_SUGGESTIONS);
+ public void cancelUpdateSuggestionStrip() {
+ removeMessages(MSG_UPDATE_SUGGESTION_STRIP);
}
public boolean hasPendingUpdateSuggestions() {
- return hasMessages(MSG_UPDATE_SUGGESTIONS);
+ return hasMessages(MSG_UPDATE_SUGGESTION_STRIP);
}
public void postUpdateShiftState() {
@@ -265,26 +249,26 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
removeMessages(MSG_UPDATE_SHIFT_STATE);
}
- public void postUpdateBigramPredictions() {
- removeMessages(MSG_SET_BIGRAM_PREDICTIONS);
- sendMessageDelayed(obtainMessage(MSG_SET_BIGRAM_PREDICTIONS), mDelayUpdateSuggestions);
- }
-
- public void cancelUpdateBigramPredictions() {
- removeMessages(MSG_SET_BIGRAM_PREDICTIONS);
+ public void showGesturePreviewAndSuggestionStrip(final SuggestedWords suggestedWords,
+ final boolean dismissGestureFloatingPreviewText) {
+ removeMessages(MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP);
+ final int arg1 = dismissGestureFloatingPreviewText
+ ? ARG1_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT : 0;
+ obtainMessage(MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP, arg1, 0, suggestedWords)
+ .sendToTarget();
}
public void startDoubleSpacesTimer() {
- removeMessages(MSG_SPACE_TYPED);
- sendMessageDelayed(obtainMessage(MSG_SPACE_TYPED), mDoubleSpacesTurnIntoPeriodTimeout);
+ mDoubleSpaceTimerStart = SystemClock.uptimeMillis();
}
public void cancelDoubleSpacesTimer() {
- removeMessages(MSG_SPACE_TYPED);
+ mDoubleSpaceTimerStart = 0;
}
public boolean isAcceptingDoubleSpaces() {
- return hasMessages(MSG_SPACE_TYPED);
+ return SystemClock.uptimeMillis() - mDoubleSpaceTimerStart
+ < mDoubleSpacesTurnIntoPeriodTimeout;
}
// Working variables for the following methods.
@@ -311,7 +295,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mHasPendingStartInput = false;
}
- private void executePendingImsCallback(LatinIME latinIme, EditorInfo editorInfo,
+ private void executePendingImsCallback(final LatinIME latinIme, final EditorInfo editorInfo,
boolean restarting) {
if (mHasPendingFinishInputView)
latinIme.onFinishInputViewInternal(mHasPendingFinishInput);
@@ -322,7 +306,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
resetPendingImsCallback();
}
- public void onStartInput(EditorInfo editorInfo, boolean restarting) {
+ public void onStartInput(final EditorInfo editorInfo, final boolean restarting) {
if (hasMessages(MSG_PENDING_IMS_CALLBACK)) {
// Typically this is the second onStartInput after orientation changed.
mHasPendingStartInput = true;
@@ -338,7 +322,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
}
- public void onStartInputView(EditorInfo editorInfo, boolean restarting) {
+ public void onStartInputView(final EditorInfo editorInfo, final boolean restarting) {
if (hasMessages(MSG_PENDING_IMS_CALLBACK)
&& KeyboardId.equivalentEditorInfoForKeyboard(editorInfo, mAppliedEditorInfo)) {
// Typically this is the second onStartInputView after orientation changed.
@@ -358,7 +342,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
}
- public void onFinishInputView(boolean finishingInput) {
+ public void onFinishInputView(final boolean finishingInput) {
if (hasMessages(MSG_PENDING_IMS_CALLBACK)) {
// Typically this is the first onFinishInputView after orientation changed.
mHasPendingFinishInputView = true;
@@ -385,6 +369,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
super();
mSubtypeSwitcher = SubtypeSwitcher.getInstance();
mKeyboardSwitcher = KeyboardSwitcher.getInstance();
+ mIsHardwareAcceleratedDrawingEnabled =
+ InputMethodServiceCompatUtils.enableHardwareAcceleration(this);
+ Log.i(TAG, "Hardware accelerated drawing: " + mIsHardwareAcceleratedDrawingEnabled);
}
@Override
@@ -393,7 +380,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mPrefs = prefs;
LatinImeLogger.init(this, prefs);
if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.init(this, prefs);
+ ResearchLogger.getInstance().init(this, prefs);
}
InputMethodManagerCompatWrapper.init(this);
SubtypeSwitcher.init(this);
@@ -411,22 +398,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
loadSettings();
- ImfUtils.setAdditionalInputMethodSubtypes(this, mSettingsValues.getAdditionalSubtypes());
-
- // TODO: remove the following when it's not needed by updateCorrectionMode() any more
- mInputAttributes = new InputAttributes(null, false /* isFullscreenMode */);
- updateCorrectionMode();
+ ImfUtils.setAdditionalInputMethodSubtypes(this, mCurrentSettings.getAdditionalSubtypes());
- Utils.GCUtils.getInstance().reset();
- boolean tryGC = true;
- for (int i = 0; i < Utils.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) {
- try {
- initSuggest();
- tryGC = false;
- } catch (OutOfMemoryError e) {
- tryGC = Utils.GCUtils.getInstance().tryGCOrWait("InitSuggest", e);
- }
- }
+ initSuggest();
mDisplayOrientation = res.getConfiguration().orientation;
@@ -450,46 +424,58 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
// Has to be package-visible for unit tests
- /* package */ void loadSettings() {
+ /* package for test */
+ void loadSettings() {
// Note that the calling sequence of onCreate() and onCurrentInputMethodSubtypeChanged()
// is not guaranteed. It may even be called at the same time on a different thread.
if (null == mPrefs) mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
+ final InputAttributes inputAttributes =
+ new InputAttributes(getCurrentInputEditorInfo(), isFullscreenMode());
final RunInLocale<SettingsValues> job = new RunInLocale<SettingsValues>() {
@Override
protected SettingsValues job(Resources res) {
- return new SettingsValues(mPrefs, LatinIME.this);
+ return new SettingsValues(mPrefs, inputAttributes, LatinIME.this);
}
};
- mSettingsValues = job.runInLocale(mResources, mSubtypeSwitcher.getCurrentSubtypeLocale());
- mFeedbackManager = new AudioAndHapticFeedbackManager(this, mSettingsValues);
+ mCurrentSettings = job.runInLocale(mResources, mSubtypeSwitcher.getCurrentSubtypeLocale());
+ mFeedbackManager = new AudioAndHapticFeedbackManager(this, mCurrentSettings);
resetContactsDictionary(null == mSuggest ? null : mSuggest.getContactsDictionary());
}
+ // Note that this method is called from a non-UI thread.
+ @Override
+ public void onUpdateMainDictionaryAvailability(final boolean isMainDictionaryAvailable) {
+ mIsMainDictionaryAvailable = isMainDictionaryAvailable;
+ final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
+ if (mainKeyboardView != null) {
+ mainKeyboardView.setMainDictionaryAvailability(isMainDictionaryAvailable);
+ }
+ }
+
private void initSuggest() {
final Locale subtypeLocale = mSubtypeSwitcher.getCurrentSubtypeLocale();
final String localeStr = subtypeLocale.toString();
- final Dictionary oldContactsDictionary;
+ final ContactsBinaryDictionary oldContactsDictionary;
if (mSuggest != null) {
oldContactsDictionary = mSuggest.getContactsDictionary();
mSuggest.close();
} else {
oldContactsDictionary = null;
}
- mSuggest = new Suggest(this, subtypeLocale);
- if (mSettingsValues.mAutoCorrectEnabled) {
- mSuggest.setAutoCorrectionThreshold(mSettingsValues.mAutoCorrectionThreshold);
+ mSuggest = new Suggest(this /* Context */, subtypeLocale,
+ this /* SuggestInitializationListener */);
+ if (mCurrentSettings.mCorrectionEnabled) {
+ mSuggest.setAutoCorrectionThreshold(mCurrentSettings.mAutoCorrectionThreshold);
}
mIsMainDictionaryAvailable = DictionaryFactory.isDictionaryAvailable(this, subtypeLocale);
-
- if (USE_BINARY_USER_DICTIONARY) {
- mUserDictionary = new UserBinaryDictionary(this, localeStr);
- mIsUserDictionaryAvailable = ((UserBinaryDictionary)mUserDictionary).isEnabled();
- } else {
- mUserDictionary = new UserDictionary(this, localeStr);
- mIsUserDictionaryAvailable = ((UserDictionary)mUserDictionary).isEnabled();
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.getInstance().initSuggest(mSuggest);
}
+
+ mUserDictionary = new UserBinaryDictionary(this, localeStr);
+ mIsUserDictionaryAvailable = mUserDictionary.isEnabled();
mSuggest.setUserDictionary(mUserDictionary);
resetContactsDictionary(oldContactsDictionary);
@@ -497,44 +483,43 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// Note that the calling sequence of onCreate() and onCurrentInputMethodSubtypeChanged()
// is not guaranteed. It may even be called at the same time on a different thread.
if (null == mPrefs) mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
- mUserHistoryDictionary = UserHistoryDictionary.getInstance(
- this, localeStr, Suggest.DIC_USER_HISTORY, mPrefs);
+ mUserHistoryDictionary = UserHistoryDictionary.getInstance(this, localeStr, mPrefs);
mSuggest.setUserHistoryDictionary(mUserHistoryDictionary);
}
/**
* Resets the contacts dictionary in mSuggest according to the user settings.
*
- * This method takes an optional contacts dictionary to use. Since the contacts dictionary
- * does not depend on the locale, it can be reused across different instances of Suggest.
- * The dictionary will also be opened or closed as necessary depending on the settings.
+ * This method takes an optional contacts dictionary to use when the locale hasn't changed
+ * since the contacts dictionary can be opened or closed as necessary depending on the settings.
*
* @param oldContactsDictionary an optional dictionary to use, or null
*/
- private void resetContactsDictionary(final Dictionary oldContactsDictionary) {
- final boolean shouldSetDictionary = (null != mSuggest && mSettingsValues.mUseContactsDict);
+ private void resetContactsDictionary(final ContactsBinaryDictionary oldContactsDictionary) {
+ final boolean shouldSetDictionary = (null != mSuggest && mCurrentSettings.mUseContactsDict);
- final Dictionary dictionaryToUse;
+ final ContactsBinaryDictionary dictionaryToUse;
if (!shouldSetDictionary) {
// Make sure the dictionary is closed. If it is already closed, this is a no-op,
// so it's safe to call it anyways.
if (null != oldContactsDictionary) oldContactsDictionary.close();
dictionaryToUse = null;
- } else if (null != oldContactsDictionary) {
- // Make sure the old contacts dictionary is opened. If it is already open, this is a
- // no-op, so it's safe to call it anyways.
- if (USE_BINARY_CONTACTS_DICTIONARY) {
- ((ContactsBinaryDictionary)oldContactsDictionary).reopen(this);
- } else {
- ((ContactsDictionary)oldContactsDictionary).reopen(this);
- }
- dictionaryToUse = oldContactsDictionary;
} else {
- if (USE_BINARY_CONTACTS_DICTIONARY) {
- dictionaryToUse = new ContactsBinaryDictionary(this, Suggest.DIC_CONTACTS,
- mSubtypeSwitcher.getCurrentSubtypeLocale());
+ final Locale locale = mSubtypeSwitcher.getCurrentSubtypeLocale();
+ if (null != oldContactsDictionary) {
+ if (!oldContactsDictionary.mLocale.equals(locale)) {
+ // If the locale has changed then recreate the contacts dictionary. This
+ // allows locale dependent rules for handling bigram name predictions.
+ oldContactsDictionary.close();
+ dictionaryToUse = new ContactsBinaryDictionary(this, locale);
+ } else {
+ // Make sure the old contacts dictionary is opened. If it is already open,
+ // this is a no-op, so it's safe to call it anyways.
+ oldContactsDictionary.reopen(this);
+ dictionaryToUse = oldContactsDictionary;
+ }
} else {
- dictionaryToUse = new ContactsDictionary(this, Suggest.DIC_CONTACTS);
+ dictionaryToUse = new ContactsBinaryDictionary(this, locale);
}
}
@@ -545,7 +530,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
/* package private */ void resetSuggestMainDict() {
final Locale subtypeLocale = mSubtypeSwitcher.getCurrentSubtypeLocale();
- mSuggest.resetMainDict(this, subtypeLocale);
+ mSuggest.resetMainDict(this, subtypeLocale, this /* SuggestInitializationListener */);
mIsMainDictionaryAvailable = DictionaryFactory.isDictionaryAvailable(this, subtypeLocale);
}
@@ -563,59 +548,64 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
@Override
- public void onConfigurationChanged(Configuration conf) {
- mSubtypeSwitcher.onConfigurationChanged(conf);
+ public void onConfigurationChanged(final Configuration conf) {
+ // System locale has been changed. Needs to reload keyboard.
+ if (mSubtypeSwitcher.onConfigurationChanged(conf, this)) {
+ loadKeyboard();
+ }
// If orientation changed while predicting, commit the change
if (mDisplayOrientation != conf.orientation) {
mDisplayOrientation = conf.orientation;
mHandler.startOrientationChanging();
- final InputConnection ic = getCurrentInputConnection();
- commitTyped(ic, LastComposedWord.NOT_A_SEPARATOR);
- if (ic != null) ic.finishComposingText(); // For voice input
- if (isShowingOptionDialog())
+ mConnection.beginBatchEdit();
+ commitTyped(LastComposedWord.NOT_A_SEPARATOR);
+ mConnection.finishComposingText();
+ mConnection.endBatchEdit();
+ if (isShowingOptionDialog()) {
mOptionsDialog.dismiss();
+ }
}
super.onConfigurationChanged(conf);
}
@Override
public View onCreateInputView() {
- return mKeyboardSwitcher.onCreateInputView();
+ return mKeyboardSwitcher.onCreateInputView(mIsHardwareAcceleratedDrawingEnabled);
}
@Override
- public void setInputView(View view) {
+ public void setInputView(final View view) {
super.setInputView(view);
mExtractArea = getWindow().getWindow().getDecorView()
.findViewById(android.R.id.extractArea);
mKeyPreviewBackingView = view.findViewById(R.id.key_preview_backing);
mSuggestionsContainer = view.findViewById(R.id.suggestions_container);
- mSuggestionsView = (SuggestionsView) view.findViewById(R.id.suggestions_view);
- if (mSuggestionsView != null)
- mSuggestionsView.setListener(this, view);
+ mSuggestionStripView = (SuggestionStripView)view.findViewById(R.id.suggestion_strip_view);
+ if (mSuggestionStripView != null)
+ mSuggestionStripView.setListener(this, view);
if (LatinImeLogger.sVISUALDEBUG) {
mKeyPreviewBackingView.setBackgroundColor(0x10FF0000);
}
}
@Override
- public void setCandidatesView(View view) {
+ public void setCandidatesView(final View view) {
// To ensure that CandidatesView will never be set.
return;
}
@Override
- public void onStartInput(EditorInfo editorInfo, boolean restarting) {
+ public void onStartInput(final EditorInfo editorInfo, final boolean restarting) {
mHandler.onStartInput(editorInfo, restarting);
}
@Override
- public void onStartInputView(EditorInfo editorInfo, boolean restarting) {
+ public void onStartInputView(final EditorInfo editorInfo, final boolean restarting) {
mHandler.onStartInputView(editorInfo, restarting);
}
@Override
- public void onFinishInputView(boolean finishingInput) {
+ public void onFinishInputView(final boolean finishingInput) {
mHandler.onFinishInputView(finishingInput);
}
@@ -625,21 +615,22 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
@Override
- public void onCurrentInputMethodSubtypeChanged(InputMethodSubtype subtype) {
+ public void onCurrentInputMethodSubtypeChanged(final InputMethodSubtype subtype) {
// Note that the calling sequence of onCreate() and onCurrentInputMethodSubtypeChanged()
// is not guaranteed. It may even be called at the same time on a different thread.
mSubtypeSwitcher.updateSubtype(subtype);
+ loadKeyboard();
}
- private void onStartInputInternal(EditorInfo editorInfo, boolean restarting) {
+ private void onStartInputInternal(final EditorInfo editorInfo, final boolean restarting) {
super.onStartInput(editorInfo, restarting);
}
@SuppressWarnings("deprecation")
- private void onStartInputViewInternal(EditorInfo editorInfo, boolean restarting) {
+ private void onStartInputViewInternal(final EditorInfo editorInfo, final boolean restarting) {
super.onStartInputView(editorInfo, restarting);
final KeyboardSwitcher switcher = mKeyboardSwitcher;
- LatinKeyboardView inputView = switcher.getKeyboardView();
+ final MainKeyboardView mainKeyboardView = switcher.getMainKeyboardView();
if (editorInfo == null) {
Log.e(TAG, "Null EditorInfo in onStartInputView()");
@@ -682,93 +673,143 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
LatinImeLogger.onStartInputView(editorInfo);
// In landscape mode, this method gets called without the input view being created.
- if (inputView == null) {
+ if (mainKeyboardView == null) {
return;
}
// Forward this event to the accessibility utilities, if enabled.
final AccessibilityUtils accessUtils = AccessibilityUtils.getInstance();
if (accessUtils.isTouchExplorationEnabled()) {
- accessUtils.onStartInputViewInternal(editorInfo, restarting);
+ accessUtils.onStartInputViewInternal(mainKeyboardView, editorInfo, restarting);
}
- mSubtypeSwitcher.updateParametersOnStartInputView();
+ final boolean selectionChanged = mLastSelectionStart != editorInfo.initialSelStart
+ || mLastSelectionEnd != editorInfo.initialSelEnd;
+ final boolean inputTypeChanged = !mCurrentSettings.isSameInputType(editorInfo);
+ final boolean isDifferentTextField = !restarting || inputTypeChanged;
+ if (isDifferentTextField) {
+ final boolean currentSubtypeEnabled = mSubtypeSwitcher
+ .updateParametersOnStartInputViewAndReturnIfCurrentSubtypeEnabled();
+ if (!currentSubtypeEnabled) {
+ // Current subtype is disabled. Needs to update subtype and keyboard.
+ final InputMethodSubtype newSubtype = ImfUtils.getCurrentInputMethodSubtype(
+ this, mSubtypeSwitcher.getNoLanguageSubtype());
+ mSubtypeSwitcher.updateSubtype(newSubtype);
+ loadKeyboard();
+ }
+ }
// The EditorInfo might have a flag that affects fullscreen mode.
// Note: This call should be done by InputMethodService?
updateFullscreenMode();
- mLastSelectionStart = editorInfo.initialSelStart;
- mLastSelectionEnd = editorInfo.initialSelEnd;
- mInputAttributes = new InputAttributes(editorInfo, isFullscreenMode());
mApplicationSpecifiedCompletions = null;
- inputView.closing();
- mEnteredText = null;
- resetComposingState(true /* alsoResetLastComposedWord */);
- mDeleteCount = 0;
- mSpaceState = SPACE_STATE_NONE;
-
- loadSettings();
- updateCorrectionMode();
- updateSuggestionVisibility(mResources);
+ if (isDifferentTextField || selectionChanged) {
+ // If the selection changed, we reset the input state. Essentially, we come here with
+ // restarting == true when the app called setText() or similar. We should reset the
+ // state if the app set the text to something else, but keep it if it set a suggestion
+ // or something.
+ mEnteredText = null;
+ resetComposingState(true /* alsoResetLastComposedWord */);
+ mDeleteCount = 0;
+ mSpaceState = SPACE_STATE_NONE;
- if (mSuggest != null && mSettingsValues.mAutoCorrectEnabled) {
- mSuggest.setAutoCorrectionThreshold(mSettingsValues.mAutoCorrectionThreshold);
+ if (mSuggestionStripView != null) {
+ // This will set the punctuation suggestions if next word suggestion is off;
+ // otherwise it will clear the suggestion strip.
+ setPunctuationSuggestions();
+ }
}
- switcher.loadKeyboard(editorInfo, mSettingsValues);
+ mConnection.resetCachesUponCursorMove(editorInfo.initialSelStart);
- if (mSuggestionsView != null)
- mSuggestionsView.clear();
+ if (isDifferentTextField) {
+ mainKeyboardView.closing();
+ loadSettings();
+
+ if (mSuggest != null && mCurrentSettings.mCorrectionEnabled) {
+ mSuggest.setAutoCorrectionThreshold(mCurrentSettings.mAutoCorrectionThreshold);
+ }
+
+ switcher.loadKeyboard(editorInfo, mCurrentSettings);
+ }
setSuggestionStripShownInternal(
isSuggestionsStripVisible(), /* needsInputViewShown */ false);
- // Delay updating suggestions because keyboard input view may not be shown at this point.
- mHandler.postUpdateSuggestions();
+
+ mLastSelectionStart = editorInfo.initialSelStart;
+ mLastSelectionEnd = editorInfo.initialSelEnd;
+ // If we come here something in the text state is very likely to have changed.
+ // We should update the shift state regardless of whether we are restarting or not, because
+ // this is not perceived as a layout change that may be disruptive like we may have with
+ // 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();
mHandler.cancelDoubleSpacesTimer();
- inputView.setKeyPreviewPopupEnabled(mSettingsValues.mKeyPreviewPopupOn,
- mSettingsValues.mKeyPreviewPopupDismissDelay);
- inputView.setProximityCorrectionEnabled(true);
+ mainKeyboardView.setMainDictionaryAvailability(mIsMainDictionaryAvailable);
+ mainKeyboardView.setKeyPreviewPopupEnabled(mCurrentSettings.mKeyPreviewPopupOn,
+ mCurrentSettings.mKeyPreviewPopupDismissDelay);
+ mainKeyboardView.setGestureHandlingEnabledByUser(mCurrentSettings.mGestureInputEnabled);
+ mainKeyboardView.setGesturePreviewMode(mCurrentSettings.mGesturePreviewTrailEnabled,
+ mCurrentSettings.mGestureFloatingPreviewTextEnabled);
if (TRACE) Debug.startMethodTracing("/data/trace/latinime");
}
+ // Callback for the TargetApplicationGetter
+ @Override
public void onTargetApplicationKnown(final ApplicationInfo info) {
mTargetApplicationInfo = info;
}
@Override
public void onWindowHidden() {
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.latinIME_onWindowHidden(mLastSelectionStart, mLastSelectionEnd,
+ getCurrentInputConnection());
+ }
super.onWindowHidden();
- KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
- if (inputView != null) inputView.closing();
+ final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
+ if (mainKeyboardView != null) {
+ mainKeyboardView.closing();
+ }
}
private void onFinishInputInternal() {
super.onFinishInput();
LatinImeLogger.commit();
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.getInstance().latinIME_onFinishInputInternal();
+ }
- KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
- if (inputView != null) inputView.closing();
+ final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
+ if (mainKeyboardView != null) {
+ mainKeyboardView.closing();
+ }
}
- private void onFinishInputViewInternal(boolean finishingInput) {
+ private void onFinishInputViewInternal(final boolean finishingInput) {
super.onFinishInputView(finishingInput);
mKeyboardSwitcher.onFinishInputView();
- KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
- if (inputView != null) inputView.cancelAllMessages();
+ final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
+ if (mainKeyboardView != null) {
+ mainKeyboardView.cancelAllMessages();
+ }
// Remove pending messages related to update suggestions
- mHandler.cancelUpdateSuggestions();
+ mHandler.cancelUpdateSuggestionStrip();
}
@Override
- public void onUpdateSelection(int oldSelStart, int oldSelEnd,
- int newSelStart, int newSelEnd,
- int composingSpanStart, int composingSpanEnd) {
+ public void onUpdateSelection(final int oldSelStart, final int oldSelEnd,
+ final int newSelStart, final int newSelEnd,
+ final int composingSpanStart, final int composingSpanEnd) {
super.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd,
composingSpanStart, composingSpanEnd);
-
if (DEBUG) {
Log.i(TAG, "onUpdateSelection: oss=" + oldSelStart
+ ", ose=" + oldSelEnd
@@ -780,9 +821,16 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
+ ", ce=" + composingSpanEnd);
}
if (ProductionFlag.IS_EXPERIMENTAL) {
+ final boolean expectingUpdateSelectionFromLogger =
+ ResearchLogger.getAndClearLatinIMEExpectingUpdateSelection();
ResearchLogger.latinIME_onUpdateSelection(mLastSelectionStart, mLastSelectionEnd,
oldSelStart, oldSelEnd, newSelStart, newSelEnd, composingSpanStart,
- composingSpanEnd);
+ composingSpanEnd, mExpectingUpdateSelection,
+ expectingUpdateSelectionFromLogger, mConnection);
+ if (expectingUpdateSelectionFromLogger) {
+ // TODO: Investigate. Quitting now sounds wrong - we won't do the resetting work
+ return;
+ }
}
// TODO: refactor the following code to be less contrived.
@@ -799,7 +847,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// we know for sure the cursor moved while we were composing and we should reset
// the state.
final boolean noComposingSpan = composingSpanStart == -1 && composingSpanEnd == -1;
- if (!mExpectingUpdateSelection) {
+ if (!mExpectingUpdateSelection
+ && !mConnection.isBelatedExpectedUpdate(oldSelStart, newSelStart)) {
// TAKE CARE: there is a race condition when we enter this test even when the user
// did not explicitly move the cursor. This happens when typing fast, where two keys
// turn this flag on in succession and both onUpdateSelection() calls arrive after
@@ -809,16 +858,25 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// TODO: the following is probably better done in resetEntireInputState().
// it should only happen when the cursor moved, and the very purpose of the
// test below is to narrow down whether this happened or not. Likewise with
- // the call to postUpdateShiftState.
+ // the call to updateShiftState.
// We set this to NONE because after a cursor move, we don't want the space
// state-related special processing to kick in.
mSpaceState = SPACE_STATE_NONE;
if ((!mWordComposer.isComposingWord()) || selectionChanged || noComposingSpan) {
- resetEntireInputState();
+ // If we are composing a word and moving the cursor, we would want to set a
+ // suggestion span for recorrection to work correctly. Unfortunately, that
+ // would involve the keyboard committing some new text, which would move the
+ // cursor back to where it was. Latin IME could then fix the position of the cursor
+ // again, but the asynchronous nature of the calls results in this wreaking havoc
+ // with selection on double tap and the like.
+ // Another option would be to send suggestions each time we set the composing
+ // text, but that is probably too expensive to do, so we decided to leave things
+ // as is.
+ resetEntireInputState(newSelStart);
}
- mHandler.postUpdateShiftState();
+ mKeyboardSwitcher.updateShiftState();
}
mExpectingUpdateSelection = false;
// TODO: Decide to call restartSuggestionsOnWordBeforeCursorIfAtEndOfWord() or not
@@ -841,7 +899,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
*/
@Override
public void onExtractedTextClicked() {
- if (isSuggestionsRequested()) return;
+ if (mCurrentSettings.isSuggestionsRequested(mDisplayOrientation)) return;
super.onExtractedTextClicked();
}
@@ -856,8 +914,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
* cause the suggestions strip to disappear and re-appear.
*/
@Override
- public void onExtractedCursorMovement(int dx, int dy) {
- if (isSuggestionsRequested()) return;
+ public void onExtractedCursorMovement(final int dx, final int dy) {
+ if (mCurrentSettings.isSuggestionsRequested(mDisplayOrientation)) return;
super.onExtractedCursorMovement(dx, dy);
}
@@ -876,7 +934,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
@Override
- public void onDisplayCompletions(CompletionInfo[] applicationSpecifiedCompletions) {
+ public void onDisplayCompletions(final CompletionInfo[] applicationSpecifiedCompletions) {
if (DEBUG) {
Log.i(TAG, "Received completions:");
if (applicationSpecifiedCompletions != null) {
@@ -885,43 +943,46 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
}
}
+ if (!mCurrentSettings.isApplicationSpecifiedCompletionsOn()) return;
+ mApplicationSpecifiedCompletions = applicationSpecifiedCompletions;
+ if (applicationSpecifiedCompletions == null) {
+ clearSuggestionStrip();
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.latinIME_onDisplayCompletions(null);
+ }
+ return;
+ }
+
+ final ArrayList<SuggestedWords.SuggestedWordInfo> applicationSuggestedWords =
+ SuggestedWords.getFromApplicationSpecifiedCompletions(
+ applicationSpecifiedCompletions);
+ final SuggestedWords suggestedWords = new SuggestedWords(
+ applicationSuggestedWords,
+ false /* typedWordValid */,
+ false /* hasAutoCorrectionCandidate */,
+ false /* isPunctuationSuggestions */,
+ false /* isObsoleteSuggestions */,
+ false /* isPrediction */);
+ // When in fullscreen mode, show completions generated by the application
+ final boolean isAutoCorrection = false;
+ setSuggestionStrip(suggestedWords, isAutoCorrection);
+ setAutoCorrectionIndicator(isAutoCorrection);
+ // TODO: is this the right thing to do? What should we auto-correct to in
+ // this case? This says to keep whatever the user typed.
+ mWordComposer.setAutoCorrection(mWordComposer.getTypedWord());
+ setSuggestionStripShown(true);
if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.latinIME_onDisplayCompletions(applicationSpecifiedCompletions);
}
- if (mInputAttributes.mApplicationSpecifiedCompletionOn) {
- mApplicationSpecifiedCompletions = applicationSpecifiedCompletions;
- if (applicationSpecifiedCompletions == null) {
- clearSuggestions();
- return;
- }
+ }
- final ArrayList<SuggestedWords.SuggestedWordInfo> applicationSuggestedWords =
- SuggestedWords.getFromApplicationSpecifiedCompletions(
- applicationSpecifiedCompletions);
- final SuggestedWords suggestedWords = new SuggestedWords(
- applicationSuggestedWords,
- false /* typedWordValid */,
- false /* hasAutoCorrectionCandidate */,
- false /* allowsToBeAutoCorrected */,
- false /* isPunctuationSuggestions */,
- false /* isObsoleteSuggestions */,
- false /* isPrediction */);
- // When in fullscreen mode, show completions generated by the application
- final boolean isAutoCorrection = false;
- setSuggestions(suggestedWords, isAutoCorrection);
- setAutoCorrectionIndicator(isAutoCorrection);
- // TODO: is this the right thing to do? What should we auto-correct to in
- // this case? This says to keep whatever the user typed.
- mWordComposer.setAutoCorrection(mWordComposer.getTypedWord());
- setSuggestionStripShown(true);
- }
- }
-
- private void setSuggestionStripShownInternal(boolean shown, boolean needsInputViewShown) {
+ private void setSuggestionStripShownInternal(final boolean shown,
+ final boolean needsInputViewShown) {
// TODO: Modify this if we support suggestions with hard keyboard
if (onEvaluateInputViewShown() && mSuggestionsContainer != null) {
- final LatinKeyboardView keyboardView = mKeyboardSwitcher.getKeyboardView();
- final boolean inputViewShown = (keyboardView != null) ? keyboardView.isShown() : false;
+ final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
+ final boolean inputViewShown = (mainKeyboardView != null)
+ ? mainKeyboardView.isShown() : false;
final boolean shouldShowSuggestions = shown
&& (needsInputViewShown ? inputViewShown : true);
if (isFullscreenMode()) {
@@ -934,7 +995,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
}
- private void setSuggestionStripShown(boolean shown) {
+ private void setSuggestionStripShown(final boolean shown) {
setSuggestionStripShownInternal(shown, /* needsInputViewShown */true);
}
@@ -944,11 +1005,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
return currentHeight;
}
- final KeyboardView keyboardView = mKeyboardSwitcher.getKeyboardView();
- if (keyboardView == null) {
+ final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
+ if (mainKeyboardView == null) {
return 0;
}
- final int keyboardHeight = keyboardView.getHeight();
+ final int keyboardHeight = mainKeyboardView.getHeight();
final int suggestionsHeight = mSuggestionsContainer.getHeight();
final int displayHeight = mResources.getDisplayMetrics().heightPixels;
final Rect rect = new Rect();
@@ -958,17 +1019,18 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
- keyboardHeight;
final LayoutParams params = mKeyPreviewBackingView.getLayoutParams();
- params.height = mSuggestionsView.setMoreSuggestionsHeight(remainingHeight);
+ params.height = mSuggestionStripView.setMoreSuggestionsHeight(remainingHeight);
mKeyPreviewBackingView.setLayoutParams(params);
return params.height;
}
@Override
- public void onComputeInsets(InputMethodService.Insets outInsets) {
+ public void onComputeInsets(final InputMethodService.Insets outInsets) {
super.onComputeInsets(outInsets);
- final KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
- if (inputView == null || mSuggestionsContainer == null)
+ final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
+ if (mainKeyboardView == null || mSuggestionsContainer == null) {
return;
+ }
final int adjustedBackingHeight = getAdjustedBackingViewHeight();
final boolean backingGone = (mKeyPreviewBackingView.getVisibility() == View.GONE);
final int backingHeight = backingGone ? 0 : adjustedBackingHeight;
@@ -981,13 +1043,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
final int extraHeight = extractHeight + backingHeight + suggestionsHeight;
int touchY = extraHeight;
// Need to set touchable region only if input view is being shown
- final LatinKeyboardView keyboardView = mKeyboardSwitcher.getKeyboardView();
- if (keyboardView != null && keyboardView.isShown()) {
+ if (mainKeyboardView.isShown()) {
if (mSuggestionsContainer.getVisibility() == View.VISIBLE) {
touchY -= suggestionsHeight;
}
- final int touchWidth = inputView.getWidth();
- final int touchHeight = inputView.getHeight() + extraHeight
+ final int touchWidth = mainKeyboardView.getWidth();
+ final int touchHeight = mainKeyboardView.getHeight() + extraHeight
// Extend touchable region below the keyboard.
+ EXTENDED_TOUCHABLE_REGION_HEIGHT;
outInsets.touchableInsets = InputMethodService.Insets.TOUCHABLE_INSETS_REGION;
@@ -1001,8 +1062,17 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
public boolean onEvaluateFullscreenMode() {
// Reread resource value here, because this method is called by framework anytime as needed.
final boolean isFullscreenModeAllowed =
- mSettingsValues.isFullscreenModeAllowed(getResources());
- return super.onEvaluateFullscreenMode() && isFullscreenModeAllowed;
+ mCurrentSettings.isFullscreenModeAllowed(getResources());
+ if (super.onEvaluateFullscreenMode() && isFullscreenModeAllowed) {
+ // TODO: Remove this hack. Actually we should not really assume NO_EXTRACT_UI
+ // implies NO_FULLSCREEN. However, the framework mistakenly does. i.e. NO_EXTRACT_UI
+ // without NO_FULLSCREEN doesn't work as expected. Because of this we need this
+ // hack for now. Let's get rid of this once the framework gets fixed.
+ final EditorInfo ei = getCurrentInputEditorInfo();
+ return !(ei != null && ((ei.imeOptions & EditorInfo.IME_FLAG_NO_EXTRACT_UI) != 0));
+ } else {
+ return false;
+ }
}
@Override
@@ -1016,15 +1086,15 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
// This will reset the whole input state to the starting state. It will clear
- // the composing word, reset the last composed word, tell the inputconnection
- // and the composingStateManager about it.
- private void resetEntireInputState() {
+ // the composing word, reset the last composed word, tell the inputconnection about it.
+ private void resetEntireInputState(final int newCursorPosition) {
resetComposingState(true /* alsoResetLastComposedWord */);
- updateSuggestions();
- final InputConnection ic = getCurrentInputConnection();
- if (ic != null) {
- ic.finishComposingText();
+ if (mCurrentSettings.mBigramPredictionEnabled) {
+ clearSuggestionStrip();
+ } else {
+ setSuggestionStrip(mCurrentSettings.mSuggestPuncList, false);
}
+ mConnection.resetCachesUponCursorMove(newCursorPosition);
}
private void resetComposingState(final boolean alsoResetLastComposedWord) {
@@ -1033,86 +1103,66 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
}
- public void commitTyped(final InputConnection ic, final int separatorCode) {
+ private void commitTyped(final String separatorString) {
if (!mWordComposer.isComposingWord()) return;
final CharSequence typedWord = mWordComposer.getTypedWord();
if (typedWord.length() > 0) {
- if (ic != null) {
- ic.commitText(typedWord, 1);
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_commitText(typedWord);
- }
- }
- final CharSequence prevWord = addToUserHistoryDictionary(typedWord);
- mLastComposedWord = mWordComposer.commitWord(
- LastComposedWord.COMMIT_TYPE_USER_TYPED_WORD, typedWord.toString(),
- separatorCode, prevWord);
+ commitChosenWord(typedWord, LastComposedWord.COMMIT_TYPE_USER_TYPED_WORD,
+ separatorString);
}
- updateSuggestions();
}
+ // Called from the KeyboardSwitcher which needs to know auto caps state to display
+ // the right layout.
public int getCurrentAutoCapsState() {
- if (!mSettingsValues.mAutoCap) return Constants.TextUtils.CAP_MODE_OFF;
+ if (!mCurrentSettings.mAutoCap) return Constants.TextUtils.CAP_MODE_OFF;
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;
+ // 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);
+ }
- final InputConnection ic = getCurrentInputConnection();
- if (ic == null) 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 ic.getCursorCapsMode(inputType);
+ // Factor in auto-caps and manual caps and compute the current caps mode.
+ private int getActualCapsMode() {
+ final int keyboardShiftMode = mKeyboardSwitcher.getKeyboardShiftMode();
+ if (keyboardShiftMode != WordComposer.CAPS_MODE_AUTO_SHIFTED) return keyboardShiftMode;
+ final int auto = getCurrentAutoCapsState();
+ if (0 != (auto & TextUtils.CAP_MODE_CHARACTERS)) {
+ return WordComposer.CAPS_MODE_AUTO_SHIFT_LOCKED;
+ }
+ if (0 != auto) return WordComposer.CAPS_MODE_AUTO_SHIFTED;
+ return WordComposer.CAPS_MODE_OFF;
}
- // "ic" may be null
- private void swapSwapperAndSpaceWhileInBatchEdit(final InputConnection ic) {
- if (null == ic) return;
- CharSequence lastTwo = ic.getTextBeforeCursor(2, 0);
+ private void swapSwapperAndSpace() {
+ CharSequence lastTwo = mConnection.getTextBeforeCursor(2, 0);
// It is guaranteed lastTwo.charAt(1) is a swapper - else this method is not called.
if (lastTwo != null && lastTwo.length() == 2
&& lastTwo.charAt(0) == Keyboard.CODE_SPACE) {
- ic.deleteSurroundingText(2, 0);
+ mConnection.deleteSurroundingText(2, 0);
+ mConnection.commitText(lastTwo.charAt(1) + " ", 1);
if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_deleteSurroundingText(2);
- }
- ic.commitText(lastTwo.charAt(1) + " ", 1);
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_swapSwapperAndSpaceWhileInBatchEdit();
+ ResearchLogger.latinIME_swapSwapperAndSpace();
}
mKeyboardSwitcher.updateShiftState();
}
}
- private boolean maybeDoubleSpaceWhileInBatchEdit(final InputConnection ic) {
- if (mCorrectionMode == Suggest.CORRECTION_NONE) return false;
- if (ic == null) return false;
- final CharSequence lastThree = ic.getTextBeforeCursor(3, 0);
+ private boolean maybeDoubleSpace() {
+ if (!mCurrentSettings.mCorrectionEnabled) return false;
+ if (!mHandler.isAcceptingDoubleSpaces()) return false;
+ final CharSequence lastThree = mConnection.getTextBeforeCursor(3, 0);
if (lastThree != null && lastThree.length() == 3
&& canBeFollowedByPeriod(lastThree.charAt(0))
&& lastThree.charAt(1) == Keyboard.CODE_SPACE
- && lastThree.charAt(2) == Keyboard.CODE_SPACE
- && mHandler.isAcceptingDoubleSpaces()) {
+ && lastThree.charAt(2) == Keyboard.CODE_SPACE) {
mHandler.cancelDoubleSpacesTimer();
- ic.deleteSurroundingText(2, 0);
- ic.commitText(". ", 1);
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_doubleSpaceAutoPeriod();
- }
+ mConnection.deleteSurroundingText(2, 0);
+ mConnection.commitText(". ", 1);
mKeyboardSwitcher.updateShiftState();
return true;
}
@@ -1131,33 +1181,15 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|| codePoint == Keyboard.CODE_CLOSING_ANGLE_BRACKET;
}
- // "ic" may be null
- private static void removeTrailingSpaceWhileInBatchEdit(final InputConnection ic) {
- if (ic == null) return;
- final CharSequence lastOne = ic.getTextBeforeCursor(1, 0);
- if (lastOne != null && lastOne.length() == 1
- && lastOne.charAt(0) == Keyboard.CODE_SPACE) {
- ic.deleteSurroundingText(1, 0);
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_deleteSurroundingText(1);
- }
- }
- }
-
+ // Callback for the {@link SuggestionStripView}, to call when the "add to dictionary" hint is
+ // pressed.
@Override
- public boolean addWordToDictionary(String word) {
- if (USE_BINARY_USER_DICTIONARY) {
- ((UserBinaryDictionary)mUserDictionary).addWordToUserDictionary(word, 128);
- } else {
- ((UserDictionary)mUserDictionary).addWordToUserDictionary(word, 128);
- }
- // Suggestion strip should be updated after the operation of adding word to the
- // user dictionary
- mHandler.postUpdateSuggestions();
+ public boolean addWordToUserDictionary(final String word) {
+ mUserDictionary.addWordToUserDictionary(word, 128);
return true;
}
- private static boolean isAlphabet(int code) {
+ private static boolean isAlphabet(final int code) {
return Character.isLetter(code);
}
@@ -1170,7 +1202,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
public static final int CODE_SHOW_INPUT_METHOD_PICKER = 1;
@Override
- public boolean onCustomRequest(int requestCode) {
+ public boolean onCustomRequest(final int requestCode) {
if (isShowingOptionDialog()) return false;
switch (requestCode) {
case CODE_SHOW_INPUT_METHOD_PICKER:
@@ -1188,93 +1220,81 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
return mOptionsDialog != null && mOptionsDialog.isShowing();
}
- private static int getActionId(Keyboard keyboard) {
+ private static int getActionId(final Keyboard keyboard) {
return keyboard != null ? keyboard.mId.imeActionId() : EditorInfo.IME_ACTION_NONE;
}
- private void performEditorAction(int actionId) {
- final InputConnection ic = getCurrentInputConnection();
- if (ic != null) {
- ic.performEditorAction(actionId);
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_performEditorAction(actionId);
- }
- }
+ private void performEditorAction(final int actionId) {
+ mConnection.performEditorAction(actionId);
}
+ // TODO: Revise the language switch key behavior to make it much smarter and more reasonable.
private void handleLanguageSwitchKey() {
- final boolean includesOtherImes = mSettingsValues.mIncludesOtherImesInLanguageSwitchList;
final IBinder token = getWindow().getWindow().getAttributes().token;
+ if (mCurrentSettings.mIncludesOtherImesInLanguageSwitchList) {
+ mImm.switchToNextInputMethod(token, false /* onlyCurrentIme */);
+ return;
+ }
if (mShouldSwitchToLastSubtype) {
final InputMethodSubtype lastSubtype = mImm.getLastInputMethodSubtype();
final boolean lastSubtypeBelongsToThisIme =
ImfUtils.checkIfSubtypeBelongsToThisImeAndEnabled(this, lastSubtype);
- if ((includesOtherImes || lastSubtypeBelongsToThisIme)
- && mImm.switchToLastInputMethod(token)) {
+ if (lastSubtypeBelongsToThisIme && mImm.switchToLastInputMethod(token)) {
mShouldSwitchToLastSubtype = false;
} else {
- mImm.switchToNextInputMethod(token, !includesOtherImes);
+ mImm.switchToNextInputMethod(token, true /* onlyCurrentIme */);
mShouldSwitchToLastSubtype = true;
}
} else {
- mImm.switchToNextInputMethod(token, !includesOtherImes);
+ mImm.switchToNextInputMethod(token, true /* onlyCurrentIme */);
}
}
- static private void sendUpDownEnterOrBackspace(final int code, final InputConnection ic) {
+ private void sendDownUpKeyEventForBackwardCompatibility(final int code) {
final long eventTime = SystemClock.uptimeMillis();
- ic.sendKeyEvent(new KeyEvent(eventTime, eventTime,
+ mConnection.sendKeyEvent(new KeyEvent(eventTime, eventTime,
KeyEvent.ACTION_DOWN, code, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE));
- ic.sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime,
+ mConnection.sendKeyEvent(new KeyEvent(SystemClock.uptimeMillis(), eventTime,
KeyEvent.ACTION_UP, code, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0,
KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE));
}
- private void sendKeyCodePoint(int code) {
+ private void sendKeyCodePoint(final int code) {
// TODO: Remove this special handling of digit letters.
// For backward compatibility. See {@link InputMethodService#sendKeyChar(char)}.
if (code >= '0' && code <= '9') {
- super.sendKeyChar((char)code);
- return;
- }
-
- final InputConnection ic = getCurrentInputConnection();
- if (ic != null) {
- // 16 is android.os.Build.VERSION_CODES.JELLY_BEAN but we can't write it because
- // we want to be able to compile against the Ice Cream Sandwich SDK.
- if (Keyboard.CODE_ENTER == code && mTargetApplicationInfo != null
- && mTargetApplicationInfo.targetSdkVersion < 16) {
- // Backward compatibility mode. Before Jelly bean, the keyboard would simulate
- // a hardware keyboard event on pressing enter or delete. This is bad for many
- // reasons (there are race conditions with commits) but some applications are
- // relying on this behavior so we continue to support it for older apps.
- sendUpDownEnterOrBackspace(KeyEvent.KEYCODE_ENTER, ic);
- } else {
- final String text = new String(new int[] { code }, 0, 1);
- ic.commitText(text, text.length());
- }
+ sendDownUpKeyEventForBackwardCompatibility(code - '0' + KeyEvent.KEYCODE_0);
if (ProductionFlag.IS_EXPERIMENTAL) {
ResearchLogger.latinIME_sendKeyCodePoint(code);
}
+ return;
+ }
+
+ // 16 is android.os.Build.VERSION_CODES.JELLY_BEAN but we can't write it because
+ // we want to be able to compile against the Ice Cream Sandwich SDK.
+ if (Keyboard.CODE_ENTER == code && mTargetApplicationInfo != null
+ && mTargetApplicationInfo.targetSdkVersion < 16) {
+ // Backward compatibility mode. Before Jelly bean, the keyboard would simulate
+ // a hardware keyboard event on pressing enter or delete. This is bad for many
+ // reasons (there are race conditions with commits) but some applications are
+ // relying on this behavior so we continue to support it for older apps.
+ sendDownUpKeyEventForBackwardCompatibility(KeyEvent.KEYCODE_ENTER);
+ } else {
+ final String text = new String(new int[] { code }, 0, 1);
+ mConnection.commitText(text, text.length());
}
}
// Implementation of {@link KeyboardActionListener}.
@Override
- public void onCodeInput(int primaryCode, int x, int y) {
+ public void onCodeInput(final int primaryCode, final int x, final int y) {
final long when = SystemClock.uptimeMillis();
if (primaryCode != Keyboard.CODE_DELETE || when > mLastKeyTime + QUICK_PRESS) {
mDeleteCount = 0;
}
mLastKeyTime = when;
-
- if (ProductionFlag.IS_EXPERIMENTAL) {
- if (ResearchLogger.sIsLogging) {
- ResearchLogger.getInstance().logKeyEvent(primaryCode, x, y);
- }
- }
-
+ mConnection.beginBatchEdit();
final KeyboardSwitcher switcher = mKeyboardSwitcher;
// The space state depends only on the last character pressed and its own previous
// state. Here, we revert the space state to neutral if the key is actually modifying
@@ -1307,7 +1327,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
onSettingsKeyPressed();
break;
case Keyboard.CODE_SHORTCUT:
- mSubtypeSwitcher.switchToShortcutIME();
+ mSubtypeSwitcher.switchToShortcutIME(this);
break;
case Keyboard.CODE_ACTION_ENTER:
performEditorAction(getActionId(switcher.getKeyboard()));
@@ -1321,23 +1341,35 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
case Keyboard.CODE_LANGUAGE_SWITCH:
handleLanguageSwitchKey();
break;
- default:
- if (primaryCode == Keyboard.CODE_TAB
- && mInputAttributes.mEditorAction == EditorInfo.IME_ACTION_NEXT) {
- performEditorAction(EditorInfo.IME_ACTION_NEXT);
- break;
+ case Keyboard.CODE_RESEARCH:
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.getInstance().onResearchKeySelected(this);
}
+ break;
+ default:
mSpaceState = SPACE_STATE_NONE;
- if (mSettingsValues.isWordSeparator(primaryCode)) {
+ if (mCurrentSettings.isWordSeparator(primaryCode)) {
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;
final Keyboard keyboard = mKeyboardSwitcher.getKeyboard();
if (keyboard != null && keyboard.hasProximityCharsCorrection(primaryCode)) {
- handleCharacter(primaryCode, x, y, spaceState);
+ keyX = x;
+ keyY = y;
} else {
- handleCharacter(primaryCode, NOT_A_TOUCH_COORDINATE, NOT_A_TOUCH_COORDINATE,
- spaceState);
+ keyX = Constants.NOT_A_COORDINATE;
+ keyY = Constants.NOT_A_COORDINATE;
}
+ handleCharacter(primaryCode, keyX, keyY, spaceState);
}
mExpectingUpdateSelection = true;
mShouldSwitchToLastSubtype = true;
@@ -1348,34 +1380,201 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (!didAutoCorrect && primaryCode != Keyboard.CODE_SHIFT
&& primaryCode != Keyboard.CODE_SWITCH_ALPHA_SYMBOL)
mLastComposedWord.deactivate();
- mEnteredText = null;
+ if (Keyboard.CODE_DELETE != primaryCode) {
+ mEnteredText = null;
+ }
+ mConnection.endBatchEdit();
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.latinIME_onCodeInput(primaryCode, x, y);
+ }
}
+ // Called from PointerTracker through the KeyboardActionListener interface
@Override
- public void onTextInput(CharSequence text) {
- final InputConnection ic = getCurrentInputConnection();
- if (ic == null) return;
- ic.beginBatchEdit();
- commitTyped(ic, LastComposedWord.NOT_A_SEPARATOR);
- text = specificTldProcessingOnTextInput(ic, text);
+ public void onTextInput(final CharSequence rawText) {
+ mConnection.beginBatchEdit();
+ if (mWordComposer.isComposingWord()) {
+ commitCurrentAutoCorrection(rawText.toString());
+ } else {
+ resetComposingState(true /* alsoResetLastComposedWord */);
+ }
+ mHandler.postUpdateSuggestionStrip();
+ final CharSequence text = specificTldProcessingOnTextInput(rawText);
if (SPACE_STATE_PHANTOM == mSpaceState) {
sendKeyCodePoint(Keyboard.CODE_SPACE);
}
- ic.commitText(text, 1);
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_commitText(text);
- }
- ic.endBatchEdit();
+ 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;
- resetComposingState(true /* alsoResetLastComposedWord */);
}
- // ic may not be null
- private CharSequence specificTldProcessingOnTextInput(final InputConnection ic,
- final CharSequence text) {
+ @Override
+ public void onStartBatchInput() {
+ BatchInputUpdater.getInstance().onStartBatchInput();
+ mConnection.beginBatchEdit();
+ if (mWordComposer.isComposingWord()) {
+ 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;
+ // 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;
+ } else {
+ final int codePointBeforeCursor = mConnection.getCodePointBeforeCursor();
+ // TODO: reverse this logic. We should have the means to determine whether a character
+ // should usually be followed by a space, and it should be more readable.
+ if (Constants.NOT_A_CODE != codePointBeforeCursor
+ && !Character.isWhitespace(codePointBeforeCursor)
+ && !mCurrentSettings.isPhantomSpacePromotingSymbol(codePointBeforeCursor)
+ && !mCurrentSettings.isWeakSpaceStripper(codePointBeforeCursor)) {
+ mSpaceState = SPACE_STATE_PHANTOM;
+ }
+ }
+ mConnection.endBatchEdit();
+ mWordComposer.setCapitalizedModeAtStartComposingTime(getActualCapsMode());
+ }
+
+ private static final class BatchInputUpdater implements Handler.Callback {
+ private final Handler mHandler;
+ private LatinIME mLatinIme;
+ private boolean mInBatchInput; // synchornized using "this".
+
+ private BatchInputUpdater() {
+ final HandlerThread handlerThread = new HandlerThread(
+ BatchInputUpdater.class.getSimpleName());
+ handlerThread.start();
+ mHandler = new Handler(handlerThread.getLooper(), this);
+ }
+
+ // Initialization-on-demand holder
+ private static final class OnDemandInitializationHolder {
+ public static final BatchInputUpdater sInstance = new BatchInputUpdater();
+ }
+
+ public static BatchInputUpdater getInstance() {
+ return OnDemandInitializationHolder.sInstance;
+ }
+
+ private static final int MSG_UPDATE_GESTURE_PREVIEW_AND_SUGGESTION_STRIP = 1;
+
+ @Override
+ public boolean handleMessage(final Message msg) {
+ switch (msg.what) {
+ case MSG_UPDATE_GESTURE_PREVIEW_AND_SUGGESTION_STRIP:
+ updateBatchInput((InputPointers)msg.obj, mLatinIme);
+ break;
+ }
+ return true;
+ }
+
+ // Run in the UI thread.
+ public synchronized void onStartBatchInput() {
+ mInBatchInput = true;
+ }
+
+ // Run in the Handler thread.
+ private synchronized void updateBatchInput(final InputPointers batchPointers,
+ final LatinIME latinIme) {
+ if (!mInBatchInput) {
+ // Batch input has ended while the message was being delivered.
+ return;
+ }
+ final SuggestedWords suggestedWords = getSuggestedWordsGestureLocked(
+ batchPointers, latinIme);
+ latinIme.mHandler.showGesturePreviewAndSuggestionStrip(
+ suggestedWords, false /* dismissGestureFloatingPreviewText */);
+ }
+
+ // Run in the UI thread.
+ public void onUpdateBatchInput(final InputPointers batchPointers, final LatinIME latinIme) {
+ mLatinIme = latinIme;
+ if (mHandler.hasMessages(MSG_UPDATE_GESTURE_PREVIEW_AND_SUGGESTION_STRIP)) {
+ return;
+ }
+ mHandler.obtainMessage(
+ MSG_UPDATE_GESTURE_PREVIEW_AND_SUGGESTION_STRIP, batchPointers)
+ .sendToTarget();
+ }
+
+ // Run in the UI thread.
+ public synchronized SuggestedWords onEndBatchInput(final InputPointers batchPointers,
+ final LatinIME latinIme) {
+ mInBatchInput = false;
+ final SuggestedWords suggestedWords = getSuggestedWordsGestureLocked(
+ batchPointers, latinIme);
+ latinIme.mHandler.showGesturePreviewAndSuggestionStrip(
+ suggestedWords, true /* dismissGestureFloatingPreviewText */);
+ return suggestedWords;
+ }
+
+ // {@link LatinIME#getSuggestedWords(int)} method calls with same session id have to
+ // be synchronized.
+ private static SuggestedWords getSuggestedWordsGestureLocked(
+ final InputPointers batchPointers, final LatinIME latinIme) {
+ latinIme.mWordComposer.setBatchInputPointers(batchPointers);
+ return latinIme.getSuggestedWords(Suggest.SESSION_GESTURE);
+ }
+ }
+
+ private void showGesturePreviewAndSuggestionStrip(final SuggestedWords suggestedWords,
+ final boolean dismissGestureFloatingPreviewText) {
+ final String batchInputText = (suggestedWords.size() > 0)
+ ? suggestedWords.getWord(0) : null;
+ final KeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
+ mainKeyboardView.showGestureFloatingPreviewText(batchInputText);
+ showSuggestionStrip(suggestedWords, null);
+ if (dismissGestureFloatingPreviewText) {
+ mainKeyboardView.dismissGestureFloatingPreviewText();
+ }
+ }
+
+ @Override
+ public void onUpdateBatchInput(final InputPointers batchPointers) {
+ BatchInputUpdater.getInstance().onUpdateBatchInput(batchPointers, this);
+ }
+
+ @Override
+ public void onEndBatchInput(final InputPointers batchPointers) {
+ final SuggestedWords suggestedWords = BatchInputUpdater.getInstance().onEndBatchInput(
+ batchPointers, this);
+ final String batchInputText = (suggestedWords.size() > 0)
+ ? suggestedWords.getWord(0) : null;
+ if (TextUtils.isEmpty(batchInputText)) {
+ return;
+ }
+ mWordComposer.setBatchInputWord(batchInputText);
+ mConnection.beginBatchEdit();
+ if (SPACE_STATE_PHANTOM == mSpaceState) {
+ sendKeyCodePoint(Keyboard.CODE_SPACE);
+ }
+ mConnection.setComposingText(batchInputText, 1);
+ mExpectingUpdateSelection = true;
+ mConnection.endBatchEdit();
+ // Space state must be updated before calling updateShiftState
+ mSpaceState = SPACE_STATE_PHANTOM;
+ mKeyboardSwitcher.updateShiftState();
+ }
+
+ private CharSequence specificTldProcessingOnTextInput(final CharSequence text) {
if (text.length() <= 1 || text.charAt(0) != Keyboard.CODE_PERIOD
|| !Character.isLetter(text.charAt(1))) {
// Not a tld: do nothing.
@@ -1384,7 +1583,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// We have a TLD (or something that looks like this): make sure we don't add
// a space even if currently in phantom mode.
mSpaceState = SPACE_STATE_NONE;
- final CharSequence lastOne = ic.getTextBeforeCursor(1, 0);
+ // TODO: use getCodePointBeforeCursor instead to improve performance and simplify the code
+ final CharSequence lastOne = mConnection.getTextBeforeCursor(1, 0);
if (lastOne != null && lastOne.length() == 1
&& lastOne.charAt(0) == Keyboard.CODE_PERIOD) {
return text.subSequence(1, text.length());
@@ -1393,6 +1593,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
}
+ // Called from PointerTracker through the KeyboardActionListener interface
@Override
public void onCancelInput() {
// User released a finger outside any key
@@ -1400,67 +1601,54 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
private void handleBackspace(final int spaceState) {
- final InputConnection ic = getCurrentInputConnection();
- if (ic == null) return;
- ic.beginBatchEdit();
- handleBackspaceWhileInBatchEdit(spaceState, ic);
- ic.endBatchEdit();
- }
-
- // "ic" may not be null.
- private void handleBackspaceWhileInBatchEdit(final int spaceState, final InputConnection ic) {
- // In many cases, we may have to put the keyboard in auto-shift state again.
+ // In many cases, we may have to put the keyboard in auto-shift state again. However
+ // we want to wait a few milliseconds before doing it to avoid the keyboard flashing
+ // during key repeat.
mHandler.postUpdateShiftState();
- if (mEnteredText != null && sameAsTextBeforeCursor(ic, mEnteredText)) {
- // Cancel multi-character input: remove the text we just entered.
- // This is triggered on backspace after a key that inputs multiple characters,
- // like the smiley key or the .com key.
- final int length = mEnteredText.length();
- ic.deleteSurroundingText(length, 0);
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_deleteSurroundingText(length);
- }
- // If we have mEnteredText, then we know that mHasUncommittedTypedChars == false.
- // In addition we know that spaceState is false, and that we should not be
- // reverting any autocorrect at this point. So we can safely return.
- return;
- }
-
if (mWordComposer.isComposingWord()) {
final int length = mWordComposer.size();
if (length > 0) {
- mWordComposer.deleteLast();
- ic.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
- // If we have deleted the last remaining character of a word, then we are not
- // isComposingWord() any more.
- if (!mWordComposer.isComposingWord()) {
- // Not composing word any more, so we can show bigrams.
- mHandler.postUpdateBigramPredictions();
+ // Immediately after a batch input.
+ if (SPACE_STATE_PHANTOM == spaceState) {
+ mWordComposer.reset();
} else {
- // Still composing a word, so we still have letters to deduce a suggestion from.
- mHandler.postUpdateSuggestions();
+ mWordComposer.deleteLast();
}
+ mConnection.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
+ mHandler.postUpdateSuggestionStrip();
} else {
- ic.deleteSurroundingText(1, 0);
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_deleteSurroundingText(1);
- }
+ mConnection.deleteSurroundingText(1, 0);
}
} else {
if (mLastComposedWord.canRevertCommit()) {
- Utils.Stats.onAutoCorrectionCancellation();
- revertCommit(ic);
+ if (ProductionFlag.IS_INTERNAL) {
+ Stats.onAutoCorrectionCancellation();
+ }
+ revertCommit();
+ return;
+ }
+ if (mEnteredText != null && mConnection.sameAsTextBeforeCursor(mEnteredText)) {
+ // Cancel multi-character input: remove the text we just entered.
+ // This is triggered on backspace after a key that inputs multiple characters,
+ // like the smiley key or the .com key.
+ final int length = mEnteredText.length();
+ mConnection.deleteSurroundingText(length, 0);
+ mEnteredText = null;
+ // If we have mEnteredText, then we know that mHasUncommittedTypedChars == false.
+ // In addition we know that spaceState is false, and that we should not be
+ // reverting any autocorrect at this point. So we can safely return.
return;
}
if (SPACE_STATE_DOUBLE == spaceState) {
- if (revertDoubleSpaceWhileInBatchEdit(ic)) {
+ mHandler.cancelDoubleSpacesTimer();
+ if (mConnection.revertDoubleSpace()) {
// No need to reset mSpaceState, it has already be done (that's why we
// receive it as a parameter)
return;
}
} else if (SPACE_STATE_SWAP_PUNCTUATION == spaceState) {
- if (revertSwapPunctuation(ic)) {
+ if (mConnection.revertSwapPunctuation()) {
// Likewise
return;
}
@@ -1471,11 +1659,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (mLastSelectionStart != mLastSelectionEnd) {
// If there is a selection, remove it.
final int lengthToDelete = mLastSelectionEnd - mLastSelectionStart;
- ic.setSelection(mLastSelectionEnd, mLastSelectionEnd);
- ic.deleteSurroundingText(lengthToDelete, 0);
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_deleteSurroundingText(lengthToDelete);
- }
+ mConnection.setSelection(mLastSelectionEnd, mLastSelectionEnd);
+ mConnection.deleteSurroundingText(lengthToDelete, 0);
} else {
// There is no selection, just delete one character.
if (NOT_A_CURSOR_POSITION == mLastSelectionEnd) {
@@ -1490,40 +1675,33 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// a hardware keyboard event on pressing enter or delete. This is bad for many
// reasons (there are race conditions with commits) but some applications are
// relying on this behavior so we continue to support it for older apps.
- sendUpDownEnterOrBackspace(KeyEvent.KEYCODE_DEL, ic);
+ sendDownUpKeyEventForBackwardCompatibility(KeyEvent.KEYCODE_DEL);
} else {
- ic.deleteSurroundingText(1, 0);
- }
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_deleteSurroundingText(1);
+ mConnection.deleteSurroundingText(1, 0);
}
if (mDeleteCount > DELETE_ACCELERATE_AT) {
- ic.deleteSurroundingText(1, 0);
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_deleteSurroundingText(1);
- }
+ mConnection.deleteSurroundingText(1, 0);
}
}
- if (isSuggestionsRequested()) {
- restartSuggestionsOnWordBeforeCursorIfAtEndOfWord(ic);
+ if (mCurrentSettings.isSuggestionsRequested(mDisplayOrientation)) {
+ restartSuggestionsOnWordBeforeCursorIfAtEndOfWord();
}
}
}
- // ic may be null
- private boolean maybeStripSpaceWhileInBatchEdit(final InputConnection ic, final int code,
+ private boolean maybeStripSpace(final int code,
final int spaceState, final boolean isFromSuggestionStrip) {
if (Keyboard.CODE_ENTER == code && SPACE_STATE_SWAP_PUNCTUATION == spaceState) {
- removeTrailingSpaceWhileInBatchEdit(ic);
+ mConnection.removeTrailingSpace();
return false;
} else if ((SPACE_STATE_WEAK == spaceState
|| SPACE_STATE_SWAP_PUNCTUATION == spaceState)
&& isFromSuggestionStrip) {
- if (mSettingsValues.isWeakSpaceSwapper(code)) {
+ if (mCurrentSettings.isWeakSpaceSwapper(code)) {
return true;
} else {
- if (mSettingsValues.isWeakSpaceStripper(code)) {
- removeTrailingSpaceWhileInBatchEdit(ic);
+ if (mCurrentSettings.isWeakSpaceStripper(code)) {
+ mConnection.removeTrailingSpace();
}
return false;
}
@@ -1534,20 +1712,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private void handleCharacter(final int primaryCode, final int x,
final int y, final int spaceState) {
- final InputConnection ic = getCurrentInputConnection();
- if (null != ic) ic.beginBatchEdit();
- // TODO: if ic is null, does it make any sense to call this?
- handleCharacterWhileInBatchEdit(primaryCode, x, y, spaceState, ic);
- if (null != ic) ic.endBatchEdit();
- }
-
- // "ic" may be null without this crashing, but the behavior will be really strange
- private void handleCharacterWhileInBatchEdit(final int primaryCode,
- final int x, final int y, final int spaceState, final InputConnection ic) {
boolean isComposingWord = mWordComposer.isComposingWord();
if (SPACE_STATE_PHANTOM == spaceState &&
- !mSettingsValues.isSymbolExcludedFromWordSeparators(primaryCode)) {
+ !mCurrentSettings.isSymbolExcludedFromWordSeparators(primaryCode)) {
if (isComposingWord) {
// Sanity check
throw new RuntimeException("Should not be composing here");
@@ -1559,8 +1727,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// dozen milliseconds. Avoid calling it as much as possible, since we are on the UI
// thread here.
if (!isComposingWord && (isAlphabet(primaryCode)
- || mSettingsValues.isSymbolExcludedFromWordSeparators(primaryCode))
- && isSuggestionsRequested() && !isCursorTouchingWord()) {
+ || mCurrentSettings.isSymbolExcludedFromWordSeparators(primaryCode))
+ && mCurrentSettings.isSuggestionsRequested(mDisplayOrientation) &&
+ !mConnection.isCursorTouchingWord(mCurrentSettings)) {
// Reset entirely the composing state anyway, then start composing a new word unless
// the character is a single quote. The idea here is, single quote is not a
// separator and it should be treated as a normal character, except in the first
@@ -1571,85 +1740,71 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// it entirely and resume suggestions on the previous word, we'd like to still
// have touch coordinates for it.
resetComposingState(false /* alsoResetLastComposedWord */);
- clearSuggestions();
}
if (isComposingWord) {
- mWordComposer.add(
- primaryCode, x, y, mKeyboardSwitcher.getKeyboardView().getKeyDetector());
- if (ic != null) {
- // If it's the first letter, make note of auto-caps state
- if (mWordComposer.size() == 1) {
- mWordComposer.setAutoCapitalized(
- getCurrentAutoCapsState() != Constants.TextUtils.CAP_MODE_OFF);
- }
- ic.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
+ final int keyX, keyY;
+ if (KeyboardActionListener.Adapter.isInvalidCoordinate(x)
+ || KeyboardActionListener.Adapter.isInvalidCoordinate(y)) {
+ keyX = x;
+ keyY = y;
+ } else {
+ final KeyDetector keyDetector =
+ mKeyboardSwitcher.getMainKeyboardView().getKeyDetector();
+ keyX = keyDetector.getTouchX(x);
+ keyY = keyDetector.getTouchY(y);
+ }
+ mWordComposer.add(primaryCode, keyX, keyY);
+ // If it's the first letter, make note of auto-caps state
+ if (mWordComposer.size() == 1) {
+ mWordComposer.setCapitalizedModeAtStartComposingTime(getActualCapsMode());
}
- mHandler.postUpdateSuggestions();
+ mConnection.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
} else {
- final boolean swapWeakSpace = maybeStripSpaceWhileInBatchEdit(ic, primaryCode,
- spaceState, KeyboardActionListener.SUGGESTION_STRIP_COORDINATE == x);
+ final boolean swapWeakSpace = maybeStripSpace(primaryCode,
+ spaceState, Constants.SUGGESTION_STRIP_COORDINATE == x);
sendKeyCodePoint(primaryCode);
if (swapWeakSpace) {
- swapSwapperAndSpaceWhileInBatchEdit(ic);
+ swapSwapperAndSpace();
mSpaceState = SPACE_STATE_WEAK;
}
- // Some characters are not word separators, yet they don't start a new
- // composing span. For these, we haven't changed the suggestion strip, and
- // if the "add to dictionary" hint is shown, we should do so now. Examples of
- // such characters include single quote, dollar, and others; the exact list is
- // the list of characters for which we enter handleCharacterWhileInBatchEdit
- // that don't match the test if ((isAlphabet...)) at the top of this method.
- if (null != mSuggestionsView && mSuggestionsView.dismissAddToDictionaryHint()) {
- mHandler.postUpdateBigramPredictions();
- }
+ // In case the "add to dictionary" hint was still displayed.
+ if (null != mSuggestionStripView) mSuggestionStripView.dismissAddToDictionaryHint();
+ }
+ mHandler.postUpdateSuggestionStrip();
+ if (ProductionFlag.IS_INTERNAL) {
+ Utils.Stats.onNonSeparator((char)primaryCode, x, y);
}
- Utils.Stats.onNonSeparator((char)primaryCode, x, y);
}
// Returns true if we did an autocorrection, false otherwise.
private boolean handleSeparator(final int primaryCode, final int x, final int y,
final int spaceState) {
- // Should dismiss the "Touch again to save" message when handling separator
- if (mSuggestionsView != null && mSuggestionsView.dismissAddToDictionaryHint()) {
- mHandler.cancelUpdateBigramPredictions();
- mHandler.postUpdateSuggestions();
- }
-
boolean didAutoCorrect = false;
// Handle separator
- final InputConnection ic = getCurrentInputConnection();
- if (ic != null) {
- ic.beginBatchEdit();
- }
if (mWordComposer.isComposingWord()) {
- // In certain languages where single quote is a separator, it's better
- // not to auto correct, but accept the typed word. For instance,
- // in Italian dov' should not be expanded to dove' because the elision
- // requires the last vowel to be removed.
- final boolean shouldAutoCorrect = mSettingsValues.mAutoCorrectEnabled
- && !mInputAttributes.mInputTypeNoAutoCorrect;
- if (shouldAutoCorrect && primaryCode != Keyboard.CODE_SINGLE_QUOTE) {
- commitCurrentAutoCorrection(primaryCode, ic);
+ if (mCurrentSettings.mCorrectionEnabled) {
+ // TODO: maybe cache Strings in an <String> sparse array or something
+ commitCurrentAutoCorrection(new String(new int[]{primaryCode}, 0, 1));
didAutoCorrect = true;
} else {
- commitTyped(ic, primaryCode);
+ commitTyped(new String(new int[]{primaryCode}, 0, 1));
}
}
- final boolean swapWeakSpace = maybeStripSpaceWhileInBatchEdit(ic, primaryCode, spaceState,
- KeyboardActionListener.SUGGESTION_STRIP_COORDINATE == x);
+ final boolean swapWeakSpace = maybeStripSpace(primaryCode, spaceState,
+ Constants.SUGGESTION_STRIP_COORDINATE == x);
if (SPACE_STATE_PHANTOM == spaceState &&
- mSettingsValues.isPhantomSpacePromotingSymbol(primaryCode)) {
+ mCurrentSettings.isPhantomSpacePromotingSymbol(primaryCode)) {
sendKeyCodePoint(Keyboard.CODE_SPACE);
}
sendKeyCodePoint(primaryCode);
if (Keyboard.CODE_SPACE == primaryCode) {
- if (isSuggestionsRequested()) {
- if (maybeDoubleSpaceWhileInBatchEdit(ic)) {
+ if (mCurrentSettings.isSuggestionsRequested(mDisplayOrientation)) {
+ if (maybeDoubleSpace()) {
mSpaceState = SPACE_STATE_DOUBLE;
} else if (!isShowingPunctuationList()) {
mSpaceState = SPACE_STATE_WEAK;
@@ -1657,21 +1812,26 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
mHandler.startDoubleSpacesTimer();
- if (!isCursorTouchingWord()) {
- mHandler.cancelUpdateSuggestions();
- mHandler.postUpdateBigramPredictions();
+ if (!mConnection.isCursorTouchingWord(mCurrentSettings)) {
+ mHandler.postUpdateSuggestionStrip();
}
} else {
if (swapWeakSpace) {
- swapSwapperAndSpaceWhileInBatchEdit(ic);
+ swapSwapperAndSpace();
mSpaceState = SPACE_STATE_SWAP_PUNCTUATION;
- } else if (SPACE_STATE_PHANTOM == spaceState) {
+ } else if (SPACE_STATE_PHANTOM == spaceState
+ && !mCurrentSettings.isWeakSpaceStripper(primaryCode)
+ && !mCurrentSettings.isPhantomSpacePromotingSymbol(primaryCode)) {
// If we are in phantom space state, and the user presses a separator, we want to
// stay in phantom space state so that the next keypress has a chance to add the
// space. For example, if I type "Good dat", pick "day" from the suggestion strip
// then insert a comma and go on to typing the next word, I want the space to be
// inserted automatically before the next word, the same way it is when I don't
// input the comma.
+ // The case is a little different if the separator is a space stripper. Such a
+ // separator does not normally need a space on the right (that's the difference
+ // between swappers and strippers), so we should not stay in phantom space state if
+ // the separator is a stripper. Hence the additional test above.
mSpaceState = SPACE_STATE_PHANTOM;
}
@@ -1679,12 +1839,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// already displayed or not, so it's okay.
setPunctuationSuggestions();
}
-
- Utils.Stats.onSeparator((char)primaryCode, x, y);
-
- if (ic != null) {
- ic.endBatchEdit();
+ if (ProductionFlag.IS_INTERNAL) {
+ Utils.Stats.onSeparator((char)primaryCode, x, y);
}
+
+ mKeyboardSwitcher.updateShiftState();
return didAutoCorrect;
}
@@ -1695,153 +1854,142 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
private void handleClose() {
- commitTyped(getCurrentInputConnection(), LastComposedWord.NOT_A_SEPARATOR);
+ commitTyped(LastComposedWord.NOT_A_SEPARATOR);
requestHideSelf(0);
- LatinKeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
- if (inputView != null)
- inputView.closing();
- }
-
- public boolean isSuggestionsRequested() {
- return mInputAttributes.mIsSettingsSuggestionStripOn
- && (mCorrectionMode > 0 || isShowingSuggestionsStrip());
- }
-
- public boolean isShowingPunctuationList() {
- if (mSuggestionsView == null) return false;
- return mSettingsValues.mSuggestPuncList == mSuggestionsView.getSuggestions();
+ final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
+ if (mainKeyboardView != null) {
+ mainKeyboardView.closing();
+ }
}
- public boolean isShowingSuggestionsStrip() {
- return (mSuggestionVisibility == SUGGESTION_VISIBILILTY_SHOW_VALUE)
- || (mSuggestionVisibility == SUGGESTION_VISIBILILTY_SHOW_ONLY_PORTRAIT_VALUE
- && mDisplayOrientation == Configuration.ORIENTATION_PORTRAIT);
+ // TODO: make this private
+ // Outside LatinIME, only used by the test suite.
+ /* package for tests */
+ boolean isShowingPunctuationList() {
+ if (mSuggestionStripView == null) return false;
+ return mCurrentSettings.mSuggestPuncList == mSuggestionStripView.getSuggestions();
}
- public boolean isSuggestionsStripVisible() {
- if (mSuggestionsView == null)
+ private boolean isSuggestionsStripVisible() {
+ if (mSuggestionStripView == null)
return false;
- if (mSuggestionsView.isShowingAddToDictionaryHint())
+ if (mSuggestionStripView.isShowingAddToDictionaryHint())
return true;
- if (!isShowingSuggestionsStrip())
+ if (!mCurrentSettings.isSuggestionStripVisibleInOrientation(mDisplayOrientation))
return false;
- if (mInputAttributes.mApplicationSpecifiedCompletionOn)
+ if (mCurrentSettings.isApplicationSpecifiedCompletionsOn())
return true;
- return isSuggestionsRequested();
- }
-
- public void switchToKeyboardView() {
- if (DEBUG) {
- Log.d(TAG, "Switch to keyboard view.");
- }
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_switchToKeyboardView();
- }
- View v = mKeyboardSwitcher.getKeyboardView();
- if (v != null) {
- // Confirms that the keyboard view doesn't have parent view.
- ViewParent p = v.getParent();
- if (p != null && p instanceof ViewGroup) {
- ((ViewGroup) p).removeView(v);
- }
- setInputView(v);
- }
- setSuggestionStripShown(isSuggestionsStripVisible());
- updateInputViewShown();
- mHandler.postUpdateSuggestions();
+ return mCurrentSettings.isSuggestionsRequested(mDisplayOrientation);
}
- public void clearSuggestions() {
- setSuggestions(SuggestedWords.EMPTY, false);
+ private void clearSuggestionStrip() {
+ setSuggestionStrip(SuggestedWords.EMPTY, false);
setAutoCorrectionIndicator(false);
}
- private void setSuggestions(final SuggestedWords words, final boolean isAutoCorrection) {
- if (mSuggestionsView != null) {
- mSuggestionsView.setSuggestions(words);
+ private void setSuggestionStrip(final SuggestedWords words, final boolean isAutoCorrection) {
+ if (mSuggestionStripView != null) {
+ mSuggestionStripView.setSuggestions(words);
mKeyboardSwitcher.onAutoCorrectionStateChanged(isAutoCorrection);
}
}
private void setAutoCorrectionIndicator(final boolean newAutoCorrectionIndicator) {
// Put a blue underline to a word in TextView which will be auto-corrected.
- final InputConnection ic = getCurrentInputConnection();
- if (ic == null) return;
if (mIsAutoCorrectionIndicatorOn != newAutoCorrectionIndicator
&& mWordComposer.isComposingWord()) {
mIsAutoCorrectionIndicatorOn = newAutoCorrectionIndicator;
final CharSequence textWithUnderline =
getTextWithUnderline(mWordComposer.getTypedWord());
- ic.setComposingText(textWithUnderline, 1);
+ // TODO: when called from an updateSuggestionStrip() call that results from a posted
+ // message, this is called outside any batch edit. Potentially, this may result in some
+ // janky flickering of the screen, although the display speed makes it unlikely in
+ // the practice.
+ mConnection.setComposingText(textWithUnderline, 1);
}
}
- public void updateSuggestions() {
+ private void updateSuggestionStrip() {
+ mHandler.cancelUpdateSuggestionStrip();
+
// Check if we have a suggestion engine attached.
- if ((mSuggest == null || !isSuggestionsRequested())) {
+ if (mSuggest == null || !mCurrentSettings.isSuggestionsRequested(mDisplayOrientation)) {
if (mWordComposer.isComposingWord()) {
- Log.w(TAG, "Called updateSuggestions but suggestions were not requested!");
+ Log.w(TAG, "Called updateSuggestionsOrPredictions but suggestions were not "
+ + "requested!");
mWordComposer.setAutoCorrection(mWordComposer.getTypedWord());
}
return;
}
- mHandler.cancelUpdateSuggestions();
- mHandler.cancelUpdateBigramPredictions();
-
- if (!mWordComposer.isComposingWord()) {
+ if (!mWordComposer.isComposingWord() && !mCurrentSettings.mBigramPredictionEnabled) {
setPunctuationSuggestions();
return;
}
- // TODO: May need a better way of retrieving previous word
- final InputConnection ic = getCurrentInputConnection();
- final CharSequence prevWord;
- if (null == ic) {
- prevWord = null;
- } else {
- prevWord = EditingUtils.getPreviousWord(ic, mSettingsValues.mWordSeparators);
- }
+ final SuggestedWords suggestedWords = getSuggestedWords(Suggest.SESSION_TYPING);
+ final String typedWord = mWordComposer.getTypedWord();
+ showSuggestionStrip(suggestedWords, typedWord);
+ }
- final CharSequence typedWord = mWordComposer.getTypedWord();
- // getSuggestedWords handles gracefully a null value of prevWord
+ private SuggestedWords getSuggestedWords(final int sessionId) {
+ final Keyboard keyboard = mKeyboardSwitcher.getKeyboard();
+ if (keyboard == null) {
+ return SuggestedWords.EMPTY;
+ }
+ final String typedWord = mWordComposer.getTypedWord();
+ // Get the word on which we should search the bigrams. If we are composing a word, it's
+ // whatever is *before* the half-committed word in the buffer, hence 2; if we aren't, we
+ // should just skip whitespace if any, so 1.
+ // TODO: this is slow (2-way IPC) - we should probably cache this instead.
+ final CharSequence prevWord =
+ mConnection.getNthPreviousWord(mCurrentSettings.mWordSeparators,
+ mWordComposer.isComposingWord() ? 2 : 1);
final SuggestedWords suggestedWords = mSuggest.getSuggestedWords(mWordComposer,
- prevWord, mKeyboardSwitcher.getKeyboard().getProximityInfo(), mCorrectionMode);
-
- // Basically, we update the suggestion strip only when suggestion count > 1. However,
- // there is an exception: We update the suggestion strip whenever typed word's length
- // is 1 or typed word is found in dictionary, regardless of suggestion count. Actually,
- // in most cases, suggestion count is 1 when typed word's length is 1, but we do always
- // need to clear the previous state when the user starts typing a word (i.e. typed word's
- // length == 1).
- if (suggestedWords.size() > 1 || typedWord.length() == 1
- || !suggestedWords.mAllowsToBeAutoCorrected
- || mSuggestionsView.isShowingAddToDictionaryHint()) {
- showSuggestions(suggestedWords, typedWord);
+ prevWord, keyboard.getProximityInfo(), mCurrentSettings.mCorrectionEnabled,
+ sessionId);
+ return maybeRetrieveOlderSuggestions(typedWord, suggestedWords);
+ }
+
+ private SuggestedWords maybeRetrieveOlderSuggestions(final CharSequence typedWord,
+ final SuggestedWords suggestedWords) {
+ // TODO: consolidate this into getSuggestedWords
+ // We update the suggestion strip only when we have some suggestions to show, i.e. when
+ // the suggestion count is > 1; else, we leave the old suggestions, with the typed word
+ // replaced with the new one. However, when the word is a dictionary word, or when the
+ // length of the typed word is 1 or 0 (after a deletion typically), we do want to remove the
+ // old suggestions. Also, if we are showing the "add to dictionary" hint, we need to
+ // revert to suggestions - although it is unclear how we can come here if it's displayed.
+ if (suggestedWords.size() > 1 || typedWord.length() <= 1
+ || !suggestedWords.mTypedWordValid
+ || mSuggestionStripView.isShowingAddToDictionaryHint()) {
+ return suggestedWords;
} else {
- SuggestedWords previousSuggestions = mSuggestionsView.getSuggestions();
- if (previousSuggestions == mSettingsValues.mSuggestPuncList) {
+ SuggestedWords previousSuggestions = mSuggestionStripView.getSuggestions();
+ if (previousSuggestions == mCurrentSettings.mSuggestPuncList) {
previousSuggestions = SuggestedWords.EMPTY;
}
final ArrayList<SuggestedWords.SuggestedWordInfo> typedWordAndPreviousSuggestions =
SuggestedWords.getTypedWordAndPreviousSuggestions(
typedWord, previousSuggestions);
- final SuggestedWords obsoleteSuggestedWords =
- new SuggestedWords(typedWordAndPreviousSuggestions,
+ return new SuggestedWords(typedWordAndPreviousSuggestions,
false /* typedWordValid */,
false /* hasAutoCorrectionCandidate */,
- false /* allowsToBeAutoCorrected */,
false /* isPunctuationSuggestions */,
true /* isObsoleteSuggestions */,
false /* isPrediction */);
- showSuggestions(obsoleteSuggestedWords, typedWord);
}
}
- public void showSuggestions(final SuggestedWords suggestedWords, final CharSequence typedWord) {
+ private void showSuggestionStrip(final SuggestedWords suggestedWords,
+ final CharSequence typedWord) {
+ if (null == suggestedWords || suggestedWords.size() <= 0) {
+ clearSuggestionStrip();
+ return;
+ }
final CharSequence autoCorrection;
if (suggestedWords.size() > 0) {
- if (suggestedWords.hasAutoCorrectionWord()) {
+ if (suggestedWords.mWillAutoCorrect) {
autoCorrection = suggestedWords.getWord(1);
} else {
autoCorrection = typedWord;
@@ -1851,94 +1999,89 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
mWordComposer.setAutoCorrection(autoCorrection);
final boolean isAutoCorrection = suggestedWords.willAutoCorrect();
- setSuggestions(suggestedWords, isAutoCorrection);
+ setSuggestionStrip(suggestedWords, isAutoCorrection);
setAutoCorrectionIndicator(isAutoCorrection);
setSuggestionStripShown(isSuggestionsStripVisible());
}
- private void commitCurrentAutoCorrection(final int separatorCodePoint,
- final InputConnection ic) {
+ private void commitCurrentAutoCorrection(final String separatorString) {
// Complete any pending suggestions query first
if (mHandler.hasPendingUpdateSuggestions()) {
- mHandler.cancelUpdateSuggestions();
- updateSuggestions();
+ updateSuggestionStrip();
}
- final CharSequence autoCorrection = mWordComposer.getAutoCorrectionOrNull();
+ final CharSequence typedAutoCorrection = mWordComposer.getAutoCorrectionOrNull();
+ final String typedWord = mWordComposer.getTypedWord();
+ final CharSequence autoCorrection = (typedAutoCorrection != null)
+ ? typedAutoCorrection : typedWord;
if (autoCorrection != null) {
- final String typedWord = mWordComposer.getTypedWord();
if (TextUtils.isEmpty(typedWord)) {
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(), separatorCodePoint);
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_commitCurrentAutoCorrection(typedWord,
- autoCorrection.toString());
+ if (ProductionFlag.IS_INTERNAL) {
+ Stats.onAutoCorrection(
+ typedWord, autoCorrection.toString(), separatorString, mWordComposer);
}
mExpectingUpdateSelection = true;
commitChosenWord(autoCorrection, LastComposedWord.COMMIT_TYPE_DECIDED_WORD,
- separatorCodePoint);
- if (!typedWord.equals(autoCorrection) && null != ic) {
+ separatorString);
+ if (!typedWord.equals(autoCorrection)) {
// This will make the correction flash for a short while as a visual clue
- // to the user that auto-correction happened.
- ic.commitCorrection(new CorrectionInfo(mLastSelectionEnd - typedWord.length(),
+ // to the user that auto-correction happened. It has no other effect; in particular
+ // note that this won't affect the text inside the text field AT ALL: it only makes
+ // the segment of text starting at the supplied index and running for the length
+ // of the auto-correction flash. At this moment, the "typedWord" argument is
+ // ignored by TextView.
+ mConnection.commitCorrection(
+ new CorrectionInfo(mLastSelectionEnd - typedWord.length(),
typedWord, autoCorrection));
}
}
}
+ // Called from {@link SuggestionStripView} through the {@link SuggestionStripView#Listener}
+ // interface
@Override
- public void pickSuggestionManually(final int index, final CharSequence suggestion,
- int x, int y) {
- final InputConnection ic = getCurrentInputConnection();
- if (null != ic) ic.beginBatchEdit();
- pickSuggestionManuallyWhileInBatchEdit(index, suggestion, x, y, ic);
- if (null != ic) ic.endBatchEdit();
- }
-
- public void pickSuggestionManuallyWhileInBatchEdit(final int index,
- final CharSequence suggestion, final int x, final int y, final InputConnection ic) {
- final SuggestedWords suggestedWords = mSuggestionsView.getSuggestions();
+ public void pickSuggestionManually(final int index, final CharSequence suggestion) {
+ final SuggestedWords suggestedWords = mSuggestionStripView.getSuggestions();
// If this is a punctuation picked from the suggestion strip, pass it to onCodeInput
if (suggestion.length() == 1 && isShowingPunctuationList()) {
// Word separators are suggested before the user inputs something.
// So, LatinImeLogger logs "" as a user's input.
LatinImeLogger.logOnManualSuggestion("", suggestion.toString(), index, suggestedWords);
// Rely on onCodeInput to do the complicated swapping/stripping logic consistently.
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_punctuationSuggestion(index, suggestion, x, y);
- }
final int primaryCode = suggestion.charAt(0);
onCodeInput(primaryCode,
- KeyboardActionListener.SUGGESTION_STRIP_COORDINATE,
- KeyboardActionListener.SUGGESTION_STRIP_COORDINATE);
+ Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE);
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.latinIME_punctuationSuggestion(index, suggestion);
+ }
return;
}
- if (SPACE_STATE_PHANTOM == mSpaceState && suggestion.length() > 0) {
+ mConnection.beginBatchEdit();
+ if (SPACE_STATE_PHANTOM == mSpaceState && suggestion.length() > 0
+ // In the batch input mode, a manually picked suggested word should just replace
+ // the current batch input text and there is no need for a phantom space.
+ && !mWordComposer.isBatchMode()) {
int firstChar = Character.codePointAt(suggestion, 0);
- if ((!mSettingsValues.isWeakSpaceStripper(firstChar))
- && (!mSettingsValues.isWeakSpaceSwapper(firstChar))) {
+ if ((!mCurrentSettings.isWeakSpaceStripper(firstChar))
+ && (!mCurrentSettings.isWeakSpaceSwapper(firstChar))) {
sendKeyCodePoint(Keyboard.CODE_SPACE);
}
}
- if (mInputAttributes.mApplicationSpecifiedCompletionOn
+ if (mCurrentSettings.isApplicationSpecifiedCompletionsOn()
&& mApplicationSpecifiedCompletions != null
&& index >= 0 && index < mApplicationSpecifiedCompletions.length) {
- if (mSuggestionsView != null) {
- mSuggestionsView.clear();
+ if (mSuggestionStripView != null) {
+ mSuggestionStripView.clear();
}
mKeyboardSwitcher.updateShiftState();
resetComposingState(true /* alsoResetLastComposedWord */);
- if (ic != null) {
- final CompletionInfo completionInfo = mApplicationSpecifiedCompletions[index];
- ic.commitCompletion(completionInfo);
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_pickApplicationSpecifiedCompletion(index,
- completionInfo.getText(), x, y);
- }
- }
+ final CompletionInfo completionInfo = mApplicationSpecifiedCompletions[index];
+ mConnection.commitCompletion(completionInfo);
+ mConnection.endBatchEdit();
return;
}
@@ -1947,50 +2090,37 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
final String replacedWord = mWordComposer.getTypedWord().toString();
LatinImeLogger.logOnManualSuggestion(replacedWord,
suggestion.toString(), index, suggestedWords);
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_pickSuggestionManually(replacedWord, index, suggestion, x, y);
- }
mExpectingUpdateSelection = true;
commitChosenWord(suggestion, LastComposedWord.COMMIT_TYPE_MANUAL_PICK,
LastComposedWord.NOT_A_SEPARATOR);
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.latinIME_pickSuggestionManually(replacedWord, index, suggestion);
+ }
+ 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
- // AND either:
- // - There is no dictionary (we know that because we tried to load it => null != mSuggest
- // AND mSuggest.hasMainDictionary() is false)
- // - There is a dictionary and the word is not in it
+ // AND it's in none of our current dictionaries (main, user or otherwise).
// Please note that if mSuggest is null, it means that everything is off: suggestion
// and correction, so we shouldn't try to show the hint
- // We used to look at mCorrectionMode here, but showing the hint should have nothing
- // to do with the autocorrection setting.
final boolean showingAddToDictionaryHint = index == 0 && mSuggest != null
- // If there is no dictionary the hint should be shown.
- && (!mSuggest.hasMainDictionary()
- // If "suggestion" is not in the dictionary, the hint should be shown.
- || !AutoCorrection.isValidWord(
- mSuggest.getUnigramDictionaries(), suggestion, true));
-
- Utils.Stats.onSeparator((char)Keyboard.CODE_SPACE, WordComposer.NOT_A_COORDINATE,
- WordComposer.NOT_A_COORDINATE);
- if (!showingAddToDictionaryHint) {
- // If we're not showing the "Touch again to save", then show corrections again.
- // In case the cursor position doesn't change, make sure we show the suggestions again.
- updateBigramPredictions();
- // Updating the predictions right away may be slow and feel unresponsive on slower
- // terminals. On the other hand if we just postUpdateBigramPredictions() it will
- // take a noticeable delay to update them which may feel uneasy.
+ // If the suggestion is not in the dictionary, the hint should be shown.
+ && !AutoCorrection.isValidWord(mSuggest.getUnigramDictionaries(), suggestion, true);
+
+ 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);
} else {
- if (mIsUserDictionaryAvailable) {
- mSuggestionsView.showAddToDictionaryHint(
- suggestion, mSettingsValues.mHintToSaveText);
- } else {
- mHandler.postUpdateSuggestions();
- }
+ // If we're not showing the "Touch again to save", then update the suggestion strip.
+ mHandler.postUpdateSuggestionStrip();
}
}
@@ -1998,24 +2128,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
* Commits the chosen word to the text field and saves it for later retrieval.
*/
private void commitChosenWord(final CharSequence chosenWord, final int commitType,
- final int separatorCode) {
- final InputConnection ic = getCurrentInputConnection();
- if (ic != null) {
- if (mSettingsValues.mEnableSuggestionSpanInsertion) {
- final SuggestedWords suggestedWords = mSuggestionsView.getSuggestions();
- ic.commitText(SuggestionSpanUtils.getTextWithSuggestionSpan(
- this, chosenWord, suggestedWords, mIsMainDictionaryAvailable),
- 1);
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_commitText(chosenWord);
- }
- } else {
- ic.commitText(chosenWord, 1);
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_commitText(chosenWord);
- }
- }
- }
+ final String separatorString) {
+ final SuggestedWords suggestedWords = mSuggestionStripView.getSuggestions();
+ mConnection.commitText(SuggestionSpanUtils.getTextWithSuggestionSpan(
+ this, chosenWord, suggestedWords, mIsMainDictionaryAvailable), 1);
// Add the word to the user history dictionary
final CharSequence prevWord = addToUserHistoryDictionary(chosenWord);
// TODO: figure out here if this is an auto-correct or if the best word is actually
@@ -2023,45 +2139,14 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// LastComposedWord#didCommitTypedWord by string equality of the remembered
// strings.
mLastComposedWord = mWordComposer.commitWord(commitType, chosenWord.toString(),
- separatorCode, prevWord);
- }
-
- public void updateBigramPredictions() {
- if (mSuggest == null || !isSuggestionsRequested())
- return;
-
- if (!mSettingsValues.mBigramPredictionEnabled) {
- setPunctuationSuggestions();
- return;
- }
-
- final SuggestedWords suggestedWords;
- if (mCorrectionMode == Suggest.CORRECTION_FULL_BIGRAM) {
- final CharSequence prevWord = EditingUtils.getThisWord(getCurrentInputConnection(),
- mSettingsValues.mWordSeparators);
- if (!TextUtils.isEmpty(prevWord)) {
- suggestedWords = mSuggest.getBigramPredictions(prevWord);
- } else {
- suggestedWords = null;
- }
- } else {
- suggestedWords = null;
- }
-
- if (null != suggestedWords && suggestedWords.size() > 0) {
- // Explicitly supply an empty typed word (the no-second-arg version of
- // showSuggestions will retrieve the word near the cursor, we don't want that here)
- showSuggestions(suggestedWords, "");
- } else {
- clearSuggestions();
- }
+ separatorString, prevWord);
}
- public void setPunctuationSuggestions() {
- if (mSettingsValues.mBigramPredictionEnabled) {
- clearSuggestions();
+ private void setPunctuationSuggestions() {
+ if (mCurrentSettings.mBigramPredictionEnabled) {
+ clearSuggestionStrip();
} else {
- setSuggestions(mSettingsValues.mSuggestPuncList, false);
+ setSuggestionStrip(mCurrentSettings.mSuggestPuncList, false);
}
setAutoCorrectionIndicator(false);
setSuggestionStripShown(isSuggestionsStripVisible());
@@ -2069,25 +2154,19 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private CharSequence addToUserHistoryDictionary(final CharSequence suggestion) {
if (TextUtils.isEmpty(suggestion)) return null;
+ if (mSuggest == null) return null;
- // Only auto-add to dictionary if auto-correct is ON. Otherwise we'll be
- // adding words in situations where the user or application really didn't
- // want corrections enabled or learned.
- if (!(mCorrectionMode == Suggest.CORRECTION_FULL
- || mCorrectionMode == Suggest.CORRECTION_FULL_BIGRAM)) {
- return null;
- }
+ // If correction is not enabled, we don't add words to the user history dictionary.
+ // That's to avoid unintended additions in some sensitive fields, or fields that
+ // expect to receive non-words.
+ if (!mCurrentSettings.mCorrectionEnabled) return null;
- if (mUserHistoryDictionary != null) {
- final InputConnection ic = getCurrentInputConnection();
- final CharSequence prevWord;
- if (null != ic) {
- prevWord = EditingUtils.getPreviousWord(ic, mSettingsValues.mWordSeparators);
- } else {
- prevWord = null;
- }
+ final UserHistoryDictionary userHistoryDictionary = mUserHistoryDictionary;
+ if (userHistoryDictionary != null) {
+ final CharSequence prevWord
+ = mConnection.getNthPreviousWord(mCurrentSettings.mWordSeparators, 2);
final String secondWord;
- if (mWordComposer.isAutoCapitalized() && !mWordComposer.isMostlyCaps()) {
+ if (mWordComposer.wasAutoCapitalized() && !mWordComposer.isMostlyCaps()) {
secondWord = suggestion.toString().toLowerCase(
mSubtypeSwitcher.getCurrentSubtypeLocale());
} else {
@@ -2098,101 +2177,39 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
final int maxFreq = AutoCorrection.getMaxFrequency(
mSuggest.getUnigramDictionaries(), suggestion);
if (maxFreq == 0) return null;
- mUserHistoryDictionary.addToUserHistory(null == prevWord ? null : prevWord.toString(),
+ userHistoryDictionary.addToUserHistory(null == prevWord ? null : prevWord.toString(),
secondWord, maxFreq > 0);
return prevWord;
}
return null;
}
- public boolean isCursorTouchingWord() {
- final InputConnection ic = getCurrentInputConnection();
- if (ic == null) return false;
- CharSequence before = ic.getTextBeforeCursor(1, 0);
- CharSequence after = ic.getTextAfterCursor(1, 0);
- if (!TextUtils.isEmpty(before) && !mSettingsValues.isWordSeparator(before.charAt(0))
- && !mSettingsValues.isSymbolExcludedFromWordSeparators(before.charAt(0))) {
- return true;
- }
- if (!TextUtils.isEmpty(after) && !mSettingsValues.isWordSeparator(after.charAt(0))
- && !mSettingsValues.isSymbolExcludedFromWordSeparators(after.charAt(0))) {
- return true;
- }
- return false;
- }
-
- // "ic" must not be null
- private static boolean sameAsTextBeforeCursor(final InputConnection ic,
- final CharSequence text) {
- final CharSequence beforeText = ic.getTextBeforeCursor(text.length(), 0);
- return TextUtils.equals(text, beforeText);
- }
-
- // "ic" must not be null
/**
* Check if the cursor is actually at the end of a word. If so, restart suggestions on this
* word, else do nothing.
*/
- private void restartSuggestionsOnWordBeforeCursorIfAtEndOfWord(
- final InputConnection ic) {
- // Bail out if the cursor is not at the end of a word (cursor must be preceded by
- // non-whitespace, non-separator, non-start-of-text)
- // Example ("|" is the cursor here) : <SOL>"|a" " |a" " | " all get rejected here.
- final CharSequence textBeforeCursor = ic.getTextBeforeCursor(1, 0);
- if (TextUtils.isEmpty(textBeforeCursor)
- || mSettingsValues.isWordSeparator(textBeforeCursor.charAt(0))) return;
-
- // Bail out if the cursor is in the middle of a word (cursor must be followed by whitespace,
- // separator or end of line/text)
- // Example: "test|"<EOL> "te|st" get rejected here
- final CharSequence textAfterCursor = ic.getTextAfterCursor(1, 0);
- if (!TextUtils.isEmpty(textAfterCursor)
- && !mSettingsValues.isWordSeparator(textAfterCursor.charAt(0))) return;
-
- // Bail out if word before cursor is 0-length or a single non letter (like an apostrophe)
- // Example: " -|" gets rejected here but "e-|" and "e|" are okay
- CharSequence word = EditingUtils.getWordAtCursor(ic, mSettingsValues.mWordSeparators);
- // We don't suggest on leading single quotes, so we have to remove them from the word if
- // it starts with single quotes.
- while (!TextUtils.isEmpty(word) && Keyboard.CODE_SINGLE_QUOTE == word.charAt(0)) {
- word = word.subSequence(1, word.length());
- }
- if (TextUtils.isEmpty(word)) return;
- final char firstChar = word.charAt(0); // we just tested that word is not empty
- if (word.length() == 1 && !Character.isLetter(firstChar)) return;
-
- // We only suggest on words that start with a letter or a symbol that is excluded from
- // word separators (see #handleCharacterWhileInBatchEdit).
- if (!(isAlphabet(firstChar)
- || mSettingsValues.isSymbolExcludedFromWordSeparators(firstChar))) {
- return;
+ private void restartSuggestionsOnWordBeforeCursorIfAtEndOfWord() {
+ final CharSequence word = mConnection.getWordBeforeCursorIfAtEndOfWord(mCurrentSettings);
+ if (null != word) {
+ restartSuggestionsOnWordBeforeCursor(word);
}
-
- // Okay, we are at the end of a word. Restart suggestions.
- restartSuggestionsOnWordBeforeCursor(ic, word);
}
- // "ic" must not be null
- private void restartSuggestionsOnWordBeforeCursor(final InputConnection ic,
- final CharSequence word) {
+ private void restartSuggestionsOnWordBeforeCursor(final CharSequence word) {
mWordComposer.setComposingWord(word, mKeyboardSwitcher.getKeyboard());
final int length = word.length();
- ic.deleteSurroundingText(length, 0);
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_deleteSurroundingText(length);
- }
- ic.setComposingText(word, 1);
- mHandler.postUpdateSuggestions();
+ mConnection.deleteSurroundingText(length, 0);
+ mConnection.setComposingText(word, 1);
+ mHandler.postUpdateSuggestionStrip();
}
- // "ic" must not be null
- private void revertCommit(final InputConnection ic) {
+ private void revertCommit() {
final CharSequence previousWord = mLastComposedWord.mPrevWord;
final String originallyTypedWord = mLastComposedWord.mTypedWord;
final CharSequence committedWord = mLastComposedWord.mCommittedWord;
final int cancelLength = committedWord.length();
final int separatorLength = LastComposedWord.getSeparatorLength(
- mLastComposedWord.mSeparatorCode);
+ mLastComposedWord.mSeparatorString);
// TODO: should we check our saved separator against the actual contents of the text view?
final int deleteLength = cancelLength + separatorLength;
if (DEBUG) {
@@ -2200,7 +2217,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
throw new RuntimeException("revertCommit, but we are composing a word");
}
final String wordBeforeCursor =
- ic.getTextBeforeCursor(deleteLength, 0)
+ mConnection.getTextBeforeCursor(deleteLength, 0)
.subSequence(0, cancelLength).toString();
if (!TextUtils.equals(committedWord, wordBeforeCursor)) {
throw new RuntimeException("revertCommit check failed: we thought we were "
@@ -2208,132 +2225,67 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
+ "\", but before the cursor we found \"" + wordBeforeCursor + "\"");
}
}
- ic.deleteSurroundingText(deleteLength, 0);
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_deleteSurroundingText(deleteLength);
- }
+ mConnection.deleteSurroundingText(deleteLength, 0);
if (!TextUtils.isEmpty(previousWord) && !TextUtils.isEmpty(committedWord)) {
mUserHistoryDictionary.cancelAddingUserHistory(
previousWord.toString(), committedWord.toString());
}
- if (0 == separatorLength || mLastComposedWord.didCommitTypedWord()) {
- // This is the case when we cancel a manual pick.
- // We should restart suggestion on the word right away.
- mWordComposer.resumeSuggestionOnLastComposedWord(mLastComposedWord);
- ic.setComposingText(originallyTypedWord, 1);
- } else {
- ic.commitText(originallyTypedWord, 1);
- // Re-insert the separator
- sendKeyCodePoint(mLastComposedWord.mSeparatorCode);
- Utils.Stats.onSeparator(mLastComposedWord.mSeparatorCode, WordComposer.NOT_A_COORDINATE,
- WordComposer.NOT_A_COORDINATE);
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_revertCommit(originallyTypedWord);
- }
- // Don't restart suggestion yet. We'll restart if the user deletes the
- // separator.
+ mConnection.commitText(originallyTypedWord + mLastComposedWord.mSeparatorString, 1);
+ if (ProductionFlag.IS_INTERNAL) {
+ Stats.onSeparator(mLastComposedWord.mSeparatorString,
+ Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
}
- mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
- mHandler.cancelUpdateBigramPredictions();
- mHandler.postUpdateSuggestions();
- }
-
- // "ic" must not be null
- private boolean revertDoubleSpaceWhileInBatchEdit(final InputConnection ic) {
- mHandler.cancelDoubleSpacesTimer();
- // Here we test whether we indeed have a period and a space before us. This should not
- // be needed, but it's there just in case something went wrong.
- final CharSequence textBeforeCursor = ic.getTextBeforeCursor(2, 0);
- if (!". ".equals(textBeforeCursor)) {
- // Theoretically we should not be coming here if there isn't ". " before the
- // cursor, but the application may be changing the text while we are typing, so
- // anything goes. We should not crash.
- Log.d(TAG, "Tried to revert double-space combo but we didn't find "
- + "\". \" just before the cursor.");
- return false;
- }
- ic.deleteSurroundingText(2, 0);
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_deleteSurroundingText(2);
- }
- ic.commitText(" ", 1);
if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_revertDoubleSpaceWhileInBatchEdit();
- }
- return true;
- }
-
- private static boolean revertSwapPunctuation(final InputConnection ic) {
- // Here we test whether we indeed have a space and something else before us. This should not
- // be needed, but it's there just in case something went wrong.
- final CharSequence textBeforeCursor = ic.getTextBeforeCursor(2, 0);
- // NOTE: This does not work with surrogate pairs. Hopefully when the keyboard is able to
- // enter surrogate pairs this code will have been removed.
- if (TextUtils.isEmpty(textBeforeCursor)
- || (Keyboard.CODE_SPACE != textBeforeCursor.charAt(1))) {
- // We may only come here if the application is changing the text while we are typing.
- // This is quite a broken case, but not logically impossible, so we shouldn't crash,
- // but some debugging log may be in order.
- Log.d(TAG, "Tried to revert a swap of punctuation but we didn't "
- + "find a space just before the cursor.");
- return false;
+ ResearchLogger.latinIME_revertCommit(originallyTypedWord);
}
- ic.beginBatchEdit();
- ic.deleteSurroundingText(2, 0);
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_deleteSurroundingText(2);
- }
- ic.commitText(" " + textBeforeCursor.subSequence(0, 1), 1);
- if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.latinIME_revertSwapPunctuation();
- }
- ic.endBatchEdit();
- return true;
- }
-
- public boolean isWordSeparator(int code) {
- return mSettingsValues.isWordSeparator(code);
+ // Don't restart suggestion yet. We'll restart if the user deletes the
+ // separator.
+ mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
+ // We have a separator between the word and the cursor: we should show predictions.
+ mHandler.postUpdateSuggestionStrip();
}
- public boolean preferCapitalization() {
- return mWordComposer.isFirstCharCapitalized();
+ // Used by the RingCharBuffer
+ public boolean isWordSeparator(final int code) {
+ return mCurrentSettings.isWordSeparator(code);
}
- // Notify that language or mode have been changed and toggleLanguage will update KeyboardID
- // according to new language or mode.
- public void onRefreshKeyboard() {
+ // TODO: Make this private
+ // Outside LatinIME, only used by the {@link InputTestsBase} test suite.
+ /* package for test */
+ void loadKeyboard() {
// When the device locale is changed in SetupWizard etc., this method may get called via
// onConfigurationChanged before SoftInputWindow is shown.
- if (mKeyboardSwitcher.getKeyboardView() != null) {
- // Reload keyboard because the current language has been changed.
- mKeyboardSwitcher.loadKeyboard(getCurrentInputEditorInfo(), mSettingsValues);
- }
initSuggest();
- updateCorrectionMode();
loadSettings();
+ if (mKeyboardSwitcher.getMainKeyboardView() != null) {
+ // Reload keyboard because the current language has been changed.
+ mKeyboardSwitcher.loadKeyboard(getCurrentInputEditorInfo(), mCurrentSettings);
+ }
// Since we just changed languages, we should re-evaluate suggestions with whatever word
// we are currently composing. If we are not composing anything, we may want to display
- // predictions or punctuation signs (which is done by updateBigramPredictions anyway).
- if (isCursorTouchingWord()) {
- mHandler.postUpdateSuggestions();
- } else {
- mHandler.postUpdateBigramPredictions();
- }
+ // predictions or punctuation signs (which is done by the updateSuggestionStrip anyway).
+ mHandler.postUpdateSuggestionStrip();
}
// TODO: Remove this method from {@link LatinIME} and move {@link FeedbackManager} to
- // {@link KeyboardSwitcher}.
+ // {@link KeyboardSwitcher}. Called from KeyboardSwitcher
public void hapticAndAudioFeedback(final int primaryCode) {
- mFeedbackManager.hapticAndAudioFeedback(primaryCode, mKeyboardSwitcher.getKeyboardView());
+ mFeedbackManager.hapticAndAudioFeedback(
+ primaryCode, mKeyboardSwitcher.getMainKeyboardView());
}
+ // Callback called by PointerTracker through the KeyboardActionListener. This is called when a
+ // key is depressed; release matching call is onReleaseKey below.
@Override
- public void onPressKey(int primaryCode) {
+ public void onPressKey(final int primaryCode) {
mKeyboardSwitcher.onPressKey(primaryCode);
}
+ // Callback by PointerTracker through the KeyboardActionListener. This is called when a key
+ // is released; press matching call is onPressKey above.
@Override
- public void onReleaseKey(int primaryCode, boolean withSliding) {
+ public void onReleaseKey(final int primaryCode, final boolean withSliding) {
mKeyboardSwitcher.onReleaseKey(primaryCode, withSliding);
// If accessibility is on, ensure the user receives keyboard state updates.
@@ -2352,12 +2304,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// This is a stopgap solution to avoid leaving a high surrogate alone in a text view.
// In the future, we need to deprecate deteleSurroundingText() and have a surrogate
// pair-friendly way of deleting characters in InputConnection.
- final InputConnection ic = getCurrentInputConnection();
- if (null != ic) {
- final CharSequence lastChar = ic.getTextBeforeCursor(1, 0);
- if (!TextUtils.isEmpty(lastChar) && Character.isHighSurrogate(lastChar.charAt(0))) {
- ic.deleteSurroundingText(1, 0);
- }
+ // TODO: use getCodePointBeforeCursor instead to improve performance
+ final CharSequence lastChar = mConnection.getTextBeforeCursor(1, 0);
+ if (!TextUtils.isEmpty(lastChar) && Character.isHighSurrogate(lastChar.charAt(0))) {
+ mConnection.deleteSurroundingText(1, 0);
}
}
}
@@ -2365,7 +2315,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// receive ringer mode change and network state change.
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
- public void onReceive(Context context, Intent intent) {
+ public void onReceive(final Context context, final Intent intent) {
final String action = intent.getAction();
if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
mSubtypeSwitcher.onNetworkStateChanged(intent);
@@ -2375,37 +2325,27 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
};
- private void updateCorrectionMode() {
- // TODO: cleanup messy flags
- final boolean shouldAutoCorrect = mSettingsValues.mAutoCorrectEnabled
- && !mInputAttributes.mInputTypeNoAutoCorrect;
- mCorrectionMode = shouldAutoCorrect ? Suggest.CORRECTION_FULL : Suggest.CORRECTION_NONE;
- mCorrectionMode = (mSettingsValues.mBigramSuggestionEnabled && shouldAutoCorrect)
- ? Suggest.CORRECTION_FULL_BIGRAM : mCorrectionMode;
- }
-
- private void updateSuggestionVisibility(final Resources res) {
- final String suggestionVisiblityStr = mSettingsValues.mShowSuggestionsSetting;
- for (int visibility : SUGGESTION_VISIBILITY_VALUE_ARRAY) {
- if (suggestionVisiblityStr.equals(res.getString(visibility))) {
- mSuggestionVisibility = visibility;
- break;
- }
- }
- }
-
private void launchSettings() {
- launchSettingsClass(SettingsActivity.class);
+ handleClose();
+ launchSubActivity(SettingsActivity.class);
}
+ // Called from debug code only
public void launchDebugSettings() {
- launchSettingsClass(DebugSettingsActivity.class);
+ handleClose();
+ launchSubActivity(DebugSettingsActivity.class);
}
- private void launchSettingsClass(Class<? extends PreferenceActivity> settingsClass) {
- handleClose();
+ public void launchKeyboardedDialogActivity(final Class<? extends Activity> activityClass) {
+ // Put the text in the attached EditText into a safe, saved state before switching to a
+ // new activity that will also use the soft keyboard.
+ commitTyped(LastComposedWord.NOT_A_SEPARATOR);
+ launchSubActivity(activityClass);
+ }
+
+ private void launchSubActivity(final Class<? extends Activity> activityClass) {
Intent intent = new Intent();
- intent.setClass(LatinIME.this, settingsClass);
+ intent.setClass(LatinIME.this, activityClass);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
@@ -2440,12 +2380,14 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
final AlertDialog.Builder builder = new AlertDialog.Builder(this)
.setItems(items, listener)
.setTitle(title);
- showOptionDialogInternal(builder.create());
+ showOptionDialog(builder.create());
}
- private void showOptionDialogInternal(AlertDialog dialog) {
- final IBinder windowToken = mKeyboardSwitcher.getKeyboardView().getWindowToken();
- if (windowToken == null) return;
+ public void showOptionDialog(final AlertDialog dialog) {
+ final IBinder windowToken = mKeyboardSwitcher.getMainKeyboardView().getWindowToken();
+ if (windowToken == null) {
+ return;
+ }
dialog.setCancelable(true);
dialog.setCanceledOnTouchOutside(true);
@@ -2461,8 +2403,19 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
dialog.show();
}
+ public void debugDumpStateAndCrashWithException(final String context) {
+ final StringBuilder s = new StringBuilder();
+ s.append("Target application : ").append(mTargetApplicationInfo.name)
+ .append("\nPackage : ").append(mTargetApplicationInfo.packageName)
+ .append("\nTarget app sdk version : ")
+ .append(mTargetApplicationInfo.targetSdkVersion)
+ .append("\nAttributes : ").append(mCurrentSettings.getInputAttributesDebugString())
+ .append("\nContext : ").append(context);
+ throw new RuntimeException(s.toString());
+ }
+
@Override
- protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+ protected void dump(final FileDescriptor fd, final PrintWriter fout, final String[] args) {
super.dump(fd, fout, args);
final Printer p = new PrintWriterPrinter(fout);
@@ -2470,13 +2423,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
final Keyboard keyboard = mKeyboardSwitcher.getKeyboard();
final int keyboardMode = keyboard != null ? keyboard.mId.mMode : -1;
p.println(" Keyboard mode = " + keyboardMode);
- p.println(" mIsSuggestionsRequested=" + mInputAttributes.mIsSettingsSuggestionStripOn);
- p.println(" mCorrectionMode=" + mCorrectionMode);
+ p.println(" mIsSuggestionsSuggestionsRequested = "
+ + mCurrentSettings.isSuggestionsRequested(mDisplayOrientation));
+ p.println(" mCorrectionEnabled=" + mCurrentSettings.mCorrectionEnabled);
p.println(" isComposingWord=" + mWordComposer.isComposingWord());
- p.println(" mAutoCorrectEnabled=" + mSettingsValues.mAutoCorrectEnabled);
- p.println(" mSoundOn=" + mSettingsValues.mSoundOn);
- p.println(" mVibrateOn=" + mSettingsValues.mVibrateOn);
- p.println(" mKeyPreviewPopupOn=" + mSettingsValues.mKeyPreviewPopupOn);
- p.println(" mInputAttributes=" + mInputAttributes.toString());
+ p.println(" mSoundOn=" + mCurrentSettings.mSoundOn);
+ p.println(" mVibrateOn=" + mCurrentSettings.mVibrateOn);
+ p.println(" mKeyPreviewPopupOn=" + mCurrentSettings.mKeyPreviewPopupOn);
+ p.println(" inputAttributes=" + mCurrentSettings.getInputAttributesDebugString());
}
}
diff --git a/java/src/com/android/inputmethod/latin/LatinImeLogger.java b/java/src/com/android/inputmethod/latin/LatinImeLogger.java
index dc0868e7c..394a9c7aa 100644
--- a/java/src/com/android/inputmethod/latin/LatinImeLogger.java
+++ b/java/src/com/android/inputmethod/latin/LatinImeLogger.java
@@ -21,7 +21,7 @@ import android.view.inputmethod.EditorInfo;
import com.android.inputmethod.keyboard.Keyboard;
-public class LatinImeLogger implements SharedPreferences.OnSharedPreferenceChangeListener {
+public final class LatinImeLogger implements SharedPreferences.OnSharedPreferenceChangeListener {
public static boolean sDBG = false;
public static boolean sVISUALDEBUG = false;
@@ -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() {
@@ -71,7 +76,7 @@ public class LatinImeLogger implements SharedPreferences.OnSharedPreferenceChang
public static void onStartSuggestion(CharSequence previousWords) {
}
- public static void onAddSuggestedWord(String word, int typeId, int dataType) {
+ public static void onAddSuggestedWord(String word, String sourceDictionaryId) {
}
public static void onSetKeyboard(Keyboard kb) {
diff --git a/java/src/com/android/inputmethod/latin/LocaleUtils.java b/java/src/com/android/inputmethod/latin/LocaleUtils.java
index b938dd336..feb1b2d0e 100644
--- a/java/src/com/android/inputmethod/latin/LocaleUtils.java
+++ b/java/src/com/android/inputmethod/latin/LocaleUtils.java
@@ -31,7 +31,10 @@ import java.util.Locale;
* update/bugfix to this file, consider also updating/fixing the version in the
* dictionary pack.
*/
-public class LocaleUtils {
+public final class LocaleUtils {
+ private static final HashMap<String, Long> EMPTY_LT_HASH_MAP = CollectionUtils.newHashMap();
+ private static final String LOCALE_AND_TIME_STR_SEPARATER = ",";
+
private LocaleUtils() {
// Intentional empty constructor for utility class.
}
@@ -193,7 +196,7 @@ public class LocaleUtils {
}
}
- private static final HashMap<String, Locale> sLocaleCache = new HashMap<String, Locale>();
+ private static final HashMap<String, Locale> sLocaleCache = CollectionUtils.newHashMap();
/**
* Creates a locale from a string specification.
@@ -219,4 +222,38 @@ public class LocaleUtils {
return retval;
}
}
+
+ public static HashMap<String, Long> localeAndTimeStrToHashMap(String str) {
+ if (TextUtils.isEmpty(str)) {
+ return EMPTY_LT_HASH_MAP;
+ }
+ final String[] ss = str.split(LOCALE_AND_TIME_STR_SEPARATER);
+ final int N = ss.length;
+ if (N < 2 || N % 2 != 0) {
+ return EMPTY_LT_HASH_MAP;
+ }
+ final HashMap<String, Long> retval = CollectionUtils.newHashMap();
+ for (int i = 0; i < N / 2; ++i) {
+ final String localeStr = ss[i * 2];
+ final long time = Long.valueOf(ss[i * 2 + 1]);
+ retval.put(localeStr, time);
+ }
+ return retval;
+ }
+
+ public static String localeAndTimeHashMapToStr(HashMap<String, Long> map) {
+ if (map == null || map.isEmpty()) {
+ return "";
+ }
+ final StringBuilder builder = new StringBuilder();
+ for (String localeStr : map.keySet()) {
+ if (builder.length() > 0) {
+ builder.append(LOCALE_AND_TIME_STR_SEPARATER);
+ }
+ final Long time = map.get(localeStr);
+ builder.append(localeStr).append(LOCALE_AND_TIME_STR_SEPARATER);
+ builder.append(String.valueOf(time));
+ }
+ return builder.toString();
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/ResearchLogger.java b/java/src/com/android/inputmethod/latin/ResearchLogger.java
deleted file mode 100644
index 66d6d58b1..000000000
--- a/java/src/com/android/inputmethod/latin/ResearchLogger.java
+++ /dev/null
@@ -1,757 +0,0 @@
-/*
- * 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;
-
-import android.content.SharedPreferences;
-import android.inputmethodservice.InputMethodService;
-import android.os.Build;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Process;
-import android.os.SystemClock;
-import android.preference.PreferenceManager;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.MotionEvent;
-import android.view.inputmethod.CompletionInfo;
-import android.view.inputmethod.EditorInfo;
-
-import com.android.inputmethod.keyboard.Key;
-import com.android.inputmethod.keyboard.KeyDetector;
-import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.keyboard.internal.KeyboardState;
-import com.android.inputmethod.latin.define.ProductionFlag;
-
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.nio.ByteBuffer;
-import java.nio.CharBuffer;
-import java.nio.channels.FileChannel;
-import java.nio.charset.Charset;
-import java.util.Map;
-
-/**
- * Logs the use of the LatinIME keyboard.
- *
- * This class logs operations on the IME keyboard, including what the user has typed.
- * Data is stored locally in a file in app-specific storage.
- *
- * This functionality is off by default. See {@link ProductionFlag#IS_EXPERIMENTAL}.
- */
-public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChangeListener {
- private static final String TAG = ResearchLogger.class.getSimpleName();
- private static final String PREF_USABILITY_STUDY_MODE = "usability_study_mode";
- private static final boolean DEBUG = false;
-
- private static final ResearchLogger sInstance = new ResearchLogger(new LogFileManager());
- public static boolean sIsLogging = false;
- /* package */ final Handler mLoggingHandler;
- private InputMethodService mIms;
-
- /**
- * Isolates management of files. This variable should never be null, but can be changed
- * to support testing.
- */
- /* package */ LogFileManager mLogFileManager;
-
- /**
- * Manages the file(s) that stores the logs.
- *
- * Handles creation, deletion, and provides Readers, Writers, and InputStreams to access
- * the logs.
- */
- /* package */ static class LogFileManager {
- public static final String RESEARCH_LOG_FILENAME_KEY = "RESEARCH_LOG_FILENAME";
-
- private static final String DEFAULT_FILENAME = "researchLog.txt";
- private static final long LOGFILE_PURGE_INTERVAL = 1000 * 60 * 60 * 24;
-
- protected InputMethodService mIms;
- protected File mFile;
- protected PrintWriter mPrintWriter;
-
- /* package */ LogFileManager() {
- }
-
- public void init(final InputMethodService ims) {
- mIms = ims;
- }
-
- public synchronized void createLogFile() throws IOException {
- createLogFile(DEFAULT_FILENAME);
- }
-
- public synchronized void createLogFile(final SharedPreferences prefs)
- throws IOException {
- final String filename =
- prefs.getString(RESEARCH_LOG_FILENAME_KEY, DEFAULT_FILENAME);
- createLogFile(filename);
- }
-
- public synchronized void createLogFile(final String filename)
- throws IOException {
- if (mIms == null) {
- final String msg = "InputMethodService is not configured. Logging is off.";
- Log.w(TAG, msg);
- throw new IOException(msg);
- }
- final File filesDir = mIms.getFilesDir();
- if (filesDir == null || !filesDir.exists()) {
- final String msg = "Storage directory does not exist. Logging is off.";
- Log.w(TAG, msg);
- throw new IOException(msg);
- }
- close();
- final File file = new File(filesDir, filename);
- mFile = file;
- boolean append = true;
- if (file.exists() && file.lastModified() + LOGFILE_PURGE_INTERVAL <
- System.currentTimeMillis()) {
- append = false;
- }
- mPrintWriter = new PrintWriter(new BufferedWriter(new FileWriter(file, append)), true);
- }
-
- public synchronized boolean append(final String s) {
- PrintWriter printWriter = mPrintWriter;
- if (printWriter == null || !mFile.exists()) {
- if (DEBUG) {
- Log.w(TAG, "PrintWriter is null... attempting to create default log file");
- }
- try {
- createLogFile();
- printWriter = mPrintWriter;
- } catch (IOException e) {
- Log.w(TAG, "Failed to create log file. Not logging.");
- return false;
- }
- }
- printWriter.print(s);
- printWriter.flush();
- return !printWriter.checkError();
- }
-
- public synchronized void reset() {
- if (mPrintWriter != null) {
- mPrintWriter.close();
- mPrintWriter = null;
- if (DEBUG) {
- Log.d(TAG, "logfile closed");
- }
- }
- if (mFile != null) {
- mFile.delete();
- if (DEBUG) {
- Log.d(TAG, "logfile deleted");
- }
- mFile = null;
- }
- }
-
- public synchronized void close() {
- if (mPrintWriter != null) {
- mPrintWriter.close();
- mPrintWriter = null;
- mFile = null;
- if (DEBUG) {
- Log.d(TAG, "logfile closed");
- }
- }
- }
-
- /* package */ synchronized void flush() {
- if (mPrintWriter != null) {
- mPrintWriter.flush();
- }
- }
-
- /* package */ synchronized String getContents() {
- final File file = mFile;
- if (file == null) {
- return "";
- }
- if (mPrintWriter != null) {
- mPrintWriter.flush();
- }
- FileInputStream stream = null;
- FileChannel fileChannel = null;
- String s = "";
- try {
- stream = new FileInputStream(file);
- fileChannel = stream.getChannel();
- final ByteBuffer byteBuffer = ByteBuffer.allocate((int) file.length());
- fileChannel.read(byteBuffer);
- byteBuffer.rewind();
- CharBuffer charBuffer = Charset.defaultCharset().decode(byteBuffer);
- s = charBuffer.toString();
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- try {
- if (fileChannel != null) {
- fileChannel.close();
- }
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- try {
- if (stream != null) {
- stream.close();
- }
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- return s;
- }
- }
-
- private ResearchLogger(final LogFileManager logFileManager) {
- final HandlerThread handlerThread = new HandlerThread("ResearchLogger logging task",
- Process.THREAD_PRIORITY_BACKGROUND);
- handlerThread.start();
- mLoggingHandler = new Handler(handlerThread.getLooper());
- mLogFileManager = logFileManager;
- }
-
- public static ResearchLogger getInstance() {
- return sInstance;
- }
-
- public static void init(final InputMethodService ims, final SharedPreferences prefs) {
- sInstance.initInternal(ims, prefs);
- }
-
- /* package */ void initInternal(final InputMethodService ims, final SharedPreferences prefs) {
- mIms = ims;
- final LogFileManager logFileManager = mLogFileManager;
- if (logFileManager != null) {
- logFileManager.init(ims);
- try {
- logFileManager.createLogFile(prefs);
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- if (prefs != null) {
- sIsLogging = prefs.getBoolean(PREF_USABILITY_STUDY_MODE, false);
- prefs.registerOnSharedPreferenceChangeListener(this);
- }
- }
-
- /**
- * Represents a category of logging events that share the same subfield structure.
- */
- private static enum LogGroup {
- MOTION_EVENT("m"),
- KEY("k"),
- CORRECTION("c"),
- STATE_CHANGE("s"),
- UNSTRUCTURED("u");
-
- private final String mLogString;
-
- private LogGroup(final String logString) {
- mLogString = logString;
- }
- }
-
- public void logMotionEvent(final int action, final long eventTime, final int id,
- final int x, final int y, final float size, final float pressure) {
- final String eventTag;
- switch (action) {
- case MotionEvent.ACTION_CANCEL: eventTag = "[Cancel]"; break;
- case MotionEvent.ACTION_UP: eventTag = "[Up]"; break;
- case MotionEvent.ACTION_DOWN: eventTag = "[Down]"; break;
- case MotionEvent.ACTION_POINTER_UP: eventTag = "[PointerUp]"; break;
- case MotionEvent.ACTION_POINTER_DOWN: eventTag = "[PointerDown]"; break;
- case MotionEvent.ACTION_MOVE: eventTag = "[Move]"; break;
- case MotionEvent.ACTION_OUTSIDE: eventTag = "[Outside]"; break;
- default: eventTag = "[Action" + action + "]"; break;
- }
- if (!TextUtils.isEmpty(eventTag)) {
- final StringBuilder sb = new StringBuilder();
- sb.append(eventTag);
- sb.append('\t'); sb.append(eventTime);
- sb.append('\t'); sb.append(id);
- sb.append('\t'); sb.append(x);
- sb.append('\t'); sb.append(y);
- sb.append('\t'); sb.append(size);
- sb.append('\t'); sb.append(pressure);
- write(LogGroup.MOTION_EVENT, sb.toString());
- }
- }
-
- public void logKeyEvent(final int code, final int x, final int y) {
- final StringBuilder sb = new StringBuilder();
- sb.append(Keyboard.printableCode(code));
- sb.append('\t'); sb.append(x);
- sb.append('\t'); sb.append(y);
- write(LogGroup.KEY, sb.toString());
- }
-
- public void logCorrection(final String subgroup, final String before, final String after,
- final int position) {
- final StringBuilder sb = new StringBuilder();
- sb.append(subgroup);
- sb.append('\t'); sb.append(before);
- sb.append('\t'); sb.append(after);
- sb.append('\t'); sb.append(position);
- write(LogGroup.CORRECTION, sb.toString());
- }
-
- public void logStateChange(final String subgroup, final String details) {
- write(LogGroup.STATE_CHANGE, subgroup + "\t" + details);
- }
-
- public static class UnsLogGroup {
- private static final boolean DEFAULT_ENABLED = true;
-
- private static final boolean KEYBOARDSTATE_ONCANCELINPUT_ENABLED = DEFAULT_ENABLED;
- private static final boolean KEYBOARDSTATE_ONCODEINPUT_ENABLED = DEFAULT_ENABLED;
- private static final boolean KEYBOARDSTATE_ONLONGPRESSTIMEOUT_ENABLED = DEFAULT_ENABLED;
- private static final boolean KEYBOARDSTATE_ONPRESSKEY_ENABLED = DEFAULT_ENABLED;
- private static final boolean KEYBOARDSTATE_ONRELEASEKEY_ENABLED = DEFAULT_ENABLED;
- private static final boolean LATINIME_COMMITCURRENTAUTOCORRECTION_ENABLED = DEFAULT_ENABLED;
- private static final boolean LATINIME_COMMITTEXT_ENABLED = DEFAULT_ENABLED;
- private static final boolean LATINIME_DELETESURROUNDINGTEXT_ENABLED = DEFAULT_ENABLED;
- private static final boolean LATINIME_DOUBLESPACEAUTOPERIOD_ENABLED = DEFAULT_ENABLED;
- private static final boolean LATINIME_ONDISPLAYCOMPLETIONS_ENABLED = DEFAULT_ENABLED;
- private static final boolean LATINIME_ONSTARTINPUTVIEWINTERNAL_ENABLED = DEFAULT_ENABLED;
- private static final boolean LATINIME_ONUPDATESELECTION_ENABLED = DEFAULT_ENABLED;
- private static final boolean LATINIME_PERFORMEDITORACTION_ENABLED = DEFAULT_ENABLED;
- private static final boolean LATINIME_PICKAPPLICATIONSPECIFIEDCOMPLETION_ENABLED
- = DEFAULT_ENABLED;
- private static final boolean LATINIME_PICKPUNCTUATIONSUGGESTION_ENABLED = DEFAULT_ENABLED;
- private static final boolean LATINIME_PICKSUGGESTIONMANUALLY_ENABLED = DEFAULT_ENABLED;
- private static final boolean LATINIME_REVERTCOMMIT_ENABLED = DEFAULT_ENABLED;
- private static final boolean LATINIME_REVERTDOUBLESPACEWHILEINBATCHEDIT_ENABLED
- = DEFAULT_ENABLED;
- private static final boolean LATINIME_REVERTSWAPPUNCTUATION_ENABLED = DEFAULT_ENABLED;
- private static final boolean LATINIME_SENDKEYCODEPOINT_ENABLED = DEFAULT_ENABLED;
- private static final boolean LATINIME_SWAPSWAPPERANDSPACEWHILEINBATCHEDIT_ENABLED
- = DEFAULT_ENABLED;
- private static final boolean LATINIME_SWITCHTOKEYBOARDVIEW_ENABLED = DEFAULT_ENABLED;
- private static final boolean LATINKEYBOARDVIEW_ONLONGPRESS_ENABLED = DEFAULT_ENABLED;
- private static final boolean LATINKEYBOARDVIEW_ONPROCESSMOTIONEVENT_ENABLED
- = DEFAULT_ENABLED;
- private static final boolean LATINKEYBOARDVIEW_SETKEYBOARD_ENABLED = DEFAULT_ENABLED;
- private static final boolean POINTERTRACKER_CALLLISTENERONCANCELINPUT_ENABLED
- = DEFAULT_ENABLED;
- private static final boolean POINTERTRACKER_CALLLISTENERONCODEINPUT_ENABLED
- = DEFAULT_ENABLED;
- private static final boolean
- POINTERTRACKER_CALLLISTENERONPRESSANDCHECKKEYBOARDLAYOUTCHANGE_ENABLED
- = DEFAULT_ENABLED;
- private static final boolean POINTERTRACKER_CALLLISTENERONRELEASE_ENABLED = DEFAULT_ENABLED;
- private static final boolean POINTERTRACKER_ONDOWNEVENT_ENABLED = DEFAULT_ENABLED;
- private static final boolean POINTERTRACKER_ONMOVEEVENT_ENABLED = DEFAULT_ENABLED;
- private static final boolean SUDDENJUMPINGTOUCHEVENTHANDLER_ONTOUCHEVENT_ENABLED
- = DEFAULT_ENABLED;
- private static final boolean SUGGESTIONSVIEW_SETSUGGESTIONS_ENABLED = DEFAULT_ENABLED;
- }
-
- public static void logUnstructured(String logGroup, final String details) {
- // TODO: improve performance by making entire class static and/or implementing natively
- getInstance().write(LogGroup.UNSTRUCTURED, logGroup + "\t" + details);
- }
-
- private void write(final LogGroup logGroup, final String log) {
- // TODO: rewrite in native for better performance
- mLoggingHandler.post(new Runnable() {
- @Override
- public void run() {
- final long currentTime = System.currentTimeMillis();
- final long upTime = SystemClock.uptimeMillis();
- final StringBuilder builder = new StringBuilder();
- builder.append(currentTime);
- builder.append('\t'); builder.append(upTime);
- builder.append('\t'); builder.append(logGroup.mLogString);
- builder.append('\t'); builder.append(log);
- builder.append('\n');
- if (DEBUG) {
- Log.d(TAG, "Write: " + '[' + logGroup.mLogString + ']' + log);
- }
- final String s = builder.toString();
- if (mLogFileManager.append(s)) {
- // success
- } else {
- if (DEBUG) {
- Log.w(TAG, "Unable to write to log.");
- }
- // perhaps logfile was deleted. try to recreate and relog.
- try {
- mLogFileManager.createLogFile(PreferenceManager
- .getDefaultSharedPreferences(mIms));
- mLogFileManager.append(s);
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- });
- }
-
- public void clearAll() {
- mLoggingHandler.post(new Runnable() {
- @Override
- public void run() {
- if (DEBUG) {
- Log.d(TAG, "Delete log file.");
- }
- mLogFileManager.reset();
- }
- });
- }
-
- /* package */ LogFileManager getLogFileManager() {
- return mLogFileManager;
- }
-
- @Override
- public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
- if (key == null || prefs == null) {
- return;
- }
- sIsLogging = prefs.getBoolean(PREF_USABILITY_STUDY_MODE, false);
- }
-
- public static void keyboardState_onCancelInput(final boolean isSinglePointer,
- final KeyboardState keyboardState) {
- if (UnsLogGroup.KEYBOARDSTATE_ONCANCELINPUT_ENABLED) {
- final String s = "onCancelInput: single=" + isSinglePointer + " " + keyboardState;
- logUnstructured("KeyboardState_onCancelInput", s);
- }
- }
-
- public static void keyboardState_onCodeInput(
- final int code, final boolean isSinglePointer, final int autoCaps,
- final KeyboardState keyboardState) {
- if (UnsLogGroup.KEYBOARDSTATE_ONCODEINPUT_ENABLED) {
- final String s = "onCodeInput: code=" + Keyboard.printableCode(code)
- + " single=" + isSinglePointer
- + " autoCaps=" + autoCaps + " " + keyboardState;
- logUnstructured("KeyboardState_onCodeInput", s);
- }
- }
-
- public static void keyboardState_onLongPressTimeout(final int code,
- final KeyboardState keyboardState) {
- if (UnsLogGroup.KEYBOARDSTATE_ONLONGPRESSTIMEOUT_ENABLED) {
- final String s = "onLongPressTimeout: code=" + Keyboard.printableCode(code) + " "
- + keyboardState;
- logUnstructured("KeyboardState_onLongPressTimeout", s);
- }
- }
-
- public static void keyboardState_onPressKey(final int code,
- final KeyboardState keyboardState) {
- if (UnsLogGroup.KEYBOARDSTATE_ONPRESSKEY_ENABLED) {
- final String s = "onPressKey: code=" + Keyboard.printableCode(code) + " "
- + keyboardState;
- logUnstructured("KeyboardState_onPressKey", s);
- }
- }
-
- public static void keyboardState_onReleaseKey(final KeyboardState keyboardState, final int code,
- final boolean withSliding) {
- if (UnsLogGroup.KEYBOARDSTATE_ONRELEASEKEY_ENABLED) {
- final String s = "onReleaseKey: code=" + Keyboard.printableCode(code)
- + " sliding=" + withSliding + " " + keyboardState;
- logUnstructured("KeyboardState_onReleaseKey", s);
- }
- }
-
- public static void latinIME_commitCurrentAutoCorrection(final String typedWord,
- final String autoCorrection) {
- if (UnsLogGroup.LATINIME_COMMITCURRENTAUTOCORRECTION_ENABLED) {
- if (typedWord.equals(autoCorrection)) {
- getInstance().logCorrection("[----]", typedWord, autoCorrection, -1);
- } else {
- getInstance().logCorrection("[Auto]", typedWord, autoCorrection, -1);
- }
- }
- }
-
- public static void latinIME_commitText(final CharSequence typedWord) {
- if (UnsLogGroup.LATINIME_COMMITTEXT_ENABLED) {
- logUnstructured("LatinIME_commitText", typedWord.toString());
- }
- }
-
- public static void latinIME_deleteSurroundingText(final int length) {
- if (UnsLogGroup.LATINIME_DELETESURROUNDINGTEXT_ENABLED) {
- logUnstructured("LatinIME_deleteSurroundingText", String.valueOf(length));
- }
- }
-
- public static void latinIME_doubleSpaceAutoPeriod() {
- if (UnsLogGroup.LATINIME_DOUBLESPACEAUTOPERIOD_ENABLED) {
- logUnstructured("LatinIME_doubleSpaceAutoPeriod", "");
- }
- }
-
- public static void latinIME_onDisplayCompletions(
- final CompletionInfo[] applicationSpecifiedCompletions) {
- if (UnsLogGroup.LATINIME_ONDISPLAYCOMPLETIONS_ENABLED) {
- final StringBuilder builder = new StringBuilder();
- builder.append("Received completions:");
- if (applicationSpecifiedCompletions != null) {
- for (int i = 0; i < applicationSpecifiedCompletions.length; i++) {
- builder.append(" #");
- builder.append(i);
- builder.append(": ");
- builder.append(applicationSpecifiedCompletions[i]);
- builder.append("\n");
- }
- }
- logUnstructured("LatinIME_onDisplayCompletions", builder.toString());
- }
- }
-
- public static void latinIME_onStartInputViewInternal(final EditorInfo editorInfo,
- final SharedPreferences prefs) {
- if (UnsLogGroup.LATINIME_ONSTARTINPUTVIEWINTERNAL_ENABLED) {
- final StringBuilder builder = new StringBuilder();
- builder.append("onStartInputView: editorInfo:");
- builder.append("\tinputType=");
- builder.append(Integer.toHexString(editorInfo.inputType));
- builder.append("\timeOptions=");
- builder.append(Integer.toHexString(editorInfo.imeOptions));
- builder.append("\tdisplay="); builder.append(Build.DISPLAY);
- builder.append("\tmodel="); builder.append(Build.MODEL);
- for (Map.Entry<String,?> entry : prefs.getAll().entrySet()) {
- builder.append("\t" + entry.getKey());
- Object value = entry.getValue();
- builder.append("=" + ((value == null) ? "<null>" : value.toString()));
- }
- logUnstructured("LatinIME_onStartInputViewInternal", builder.toString());
- }
- }
-
- public static void latinIME_onUpdateSelection(final int lastSelectionStart,
- final int lastSelectionEnd, final int oldSelStart, final int oldSelEnd,
- final int newSelStart, final int newSelEnd, final int composingSpanStart,
- final int composingSpanEnd) {
- if (UnsLogGroup.LATINIME_ONUPDATESELECTION_ENABLED) {
- final String s = "onUpdateSelection: oss=" + oldSelStart
- + ", ose=" + oldSelEnd
- + ", lss=" + lastSelectionStart
- + ", lse=" + lastSelectionEnd
- + ", nss=" + newSelStart
- + ", nse=" + newSelEnd
- + ", cs=" + composingSpanStart
- + ", ce=" + composingSpanEnd;
- logUnstructured("LatinIME_onUpdateSelection", s);
- }
- }
-
- public static void latinIME_performEditorAction(final int imeActionNext) {
- if (UnsLogGroup.LATINIME_PERFORMEDITORACTION_ENABLED) {
- logUnstructured("LatinIME_performEditorAction", String.valueOf(imeActionNext));
- }
- }
-
- public static void latinIME_pickApplicationSpecifiedCompletion(final int index,
- final CharSequence text, int x, int y) {
- if (UnsLogGroup.LATINIME_PICKAPPLICATIONSPECIFIEDCOMPLETION_ENABLED) {
- final String s = String.valueOf(index) + '\t' + text + '\t' + x + '\t' + y;
- logUnstructured("LatinIME_pickApplicationSpecifiedCompletion", s);
- }
- }
-
- public static void latinIME_pickSuggestionManually(final String replacedWord,
- final int index, CharSequence suggestion, int x, int y) {
- if (UnsLogGroup.LATINIME_PICKSUGGESTIONMANUALLY_ENABLED) {
- final String s = String.valueOf(index) + '\t' + suggestion + '\t' + x + '\t' + y;
- logUnstructured("LatinIME_pickSuggestionManually", s);
- }
- }
-
- public static void latinIME_punctuationSuggestion(final int index,
- final CharSequence suggestion, int x, int y) {
- if (UnsLogGroup.LATINIME_PICKPUNCTUATIONSUGGESTION_ENABLED) {
- final String s = String.valueOf(index) + '\t' + suggestion + '\t' + x + '\t' + y;
- logUnstructured("LatinIME_pickPunctuationSuggestion", s);
- }
- }
-
- public static void latinIME_revertDoubleSpaceWhileInBatchEdit() {
- if (UnsLogGroup.LATINIME_REVERTDOUBLESPACEWHILEINBATCHEDIT_ENABLED) {
- logUnstructured("LatinIME_revertDoubleSpaceWhileInBatchEdit", "");
- }
- }
-
- public static void latinIME_revertSwapPunctuation() {
- if (UnsLogGroup.LATINIME_REVERTSWAPPUNCTUATION_ENABLED) {
- logUnstructured("LatinIME_revertSwapPunctuation", "");
- }
- }
-
- public static void latinIME_sendKeyCodePoint(final int code) {
- if (UnsLogGroup.LATINIME_SENDKEYCODEPOINT_ENABLED) {
- logUnstructured("LatinIME_sendKeyCodePoint", String.valueOf(code));
- }
- }
-
- public static void latinIME_swapSwapperAndSpaceWhileInBatchEdit() {
- if (UnsLogGroup.LATINIME_SWAPSWAPPERANDSPACEWHILEINBATCHEDIT_ENABLED) {
- logUnstructured("latinIME_swapSwapperAndSpaceWhileInBatchEdit", "");
- }
- }
-
- public static void latinIME_switchToKeyboardView() {
- if (UnsLogGroup.LATINIME_SWITCHTOKEYBOARDVIEW_ENABLED) {
- final String s = "Switch to keyboard view.";
- logUnstructured("LatinIME_switchToKeyboardView", s);
- }
- }
-
- public static void latinKeyboardView_onLongPress() {
- if (UnsLogGroup.LATINKEYBOARDVIEW_ONLONGPRESS_ENABLED) {
- final String s = "long press detected";
- logUnstructured("LatinKeyboardView_onLongPress", s);
- }
- }
-
- public static void latinKeyboardView_processMotionEvent(MotionEvent me, int action,
- long eventTime, int index, int id, int x, int y) {
- if (UnsLogGroup.LATINKEYBOARDVIEW_ONPROCESSMOTIONEVENT_ENABLED) {
- final float size = me.getSize(index);
- final float pressure = me.getPressure(index);
- if (action != MotionEvent.ACTION_MOVE) {
- getInstance().logMotionEvent(action, eventTime, id, x, y, size, pressure);
- }
- }
- }
-
- public static void latinKeyboardView_setKeyboard(final Keyboard keyboard) {
- if (UnsLogGroup.LATINKEYBOARDVIEW_SETKEYBOARD_ENABLED) {
- StringBuilder builder = new StringBuilder();
- builder.append("id=");
- builder.append(keyboard.mId);
- builder.append("\tw=");
- builder.append(keyboard.mOccupiedWidth);
- builder.append("\th=");
- builder.append(keyboard.mOccupiedHeight);
- builder.append("\tkeys=[");
- boolean first = true;
- for (Key key : keyboard.mKeys) {
- if (first) {
- first = false;
- } else {
- builder.append(",");
- }
- builder.append("{code:");
- builder.append(key.mCode);
- builder.append(",altCode:");
- builder.append(key.mAltCode);
- builder.append(",x:");
- builder.append(key.mX);
- builder.append(",y:");
- builder.append(key.mY);
- builder.append(",w:");
- builder.append(key.mWidth);
- builder.append(",h:");
- builder.append(key.mHeight);
- builder.append("}");
- }
- builder.append("]");
- logUnstructured("LatinKeyboardView_setKeyboard", builder.toString());
- }
- }
-
- public static void latinIME_revertCommit(final String originallyTypedWord) {
- if (UnsLogGroup.LATINIME_REVERTCOMMIT_ENABLED) {
- logUnstructured("LatinIME_revertCommit", originallyTypedWord);
- }
- }
-
- public static void pointerTracker_callListenerOnCancelInput() {
- final String s = "onCancelInput";
- if (UnsLogGroup.POINTERTRACKER_CALLLISTENERONCANCELINPUT_ENABLED) {
- logUnstructured("PointerTracker_callListenerOnCancelInput", s);
- }
- }
-
- public static void pointerTracker_callListenerOnCodeInput(final Key key, final int x,
- final int y, final boolean ignoreModifierKey, final boolean altersCode,
- final int code) {
- if (UnsLogGroup.POINTERTRACKER_CALLLISTENERONCODEINPUT_ENABLED) {
- final String s = "onCodeInput: " + Keyboard.printableCode(code)
- + " text=" + key.mOutputText + " x=" + x + " y=" + y
- + " ignoreModifier=" + ignoreModifierKey + " altersCode=" + altersCode
- + " enabled=" + key.isEnabled();
- logUnstructured("PointerTracker_callListenerOnCodeInput", s);
- }
- }
-
- public static void pointerTracker_callListenerOnPressAndCheckKeyboardLayoutChange(
- final Key key, final boolean ignoreModifierKey) {
- if (UnsLogGroup.POINTERTRACKER_CALLLISTENERONPRESSANDCHECKKEYBOARDLAYOUTCHANGE_ENABLED) {
- final String s = "onPress : " + KeyDetector.printableCode(key)
- + " ignoreModifier=" + ignoreModifierKey
- + " enabled=" + key.isEnabled();
- logUnstructured("PointerTracker_callListenerOnPressAndCheckKeyboardLayoutChange", s);
- }
- }
-
- public static void pointerTracker_callListenerOnRelease(final Key key, final int primaryCode,
- final boolean withSliding, final boolean ignoreModifierKey) {
- if (UnsLogGroup.POINTERTRACKER_CALLLISTENERONRELEASE_ENABLED) {
- final String s = "onRelease : " + Keyboard.printableCode(primaryCode)
- + " sliding=" + withSliding + " ignoreModifier=" + ignoreModifierKey
- + " enabled="+ key.isEnabled();
- logUnstructured("PointerTracker_callListenerOnRelease", s);
- }
- }
-
- public static void pointerTracker_onDownEvent(long deltaT, int distanceSquared) {
- if (UnsLogGroup.POINTERTRACKER_ONDOWNEVENT_ENABLED) {
- final String s = "onDownEvent: ignore potential noise: time=" + deltaT
- + " distance=" + distanceSquared;
- logUnstructured("PointerTracker_onDownEvent", s);
- }
- }
-
- public static void pointerTracker_onMoveEvent(final int x, final int y, final int lastX,
- final int lastY) {
- if (UnsLogGroup.POINTERTRACKER_ONMOVEEVENT_ENABLED) {
- final String s = String.format("onMoveEvent: sudden move is translated to "
- + "up[%d,%d]/down[%d,%d] events", lastX, lastY, x, y);
- logUnstructured("PointerTracker_onMoveEvent", s);
- }
- }
-
- public static void suddenJumpingTouchEventHandler_onTouchEvent(final MotionEvent me) {
- if (UnsLogGroup.SUDDENJUMPINGTOUCHEVENTHANDLER_ONTOUCHEVENT_ENABLED) {
- final String s = "onTouchEvent: ignore sudden jump " + me;
- logUnstructured("SuddenJumpingTouchEventHandler_onTouchEvent", s);
- }
- }
-
- public static void suggestionsView_setSuggestions(final SuggestedWords mSuggestedWords) {
- if (UnsLogGroup.SUGGESTIONSVIEW_SETSUGGESTIONS_ENABLED) {
- logUnstructured("SuggestionsView_setSuggestions", mSuggestedWords.toString());
- }
- }
-} \ No newline at end of file
diff --git a/java/src/com/android/inputmethod/latin/ResizableIntArray.java b/java/src/com/android/inputmethod/latin/ResizableIntArray.java
new file mode 100644
index 000000000..9a46f160b
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/ResizableIntArray.java
@@ -0,0 +1,146 @@
+/*
+ * 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;
+
+import java.util.Arrays;
+
+// TODO: This class is not thread-safe.
+public final class ResizableIntArray {
+ private int[] mArray;
+ private int mLength;
+
+ public ResizableIntArray(final int capacity) {
+ reset(capacity);
+ }
+
+ public int get(final int index) {
+ if (index < mLength) {
+ return mArray[index];
+ }
+ throw new ArrayIndexOutOfBoundsException("length=" + mLength + "; index=" + index);
+ }
+
+ public void add(final int index, final int val) {
+ if (index < mLength) {
+ mArray[index] = val;
+ } else {
+ mLength = index;
+ add(val);
+ }
+ }
+
+ public void add(final int val) {
+ final int currentLength = mLength;
+ ensureCapacity(currentLength + 1);
+ mArray[currentLength] = val;
+ mLength = currentLength + 1;
+ }
+
+ /**
+ * Calculate the new capacity of {@code mArray}.
+ * @param minimumCapacity the minimum capacity that the {@code mArray} should have.
+ * @return the new capacity that the {@code mArray} should have. Returns zero when there is no
+ * need to expand {@code mArray}.
+ */
+ private int calculateCapacity(final int minimumCapacity) {
+ final int currentCapcity = mArray.length;
+ if (currentCapcity < minimumCapacity) {
+ final int nextCapacity = currentCapcity * 2;
+ // The following is the same as return Math.max(minimumCapacity, nextCapacity);
+ return minimumCapacity > nextCapacity ? minimumCapacity : nextCapacity;
+ }
+ return 0;
+ }
+
+ private void ensureCapacity(final int minimumCapacity) {
+ final int newCapacity = calculateCapacity(minimumCapacity);
+ if (newCapacity > 0) {
+ // TODO: Implement primitive array pool.
+ mArray = Arrays.copyOf(mArray, newCapacity);
+ }
+ }
+
+ public int getLength() {
+ return mLength;
+ }
+
+ public void setLength(final int newLength) {
+ ensureCapacity(newLength);
+ mLength = newLength;
+ }
+
+ public void reset(final int capacity) {
+ // TODO: Implement primitive array pool.
+ mArray = new int[capacity];
+ mLength = 0;
+ }
+
+ public int[] getPrimitiveArray() {
+ return mArray;
+ }
+
+ public void set(final ResizableIntArray ip) {
+ // TODO: Implement primitive array pool.
+ mArray = ip.mArray;
+ mLength = ip.mLength;
+ }
+
+ public void copy(final ResizableIntArray ip) {
+ final int newCapacity = calculateCapacity(ip.mLength);
+ if (newCapacity > 0) {
+ // TODO: Implement primitive array pool.
+ mArray = new int[newCapacity];
+ }
+ System.arraycopy(ip.mArray, 0, mArray, 0, ip.mLength);
+ mLength = ip.mLength;
+ }
+
+ public void append(final ResizableIntArray src, final int startPos, final int length) {
+ if (length == 0) {
+ return;
+ }
+ final int currentLength = mLength;
+ final int newLength = currentLength + length;
+ ensureCapacity(newLength);
+ System.arraycopy(src.mArray, startPos, mArray, currentLength, length);
+ mLength = newLength;
+ }
+
+ public void fill(final int value, final int startPos, final int length) {
+ if (startPos < 0 || length < 0) {
+ throw new IllegalArgumentException("startPos=" + startPos + "; length=" + length);
+ }
+ final int endPos = startPos + length;
+ ensureCapacity(endPos);
+ Arrays.fill(mArray, startPos, endPos, value);
+ if (mLength < endPos) {
+ mLength = endPos;
+ }
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < mLength; i++) {
+ if (i != 0) {
+ sb.append(",");
+ }
+ sb.append(mArray[i]);
+ }
+ return "[" + sb + "]";
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/ResourceUtils.java b/java/src/com/android/inputmethod/latin/ResourceUtils.java
new file mode 100644
index 000000000..5021ad384
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/ResourceUtils.java
@@ -0,0 +1,128 @@
+/*
+ * 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;
+
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.os.Build;
+import android.util.TypedValue;
+
+import java.util.HashMap;
+
+public final class ResourceUtils {
+ public static final float UNDEFINED_RATIO = -1.0f;
+ public static final int UNDEFINED_DIMENSION = -1;
+
+ private ResourceUtils() {
+ // This utility class is not publicly instantiable.
+ }
+
+ private static final String HARDWARE_PREFIX = Build.HARDWARE + ",";
+ private static final HashMap<String, String> sDeviceOverrideValueMap =
+ CollectionUtils.newHashMap();
+
+ public static String getDeviceOverrideValue(Resources res, int overrideResId, String defValue) {
+ final int orientation = res.getConfiguration().orientation;
+ final String key = overrideResId + "-" + orientation;
+ if (!sDeviceOverrideValueMap.containsKey(key)) {
+ String overrideValue = defValue;
+ for (final String element : res.getStringArray(overrideResId)) {
+ if (element.startsWith(HARDWARE_PREFIX)) {
+ overrideValue = element.substring(HARDWARE_PREFIX.length());
+ break;
+ }
+ }
+ sDeviceOverrideValueMap.put(key, overrideValue);
+ }
+ return sDeviceOverrideValueMap.get(key);
+ }
+
+ public static boolean isValidFraction(final float fraction) {
+ return fraction >= 0.0f;
+ }
+
+ // {@link Resources#getDimensionPixelSize(int)} returns at least one pixel size.
+ public static boolean isValidDimensionPixelSize(final int dimension) {
+ return dimension > 0;
+ }
+
+ // {@link Resources#getDimensionPixelOffset(int)} may return zero pixel offset.
+ public static boolean isValidDimensionPixelOffset(final int dimension) {
+ return dimension >= 0;
+ }
+
+ public static float getFraction(final TypedArray a, final int index, final float defValue) {
+ final TypedValue value = a.peekValue(index);
+ if (value == null || !isFractionValue(value)) {
+ return defValue;
+ }
+ return a.getFraction(index, 1, 1, defValue);
+ }
+
+ public static float getFraction(final TypedArray a, final int index) {
+ return getFraction(a, index, UNDEFINED_RATIO);
+ }
+
+ public static int getDimensionPixelSize(final TypedArray a, final int index) {
+ final TypedValue value = a.peekValue(index);
+ if (value == null || !isDimensionValue(value)) {
+ return ResourceUtils.UNDEFINED_DIMENSION;
+ }
+ return a.getDimensionPixelSize(index, ResourceUtils.UNDEFINED_DIMENSION);
+ }
+
+ public static float getDimensionOrFraction(TypedArray a, int index, int base,
+ float defValue) {
+ final TypedValue value = a.peekValue(index);
+ if (value == null) {
+ return defValue;
+ }
+ if (isFractionValue(value)) {
+ return a.getFraction(index, base, base, defValue);
+ } else if (isDimensionValue(value)) {
+ return a.getDimension(index, defValue);
+ }
+ return defValue;
+ }
+
+ public static int getEnumValue(TypedArray a, int index, int defValue) {
+ final TypedValue value = a.peekValue(index);
+ if (value == null) {
+ return defValue;
+ }
+ if (isIntegerValue(value)) {
+ return a.getInt(index, defValue);
+ }
+ return defValue;
+ }
+
+ public static boolean isFractionValue(TypedValue v) {
+ return v.type == TypedValue.TYPE_FRACTION;
+ }
+
+ public static boolean isDimensionValue(TypedValue v) {
+ return v.type == TypedValue.TYPE_DIMENSION;
+ }
+
+ public static boolean isIntegerValue(TypedValue v) {
+ return v.type >= TypedValue.TYPE_FIRST_INT && v.type <= TypedValue.TYPE_LAST_INT;
+ }
+
+ public static boolean isStringValue(TypedValue v) {
+ return v.type == TypedValue.TYPE_STRING;
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java
new file mode 100644
index 000000000..21441369e
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java
@@ -0,0 +1,709 @@
+/*
+ * 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;
+
+import android.inputmethodservice.InputMethodService;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.inputmethod.CompletionInfo;
+import android.view.inputmethod.CorrectionInfo;
+import android.view.inputmethod.ExtractedText;
+import android.view.inputmethod.ExtractedTextRequest;
+import android.view.inputmethod.InputConnection;
+
+import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.latin.define.ProductionFlag;
+import com.android.inputmethod.research.ResearchLogger;
+
+import java.util.Locale;
+import java.util.regex.Pattern;
+
+/**
+ * Enrichment class for InputConnection to simplify interaction and add functionality.
+ *
+ * This class serves as a wrapper to be able to simply add hooks to any calls to the underlying
+ * InputConnection. It also keeps track of a number of things to avoid having to call upon IPC
+ * all the time to find out what text is in the buffer, when we need it to determine caps mode
+ * for example.
+ */
+public final class RichInputConnection {
+ private static final String TAG = RichInputConnection.class.getSimpleName();
+ private static final boolean DBG = false;
+ private static final boolean DEBUG_PREVIOUS_TEXT = false;
+ private static final boolean DEBUG_BATCH_NESTING = false;
+ // Provision for a long word pair and a separator
+ private static final int LOOKBACK_CHARACTER_NUM = BinaryDictionary.MAX_WORD_LENGTH * 2 + 1;
+ private static final Pattern spaceRegex = Pattern.compile("\\s+");
+ private static final int INVALID_CURSOR_POSITION = -1;
+
+ /**
+ * This variable contains the value LatinIME thinks the cursor position should be at now.
+ * This is a few steps in advance of what the TextView thinks it is, because TextView will
+ * only know after the IPC calls gets through.
+ */
+ private int mCurrentCursorPosition = INVALID_CURSOR_POSITION; // in chars, not code points
+ /**
+ * This contains the committed text immediately preceding the cursor and the composing
+ * text if any. It is refreshed when the cursor moves by calling upon the TextView.
+ */
+ private StringBuilder mCommittedTextBeforeComposingText = new StringBuilder();
+ /**
+ * This contains the currently composing text, as LatinIME thinks the TextView is seeing it.
+ */
+ private StringBuilder mComposingText = new StringBuilder();
+ /**
+ * This is a one-character string containing the character after the cursor. Since LatinIME
+ * never touches it directly, it's never modified by any means other than re-reading from the
+ * TextView when the cursor position is changed by the user.
+ */
+ private CharSequence mCharAfterTheCursor = "";
+ // A hint on how many characters to cache from the TextView. A good value of this is given by
+ // how many characters we need to be able to almost always find the caps mode.
+ private static final int DEFAULT_TEXT_CACHE_SIZE = 100;
+
+ private final InputMethodService mParent;
+ InputConnection mIC;
+ int mNestLevel;
+ public RichInputConnection(final InputMethodService parent) {
+ mParent = parent;
+ mIC = null;
+ mNestLevel = 0;
+ }
+
+ private void checkConsistencyForDebug() {
+ final ExtractedTextRequest r = new ExtractedTextRequest();
+ r.hintMaxChars = 0;
+ r.hintMaxLines = 0;
+ r.token = 1;
+ r.flags = 0;
+ final ExtractedText et = mIC.getExtractedText(r, 0);
+ final CharSequence beforeCursor = getTextBeforeCursor(DEFAULT_TEXT_CACHE_SIZE, 0);
+ final StringBuilder internal = new StringBuilder().append(mCommittedTextBeforeComposingText)
+ .append(mComposingText);
+ if (null == et || null == beforeCursor) return;
+ final int actualLength = Math.min(beforeCursor.length(), internal.length());
+ if (internal.length() > actualLength) {
+ internal.delete(0, internal.length() - actualLength);
+ }
+ final String reference = (beforeCursor.length() <= actualLength) ? beforeCursor.toString()
+ : beforeCursor.subSequence(beforeCursor.length() - actualLength,
+ beforeCursor.length()).toString();
+ if (et.selectionStart != mCurrentCursorPosition
+ || !(reference.equals(internal.toString()))) {
+ final String context = "Expected cursor position = " + mCurrentCursorPosition
+ + "\nActual cursor position = " + et.selectionStart
+ + "\nExpected text = " + internal.length() + " " + internal
+ + "\nActual text = " + reference.length() + " " + reference;
+ ((LatinIME)mParent).debugDumpStateAndCrashWithException(context);
+ } else {
+ Log.e(TAG, Utils.getStackTrace(2));
+ Log.e(TAG, "Exp <> Actual : " + mCurrentCursorPosition + " <> " + et.selectionStart);
+ }
+ }
+
+ public void beginBatchEdit() {
+ if (++mNestLevel == 1) {
+ mIC = mParent.getCurrentInputConnection();
+ if (null != mIC) {
+ mIC.beginBatchEdit();
+ }
+ } else {
+ if (DBG) {
+ throw new RuntimeException("Nest level too deep");
+ } else {
+ Log.e(TAG, "Nest level too deep : " + mNestLevel);
+ }
+ }
+ if (DEBUG_BATCH_NESTING) checkBatchEdit();
+ if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
+ }
+
+ public void endBatchEdit() {
+ if (mNestLevel <= 0) Log.e(TAG, "Batch edit not in progress!"); // TODO: exception instead
+ if (--mNestLevel == 0 && null != mIC) {
+ mIC.endBatchEdit();
+ }
+ if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
+ }
+
+ public void resetCachesUponCursorMove(final int newCursorPosition) {
+ mCurrentCursorPosition = newCursorPosition;
+ mComposingText.setLength(0);
+ mCommittedTextBeforeComposingText.setLength(0);
+ mCommittedTextBeforeComposingText.append(getTextBeforeCursor(DEFAULT_TEXT_CACHE_SIZE, 0));
+ mCharAfterTheCursor = getTextAfterCursor(1, 0);
+ if (null != mIC) {
+ mIC.finishComposingText();
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.richInputConnection_finishComposingText();
+ }
+ }
+ }
+
+ private void checkBatchEdit() {
+ if (mNestLevel != 1) {
+ // TODO: exception instead
+ Log.e(TAG, "Batch edit level incorrect : " + mNestLevel);
+ Log.e(TAG, Utils.getStackTrace(4));
+ }
+ }
+
+ public void finishComposingText() {
+ if (DEBUG_BATCH_NESTING) checkBatchEdit();
+ if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
+ mCommittedTextBeforeComposingText.append(mComposingText);
+ mCurrentCursorPosition += mComposingText.length();
+ mComposingText.setLength(0);
+ if (null != mIC) {
+ mIC.finishComposingText();
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.richInputConnection_finishComposingText();
+ }
+ }
+ }
+
+ public void commitText(final CharSequence text, final int i) {
+ if (DEBUG_BATCH_NESTING) checkBatchEdit();
+ if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
+ mCommittedTextBeforeComposingText.append(text);
+ mCurrentCursorPosition += text.length() - mComposingText.length();
+ mComposingText.setLength(0);
+ if (null != mIC) {
+ mIC.commitText(text, i);
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.richInputConnection_commitText(text, i);
+ }
+ }
+ }
+
+ /**
+ * 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)) {
+ 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.
+ // getCapsMode should be updated to be able to return a "not enough info" result so that
+ // we can get more context only when needed.
+ if (TextUtils.isEmpty(mCommittedTextBeforeComposingText) && 0 != mCurrentCursorPosition) {
+ mCommittedTextBeforeComposingText.append(
+ getTextBeforeCursor(DEFAULT_TEXT_CACHE_SIZE, 0));
+ }
+ // This never calls InputConnection#getCapsMode - in fact, it's a static method that
+ // never blocks or initiates IPC.
+ return StringUtils.getCapsMode(mCommittedTextBeforeComposingText, inputType, locale,
+ hasSpaceBefore);
+ }
+
+ public int getCodePointBeforeCursor() {
+ if (mCommittedTextBeforeComposingText.length() < 1) return Constants.NOT_A_CODE;
+ return Character.codePointBefore(mCommittedTextBeforeComposingText,
+ mCommittedTextBeforeComposingText.length());
+ }
+
+ public CharSequence getTextBeforeCursor(final int i, final int j) {
+ // TODO: use mCommittedTextBeforeComposingText if possible to improve performance
+ mIC = mParent.getCurrentInputConnection();
+ if (null != mIC) return mIC.getTextBeforeCursor(i, j);
+ return null;
+ }
+
+ public CharSequence getTextAfterCursor(final int i, final int j) {
+ mIC = mParent.getCurrentInputConnection();
+ if (null != mIC) return mIC.getTextAfterCursor(i, j);
+ return null;
+ }
+
+ public void deleteSurroundingText(final int i, final int j) {
+ if (DEBUG_BATCH_NESTING) checkBatchEdit();
+ final int remainingChars = mComposingText.length() - i;
+ if (remainingChars >= 0) {
+ mComposingText.setLength(remainingChars);
+ } else {
+ mComposingText.setLength(0);
+ // Never cut under 0
+ final int len = Math.max(mCommittedTextBeforeComposingText.length()
+ + remainingChars, 0);
+ mCommittedTextBeforeComposingText.setLength(len);
+ }
+ if (mCurrentCursorPosition > i) {
+ mCurrentCursorPosition -= i;
+ } else {
+ mCurrentCursorPosition = 0;
+ }
+ if (null != mIC) {
+ mIC.deleteSurroundingText(i, j);
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.richInputConnection_deleteSurroundingText(i, j);
+ }
+ }
+ if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
+ }
+
+ public void performEditorAction(final int actionId) {
+ mIC = mParent.getCurrentInputConnection();
+ if (null != mIC) {
+ mIC.performEditorAction(actionId);
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.richInputConnection_performEditorAction(actionId);
+ }
+ }
+ }
+
+ public void sendKeyEvent(final KeyEvent keyEvent) {
+ if (DEBUG_BATCH_NESTING) checkBatchEdit();
+ if (keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
+ if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
+ // This method is only called for enter or backspace when speaking to old
+ // applications (target SDK <= 15), or for digits.
+ // When talking to new applications we never use this method because it's inherently
+ // racy and has unpredictable results, but for backward compatibility we continue
+ // sending the key events for only Enter and Backspace because some applications
+ // mistakenly catch them to do some stuff.
+ switch (keyEvent.getKeyCode()) {
+ case KeyEvent.KEYCODE_ENTER:
+ mCommittedTextBeforeComposingText.append("\n");
+ mCurrentCursorPosition += 1;
+ break;
+ case KeyEvent.KEYCODE_DEL:
+ if (0 == mComposingText.length()) {
+ if (mCommittedTextBeforeComposingText.length() > 0) {
+ mCommittedTextBeforeComposingText.delete(
+ mCommittedTextBeforeComposingText.length() - 1,
+ mCommittedTextBeforeComposingText.length());
+ }
+ } else {
+ mComposingText.delete(mComposingText.length() - 1, mComposingText.length());
+ }
+ if (mCurrentCursorPosition > 0) mCurrentCursorPosition -= 1;
+ break;
+ case KeyEvent.KEYCODE_UNKNOWN:
+ if (null != keyEvent.getCharacters()) {
+ mCommittedTextBeforeComposingText.append(keyEvent.getCharacters());
+ mCurrentCursorPosition += keyEvent.getCharacters().length();
+ }
+ break;
+ default:
+ final String text = new String(new int[] { keyEvent.getUnicodeChar() }, 0, 1);
+ mCommittedTextBeforeComposingText.append(text);
+ mCurrentCursorPosition += text.length();
+ break;
+ }
+ }
+ if (null != mIC) {
+ mIC.sendKeyEvent(keyEvent);
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.richInputConnection_sendKeyEvent(keyEvent);
+ }
+ }
+ }
+
+ public void setComposingText(final CharSequence text, final int i) {
+ if (DEBUG_BATCH_NESTING) checkBatchEdit();
+ if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
+ mCurrentCursorPosition += text.length() - mComposingText.length();
+ mComposingText.setLength(0);
+ mComposingText.append(text);
+ // TODO: support values of i != 1. At this time, this is never called with i != 1.
+ if (null != mIC) {
+ mIC.setComposingText(text, i);
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.richInputConnection_setComposingText(text, i);
+ }
+ }
+ if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
+ }
+
+ public void setSelection(final int from, final int to) {
+ if (DEBUG_BATCH_NESTING) checkBatchEdit();
+ if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
+ if (null != mIC) {
+ mIC.setSelection(from, to);
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.richInputConnection_setSelection(from, to);
+ }
+ }
+ mCurrentCursorPosition = from;
+ mCommittedTextBeforeComposingText.setLength(0);
+ mCommittedTextBeforeComposingText.append(getTextBeforeCursor(DEFAULT_TEXT_CACHE_SIZE, 0));
+ }
+
+ public void commitCorrection(final CorrectionInfo correctionInfo) {
+ if (DEBUG_BATCH_NESTING) checkBatchEdit();
+ if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
+ // This has no effect on the text field and does not change its content. It only makes
+ // TextView flash the text for a second based on indices contained in the argument.
+ if (null != mIC) {
+ mIC.commitCorrection(correctionInfo);
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.richInputConnection_commitCorrection(correctionInfo);
+ }
+ }
+ if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
+ }
+
+ public void commitCompletion(final CompletionInfo completionInfo) {
+ if (DEBUG_BATCH_NESTING) checkBatchEdit();
+ if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
+ final CharSequence text = completionInfo.getText();
+ mCommittedTextBeforeComposingText.append(text);
+ mCurrentCursorPosition += text.length() - mComposingText.length();
+ mComposingText.setLength(0);
+ if (null != mIC) {
+ mIC.commitCompletion(completionInfo);
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ ResearchLogger.richInputConnection_commitCompletion(completionInfo);
+ }
+ }
+ if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
+ }
+
+ public CharSequence getNthPreviousWord(final String sentenceSeperators, final int n) {
+ mIC = mParent.getCurrentInputConnection();
+ if (null == mIC) return null;
+ final CharSequence prev = mIC.getTextBeforeCursor(LOOKBACK_CHARACTER_NUM, 0);
+ if (DEBUG_PREVIOUS_TEXT && null != prev) {
+ final int checkLength = LOOKBACK_CHARACTER_NUM - 1;
+ final String reference = prev.length() <= checkLength ? prev.toString()
+ : prev.subSequence(prev.length() - checkLength, prev.length()).toString();
+ final StringBuilder internal = new StringBuilder()
+ .append(mCommittedTextBeforeComposingText).append(mComposingText);
+ if (internal.length() > checkLength) {
+ internal.delete(0, internal.length() - checkLength);
+ if (!(reference.equals(internal.toString()))) {
+ final String context =
+ "Expected text = " + internal + "\nActual text = " + reference;
+ ((LatinIME)mParent).debugDumpStateAndCrashWithException(context);
+ }
+ }
+ }
+ return getNthPreviousWord(prev, sentenceSeperators, n);
+ }
+
+ /**
+ * Represents a range of text, relative to the current cursor position.
+ */
+ public static final class Range {
+ /** Characters before selection start */
+ public final int mCharsBefore;
+
+ /**
+ * Characters after selection start, including one trailing word
+ * separator.
+ */
+ public final int mCharsAfter;
+
+ /** The actual characters that make up a word */
+ public final String mWord;
+
+ public Range(int charsBefore, int charsAfter, String word) {
+ if (charsBefore < 0 || charsAfter < 0) {
+ throw new IndexOutOfBoundsException();
+ }
+ this.mCharsBefore = charsBefore;
+ this.mCharsAfter = charsAfter;
+ this.mWord = word;
+ }
+ }
+
+ private static boolean isSeparator(int code, String sep) {
+ return sep.indexOf(code) != -1;
+ }
+
+ // Get the nth word before cursor. n = 1 retrieves the word immediately before the cursor,
+ // n = 2 retrieves the word before that, and so on. This splits on whitespace only.
+ // Also, it won't return words that end in a separator (if the nth word before the cursor
+ // ends in a separator, it returns null).
+ // Example :
+ // (n = 1) "abc def|" -> def
+ // (n = 1) "abc def |" -> def
+ // (n = 1) "abc def. |" -> null
+ // (n = 1) "abc def . |" -> null
+ // (n = 2) "abc def|" -> abc
+ // (n = 2) "abc def |" -> abc
+ // (n = 2) "abc def. |" -> abc
+ // (n = 2) "abc def . |" -> def
+ // (n = 2) "abc|" -> null
+ // (n = 2) "abc |" -> null
+ // (n = 2) "abc. def|" -> null
+ public static CharSequence getNthPreviousWord(final CharSequence prev,
+ final String sentenceSeperators, final int n) {
+ if (prev == null) return null;
+ String[] w = spaceRegex.split(prev);
+
+ // If we can't find n words, or we found an empty word, return null.
+ if (w.length < n || w[w.length - n].length() <= 0) return null;
+
+ // If ends in a separator, return null
+ char lastChar = w[w.length - n].charAt(w[w.length - n].length() - 1);
+ if (sentenceSeperators.contains(String.valueOf(lastChar))) return null;
+
+ return w[w.length - n];
+ }
+
+ /**
+ * @param separators characters which may separate words
+ * @return the word that surrounds the cursor, including up to one trailing
+ * separator. For example, if the field contains "he|llo world", where |
+ * represents the cursor, then "hello " will be returned.
+ */
+ public String getWordAtCursor(String separators) {
+ // getWordRangeAtCursor returns null if the connection is null
+ Range r = getWordRangeAtCursor(separators, 0);
+ return (r == null) ? null : r.mWord;
+ }
+
+ private int getCursorPosition() {
+ mIC = mParent.getCurrentInputConnection();
+ if (null == mIC) return INVALID_CURSOR_POSITION;
+ final ExtractedText extracted = mIC.getExtractedText(new ExtractedTextRequest(), 0);
+ if (extracted == null) {
+ return INVALID_CURSOR_POSITION;
+ }
+ return extracted.startOffset + extracted.selectionStart;
+ }
+
+ /**
+ * Returns the text surrounding the cursor.
+ *
+ * @param sep a string of characters that split words.
+ * @param additionalPrecedingWordsCount the number of words before the current word that should
+ * be included in the returned range
+ * @return a range containing the text surrounding the cursor
+ */
+ public Range getWordRangeAtCursor(String sep, int additionalPrecedingWordsCount) {
+ mIC = mParent.getCurrentInputConnection();
+ if (mIC == null || sep == null) {
+ return null;
+ }
+ CharSequence before = mIC.getTextBeforeCursor(1000, 0);
+ CharSequence after = mIC.getTextAfterCursor(1000, 0);
+ if (before == null || after == null) {
+ return null;
+ }
+
+ // Going backward, alternate skipping non-separators and separators until enough words
+ // have been read.
+ int start = before.length();
+ boolean isStoppingAtWhitespace = true; // toggles to indicate what to stop at
+ while (true) { // see comments below for why this is guaranteed to halt
+ while (start > 0) {
+ final int codePoint = Character.codePointBefore(before, start);
+ if (isStoppingAtWhitespace == isSeparator(codePoint, sep)) {
+ break; // inner loop
+ }
+ --start;
+ if (Character.isSupplementaryCodePoint(codePoint)) {
+ --start;
+ }
+ }
+ // isStoppingAtWhitespace is true every other time through the loop,
+ // so additionalPrecedingWordsCount is guaranteed to become < 0, which
+ // guarantees outer loop termination
+ if (isStoppingAtWhitespace && (--additionalPrecedingWordsCount < 0)) {
+ break; // outer loop
+ }
+ isStoppingAtWhitespace = !isStoppingAtWhitespace;
+ }
+
+ // Find last word separator after the cursor
+ int end = -1;
+ while (++end < after.length()) {
+ final int codePoint = Character.codePointAt(after, end);
+ if (isSeparator(codePoint, sep)) {
+ break;
+ }
+ if (Character.isSupplementaryCodePoint(codePoint)) {
+ ++end;
+ }
+ }
+
+ int cursor = getCursorPosition();
+ if (start >= 0 && cursor + end <= after.length() + before.length()) {
+ String word = before.toString().substring(start, before.length())
+ + after.toString().substring(0, end);
+ return new Range(before.length() - start, end, word);
+ }
+
+ return null;
+ }
+
+ public boolean isCursorTouchingWord(final SettingsValues settingsValues) {
+ CharSequence before = getTextBeforeCursor(1, 0);
+ CharSequence after = getTextAfterCursor(1, 0);
+ if (!TextUtils.isEmpty(before) && !settingsValues.isWordSeparator(before.charAt(0))
+ && !settingsValues.isSymbolExcludedFromWordSeparators(before.charAt(0))) {
+ return true;
+ }
+ if (!TextUtils.isEmpty(after) && !settingsValues.isWordSeparator(after.charAt(0))
+ && !settingsValues.isSymbolExcludedFromWordSeparators(after.charAt(0))) {
+ return true;
+ }
+ return false;
+ }
+
+ public void removeTrailingSpace() {
+ if (DEBUG_BATCH_NESTING) checkBatchEdit();
+ final CharSequence lastOne = getTextBeforeCursor(1, 0);
+ if (lastOne != null && lastOne.length() == 1
+ && lastOne.charAt(0) == Keyboard.CODE_SPACE) {
+ deleteSurroundingText(1, 0);
+ }
+ }
+
+ public boolean sameAsTextBeforeCursor(final CharSequence text) {
+ final CharSequence beforeText = getTextBeforeCursor(text.length(), 0);
+ return TextUtils.equals(text, beforeText);
+ }
+
+ /* (non-javadoc)
+ * Returns the word before the cursor if the cursor is at the end of a word, null otherwise
+ */
+ public CharSequence getWordBeforeCursorIfAtEndOfWord(final SettingsValues settings) {
+ // Bail out if the cursor is in the middle of a word (cursor must be followed by whitespace,
+ // separator or end of line/text)
+ // Example: "test|"<EOL> "te|st" get rejected here
+ final CharSequence textAfterCursor = getTextAfterCursor(1, 0);
+ if (!TextUtils.isEmpty(textAfterCursor)
+ && !settings.isWordSeparator(textAfterCursor.charAt(0))) return null;
+
+ // Bail out if word before cursor is 0-length or a single non letter (like an apostrophe)
+ // Example: " -|" gets rejected here but "e-|" and "e|" are okay
+ CharSequence word = getWordAtCursor(settings.mWordSeparators);
+ // We don't suggest on leading single quotes, so we have to remove them from the word if
+ // it starts with single quotes.
+ while (!TextUtils.isEmpty(word) && Keyboard.CODE_SINGLE_QUOTE == word.charAt(0)) {
+ word = word.subSequence(1, word.length());
+ }
+ if (TextUtils.isEmpty(word)) return null;
+ // Find the last code point of the string
+ final int lastCodePoint = Character.codePointBefore(word, word.length());
+ // If for some reason the text field contains non-unicode binary data, or if the
+ // charsequence is exactly one char long and the contents is a low surrogate, return null.
+ if (!Character.isDefined(lastCodePoint)) return null;
+ // Bail out if the cursor is not at the end of a word (cursor must be preceded by
+ // non-whitespace, non-separator, non-start-of-text)
+ // Example ("|" is the cursor here) : <SOL>"|a" " |a" " | " all get rejected here.
+ if (settings.isWordSeparator(lastCodePoint)) return null;
+ final char firstChar = word.charAt(0); // we just tested that word is not empty
+ if (word.length() == 1 && !Character.isLetter(firstChar)) return null;
+
+ // We only suggest on words that start with a letter or a symbol that is excluded from
+ // word separators (see #handleCharacterWhileInBatchEdit).
+ if (!(Character.isLetter(firstChar)
+ || settings.isSymbolExcludedFromWordSeparators(firstChar))) {
+ return null;
+ }
+
+ return word;
+ }
+
+ public boolean revertDoubleSpace() {
+ if (DEBUG_BATCH_NESTING) checkBatchEdit();
+ // Here we test whether we indeed have a period and a space before us. This should not
+ // be needed, but it's there just in case something went wrong.
+ final CharSequence textBeforeCursor = getTextBeforeCursor(2, 0);
+ if (!". ".equals(textBeforeCursor)) {
+ // Theoretically we should not be coming here if there isn't ". " before the
+ // cursor, but the application may be changing the text while we are typing, so
+ // anything goes. We should not crash.
+ Log.d(TAG, "Tried to revert double-space combo but we didn't find "
+ + "\". \" just before the cursor.");
+ return false;
+ }
+ deleteSurroundingText(2, 0);
+ commitText(" ", 1);
+ return true;
+ }
+
+ public boolean revertSwapPunctuation() {
+ if (DEBUG_BATCH_NESTING) checkBatchEdit();
+ // Here we test whether we indeed have a space and something else before us. This should not
+ // be needed, but it's there just in case something went wrong.
+ final CharSequence textBeforeCursor = getTextBeforeCursor(2, 0);
+ // NOTE: This does not work with surrogate pairs. Hopefully when the keyboard is able to
+ // enter surrogate pairs this code will have been removed.
+ if (TextUtils.isEmpty(textBeforeCursor)
+ || (Keyboard.CODE_SPACE != textBeforeCursor.charAt(1))) {
+ // We may only come here if the application is changing the text while we are typing.
+ // This is quite a broken case, but not logically impossible, so we shouldn't crash,
+ // but some debugging log may be in order.
+ Log.d(TAG, "Tried to revert a swap of punctuation but we didn't "
+ + "find a space just before the cursor.");
+ return false;
+ }
+ deleteSurroundingText(2, 0);
+ commitText(" " + textBeforeCursor.subSequence(0, 1), 1);
+ return true;
+ }
+
+ /**
+ * Heuristic to determine if this is an expected update of the cursor.
+ *
+ * Sometimes updates to the cursor position are late because of their asynchronous nature.
+ * This method tries to determine if this update is one, based on the values of the cursor
+ * position in the update, and the currently expected position of the cursor according to
+ * LatinIME's internal accounting. If this is not a belated expected update, then it should
+ * mean that the user moved the cursor explicitly.
+ * This is quite robust, but of course it's not perfect. In particular, it will fail in the
+ * case we get an update A, the user types in N characters so as to move the cursor to A+N but
+ * we don't get those, and then the user places the cursor between A and A+N, and we get only
+ * this update and not the ones in-between. This is almost impossible to achieve even trying
+ * very very hard.
+ *
+ * @param oldSelStart The value of the old cursor position in the update.
+ * @param newSelStart The value of the new cursor position in the update.
+ * @return whether this is a belated expected update or not.
+ */
+ public boolean isBelatedExpectedUpdate(final int oldSelStart, final int newSelStart) {
+ // If this is an update that arrives at our expected position, it's a belated update.
+ if (newSelStart == mCurrentCursorPosition) return true;
+ // If this is an update that moves the cursor from our expected position, it must be
+ // an explicit move.
+ if (oldSelStart == mCurrentCursorPosition) return false;
+ // The following returns true if newSelStart is between oldSelStart and
+ // mCurrentCursorPosition. We assume that if the updated position is between the old
+ // position and the expected position, then it must be a belated update.
+ return (newSelStart - oldSelStart) * (mCurrentCursorPosition - newSelStart) >= 0;
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java
index 4bb21720b..238724610 100644
--- a/java/src/com/android/inputmethod/latin/Settings.java
+++ b/java/src/com/android/inputmethod/latin/Settings.java
@@ -41,9 +41,8 @@ import android.widget.TextView;
import com.android.inputmethod.latin.define.ProductionFlag;
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;
// In the same order as xml/prefs.xml
public static final String PREF_GENERAL_SETTINGS = "general_settings";
@@ -57,25 +56,26 @@ public class Settings extends InputMethodSettingsFragment
public static final String PREF_AUTO_CORRECTION_THRESHOLD = "auto_correction_threshold";
public static final String PREF_SHOW_SUGGESTIONS_SETTING = "show_suggestions_setting";
public static final String PREF_MISC_SETTINGS = "misc_settings";
- public static final String PREF_USABILITY_STUDY_MODE = "usability_study_mode";
public static final String PREF_LAST_USER_DICTIONARY_WRITE_TIME =
"last_user_dictionary_write_time";
public static final String PREF_ADVANCED_SETTINGS = "pref_advanced_settings";
- public static final String PREF_SUPPRESS_LANGUAGE_SWITCH_KEY =
- "pref_suppress_language_switch_key";
+ public static final String PREF_KEY_USE_CONTACTS_DICT = "pref_key_use_contacts_dict";
+ 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";
public static final String PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY =
"pref_key_preview_popup_dismiss_delay";
- public static final String PREF_KEY_USE_CONTACTS_DICT = "pref_key_use_contacts_dict";
- public static final String PREF_BIGRAM_SUGGESTION = "next_word_suggestion";
public static final String PREF_BIGRAM_PREDICTIONS = "next_word_prediction";
- public static final String PREF_KEY_ENABLE_SPAN_INSERT = "enable_span_insert";
+ public static final String PREF_GESTURE_INPUT = "gesture_input";
public static final String PREF_VIBRATION_DURATION_SETTINGS =
"pref_vibration_duration_settings";
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_INPUT_LANGUAGE = "input_language";
public static final String PREF_SELECTED_LANGUAGES = "selected_languages";
@@ -87,27 +87,28 @@ public class Settings extends InputMethodSettingsFragment
private ListPreference mShowCorrectionSuggestionsPreference;
private ListPreference mAutoCorrectionThresholdPreference;
private ListPreference mKeyPreviewPopupDismissDelay;
- // Suggestion: use bigrams to adjust scores of suggestions obtained from unigram dictionary
- private CheckBoxPreference mBigramSuggestion;
- // Prediction: use bigrams to predict the next word when there is no input for it yet
+ // Use bigrams to predict the next word when there is no input for it yet
private CheckBoxPreference mBigramPrediction;
private Preference mDebugSettingsPreference;
private TextView mKeypressVibrationDurationSettingsTextView;
private TextView mKeypressSoundVolumeSettingsTextView;
+ private static void setPreferenceEnabled(final Preference preference, final boolean enabled) {
+ if (preference != null) {
+ preference.setEnabled(enabled);
+ }
+ }
+
private void ensureConsistencyOfAutoCorrectionSettings() {
final String autoCorrectionOff = getResources().getString(
R.string.auto_correction_threshold_mode_index_off);
final String currentSetting = mAutoCorrectionThresholdPreference.getValue();
- mBigramSuggestion.setEnabled(!currentSetting.equals(autoCorrectionOff));
- if (null != mBigramPrediction) {
- mBigramPrediction.setEnabled(!currentSetting.equals(autoCorrectionOff));
- }
+ setPreferenceEnabled(mBigramPrediction, !currentSetting.equals(autoCorrectionOff));
}
@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);
@@ -128,16 +129,7 @@ public class Settings extends InputMethodSettingsFragment
mAutoCorrectionThresholdPreference =
(ListPreference) findPreference(PREF_AUTO_CORRECTION_THRESHOLD);
- mBigramSuggestion = (CheckBoxPreference) findPreference(PREF_BIGRAM_SUGGESTION);
mBigramPrediction = (CheckBoxPreference) findPreference(PREF_BIGRAM_PREDICTIONS);
- mDebugSettingsPreference = findPreference(PREF_DEBUG_SETTINGS);
- if (mDebugSettingsPreference != null) {
- final Intent debugSettingsIntent = new Intent(Intent.ACTION_MAIN);
- debugSettingsIntent.setClassName(
- context.getPackageName(), DebugSettings.class.getName());
- mDebugSettingsPreference.setIntent(debugSettingsIntent);
- }
-
ensureConsistencyOfAutoCorrectionSettings();
final PreferenceGroup generalSettings =
@@ -147,6 +139,18 @@ public class Settings extends InputMethodSettingsFragment
final PreferenceGroup miscSettings =
(PreferenceGroup) findPreference(PREF_MISC_SETTINGS);
+ mDebugSettingsPreference = findPreference(PREF_DEBUG_SETTINGS);
+ if (mDebugSettingsPreference != null) {
+ if (ProductionFlag.IS_INTERNAL) {
+ final Intent debugSettingsIntent = new Intent(Intent.ACTION_MAIN);
+ debugSettingsIntent.setClassName(
+ context.getPackageName(), DebugSettingsActivity.class.getName());
+ mDebugSettingsPreference.setIntent(debugSettingsIntent);
+ } else {
+ miscSettings.removePreference(mDebugSettingsPreference);
+ }
+ }
+
final boolean showVoiceKeyOption = res.getBoolean(
R.bool.config_enable_show_voice_key_option);
if (!showVoiceKeyOption) {
@@ -155,9 +159,6 @@ public class Settings extends InputMethodSettingsFragment
final PreferenceGroup advancedSettings =
(PreferenceGroup) findPreference(PREF_ADVANCED_SETTINGS);
- // Remove those meaningless options for now. TODO: delete them for good
- advancedSettings.removePreference(findPreference(PREF_BIGRAM_SUGGESTION));
- advancedSettings.removePreference(findPreference(PREF_KEY_ENABLE_SPAN_INSERT));
if (!VibratorUtils.getInstance(context).hasVibrator()) {
generalSettings.removePreference(findPreference(PREF_VIBRATE_ON));
if (null != advancedSettings) { // Theoretically advancedSettings cannot be null
@@ -165,42 +166,34 @@ public class Settings extends InputMethodSettingsFragment
}
}
- final boolean showPopupOption = res.getBoolean(
+ final boolean showKeyPreviewPopupOption = res.getBoolean(
R.bool.config_enable_show_popup_on_keypress_option);
- if (!showPopupOption) {
+ mKeyPreviewPopupDismissDelay =
+ (ListPreference) findPreference(PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY);
+ if (!showKeyPreviewPopupOption) {
generalSettings.removePreference(findPreference(PREF_POPUP_ON));
- }
-
- final boolean showBigramSuggestionsOption = res.getBoolean(
- R.bool.config_enable_next_word_suggestions_option);
- if (!showBigramSuggestionsOption) {
- textCorrectionGroup.removePreference(mBigramSuggestion);
- if (null != mBigramPrediction) {
- textCorrectionGroup.removePreference(mBigramPrediction);
+ if (null != advancedSettings) { // Theoretically advancedSettings cannot be null
+ advancedSettings.removePreference(mKeyPreviewPopupDismissDelay);
+ }
+ } else {
+ final String[] entries = new String[] {
+ res.getString(R.string.key_preview_popup_dismiss_no_delay),
+ res.getString(R.string.key_preview_popup_dismiss_default_delay),
+ };
+ final String popupDismissDelayDefaultValue = Integer.toString(res.getInteger(
+ R.integer.config_key_preview_linger_timeout));
+ mKeyPreviewPopupDismissDelay.setEntries(entries);
+ mKeyPreviewPopupDismissDelay.setEntryValues(
+ new String[] { "0", popupDismissDelayDefaultValue });
+ if (null == mKeyPreviewPopupDismissDelay.getValue()) {
+ mKeyPreviewPopupDismissDelay.setValue(popupDismissDelayDefaultValue);
}
+ setPreferenceEnabled(mKeyPreviewPopupDismissDelay,
+ SettingsValues.isKeyPreviewPopupEnabled(prefs, res));
}
- final CheckBoxPreference includeOtherImesInLanguageSwitchList =
- (CheckBoxPreference)findPreference(PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST);
- includeOtherImesInLanguageSwitchList.setEnabled(
- !SettingsValues.isLanguageSwitchKeySupressed(prefs));
-
- mKeyPreviewPopupDismissDelay =
- (ListPreference)findPreference(PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY);
- final String[] entries = new String[] {
- res.getString(R.string.key_preview_popup_dismiss_no_delay),
- res.getString(R.string.key_preview_popup_dismiss_default_delay),
- };
- final String popupDismissDelayDefaultValue = Integer.toString(res.getInteger(
- R.integer.config_key_preview_linger_timeout));
- mKeyPreviewPopupDismissDelay.setEntries(entries);
- mKeyPreviewPopupDismissDelay.setEntryValues(
- new String[] { "0", popupDismissDelayDefaultValue });
- if (null == mKeyPreviewPopupDismissDelay.getValue()) {
- mKeyPreviewPopupDismissDelay.setValue(popupDismissDelayDefaultValue);
- }
- mKeyPreviewPopupDismissDelay.setEnabled(
- SettingsValues.isKeyPreviewPopupEnabled(prefs, res));
+ setPreferenceEnabled(findPreference(PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST),
+ SettingsValues.showsLanguageSwitchKey(prefs));
final PreferenceScreen dictionaryLink =
(PreferenceScreen) findPreference(PREF_CONFIGURE_DICTIONARIES_KEY);
@@ -211,21 +204,19 @@ public class Settings extends InputMethodSettingsFragment
textCorrectionGroup.removePreference(dictionaryLink);
}
- final boolean showUsabilityStudyModeOption =
- res.getBoolean(R.bool.config_enable_usability_study_mode_option)
- || ProductionFlag.IS_EXPERIMENTAL || ENABLE_EXPERIMENTAL_SETTINGS;
- final Preference usabilityStudyPref = findPreference(PREF_USABILITY_STUDY_MODE);
- if (!showUsabilityStudyModeOption) {
- if (usabilityStudyPref != null) {
- miscSettings.removePreference(usabilityStudyPref);
- }
- }
- if (ProductionFlag.IS_EXPERIMENTAL) {
- if (usabilityStudyPref instanceof CheckBoxPreference) {
- CheckBoxPreference checkbox = (CheckBoxPreference)usabilityStudyPref;
- checkbox.setChecked(prefs.getBoolean(PREF_USABILITY_STUDY_MODE, true));
- checkbox.setSummary(R.string.settings_warning_researcher_mode);
- }
+ final boolean gestureInputEnabledByBuildConfig = res.getBoolean(
+ 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);
+ if (!gestureInputEnabledByBuildConfig) {
+ miscSettings.removePreference(findPreference(PREF_GESTURE_INPUT));
+ miscSettings.removePreference(gesturePreviewTrail);
+ miscSettings.removePreference(gestureFloatingPreviewText);
+ } else {
+ final boolean gestureInputEnabledByUser = prefs.getBoolean(PREF_GESTURE_INPUT, true);
+ setPreferenceEnabled(gesturePreviewTrail, gestureInputEnabledByUser);
+ setPreferenceEnabled(gestureFloatingPreviewText, gestureInputEnabledByUser);
}
mKeypressVibrationDurationSettingsPref =
@@ -280,20 +271,25 @@ 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)) {
- final ListPreference popupDismissDelay =
- (ListPreference)findPreference(PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY);
- if (null != popupDismissDelay) {
- popupDismissDelay.setEnabled(prefs.getBoolean(PREF_POPUP_ON, true));
+ setPreferenceEnabled(findPreference(PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY),
+ prefs.getBoolean(PREF_POPUP_ON, true));
+ } else if (key.equals(PREF_SHOW_LANGUAGE_SWITCH_KEY)) {
+ setPreferenceEnabled(findPreference(PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST),
+ SettingsValues.showsLanguageSwitchKey(prefs));
+ } else if (key.equals(PREF_GESTURE_INPUT)) {
+ final boolean gestureInputEnabledByConfig = getResources().getBoolean(
+ R.bool.config_gesture_input_enabled_by_build_config);
+ if (gestureInputEnabledByConfig) {
+ final boolean gestureInputEnabledByUser = prefs.getBoolean(
+ PREF_GESTURE_INPUT, true);
+ setPreferenceEnabled(findPreference(PREF_GESTURE_PREVIEW_TRAIL),
+ gestureInputEnabledByUser);
+ setPreferenceEnabled(findPreference(PREF_GESTURE_FLOATING_PREVIEW_TEXT),
+ gestureInputEnabledByUser);
}
- } else if (key.equals(PREF_SUPPRESS_LANGUAGE_SWITCH_KEY)) {
- final CheckBoxPreference includeOtherImesInLanguageSwicthList =
- (CheckBoxPreference)findPreference(
- PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST);
- includeOtherImesInLanguageSwicthList.setEnabled(
- !SettingsValues.isLanguageSwitchKeySupressed(prefs));
}
ensureConsistencyOfAutoCorrectionSettings();
updateVoiceModeSummary();
@@ -327,33 +323,37 @@ public class Settings extends InputMethodSettingsFragment
private void updateKeyPreviewPopupDelaySummary() {
final ListPreference lp = mKeyPreviewPopupDismissDelay;
- lp.setSummary(lp.getEntries()[lp.findIndexOfValue(lp.getValue())]);
+ final CharSequence[] entries = lp.getEntries();
+ if (entries == null || entries.length <= 0) return;
+ lp.setSummary(entries[lp.findIndexOfValue(lp.getValue())]);
}
private void updateVoiceModeSummary() {
mVoicePreference.setSummary(
getResources().getStringArray(R.array.voice_input_modes_summary)
- [mVoicePreference.findIndexOfValue(mVoicePreference.getValue())]);
+ [mVoicePreference.findIndexOfValue(mVoicePreference.getValue())]);
}
private void refreshEnablingsOfKeypressSoundAndVibrationSettings(
- SharedPreferences sp, Resources res) {
+ final SharedPreferences sp, final Resources res) {
if (mKeypressVibrationDurationSettingsPref != null) {
- final boolean hasVibrator = VibratorUtils.getInstance(getActivity()).hasVibrator();
- final boolean vibrateOn = hasVibrator && sp.getBoolean(Settings.PREF_VIBRATE_ON,
+ final boolean hasVibratorHardware = VibratorUtils.getInstance(getActivity())
+ .hasVibrator();
+ final boolean vibrateOnByUser = sp.getBoolean(Settings.PREF_VIBRATE_ON,
res.getBoolean(R.bool.config_default_vibration_enabled));
- mKeypressVibrationDurationSettingsPref.setEnabled(vibrateOn);
+ setPreferenceEnabled(mKeypressVibrationDurationSettingsPref,
+ hasVibratorHardware && vibrateOnByUser);
}
if (mKeypressSoundVolumeSettingsPref != null) {
final boolean soundOn = sp.getBoolean(Settings.PREF_SOUND_ON,
res.getBoolean(R.bool.config_default_sound_enabled));
- mKeypressSoundVolumeSettingsPref.setEnabled(soundOn);
+ setPreferenceEnabled(mKeypressSoundVolumeSettingsPref, soundOn);
}
}
private void updateKeypressVibrationDurationSettingsSummary(
- SharedPreferences sp, Resources res) {
+ final SharedPreferences sp, final Resources res) {
if (mKeypressVibrationDurationSettingsPref != null) {
mKeypressVibrationDurationSettingsPref.setSummary(
SettingsValues.getCurrentVibrationDuration(sp, res)
@@ -411,7 +411,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)));
diff --git a/java/src/com/android/inputmethod/latin/SettingsActivity.java b/java/src/com/android/inputmethod/latin/SettingsActivity.java
index 68f8582fc..0d3c8ebb7 100644
--- a/java/src/com/android/inputmethod/latin/SettingsActivity.java
+++ b/java/src/com/android/inputmethod/latin/SettingsActivity.java
@@ -19,7 +19,7 @@ package com.android.inputmethod.latin;
import android.content.Intent;
import android.preference.PreferenceActivity;
-public class SettingsActivity extends PreferenceActivity {
+public final class SettingsActivity extends PreferenceActivity {
private static final String DEFAULT_FRAGMENT = Settings.class.getName();
@Override
diff --git a/java/src/com/android/inputmethod/latin/SettingsValues.java b/java/src/com/android/inputmethod/latin/SettingsValues.java
index b07c3e59f..2a778aa0d 100644
--- a/java/src/com/android/inputmethod/latin/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/SettingsValues.java
@@ -18,6 +18,7 @@ package com.android.inputmethod.latin;
import android.content.Context;
import android.content.SharedPreferences;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.util.Log;
import android.view.inputmethod.EditorInfo;
@@ -29,15 +30,27 @@ import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
-import java.util.Map;
/**
* When you call the constructor of this class, you may want to change the current system locale by
* using {@link LocaleUtils.RunInLocale}.
*/
-public class SettingsValues {
+public final class SettingsValues {
private static final String TAG = SettingsValues.class.getSimpleName();
+ private static final int SUGGESTION_VISIBILITY_SHOW_VALUE
+ = R.string.prefs_suggestion_visibility_show_value;
+ private static final int SUGGESTION_VISIBILITY_SHOW_ONLY_PORTRAIT_VALUE
+ = R.string.prefs_suggestion_visibility_show_only_portrait_value;
+ private static final int SUGGESTION_VISIBILITY_HIDE_VALUE
+ = R.string.prefs_suggestion_visibility_hide_value;
+
+ private static final int[] SUGGESTION_VISIBILITY_VALUE_ARRAY = new int[] {
+ SUGGESTION_VISIBILITY_SHOW_VALUE,
+ SUGGESTION_VISIBILITY_SHOW_ONLY_PORTRAIT_VALUE,
+ SUGGESTION_VISIBILITY_HIDE_VALUE
+ };
+
// From resources:
public final int mDelayUpdateOldSuggestions;
public final String mWeakSpaceStrippers;
@@ -59,31 +72,37 @@ public 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;
- // Suggestion: use bigrams to adjust scores of suggestions obtained from unigram dictionary
- public final boolean mBigramSuggestionEnabled;
- // Prediction: use bigrams to predict the next word when there is no input for it yet
+ // Use bigrams to predict the next word when there is no input for it yet
public final boolean mBigramPredictionEnabled;
- public final boolean mEnableSuggestionSpanInsertion;
@SuppressWarnings("unused") // TODO: Use this
private final int mVibrationDurationSettingsRawValue;
@SuppressWarnings("unused") // TODO: Use this
private final float mKeypressSoundVolumeRawValue;
private final InputMethodSubtype[] mAdditionalSubtypes;
+ public final boolean mGestureInputEnabled;
+ public final boolean mGesturePreviewTrailEnabled;
+ public final boolean mGestureFloatingPreviewTextEnabled;
+
+ // From the input box
+ private final InputAttributes mInputAttributes;
// Deduced settings
public final int mKeypressVibrationDuration;
public final float mFxVolume;
public final int mKeyPreviewPopupDismissDelay;
- public final boolean mAutoCorrectEnabled;
+ private final boolean mAutoCorrectEnabled;
public final float mAutoCorrectionThreshold;
+ public final boolean mCorrectionEnabled;
+ public final int mSuggestionVisibility;
private final boolean mVoiceKeyEnabled;
private final boolean mVoiceKeyOnMain;
- public SettingsValues(final SharedPreferences prefs, final Context context) {
+ public SettingsValues(final SharedPreferences prefs, final InputAttributes inputAttributes,
+ final Context context) {
final Resources res = context.getResources();
// Get the resources
@@ -109,6 +128,13 @@ public class SettingsValues {
mSymbolsExcludedFromWordSeparators, res);
mHintToSaveText = context.getText(R.string.hint_add_to_dictionary);
+ // Store the input attributes
+ if (null == inputAttributes) {
+ mInputAttributes = new InputAttributes(null, false /* isFullscreenMode */);
+ } else {
+ mInputAttributes = inputAttributes;
+ }
+
// Get the settings preferences
mAutoCap = prefs.getBoolean(Settings.PREF_AUTO_CAP, true);
mVibrateOn = isVibrateOn(context, prefs, res);
@@ -125,18 +151,13 @@ public 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)));
mUseContactsDict = prefs.getBoolean(Settings.PREF_KEY_USE_CONTACTS_DICT, true);
mAutoCorrectEnabled = isAutoCorrectEnabled(res, mAutoCorrectionThresholdRawValue);
- mBigramSuggestionEnabled = mAutoCorrectEnabled
- && isBigramSuggestionEnabled(prefs, res, mAutoCorrectEnabled);
- mBigramPredictionEnabled = mBigramSuggestionEnabled
- && isBigramPredictionEnabled(prefs, res);
- // TODO: remove mEnableSuggestionSpanInsertion. It's always true.
- mEnableSuggestionSpanInsertion = true;
+ mBigramPredictionEnabled = isBigramPredictionEnabled(prefs, res);
mVibrationDurationSettingsRawValue =
prefs.getInt(Settings.PREF_VIBRATION_DURATION_SETTINGS, -1);
mKeypressSoundVolumeRawValue = prefs.getFloat(Settings.PREF_KEYPRESS_SOUND_VOLUME, -1.0f);
@@ -151,22 +172,30 @@ public class SettingsValues {
mVoiceKeyOnMain = mVoiceMode != null && mVoiceMode.equals(voiceModeMain);
mAdditionalSubtypes = AdditionalSubtype.createAdditionalSubtypesArray(
getPrefAdditionalSubtypes(prefs, res));
+ final boolean gestureInputEnabledByBuildConfig = res.getBoolean(
+ R.bool.config_gesture_input_enabled_by_build_config);
+ mGestureInputEnabled = gestureInputEnabledByBuildConfig
+ && 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);
+ mCorrectionEnabled = mAutoCorrectEnabled && !mInputAttributes.mInputTypeNoAutoCorrect;
+ mSuggestionVisibility = createSuggestionVisibility(res);
}
// Helper functions to create member values.
private static SuggestedWords createSuggestPuncList(final String[] puncs) {
- final ArrayList<SuggestedWords.SuggestedWordInfo> puncList =
- new ArrayList<SuggestedWords.SuggestedWordInfo>();
+ final ArrayList<SuggestedWordInfo> puncList = CollectionUtils.newArrayList();
if (puncs != null) {
for (final String puncSpec : puncs) {
- puncList.add(new SuggestedWords.SuggestedWordInfo(
- KeySpecParser.getLabel(puncSpec), SuggestedWordInfo.MAX_SCORE));
+ puncList.add(new SuggestedWordInfo(KeySpecParser.getLabel(puncSpec),
+ SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_HARDCODED,
+ Dictionary.TYPE_HARDCODED));
}
}
return new SuggestedWords(puncList,
false /* typedWordValid */,
false /* hasAutoCorrectionCandidate */,
- false /* allowsToBeAutoCorrected */,
true /* isPunctuationSuggestions */,
false /* isObsoleteSuggestions */,
false /* isPrediction */);
@@ -184,6 +213,16 @@ public class SettingsValues {
return wordSeparators;
}
+ private int createSuggestionVisibility(final Resources res) {
+ final String suggestionVisiblityStr = mShowSuggestionsSetting;
+ for (int visibility : SUGGESTION_VISIBILITY_VALUE_ARRAY) {
+ if (suggestionVisiblityStr.equals(res.getString(visibility))) {
+ return visibility;
+ }
+ }
+ throw new RuntimeException("Bug: visibility string is not configured correctly");
+ }
+
private static boolean isVibrateOn(final Context context, final SharedPreferences prefs,
final Resources res) {
final boolean hasVibrator = VibratorUtils.getInstance(context).hasVibrator();
@@ -191,70 +230,83 @@ public class SettingsValues {
res.getBoolean(R.bool.config_default_vibration_enabled));
}
- public boolean isWordSeparator(int code) {
+ public boolean isApplicationSpecifiedCompletionsOn() {
+ return mInputAttributes.mApplicationSpecifiedCompletionOn;
+ }
+
+ public boolean isSuggestionsRequested(final int displayOrientation) {
+ return mInputAttributes.mIsSettingsSuggestionStripOn
+ && (mCorrectionEnabled
+ || isSuggestionStripVisibleInOrientation(displayOrientation));
+ }
+
+ public boolean isSuggestionStripVisibleInOrientation(final int orientation) {
+ return (mSuggestionVisibility == SUGGESTION_VISIBILITY_SHOW_VALUE)
+ || (mSuggestionVisibility == SUGGESTION_VISIBILITY_SHOW_ONLY_PORTRAIT_VALUE
+ && orientation == Configuration.ORIENTATION_PORTRAIT);
+ }
+
+ public boolean isWordSeparator(final int code) {
return mWordSeparators.contains(String.valueOf((char)code));
}
- public boolean isSymbolExcludedFromWordSeparators(int code) {
+ public boolean isSymbolExcludedFromWordSeparators(final int code) {
return mSymbolsExcludedFromWordSeparators.contains(String.valueOf((char)code));
}
- public boolean isWeakSpaceStripper(int code) {
+ // TODO: use "Phantom" instead of "Weak" in this method name
+ public boolean isWeakSpaceStripper(final int code) {
// TODO: this does not work if the code does not fit in a char
return mWeakSpaceStrippers.contains(String.valueOf((char)code));
}
- public boolean isWeakSpaceSwapper(int code) {
+ // TODO: use "Phantom" instead of "Weak" in this method name
+ public boolean isWeakSpaceSwapper(final int code) {
// TODO: this does not work if the code does not fit in a char
return mWeakSpaceSwappers.contains(String.valueOf((char)code));
}
- public boolean isPhantomSpacePromotingSymbol(int code) {
+ public boolean isPhantomSpacePromotingSymbol(final int code) {
// TODO: this does not work if the code does not fit in a char
return mPhantomSpacePromotingSymbols.contains(String.valueOf((char)code));
}
- private static boolean isAutoCorrectEnabled(final Resources resources,
+ private static boolean isAutoCorrectEnabled(final Resources res,
final String currentAutoCorrectionSetting) {
- final String autoCorrectionOff = resources.getString(
+ final String autoCorrectionOff = res.getString(
R.string.auto_correction_threshold_mode_index_off);
return !currentAutoCorrectionSetting.equals(autoCorrectionOff);
}
// Public to access from KeyboardSwitcher. Should it have access to some
// process-global instance instead?
- public static boolean isKeyPreviewPopupEnabled(SharedPreferences sp, Resources resources) {
- final boolean showPopupOption = resources.getBoolean(
+ public static boolean isKeyPreviewPopupEnabled(final SharedPreferences prefs,
+ final Resources res) {
+ final boolean showPopupOption = res.getBoolean(
R.bool.config_enable_show_popup_on_keypress_option);
- if (!showPopupOption) return resources.getBoolean(R.bool.config_default_popup_preview);
- return sp.getBoolean(Settings.PREF_POPUP_ON,
- resources.getBoolean(R.bool.config_default_popup_preview));
+ if (!showPopupOption) return res.getBoolean(R.bool.config_default_popup_preview);
+ return prefs.getBoolean(Settings.PREF_POPUP_ON,
+ res.getBoolean(R.bool.config_default_popup_preview));
}
// Likewise
- public static int getKeyPreviewPopupDismissDelay(SharedPreferences sp,
- Resources resources) {
+ public static int getKeyPreviewPopupDismissDelay(final SharedPreferences prefs,
+ final Resources res) {
// TODO: use mKeyPreviewPopupDismissDelayRawValue instead of reading it again here.
- return Integer.parseInt(sp.getString(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY,
- Integer.toString(resources.getInteger(
+ return Integer.parseInt(prefs.getString(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY,
+ Integer.toString(res.getInteger(
R.integer.config_key_preview_linger_timeout))));
}
- private static boolean isBigramSuggestionEnabled(final SharedPreferences sp,
- final Resources resources, final boolean autoCorrectEnabled) {
- // TODO: remove this method. Bigram suggestion is always true.
- return true;
- }
-
- private static boolean isBigramPredictionEnabled(final SharedPreferences sp,
- final Resources resources) {
- return sp.getBoolean(Settings.PREF_BIGRAM_PREDICTIONS, resources.getBoolean(
+ private static boolean isBigramPredictionEnabled(final SharedPreferences prefs,
+ final Resources res) {
+ return prefs.getBoolean(Settings.PREF_BIGRAM_PREDICTIONS, res.getBoolean(
R.bool.config_default_next_word_prediction));
}
- private static float getAutoCorrectionThreshold(final Resources resources,
+ private static float getAutoCorrectionThreshold(final Resources res,
final String currentAutoCorrectionSetting) {
- final String[] autoCorrectionThresholdValues = resources.getStringArray(
+ final String[] autoCorrectionThresholdValues = res.getStringArray(
R.array.auto_correction_threshold_values);
// When autoCorrectionThreshold is greater than 1.0, it's like auto correction is off.
float autoCorrectionThreshold = Float.MAX_VALUE;
@@ -286,12 +338,25 @@ public class SettingsValues {
return mVoiceKeyOnMain;
}
- public static boolean isLanguageSwitchKeySupressed(SharedPreferences sp) {
- return sp.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(Context context) {
- if (mIsLanguageSwitchKeySuppressed) {
+ public boolean isLanguageSwitchKeyEnabled(final Context context) {
+ if (!mShowsLanguageSwitchKey) {
return false;
}
if (mIncludesOtherImesInLanguageSwitchList) {
@@ -303,7 +368,7 @@ public class SettingsValues {
}
}
- public boolean isFullscreenModeAllowed(Resources res) {
+ public static boolean isFullscreenModeAllowed(final Resources res) {
return res.getBoolean(R.bool.config_use_fullscreen_mode);
}
@@ -313,58 +378,68 @@ public class SettingsValues {
public static String getPrefAdditionalSubtypes(final SharedPreferences prefs,
final Resources res) {
- final String prefSubtypes = res.getString(R.string.predefined_subtypes, "");
- return prefs.getString(Settings.PREF_CUSTOM_INPUT_STYLES, prefSubtypes);
+ final String predefinedPrefSubtypes = AdditionalSubtype.createPrefSubtypes(
+ res.getStringArray(R.array.predefined_subtypes));
+ return prefs.getString(Settings.PREF_CUSTOM_INPUT_STYLES, predefinedPrefSubtypes);
}
// Accessed from the settings interface, hence public
- public static float getCurrentKeypressSoundVolume(final SharedPreferences sp,
- final Resources res) {
+ public static float getCurrentKeypressSoundVolume(final SharedPreferences prefs,
+ final Resources res) {
// TODO: use mVibrationDurationSettingsRawValue instead of reading it again here
- final float volume = sp.getFloat(Settings.PREF_KEYPRESS_SOUND_VOLUME, -1.0f);
+ final float volume = prefs.getFloat(Settings.PREF_KEYPRESS_SOUND_VOLUME, -1.0f);
if (volume >= 0) {
return volume;
}
- return Float.parseFloat(
- Utils.getDeviceOverrideValue(res, R.array.keypress_volumes, "-1.0f"));
+ return Float.parseFloat(ResourceUtils.getDeviceOverrideValue(
+ res, R.array.keypress_volumes, "-1.0f"));
}
// Likewise
- public static int getCurrentVibrationDuration(final SharedPreferences sp,
- final Resources res) {
+ public static int getCurrentVibrationDuration(final SharedPreferences prefs,
+ final Resources res) {
// TODO: use mKeypressVibrationDuration instead of reading it again here
- final int ms = sp.getInt(Settings.PREF_VIBRATION_DURATION_SETTINGS, -1);
+ final int ms = prefs.getInt(Settings.PREF_VIBRATION_DURATION_SETTINGS, -1);
if (ms >= 0) {
return ms;
}
- return Integer.parseInt(
- Utils.getDeviceOverrideValue(res, R.array.keypress_vibration_durations, "-1"));
+ return Integer.parseInt(ResourceUtils.getDeviceOverrideValue(
+ res, R.array.keypress_vibration_durations, "-1"));
}
// Likewise
public static boolean getUsabilityStudyMode(final SharedPreferences prefs) {
// TODO: use mUsabilityStudyMode instead of reading it again here
- return prefs.getBoolean(Settings.PREF_USABILITY_STUDY_MODE, true);
+ return prefs.getBoolean(DebugSettings.PREF_USABILITY_STUDY_MODE, true);
}
- public static long getLastUserHistoryWriteTime(
- final SharedPreferences prefs, final String locale) {
+ public static long getLastUserHistoryWriteTime(final SharedPreferences prefs,
+ final String locale) {
final String str = prefs.getString(Settings.PREF_LAST_USER_DICTIONARY_WRITE_TIME, "");
- final HashMap<String, Long> map = Utils.localeAndTimeStrToHashMap(str);
+ final HashMap<String, Long> map = LocaleUtils.localeAndTimeStrToHashMap(str);
if (map.containsKey(locale)) {
return map.get(locale);
}
return 0;
}
- public static void setLastUserHistoryWriteTime(
- final SharedPreferences prefs, final String locale) {
+ public static void setLastUserHistoryWriteTime(final SharedPreferences prefs,
+ final String locale) {
final String oldStr = prefs.getString(Settings.PREF_LAST_USER_DICTIONARY_WRITE_TIME, "");
- final HashMap<String, Long> map = Utils.localeAndTimeStrToHashMap(oldStr);
+ final HashMap<String, Long> map = LocaleUtils.localeAndTimeStrToHashMap(oldStr);
map.put(locale, System.currentTimeMillis());
- final String newStr = Utils.localeAndTimeHashMapToStr(map);
+ final String newStr = LocaleUtils.localeAndTimeHashMapToStr(map);
prefs.edit().putString(Settings.PREF_LAST_USER_DICTIONARY_WRITE_TIME, newStr).apply();
}
+
+ public boolean isSameInputType(final EditorInfo editorInfo) {
+ return mInputAttributes.isSameInputType(editorInfo);
+ }
+
+ // For debug.
+ public String getInputAttributesDebugString() {
+ return mInputAttributes.toString();
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/StringUtils.java b/java/src/com/android/inputmethod/latin/StringUtils.java
index a43b90525..df7709892 100644
--- a/java/src/com/android/inputmethod/latin/StringUtils.java
+++ b/java/src/com/android/inputmethod/latin/StringUtils.java
@@ -18,10 +18,12 @@ package com.android.inputmethod.latin;
import android.text.TextUtils;
+import com.android.inputmethod.keyboard.Keyboard; // For character constants
+
import java.util.ArrayList;
import java.util.Locale;
-public class StringUtils {
+public final class StringUtils {
private StringUtils() {
// This utility class is not publicly instantiable.
}
@@ -53,7 +55,7 @@ public class StringUtils {
if (TextUtils.isEmpty(csv)) return "";
final String[] elements = csv.split(",");
if (!containsInArray(key, elements)) return csv;
- final ArrayList<String> result = new ArrayList<String>(elements.length - 1);
+ final ArrayList<String> result = CollectionUtils.newArrayList(elements.length - 1);
for (final String element : elements) {
if (!key.equals(element)) result.add(element);
}
@@ -123,23 +125,6 @@ public class StringUtils {
}
/**
- * Returns true if cs contains any upper case characters.
- *
- * @param cs the CharSequence to check
- * @return {@code true} if cs contains any upper case characters, {@code false} otherwise.
- */
- public static boolean hasUpperCase(final CharSequence cs) {
- final int length = cs.length();
- for (int i = 0, cp = 0; i < length; i += Character.charCount(cp)) {
- cp = Character.codePointAt(cs, i);
- if (Character.isUpperCase(cp)) {
- return true;
- }
- }
- return false;
- }
-
- /**
* Remove duplicates from an array of strings.
*
* This method will always keep the first occurrence of all strings at their position
@@ -184,6 +169,9 @@ public class StringUtils {
final char[] characters = string.toCharArray();
final int length = characters.length;
final int[] codePoints = new int[Character.codePointCount(characters, 0, length)];
+ if (length <= 0) {
+ return new int[0];
+ }
int codePoint = Character.codePointAt(characters, 0);
int dsti = 0;
for (int srci = Character.charCount(codePoint);
@@ -194,4 +182,211 @@ public class StringUtils {
codePoints[dsti] = codePoint;
return codePoints;
}
+
+ /**
+ * Determine what caps mode should be in effect at the current offset in
+ * the text. Only the mode bits set in <var>reqModes</var> will be
+ * checked. Note that the caps mode flags here are explicitly defined
+ * to match those in {@link InputType}.
+ *
+ * This code is a straight copy of TextUtils.getCapsMode (modulo namespace and formatting
+ * issues). This will change in the future as we simplify the code for our use and fix bugs.
+ *
+ * @param cs The text that should be checked for caps modes.
+ * @param reqModes The modes to be checked: may be any combination of
+ * {@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,
+ 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.
+ // CAP_MODE_SENTENCES is on if there is some whitespace before the cursor, and the end
+ // of a sentence just before that.
+ // We ignore opening parentheses and the like just before the cursor for purposes of
+ // finding whitespace for WORDS and SENTENCES modes.
+ // The end of a sentence ends with a period, question mark or exclamation mark. If it's
+ // a period, it also needs not to be an abbreviation, which means it also needs to either
+ // be immediately preceded by punctuation, or by a string of only letters with single
+ // periods interleaved.
+
+ // Step 1 : check for cap MODE_CHARACTERS. If it's looked for, it's always on.
+ if ((reqModes & (TextUtils.CAP_MODE_WORDS | TextUtils.CAP_MODE_SENTENCES)) == 0) {
+ // Here we are not looking for MODE_WORDS or MODE_SENTENCES, so since we already
+ // evaluated MODE_CHARACTERS, we can return.
+ return TextUtils.CAP_MODE_CHARACTERS & reqModes;
+ }
+
+ // Step 2 : Skip (ignore at the end of input) any opening punctuation. This includes
+ // opening parentheses, brackets, opening quotes, everything that *opens* a span of
+ // text in the linguistic sense. In RTL languages, this is still an opening sign, although
+ // it may look like a right parenthesis for example. We also include double quote and
+ // 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;
+ 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;
+ }
+ }
+ }
+
+ // We are now on the character that precedes any starting punctuation, so in the most
+ // frequent case this will be whitespace or a letter, although it may occasionally be a
+ // start of line, or some symbol.
+
+ // Step 3 : Search for the start of a paragraph. From the starting point computed in step 2,
+ // we go back over any space or tab char sitting there. We find the start of a paragraph
+ // if the first char that's not a space or tab is a start of line (as in \n, start of text,
+ // or some other similar characters).
+ int j = i;
+ char prevChar = Keyboard.CODE_SPACE;
+ if (hasSpaceBefore) --j;
+ while (j > 0) {
+ prevChar = cs.charAt(j - 1);
+ if (!Character.isSpaceChar(prevChar) && prevChar != Keyboard.CODE_TAB) break;
+ j--;
+ }
+ if (j <= 0 || Character.isWhitespace(prevChar)) {
+ // There are only spacing chars between the start of the paragraph and the cursor,
+ // defined as a isWhitespace() char that is neither a isSpaceChar() nor a tab. Both
+ // MODE_WORDS and MODE_SENTENCES should be active.
+ return (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS
+ | TextUtils.CAP_MODE_SENTENCES) & reqModes;
+ }
+ if (i == j) {
+ // If we don't have whitespace before index i, it means neither MODE_WORDS
+ // nor mode sentences should be on so we can return right away.
+ return TextUtils.CAP_MODE_CHARACTERS & reqModes;
+ }
+ if ((reqModes & TextUtils.CAP_MODE_SENTENCES) == 0) {
+ // Here we know we have whitespace before the cursor (if not, we returned in the above
+ // if i == j clause), so we need MODE_WORDS to be on. And we don't need to evaluate
+ // MODE_SENTENCES so we can return right away.
+ return (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS) & reqModes;
+ }
+ // Please note that because of the reqModes & CAP_MODE_SENTENCES test a few lines above,
+ // we know that MODE_SENTENCES is being requested.
+
+ // Step 4 : Search for MODE_SENTENCES.
+ // English is a special case in that "American typography" rules, which are the most common
+ // in English, state that a sentence terminator immediately following a quotation mark
+ // should be swapped with it and de-duplicated (included in the quotation mark),
+ // e.g. <<Did he say, "let's go home?">>
+ // No other language has such a rule as far as I know, instead putting inside the quotation
+ // mark as the exact thing quoted and handling the surrounding punctuation independently,
+ // e.g. <<Did he say, "let's go home"?>>
+ // Hence, specifically for English, we treat this special case here.
+ if (Locale.ENGLISH.getLanguage().equals(locale.getLanguage())) {
+ for (; j > 0; j--) {
+ // Here we look to go over any closing punctuation. This is because in dominant
+ // variants of English, the final period is placed within double quotes and maybe
+ // other closing punctuation signs. This is generally not true in other languages.
+ final char c = cs.charAt(j - 1);
+ if (c != Keyboard.CODE_DOUBLE_QUOTE && c != Keyboard.CODE_SINGLE_QUOTE
+ && Character.getType(c) != Character.END_PUNCTUATION) {
+ break;
+ }
+ }
+ }
+
+ if (j <= 0) return TextUtils.CAP_MODE_CHARACTERS & reqModes;
+ char c = cs.charAt(--j);
+
+ // We found the next interesting chunk of text ; next we need to determine if it's the
+ // end of a sentence. If we have a question mark or an exclamation mark, it's the end of
+ // a sentence. If it's neither, the only remaining case is the period so we get the opposite
+ // case out of the way.
+ if (c == Keyboard.CODE_QUESTION_MARK || c == Keyboard.CODE_EXCLAMATION_MARK) {
+ return (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_SENTENCES) & reqModes;
+ }
+ if (c != Keyboard.CODE_PERIOD || j <= 0) {
+ return (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS) & reqModes;
+ }
+
+ // We found out that we have a period. We need to determine if this is a full stop or
+ // otherwise sentence-ending period, or an abbreviation like "e.g.". An abbreviation
+ // looks like (\w\.){2,}
+ // To find out, we will have a simple state machine with the following states :
+ // START, WORD, PERIOD, ABBREVIATION
+ // On START : (just before the first period)
+ // letter => WORD
+ // whitespace => end with no caps (it was a stand-alone period)
+ // otherwise => end with caps (several periods/symbols in a row)
+ // On WORD : (within the word just before the first period)
+ // letter => WORD
+ // period => PERIOD
+ // otherwise => end with caps (it was a word with a full stop at the end)
+ // On PERIOD : (period within a potential abbreviation)
+ // letter => LETTER
+ // otherwise => end with caps (it was not an abbreviation)
+ // On LETTER : (letter within a potential abbreviation)
+ // letter => LETTER
+ // period => PERIOD
+ // otherwise => end with no caps (it was an abbreviation)
+ // "Not an abbreviation" in the above chart essentially covers cases like "...yes.". This
+ // should capitalize.
+
+ final int START = 0;
+ final int WORD = 1;
+ final int PERIOD = 2;
+ final int LETTER = 3;
+ final int caps = (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS
+ | TextUtils.CAP_MODE_SENTENCES) & reqModes;
+ final int noCaps = (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS) & reqModes;
+ int state = START;
+ while (j > 0) {
+ c = cs.charAt(--j);
+ switch (state) {
+ case START:
+ if (Character.isLetter(c)) {
+ state = WORD;
+ } else if (Character.isWhitespace(c)) {
+ return noCaps;
+ } else {
+ return caps;
+ }
+ break;
+ case WORD:
+ if (Character.isLetter(c)) {
+ state = WORD;
+ } else if (c == Keyboard.CODE_PERIOD) {
+ state = PERIOD;
+ } else {
+ return caps;
+ }
+ break;
+ case PERIOD:
+ if (Character.isLetter(c)) {
+ state = LETTER;
+ } else {
+ return caps;
+ }
+ break;
+ case LETTER:
+ if (Character.isLetter(c)) {
+ state = LETTER;
+ } else if (c == Keyboard.CODE_PERIOD) {
+ state = PERIOD;
+ } else {
+ return noCaps;
+ }
+ }
+ }
+ // Here we arrived at the start of the line. This should behave exactly like whitespace.
+ return (START == state || LETTER == state) ? noCaps : caps;
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/SubtypeLocale.java b/java/src/com/android/inputmethod/latin/SubtypeLocale.java
index 21c9c0d1e..579f96bb4 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeLocale.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeLocale.java
@@ -30,7 +30,7 @@ import com.android.inputmethod.latin.LocaleUtils.RunInLocale;
import java.util.HashMap;
import java.util.Locale;
-public class SubtypeLocale {
+public final class SubtypeLocale {
static final String TAG = SubtypeLocale.class.getSimpleName();
// This class must be located in the same package as LatinIME.java.
private static final String RESOURCE_PACKAGE_NAME =
@@ -45,13 +45,13 @@ public class SubtypeLocale {
private static String[] sPredefinedKeyboardLayoutSet;
// Keyboard layout to its display name map.
private static final HashMap<String, String> sKeyboardLayoutToDisplayNameMap =
- new HashMap<String, String>();
+ CollectionUtils.newHashMap();
// Keyboard layout to subtype name resource id map.
private static final HashMap<String, Integer> sKeyboardLayoutToNameIdsMap =
- new HashMap<String, Integer>();
+ CollectionUtils.newHashMap();
// Exceptional locale to subtype name resource id map.
private static final HashMap<String, Integer> sExceptionalLocaleToWithLayoutNameIdsMap =
- new HashMap<String, Integer>();
+ CollectionUtils.newHashMap();
private static final String SUBTYPE_NAME_RESOURCE_GENERIC_PREFIX =
"string/subtype_generic_";
private static final String SUBTYPE_NAME_RESOURCE_WITH_LAYOUT_PREFIX =
@@ -60,11 +60,11 @@ public class SubtypeLocale {
"string/subtype_no_language_";
// Exceptional locales to display name map.
private static final HashMap<String, String> sExceptionalDisplayNamesMap =
- new HashMap<String, String>();
+ CollectionUtils.newHashMap();
// Keyboard layout set name for the subtypes that don't have a keyboardLayoutSet extra value.
// This is for compatibility to keep the same subtype ids as pre-JellyBean.
private static final HashMap<String,String> sLocaleAndExtraValueToKeyboardLayoutSetMap =
- new HashMap<String,String>();
+ CollectionUtils.newHashMap();
private SubtypeLocale() {
// Intentional empty constructor for utility class.
diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
index 664de6774..8e51a372b 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
@@ -22,6 +22,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.inputmethodservice.InputMethodService;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.AsyncTask;
@@ -37,12 +38,11 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
-public class SubtypeSwitcher {
+public final class SubtypeSwitcher {
private static boolean DBG = LatinImeLogger.sDBG;
private static final String TAG = SubtypeSwitcher.class.getSimpleName();
private static final SubtypeSwitcher sInstance = new SubtypeSwitcher();
- private /* final */ LatinIME mService;
private /* final */ InputMethodManager mImm;
private /* final */ Resources mResources;
private /* final */ ConnectivityManager mConnectivityManager;
@@ -60,7 +60,7 @@ public class SubtypeSwitcher {
private boolean mIsNetworkConnected;
- static class NeedsToDisplayLanguage {
+ static final class NeedsToDisplayLanguage {
private int mEnabledSubtypeCount;
private boolean mIsSystemLanguageSameAsInputLanguage;
@@ -68,11 +68,11 @@ public class SubtypeSwitcher {
return mEnabledSubtypeCount >= 2 || !mIsSystemLanguageSameAsInputLanguage;
}
- public void updateEnabledSubtypeCount(int count) {
+ public void updateEnabledSubtypeCount(final int count) {
mEnabledSubtypeCount = count;
}
- public void updateIsSystemLanguageSameAsInputLanguage(boolean isSame) {
+ public void updateIsSystemLanguageSameAsInputLanguage(final boolean isSame) {
mIsSystemLanguageSameAsInputLanguage = isSame;
}
}
@@ -81,26 +81,25 @@ public class SubtypeSwitcher {
return sInstance;
}
- public static void init(LatinIME service) {
- SubtypeLocale.init(service);
- sInstance.initialize(service);
- sInstance.updateAllParameters();
+ public static void init(final Context context) {
+ SubtypeLocale.init(context);
+ sInstance.initialize(context);
+ sInstance.updateAllParameters(context);
}
private SubtypeSwitcher() {
// Intentional empty constructor for singleton.
}
- private void initialize(LatinIME service) {
- mService = service;
+ private void initialize(final Context service) {
mResources = service.getResources();
mImm = ImfUtils.getInputMethodManager(service);
mConnectivityManager = (ConnectivityManager) service.getSystemService(
Context.CONNECTIVITY_SERVICE);
mCurrentSystemLocale = mResources.getConfiguration().locale;
- mCurrentSubtype = mImm.getCurrentInputMethodSubtype();
mNoLanguageSubtype = ImfUtils.findSubtypeByLocaleAndKeyboardLayoutSet(
service, SubtypeLocale.NO_LANGUAGE, SubtypeLocale.QWERTY);
+ mCurrentSubtype = ImfUtils.getCurrentInputMethodSubtype(service, mNoLanguageSubtype);
if (mNoLanguageSubtype == null) {
throw new RuntimeException("Can't find no lanugage with QWERTY subtype");
}
@@ -111,39 +110,46 @@ public class SubtypeSwitcher {
// Update all parameters stored in SubtypeSwitcher.
// Only configuration changed event is allowed to call this because this is heavy.
- private void updateAllParameters() {
+ private void updateAllParameters(final Context context) {
mCurrentSystemLocale = mResources.getConfiguration().locale;
- updateSubtype(mImm.getCurrentInputMethodSubtype());
- updateParametersOnStartInputView();
+ updateSubtype(ImfUtils.getCurrentInputMethodSubtype(context, mNoLanguageSubtype));
+ updateParametersOnStartInputViewAndReturnIfCurrentSubtypeEnabled();
}
- // Update parameters which are changed outside LatinIME. This parameters affect UI so they
- // should be updated every time onStartInputview.
- public void updateParametersOnStartInputView() {
- updateEnabledSubtypes();
+ /**
+ * Update parameters which are changed outside LatinIME. This parameters affect UI so they
+ * should be updated every time onStartInputView.
+ *
+ * @return true if the current subtype is enabled.
+ */
+ public boolean updateParametersOnStartInputViewAndReturnIfCurrentSubtypeEnabled() {
+ final boolean currentSubtypeEnabled =
+ updateEnabledSubtypesAndReturnIfEnabled(mCurrentSubtype);
updateShortcutIME();
+ return currentSubtypeEnabled;
}
- // Reload enabledSubtypes from the framework.
- private void updateEnabledSubtypes() {
- final InputMethodSubtype currentSubtype = mCurrentSubtype;
- boolean foundCurrentSubtypeBecameDisabled = true;
+ /**
+ * Update enabled subtypes from the framework.
+ *
+ * @param subtype the subtype to be checked
+ * @return true if the {@code subtype} is enabled.
+ */
+ private boolean updateEnabledSubtypesAndReturnIfEnabled(final InputMethodSubtype subtype) {
final List<InputMethodSubtype> enabledSubtypesOfThisIme =
mImm.getEnabledInputMethodSubtypeList(null, true);
- for (InputMethodSubtype ims : enabledSubtypesOfThisIme) {
- if (ims.equals(currentSubtype)) {
- foundCurrentSubtypeBecameDisabled = false;
- }
- }
mNeedsToDisplayLanguage.updateEnabledSubtypeCount(enabledSubtypesOfThisIme.size());
- if (foundCurrentSubtypeBecameDisabled) {
- if (DBG) {
- Log.w(TAG, "Last subtype: "
- + currentSubtype.getLocale() + "/" + currentSubtype.getExtraValue());
- Log.w(TAG, "Last subtype was disabled. Update to the current one.");
+
+ for (final InputMethodSubtype ims : enabledSubtypesOfThisIme) {
+ if (ims.equals(subtype)) {
+ return true;
}
- updateSubtype(mImm.getCurrentInputMethodSubtype());
}
+ if (DBG) {
+ Log.w(TAG, "Subtype: " + subtype.getLocale() + "/" + subtype.getExtraValue()
+ + " was disabled");
+ }
+ return false;
}
private void updateShortcutIME() {
@@ -159,8 +165,8 @@ public class SubtypeSwitcher {
mImm.getShortcutInputMethodsAndSubtypes();
mShortcutInputMethodInfo = null;
mShortcutSubtype = null;
- for (InputMethodInfo imi : shortcuts.keySet()) {
- List<InputMethodSubtype> subtypes = shortcuts.get(imi);
+ for (final InputMethodInfo imi : shortcuts.keySet()) {
+ final List<InputMethodSubtype> subtypes = shortcuts.get(imi);
// TODO: Returns the first found IMI for now. Should handle all shortcuts as
// appropriate.
mShortcutInputMethodInfo = imi;
@@ -194,24 +200,24 @@ public class SubtypeSwitcher {
mCurrentSubtype = newSubtype;
updateShortcutIME();
- mService.onRefreshKeyboard();
}
////////////////////////////
// Shortcut IME functions //
////////////////////////////
- public void switchToShortcutIME() {
+ public void switchToShortcutIME(final InputMethodService context) {
if (mShortcutInputMethodInfo == null) {
return;
}
final String imiId = mShortcutInputMethodInfo.getId();
- switchToTargetIME(imiId, mShortcutSubtype);
+ switchToTargetIME(imiId, mShortcutSubtype, context);
}
- private void switchToTargetIME(final String imiId, final InputMethodSubtype subtype) {
- final IBinder token = mService.getWindow().getWindow().getAttributes().token;
+ private void switchToTargetIME(final String imiId, final InputMethodSubtype subtype,
+ final InputMethodService context) {
+ final IBinder token = context.getWindow().getWindow().getAttributes().token;
if (token == null) {
return;
}
@@ -253,7 +259,7 @@ public class SubtypeSwitcher {
return true;
}
- public void onNetworkStateChanged(Intent intent) {
+ public void onNetworkStateChanged(final Intent intent) {
final boolean noConnection = intent.getBooleanExtra(
ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
mIsNetworkConnected = !noConnection;
@@ -265,7 +271,7 @@ public class SubtypeSwitcher {
// Subtype Switching functions //
//////////////////////////////////
- public boolean needsToDisplayLanguage(Locale keyboardLocale) {
+ public boolean needsToDisplayLanguage(final Locale keyboardLocale) {
if (keyboardLocale.toString().equals(SubtypeLocale.NO_LANGUAGE)) {
return true;
}
@@ -279,12 +285,14 @@ public class SubtypeSwitcher {
return SubtypeLocale.getSubtypeLocale(mCurrentSubtype);
}
- public void onConfigurationChanged(Configuration conf) {
+ public boolean onConfigurationChanged(final Configuration conf, final Context context) {
final Locale systemLocale = conf.locale;
+ final boolean systemLocaleChanged = !systemLocale.equals(mCurrentSystemLocale);
// If system configuration was changed, update all parameters.
- if (!systemLocale.equals(mCurrentSystemLocale)) {
- updateAllParameters();
+ if (systemLocaleChanged) {
+ updateAllParameters(context);
}
+ return systemLocaleChanged;
}
public InputMethodSubtype getCurrentSubtype() {
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index 336a76f4b..f0e3b4ebd 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -18,7 +18,6 @@ package com.android.inputmethod.latin;
import android.content.Context;
import android.text.TextUtils;
-import android.util.Log;
import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.ProximityInfo;
@@ -26,6 +25,7 @@ import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import java.io.File;
import java.util.ArrayList;
+import java.util.Comparator;
import java.util.HashSet;
import java.util.Locale;
import java.util.concurrent.ConcurrentHashMap;
@@ -34,87 +34,55 @@ import java.util.concurrent.ConcurrentHashMap;
* This class loads a dictionary and provides a list of suggestions for a given sequence of
* characters. This includes corrections and completions.
*/
-public class Suggest implements Dictionary.WordCallback {
+public final class Suggest {
public static final String TAG = Suggest.class.getSimpleName();
- public static final int APPROX_MAX_WORD_LENGTH = 32;
+ // Session id for
+ // {@link #getSuggestedWords(WordComposer,CharSequence,ProximityInfo,boolean,int)}.
+ public static final int SESSION_TYPING = 0;
+ public static final int SESSION_GESTURE = 1;
+ // TODO: rename this to CORRECTION_OFF
public static final int CORRECTION_NONE = 0;
+ // TODO: rename this to CORRECTION_ON
public static final int CORRECTION_FULL = 1;
- public static final int CORRECTION_FULL_BIGRAM = 2;
-
- // It seems the following values are only used for logging.
- public static final int DIC_USER_TYPED = 0;
- public static final int DIC_MAIN = 1;
- public static final int DIC_USER = 2;
- public static final int DIC_USER_HISTORY = 3;
- public static final int DIC_CONTACTS = 4;
- public static final int DIC_WHITELIST = 6;
- // If you add a type of dictionary, increment DIC_TYPE_LAST_ID
- // TODO: this value seems unused. Remove it?
- public static final int DIC_TYPE_LAST_ID = 6;
- public static final String DICT_KEY_MAIN = "main";
- public static final String DICT_KEY_CONTACTS = "contacts";
- // User dictionary, the system-managed one.
- public static final String DICT_KEY_USER = "user";
- // User history dictionary for the unigram map, internal to LatinIME
- public static final String DICT_KEY_USER_HISTORY_UNIGRAM = "history_unigram";
- // User history dictionary for the bigram map, internal to LatinIME
- public static final String DICT_KEY_USER_HISTORY_BIGRAM = "history_bigram";
- public static final String DICT_KEY_WHITELIST ="whitelist";
- private static final boolean DBG = LatinImeLogger.sDBG;
+ public interface SuggestInitializationListener {
+ public void onUpdateMainDictionaryAvailability(boolean isMainDictionaryAvailable);
+ }
- private boolean mHasMainDictionary;
- private Dictionary mContactsDict;
- private WhitelistDictionary mWhiteListDictionary;
- private final ConcurrentHashMap<String, Dictionary> mUnigramDictionaries =
- new ConcurrentHashMap<String, Dictionary>();
- private final ConcurrentHashMap<String, Dictionary> mBigramDictionaries =
- new ConcurrentHashMap<String, Dictionary>();
+ private static final boolean DBG = LatinImeLogger.sDBG;
- private int mPrefMaxSuggestions = 18;
+ private Dictionary mMainDictionary;
+ private ContactsBinaryDictionary mContactsDict;
+ private final ConcurrentHashMap<String, Dictionary> mDictionaries =
+ CollectionUtils.newConcurrentHashMap();
- private static final int PREF_MAX_BIGRAMS = 60;
+ public static final int MAX_SUGGESTIONS = 18;
private float mAutoCorrectionThreshold;
- private ArrayList<SuggestedWordInfo> mSuggestions = new ArrayList<SuggestedWordInfo>();
- private ArrayList<SuggestedWordInfo> mBigramSuggestions = new ArrayList<SuggestedWordInfo>();
- private CharSequence mConsideredWord;
+ // Locale used for upper- and title-casing words
+ private final Locale mLocale;
- // TODO: Remove these member variables by passing more context to addWord() callback method
- private boolean mIsFirstCharCapitalized;
- private boolean mIsAllUpperCase;
- private int mTrailingSingleQuotesCount;
-
- private static final int MINIMUM_SAFETY_NET_CHAR_LENGTH = 4;
-
- public Suggest(final Context context, final Locale locale) {
- initAsynchronously(context, locale);
+ public Suggest(final Context context, final Locale locale,
+ final SuggestInitializationListener listener) {
+ initAsynchronously(context, locale, listener);
+ mLocale = locale;
}
/* package for test */ Suggest(final Context context, final File dictionary,
final long startOffset, final long length, final Locale locale) {
final Dictionary mainDict = DictionaryFactory.createDictionaryForTest(context, dictionary,
startOffset, length /* useFullEditDistance */, false, locale);
- mHasMainDictionary = null != mainDict;
- addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_MAIN, mainDict);
- addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_MAIN, mainDict);
- initWhitelistAndAutocorrectAndPool(context, locale);
+ mLocale = locale;
+ mMainDictionary = mainDict;
+ addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_MAIN, mainDict);
}
- private void initWhitelistAndAutocorrectAndPool(final Context context, final Locale locale) {
- mWhiteListDictionary = new WhitelistDictionary(context, locale);
- addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_WHITELIST, mWhiteListDictionary);
- }
-
- private void initAsynchronously(final Context context, final Locale locale) {
- resetMainDict(context, locale);
-
- // TODO: read the whitelist and init the pool asynchronously too.
- // initPool should be done asynchronously now that the pool is thread-safe.
- initWhitelistAndAutocorrectAndPool(context, locale);
+ private void initAsynchronously(final Context context, final Locale locale,
+ final SuggestInitializationListener listener) {
+ resetMainDict(context, locale, listener);
}
private static void addOrReplaceDictionary(
@@ -128,16 +96,22 @@ public class Suggest implements Dictionary.WordCallback {
}
}
- public void resetMainDict(final Context context, final Locale locale) {
- mHasMainDictionary = false;
+ public void resetMainDict(final Context context, final Locale locale,
+ final SuggestInitializationListener listener) {
+ mMainDictionary = null;
+ if (listener != null) {
+ listener.onUpdateMainDictionaryAvailability(hasMainDictionary());
+ }
new Thread("InitializeBinaryDictionary") {
@Override
public void run() {
final DictionaryCollection newMainDict =
DictionaryFactory.createMainDictionaryFromManager(context, locale);
- mHasMainDictionary = null != newMainDict && !newMainDict.isEmpty();
- addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_MAIN, newMainDict);
- addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_MAIN, newMainDict);
+ addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_MAIN, newMainDict);
+ mMainDictionary = newMainDict;
+ if (listener != null) {
+ listener.onUpdateMainDictionaryAvailability(hasMainDictionary());
+ }
}
}.start();
}
@@ -145,27 +119,27 @@ public class Suggest implements Dictionary.WordCallback {
// The main dictionary could have been loaded asynchronously. Don't cache the return value
// of this method.
public boolean hasMainDictionary() {
- return mHasMainDictionary;
+ return null != mMainDictionary && mMainDictionary.isInitialized();
}
- public Dictionary getContactsDictionary() {
- return mContactsDict;
+ public Dictionary getMainDictionary() {
+ return mMainDictionary;
}
- public ConcurrentHashMap<String, Dictionary> getUnigramDictionaries() {
- return mUnigramDictionaries;
+ public ContactsBinaryDictionary getContactsDictionary() {
+ return mContactsDict;
}
- public static int getApproxMaxWordLength() {
- return APPROX_MAX_WORD_LENGTH;
+ public ConcurrentHashMap<String, Dictionary> getUnigramDictionaries() {
+ return mDictionaries;
}
/**
* Sets an optional user dictionary resource to be loaded. The user dictionary is consulted
* before the main dictionary, if set. This refers to the system-managed user dictionary.
*/
- public void setUserDictionary(Dictionary userDictionary) {
- addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_USER, userDictionary);
+ public void setUserDictionary(UserBinaryDictionary userDictionary) {
+ addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_USER, userDictionary);
}
/**
@@ -173,236 +147,193 @@ public class Suggest implements Dictionary.WordCallback {
* the contacts dictionary by passing null to this method. In this case no contacts dictionary
* won't be used.
*/
- public void setContactsDictionary(Dictionary contactsDictionary) {
+ public void setContactsDictionary(ContactsBinaryDictionary contactsDictionary) {
mContactsDict = contactsDictionary;
- addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_CONTACTS, contactsDictionary);
- addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_CONTACTS, contactsDictionary);
+ addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_CONTACTS, contactsDictionary);
}
- public void setUserHistoryDictionary(Dictionary userHistoryDictionary) {
- addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_USER_HISTORY_UNIGRAM,
- userHistoryDictionary);
- addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_USER_HISTORY_BIGRAM,
- userHistoryDictionary);
+ public void setUserHistoryDictionary(UserHistoryDictionary userHistoryDictionary) {
+ addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_USER_HISTORY, userHistoryDictionary);
}
public void setAutoCorrectionThreshold(float threshold) {
mAutoCorrectionThreshold = threshold;
}
- private static CharSequence capitalizeWord(final boolean all, final boolean first,
- final CharSequence word) {
- if (TextUtils.isEmpty(word) || !(all || first)) return word;
- final int wordLength = word.length();
- final StringBuilder sb = new StringBuilder(getApproxMaxWordLength());
- // TODO: Must pay attention to locale when changing case.
- if (all) {
- sb.append(word.toString().toUpperCase());
- } else if (first) {
- sb.append(Character.toUpperCase(word.charAt(0)));
- if (wordLength > 1) {
- sb.append(word.subSequence(1, wordLength));
- }
- }
- return sb;
- }
-
- protected void addBigramToSuggestions(SuggestedWordInfo bigram) {
- mSuggestions.add(bigram);
- }
-
- private static final WordComposer sEmptyWordComposer = new WordComposer();
- public SuggestedWords getBigramPredictions(CharSequence prevWordForBigram) {
+ public SuggestedWords getSuggestedWords(
+ final WordComposer wordComposer, CharSequence prevWordForBigram,
+ final ProximityInfo proximityInfo, final boolean isCorrectionEnabled, int sessionId) {
LatinImeLogger.onStartSuggestion(prevWordForBigram);
- mIsFirstCharCapitalized = false;
- mIsAllUpperCase = false;
- mTrailingSingleQuotesCount = 0;
- mSuggestions = new ArrayList<SuggestedWordInfo>(mPrefMaxSuggestions);
-
- // Treating USER_TYPED as UNIGRAM suggestion for logging now.
- LatinImeLogger.onAddSuggestedWord("", Suggest.DIC_USER_TYPED, Dictionary.UNIGRAM);
- mConsideredWord = "";
-
- mBigramSuggestions = new ArrayList<SuggestedWordInfo>(PREF_MAX_BIGRAMS);
-
- getAllBigrams(prevWordForBigram, sEmptyWordComposer);
-
- // Nothing entered: return all bigrams for the previous word
- int insertCount = Math.min(mBigramSuggestions.size(), mPrefMaxSuggestions);
- for (int i = 0; i < insertCount; ++i) {
- addBigramToSuggestions(mBigramSuggestions.get(i));
+ if (wordComposer.isBatchMode()) {
+ return getSuggestedWordsForBatchInput(
+ wordComposer, prevWordForBigram, proximityInfo, sessionId);
+ } else {
+ return getSuggestedWordsForTypingInput(wordComposer, prevWordForBigram, proximityInfo,
+ isCorrectionEnabled);
}
-
- SuggestedWordInfo.removeDups(mSuggestions);
-
- return new SuggestedWords(mSuggestions,
- false /* typedWordValid */,
- false /* hasAutoCorrectionCandidate */,
- false /* allowsToBeAutoCorrected */,
- false /* isPunctuationSuggestions */,
- false /* isObsoleteSuggestions */,
- true /* isPrediction */);
}
- // TODO: cleanup dictionaries looking up and suggestions building with SuggestedWords.Builder
- public SuggestedWords getSuggestedWords(
+ // Retrieves suggestions for the typing input.
+ private SuggestedWords getSuggestedWordsForTypingInput(
final WordComposer wordComposer, CharSequence prevWordForBigram,
- final ProximityInfo proximityInfo, final int correctionMode) {
- LatinImeLogger.onStartSuggestion(prevWordForBigram);
- mIsFirstCharCapitalized = wordComposer.isFirstCharCapitalized();
- mIsAllUpperCase = wordComposer.isAllUpperCase();
- mTrailingSingleQuotesCount = wordComposer.trailingSingleQuotesCount();
- mSuggestions = new ArrayList<SuggestedWordInfo>(mPrefMaxSuggestions);
+ final ProximityInfo proximityInfo, final boolean isCorrectionEnabled) {
+ final int trailingSingleQuotesCount = wordComposer.trailingSingleQuotesCount();
+ final BoundedTreeSet suggestionsSet = new BoundedTreeSet(sSuggestedWordInfoComparator,
+ MAX_SUGGESTIONS);
final String typedWord = wordComposer.getTypedWord();
- final String consideredWord = mTrailingSingleQuotesCount > 0
- ? typedWord.substring(0, typedWord.length() - mTrailingSingleQuotesCount)
+ final String consideredWord = trailingSingleQuotesCount > 0
+ ? typedWord.substring(0, typedWord.length() - trailingSingleQuotesCount)
: typedWord;
- // Treating USER_TYPED as UNIGRAM suggestion for logging now.
- LatinImeLogger.onAddSuggestedWord(typedWord, Suggest.DIC_USER_TYPED, Dictionary.UNIGRAM);
- mConsideredWord = consideredWord;
-
- if (wordComposer.size() <= 1 && (correctionMode == CORRECTION_FULL_BIGRAM)) {
- // At first character typed, search only the bigrams
- mBigramSuggestions = new ArrayList<SuggestedWordInfo>(PREF_MAX_BIGRAMS);
-
- if (!TextUtils.isEmpty(prevWordForBigram)) {
- getAllBigrams(prevWordForBigram, wordComposer);
- if (TextUtils.isEmpty(consideredWord)) {
- // Nothing entered: return all bigrams for the previous word
- int insertCount = Math.min(mBigramSuggestions.size(), mPrefMaxSuggestions);
- for (int i = 0; i < insertCount; ++i) {
- addBigramToSuggestions(mBigramSuggestions.get(i));
- }
- } else {
- // Word entered: return only bigrams that match the first char of the typed word
- final char currentChar = consideredWord.charAt(0);
- // TODO: Must pay attention to locale when changing case.
- // TODO: Use codepoint instead of char
- final char currentCharUpper = Character.toUpperCase(currentChar);
- int count = 0;
- final int bigramSuggestionSize = mBigramSuggestions.size();
- for (int i = 0; i < bigramSuggestionSize; i++) {
- final SuggestedWordInfo bigramSuggestion = mBigramSuggestions.get(i);
- final char bigramSuggestionFirstChar =
- (char)bigramSuggestion.codePointAt(0);
- if (bigramSuggestionFirstChar == currentChar
- || bigramSuggestionFirstChar == currentCharUpper) {
- addBigramToSuggestions(bigramSuggestion);
- if (++count > mPrefMaxSuggestions) break;
- }
- }
- }
- }
+ LatinImeLogger.onAddSuggestedWord(typedWord, Dictionary.TYPE_USER_TYPED);
- } else if (wordComposer.size() > 1) {
- final WordComposer wordComposerForLookup;
- if (mTrailingSingleQuotesCount > 0) {
- wordComposerForLookup = new WordComposer(wordComposer);
- for (int i = mTrailingSingleQuotesCount - 1; i >= 0; --i) {
- wordComposerForLookup.deleteLast();
- }
- } else {
- wordComposerForLookup = wordComposer;
- }
- // At second character typed, search the unigrams (scores being affected by bigrams)
- for (final String key : mUnigramDictionaries.keySet()) {
- // Skip UserUnigramDictionary and WhitelistDictionary to lookup
- if (key.equals(DICT_KEY_USER_HISTORY_UNIGRAM) || key.equals(DICT_KEY_WHITELIST))
- continue;
- final Dictionary dictionary = mUnigramDictionaries.get(key);
- dictionary.getWords(wordComposerForLookup, prevWordForBigram, this, proximityInfo);
+ final WordComposer wordComposerForLookup;
+ if (trailingSingleQuotesCount > 0) {
+ wordComposerForLookup = new WordComposer(wordComposer);
+ for (int i = trailingSingleQuotesCount - 1; i >= 0; --i) {
+ wordComposerForLookup.deleteLast();
}
+ } else {
+ wordComposerForLookup = wordComposer;
}
- final CharSequence whitelistedWord = capitalizeWord(mIsAllUpperCase,
- mIsFirstCharCapitalized, mWhiteListDictionary.getWhitelistedWord(consideredWord));
+ for (final String key : mDictionaries.keySet()) {
+ final Dictionary dictionary = mDictionaries.get(key);
+ suggestionsSet.addAll(dictionary.getSuggestions(
+ wordComposerForLookup, prevWordForBigram, proximityInfo));
+ }
- final boolean hasAutoCorrection;
- if (CORRECTION_FULL == correctionMode || CORRECTION_FULL_BIGRAM == correctionMode) {
- final CharSequence autoCorrection =
- AutoCorrection.computeAutoCorrectionWord(mUnigramDictionaries, wordComposer,
- mSuggestions, consideredWord, mAutoCorrectionThreshold,
- whitelistedWord);
- hasAutoCorrection = (null != autoCorrection);
+ final CharSequence whitelistedWord;
+ if (suggestionsSet.isEmpty()) {
+ whitelistedWord = null;
+ } else if (SuggestedWordInfo.KIND_WHITELIST != suggestionsSet.first().mKind) {
+ whitelistedWord = null;
} else {
+ whitelistedWord = suggestionsSet.first().mWord;
+ }
+
+ // The word can be auto-corrected if it has a whitelist entry that is not itself,
+ // or if it's a 2+ characters non-word (i.e. it's not in the dictionary).
+ final boolean allowsToBeAutoCorrected = (null != whitelistedWord
+ && !whitelistedWord.equals(consideredWord))
+ || (consideredWord.length() > 1 && !AutoCorrection.isInTheDictionary(mDictionaries,
+ consideredWord, wordComposer.isFirstCharCapitalized()));
+
+ final boolean hasAutoCorrection;
+ // TODO: using isCorrectionEnabled here is not very good. It's probably useless, because
+ // any attempt to do auto-correction is already shielded with a test for this flag; at the
+ // same time, it feels wrong that the SuggestedWord object includes information about
+ // the current settings. It may also be useful to know, when the setting is off, whether
+ // the word *would* have been auto-corrected.
+ if (!isCorrectionEnabled || !allowsToBeAutoCorrected || !wordComposer.isComposingWord()
+ || suggestionsSet.isEmpty() || wordComposer.hasDigits()
+ || wordComposer.isMostlyCaps() || wordComposer.isResumed()
+ || !hasMainDictionary()) {
+ // If we don't have a main dictionary, we never want to auto-correct. The reason for
+ // this is, the user may have a contact whose name happens to match a valid word in
+ // their language, and it will unexpectedly auto-correct. For example, if the user
+ // types in English with no dictionary and has a "Will" in their contact list, "will"
+ // would always auto-correct to "Will" which is unwanted. Hence, no main dict => no
+ // auto-correct.
hasAutoCorrection = false;
+ } else {
+ hasAutoCorrection = AutoCorrection.suggestionExceedsAutoCorrectionThreshold(
+ suggestionsSet.first(), consideredWord, mAutoCorrectionThreshold);
}
- if (whitelistedWord != null) {
- if (mTrailingSingleQuotesCount > 0) {
- final StringBuilder sb = new StringBuilder(whitelistedWord);
- for (int i = mTrailingSingleQuotesCount - 1; i >= 0; --i) {
- sb.appendCodePoint(Keyboard.CODE_SINGLE_QUOTE);
- }
- mSuggestions.add(0, new SuggestedWordInfo(
- sb.toString(), SuggestedWordInfo.MAX_SCORE));
- } else {
- mSuggestions.add(0, new SuggestedWordInfo(
- whitelistedWord, SuggestedWordInfo.MAX_SCORE));
+ final ArrayList<SuggestedWordInfo> suggestionsContainer =
+ CollectionUtils.newArrayList(suggestionsSet);
+ final int suggestionsCount = suggestionsContainer.size();
+ final boolean isFirstCharCapitalized = wordComposer.isFirstCharCapitalized();
+ final boolean isAllUpperCase = wordComposer.isAllUpperCase();
+ if (isFirstCharCapitalized || isAllUpperCase || 0 != trailingSingleQuotesCount) {
+ for (int i = 0; i < suggestionsCount; ++i) {
+ final SuggestedWordInfo wordInfo = suggestionsContainer.get(i);
+ final SuggestedWordInfo transformedWordInfo = getTransformedSuggestedWordInfo(
+ wordInfo, mLocale, isAllUpperCase, isFirstCharCapitalized,
+ trailingSingleQuotesCount);
+ suggestionsContainer.set(i, transformedWordInfo);
}
}
- mSuggestions.add(0, new SuggestedWordInfo(typedWord, SuggestedWordInfo.MAX_SCORE));
- SuggestedWordInfo.removeDups(mSuggestions);
+ for (int i = 0; i < suggestionsCount; ++i) {
+ final SuggestedWordInfo wordInfo = suggestionsContainer.get(i);
+ LatinImeLogger.onAddSuggestedWord(wordInfo.mWord.toString(), wordInfo.mSourceDict);
+ }
+
+ if (!TextUtils.isEmpty(typedWord)) {
+ suggestionsContainer.add(0, new SuggestedWordInfo(typedWord,
+ SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_TYPED,
+ Dictionary.TYPE_USER_TYPED));
+ }
+ SuggestedWordInfo.removeDups(suggestionsContainer);
final ArrayList<SuggestedWordInfo> suggestionsList;
- if (DBG) {
- suggestionsList = getSuggestionsInfoListWithDebugInfo(typedWord, mSuggestions);
+ if (DBG && !suggestionsContainer.isEmpty()) {
+ suggestionsList = getSuggestionsInfoListWithDebugInfo(typedWord, suggestionsContainer);
} else {
- suggestionsList = mSuggestions;
+ suggestionsList = suggestionsContainer;
}
- // TODO: Change this scheme - a boolean is not enough. A whitelisted word may be "valid"
- // but still autocorrected from - in the case the whitelist only capitalizes the word.
- // The whitelist should be case-insensitive, so it's not possible to be consistent with
- // a boolean flag. Right now this is handled with a slight hack in
- // WhitelistDictionary#shouldForciblyAutoCorrectFrom.
- final boolean allowsToBeAutoCorrected = AutoCorrection.allowsToBeAutoCorrected(
- getUnigramDictionaries(), consideredWord, wordComposer.isFirstCharCapitalized())
- // If we don't have a main dictionary, we never want to auto-correct. The reason for this
- // is, the user may have a contact whose name happens to match a valid word in their
- // language, and it will unexpectedly auto-correct. For example, if the user types in
- // English with no dictionary and has a "Will" in their contact list, "will" would
- // always auto-correct to "Will" which is unwanted. Hence, no main dict => no auto-correct.
- && mHasMainDictionary;
-
- boolean autoCorrectionAvailable = hasAutoCorrection;
- if (correctionMode == CORRECTION_FULL || correctionMode == CORRECTION_FULL_BIGRAM) {
- autoCorrectionAvailable |= !allowsToBeAutoCorrected;
- }
- // Don't auto-correct words with multiple capital letter
- autoCorrectionAvailable &= !wordComposer.isMostlyCaps();
- autoCorrectionAvailable &= !wordComposer.isResumed();
- if (allowsToBeAutoCorrected && suggestionsList.size() > 1 && mAutoCorrectionThreshold > 0
- && Suggest.shouldBlockAutoCorrectionBySafetyNet(typedWord,
- suggestionsList.get(1).mWord)) {
- autoCorrectionAvailable = false;
- }
return new SuggestedWords(suggestionsList,
+ // TODO: this first argument is lying. If this is a whitelisted word which is an
+ // actual word, it says typedWordValid = false, which looks wrong. We should either
+ // rename the attribute or change the value.
!allowsToBeAutoCorrected /* typedWordValid */,
- autoCorrectionAvailable /* hasAutoCorrectionCandidate */,
- allowsToBeAutoCorrected /* allowsToBeAutoCorrected */,
+ hasAutoCorrection, /* willAutoCorrect */
false /* isPunctuationSuggestions */,
false /* isObsoleteSuggestions */,
- false /* isPrediction */);
+ !wordComposer.isComposingWord() /* isPrediction */);
}
- /**
- * Adds all bigram predictions for prevWord. Also checks the lower case version of prevWord if
- * it contains any upper case characters.
- */
- private void getAllBigrams(final CharSequence prevWord, final WordComposer wordComposer) {
- if (StringUtils.hasUpperCase(prevWord)) {
- // TODO: Must pay attention to locale when changing case.
- final CharSequence lowerPrevWord = prevWord.toString().toLowerCase();
- for (final Dictionary dictionary : mBigramDictionaries.values()) {
- dictionary.getBigrams(wordComposer, lowerPrevWord, this);
+ // Retrieves suggestions for the batch input.
+ private SuggestedWords getSuggestedWordsForBatchInput(
+ final WordComposer wordComposer, CharSequence prevWordForBigram,
+ final ProximityInfo proximityInfo, int sessionId) {
+ final BoundedTreeSet suggestionsSet = new BoundedTreeSet(sSuggestedWordInfoComparator,
+ MAX_SUGGESTIONS);
+
+ // At second character typed, search the unigrams (scores being affected by bigrams)
+ for (final String key : mDictionaries.keySet()) {
+ // Skip User history dictionary for lookup
+ // TODO: The user history dictionary should just override getSuggestionsWithSessionId
+ // to make sure it doesn't return anything and we should remove this test
+ if (key.equals(Dictionary.TYPE_USER_HISTORY)) {
+ continue;
}
+ final Dictionary dictionary = mDictionaries.get(key);
+ suggestionsSet.addAll(dictionary.getSuggestionsWithSessionId(
+ wordComposer, prevWordForBigram, proximityInfo, sessionId));
}
- for (final Dictionary dictionary : mBigramDictionaries.values()) {
- dictionary.getBigrams(wordComposer, prevWord, this);
+
+ for (SuggestedWordInfo wordInfo : suggestionsSet) {
+ LatinImeLogger.onAddSuggestedWord(wordInfo.mWord.toString(), wordInfo.mSourceDict);
}
+
+ final ArrayList<SuggestedWordInfo> suggestionsContainer =
+ CollectionUtils.newArrayList(suggestionsSet);
+ final int suggestionsCount = suggestionsContainer.size();
+ final boolean isFirstCharCapitalized = wordComposer.wasShiftedNoLock();
+ final boolean isAllUpperCase = wordComposer.isAllUpperCase();
+ if (isFirstCharCapitalized || isAllUpperCase) {
+ for (int i = 0; i < suggestionsCount; ++i) {
+ final SuggestedWordInfo wordInfo = suggestionsContainer.get(i);
+ final SuggestedWordInfo transformedWordInfo = getTransformedSuggestedWordInfo(
+ wordInfo, mLocale, isAllUpperCase, isFirstCharCapitalized,
+ 0 /* trailingSingleQuotesCount */);
+ suggestionsContainer.set(i, transformedWordInfo);
+ }
+ }
+
+ SuggestedWordInfo.removeDups(suggestionsContainer);
+ // In the batch input mode, the most relevant suggested word should act as a "typed word"
+ // (typedWordValid=true), not as an "auto correct word" (willAutoCorrect=false).
+ return new SuggestedWords(suggestionsContainer,
+ true /* typedWordValid */,
+ false /* willAutoCorrect */,
+ false /* isPunctuationSuggestions */,
+ false /* isObsoleteSuggestions */,
+ false /* isPrediction */);
}
private static ArrayList<SuggestedWordInfo> getSuggestionsInfoListWithDebugInfo(
@@ -411,7 +342,7 @@ public class Suggest implements Dictionary.WordCallback {
typedWordInfo.setDebugString("+");
final int suggestionsSize = suggestions.size();
final ArrayList<SuggestedWordInfo> suggestionsList =
- new ArrayList<SuggestedWordInfo>(suggestionsSize);
+ CollectionUtils.newArrayList(suggestionsSize);
suggestionsList.add(typedWordInfo);
// Note: i here is the index in mScores[], but the index in mSuggestions is one more
// than i because we added the typed word to mSuggestions without touching mScores.
@@ -431,119 +362,45 @@ public class Suggest implements Dictionary.WordCallback {
return suggestionsList;
}
- // TODO: Use codepoint instead of char
- @Override
- public boolean addWord(final char[] word, final int offset, final int length, int score,
- final int dicTypeId, final int dataType) {
- int dataTypeForLog = dataType;
- final ArrayList<SuggestedWordInfo> suggestions;
- final int prefMaxSuggestions;
- if (dataType == Dictionary.BIGRAM) {
- suggestions = mBigramSuggestions;
- prefMaxSuggestions = PREF_MAX_BIGRAMS;
- } else {
- suggestions = mSuggestions;
- prefMaxSuggestions = mPrefMaxSuggestions;
+ private static final class SuggestedWordInfoComparator
+ implements Comparator<SuggestedWordInfo> {
+ // This comparator ranks the word info with the higher frequency first. That's because
+ // that's the order we want our elements in.
+ @Override
+ public int compare(final SuggestedWordInfo o1, final SuggestedWordInfo o2) {
+ if (o1.mScore > o2.mScore) return -1;
+ if (o1.mScore < o2.mScore) return 1;
+ if (o1.mCodePointCount < o2.mCodePointCount) return -1;
+ if (o1.mCodePointCount > o2.mCodePointCount) return 1;
+ return o1.mWord.toString().compareTo(o2.mWord.toString());
}
-
- int pos = 0;
-
- // Check if it's the same word, only caps are different
- if (StringUtils.equalsIgnoreCase(mConsideredWord, word, offset, length)) {
- // TODO: remove this surrounding if clause and move this logic to
- // getSuggestedWordBuilder.
- if (suggestions.size() > 0) {
- final SuggestedWordInfo currentHighestWord = suggestions.get(0);
- // If the current highest word is also equal to typed word, we need to compare
- // frequency to determine the insertion position. This does not ensure strictly
- // correct ordering, but ensures the top score is on top which is enough for
- // removing duplicates correctly.
- if (StringUtils.equalsIgnoreCase(currentHighestWord.mWord, word, offset, length)
- && score <= currentHighestWord.mScore) {
- pos = 1;
- }
- }
- } else {
- // Check the last one's score and bail
- if (suggestions.size() >= prefMaxSuggestions
- && suggestions.get(prefMaxSuggestions - 1).mScore >= score) return true;
- while (pos < suggestions.size()) {
- final int curScore = suggestions.get(pos).mScore;
- if (curScore < score
- || (curScore == score && length < suggestions.get(pos).codePointCount())) {
- break;
- }
- pos++;
- }
- }
- if (pos >= prefMaxSuggestions) {
- return true;
- }
-
- final StringBuilder sb = new StringBuilder(getApproxMaxWordLength());
- // TODO: Must pay attention to locale when changing case.
- if (mIsAllUpperCase) {
- sb.append(new String(word, offset, length).toUpperCase());
- } else if (mIsFirstCharCapitalized) {
- sb.append(Character.toUpperCase(word[offset]));
- if (length > 1) {
- sb.append(word, offset + 1, length - 1);
- }
+ }
+ private static final SuggestedWordInfoComparator sSuggestedWordInfoComparator =
+ new SuggestedWordInfoComparator();
+
+ private static SuggestedWordInfo getTransformedSuggestedWordInfo(
+ final SuggestedWordInfo wordInfo, final Locale locale, final boolean isAllUpperCase,
+ final boolean isFirstCharCapitalized, final int trailingSingleQuotesCount) {
+ final StringBuilder sb = new StringBuilder(wordInfo.mWord.length());
+ if (isAllUpperCase) {
+ sb.append(wordInfo.mWord.toString().toUpperCase(locale));
+ } else if (isFirstCharCapitalized) {
+ sb.append(StringUtils.toTitleCase(wordInfo.mWord.toString(), locale));
} else {
- sb.append(word, offset, length);
+ sb.append(wordInfo.mWord);
}
- for (int i = mTrailingSingleQuotesCount - 1; i >= 0; --i) {
+ for (int i = trailingSingleQuotesCount - 1; i >= 0; --i) {
sb.appendCodePoint(Keyboard.CODE_SINGLE_QUOTE);
}
- suggestions.add(pos, new SuggestedWordInfo(sb, score));
- if (suggestions.size() > prefMaxSuggestions) {
- suggestions.remove(prefMaxSuggestions);
- } else {
- LatinImeLogger.onAddSuggestedWord(sb.toString(), dicTypeId, dataTypeForLog);
- }
- return true;
+ return new SuggestedWordInfo(sb, wordInfo.mScore, wordInfo.mKind, wordInfo.mSourceDict);
}
public void close() {
- final HashSet<Dictionary> dictionaries = new HashSet<Dictionary>();
- dictionaries.addAll(mUnigramDictionaries.values());
- dictionaries.addAll(mBigramDictionaries.values());
+ final HashSet<Dictionary> dictionaries = CollectionUtils.newHashSet();
+ dictionaries.addAll(mDictionaries.values());
for (final Dictionary dictionary : dictionaries) {
dictionary.close();
}
- mHasMainDictionary = false;
- }
-
- // TODO: Resolve the inconsistencies between the native auto correction algorithms and
- // this safety net
- public static boolean shouldBlockAutoCorrectionBySafetyNet(final String typedWord,
- final CharSequence suggestion) {
- // Safety net for auto correction.
- // Actually if we hit this safety net, it's a bug.
- // If user selected aggressive auto correction mode, there is no need to use the safety
- // net.
- // If the length of typed word is less than MINIMUM_SAFETY_NET_CHAR_LENGTH,
- // we should not use net because relatively edit distance can be big.
- final int typedWordLength = typedWord.length();
- if (typedWordLength < Suggest.MINIMUM_SAFETY_NET_CHAR_LENGTH) {
- return false;
- }
- final int maxEditDistanceOfNativeDictionary =
- (typedWordLength < 5 ? 2 : typedWordLength / 2) + 1;
- final int distance = BinaryDictionary.editDistance(typedWord, suggestion.toString());
- if (DBG) {
- Log.d(TAG, "Autocorrected edit distance = " + distance
- + ", " + maxEditDistanceOfNativeDictionary);
- }
- if (distance > maxEditDistanceOfNativeDictionary) {
- if (DBG) {
- Log.e(TAG, "Safety net: before = " + typedWord + ", after = " + suggestion);
- Log.e(TAG, "(Error) The edit distance of this correction exceeds limit. "
- + "Turning off auto-correction.");
- }
- return true;
- } else {
- return false;
- }
+ mMainDictionary = null;
}
}
diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java
index 497fd3bfa..52e292a86 100644
--- a/java/src/com/android/inputmethod/latin/SuggestedWords.java
+++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java
@@ -23,29 +23,31 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
-public class SuggestedWords {
+public final class SuggestedWords {
+ private static final ArrayList<SuggestedWordInfo> EMPTY_WORD_INFO_LIST =
+ CollectionUtils.newArrayList(0);
public static final SuggestedWords EMPTY = new SuggestedWords(
- new ArrayList<SuggestedWordInfo>(0), false, false, false, false, false, false);
+ EMPTY_WORD_INFO_LIST, false, false, false, false, false);
public final boolean mTypedWordValid;
- public final boolean mHasAutoCorrectionCandidate;
+ // Note: this INCLUDES cases where the word will auto-correct to itself. A good definition
+ // of what this flag means would be "the top suggestion is strong enough to auto-correct",
+ // whether this exactly matches the user entry or not.
+ public final boolean mWillAutoCorrect;
public final boolean mIsPunctuationSuggestions;
- public final boolean mAllowsToBeAutoCorrected;
public final boolean mIsObsoleteSuggestions;
public final boolean mIsPrediction;
private final ArrayList<SuggestedWordInfo> mSuggestedWordInfoList;
public SuggestedWords(final ArrayList<SuggestedWordInfo> suggestedWordInfoList,
final boolean typedWordValid,
- final boolean hasAutoCorrectionCandidate,
- final boolean allowsToBeAutoCorrected,
+ final boolean willAutoCorrect,
final boolean isPunctuationSuggestions,
final boolean isObsoleteSuggestions,
final boolean isPrediction) {
mSuggestedWordInfoList = suggestedWordInfoList;
mTypedWordValid = typedWordValid;
- mHasAutoCorrectionCandidate = hasAutoCorrectionCandidate;
- mAllowsToBeAutoCorrected = allowsToBeAutoCorrected;
+ mWillAutoCorrect = willAutoCorrect;
mIsPunctuationSuggestions = isPunctuationSuggestions;
mIsObsoleteSuggestions = isObsoleteSuggestions;
mIsPrediction = isPrediction;
@@ -55,7 +57,7 @@ public class SuggestedWords {
return mSuggestedWordInfoList.size();
}
- public CharSequence getWord(int pos) {
+ public String getWord(int pos) {
return mSuggestedWordInfoList.get(pos).mWord;
}
@@ -67,12 +69,8 @@ public class SuggestedWords {
return mSuggestedWordInfoList.get(pos);
}
- public boolean hasAutoCorrectionWord() {
- return mHasAutoCorrectionCandidate && size() > 1 && !mTypedWordValid;
- }
-
public boolean willAutoCorrect() {
- return !mTypedWordValid && mHasAutoCorrectionCandidate;
+ return mWillAutoCorrect;
}
@Override
@@ -80,18 +78,18 @@ public class SuggestedWords {
// Pretty-print method to help debug
return "SuggestedWords:"
+ " mTypedWordValid=" + mTypedWordValid
- + " mHasAutoCorrectionCandidate=" + mHasAutoCorrectionCandidate
- + " mAllowsToBeAutoCorrected=" + mAllowsToBeAutoCorrected
+ + " mWillAutoCorrect=" + mWillAutoCorrect
+ " mIsPunctuationSuggestions=" + mIsPunctuationSuggestions
+ " words=" + Arrays.toString(mSuggestedWordInfoList.toArray());
}
public static ArrayList<SuggestedWordInfo> getFromApplicationSpecifiedCompletions(
final CompletionInfo[] infos) {
- final ArrayList<SuggestedWordInfo> result = new ArrayList<SuggestedWordInfo>();
+ final ArrayList<SuggestedWordInfo> result = CollectionUtils.newArrayList();
for (CompletionInfo info : infos) {
if (null != info && info.getText() != null) {
- result.add(new SuggestedWordInfo(info.getText(), SuggestedWordInfo.MAX_SCORE));
+ result.add(new SuggestedWordInfo(info.getText(), SuggestedWordInfo.MAX_SCORE,
+ SuggestedWordInfo.KIND_APP_DEFINED, Dictionary.TYPE_APPLICATION_DEFINED));
}
}
return result;
@@ -101,9 +99,10 @@ public class SuggestedWords {
// and replace it with what the user currently typed.
public static ArrayList<SuggestedWordInfo> getTypedWordAndPreviousSuggestions(
final CharSequence typedWord, final SuggestedWords previousSuggestions) {
- final ArrayList<SuggestedWordInfo> suggestionsList = new ArrayList<SuggestedWordInfo>();
- final HashSet<String> alreadySeen = new HashSet<String>();
- suggestionsList.add(new SuggestedWordInfo(typedWord, SuggestedWordInfo.MAX_SCORE));
+ final ArrayList<SuggestedWordInfo> suggestionsList = CollectionUtils.newArrayList();
+ final HashSet<String> alreadySeen = CollectionUtils.newHashSet();
+ suggestionsList.add(new SuggestedWordInfo(typedWord, SuggestedWordInfo.MAX_SCORE,
+ SuggestedWordInfo.KIND_TYPED, Dictionary.TYPE_USER_TYPED));
alreadySeen.add(typedWord.toString());
final int previousSize = previousSuggestions.size();
for (int pos = 1; pos < previousSize; pos++) {
@@ -118,19 +117,31 @@ public class SuggestedWords {
return suggestionsList;
}
- public static class SuggestedWordInfo {
+ public static final class SuggestedWordInfo {
public static final int MAX_SCORE = Integer.MAX_VALUE;
- private final String mWordStr;
- public final CharSequence mWord;
+ public static final int KIND_TYPED = 0; // What user typed
+ public static final int KIND_CORRECTION = 1; // Simple correction/suggestion
+ public static final int KIND_COMPLETION = 2; // Completion (suggestion with appended chars)
+ public static final int KIND_WHITELIST = 3; // Whitelisted word
+ public static final int KIND_BLACKLIST = 4; // Blacklisted word
+ public static final int KIND_HARDCODED = 5; // Hardcoded suggestion, e.g. punctuation
+ public static final int KIND_APP_DEFINED = 6; // Suggested by the application
+ public static final int KIND_SHORTCUT = 7; // A shortcut
+ public static final int KIND_PREDICTION = 8; // A prediction (== a suggestion with no input)
+ public final String mWord;
public final int mScore;
+ public final int mKind; // one of the KIND_* constants above
public final int mCodePointCount;
+ public final String mSourceDict;
private String mDebugString = "";
- public SuggestedWordInfo(final CharSequence word, final int score) {
- mWordStr = word.toString();
- mWord = word;
+ public SuggestedWordInfo(final CharSequence word, final int score, final int kind,
+ final String sourceDict) {
+ mWord = word.toString();
mScore = score;
- mCodePointCount = mWordStr.codePointCount(0, mWordStr.length());
+ mKind = kind;
+ mSourceDict = sourceDict;
+ mCodePointCount = StringUtils.codePointCount(mWord);
}
@@ -148,15 +159,15 @@ public class SuggestedWords {
}
public int codePointAt(int i) {
- return mWordStr.codePointAt(i);
+ return mWord.codePointAt(i);
}
@Override
public String toString() {
if (TextUtils.isEmpty(mDebugString)) {
- return mWordStr;
+ return mWord;
} else {
- return mWordStr + " (" + mDebugString.toString() + ")";
+ return mWord + " (" + mDebugString.toString() + ")";
}
}
@@ -166,11 +177,11 @@ public class SuggestedWords {
return;
}
int i = 1;
- while(i < candidates.size()) {
+ while (i < candidates.size()) {
final SuggestedWordInfo cur = candidates.get(i);
for (int j = 0; j < i; ++j) {
final SuggestedWordInfo previous = candidates.get(j);
- if (TextUtils.equals(cur.mWord, previous.mWord)) {
+ if (cur.mWord.equals(previous.mWord)) {
candidates.remove(cur.mScore < previous.mScore ? i : j);
--i;
break;
diff --git a/java/src/com/android/inputmethod/latin/SuggestionSpanPickedNotificationReceiver.java b/java/src/com/android/inputmethod/latin/SuggestionSpanPickedNotificationReceiver.java
index 4a3f42d5d..d188fc5ef 100644
--- a/java/src/com/android/inputmethod/latin/SuggestionSpanPickedNotificationReceiver.java
+++ b/java/src/com/android/inputmethod/latin/SuggestionSpanPickedNotificationReceiver.java
@@ -23,7 +23,7 @@ import android.content.Context;
import android.content.Intent;
import android.util.Log;
-public class SuggestionSpanPickedNotificationReceiver extends BroadcastReceiver {
+public final class SuggestionSpanPickedNotificationReceiver extends BroadcastReceiver {
private static final boolean DBG = LatinImeLogger.sDBG;
private static final String TAG =
SuggestionSpanPickedNotificationReceiver.class.getSimpleName();
diff --git a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java
index 673b54500..8f21b7b4a 100644
--- a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java
@@ -19,22 +19,23 @@ package com.android.inputmethod.latin;
import android.content.Context;
import com.android.inputmethod.keyboard.ProximityInfo;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+import java.util.ArrayList;
import java.util.Locale;
-public class SynchronouslyLoadedContactsBinaryDictionary extends ContactsBinaryDictionary {
+public final class SynchronouslyLoadedContactsBinaryDictionary extends ContactsBinaryDictionary {
private boolean mClosed;
public SynchronouslyLoadedContactsBinaryDictionary(final Context context, final Locale locale) {
- super(context, Suggest.DIC_CONTACTS, locale);
+ super(context, locale);
}
@Override
- public synchronized void getWords(final WordComposer codes,
- final CharSequence prevWordForBigrams, final WordCallback callback,
- final ProximityInfo proximityInfo) {
+ public synchronized ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer codes,
+ final CharSequence prevWordForBigrams, final ProximityInfo proximityInfo) {
syncReloadDictionaryIfRequired();
- getWordsInner(codes, prevWordForBigrams, callback, proximityInfo);
+ return super.getSuggestions(codes, prevWordForBigrams, proximityInfo);
}
@Override
diff --git a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsDictionary.java b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsDictionary.java
deleted file mode 100644
index a8b871cdf..000000000
--- a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsDictionary.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-import android.content.Context;
-
-import com.android.inputmethod.keyboard.ProximityInfo;
-
-public class SynchronouslyLoadedContactsDictionary extends ContactsDictionary {
- private boolean mClosed;
-
- public SynchronouslyLoadedContactsDictionary(final Context context) {
- super(context, Suggest.DIC_CONTACTS);
- mClosed = false;
- }
-
- @Override
- public synchronized void getWords(final WordComposer codes,
- final CharSequence prevWordForBigrams, final WordCallback callback,
- final ProximityInfo proximityInfo) {
- blockingReloadDictionaryIfRequired();
- getWordsInner(codes, prevWordForBigrams, callback, proximityInfo);
- }
-
- @Override
- public synchronized boolean isValidWord(CharSequence word) {
- blockingReloadDictionaryIfRequired();
- return getWordFrequency(word) > -1;
- }
-
- // Protect against multiple closing
- @Override
- public synchronized void close() {
- // Actually with the current implementation of ContactsDictionary it's safe to close
- // several times, so the following protection is really only for foolproofing
- if (mClosed) return;
- mClosed = true;
- super.close();
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java
index 1606a34e0..612f54d73 100644
--- a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java
@@ -19,8 +19,11 @@ package com.android.inputmethod.latin;
import android.content.Context;
import com.android.inputmethod.keyboard.ProximityInfo;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
-public class SynchronouslyLoadedUserBinaryDictionary extends UserBinaryDictionary {
+import java.util.ArrayList;
+
+public final class SynchronouslyLoadedUserBinaryDictionary extends UserBinaryDictionary {
public SynchronouslyLoadedUserBinaryDictionary(final Context context, final String locale) {
this(context, locale, false);
@@ -32,11 +35,10 @@ public class SynchronouslyLoadedUserBinaryDictionary extends UserBinaryDictionar
}
@Override
- public synchronized void getWords(final WordComposer codes,
- final CharSequence prevWordForBigrams, final WordCallback callback,
- final ProximityInfo proximityInfo) {
+ public synchronized ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer codes,
+ final CharSequence prevWordForBigrams, final ProximityInfo proximityInfo) {
syncReloadDictionaryIfRequired();
- getWordsInner(codes, prevWordForBigrams, callback, proximityInfo);
+ return super.getSuggestions(codes, prevWordForBigrams, proximityInfo);
}
@Override
diff --git a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserDictionary.java b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserDictionary.java
deleted file mode 100644
index 23a49c192..000000000
--- a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserDictionary.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin;
-
-import android.content.Context;
-
-import com.android.inputmethod.keyboard.ProximityInfo;
-
-public class SynchronouslyLoadedUserDictionary extends UserDictionary {
- private boolean mClosed;
-
- public SynchronouslyLoadedUserDictionary(final Context context, final String locale) {
- this(context, locale, false);
- }
-
- public SynchronouslyLoadedUserDictionary(final Context context, final String locale,
- final boolean alsoUseMoreRestrictiveLocales) {
- super(context, locale, alsoUseMoreRestrictiveLocales);
- }
-
- @Override
- public synchronized void getWords(final WordComposer codes,
- final CharSequence prevWordForBigrams, final WordCallback callback,
- final ProximityInfo proximityInfo) {
- blockingReloadDictionaryIfRequired();
- getWordsInner(codes, prevWordForBigrams, callback, proximityInfo);
- }
-
- @Override
- public synchronized boolean isValidWord(CharSequence word) {
- blockingReloadDictionaryIfRequired();
- return super.isValidWord(word);
- }
-
- // Protect against multiple closing
- @Override
- public synchronized void close() {
- if (mClosed) return;
- mClosed = true;
- super.close();
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/TargetApplicationGetter.java b/java/src/com/android/inputmethod/latin/TargetApplicationGetter.java
index 4265309e5..743a8c60f 100644
--- a/java/src/com/android/inputmethod/latin/TargetApplicationGetter.java
+++ b/java/src/com/android/inputmethod/latin/TargetApplicationGetter.java
@@ -22,8 +22,7 @@ import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.util.LruCache;
-public class TargetApplicationGetter extends AsyncTask<String, Void, ApplicationInfo> {
-
+public final class TargetApplicationGetter extends AsyncTask<String, Void, ApplicationInfo> {
private static final int MAX_CACHE_ENTRIES = 64; // arbitrary
private static LruCache<String, ApplicationInfo> sCache =
new LruCache<String, ApplicationInfo>(MAX_CACHE_ENTRIES);
@@ -32,6 +31,7 @@ public class TargetApplicationGetter extends AsyncTask<String, Void, Application
if (null == packageName) return null;
return sCache.get(packageName);
}
+
public static void removeApplicationInfoCache(final String packageName) {
sCache.remove(packageName);
}
diff --git a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java
index 6fa1a25a1..60e6fa127 100644
--- a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java
@@ -34,13 +34,27 @@ import java.util.Arrays;
*/
public class UserBinaryDictionary extends ExpandableBinaryDictionary {
- // TODO: use Words.SHORTCUT when it's public in the SDK
+ // The user dictionary provider uses an empty string to mean "all languages".
+ private static final String USER_DICTIONARY_ALL_LANGUAGES = "";
+
+ // TODO: use Words.SHORTCUT when we target JellyBean or above
final static String SHORTCUT = "shortcut";
- private static final String[] PROJECTION_QUERY = {
- Words.WORD,
- SHORTCUT,
- Words.FREQUENCY,
- };
+ private static final String[] PROJECTION_QUERY;
+ static {
+ // 16 is JellyBean, but we want this to compile against ICS.
+ if (android.os.Build.VERSION.SDK_INT >= 16) {
+ PROJECTION_QUERY = new String[] {
+ Words.WORD,
+ SHORTCUT,
+ Words.FREQUENCY,
+ };
+ } else {
+ PROJECTION_QUERY = new String[] {
+ Words.WORD,
+ Words.FREQUENCY,
+ };
+ }
+ }
private static final String NAME = "userunigram";
@@ -58,9 +72,14 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary {
public UserBinaryDictionary(final Context context, final String locale,
final boolean alsoUseMoreRestrictiveLocales) {
- super(context, getFilenameWithLocale(NAME, locale), Suggest.DIC_USER);
+ super(context, getFilenameWithLocale(NAME, locale), Dictionary.TYPE_USER);
if (null == locale) throw new NullPointerException(); // Catch the error earlier
- mLocale = locale;
+ if (SubtypeLocale.NO_LANGUAGE.equals(locale)) {
+ // If we don't have a locale, insert into the "all locales" user dictionary.
+ mLocale = USER_DICTIONARY_ALL_LANGUAGES;
+ } else {
+ mLocale = locale;
+ }
mAlsoUseMoreRestrictiveLocales = alsoUseMoreRestrictiveLocales;
// Perform a managed query. The Activity will handle closing and re-querying the cursor
// when needed.
@@ -136,7 +155,7 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary {
requestArguments = localeElements;
}
final Cursor cursor = mContext.getContentResolver().query(
- Words.CONTENT_URI, PROJECTION_QUERY, request.toString(), requestArguments, null);
+ Words.CONTENT_URI, PROJECTION_QUERY, request.toString(), requestArguments, null);
try {
addWords(cursor);
} finally {
@@ -182,16 +201,18 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary {
}
private void addWords(Cursor cursor) {
+ // 16 is JellyBean, but we want this to compile against ICS.
+ final boolean hasShortcutColumn = android.os.Build.VERSION.SDK_INT >= 16;
clearFusionDictionary();
if (cursor == null) return;
if (cursor.moveToFirst()) {
final int indexWord = cursor.getColumnIndex(Words.WORD);
- final int indexShortcut = cursor.getColumnIndex(SHORTCUT);
+ final int indexShortcut = hasShortcutColumn ? cursor.getColumnIndex(SHORTCUT) : 0;
final int indexFrequency = cursor.getColumnIndex(Words.FREQUENCY);
while (!cursor.isAfterLast()) {
- String word = cursor.getString(indexWord);
- String shortcut = cursor.getString(indexShortcut);
- int frequency = cursor.getInt(indexFrequency);
+ final String word = cursor.getString(indexWord);
+ final String shortcut = hasShortcutColumn ? cursor.getString(indexShortcut) : null;
+ final int frequency = cursor.getInt(indexFrequency);
// Safeguard against adding really long words.
if (word.length() < MAX_WORD_LENGTH) {
super.addWord(word, null, frequency);
diff --git a/java/src/com/android/inputmethod/latin/UserDictionary.java b/java/src/com/android/inputmethod/latin/UserDictionary.java
deleted file mode 100644
index c1efadd44..000000000
--- a/java/src/com/android/inputmethod/latin/UserDictionary.java
+++ /dev/null
@@ -1,225 +0,0 @@
-/*
- * Copyright (C) 2008 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;
-
-import android.content.ContentProviderClient;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.database.ContentObserver;
-import android.database.Cursor;
-import android.provider.UserDictionary.Words;
-import android.text.TextUtils;
-
-import com.android.inputmethod.keyboard.ProximityInfo;
-
-import java.util.Arrays;
-
-// TODO: This class is superseded by {@link UserBinaryDictionary}. Should be cleaned up.
-/**
- * An expandable dictionary that stores the words in the user unigram dictionary.
- *
- * @deprecated Use {@link UserBinaryDictionary}.
- */
-@Deprecated
-public class UserDictionary extends ExpandableDictionary {
-
- // TODO: use Words.SHORTCUT when it's public in the SDK
- final static String SHORTCUT = "shortcut";
- private static final String[] PROJECTION_QUERY = {
- Words.WORD,
- SHORTCUT,
- Words.FREQUENCY,
- };
-
- // This is not exported by the framework so we pretty much have to write it here verbatim
- private static final String ACTION_USER_DICTIONARY_INSERT =
- "com.android.settings.USER_DICTIONARY_INSERT";
-
- private ContentObserver mObserver;
- final private String mLocale;
- final private boolean mAlsoUseMoreRestrictiveLocales;
-
- public UserDictionary(final Context context, final String locale) {
- this(context, locale, false);
- }
-
- public UserDictionary(final Context context, final String locale,
- final boolean alsoUseMoreRestrictiveLocales) {
- super(context, Suggest.DIC_USER);
- if (null == locale) throw new NullPointerException(); // Catch the error earlier
- mLocale = locale;
- mAlsoUseMoreRestrictiveLocales = alsoUseMoreRestrictiveLocales;
- // Perform a managed query. The Activity will handle closing and re-querying the cursor
- // when needed.
- ContentResolver cres = context.getContentResolver();
-
- mObserver = new ContentObserver(null) {
- @Override
- public void onChange(boolean self) {
- setRequiresReload(true);
- }
- };
- cres.registerContentObserver(Words.CONTENT_URI, true, mObserver);
-
- loadDictionary();
- }
-
- @Override
- public synchronized void close() {
- if (mObserver != null) {
- getContext().getContentResolver().unregisterContentObserver(mObserver);
- mObserver = null;
- }
- super.close();
- }
-
- @Override
- public void loadDictionaryAsync() {
- // Split the locale. For example "en" => ["en"], "de_DE" => ["de", "DE"],
- // "en_US_foo_bar_qux" => ["en", "US", "foo_bar_qux"] because of the limit of 3.
- // This is correct for locale processing.
- // For this example, we'll look at the "en_US_POSIX" case.
- final String[] localeElements =
- TextUtils.isEmpty(mLocale) ? new String[] {} : mLocale.split("_", 3);
- final int length = localeElements.length;
-
- final StringBuilder request = new StringBuilder("(locale is NULL)");
- String localeSoFar = "";
- // At start, localeElements = ["en", "US", "POSIX"] ; localeSoFar = "" ;
- // and request = "(locale is NULL)"
- for (int i = 0; i < length; ++i) {
- // i | localeSoFar | localeElements
- // 0 | "" | ["en", "US", "POSIX"]
- // 1 | "en_" | ["en", "US", "POSIX"]
- // 2 | "en_US_" | ["en", "en_US", "POSIX"]
- localeElements[i] = localeSoFar + localeElements[i];
- localeSoFar = localeElements[i] + "_";
- // i | request
- // 0 | "(locale is NULL)"
- // 1 | "(locale is NULL) or (locale=?)"
- // 2 | "(locale is NULL) or (locale=?) or (locale=?)"
- request.append(" or (locale=?)");
- }
- // At the end, localeElements = ["en", "en_US", "en_US_POSIX"]; localeSoFar = en_US_POSIX_"
- // and request = "(locale is NULL) or (locale=?) or (locale=?) or (locale=?)"
-
- final String[] requestArguments;
- // If length == 3, we already have all the arguments we need (common prefix is meaningless
- // inside variants
- if (mAlsoUseMoreRestrictiveLocales && length < 3) {
- request.append(" or (locale like ?)");
- // The following creates an array with one more (null) position
- final String[] localeElementsWithMoreRestrictiveLocalesIncluded =
- Arrays.copyOf(localeElements, length + 1);
- localeElementsWithMoreRestrictiveLocalesIncluded[length] =
- localeElements[length - 1] + "_%";
- requestArguments = localeElementsWithMoreRestrictiveLocalesIncluded;
- // If for example localeElements = ["en"]
- // then requestArguments = ["en", "en_%"]
- // and request = (locale is NULL) or (locale=?) or (locale like ?)
- // If localeElements = ["en", "en_US"]
- // then requestArguments = ["en", "en_US", "en_US_%"]
- } else {
- requestArguments = localeElements;
- }
- final Cursor cursor = getContext().getContentResolver()
- .query(Words.CONTENT_URI, PROJECTION_QUERY, request.toString(),
- requestArguments, null);
- try {
- addWords(cursor);
- } finally {
- if (null != cursor) cursor.close();
- }
- }
-
- public boolean isEnabled() {
- final ContentResolver cr = getContext().getContentResolver();
- final ContentProviderClient client = cr.acquireContentProviderClient(Words.CONTENT_URI);
- if (client != null) {
- client.release();
- return true;
- } else {
- return false;
- }
- }
-
- /**
- * Adds a word to the user dictionary and makes it persistent.
- *
- * This will call upon the system interface to do the actual work through the intent
- * readied by the system to this effect.
- *
- * @param word the word to add. If the word is capitalized, then the dictionary will
- * recognize it as a capitalized word when searched.
- * @param frequency the frequency of occurrence of the word. A frequency of 255 is considered
- * the highest.
- * @TODO use a higher or float range for frequency
- */
- public synchronized void addWordToUserDictionary(final String word, final int frequency) {
- // Force load the dictionary here synchronously
- if (getRequiresReload()) loadDictionaryAsync();
- // TODO: do something for the UI. With the following, any sufficiently long word will
- // look like it will go to the user dictionary but it won't.
- // Safeguard against adding long words. Can cause stack overflow.
- if (word.length() >= getMaxWordLength()) return;
-
- // TODO: Add an argument to the intent to specify the frequency.
- Intent intent = new Intent(ACTION_USER_DICTIONARY_INSERT);
- intent.putExtra(Words.WORD, word);
- intent.putExtra(Words.LOCALE, mLocale);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- getContext().startActivity(intent);
- }
-
- @Override
- public synchronized void getWords(final WordComposer codes,
- final CharSequence prevWordForBigrams, final WordCallback callback,
- final ProximityInfo proximityInfo) {
- super.getWords(codes, prevWordForBigrams, callback, proximityInfo);
- }
-
- @Override
- public synchronized boolean isValidWord(CharSequence word) {
- return super.isValidWord(word);
- }
-
- private void addWords(Cursor cursor) {
- clearDictionary();
- if (cursor == null) return;
- final int maxWordLength = getMaxWordLength();
- if (cursor.moveToFirst()) {
- final int indexWord = cursor.getColumnIndex(Words.WORD);
- final int indexShortcut = cursor.getColumnIndex(SHORTCUT);
- final int indexFrequency = cursor.getColumnIndex(Words.FREQUENCY);
- while (!cursor.isAfterLast()) {
- String word = cursor.getString(indexWord);
- String shortcut = cursor.getString(indexShortcut);
- int frequency = cursor.getInt(indexFrequency);
- // Safeguard against adding really long words. Stack may overflow due
- // to recursion
- if (word.length() < maxWordLength) {
- super.addWord(word, null, frequency);
- }
- if (null != shortcut && shortcut.length() < maxWordLength) {
- super.addWord(shortcut, word, frequency);
- }
- cursor.moveToNext();
- }
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java b/java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java
new file mode 100644
index 000000000..e39011145
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java
@@ -0,0 +1,214 @@
+/*
+ * 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;
+
+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;
+import com.android.inputmethod.latin.makedict.FusionDictionary;
+import com.android.inputmethod.latin.makedict.FusionDictionary.Node;
+import com.android.inputmethod.latin.makedict.PendingAttribute;
+import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Reads and writes Binary files for a UserHistoryDictionary.
+ *
+ * All the methods in this class are static.
+ */
+public final class UserHistoryDictIOUtils {
+ private static final String TAG = UserHistoryDictIOUtils.class.getSimpleName();
+ private static final boolean DEBUG = false;
+
+ public interface OnAddWordListener {
+ public void setUnigram(final String word, final String shortcutTarget, final int frequency);
+ public void setBigram(final String word1, final String word2, final int frequency);
+ }
+
+ public interface BigramDictionaryInterface {
+ public int getFrequency(final String word1, final String word2);
+ }
+
+ public static final class ByteArrayWrapper implements FusionDictionaryBufferInterface {
+ private byte[] mBuffer;
+ private int mPosition;
+
+ public ByteArrayWrapper(final byte[] buffer) {
+ mBuffer = buffer;
+ mPosition = 0;
+ }
+
+ @Override
+ public int readUnsignedByte() {
+ return ((int)mBuffer[mPosition++]) & 0xFF;
+ }
+
+ @Override
+ public int readUnsignedShort() {
+ final int retval = readUnsignedByte();
+ return (retval << 8) + readUnsignedByte();
+ }
+
+ @Override
+ public int readUnsignedInt24() {
+ final int retval = readUnsignedShort();
+ return (retval << 8) + readUnsignedByte();
+ }
+
+ @Override
+ public int readInt() {
+ final int retval = readUnsignedShort();
+ return (retval << 16) + readUnsignedShort();
+ }
+
+ @Override
+ public int position() {
+ return mPosition;
+ }
+
+ @Override
+ public void position(int position) {
+ mPosition = position;
+ }
+
+ @Override
+ public void put(final byte b) {
+ mBuffer[mPosition++] = b;
+ }
+
+ @Override
+ public int limit() {
+ return mBuffer.length - 1;
+ }
+
+ @Override
+ public int capacity() {
+ return mBuffer.length;
+ }
+ }
+
+ /**
+ * Writes dictionary to file.
+ */
+ public static void writeDictionaryBinary(final OutputStream destination,
+ final BigramDictionaryInterface dict, final UserHistoryDictionaryBigramList bigrams,
+ final FormatOptions formatOptions) {
+ final FusionDictionary fusionDict = constructFusionDictionary(dict, bigrams);
+ try {
+ BinaryDictInputOutput.writeDictionaryBinary(destination, fusionDict, formatOptions);
+ Log.d(TAG, "end writing");
+ } catch (IOException e) {
+ Log.e(TAG, "IO exception while writing file: " + e);
+ } catch (UnsupportedFormatException e) {
+ Log.e(TAG, "Unsupported fomat: " + e);
+ }
+ }
+
+ /**
+ * Constructs a new FusionDictionary from BigramDictionaryInterface.
+ */
+ /* packages for test */ static FusionDictionary constructFusionDictionary(
+ final BigramDictionaryInterface dict, final UserHistoryDictionaryBigramList bigrams) {
+ final FusionDictionary fusionDict = new FusionDictionary(new Node(),
+ new FusionDictionary.DictionaryOptions(new HashMap<String, String>(), false,
+ false));
+ int profTotal = 0;
+ for (final String word1 : bigrams.keySet()) {
+ final HashMap<String, Byte> word1Bigrams = bigrams.getBigrams(word1);
+ for (final String word2 : word1Bigrams.keySet()) {
+ final int freq = dict.getFrequency(word1, word2);
+ if (freq == -1) {
+ // don't add this bigram.
+ continue;
+ }
+ if (DEBUG) {
+ if (word1 == null) {
+ Log.d(TAG, "add unigram: " + word2 + "," + Integer.toString(freq));
+ } else {
+ Log.d(TAG, "add bigram: " + word1
+ + "," + word2 + "," + Integer.toString(freq));
+ }
+ profTotal++;
+ }
+ if (word1 == null) { // unigram
+ fusionDict.add(word2, freq, null, false /* isNotAWord */);
+ } else { // bigram
+ if (FusionDictionary.findWordInTree(fusionDict.mRoot, word1) == null) {
+ fusionDict.add(word1, 2, null, false /* isNotAWord */);
+ }
+ fusionDict.setBigram(word1, word2, freq);
+ }
+ bigrams.updateBigram(word1, word2, (byte)freq);
+ }
+ }
+ if (DEBUG) {
+ Log.d(TAG, "add " + profTotal + "words");
+ }
+ return fusionDict;
+ }
+
+ /**
+ * Reads dictionary from file.
+ */
+ public static void readDictionaryBinary(final FusionDictionaryBufferInterface buffer,
+ final OnAddWordListener dict) {
+ final Map<Integer, String> unigrams = CollectionUtils.newTreeMap();
+ final Map<Integer, Integer> frequencies = CollectionUtils.newTreeMap();
+ final Map<Integer, ArrayList<PendingAttribute>> bigrams = CollectionUtils.newTreeMap();
+ try {
+ BinaryDictIOUtils.readUnigramsAndBigramsBinary(buffer, unigrams, frequencies,
+ bigrams);
+ } catch (IOException e) {
+ Log.e(TAG, "IO exception while reading file: " + e);
+ } catch (UnsupportedFormatException e) {
+ Log.e(TAG, "Unsupported format: " + e);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ Log.e(TAG, "ArrayIndexOutOfBoundsException while reading file: " + e);
+ }
+ addWordsFromWordMap(unigrams, frequencies, bigrams, dict);
+ }
+
+ /**
+ * Adds all unigrams and bigrams in maps to OnAddWordListener.
+ */
+ /* package for test */ static void addWordsFromWordMap(final Map<Integer, String> unigrams,
+ final Map<Integer, Integer> frequencies,
+ final Map<Integer, ArrayList<PendingAttribute>> bigrams, final OnAddWordListener to) {
+ for (Map.Entry<Integer, String> entry : unigrams.entrySet()) {
+ final String word1 = entry.getValue();
+ final int unigramFrequency = frequencies.get(entry.getKey());
+ to.setUnigram(word1, null, unigramFrequency);
+ final ArrayList<PendingAttribute> attrList = bigrams.get(entry.getKey());
+ if (attrList != null) {
+ for (final PendingAttribute attr : attrList) {
+ to.setBigram(word1, unigrams.get(attr.mAddress),
+ BinaryDictInputOutput.reconstructBigramFrequency(unigramFrequency,
+ attr.mFrequency));
+ }
+ }
+ }
+
+ }
+} \ No newline at end of file
diff --git a/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java b/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java
index 5095f6582..3615fa1fb 100644
--- a/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java
@@ -16,21 +16,25 @@
package com.android.inputmethod.latin;
-import android.content.ContentValues;
import android.content.Context;
import android.content.SharedPreferences;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteOpenHelper;
-import android.database.sqlite.SQLiteQueryBuilder;
import android.os.AsyncTask;
-import android.provider.BaseColumns;
import android.util.Log;
+import com.android.inputmethod.keyboard.ProximityInfo;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+import com.android.inputmethod.latin.UserHistoryDictIOUtils.BigramDictionaryInterface;
+import com.android.inputmethod.latin.UserHistoryDictIOUtils.OnAddWordListener;
import com.android.inputmethod.latin.UserHistoryForgettingCurveUtils.ForgettingCurveParams;
+import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
import java.lang.ref.SoftReference;
-import java.util.HashMap;
+import java.util.ArrayList;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantLock;
@@ -38,47 +42,29 @@ import java.util.concurrent.locks.ReentrantLock;
* Locally gathers stats about the words user types and various other signals like auto-correction
* cancellation or manual picks. This allows the keyboard to adapt to the typist over time.
*/
-public class UserHistoryDictionary extends ExpandableDictionary {
- private static final String TAG = "UserHistoryDictionary";
+public final class UserHistoryDictionary extends ExpandableDictionary {
+ private static final String TAG = UserHistoryDictionary.class.getSimpleName();
+ private static final String NAME = UserHistoryDictionary.class.getSimpleName();
public static final boolean DBG_SAVE_RESTORE = false;
public static final boolean DBG_STRESS_TEST = false;
public static final boolean DBG_ALWAYS_WRITE = false;
public static final boolean PROFILE_SAVE_RESTORE = LatinImeLogger.sDBG;
+ private static final FormatOptions VERSION3 = new FormatOptions(3,
+ true /* supportsDynamicUpdate */);
+
/** Any pair being typed or picked */
private static final int FREQUENCY_FOR_TYPED = 2;
/** Maximum number of pairs. Pruning will start when databases goes above this number. */
- private static int sMaxHistoryBigrams = 10000;
+ public static final int MAX_HISTORY_BIGRAMS = 10000;
/**
* When it hits maximum bigram pair, it will delete until you are left with
* only (sMaxHistoryBigrams - sDeleteHistoryBigrams) pairs.
* Do not keep this number small to avoid deleting too often.
*/
- private static int sDeleteHistoryBigrams = 1000;
-
- /**
- * Database version should increase if the database structure changes
- */
- private static final int DATABASE_VERSION = 1;
-
- private static final String DATABASE_NAME = "userbigram_dict.db";
-
- /** Name of the words table in the database */
- private static final String MAIN_TABLE_NAME = "main";
- // TODO: Consume less space by using a unique id for locale instead of the whole
- // 2-5 character string.
- private static final String MAIN_COLUMN_ID = BaseColumns._ID;
- private static final String MAIN_COLUMN_WORD1 = "word1";
- private static final String MAIN_COLUMN_WORD2 = "word2";
- private static final String MAIN_COLUMN_LOCALE = "locale";
-
- /** Name of the frequency table in the database */
- private static final String FREQ_TABLE_NAME = "frequency";
- private static final String FREQ_COLUMN_ID = BaseColumns._ID;
- private static final String FREQ_COLUMN_PAIR_ID = "pair_id";
- private static final String COLUMN_FORGETTING_CURVE_VALUE = "freq";
+ public static final int DELETE_HISTORY_BIGRAMS = 1000;
/** Locale for which this user history dictionary is storing words */
private final String mLocale;
@@ -88,35 +74,14 @@ public class UserHistoryDictionary extends ExpandableDictionary {
private final ReentrantLock mBigramListLock = new ReentrantLock();
private final SharedPreferences mPrefs;
- private final static HashMap<String, String> sDictProjectionMap;
- private final static ConcurrentHashMap<String, SoftReference<UserHistoryDictionary>>
- sLangDictCache = new ConcurrentHashMap<String, SoftReference<UserHistoryDictionary>>();
-
- static {
- sDictProjectionMap = new HashMap<String, String>();
- sDictProjectionMap.put(MAIN_COLUMN_ID, MAIN_COLUMN_ID);
- sDictProjectionMap.put(MAIN_COLUMN_WORD1, MAIN_COLUMN_WORD1);
- sDictProjectionMap.put(MAIN_COLUMN_WORD2, MAIN_COLUMN_WORD2);
- sDictProjectionMap.put(MAIN_COLUMN_LOCALE, MAIN_COLUMN_LOCALE);
+ // Should always be false except when we use this class for test
+ /* package for test */ boolean isTest = false;
- sDictProjectionMap.put(FREQ_COLUMN_ID, FREQ_COLUMN_ID);
- sDictProjectionMap.put(FREQ_COLUMN_PAIR_ID, FREQ_COLUMN_PAIR_ID);
- sDictProjectionMap.put(COLUMN_FORGETTING_CURVE_VALUE, COLUMN_FORGETTING_CURVE_VALUE);
- }
+ private static final ConcurrentHashMap<String, SoftReference<UserHistoryDictionary>>
+ sLangDictCache = CollectionUtils.newConcurrentHashMap();
- private static DatabaseHelper sOpenHelper = null;
-
- public void setDatabaseMax(int maxHistoryBigram) {
- sMaxHistoryBigrams = maxHistoryBigram;
- }
-
- public void setDatabaseDelete(int deleteHistoryBigram) {
- sDeleteHistoryBigrams = deleteHistoryBigram;
- }
-
- public synchronized static UserHistoryDictionary getInstance(
- final Context context, final String locale,
- final int dictTypeId, final SharedPreferences sp) {
+ public static synchronized UserHistoryDictionary getInstance(
+ final Context context, final String locale, final SharedPreferences sp) {
if (sLangDictCache.containsKey(locale)) {
final SoftReference<UserHistoryDictionary> ref = sLangDictCache.get(locale);
final UserHistoryDictionary dict = ref == null ? null : ref.get();
@@ -128,19 +93,16 @@ public class UserHistoryDictionary extends ExpandableDictionary {
}
}
final UserHistoryDictionary dict =
- new UserHistoryDictionary(context, locale, dictTypeId, sp);
+ new UserHistoryDictionary(context, locale, sp);
sLangDictCache.put(locale, new SoftReference<UserHistoryDictionary>(dict));
return dict;
}
- private UserHistoryDictionary(final Context context, final String locale, final int dicTypeId,
- SharedPreferences sp) {
- super(context, dicTypeId);
+ private UserHistoryDictionary(final Context context, final String locale,
+ final SharedPreferences sp) {
+ super(context, Dictionary.TYPE_USER_HISTORY);
mLocale = locale;
mPrefs = sp;
- if (sOpenHelper == null) {
- sOpenHelper = new DatabaseHelper(getContext());
- }
if (mLocale != null && mLocale.length() > 1) {
loadDictionary();
}
@@ -158,6 +120,14 @@ public class UserHistoryDictionary extends ExpandableDictionary {
// super.close();
}
+ @Override
+ protected ArrayList<SuggestedWordInfo> getWordsInner(final WordComposer composer,
+ final CharSequence prevWord, final ProximityInfo proximityInfo) {
+ // Inhibit suggestions (not predictions) for user history for now. Removing this method
+ // is enough to use it through the standard ExpandableDictionary way.
+ return null;
+ }
+
/**
* Return whether the passed charsequence is in the dictionary.
*/
@@ -176,10 +146,15 @@ public class UserHistoryDictionary extends ExpandableDictionary {
* The second word may not be null (a NullPointerException would be thrown).
*/
public int addToUserHistory(final String word1, String word2, boolean isValid) {
+ if (word2.length() >= BinaryDictionary.MAX_WORD_LENGTH ||
+ (word1 != null && word1.length() >= BinaryDictionary.MAX_WORD_LENGTH)) {
+ return -1;
+ }
if (mBigramListLock.tryLock()) {
try {
super.addWord(
word2, null /* the "shortcut" parameter is null */, FREQUENCY_FOR_TYPED);
+ mBigramList.addBigram(null, word2, (byte)FREQUENCY_FOR_TYPED);
// Do not insert a word as a bigram of itself
if (word2.equals(word1)) {
return 0;
@@ -217,11 +192,8 @@ public class UserHistoryDictionary extends ExpandableDictionary {
* Schedules a background thread to write any pending words to the database.
*/
private void flushPendingWrites() {
- if (mBigramListLock.isLocked()) {
- return;
- }
// Create a background thread to write the pending entries
- new UpdateDbTask(sOpenHelper, mBigramList, mLocale, this, mPrefs).execute();
+ new UpdateBinaryTask(mBigramList, mLocale, this, mPrefs, getContext()).execute();
}
@Override
@@ -235,6 +207,8 @@ public class UserHistoryDictionary extends ExpandableDictionary {
}
}
+ private int profTotal;
+
private void loadDictionaryAsyncLocked() {
if (DBG_STRESS_TEST) {
try {
@@ -247,340 +221,181 @@ public class UserHistoryDictionary extends ExpandableDictionary {
final long last = SettingsValues.getLastUserHistoryWriteTime(mPrefs, mLocale);
final boolean initializing = last == 0;
final long now = System.currentTimeMillis();
- // Load the words that correspond to the current input locale
- final Cursor cursor = query(MAIN_COLUMN_LOCALE + "=?", new String[] { mLocale });
- if (null == cursor) return;
- try {
- // TODO: Call SQLiteDataBase.beginTransaction / SQLiteDataBase.endTransaction
- if (cursor.moveToFirst()) {
- final int word1Index = cursor.getColumnIndex(MAIN_COLUMN_WORD1);
- final int word2Index = cursor.getColumnIndex(MAIN_COLUMN_WORD2);
- final int fcIndex = cursor.getColumnIndex(COLUMN_FORGETTING_CURVE_VALUE);
- while (!cursor.isAfterLast()) {
- final String word1 = cursor.getString(word1Index);
- final String word2 = cursor.getString(word2Index);
- final int fc = cursor.getInt(fcIndex);
+ profTotal = 0;
+ final String fileName = NAME + "." + mLocale + ".dict";
+ final ExpandableDictionary dictionary = this;
+ final OnAddWordListener listener = new OnAddWordListener() {
+ @Override
+ public void setUnigram(String word, String shortcutTarget, int frequency) {
+ profTotal++;
+ if (DBG_SAVE_RESTORE) {
+ Log.d(TAG, "load unigram: " + word + "," + frequency);
+ }
+ dictionary.addWord(word, shortcutTarget, frequency);
+ mBigramList.addBigram(null, word, (byte)frequency);
+ }
+
+ @Override
+ public void setBigram(String word1, String word2, int frequency) {
+ if (word1.length() < BinaryDictionary.MAX_WORD_LENGTH
+ && word2.length() < BinaryDictionary.MAX_WORD_LENGTH) {
+ profTotal++;
if (DBG_SAVE_RESTORE) {
- Log.d(TAG, "--- Load user history: " + word1 + ", " + word2 + ","
- + mLocale + "," + this);
- }
- // Safeguard against adding really long words. Stack may overflow due
- // to recursive lookup
- if (null == word1) {
- super.addWord(word2, null /* shortcut */, fc);
- } else if (word1.length() < BinaryDictionary.MAX_WORD_LENGTH
- && word2.length() < BinaryDictionary.MAX_WORD_LENGTH) {
- super.setBigramAndGetFrequency(
- word1, word2, initializing ? new ForgettingCurveParams(true)
- : new ForgettingCurveParams(fc, now, last));
+ Log.d(TAG, "load bigram: " + word1 + "," + word2 + "," + frequency);
}
- mBigramList.addBigram(word1, word2, (byte)fc);
- cursor.moveToNext();
+ dictionary.setBigramAndGetFrequency(
+ word1, word2, initializing ? new ForgettingCurveParams(true)
+ : new ForgettingCurveParams(frequency, now, last));
}
+ mBigramList.addBigram(word1, word2, (byte)frequency);
}
+ };
+
+ // Load the dictionary from binary file
+ FileInputStream inStream = null;
+ try {
+ final File file = new File(getContext().getFilesDir(), fileName);
+ final byte[] buffer = new byte[(int)file.length()];
+ inStream = new FileInputStream(file);
+ inStream.read(buffer);
+ UserHistoryDictIOUtils.readDictionaryBinary(
+ new UserHistoryDictIOUtils.ByteArrayWrapper(buffer), listener);
+ } catch (FileNotFoundException e) {
+ Log.e(TAG, "when loading: file not found" + e);
+ } catch (IOException e) {
+ Log.e(TAG, "IOException when open bytebuffer: " + e);
} finally {
- cursor.close();
+ if (inStream != null) {
+ try {
+ inStream.close();
+ } catch (IOException e) {
+ // do nothing
+ }
+ }
if (PROFILE_SAVE_RESTORE) {
final long diff = System.currentTimeMillis() - now;
- Log.w(TAG, "PROF: Load User HistoryDictionary: "
- + mLocale + ", " + diff + "ms.");
+ Log.d(TAG, "PROF: Load UserHistoryDictionary: "
+ + mLocale + ", " + diff + "ms. load " + profTotal + "entries.");
}
}
}
/**
- * Query the database
- */
- private static Cursor query(String selection, String[] selectionArgs) {
- SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
-
- // main INNER JOIN frequency ON (main._id=freq.pair_id)
- qb.setTables(MAIN_TABLE_NAME + " INNER JOIN " + FREQ_TABLE_NAME + " ON ("
- + MAIN_TABLE_NAME + "." + MAIN_COLUMN_ID + "=" + FREQ_TABLE_NAME + "."
- + FREQ_COLUMN_PAIR_ID +")");
-
- qb.setProjectionMap(sDictProjectionMap);
-
- // Get the database and run the query
- try {
- SQLiteDatabase db = sOpenHelper.getReadableDatabase();
- Cursor c = qb.query(db,
- new String[] {
- MAIN_COLUMN_WORD1, MAIN_COLUMN_WORD2, COLUMN_FORGETTING_CURVE_VALUE },
- selection, selectionArgs, null, null, null);
- return c;
- } catch (android.database.sqlite.SQLiteCantOpenDatabaseException e) {
- // Can't open the database : presumably we can't access storage. That may happen
- // when the device is wedged; do a best effort to still start the keyboard.
- return null;
- }
- }
-
- /**
- * This class helps open, create, and upgrade the database file.
+ * Async task to write pending words to the binarydicts.
*/
- private static class DatabaseHelper extends SQLiteOpenHelper {
-
- DatabaseHelper(Context context) {
- super(context, DATABASE_NAME, null, DATABASE_VERSION);
- }
-
- @Override
- public void onCreate(SQLiteDatabase db) {
- db.execSQL("PRAGMA foreign_keys = ON;");
- db.execSQL("CREATE TABLE " + MAIN_TABLE_NAME + " ("
- + MAIN_COLUMN_ID + " INTEGER PRIMARY KEY,"
- + MAIN_COLUMN_WORD1 + " TEXT,"
- + MAIN_COLUMN_WORD2 + " TEXT,"
- + MAIN_COLUMN_LOCALE + " TEXT"
- + ");");
- db.execSQL("CREATE TABLE " + FREQ_TABLE_NAME + " ("
- + FREQ_COLUMN_ID + " INTEGER PRIMARY KEY,"
- + FREQ_COLUMN_PAIR_ID + " INTEGER,"
- + COLUMN_FORGETTING_CURVE_VALUE + " INTEGER,"
- + "FOREIGN KEY(" + FREQ_COLUMN_PAIR_ID + ") REFERENCES " + MAIN_TABLE_NAME
- + "(" + MAIN_COLUMN_ID + ")" + " ON DELETE CASCADE"
- + ");");
- }
-
- @Override
- public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
- + newVersion + ", which will destroy all old data");
- db.execSQL("DROP TABLE IF EXISTS " + MAIN_TABLE_NAME);
- db.execSQL("DROP TABLE IF EXISTS " + FREQ_TABLE_NAME);
- onCreate(db);
- }
- }
-
- /**
- * Async task to write pending words to the database so that it stays in sync with
- * the in-memory trie.
- */
- private static class UpdateDbTask extends AsyncTask<Void, Void, Void> {
+ private static final class UpdateBinaryTask extends AsyncTask<Void, Void, Void>
+ implements BigramDictionaryInterface {
private final UserHistoryDictionaryBigramList mBigramList;
- private final DatabaseHelper mDbHelper;
+ private final boolean mAddLevel0Bigrams;
private final String mLocale;
private final UserHistoryDictionary mUserHistoryDictionary;
private final SharedPreferences mPrefs;
+ private final Context mContext;
- public UpdateDbTask(
- DatabaseHelper openHelper, UserHistoryDictionaryBigramList pendingWrites,
- String locale, UserHistoryDictionary dict, SharedPreferences prefs) {
+ public UpdateBinaryTask(UserHistoryDictionaryBigramList pendingWrites, String locale,
+ UserHistoryDictionary dict, SharedPreferences prefs, Context context) {
mBigramList = pendingWrites;
mLocale = locale;
- mDbHelper = openHelper;
mUserHistoryDictionary = dict;
mPrefs = prefs;
- }
-
- /** Prune any old data if the database is getting too big. */
- private static void checkPruneData(SQLiteDatabase db) {
- db.execSQL("PRAGMA foreign_keys = ON;");
- Cursor c = db.query(FREQ_TABLE_NAME, new String[] { FREQ_COLUMN_PAIR_ID },
- null, null, null, null, null);
- try {
- int totalRowCount = c.getCount();
- // prune out old data if we have too much data
- if (totalRowCount > sMaxHistoryBigrams) {
- int numDeleteRows = (totalRowCount - sMaxHistoryBigrams)
- + sDeleteHistoryBigrams;
- int pairIdColumnId = c.getColumnIndex(FREQ_COLUMN_PAIR_ID);
- c.moveToFirst();
- int count = 0;
- while (count < numDeleteRows && !c.isAfterLast()) {
- String pairId = c.getString(pairIdColumnId);
- // Deleting from MAIN table will delete the frequencies
- // due to FOREIGN KEY .. ON DELETE CASCADE
- db.delete(MAIN_TABLE_NAME, MAIN_COLUMN_ID + "=?",
- new String[] { pairId });
- c.moveToNext();
- count++;
- }
- }
- } finally {
- c.close();
- }
+ mContext = context;
+ mAddLevel0Bigrams = mBigramList.size() <= MAX_HISTORY_BIGRAMS;
}
@Override
protected Void doInBackground(Void... v) {
- SQLiteDatabase db = null;
- if (mUserHistoryDictionary.mBigramListLock.tryLock()) {
+ if (mUserHistoryDictionary.isTest) {
+ // If isTest == true, wait until the lock is released.
+ mUserHistoryDictionary.mBigramListLock.lock();
try {
- try {
- db = mDbHelper.getWritableDatabase();
- } catch (android.database.sqlite.SQLiteCantOpenDatabaseException e) {
- // If we can't open the db, don't do anything. Exit through the next test
- // for non-nullity of the db variable.
- }
- if (null == db) {
- // Not much we can do. Just exit.
- return null;
- }
- db.beginTransaction();
- return doLoadTaskLocked(db);
+ doWriteTaskLocked();
} finally {
- if (db != null) {
- db.endTransaction();
- }
mUserHistoryDictionary.mBigramListLock.unlock();
}
+ } else if (mUserHistoryDictionary.mBigramListLock.tryLock()) {
+ doWriteTaskLocked();
}
return null;
}
- private Void doLoadTaskLocked(SQLiteDatabase db) {
+ private void doWriteTaskLocked() {
if (DBG_STRESS_TEST) {
try {
Log.w(TAG, "Start stress in closing: " + mLocale);
Thread.sleep(15000);
Log.w(TAG, "End stress in closing");
} catch (InterruptedException e) {
+ Log.e(TAG, "In stress test: " + e);
}
}
+
final long now = PROFILE_SAVE_RESTORE ? System.currentTimeMillis() : 0;
- int profTotal = 0;
- int profInsert = 0;
- int profDelete = 0;
- db.execSQL("PRAGMA foreign_keys = ON;");
- final boolean addLevel0Bigram = mBigramList.size() <= sMaxHistoryBigrams;
-
- // Write all the entries to the db
- for (String word1 : mBigramList.keySet()) {
- final HashMap<String, Byte> word1Bigrams = mBigramList.getBigrams(word1);
- for (String word2 : word1Bigrams.keySet()) {
- if (PROFILE_SAVE_RESTORE) {
- ++profTotal;
- }
- // Get new frequency. Do not insert unigrams/bigrams which freq is "-1".
- final int freq; // -1, or 0~255
- if (word1 == null) { // unigram
- freq = FREQUENCY_FOR_TYPED;
- final byte prevFc = word1Bigrams.get(word2);
- if (prevFc == FREQUENCY_FOR_TYPED) {
- // No need to update since we found no changes for this entry.
- // Just skip to the next entry.
- if (DBG_SAVE_RESTORE) {
- Log.d(TAG, "Skip update user history: " + word1 + "," + word2
- + "," + prevFc);
- }
- if (!DBG_ALWAYS_WRITE) {
- continue;
- }
- }
- } else { // bigram
- final NextWord nw = mUserHistoryDictionary.getBigramWord(word1, word2);
- if (nw != null) {
- final ForgettingCurveParams fcp = nw.getFcParams();
- final byte prevFc = word1Bigrams.get(word2);
- final byte fc = (byte)fcp.getFc();
- final boolean isValid = fcp.isValid();
- if (prevFc > 0 && prevFc == fc) {
- // No need to update since we found no changes for this entry.
- // Just skip to the next entry.
- if (DBG_SAVE_RESTORE) {
- Log.d(TAG, "Skip update user history: " + word1 + ","
- + word2 + "," + prevFc);
- }
- if (!DBG_ALWAYS_WRITE) {
- continue;
- } else {
- freq = fc;
- }
- } else if (UserHistoryForgettingCurveUtils.
- needsToSave(fc, isValid, addLevel0Bigram)) {
- freq = fc;
- } else {
- freq = -1;
- }
- } else {
- freq = -1;
- }
- }
- // TODO: this process of making a text search for each pair each time
- // is terribly inefficient. Optimize this.
- // Find pair id
- Cursor c = null;
+ final String fileName = NAME + "." + mLocale + ".dict";
+ final File file = new File(mContext.getFilesDir(), fileName);
+ FileOutputStream out = null;
+
+ try {
+ out = new FileOutputStream(file);
+ UserHistoryDictIOUtils.writeDictionaryBinary(out, this, mBigramList, VERSION3);
+ out.flush();
+ out.close();
+ } catch (IOException e) {
+ Log.e(TAG, "IO Exception while writing file: " + e);
+ } finally {
+ if (out != null) {
try {
- if (null != word1) {
- c = db.query(MAIN_TABLE_NAME, new String[] { MAIN_COLUMN_ID },
- MAIN_COLUMN_WORD1 + "=? AND " + MAIN_COLUMN_WORD2 + "=? AND "
- + MAIN_COLUMN_LOCALE + "=?",
- new String[] { word1, word2, mLocale }, null, null,
- null);
- } else {
- c = db.query(MAIN_TABLE_NAME, new String[] { MAIN_COLUMN_ID },
- MAIN_COLUMN_WORD1 + " IS NULL AND " + MAIN_COLUMN_WORD2
- + "=? AND " + MAIN_COLUMN_LOCALE + "=?",
- new String[] { word2, mLocale }, null, null, null);
- }
-
- final int pairId;
- if (c.moveToFirst()) {
- if (PROFILE_SAVE_RESTORE) {
- ++profDelete;
- }
- // Delete existing pair
- pairId = c.getInt(c.getColumnIndex(MAIN_COLUMN_ID));
- db.delete(FREQ_TABLE_NAME, FREQ_COLUMN_PAIR_ID + "=?",
- new String[] { Integer.toString(pairId) });
- } else {
- // Create new pair
- Long pairIdLong = db.insert(MAIN_TABLE_NAME, null,
- getContentValues(word1, word2, mLocale));
- pairId = pairIdLong.intValue();
- }
- if (freq > 0) {
- if (PROFILE_SAVE_RESTORE) {
- ++profInsert;
- }
- if (DBG_SAVE_RESTORE) {
- Log.d(TAG, "--- Save user history: " + word1 + ", " + word2
- + mLocale + "," + this);
- }
- // Insert new frequency
- db.insert(FREQ_TABLE_NAME, null,
- getFrequencyContentValues(pairId, freq));
- // Update an existing bigram entry in mBigramList too in order to
- // synchronize the SQL DB and mBigramList.
- mBigramList.updateBigram(word1, word2, (byte)freq);
- }
- } finally {
- if (c != null) {
- c.close();
- }
+ out.close();
+ } catch (IOException e) {
+ // ignore
}
}
}
- checkPruneData(db);
- // Save the timestamp after we finish writing the SQL DB.
+ // Save the timestamp after we finish writing the binary dictionary.
SettingsValues.setLastUserHistoryWriteTime(mPrefs, mLocale);
if (PROFILE_SAVE_RESTORE) {
final long diff = System.currentTimeMillis() - now;
- Log.w(TAG, "PROF: Write User HistoryDictionary: " + mLocale + ", "+ diff
- + "ms. Total: " + profTotal + ". Insert: " + profInsert + ". Delete: "
- + profDelete);
+ Log.w(TAG, "PROF: Write User HistoryDictionary: " + mLocale + ", " + diff + "ms.");
}
- db.setTransactionSuccessful();
- return null;
}
- private static ContentValues getContentValues(String word1, String word2, String locale) {
- ContentValues values = new ContentValues(3);
- values.put(MAIN_COLUMN_WORD1, word1);
- values.put(MAIN_COLUMN_WORD2, word2);
- values.put(MAIN_COLUMN_LOCALE, locale);
- return values;
+ @Override
+ public int getFrequency(String word1, String word2) {
+ final int freq;
+ if (word1 == null) { // unigram
+ freq = FREQUENCY_FOR_TYPED;
+ final byte prevFc = mBigramList.getBigrams(word1).get(word2);
+ } else { // bigram
+ final NextWord nw = mUserHistoryDictionary.getBigramWord(word1, word2);
+ if (nw != null) {
+ final ForgettingCurveParams fcp = nw.getFcParams();
+ final byte prevFc = mBigramList.getBigrams(word1).get(word2);
+ final byte fc = fcp.getFc();
+ final boolean isValid = fcp.isValid();
+ if (prevFc > 0 && prevFc == fc) {
+ freq = ((int)fc) & 0xFF;
+ } else if (UserHistoryForgettingCurveUtils.
+ needsToSave(fc, isValid, mAddLevel0Bigrams)) {
+ freq = ((int)fc) & 0xFF;
+ } else {
+ // Delete this entry
+ freq = -1;
+ }
+ } else {
+ // Delete this entry
+ freq = -1;
+ }
+ }
+ return freq;
}
+ }
- private static ContentValues getFrequencyContentValues(int pairId, int frequency) {
- ContentValues values = new ContentValues(2);
- values.put(FREQ_COLUMN_PAIR_ID, pairId);
- values.put(COLUMN_FORGETTING_CURVE_VALUE, frequency);
- return values;
+ void forceAddWordForTest(final String word1, final String word2, final boolean isValid) {
+ mBigramListLock.lock();
+ try {
+ addToUserHistory(word1, word2, isValid);
+ } finally {
+ mBigramListLock.unlock();
}
}
-
}
diff --git a/java/src/com/android/inputmethod/latin/UserHistoryDictionaryBigramList.java b/java/src/com/android/inputmethod/latin/UserHistoryDictionaryBigramList.java
index 28847745e..df44948f9 100644
--- a/java/src/com/android/inputmethod/latin/UserHistoryDictionaryBigramList.java
+++ b/java/src/com/android/inputmethod/latin/UserHistoryDictionaryBigramList.java
@@ -26,12 +26,11 @@ import java.util.Set;
* All bigrams including stale ones in SQL DB should be stored in this class to avoid adding stale
* bigrams when we write to the SQL DB.
*/
-public class UserHistoryDictionaryBigramList {
+public final class UserHistoryDictionaryBigramList {
public static final byte FORGETTING_CURVE_INITIAL_VALUE = 0;
private static final String TAG = UserHistoryDictionaryBigramList.class.getSimpleName();
- private static final HashMap<String, Byte> EMPTY_BIGRAM_MAP = new HashMap<String, Byte>();
- private final HashMap<String, HashMap<String, Byte>> mBigramMap =
- new HashMap<String, HashMap<String, Byte>>();
+ private static final HashMap<String, Byte> EMPTY_BIGRAM_MAP = CollectionUtils.newHashMap();
+ private final HashMap<String, HashMap<String, Byte>> mBigramMap = CollectionUtils.newHashMap();
private int mSize = 0;
public void evictAll() {
@@ -57,7 +56,7 @@ public class UserHistoryDictionaryBigramList {
if (mBigramMap.containsKey(word1)) {
map = mBigramMap.get(word1);
} else {
- map = new HashMap<String, Byte>();
+ map = CollectionUtils.newHashMap();
mBigramMap.put(word1, map);
}
if (!map.containsKey(word2)) {
@@ -98,11 +97,11 @@ public class UserHistoryDictionaryBigramList {
}
public HashMap<String, Byte> getBigrams(String word1) {
- if (!mBigramMap.containsKey(word1)) {
- return EMPTY_BIGRAM_MAP;
- } else {
- return mBigramMap.get(word1);
- }
+ if (mBigramMap.containsKey(word1)) return mBigramMap.get(word1);
+ // TODO: lower case according to locale
+ final String lowerWord1 = word1.toLowerCase();
+ if (mBigramMap.containsKey(lowerWord1)) return mBigramMap.get(lowerWord1);
+ return EMPTY_BIGRAM_MAP;
}
public boolean removeBigram(String word1, String word2) {
diff --git a/java/src/com/android/inputmethod/latin/UserHistoryForgettingCurveUtils.java b/java/src/com/android/inputmethod/latin/UserHistoryForgettingCurveUtils.java
index e5516dc62..9053d709b 100644
--- a/java/src/com/android/inputmethod/latin/UserHistoryForgettingCurveUtils.java
+++ b/java/src/com/android/inputmethod/latin/UserHistoryForgettingCurveUtils.java
@@ -19,7 +19,7 @@ package com.android.inputmethod.latin;
import android.text.format.DateUtils;
import android.util.Log;
-public class UserHistoryForgettingCurveUtils {
+public final class UserHistoryForgettingCurveUtils {
private static final String TAG = UserHistoryForgettingCurveUtils.class.getSimpleName();
private static final boolean DEBUG = false;
private static final int FC_FREQ_MAX = 127;
@@ -36,7 +36,7 @@ public class UserHistoryForgettingCurveUtils {
// This utility class is not publicly instantiable.
}
- public static class ForgettingCurveParams {
+ public static final class ForgettingCurveParams {
private byte mFc;
long mLastTouchedTime = 0;
private final boolean mIsValid;
@@ -50,7 +50,7 @@ public class UserHistoryForgettingCurveUtils {
}
private ForgettingCurveParams(long now, boolean isValid) {
- this((int)pushCount((byte)0, isValid), now, now, isValid);
+ this(pushCount((byte)0, isValid), now, now, isValid);
}
/** This constructor is called when the user history bigram dictionary is being restored. */
@@ -195,24 +195,24 @@ public class UserHistoryForgettingCurveUtils {
return (elapsedTime < ELAPSED_TIME_MAX - 1 || level > 0);
}
- private static class MathUtils {
+ private static final class MathUtils {
public static final int[][] SCORE_TABLE = new int[FC_LEVEL_MAX][ELAPSED_TIME_MAX + 1];
static {
for (int i = 0; i < FC_LEVEL_MAX; ++i) {
- final double initialFreq;
+ final float initialFreq;
if (i >= 2) {
- initialFreq = (double)FC_FREQ_MAX;
+ initialFreq = FC_FREQ_MAX;
} else if (i == 1) {
- initialFreq = (double)FC_FREQ_MAX / 2;
+ initialFreq = FC_FREQ_MAX / 2;
} else if (i == 0) {
- initialFreq = (double)FC_FREQ_MAX / 4;
+ initialFreq = FC_FREQ_MAX / 4;
} else {
continue;
}
for (int j = 0; j < ELAPSED_TIME_MAX; ++j) {
- final double elapsedHour = j * ELAPSED_TIME_INTERVAL_HOURS;
- final double freq =
- initialFreq * Math.pow(initialFreq, elapsedHour / HALF_LIFE_HOURS);
+ final float elapsedHours = j * ELAPSED_TIME_INTERVAL_HOURS;
+ final float freq = initialFreq
+ * (float)Math.pow(initialFreq, elapsedHours / HALF_LIFE_HOURS);
final int intFreq = Math.min(FC_FREQ_MAX, Math.max(0, (int)freq));
SCORE_TABLE[i][j] = intFreq;
}
diff --git a/java/src/com/android/inputmethod/latin/Utils.java b/java/src/com/android/inputmethod/latin/Utils.java
index 4178955bc..876bc8e79 100644
--- a/java/src/com/android/inputmethod/latin/Utils.java
+++ b/java/src/com/android/inputmethod/latin/Utils.java
@@ -16,20 +16,16 @@
package com.android.inputmethod.latin;
-import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
-import android.content.res.Resources;
import android.inputmethodservice.InputMethodService;
import android.net.Uri;
import android.os.AsyncTask;
-import android.os.Build;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Process;
import android.text.TextUtils;
-import android.text.format.DateUtils;
import android.util.Log;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
@@ -44,12 +40,9 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.nio.channels.FileChannel;
import java.text.SimpleDateFormat;
-import java.util.Collections;
import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
-public class Utils {
+public final class Utils {
private Utils() {
// This utility class is not publicly instantiable.
}
@@ -67,44 +60,6 @@ public class Utils {
}
}
- public static class GCUtils {
- private static final String GC_TAG = GCUtils.class.getSimpleName();
- public static final int GC_TRY_COUNT = 2;
- // GC_TRY_LOOP_MAX is used for the hard limit of GC wait,
- // GC_TRY_LOOP_MAX should be greater than GC_TRY_COUNT.
- public static final int GC_TRY_LOOP_MAX = 5;
- private static final long GC_INTERVAL = DateUtils.SECOND_IN_MILLIS;
- private static GCUtils sInstance = new GCUtils();
- private int mGCTryCount = 0;
-
- public static GCUtils getInstance() {
- return sInstance;
- }
-
- public void reset() {
- mGCTryCount = 0;
- }
-
- public boolean tryGCOrWait(String metaData, Throwable t) {
- if (mGCTryCount == 0) {
- System.gc();
- }
- if (++mGCTryCount > GC_TRY_COUNT) {
- LatinImeLogger.logOnException(metaData, t);
- return false;
- } else {
- try {
- Thread.sleep(GC_INTERVAL);
- return true;
- } catch (InterruptedException e) {
- Log.e(GC_TAG, "Sleep was interrupted.");
- LatinImeLogger.logOnException(metaData, t);
- return false;
- }
- }
- }
- }
-
/* package */ static class RingCharBuffer {
private static RingCharBuffer sRingCharBuffer = new RingCharBuffer();
private static final char PLACEHOLDER_DELIMITER_CHAR = '\uFFFC';
@@ -206,19 +161,25 @@ public class Utils {
}
// Get the current stack trace
- public static String getStackTrace() {
+ public static String getStackTrace(final int limit) {
StringBuilder sb = new StringBuilder();
try {
throw new RuntimeException();
} catch (RuntimeException e) {
StackTraceElement[] frames = e.getStackTrace();
// Start at 1 because the first frame is here and we don't care about it
- for (int j = 1; j < frames.length; ++j) sb.append(frames[j].toString() + "\n");
+ for (int j = 1; j < frames.length && j < limit + 1; ++j) {
+ sb.append(frames[j].toString() + "\n");
+ }
}
return sb.toString();
}
- public static class UsabilityStudyLogUtils {
+ public static String getStackTrace() {
+ return getStackTrace(Integer.MAX_VALUE - 1);
+ }
+
+ public static final class UsabilityStudyLogUtils {
// TODO: remove code duplication with ResearchLog class
private static final String USABILITY_TAG = UsabilityStudyLogUtils.class.getSimpleName();
private static final String FILENAME = "log.txt";
@@ -427,34 +388,48 @@ public class Utils {
}
}
- public static float getDipScale(Context context) {
- final float scale = context.getResources().getDisplayMetrics().density;
- return scale;
- }
-
- /** Convert pixel to DIP */
- public static int dipToPixel(float scale, int dip) {
- return (int) (dip * scale + 0.5);
- }
-
- public static class Stats {
+ public static final class Stats {
public static void onNonSeparator(final char code, final int x,
final int y) {
RingCharBuffer.getInstance().push(code, x, y);
LatinImeLogger.logOnInputChar();
}
- public static void onSeparator(final int code, final int x,
- final int y) {
- // TODO: accept code points
- RingCharBuffer.getInstance().push((char)code, x, y);
+ public static void onSeparator(final int code, final int x, final int y) {
+ // Helper method to log a single code point separator
+ // TODO: cache this mapping of a code point to a string in a sparse array in StringUtils
+ onSeparator(new String(new int[]{code}, 0, 1), x, y);
+ }
+
+ public static void onSeparator(final String separator, final int x, final int y) {
+ final int length = separator.length();
+ for (int i = 0; i < length; i = Character.offsetByCodePoints(separator, i, 1)) {
+ int codePoint = Character.codePointAt(separator, i);
+ // TODO: accept code points
+ RingCharBuffer.getInstance().push((char)codePoint, x, y);
+ }
LatinImeLogger.logOnInputSeparator();
}
public static void onAutoCorrection(final String typedWord, final String correctedWord,
- final int separatorCode) {
- if (TextUtils.isEmpty(typedWord)) return;
- LatinImeLogger.logOnAutoCorrection(typedWord, correctedWord, separatorCode);
+ 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);
+ 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() {
@@ -470,60 +445,4 @@ public class Utils {
if (TextUtils.isEmpty(info)) return null;
return info;
}
-
- private static final String HARDWARE_PREFIX = Build.HARDWARE + ",";
- private static final HashMap<String, String> sDeviceOverrideValueMap =
- new HashMap<String, String>();
-
- public static String getDeviceOverrideValue(Resources res, int overrideResId, String defValue) {
- final int orientation = res.getConfiguration().orientation;
- final String key = overrideResId + "-" + orientation;
- if (!sDeviceOverrideValueMap.containsKey(key)) {
- String overrideValue = defValue;
- for (final String element : res.getStringArray(overrideResId)) {
- if (element.startsWith(HARDWARE_PREFIX)) {
- overrideValue = element.substring(HARDWARE_PREFIX.length());
- break;
- }
- }
- sDeviceOverrideValueMap.put(key, overrideValue);
- }
- return sDeviceOverrideValueMap.get(key);
- }
-
- private static final HashMap<String, Long> EMPTY_LT_HASH_MAP = new HashMap<String, Long>();
- private static final String LOCALE_AND_TIME_STR_SEPARATER = ",";
- public static HashMap<String, Long> localeAndTimeStrToHashMap(String str) {
- if (TextUtils.isEmpty(str)) {
- return EMPTY_LT_HASH_MAP;
- }
- final String[] ss = str.split(LOCALE_AND_TIME_STR_SEPARATER);
- final int N = ss.length;
- if (N < 2 || N % 2 != 0) {
- return EMPTY_LT_HASH_MAP;
- }
- final HashMap<String, Long> retval = new HashMap<String, Long>();
- for (int i = 0; i < N / 2; ++i) {
- final String localeStr = ss[i * 2];
- final long time = Long.valueOf(ss[i * 2 + 1]);
- retval.put(localeStr, time);
- }
- return retval;
- }
-
- public static String localeAndTimeHashMapToStr(HashMap<String, Long> map) {
- if (map == null || map.isEmpty()) {
- return "";
- }
- final StringBuilder builder = new StringBuilder();
- for (String localeStr : map.keySet()) {
- if (builder.length() > 0) {
- builder.append(LOCALE_AND_TIME_STR_SEPARATER);
- }
- final Long time = map.get(localeStr);
- builder.append(localeStr).append(LOCALE_AND_TIME_STR_SEPARATER);
- builder.append(String.valueOf(time));
- }
- return builder.toString();
- }
}
diff --git a/java/src/com/android/inputmethod/latin/VibratorUtils.java b/java/src/com/android/inputmethod/latin/VibratorUtils.java
index 33ffdd9c9..b6696cec0 100644
--- a/java/src/com/android/inputmethod/latin/VibratorUtils.java
+++ b/java/src/com/android/inputmethod/latin/VibratorUtils.java
@@ -19,7 +19,7 @@ package com.android.inputmethod.latin;
import android.content.Context;
import android.os.Vibrator;
-public class VibratorUtils {
+public final class VibratorUtils {
private static final VibratorUtils sInstance = new VibratorUtils();
private Vibrator mVibrator;
diff --git a/java/src/com/android/inputmethod/latin/WhitelistDictionary.java b/java/src/com/android/inputmethod/latin/WhitelistDictionary.java
deleted file mode 100644
index a0de2f970..000000000
--- a/java/src/com/android/inputmethod/latin/WhitelistDictionary.java
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-
-package com.android.inputmethod.latin;
-
-import android.content.Context;
-import android.content.res.Resources;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.Pair;
-
-import com.android.inputmethod.latin.LocaleUtils.RunInLocale;
-
-import java.util.HashMap;
-import java.util.Locale;
-
-public class WhitelistDictionary extends ExpandableDictionary {
-
- private static final boolean DBG = LatinImeLogger.sDBG;
- private static final String TAG = WhitelistDictionary.class.getSimpleName();
-
- private final HashMap<String, Pair<Integer, String>> mWhitelistWords =
- new HashMap<String, Pair<Integer, String>>();
-
- // TODO: Conform to the async load contact of ExpandableDictionary
- public WhitelistDictionary(final Context context, final Locale locale) {
- super(context, Suggest.DIC_WHITELIST);
- // TODO: Move whitelist dictionary into main dictionary.
- final RunInLocale<Void> job = new RunInLocale<Void>() {
- @Override
- protected Void job(Resources res) {
- initWordlist(res.getStringArray(R.array.wordlist_whitelist));
- return null;
- }
- };
- job.runInLocale(context.getResources(), locale);
- }
-
- private void initWordlist(String[] wordlist) {
- mWhitelistWords.clear();
- final int N = wordlist.length;
- if (N % 3 != 0) {
- if (DBG) {
- Log.d(TAG, "The number of the whitelist is invalid.");
- }
- return;
- }
- try {
- for (int i = 0; i < N; i += 3) {
- final int score = Integer.valueOf(wordlist[i]);
- final String before = wordlist[i + 1];
- final String after = wordlist[i + 2];
- if (before != null && after != null) {
- mWhitelistWords.put(
- before.toLowerCase(), new Pair<Integer, String>(score, after));
- addWord(after, null /* shortcut */, score);
- }
- }
- } catch (NumberFormatException e) {
- if (DBG) {
- Log.d(TAG, "The score of the word is invalid.");
- }
- }
- }
-
- public String getWhitelistedWord(String before) {
- if (before == null) return null;
- final String lowerCaseBefore = before.toLowerCase();
- if(mWhitelistWords.containsKey(lowerCaseBefore)) {
- if (DBG) {
- Log.d(TAG, "--- found whitelistedWord: " + lowerCaseBefore);
- }
- return mWhitelistWords.get(lowerCaseBefore).second;
- }
- return null;
- }
-
- // See LatinIME#updateSuggestions. This breaks in the (queer) case that the whitelist
- // lists that word a should autocorrect to word b, and word c would autocorrect to
- // an upper-cased version of a. In this case, the way this return value is used would
- // remove the first candidate when the user typed the upper-cased version of A.
- // Example : abc -> def and xyz -> Abc
- // A user typing Abc would experience it being autocorrected to something else (not
- // necessarily def).
- // There is no such combination in the whitelist at the time and there probably won't
- // ever be - it doesn't make sense. But still.
- public boolean shouldForciblyAutoCorrectFrom(CharSequence word) {
- if (TextUtils.isEmpty(word)) return false;
- final String correction = getWhitelistedWord(word.toString());
- if (TextUtils.isEmpty(correction)) return false;
- return !correction.equals(word);
- }
-
- // Leave implementation of getWords and isValidWord to the superclass.
- // The words have been added to the ExpandableDictionary with addWord() inside initWordlist.
-}
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
index ca9caa1d3..da0071adc 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -17,32 +17,35 @@
package com.android.inputmethod.latin;
import com.android.inputmethod.keyboard.Key;
-import com.android.inputmethod.keyboard.KeyDetector;
import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.keyboard.KeyboardActionListener;
import java.util.Arrays;
/**
* A place to store the currently composing word with information such as adjacent key codes as well
*/
-public class WordComposer {
-
- public static final int NOT_A_CODE = KeyDetector.NOT_A_CODE;
- public static final int NOT_A_COORDINATE = -1;
-
+public final class WordComposer {
private static final int N = BinaryDictionary.MAX_WORD_LENGTH;
+ public static final int CAPS_MODE_OFF = 0;
+ // 1 is shift bit, 2 is caps bit, 4 is auto bit but this is just a convention as these bits
+ // aren't used anywhere in the code
+ public static final int CAPS_MODE_MANUAL_SHIFTED = 0x1;
+ public static final int CAPS_MODE_MANUAL_SHIFT_LOCKED = 0x3;
+ public static final int CAPS_MODE_AUTO_SHIFTED = 0x5;
+ public static final int CAPS_MODE_AUTO_SHIFT_LOCKED = 0x7;
+
private int[] mPrimaryKeyCodes;
- private int[] mXCoordinates;
- private int[] mYCoordinates;
- private StringBuilder mTypedWord;
+ private final InputPointers mInputPointers = new InputPointers(N);
+ private final StringBuilder mTypedWord;
private CharSequence mAutoCorrection;
private boolean mIsResumed;
+ private boolean mIsBatchMode;
// Cache these values for performance
private int mCapsCount;
- private boolean mAutoCapitalized;
+ private int mDigitsCount;
+ private int mCapitalizedMode;
private int mTrailingSingleQuotesCount;
private int mCodePointSize;
@@ -54,28 +57,24 @@ public class WordComposer {
public WordComposer() {
mPrimaryKeyCodes = new int[N];
mTypedWord = new StringBuilder(N);
- mXCoordinates = new int[N];
- mYCoordinates = new int[N];
mAutoCorrection = null;
mTrailingSingleQuotesCount = 0;
mIsResumed = false;
+ mIsBatchMode = false;
refreshSize();
}
public WordComposer(WordComposer source) {
- init(source);
- }
-
- public void init(WordComposer source) {
mPrimaryKeyCodes = Arrays.copyOf(source.mPrimaryKeyCodes, source.mPrimaryKeyCodes.length);
mTypedWord = new StringBuilder(source.mTypedWord);
- mXCoordinates = Arrays.copyOf(source.mXCoordinates, source.mXCoordinates.length);
- mYCoordinates = Arrays.copyOf(source.mYCoordinates, source.mYCoordinates.length);
+ mInputPointers.copy(source.mInputPointers);
mCapsCount = source.mCapsCount;
+ mDigitsCount = source.mDigitsCount;
mIsFirstCharCapitalized = source.mIsFirstCharCapitalized;
- mAutoCapitalized = source.mAutoCapitalized;
+ mCapitalizedMode = source.mCapitalizedMode;
mTrailingSingleQuotesCount = source.mTrailingSingleQuotesCount;
mIsResumed = source.mIsResumed;
+ mIsBatchMode = source.mIsBatchMode;
refreshSize();
}
@@ -86,13 +85,15 @@ public class WordComposer {
mTypedWord.setLength(0);
mAutoCorrection = null;
mCapsCount = 0;
+ mDigitsCount = 0;
mIsFirstCharCapitalized = false;
mTrailingSingleQuotesCount = 0;
mIsResumed = false;
+ mIsBatchMode = false;
refreshSize();
}
- public final void refreshSize() {
+ private final void refreshSize() {
mCodePointSize = mTypedWord.codePointCount(0, mTypedWord.length());
}
@@ -116,12 +117,8 @@ public class WordComposer {
return mPrimaryKeyCodes[index];
}
- public int[] getXCoordinates() {
- return mXCoordinates;
- }
-
- public int[] getYCoordinates() {
- return mYCoordinates;
+ public InputPointers getInputPointers() {
+ return mInputPointers;
}
private static boolean isFirstCharCapitalized(int index, int codePoint, boolean previous) {
@@ -129,40 +126,28 @@ public class WordComposer {
return previous && !Character.isUpperCase(codePoint);
}
- // TODO: remove input keyDetector
- public void add(int primaryCode, int x, int y, KeyDetector keyDetector) {
- final int keyX;
- final int keyY;
- if (null == keyDetector
- || x == KeyboardActionListener.SUGGESTION_STRIP_COORDINATE
- || y == KeyboardActionListener.SUGGESTION_STRIP_COORDINATE
- || x == KeyboardActionListener.NOT_A_TOUCH_COORDINATE
- || y == KeyboardActionListener.NOT_A_TOUCH_COORDINATE) {
- keyX = x;
- keyY = y;
- } else {
- keyX = keyDetector.getTouchX(x);
- keyY = keyDetector.getTouchY(y);
- }
- add(primaryCode, keyX, keyY);
- }
-
/**
* Add a new keystroke, with the pressed key's code point with the touch point coordinates.
*/
- private void add(int primaryCode, int keyX, int keyY) {
+ public void add(int primaryCode, int keyX, int keyY) {
final int newIndex = size();
mTypedWord.appendCodePoint(primaryCode);
refreshSize();
if (newIndex < BinaryDictionary.MAX_WORD_LENGTH) {
mPrimaryKeyCodes[newIndex] = primaryCode >= Keyboard.CODE_SPACE
? Character.toLowerCase(primaryCode) : primaryCode;
- mXCoordinates[newIndex] = keyX;
- mYCoordinates[newIndex] = keyY;
+ // In the batch input mode, the {@code mInputPointers} holds batch input points and
+ // shouldn't be overridden by the "typed key" coordinates
+ // (See {@link #setBatchInputWord}).
+ if (!mIsBatchMode) {
+ // TODO: Set correct pointer id and time
+ mInputPointers.addPointer(newIndex, keyX, keyY, 0, 0);
+ }
}
mIsFirstCharCapitalized = isFirstCharCapitalized(
newIndex, primaryCode, mIsFirstCharCapitalized);
if (Character.isUpperCase(primaryCode)) mCapsCount++;
+ if (Character.isDigit(primaryCode)) mDigitsCount++;
if (Keyboard.CODE_SINGLE_QUOTE == primaryCode) {
++mTrailingSingleQuotesCount;
} else {
@@ -171,19 +156,37 @@ public class WordComposer {
mAutoCorrection = null;
}
+ public void setBatchInputPointers(InputPointers batchPointers) {
+ mInputPointers.set(batchPointers);
+ mIsBatchMode = true;
+ }
+
+ public void setBatchInputWord(CharSequence word) {
+ reset();
+ mIsBatchMode = true;
+ final int length = word.length();
+ for (int i = 0; i < length; i = Character.offsetByCodePoints(word, i, 1)) {
+ final int codePoint = Character.codePointAt(word, i);
+ // We don't want to override the batch input points that are held in mInputPointers
+ // (See {@link #add(int,int,int)}).
+ add(codePoint, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
+ }
+ }
+
/**
* Internal method to retrieve reasonable proximity info for a character.
*/
private void addKeyInfo(final int codePoint, final Keyboard keyboard) {
- for (final Key key : keyboard.mKeys) {
- if (key.mCode == codePoint) {
- final int x = key.mX + key.mWidth / 2;
- final int y = key.mY + key.mHeight / 2;
- add(codePoint, x, y);
- return;
- }
+ final int x, y;
+ final Key key;
+ if (keyboard != null && (key = keyboard.getKey(codePoint)) != null) {
+ x = key.mX + key.mWidth / 2;
+ y = key.mY + key.mHeight / 2;
+ } else {
+ x = Constants.NOT_A_COORDINATE;
+ y = Constants.NOT_A_COORDINATE;
}
- add(codePoint, WordComposer.NOT_A_COORDINATE, WordComposer.NOT_A_COORDINATE);
+ add(codePoint, x, y);
}
/**
@@ -194,7 +197,7 @@ public class WordComposer {
reset();
final int length = word.length();
for (int i = 0; i < length; i = Character.offsetByCodePoints(word, i, 1)) {
- int codePoint = Character.codePointAt(word, i);
+ final int codePoint = Character.codePointAt(word, i);
addKeyInfo(codePoint, keyboard);
}
mIsResumed = true;
@@ -219,6 +222,7 @@ public class WordComposer {
mTypedWord.deleteCharAt(stringBuilderLength - 1);
}
if (Character.isUpperCase(lastChar)) mCapsCount--;
+ if (Character.isDigit(lastChar)) mDigitsCount--;
refreshSize();
}
// We may have deleted the last one.
@@ -263,7 +267,17 @@ public class WordComposer {
* @return true if all user typed chars are upper case, false otherwise
*/
public boolean isAllUpperCase() {
- return (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() {
+ return mCapitalizedMode == CAPS_MODE_AUTO_SHIFTED
+ || mCapitalizedMode == CAPS_MODE_MANUAL_SHIFTED;
}
/**
@@ -274,20 +288,34 @@ public class WordComposer {
}
/**
- * Saves the reason why the word is capitalized - whether it was automatic or
- * due to the user hitting shift in the middle of a sentence.
- * @param auto whether it was an automatic capitalization due to start of sentence
+ * Returns true if we have digits in the composing word.
*/
- public void setAutoCapitalized(boolean auto) {
- mAutoCapitalized = auto;
+ public boolean hasDigits() {
+ return mDigitsCount > 0;
+ }
+
+ /**
+ * Saves the caps mode at the start of composing.
+ *
+ * WordComposer needs to know about this for several reasons. The first is, we need to know
+ * after the fact what the reason was, to register the correct form into the user history
+ * dictionary: if the word was automatically capitalized, we should insert it in all-lower
+ * case but if it's a manual pressing of shift, then it should be inserted as is.
+ * Also, batch input needs to know about the current caps mode to display correctly
+ * capitalized suggestions.
+ * @param mode the mode at the time of start
+ */
+ public void setCapitalizedModeAtStartComposingTime(final int mode) {
+ mCapitalizedMode = mode;
}
/**
* Returns whether the word was automatically capitalized.
* @return whether the word was automatically capitalized
*/
- public boolean isAutoCapitalized() {
- return mAutoCapitalized;
+ public boolean wasAutoCapitalized() {
+ return mCapitalizedMode == CAPS_MODE_AUTO_SHIFT_LOCKED
+ || mCapitalizedMode == CAPS_MODE_AUTO_SHIFTED;
}
/**
@@ -313,24 +341,26 @@ public class WordComposer {
// `type' should be one of the LastComposedWord.COMMIT_TYPE_* constants above.
public LastComposedWord commitWord(final int type, final String committedWord,
- final int separatorCode, final CharSequence prevWord) {
+ final String separatorString, final CharSequence prevWord) {
// Note: currently, we come here whenever we commit a word. If it's a MANUAL_PICK
// or a DECIDED_WORD we may cancel the commit later; otherwise, we should deactivate
// the last composed word to ensure this does not happen.
final int[] primaryKeyCodes = mPrimaryKeyCodes;
- final int[] xCoordinates = mXCoordinates;
- final int[] yCoordinates = mYCoordinates;
mPrimaryKeyCodes = new int[N];
- mXCoordinates = new int[N];
- mYCoordinates = new int[N];
final LastComposedWord lastComposedWord = new LastComposedWord(primaryKeyCodes,
- xCoordinates, yCoordinates, mTypedWord.toString(), committedWord, separatorCode,
+ mInputPointers, mTypedWord.toString(), committedWord, separatorString,
prevWord);
+ mInputPointers.reset();
if (type != LastComposedWord.COMMIT_TYPE_DECIDED_WORD
&& type != LastComposedWord.COMMIT_TYPE_MANUAL_PICK) {
lastComposedWord.deactivate();
}
+ mCapsCount = 0;
+ mDigitsCount = 0;
+ mIsBatchMode = false;
mTypedWord.setLength(0);
+ mTrailingSingleQuotesCount = 0;
+ mIsFirstCharCapitalized = false;
refreshSize();
mAutoCorrection = null;
mIsResumed = false;
@@ -339,12 +369,15 @@ public class WordComposer {
public void resumeSuggestionOnLastComposedWord(final LastComposedWord lastComposedWord) {
mPrimaryKeyCodes = lastComposedWord.mPrimaryKeyCodes;
- mXCoordinates = lastComposedWord.mXCoordinates;
- mYCoordinates = lastComposedWord.mYCoordinates;
+ mInputPointers.set(lastComposedWord.mInputPointers);
mTypedWord.setLength(0);
mTypedWord.append(lastComposedWord.mTypedWord);
refreshSize();
mAutoCorrection = null; // This will be filled by the next call to updateSuggestion.
mIsResumed = true;
}
+
+ public boolean isBatchMode() {
+ return mIsBatchMode;
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/WordListInfo.java b/java/src/com/android/inputmethod/latin/WordListInfo.java
index 54f04d78f..095320e25 100644
--- a/java/src/com/android/inputmethod/latin/WordListInfo.java
+++ b/java/src/com/android/inputmethod/latin/WordListInfo.java
@@ -19,7 +19,7 @@ package com.android.inputmethod.latin;
/**
* Information container for a word list.
*/
-public class WordListInfo {
+public final class WordListInfo {
public final String mId;
public final String mLocale;
public WordListInfo(final String id, final String locale) {
diff --git a/java/src/com/android/inputmethod/latin/XmlParseUtils.java b/java/src/com/android/inputmethod/latin/XmlParseUtils.java
index 481cdfa47..75dc68f1d 100644
--- a/java/src/com/android/inputmethod/latin/XmlParseUtils.java
+++ b/java/src/com/android/inputmethod/latin/XmlParseUtils.java
@@ -23,7 +23,7 @@ import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
-public class XmlParseUtils {
+public final class XmlParseUtils {
private XmlParseUtils() {
// This utility class is not publicly instantiable.
}
@@ -36,28 +36,28 @@ public class XmlParseUtils {
}
@SuppressWarnings("serial")
- public static class IllegalStartTag extends ParseException {
+ public static final class IllegalStartTag extends ParseException {
public IllegalStartTag(XmlPullParser parser, String parent) {
super("Illegal start tag " + parser.getName() + " in " + parent, parser);
}
}
@SuppressWarnings("serial")
- public static class IllegalEndTag extends ParseException {
+ public static final class IllegalEndTag extends ParseException {
public IllegalEndTag(XmlPullParser parser, String parent) {
super("Illegal end tag " + parser.getName() + " in " + parent, parser);
}
}
@SuppressWarnings("serial")
- public static class IllegalAttribute extends ParseException {
+ public static final class IllegalAttribute extends ParseException {
public IllegalAttribute(XmlPullParser parser, String attribute) {
super("Tag " + parser.getName() + " has illegal attribute " + attribute, parser);
}
}
@SuppressWarnings("serial")
- public static class NonEmptyTag extends ParseException{
+ public static final class NonEmptyTag extends ParseException{
public NonEmptyTag(String tag, XmlPullParser parser) {
super(tag + " must be empty tag", parser);
}
diff --git a/java/src/com/android/inputmethod/latin/define/ProductionFlag.java b/java/src/com/android/inputmethod/latin/define/ProductionFlag.java
index de2057669..52c066a44 100644
--- a/java/src/com/android/inputmethod/latin/define/ProductionFlag.java
+++ b/java/src/com/android/inputmethod/latin/define/ProductionFlag.java
@@ -22,4 +22,5 @@ public final class ProductionFlag {
}
public static final boolean IS_EXPERIMENTAL = false;
+ public static final boolean IS_INTERNAL = false;
}
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
new file mode 100644
index 000000000..7b0231a6b
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
@@ -0,0 +1,282 @@
+/*
+ * 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 final class BinaryDictIOUtils {
+ private static final boolean DBG = false;
+
+ private static final 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;
+ }
+ if (p.mNumOfCharGroup == 0) {
+ stack.pop();
+ continue;
+ }
+ 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++;
+
+ final boolean isMovedGroup = BinaryDictInputOutput.isMovedGroup(info.mFlags,
+ formatOptions);
+ if (!isMovedGroup
+ && 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) {
+ if (formatOptions.mSupportsDynamicUpdate) {
+ final int forwardLinkAddress = buffer.readUnsignedInt24();
+ if (forwardLinkAddress != FormatSpec.NO_FORWARD_LINK_ADDRESS) {
+ // the node has a forward link.
+ p.mNumOfCharGroup = Position.NOT_READ_GROUPCOUNT;
+ p.mAddress = forwardLinkAddress;
+ } else {
+ stack.pop();
+ }
+ } else {
+ stack.pop();
+ }
+ } else {
+ // the node has more groups.
+ p.mAddress = buffer.position();
+ }
+
+ if (!isMovedGroup && 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;
+
+ do {
+ int groupOffset = buffer.position() - header.mHeaderSize;
+ final int charGroupCount = BinaryDictInputOutput.readCharGroupCount(buffer);
+ groupOffset += BinaryDictInputOutput.getGroupCountSize(charGroupCount);
+
+ boolean foundNextCharGroup = false;
+ for (int i = 0; i < charGroupCount; ++i) {
+ final int charGroupPos = buffer.position();
+ final CharGroupInfo currentInfo = BinaryDictInputOutput.readCharGroup(buffer,
+ buffer.position(), header.mFormatOptions);
+ if (BinaryDictInputOutput.isMovedGroup(currentInfo.mFlags,
+ header.mFormatOptions)) {
+ continue;
+ }
+ 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) {
+ // found the group matches the word.
+ 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;
+ }
+ foundNextCharGroup = true;
+ buffer.position(currentInfo.mChildrenAddress);
+ break;
+ }
+ groupOffset = currentInfo.mEndAddress;
+ }
+
+ // If we found the next char group, it is under the file pointer.
+ // But if not, we are at the end of this node so we expect to have
+ // a forward link address that we need to consult and possibly resume
+ // search on the next node in the linked list.
+ if (foundNextCharGroup) break;
+ if (!header.mFormatOptions.mSupportsDynamicUpdate) {
+ return FormatSpec.NOT_VALID_WORD;
+ }
+
+ final int forwardLinkAddress = buffer.readUnsignedInt24();
+ if (forwardLinkAddress == FormatSpec.NO_FORWARD_LINK_ADDRESS) {
+ return FormatSpec.NOT_VALID_WORD;
+ }
+ buffer.position(forwardLinkAddress);
+ } while(true);
+ }
+ return FormatSpec.NOT_VALID_WORD;
+ }
+
+ /**
+ * Delete the word from the binary file.
+ *
+ * @param buffer the buffer to write.
+ * @param word the word we delete
+ * @throws IOException
+ * @throws UnsupportedFormatException
+ */
+ public static void deleteWord(final FusionDictionaryBufferInterface buffer,
+ final String word) throws IOException, UnsupportedFormatException {
+ buffer.position(0);
+ final FileHeader header = BinaryDictInputOutput.readHeader(buffer);
+ final int wordPosition = getTerminalPosition(buffer, word);
+ if (wordPosition == FormatSpec.NOT_VALID_WORD) return;
+
+ buffer.position(wordPosition);
+ final int flags = buffer.readUnsignedByte();
+ final int newFlags = flags ^ FormatSpec.FLAG_IS_TERMINAL;
+ buffer.position(wordPosition);
+ buffer.put((byte)newFlags);
+ }
+
+ private static void putSInt24(final FusionDictionaryBufferInterface buffer,
+ final int value) {
+ final int absValue = Math.abs(value);
+ buffer.put((byte)(((value < 0 ? 0x80 : 0) | (absValue >> 16)) & 0xFF));
+ buffer.put((byte)((absValue >> 8) & 0xFF));
+ buffer.put((byte)(absValue & 0xFF));
+ }
+
+ /**
+ * Update a parent address in a CharGroup that is addressed by groupOriginAddress.
+ *
+ * @param buffer the buffer to write.
+ * @param groupOriginAddress the address of the group.
+ * @param newParentAddress the absolute address of the parent.
+ * @param formatOptions file format options.
+ */
+ public static void updateParentAddress(final FusionDictionaryBufferInterface buffer,
+ final int groupOriginAddress, final int newParentAddress,
+ final FormatOptions formatOptions) {
+ final int originalPosition = buffer.position();
+ buffer.position(groupOriginAddress);
+ if (!formatOptions.mSupportsDynamicUpdate) {
+ throw new RuntimeException("this file format does not support parent addresses");
+ }
+ final int flags = buffer.readUnsignedByte();
+ final int parentOffset = newParentAddress - groupOriginAddress;
+ putSInt24(buffer, parentOffset);
+ buffer.position(originalPosition);
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java
index 89c59f809..b431a4da9 100644
--- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java
+++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java
@@ -16,16 +16,21 @@
package com.android.inputmethod.latin.makedict;
+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 com.android.inputmethod.latin.makedict.FusionDictionary.DictionaryOptions;
import com.android.inputmethod.latin.makedict.FusionDictionary.Node;
import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
-import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -38,145 +43,9 @@ import java.util.TreeMap;
*
* All the methods in this class are static.
*/
-public class BinaryDictInputOutput {
-
- final static boolean DBG = MakedictLog.DBG;
-
- /* Node layout is as follows:
- * | addressType xx : mask with MASK_GROUP_ADDRESS_TYPE
- * 2 bits, 00 = no children : FLAG_GROUP_ADDRESS_TYPE_NOADDRESS
- * f | 01 = 1 byte : FLAG_GROUP_ADDRESS_TYPE_ONEBYTE
- * l | 10 = 2 bytes : FLAG_GROUP_ADDRESS_TYPE_TWOBYTES
- * a | 11 = 3 bytes : FLAG_GROUP_ADDRESS_TYPE_THREEBYTES
- * g | has several chars ? 1 bit, 1 = yes, 0 = no : FLAG_HAS_MULTIPLE_CHARS
- * s | has a terminal ? 1 bit, 1 = yes, 0 = no : FLAG_IS_TERMINAL
- * | has shortcut targets ? 1 bit, 1 = yes, 0 = no : FLAG_HAS_SHORTCUT_TARGETS
- * | has bigrams ? 1 bit, 1 = yes, 0 = no : FLAG_HAS_BIGRAMS
- *
- * c | IF FLAG_HAS_MULTIPLE_CHARS
- * h | char, char, char, char n * (1 or 3 bytes) : use CharGroupInfo for i/o helpers
- * a | end 1 byte, = 0
- * r | ELSE
- * s | char 1 or 3 bytes
- * | END
- *
- * f |
- * r | IF FLAG_IS_TERMINAL
- * e | frequency 1 byte
- * q |
- *
- * c | IF 00 = FLAG_GROUP_ADDRESS_TYPE_NOADDRESS = addressType
- * h | // nothing
- * i | ELSIF 01 = FLAG_GROUP_ADDRESS_TYPE_ONEBYTE == addressType
- * l | children address, 1 byte
- * d | ELSIF 10 = FLAG_GROUP_ADDRESS_TYPE_TWOBYTES == addressType
- * r | children address, 2 bytes
- * e | ELSE // 11 = FLAG_GROUP_ADDRESS_TYPE_THREEBYTES = addressType
- * n | children address, 3 bytes
- * A | END
- * d
- * dress
- *
- * | IF FLAG_IS_TERMINAL && FLAG_HAS_SHORTCUT_TARGETS
- * | shortcut string list
- * | IF FLAG_IS_TERMINAL && FLAG_HAS_BIGRAMS
- * | bigrams address list
- *
- * Char format is:
- * 1 byte = bbbbbbbb match
- * case 000xxxxx: xxxxx << 16 + next byte << 8 + next byte
- * else: if 00011111 (= 0x1F) : this is the terminator. This is a relevant choice because
- * unicode code points range from 0 to 0x10FFFF, so any 3-byte value starting with
- * 00011111 would be outside unicode.
- * else: iso-latin-1 code
- * This allows for the whole unicode range to be encoded, including chars outside of
- * the BMP. Also everything in the iso-latin-1 charset is only 1 byte, except control
- * characters which should never happen anyway (and still work, but take 3 bytes).
- *
- * bigram address list is:
- * <flags> = | hasNext = 1 bit, 1 = yes, 0 = no : FLAG_ATTRIBUTE_HAS_NEXT
- * | addressSign = 1 bit, : FLAG_ATTRIBUTE_OFFSET_NEGATIVE
- * | 1 = must take -address, 0 = must take +address
- * | xx : mask with MASK_ATTRIBUTE_ADDRESS_TYPE
- * | addressFormat = 2 bits, 00 = unused : FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE
- * | 01 = 1 byte : FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE
- * | 10 = 2 bytes : FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES
- * | 11 = 3 bytes : FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES
- * | 4 bits : frequency : mask with FLAG_ATTRIBUTE_FREQUENCY
- * <address> | IF (01 == FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE == addressFormat)
- * | read 1 byte, add top 4 bits
- * | ELSIF (10 == FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES == addressFormat)
- * | read 2 bytes, add top 4 bits
- * | ELSE // 11 == FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES == addressFormat
- * | read 3 bytes, add top 4 bits
- * | END
- * | if (FLAG_ATTRIBUTE_OFFSET_NEGATIVE) then address = -address
- * if (FLAG_ATTRIBUTE_HAS_NEXT) goto bigram_and_shortcut_address_list_is
- *
- * shortcut string list is:
- * <byte size> = GROUP_SHORTCUT_LIST_SIZE_SIZE bytes, big-endian: size of the list, in bytes.
- * <flags> = | hasNext = 1 bit, 1 = yes, 0 = no : FLAG_ATTRIBUTE_HAS_NEXT
- * | reserved = 3 bits, must be 0
- * | 4 bits : frequency : mask with FLAG_ATTRIBUTE_FREQUENCY
- * <shortcut> = | string of characters at the char format described above, with the terminator
- * | used to signal the end of the string.
- * if (FLAG_ATTRIBUTE_HAS_NEXT goto flags
- */
+public final class BinaryDictInputOutput {
- private static final int VERSION_1_MAGIC_NUMBER = 0x78B1;
- private static final int VERSION_2_MAGIC_NUMBER = 0x9BC13AFE;
- private static final int MINIMUM_SUPPORTED_VERSION = 1;
- private static final int MAXIMUM_SUPPORTED_VERSION = 2;
- private static final int NOT_A_VERSION_NUMBER = -1;
- private static final int FIRST_VERSION_WITH_HEADER_SIZE = 2;
-
- // These options need to be the same numeric values as the one in the native reading code.
- private static final int GERMAN_UMLAUT_PROCESSING_FLAG = 0x1;
- private static final int FRENCH_LIGATURE_PROCESSING_FLAG = 0x4;
- private static final int CONTAINS_BIGRAMS_FLAG = 0x8;
-
- // TODO: Make this value adaptative to content data, store it in the header, and
- // use it in the reading code.
- private static final int MAX_WORD_LENGTH = 48;
-
- private static final int MASK_GROUP_ADDRESS_TYPE = 0xC0;
- private static final int FLAG_GROUP_ADDRESS_TYPE_NOADDRESS = 0x00;
- private static final int FLAG_GROUP_ADDRESS_TYPE_ONEBYTE = 0x40;
- private static final int FLAG_GROUP_ADDRESS_TYPE_TWOBYTES = 0x80;
- private static final int FLAG_GROUP_ADDRESS_TYPE_THREEBYTES = 0xC0;
-
- private static final int FLAG_HAS_MULTIPLE_CHARS = 0x20;
-
- private static final int FLAG_IS_TERMINAL = 0x10;
- private static final int FLAG_HAS_SHORTCUT_TARGETS = 0x08;
- private static final int FLAG_HAS_BIGRAMS = 0x04;
-
- private static final int FLAG_ATTRIBUTE_HAS_NEXT = 0x80;
- private static final int FLAG_ATTRIBUTE_OFFSET_NEGATIVE = 0x40;
- private static final int MASK_ATTRIBUTE_ADDRESS_TYPE = 0x30;
- private static final int FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE = 0x10;
- private static final int FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES = 0x20;
- private static final int FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES = 0x30;
- private static final int FLAG_ATTRIBUTE_FREQUENCY = 0x0F;
-
- private static final int GROUP_CHARACTERS_TERMINATOR = 0x1F;
-
- private static final int GROUP_TERMINATOR_SIZE = 1;
- private static final int GROUP_FLAGS_SIZE = 1;
- private static final int GROUP_FREQUENCY_SIZE = 1;
- private static final int GROUP_MAX_ADDRESS_SIZE = 3;
- private static final int GROUP_ATTRIBUTE_FLAGS_SIZE = 1;
- private static final int GROUP_ATTRIBUTE_MAX_ADDRESS_SIZE = 3;
- private static final int GROUP_SHORTCUT_LIST_SIZE_SIZE = 2;
-
- private static final int NO_CHILDREN_ADDRESS = Integer.MIN_VALUE;
- private static final int INVALID_CHARACTER = -1;
-
- private static final int MAX_CHARGROUPS_FOR_ONE_BYTE_CHARGROUP_COUNT = 0x7F; // 127
- private static final int MAX_CHARGROUPS_IN_A_NODE = 0x7FFF; // 32767
-
- private static final int MAX_TERMINAL_FREQUENCY = 255;
- private static final int MAX_BIGRAM_FREQUENCY = 15;
+ private static final boolean DBG = MakedictLog.DBG;
// Arbitrary limit to how much passes we consider address size compression should
// terminate in. At the time of this writing, our largest dictionary completes
@@ -184,11 +53,78 @@ public class BinaryDictInputOutput {
// If the number of passes exceeds this number, makedict bails with an exception on
// suspicion that a bug might be causing an infinite loop.
private static final int MAX_PASSES = 24;
+ private static final int MAX_JUMPS = 12;
+
+ public interface FusionDictionaryBufferInterface {
+ public int readUnsignedByte();
+ public int readUnsignedShort();
+ public int readUnsignedInt24();
+ public int readInt();
+ public int position();
+ public void position(int newPosition);
+ public void put(final byte b);
+ public int limit();
+ public int capacity();
+ }
+
+ public static final class ByteBufferWrapper implements FusionDictionaryBufferInterface {
+ private ByteBuffer mBuffer;
+
+ public ByteBufferWrapper(final ByteBuffer buffer) {
+ mBuffer = buffer;
+ }
+
+ @Override
+ public int readUnsignedByte() {
+ return ((int)mBuffer.get()) & 0xFF;
+ }
+
+ @Override
+ public int readUnsignedShort() {
+ return ((int)mBuffer.getShort()) & 0xFFFF;
+ }
+
+ @Override
+ public int readUnsignedInt24() {
+ final int retval = readUnsignedByte();
+ return (retval << 16) + readUnsignedShort();
+ }
+
+ @Override
+ public int readInt() {
+ return mBuffer.getInt();
+ }
+
+ @Override
+ public int position() {
+ return mBuffer.position();
+ }
+
+ @Override
+ public void position(int newPos) {
+ mBuffer.position(newPos);
+ }
+
+ @Override
+ public void put(final byte b) {
+ mBuffer.put(b);
+ }
+
+ @Override
+ public int limit() {
+ return mBuffer.limit();
+ }
+
+ @Override
+ public int capacity() {
+ return mBuffer.capacity();
+ }
+ }
/**
* A class grouping utility function for our specific character encoding.
*/
- private static class CharEncoding {
+ private static final class CharEncoding {
private static final int MINIMAL_ONE_BYTE_CHARACTER_VALUE = 0x20;
private static final int MAXIMAL_ONE_BYTE_CHARACTER_VALUE = 0xFF;
@@ -196,7 +132,7 @@ public class BinaryDictInputOutput {
/**
* Helper method to find out whether this code fits on one byte
*/
- private static boolean fitsOnOneByte(int character) {
+ private static boolean fitsOnOneByte(final int character) {
return character >= MINIMAL_ONE_BYTE_CHARACTER_VALUE
&& character <= MAXIMAL_ONE_BYTE_CHARACTER_VALUE;
}
@@ -218,10 +154,10 @@ public class BinaryDictInputOutput {
* @param character the character code.
* @return the size in binary encoded-form, either 1 or 3 bytes.
*/
- private static int getCharSize(int character) {
+ private static int getCharSize(final int character) {
// See char encoding in FusionDictionary.java
if (fitsOnOneByte(character)) return 1;
- if (INVALID_CHARACTER == character) return 1;
+ if (FormatSpec.INVALID_CHARACTER == character) return 1;
return 3;
}
@@ -279,7 +215,7 @@ public class BinaryDictInputOutput {
buffer[index++] = (byte)(0xFF & codePoint);
}
}
- buffer[index++] = GROUP_CHARACTERS_TERMINATOR;
+ buffer[index++] = FormatSpec.GROUP_CHARACTERS_TERMINATOR;
return index - origin;
}
@@ -291,7 +227,7 @@ public class BinaryDictInputOutput {
* @param buffer the ByteArrayOutputStream to write to.
* @param word the string to write.
*/
- private static void writeString(ByteArrayOutputStream buffer, final String word) {
+ private static void writeString(final ByteArrayOutputStream buffer, final String word) {
final int length = word.length();
for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) {
final int codePoint = word.codePointAt(i);
@@ -303,37 +239,38 @@ public class BinaryDictInputOutput {
buffer.write((byte) (0xFF & codePoint));
}
}
- buffer.write(GROUP_CHARACTERS_TERMINATOR);
+ buffer.write(FormatSpec.GROUP_CHARACTERS_TERMINATOR);
}
/**
- * Reads a string from a RandomAccessFile. This is the converse of the above method.
+ * Reads a string from a buffer. This is the converse of the above method.
*/
- private static String readString(final RandomAccessFile source) throws IOException {
+ private static String readString(final FusionDictionaryBufferInterface buffer) {
final StringBuilder s = new StringBuilder();
- int character = readChar(source);
- while (character != INVALID_CHARACTER) {
+ int character = readChar(buffer);
+ while (character != FormatSpec.INVALID_CHARACTER) {
s.appendCodePoint(character);
- character = readChar(source);
+ character = readChar(buffer);
}
return s.toString();
}
/**
- * Reads a character from the file.
+ * Reads a character from the buffer.
*
* This follows the character format documented earlier in this source file.
*
- * @param source the file, positioned over an encoded character.
+ * @param buffer the buffer, positioned over an encoded character.
* @return the character code.
*/
- private static int readChar(RandomAccessFile source) throws IOException {
- int character = source.readUnsignedByte();
+ private static int readChar(final FusionDictionaryBufferInterface buffer) {
+ int character = buffer.readUnsignedByte();
if (!fitsOnOneByte(character)) {
- if (GROUP_CHARACTERS_TERMINATOR == character)
- return INVALID_CHARACTER;
+ if (FormatSpec.GROUP_CHARACTERS_TERMINATOR == character) {
+ return FormatSpec.INVALID_CHARACTER;
+ }
character <<= 16;
- character += source.readUnsignedShort();
+ character += buffer.readUnsignedShort();
}
return character;
}
@@ -348,9 +285,9 @@ public class BinaryDictInputOutput {
* @param group the group
* @return the size of the char array, including the terminator if any
*/
- private static int getGroupCharactersSize(CharGroup group) {
+ private static int getGroupCharactersSize(final CharGroup group) {
int size = CharEncoding.getCharArraySize(group.mChars);
- if (group.hasSeveralChars()) size += GROUP_TERMINATOR_SIZE;
+ if (group.hasSeveralChars()) size += FormatSpec.GROUP_TERMINATOR_SIZE;
return size;
}
@@ -359,14 +296,15 @@ 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) {
- if (MAX_CHARGROUPS_FOR_ONE_BYTE_CHARGROUP_COUNT >= count) {
+ public static int getGroupCountSize(final int count) {
+ if (FormatSpec.MAX_CHARGROUPS_FOR_ONE_BYTE_CHARGROUP_COUNT >= count) {
return 1;
- } else if (MAX_CHARGROUPS_IN_A_NODE >= count) {
+ } else if (FormatSpec.MAX_CHARGROUPS_IN_A_NODE >= count) {
return 2;
} else {
- throw new RuntimeException("Can't have more than " + MAX_CHARGROUPS_IN_A_NODE
- + " groups in a node (found " + count +")");
+ throw new RuntimeException("Can't have more than "
+ + FormatSpec.MAX_CHARGROUPS_IN_A_NODE + " groups in a node (found " + count
+ + ")");
}
}
@@ -383,14 +321,14 @@ public class BinaryDictInputOutput {
* Compute the size of a shortcut in bytes.
*/
private static int getShortcutSize(final WeightedString shortcut) {
- int size = GROUP_ATTRIBUTE_FLAGS_SIZE;
+ int size = FormatSpec.GROUP_ATTRIBUTE_FLAGS_SIZE;
final String word = shortcut.mWord;
final int length = word.length();
for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) {
final int codePoint = word.codePointAt(i);
size += CharEncoding.getCharSize(codePoint);
}
- size += GROUP_TERMINATOR_SIZE;
+ size += FormatSpec.GROUP_TERMINATOR_SIZE;
return size;
}
@@ -402,7 +340,7 @@ public class BinaryDictInputOutput {
*/
private static int getShortcutListSize(final ArrayList<WeightedString> shortcutList) {
if (null == shortcutList) return 0;
- int size = GROUP_SHORTCUT_LIST_SIZE_SIZE;
+ int size = FormatSpec.GROUP_SHORTCUT_LIST_SIZE_SIZE;
for (final WeightedString shortcut : shortcutList) {
size += getShortcutSize(shortcut);
}
@@ -413,16 +351,18 @@ public class BinaryDictInputOutput {
* Compute the maximum size of a CharGroup, assuming 3-byte addresses for everything.
*
* @param group the CharGroup to compute the size of.
+ * @param options file format options.
* @return the maximum size of the group.
*/
- private static int getCharGroupMaximumSize(CharGroup group) {
- int size = getGroupCharactersSize(group) + GROUP_FLAGS_SIZE;
+ private static int getCharGroupMaximumSize(final CharGroup group, final FormatOptions options) {
+ int size = getGroupHeaderSize(group, options);
// If terminal, one byte for the frequency
- if (group.isTerminal()) size += GROUP_FREQUENCY_SIZE;
- size += GROUP_MAX_ADDRESS_SIZE; // For children address
+ if (group.isTerminal()) size += FormatSpec.GROUP_FREQUENCY_SIZE;
+ size += FormatSpec.GROUP_MAX_ADDRESS_SIZE; // For children address
size += getShortcutListSize(group.mShortcutTargets);
if (null != group.mBigrams) {
- size += (GROUP_ATTRIBUTE_FLAGS_SIZE + GROUP_ATTRIBUTE_MAX_ADDRESS_SIZE)
+ size += (FormatSpec.GROUP_ATTRIBUTE_FLAGS_SIZE
+ + FormatSpec.GROUP_ATTRIBUTE_MAX_ADDRESS_SIZE)
* group.mBigrams.size();
}
return size;
@@ -433,25 +373,63 @@ public class BinaryDictInputOutput {
* it in the 'actualSize' member of the node.
*
* @param node the node to compute the maximum size of.
+ * @param options file format options.
*/
- private static void setNodeMaximumSize(Node node) {
+ private static void setNodeMaximumSize(final Node node, final FormatOptions options) {
int size = getGroupCountSize(node);
for (CharGroup g : node.mData) {
- final int groupSize = getCharGroupMaximumSize(g);
+ final int groupSize = getCharGroupMaximumSize(g, options);
g.mCachedSize = groupSize;
size += groupSize;
}
+ if (options.mSupportsDynamicUpdate) {
+ 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(int address) {
- return NO_CHILDREN_ADDRESS != address;
+ public static boolean hasChildrenAddress(final int address) {
+ return FormatSpec.NO_CHILDREN_ADDRESS != address;
+ }
+
+ /**
+ * Helper method to check whether the group is moved.
+ */
+ public static boolean isMovedGroup(final int flags, final FormatOptions options) {
+ return options.mSupportsDynamicUpdate && ((flags & FormatSpec.FLAG_IS_MOVED) == 1);
}
/**
+ * Helper method to check whether the dictionary can be updated dynamically.
+ */
+ public static boolean supportsDynamicUpdate(final FormatOptions options) {
+ return options.mVersion >= FormatSpec.FIRST_VERSION_WITH_DYNAMIC_UPDATE
+ && options.mSupportsDynamicUpdate;
+ }
+
+ /**
+ * Compute the size of the header (flag + [parent address] + characters size) of a CharGroup.
+ *
+ * @param group the group of which to compute the size of the header
+ * @param options file format options.
+ */
+ private static int getGroupHeaderSize(final CharGroup group, final FormatOptions options) {
+ if (supportsDynamicUpdate(options)) {
+ return FormatSpec.GROUP_FLAGS_SIZE + FormatSpec.PARENT_ADDRESS_SIZE
+ + getGroupCharactersSize(group);
+ } else {
+ return FormatSpec.GROUP_FLAGS_SIZE + getGroupCharactersSize(group);
+ }
+ }
+
+ private static final int UINT8_MAX = 0xFF;
+ private static final int UINT16_MAX = 0xFFFF;
+ private static final int UINT24_MAX = 0xFFFFFF;
+
+ /**
* Compute the size, in bytes, that an address will occupy.
*
* This can be used either for children addresses (which are always positive) or for
@@ -461,30 +439,38 @@ public class BinaryDictInputOutput {
* @param address the address
* @return the byte size.
*/
- private static int getByteSize(int address) {
- assert(address < 0x1000000);
+ private static int getByteSize(final int address) {
+ assert(address <= UINT24_MAX);
if (!hasChildrenAddress(address)) {
return 0;
- } else if (Math.abs(address) < 0x100) {
+ } else if (Math.abs(address) <= UINT8_MAX) {
return 1;
- } else if (Math.abs(address) < 0x10000) {
+ } else if (Math.abs(address) <= UINT16_MAX) {
return 2;
} else {
return 3;
}
}
+
+ private static final int SINT8_MAX = 0x7F;
+ private static final int SINT16_MAX = 0x7FFF;
+ private static final int SINT24_MAX = 0x7FFFFF;
+ private static final int MSB8 = 0x80;
+ private static final int MSB16 = 0x8000;
+ private static final int MSB24 = 0x800000;
+
// End utility methods.
// This method is responsible for finding a nice ordering of the nodes that favors run-time
// cache performance and dictionary size.
- /* package for tests */ static ArrayList<Node> flattenTree(Node root) {
+ /* package for tests */ static ArrayList<Node> flattenTree(final Node root) {
final int treeSize = FusionDictionary.countCharGroups(root);
MakedictLog.i("Counted nodes : " + treeSize);
final ArrayList<Node> flatTree = new ArrayList<Node>(treeSize);
return flattenTreeInner(flatTree, root);
}
- private static ArrayList<Node> flattenTreeInner(ArrayList<Node> list, Node node) {
+ private static ArrayList<Node> flattenTreeInner(final ArrayList<Node> list, final Node node) {
// Removing the node is necessary if the tails are merged, because we would then
// add the same node several times when we only want it once. A number of places in
// the code also depends on any node being only once in the list.
@@ -534,9 +520,11 @@ public class BinaryDictInputOutput {
*
* @param node the node to compute the size of.
* @param dict the dictionary in which the word/attributes are to be found.
+ * @param formatOptions file format options.
* @return false if none of the cached addresses inside the node changed, true otherwise.
*/
- private static boolean computeActualNodeSize(Node node, FusionDictionary dict) {
+ private static boolean computeActualNodeSize(final Node node, final FusionDictionary dict,
+ final FormatOptions formatOptions) {
boolean changed = false;
int size = getGroupCountSize(node);
for (CharGroup group : node.mData) {
@@ -544,26 +532,38 @@ public class BinaryDictInputOutput {
changed = true;
group.mCachedAddress = node.mCachedAddress + size;
}
- int groupSize = GROUP_FLAGS_SIZE + getGroupCharactersSize(group);
- if (group.isTerminal()) groupSize += GROUP_FREQUENCY_SIZE;
- if (null != group.mChildren) {
- final int offsetBasePoint= groupSize + node.mCachedAddress + size;
+ int groupSize = getGroupHeaderSize(group, formatOptions);
+ if (group.isTerminal()) groupSize += FormatSpec.GROUP_FREQUENCY_SIZE;
+ if (null == group.mChildren && formatOptions.mSupportsDynamicUpdate) {
+ groupSize += FormatSpec.SIGNED_CHILDREN_ADDRESS_SIZE;
+ } else if (null != group.mChildren) {
+ final int offsetBasePoint = groupSize + node.mCachedAddress + size;
final int offset = group.mChildren.mCachedAddress - offsetBasePoint;
- groupSize += getByteSize(offset);
+ // assign my address to children's parent address
+ group.mChildren.mCachedParentAddress = group.mCachedAddress
+ - group.mChildren.mCachedAddress;
+ if (formatOptions.mSupportsDynamicUpdate) {
+ groupSize += FormatSpec.SIGNED_CHILDREN_ADDRESS_SIZE;
+ } else {
+ groupSize += getByteSize(offset);
+ }
}
groupSize += getShortcutListSize(group.mShortcutTargets);
if (null != group.mBigrams) {
for (WeightedString bigram : group.mBigrams) {
final int offsetBasePoint = groupSize + node.mCachedAddress + size
- + GROUP_FLAGS_SIZE;
+ + FormatSpec.GROUP_FLAGS_SIZE;
final int addressOfBigram = findAddressOfWord(dict, bigram.mWord);
final int offset = addressOfBigram - offsetBasePoint;
- groupSize += getByteSize(offset) + GROUP_FLAGS_SIZE;
+ groupSize += getByteSize(offset) + FormatSpec.GROUP_FLAGS_SIZE;
}
}
group.mCachedSize = groupSize;
size += groupSize;
}
+ if (formatOptions.mSupportsDynamicUpdate) {
+ size += FormatSpec.FORWARD_LINK_ADDRESS_SIZE;
+ }
if (node.mCachedSize != size) {
node.mCachedSize = size;
changed = true;
@@ -575,9 +575,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(ArrayList<Node> flatNodes) {
+ private static int stackNodes(final ArrayList<Node> flatNodes,
+ final FormatOptions formatOptions) {
int nodeOffset = 0;
for (Node n : flatNodes) {
n.mCachedAddress = nodeOffset;
@@ -587,7 +589,10 @@ public class BinaryDictInputOutput {
g.mCachedAddress = groupCountSize + nodeOffset + groupOffset;
groupOffset += g.mCachedSize;
}
- if (groupOffset + groupCountSize != n.mCachedSize) {
+ final int nodeSize = groupCountSize + groupOffset
+ + (formatOptions.mSupportsDynamicUpdate
+ ? FormatSpec.FORWARD_LINK_ADDRESS_SIZE : 0);
+ if (nodeSize != n.mCachedSize) {
throw new RuntimeException("Bug : Stored and computed node size differ");
}
nodeOffset += n.mCachedSize;
@@ -607,13 +612,14 @@ public class BinaryDictInputOutput {
*
* @param dict the dictionary
* @param flatNodes the ordered array of nodes
+ * @param formatOptions file format options.
* @return the same array it was passed. The nodes have been updated for address and size.
*/
- private static ArrayList<Node> computeAddresses(FusionDictionary dict,
- ArrayList<Node> flatNodes) {
+ private static ArrayList<Node> computeAddresses(final FusionDictionary dict,
+ final ArrayList<Node> flatNodes, final FormatOptions formatOptions) {
// First get the worst sizes and offsets
- for (Node n : flatNodes) setNodeMaximumSize(n);
- final int offset = stackNodes(flatNodes);
+ for (Node n : flatNodes) setNodeMaximumSize(n, formatOptions);
+ final int offset = stackNodes(flatNodes, formatOptions);
MakedictLog.i("Compressing the array addresses. Original size : " + offset);
MakedictLog.i("(Recursively seen size : " + offset + ")");
@@ -624,12 +630,12 @@ public class BinaryDictInputOutput {
changesDone = false;
for (Node n : flatNodes) {
final int oldNodeSize = n.mCachedSize;
- final boolean changed = computeActualNodeSize(n, dict);
+ final boolean changed = computeActualNodeSize(n, dict, formatOptions);
final int newNodeSize = n.mCachedSize;
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);
@@ -652,7 +658,7 @@ public class BinaryDictInputOutput {
*
* @param array the array node to check
*/
- private static void checkFlatNodeArray(ArrayList<Node> array) {
+ private static void checkFlatNodeArray(final ArrayList<Node> array) {
int offset = 0;
int index = 0;
for (Node n : array) {
@@ -694,39 +700,70 @@ public class BinaryDictInputOutput {
}
}
+ /**
+ * Helper method to write a variable-size signed address to a file.
+ *
+ * @param buffer the buffer to write to.
+ * @param index the index in the buffer to write the address to.
+ * @param address the address to write.
+ * @return the size in bytes the address actually took.
+ */
+ private static int writeVariableSignedAddress(final byte[] buffer, int index,
+ final int address) {
+ if (!hasChildrenAddress(address)) {
+ buffer[index] = buffer[index + 1] = buffer[index + 2] = 0;
+ } else {
+ final int absAddress = Math.abs(address);
+ buffer[index++] = (byte)((address < 0 ? MSB8 : 0) | (0xFF & (absAddress >> 16)));
+ buffer[index++] = (byte)(0xFF & (absAddress >> 8));
+ buffer[index++] = (byte)(0xFF & absAddress);
+ }
+ return 3;
+ }
+
private static byte makeCharGroupFlags(final CharGroup group, final int groupAddress,
- final int childrenOffset) {
+ final int childrenOffset, final FormatOptions formatOptions) {
byte flags = 0;
- if (group.mChars.length > 1) flags |= FLAG_HAS_MULTIPLE_CHARS;
+ if (group.mChars.length > 1) flags |= FormatSpec.FLAG_HAS_MULTIPLE_CHARS;
if (group.mFrequency >= 0) {
- flags |= FLAG_IS_TERMINAL;
+ flags |= FormatSpec.FLAG_IS_TERMINAL;
}
if (null != group.mChildren) {
- switch (getByteSize(childrenOffset)) {
- case 1:
- flags |= FLAG_GROUP_ADDRESS_TYPE_ONEBYTE;
- break;
- case 2:
- flags |= FLAG_GROUP_ADDRESS_TYPE_TWOBYTES;
- break;
- case 3:
- flags |= FLAG_GROUP_ADDRESS_TYPE_THREEBYTES;
- break;
- default:
- throw new RuntimeException("Node with a strange address");
- }
+ final int byteSize = formatOptions.mSupportsDynamicUpdate
+ ? FormatSpec.SIGNED_CHILDREN_ADDRESS_SIZE : getByteSize(childrenOffset);
+ switch (byteSize) {
+ case 1:
+ flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_ONEBYTE;
+ break;
+ case 2:
+ flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_TWOBYTES;
+ break;
+ case 3:
+ flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_THREEBYTES;
+ break;
+ default:
+ throw new RuntimeException("Node with a strange address");
+ }
+ } else if (formatOptions.mSupportsDynamicUpdate) {
+ flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_THREEBYTES;
}
if (null != group.mShortcutTargets) {
if (DBG && 0 == group.mShortcutTargets.size()) {
throw new RuntimeException("0-sized shortcut list must be null");
}
- flags |= FLAG_HAS_SHORTCUT_TARGETS;
+ flags |= FormatSpec.FLAG_HAS_SHORTCUT_TARGETS;
}
if (null != group.mBigrams) {
if (DBG && 0 == group.mBigrams.size()) {
throw new RuntimeException("0-sized bigram list must be null");
}
- flags |= FLAG_HAS_BIGRAMS;
+ flags |= FormatSpec.FLAG_HAS_BIGRAMS;
+ }
+ if (group.mIsNotAWord) {
+ flags |= FormatSpec.FLAG_IS_NOT_A_WORD;
+ }
+ if (group.mIsBlacklistEntry) {
+ flags |= FormatSpec.FLAG_IS_BLACKLISTED;
}
return flags;
}
@@ -743,17 +780,17 @@ public class BinaryDictInputOutput {
*/
private static final int makeBigramFlags(final boolean more, final int offset,
int bigramFrequency, final int unigramFrequency, final String word) {
- int bigramFlags = (more ? FLAG_ATTRIBUTE_HAS_NEXT : 0)
- + (offset < 0 ? FLAG_ATTRIBUTE_OFFSET_NEGATIVE : 0);
+ int bigramFlags = (more ? FormatSpec.FLAG_ATTRIBUTE_HAS_NEXT : 0)
+ + (offset < 0 ? FormatSpec.FLAG_ATTRIBUTE_OFFSET_NEGATIVE : 0);
switch (getByteSize(offset)) {
case 1:
- bigramFlags |= FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE;
+ bigramFlags |= FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE;
break;
case 2:
- bigramFlags |= FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES;
+ bigramFlags |= FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES;
break;
case 3:
- bigramFlags |= FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES;
+ bigramFlags |= FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES;
break;
default:
throw new RuntimeException("Strange offset size");
@@ -783,13 +820,14 @@ public class BinaryDictInputOutput {
// their lower bound and exclude their higher bound so we need to have the first step
// start at exactly 1 unit higher than floor(unigramFreq + half a step).
// Note : to reconstruct the score, the dictionary reader will need to divide
- // MAX_TERMINAL_FREQUENCY - unigramFreq by 16.5 likewise, and add
- // (discretizedFrequency + 0.5) times this value to get the median value of the step,
- // which is the best approximation. This is how we get the most precise result with
- // only four bits.
- final double stepSize =
- (double)(MAX_TERMINAL_FREQUENCY - unigramFrequency) / (1.5 + MAX_BIGRAM_FREQUENCY);
- final double firstStepStart = 1 + unigramFrequency + (stepSize / 2.0);
+ // MAX_TERMINAL_FREQUENCY - unigramFreq by 16.5 likewise to get the value of the step,
+ // and add (discretizedFrequency + 0.5 + 0.5) times this value to get the best
+ // approximation. (0.5 to get the first step start, and 0.5 to get the middle of the
+ // step pointed by the discretized frequency.
+ final float stepSize =
+ (FormatSpec.MAX_TERMINAL_FREQUENCY - unigramFrequency)
+ / (1.5f + FormatSpec.MAX_BIGRAM_FREQUENCY);
+ final float firstStepStart = 1 + unigramFrequency + (stepSize / 2.0f);
final int discretizedFrequency = (int)((bigramFrequency - firstStepStart) / stepSize);
// If the bigram freq is less than half-a-step higher than the unigram freq, we get -1
// here. The best approximation would be the unigram freq itself, so we should not
@@ -797,19 +835,21 @@ public class BinaryDictInputOutput {
// small over-estimation that we get in this case. TODO: actually remove this bigram
// if discretizedFrequency < 0.
final int finalBigramFrequency = discretizedFrequency > 0 ? discretizedFrequency : 0;
- bigramFlags += finalBigramFrequency & FLAG_ATTRIBUTE_FREQUENCY;
+ bigramFlags += finalBigramFrequency & FormatSpec.FLAG_ATTRIBUTE_FREQUENCY;
return bigramFlags;
}
/**
* Makes the 2-byte value for options flags.
*/
- private static final int makeOptionsValue(final FusionDictionary dictionary) {
+ private static final int makeOptionsValue(final FusionDictionary dictionary,
+ final FormatOptions formatOptions) {
final DictionaryOptions options = dictionary.mOptions;
final boolean hasBigrams = dictionary.hasBigrams();
- return (options.mFrenchLigatureProcessing ? FRENCH_LIGATURE_PROCESSING_FLAG : 0)
- + (options.mGermanUmlautProcessing ? GERMAN_UMLAUT_PROCESSING_FLAG : 0)
- + (hasBigrams ? CONTAINS_BIGRAMS_FLAG : 0);
+ return (options.mFrenchLigatureProcessing ? FormatSpec.FRENCH_LIGATURE_PROCESSING_FLAG : 0)
+ + (options.mGermanUmlautProcessing ? FormatSpec.GERMAN_UMLAUT_PROCESSING_FLAG : 0)
+ + (hasBigrams ? FormatSpec.CONTAINS_BIGRAMS_FLAG : 0)
+ + (formatOptions.mSupportsDynamicUpdate ? FormatSpec.SUPPORTS_DYNAMIC_UPDATE : 0);
}
/**
@@ -820,7 +860,27 @@ public class BinaryDictInputOutput {
* @return the flags
*/
private static final int makeShortcutFlags(final boolean more, final int frequency) {
- return (more ? FLAG_ATTRIBUTE_HAS_NEXT : 0) + (frequency & FLAG_ATTRIBUTE_FREQUENCY);
+ return (more ? FormatSpec.FLAG_ATTRIBUTE_HAS_NEXT : 0)
+ + (frequency & FormatSpec.FLAG_ATTRIBUTE_FREQUENCY);
+ }
+
+ private static final int writeParentAddress(final byte[] buffer, final int index,
+ final int address, final FormatOptions formatOptions) {
+ if (supportsDynamicUpdate(formatOptions)) {
+ if (address == FormatSpec.NO_PARENT_ADDRESS) {
+ buffer[index] = buffer[index + 1] = buffer[index + 2] = 0;
+ } else {
+ final int absAddress = Math.abs(address);
+ assert(absAddress <= SINT24_MAX);
+ buffer[index] = (byte)((address < 0 ? MSB8 : 0)
+ | ((absAddress >> 16) & 0xFF));
+ buffer[index + 1] = (byte)((absAddress >> 8) & 0xFF);
+ buffer[index + 2] = (byte)(absAddress & 0xFF);
+ }
+ return index + 3;
+ } else {
+ return index;
+ }
}
/**
@@ -832,13 +892,16 @@ public class BinaryDictInputOutput {
* @param dict the dictionary the node is a part of (for relative offsets).
* @param buffer the memory buffer to write to.
* @param node the node to write.
+ * @param formatOptions file format options.
* @return the address of the END of the node.
*/
- private static int writePlacedNode(FusionDictionary dict, byte[] buffer, Node node) {
+ private static int writePlacedNode(final FusionDictionary dict, byte[] buffer,
+ final Node node, final FormatOptions formatOptions) {
int index = node.mCachedAddress;
final int groupCount = node.mData.size();
final int countSize = getGroupCountSize(node);
+ final int parentAddress = node.mCachedParentAddress;
if (1 == countSize) {
buffer[index++] = (byte)groupCount;
} else if (2 == countSize) {
@@ -855,33 +918,50 @@ public class BinaryDictInputOutput {
if (index != group.mCachedAddress) throw new RuntimeException("Bug: write index is not "
+ "the same as the cached address of the group : "
+ index + " <> " + group.mCachedAddress);
- groupAddress += GROUP_FLAGS_SIZE + getGroupCharactersSize(group);
+ groupAddress += getGroupHeaderSize(group, formatOptions);
// Sanity checks.
- if (DBG && group.mFrequency > MAX_TERMINAL_FREQUENCY) {
- throw new RuntimeException("A node has a frequency > " + MAX_TERMINAL_FREQUENCY
+ if (DBG && group.mFrequency > FormatSpec.MAX_TERMINAL_FREQUENCY) {
+ throw new RuntimeException("A node has a frequency > "
+ + FormatSpec.MAX_TERMINAL_FREQUENCY
+ " : " + group.mFrequency);
}
- if (group.mFrequency >= 0) groupAddress += GROUP_FREQUENCY_SIZE;
+ if (group.mFrequency >= 0) groupAddress += FormatSpec.GROUP_FREQUENCY_SIZE;
final int childrenOffset = null == group.mChildren
- ? NO_CHILDREN_ADDRESS : group.mChildren.mCachedAddress - groupAddress;
- byte flags = makeCharGroupFlags(group, groupAddress, childrenOffset);
+ ? FormatSpec.NO_CHILDREN_ADDRESS
+ : group.mChildren.mCachedAddress - groupAddress;
+ byte flags = makeCharGroupFlags(group, groupAddress, childrenOffset, formatOptions);
buffer[index++] = flags;
+
+ if (parentAddress == FormatSpec.NO_PARENT_ADDRESS) {
+ index = writeParentAddress(buffer, index, parentAddress, formatOptions);
+ } else {
+ index = writeParentAddress(buffer, index,
+ parentAddress + (node.mCachedAddress - group.mCachedAddress),
+ formatOptions);
+ }
+
index = CharEncoding.writeCharArray(group.mChars, buffer, index);
if (group.hasSeveralChars()) {
- buffer[index++] = GROUP_CHARACTERS_TERMINATOR;
+ buffer[index++] = FormatSpec.GROUP_CHARACTERS_TERMINATOR;
}
if (group.mFrequency >= 0) {
buffer[index++] = (byte) group.mFrequency;
}
- final int shift = writeVariableAddress(buffer, index, childrenOffset);
+
+ final int shift;
+ if (formatOptions.mSupportsDynamicUpdate) {
+ shift = writeVariableSignedAddress(buffer, index, childrenOffset);
+ } else {
+ shift = writeVariableAddress(buffer, index, childrenOffset);
+ }
index += shift;
groupAddress += shift;
// Write shortcuts
if (null != group.mShortcutTargets) {
final int indexOfShortcutByteSize = index;
- index += GROUP_SHORTCUT_LIST_SIZE_SIZE;
- groupAddress += GROUP_SHORTCUT_LIST_SIZE_SIZE;
+ index += FormatSpec.GROUP_SHORTCUT_LIST_SIZE_SIZE;
+ groupAddress += FormatSpec.GROUP_SHORTCUT_LIST_SIZE_SIZE;
final Iterator<WeightedString> shortcutIterator = group.mShortcutTargets.iterator();
while (shortcutIterator.hasNext()) {
final WeightedString target = shortcutIterator.next();
@@ -921,6 +1001,11 @@ public class BinaryDictInputOutput {
}
}
+ if (formatOptions.mSupportsDynamicUpdate) {
+ 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 "
@@ -990,10 +1075,10 @@ public class BinaryDictInputOutput {
*
* @param destination the stream to write the binary data to.
* @param dict the dictionary to write.
- * @param version the version of the format to write, currently either 1 or 2.
+ * @param formatOptions file format options.
*/
public static void writeDictionaryBinary(final OutputStream destination,
- final FusionDictionary dict, final int version)
+ final FusionDictionary dict, final FormatOptions formatOptions)
throws IOException, UnsupportedFormatException {
// Addresses are limited to 3 bytes, but since addresses can be relative to each node, the
@@ -1002,36 +1087,39 @@ public class BinaryDictInputOutput {
// does not have a size limit, each node must still be within 16MB of all its children and
// parents. As long as this is ensured, the dictionary file may grow to any size.
- if (version < MINIMUM_SUPPORTED_VERSION || version > MAXIMUM_SUPPORTED_VERSION) {
+ final int version = formatOptions.mVersion;
+ if (version < FormatSpec.MINIMUM_SUPPORTED_VERSION
+ || version > FormatSpec.MAXIMUM_SUPPORTED_VERSION) {
throw new UnsupportedFormatException("Requested file format version " + version
+ ", but this implementation only supports versions "
- + MINIMUM_SUPPORTED_VERSION + " through " + MAXIMUM_SUPPORTED_VERSION);
+ + FormatSpec.MINIMUM_SUPPORTED_VERSION + " through "
+ + FormatSpec.MAXIMUM_SUPPORTED_VERSION);
}
ByteArrayOutputStream headerBuffer = new ByteArrayOutputStream(256);
// The magic number in big-endian order.
- if (version >= FIRST_VERSION_WITH_HEADER_SIZE) {
+ if (version >= FormatSpec.FIRST_VERSION_WITH_HEADER_SIZE) {
// Magic number for version 2+.
- headerBuffer.write((byte) (0xFF & (VERSION_2_MAGIC_NUMBER >> 24)));
- headerBuffer.write((byte) (0xFF & (VERSION_2_MAGIC_NUMBER >> 16)));
- headerBuffer.write((byte) (0xFF & (VERSION_2_MAGIC_NUMBER >> 8)));
- headerBuffer.write((byte) (0xFF & VERSION_2_MAGIC_NUMBER));
+ headerBuffer.write((byte) (0xFF & (FormatSpec.VERSION_2_MAGIC_NUMBER >> 24)));
+ headerBuffer.write((byte) (0xFF & (FormatSpec.VERSION_2_MAGIC_NUMBER >> 16)));
+ headerBuffer.write((byte) (0xFF & (FormatSpec.VERSION_2_MAGIC_NUMBER >> 8)));
+ headerBuffer.write((byte) (0xFF & FormatSpec.VERSION_2_MAGIC_NUMBER));
// Dictionary version.
headerBuffer.write((byte) (0xFF & (version >> 8)));
headerBuffer.write((byte) (0xFF & version));
} else {
// Magic number for version 1.
- headerBuffer.write((byte) (0xFF & (VERSION_1_MAGIC_NUMBER >> 8)));
- headerBuffer.write((byte) (0xFF & VERSION_1_MAGIC_NUMBER));
+ headerBuffer.write((byte) (0xFF & (FormatSpec.VERSION_1_MAGIC_NUMBER >> 8)));
+ headerBuffer.write((byte) (0xFF & FormatSpec.VERSION_1_MAGIC_NUMBER));
// Dictionary version.
headerBuffer.write((byte) (0xFF & version));
}
// Options flags
- final int options = makeOptionsValue(dict);
+ final int options = makeOptionsValue(dict, formatOptions);
headerBuffer.write((byte) (0xFF & (options >> 8)));
headerBuffer.write((byte) (0xFF & options));
- if (version >= FIRST_VERSION_WITH_HEADER_SIZE) {
+ if (version >= FormatSpec.FIRST_VERSION_WITH_HEADER_SIZE) {
final int headerSizeOffset = headerBuffer.size();
// Placeholder to be written later with header size.
for (int i = 0; i < 4; ++i) {
@@ -1062,20 +1150,20 @@ public class BinaryDictInputOutput {
ArrayList<Node> flatNodes = flattenTree(dict.mRoot);
MakedictLog.i("Computing addresses...");
- computeAddresses(dict, flatNodes);
+ computeAddresses(dict, flatNodes, formatOptions);
MakedictLog.i("Checking array...");
if (DBG) checkFlatNodeArray(flatNodes);
// Create a buffer that matches the final dictionary size.
final Node lastNode = flatNodes.get(flatNodes.size() - 1);
- final int bufferSize =(lastNode.mCachedAddress + lastNode.mCachedSize);
+ final int bufferSize = lastNode.mCachedAddress + lastNode.mCachedSize;
final byte[] buffer = new byte[bufferSize];
int index = 0;
MakedictLog.i("Writing file...");
int dataEndOffset = 0;
for (Node n : flatNodes) {
- dataEndOffset = writePlacedNode(dict, buffer, n);
+ dataEndOffset = writePlacedNode(dict, buffer, n, formatOptions);
}
if (DBG) showStatistics(flatNodes);
@@ -1090,113 +1178,161 @@ public class BinaryDictInputOutput {
// Input methods: Read a binary dictionary to memory.
// readDictionaryBinary is the public entry point for them.
- static final int[] characterBuffer = new int[MAX_WORD_LENGTH];
- private static CharGroupInfo readCharGroup(RandomAccessFile source,
- final int originalGroupAddress) throws IOException {
+ private static int getChildrenAddressSize(final int optionFlags,
+ final FormatOptions formatOptions) {
+ if (formatOptions.mSupportsDynamicUpdate) return FormatSpec.SIGNED_CHILDREN_ADDRESS_SIZE;
+ switch (optionFlags & FormatSpec.MASK_GROUP_ADDRESS_TYPE) {
+ case FormatSpec.FLAG_GROUP_ADDRESS_TYPE_ONEBYTE:
+ return 1;
+ case FormatSpec.FLAG_GROUP_ADDRESS_TYPE_TWOBYTES:
+ return 2;
+ case FormatSpec.FLAG_GROUP_ADDRESS_TYPE_THREEBYTES:
+ return 3;
+ case FormatSpec.FLAG_GROUP_ADDRESS_TYPE_NOADDRESS:
+ default:
+ return 0;
+ }
+ }
+
+ private static int readChildrenAddress(final FusionDictionaryBufferInterface buffer,
+ final int optionFlags, final FormatOptions options) {
+ if (options.mSupportsDynamicUpdate) {
+ final int address = buffer.readUnsignedInt24();
+ if (address == 0) return FormatSpec.NO_CHILDREN_ADDRESS;
+ if ((address & MSB24) != 0) {
+ return -(address & SINT24_MAX);
+ } else {
+ return address;
+ }
+ }
+ int address;
+ switch (optionFlags & FormatSpec.MASK_GROUP_ADDRESS_TYPE) {
+ case FormatSpec.FLAG_GROUP_ADDRESS_TYPE_ONEBYTE:
+ return buffer.readUnsignedByte();
+ case FormatSpec.FLAG_GROUP_ADDRESS_TYPE_TWOBYTES:
+ return buffer.readUnsignedShort();
+ case FormatSpec.FLAG_GROUP_ADDRESS_TYPE_THREEBYTES:
+ return buffer.readUnsignedInt24();
+ case FormatSpec.FLAG_GROUP_ADDRESS_TYPE_NOADDRESS:
+ default:
+ return FormatSpec.NO_CHILDREN_ADDRESS;
+ }
+ }
+
+ private static int readParentAddress(final FusionDictionaryBufferInterface buffer,
+ final FormatOptions formatOptions) {
+ if (supportsDynamicUpdate(formatOptions)) {
+ final int parentAddress = buffer.readUnsignedInt24();
+ final int sign = ((parentAddress & MSB24) != 0) ? -1 : 1;
+ return sign * (parentAddress & SINT24_MAX);
+ } else {
+ return FormatSpec.NO_PARENT_ADDRESS;
+ }
+ }
+
+ private static final int[] CHARACTER_BUFFER = new int[FormatSpec.MAX_WORD_LENGTH];
+ public static CharGroupInfo readCharGroup(final FusionDictionaryBufferInterface buffer,
+ final int originalGroupAddress, final FormatOptions options) {
int addressPointer = originalGroupAddress;
- final int flags = source.readUnsignedByte();
+ final int flags = buffer.readUnsignedByte();
++addressPointer;
+
+ final int parentAddress = readParentAddress(buffer, options);
+ if (supportsDynamicUpdate(options)) {
+ addressPointer += 3;
+ }
+
final int characters[];
- if (0 != (flags & FLAG_HAS_MULTIPLE_CHARS)) {
+ if (0 != (flags & FormatSpec.FLAG_HAS_MULTIPLE_CHARS)) {
int index = 0;
- int character = CharEncoding.readChar(source);
+ int character = CharEncoding.readChar(buffer);
addressPointer += CharEncoding.getCharSize(character);
while (-1 != character) {
- characterBuffer[index++] = character;
- character = CharEncoding.readChar(source);
+ // FusionDictionary is making sure that the length of the word is smaller than
+ // MAX_WORD_LENGTH.
+ // So we'll never write past the end of CHARACTER_BUFFER.
+ CHARACTER_BUFFER[index++] = character;
+ character = CharEncoding.readChar(buffer);
addressPointer += CharEncoding.getCharSize(character);
}
- characters = Arrays.copyOfRange(characterBuffer, 0, index);
+ characters = Arrays.copyOfRange(CHARACTER_BUFFER, 0, index);
} else {
- final int character = CharEncoding.readChar(source);
+ final int character = CharEncoding.readChar(buffer);
addressPointer += CharEncoding.getCharSize(character);
characters = new int[] { character };
}
final int frequency;
- if (0 != (FLAG_IS_TERMINAL & flags)) {
+ if (0 != (FormatSpec.FLAG_IS_TERMINAL & flags)) {
++addressPointer;
- frequency = source.readUnsignedByte();
+ frequency = buffer.readUnsignedByte();
} else {
frequency = CharGroup.NOT_A_TERMINAL;
}
- int childrenAddress = addressPointer;
- switch (flags & MASK_GROUP_ADDRESS_TYPE) {
- case FLAG_GROUP_ADDRESS_TYPE_ONEBYTE:
- childrenAddress += source.readUnsignedByte();
- addressPointer += 1;
- break;
- case FLAG_GROUP_ADDRESS_TYPE_TWOBYTES:
- childrenAddress += source.readUnsignedShort();
- addressPointer += 2;
- break;
- case FLAG_GROUP_ADDRESS_TYPE_THREEBYTES:
- childrenAddress += (source.readUnsignedByte() << 16) + source.readUnsignedShort();
- addressPointer += 3;
- break;
- case FLAG_GROUP_ADDRESS_TYPE_NOADDRESS:
- default:
- childrenAddress = NO_CHILDREN_ADDRESS;
- break;
+ int childrenAddress = readChildrenAddress(buffer, flags, options);
+ if (childrenAddress != FormatSpec.NO_CHILDREN_ADDRESS) {
+ childrenAddress += addressPointer;
}
+ addressPointer += getChildrenAddressSize(flags, options);
ArrayList<WeightedString> shortcutTargets = null;
- if (0 != (flags & FLAG_HAS_SHORTCUT_TARGETS)) {
- final long pointerBefore = source.getFilePointer();
+ if (0 != (flags & FormatSpec.FLAG_HAS_SHORTCUT_TARGETS)) {
+ final int pointerBefore = buffer.position();
shortcutTargets = new ArrayList<WeightedString>();
- source.readUnsignedShort(); // Skip the size
+ buffer.readUnsignedShort(); // Skip the size
while (true) {
- final int targetFlags = source.readUnsignedByte();
- final String word = CharEncoding.readString(source);
+ final int targetFlags = buffer.readUnsignedByte();
+ final String word = CharEncoding.readString(buffer);
shortcutTargets.add(new WeightedString(word,
- targetFlags & FLAG_ATTRIBUTE_FREQUENCY));
- if (0 == (targetFlags & FLAG_ATTRIBUTE_HAS_NEXT)) break;
+ targetFlags & FormatSpec.FLAG_ATTRIBUTE_FREQUENCY));
+ if (0 == (targetFlags & FormatSpec.FLAG_ATTRIBUTE_HAS_NEXT)) break;
}
- addressPointer += (source.getFilePointer() - pointerBefore);
+ addressPointer += buffer.position() - pointerBefore;
}
ArrayList<PendingAttribute> bigrams = null;
- if (0 != (flags & FLAG_HAS_BIGRAMS)) {
+ if (0 != (flags & FormatSpec.FLAG_HAS_BIGRAMS)) {
bigrams = new ArrayList<PendingAttribute>();
while (true) {
- final int bigramFlags = source.readUnsignedByte();
+ final int bigramFlags = buffer.readUnsignedByte();
++addressPointer;
- final int sign = 0 == (bigramFlags & FLAG_ATTRIBUTE_OFFSET_NEGATIVE) ? 1 : -1;
+ final int sign = 0 == (bigramFlags & FormatSpec.FLAG_ATTRIBUTE_OFFSET_NEGATIVE)
+ ? 1 : -1;
int bigramAddress = addressPointer;
- switch (bigramFlags & MASK_ATTRIBUTE_ADDRESS_TYPE) {
- case FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE:
- bigramAddress += sign * source.readUnsignedByte();
+ switch (bigramFlags & FormatSpec.MASK_ATTRIBUTE_ADDRESS_TYPE) {
+ case FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE:
+ bigramAddress += sign * buffer.readUnsignedByte();
addressPointer += 1;
break;
- case FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES:
- bigramAddress += sign * source.readUnsignedShort();
+ case FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES:
+ bigramAddress += sign * buffer.readUnsignedShort();
addressPointer += 2;
break;
- case FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES:
- final int offset = ((source.readUnsignedByte() << 16)
- + source.readUnsignedShort());
+ case FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES:
+ final int offset = (buffer.readUnsignedByte() << 16)
+ + buffer.readUnsignedShort();
bigramAddress += sign * offset;
addressPointer += 3;
break;
default:
throw new RuntimeException("Has bigrams with no address");
}
- bigrams.add(new PendingAttribute(bigramFlags & FLAG_ATTRIBUTE_FREQUENCY,
+ bigrams.add(new PendingAttribute(bigramFlags & FormatSpec.FLAG_ATTRIBUTE_FREQUENCY,
bigramAddress));
- if (0 == (bigramFlags & FLAG_ATTRIBUTE_HAS_NEXT)) break;
+ if (0 == (bigramFlags & FormatSpec.FLAG_ATTRIBUTE_HAS_NEXT)) break;
}
}
return new CharGroupInfo(originalGroupAddress, addressPointer, flags, characters, frequency,
- childrenAddress, shortcutTargets, bigrams);
+ parentAddress, childrenAddress, shortcutTargets, bigrams);
}
/**
- * Reads and returns the char group count out of a file and forwards the pointer.
+ * Reads and returns the char group count out of a buffer and forwards the pointer.
*/
- private static int readCharGroupCount(RandomAccessFile source) throws IOException {
- final int msb = source.readUnsignedByte();
- if (MAX_CHARGROUPS_FOR_ONE_BYTE_CHARGROUP_COUNT >= msb) {
+ public static int readCharGroupCount(final FusionDictionaryBufferInterface buffer) {
+ final int msb = buffer.readUnsignedByte();
+ if (FormatSpec.MAX_CHARGROUPS_FOR_ONE_BYTE_CHARGROUP_COUNT >= msb) {
return msb;
} else {
- return ((MAX_CHARGROUPS_FOR_ONE_BYTE_CHARGROUP_COUNT & msb) << 8)
- + source.readUnsignedByte();
+ return ((FormatSpec.MAX_CHARGROUPS_FOR_ONE_BYTE_CHARGROUP_COUNT & msb) << 8)
+ + buffer.readUnsignedByte();
}
}
@@ -1204,31 +1340,83 @@ public class BinaryDictInputOutput {
// of this method. Since it performs direct, unbuffered random access to the file and
// may be called hundreds of thousands of times, the resulting performance is not
// reasonable without some kind of cache. Thus:
- // TODO: perform buffered I/O here and in other places in the code.
private static TreeMap<Integer, String> wordCache = new TreeMap<Integer, String>();
/**
* Finds, as a string, the word at the address passed as an argument.
*
- * @param source the file to read from.
+ * @param buffer the buffer to read from.
* @param headerSize the size of the header.
* @param address the address to seek.
+ * @param formatOptions file format options.
* @return the word, as a string.
- * @throws IOException if the file can't be read.
*/
- private static String getWordAtAddress(final RandomAccessFile source, final long headerSize,
- int address) throws IOException {
+ /* 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;
- final long originalPointer = source.getFilePointer();
- source.seek(headerSize);
- final int count = readCharGroupCount(source);
+
+ final String result;
+ final int originalPointer = buffer.position();
+ buffer.position(address);
+
+ if (supportsDynamicUpdate(formatOptions)) {
+ result = getWordAtAddressWithParentAddress(buffer, headerSize, address, formatOptions);
+ } else {
+ result = getWordAtAddressWithoutParentAddress(buffer, headerSize, address,
+ formatOptions);
+ }
+
+ wordCache.put(address, result);
+ buffer.position(originalPointer);
+ return result;
+ }
+
+ private static int[] sGetWordBuffer = new int[FormatSpec.MAX_WORD_LENGTH];
+ private static String getWordAtAddressWithParentAddress(
+ final FusionDictionaryBufferInterface buffer, final int headerSize, final int address,
+ final FormatOptions options) {
+ final StringBuilder builder = new StringBuilder();
+
+ int currentAddress = address;
+ int index = FormatSpec.MAX_WORD_LENGTH - 1;
+ // the length of the path from the root to the leaf is limited by MAX_WORD_LENGTH
+ for (int count = 0; count < FormatSpec.MAX_WORD_LENGTH; ++count) {
+ CharGroupInfo currentInfo;
+ int loopCounter = 0;
+ do {
+ buffer.position(currentAddress + headerSize);
+ currentInfo = readCharGroup(buffer, currentAddress, options);
+ if (isMovedGroup(currentInfo.mFlags, options)) {
+ currentAddress = currentInfo.mParentAddress + currentInfo.mOriginalAddress;
+ }
+ if (DBG && loopCounter++ > MAX_JUMPS) {
+ MakedictLog.d("Too many jumps - probably a bug");
+ }
+ } while (isMovedGroup(currentInfo.mFlags, options));
+ for (int i = 0; i < currentInfo.mCharacters.length; ++i) {
+ sGetWordBuffer[index--] =
+ currentInfo.mCharacters[currentInfo.mCharacters.length - i - 1];
+ }
+ if (currentInfo.mParentAddress == FormatSpec.NO_PARENT_ADDRESS) break;
+ currentAddress = currentInfo.mParentAddress + currentInfo.mOriginalAddress;
+ }
+
+ return new String(sGetWordBuffer, index + 1, FormatSpec.MAX_WORD_LENGTH - index - 1);
+ }
+
+ private static String getWordAtAddressWithoutParentAddress(
+ final FusionDictionaryBufferInterface buffer, final int headerSize, final int address,
+ final FormatOptions options) {
+ buffer.position(headerSize);
+ final int count = readCharGroupCount(buffer);
int groupOffset = getGroupCountSize(count);
final StringBuilder builder = new StringBuilder();
String result = null;
CharGroupInfo last = null;
for (int i = count - 1; i >= 0; --i) {
- CharGroupInfo info = readCharGroup(source, groupOffset);
+ CharGroupInfo info = readCharGroup(buffer, groupOffset, options);
groupOffset = info.mEndAddress;
if (info.mOriginalAddress == address) {
builder.append(new String(info.mCharacters, 0, info.mCharacters.length));
@@ -1239,9 +1427,9 @@ public class BinaryDictInputOutput {
if (info.mChildrenAddress > address) {
if (null == last) continue;
builder.append(new String(last.mCharacters, 0, last.mCharacters.length));
- source.seek(last.mChildrenAddress + headerSize);
+ buffer.position(last.mChildrenAddress + headerSize);
groupOffset = last.mChildrenAddress + 1;
- i = source.readUnsignedByte();
+ i = buffer.readUnsignedByte();
last = null;
continue;
}
@@ -1249,67 +1437,91 @@ public class BinaryDictInputOutput {
}
if (0 == i && hasChildrenAddress(last.mChildrenAddress)) {
builder.append(new String(last.mCharacters, 0, last.mCharacters.length));
- source.seek(last.mChildrenAddress + headerSize);
+ buffer.position(last.mChildrenAddress + headerSize);
groupOffset = last.mChildrenAddress + 1;
- i = source.readUnsignedByte();
+ i = buffer.readUnsignedByte();
last = null;
continue;
}
}
- source.seek(originalPointer);
- wordCache.put(address, result);
return result;
}
/**
- * Reads a single node from a binary file.
+ * Reads a single node from a buffer.
*
- * This methods reads the file at the current position of its file pointer. A node is
- * fully expected to start at the current position.
+ * This methods reads the file at the current position. A node is fully expected to start at
+ * the current position.
* This will recursively read other nodes into the structure, populating the reverse
* maps on the fly and using them to keep track of already read nodes.
*
- * @param source the data file, correctly positioned at the start of a node.
+ * @param buffer the buffer, correctly positioned at the start of a node.
* @param headerSize the size, in bytes, of the file header.
* @param reverseNodeMap a mapping from addresses to already read nodes.
* @param reverseGroupMap a mapping from addresses to already read character groups.
+ * @param options file format options.
* @return the read node with all his children already read.
*/
- private static Node readNode(RandomAccessFile source, long headerSize,
- Map<Integer, Node> reverseNodeMap, Map<Integer, CharGroup> reverseGroupMap)
+ private static Node readNode(final FusionDictionaryBufferInterface buffer, final int headerSize,
+ final Map<Integer, Node> reverseNodeMap, final Map<Integer, CharGroup> reverseGroupMap,
+ final FormatOptions options)
throws IOException {
- final int nodeOrigin = (int)(source.getFilePointer() - headerSize);
- final int count = readCharGroupCount(source);
final ArrayList<CharGroup> nodeContents = new ArrayList<CharGroup>();
- int groupOffset = nodeOrigin + getGroupCountSize(count);
- for (int i = count; i > 0; --i) {
- CharGroupInfo info = readCharGroup(source, groupOffset);
- 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(source, headerSize, bigram.mAddress);
- 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);
+ if (isMovedGroup(info.mFlags, options)) continue;
+ 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;
}
- if (hasChildrenAddress(info.mChildrenAddress)) {
- Node children = reverseNodeMap.get(info.mChildrenAddress);
- if (null == children) {
- final long currentPosition = source.getFilePointer();
- source.seek(info.mChildrenAddress + headerSize);
- children = readNode(source, headerSize, reverseNodeMap, reverseGroupMap);
- source.seek(currentPosition);
+
+ // reach the end of the array.
+ if (options.mSupportsDynamicUpdate) {
+ final int nextAddress = buffer.readUnsignedInt24();
+ if (nextAddress >= 0 && nextAddress < buffer.limit()) {
+ buffer.position(nextAddress);
+ } else {
+ break;
}
- nodeContents.add(
- new CharGroup(info.mCharacters, shortcutTargets, bigrams, info.mFrequency,
- children));
- } else {
- nodeContents.add(
- new CharGroup(info.mCharacters, shortcutTargets, bigrams, info.mFrequency));
}
- groupOffset = info.mEndAddress;
- }
+ } while (options.mSupportsDynamicUpdate &&
+ buffer.position() != FormatSpec.NO_FORWARD_LINK_ADDRESS);
+
final Node node = new Node(nodeContents);
node.mCachedAddress = nodeOrigin;
reverseNodeMap.put(node.mCachedAddress, node);
@@ -1318,65 +1530,116 @@ public class BinaryDictInputOutput {
/**
* Helper function to get the binary format version from the header.
+ * @throws IOException
*/
- private static int getFormatVersion(final RandomAccessFile source) throws IOException {
- final int magic_v1 = source.readUnsignedShort();
- if (VERSION_1_MAGIC_NUMBER == magic_v1) return source.readUnsignedByte();
- final int magic_v2 = (magic_v1 << 16) + source.readUnsignedShort();
- if (VERSION_2_MAGIC_NUMBER == magic_v2) return source.readUnsignedShort();
- return NOT_A_VERSION_NUMBER;
+ private static int getFormatVersion(final FusionDictionaryBufferInterface buffer)
+ throws IOException {
+ final int magic_v1 = buffer.readUnsignedShort();
+ if (FormatSpec.VERSION_1_MAGIC_NUMBER == magic_v1) return buffer.readUnsignedByte();
+ final int magic_v2 = (magic_v1 << 16) + buffer.readUnsignedShort();
+ if (FormatSpec.VERSION_2_MAGIC_NUMBER == magic_v2) return buffer.readUnsignedShort();
+ return FormatSpec.NOT_A_VERSION_NUMBER;
}
/**
- * Reads a random access file and returns the memory representation of the dictionary.
- *
- * This high-level method takes a binary file and reads its contents, populating a
- * FusionDictionary structure. The optional dict argument is an existing dictionary to
- * which words from the file should be added. If it is null, a new dictionary is created.
- *
- * @param source the file to read.
- * @param dict an optional dictionary to add words to, or null.
- * @return the created (or merged) dictionary.
+ * Helper function to get and validate the binary format version.
+ * @throws UnsupportedFormatException
+ * @throws IOException
*/
- public static FusionDictionary readDictionaryBinary(final RandomAccessFile source,
- final FusionDictionary dict) throws IOException, UnsupportedFormatException {
- // Check file version
- final int version = getFormatVersion(source);
- if (version < MINIMUM_SUPPORTED_VERSION || version > MAXIMUM_SUPPORTED_VERSION ) {
+ private static int checkFormatVersion(final FusionDictionaryBufferInterface buffer)
+ throws IOException, UnsupportedFormatException {
+ final int version = getFormatVersion(buffer);
+ if (version < FormatSpec.MINIMUM_SUPPORTED_VERSION
+ || version > FormatSpec.MAXIMUM_SUPPORTED_VERSION) {
throw new UnsupportedFormatException("This file has version " + version
+ ", but this implementation does not support versions above "
- + MAXIMUM_SUPPORTED_VERSION);
+ + FormatSpec.MAXIMUM_SUPPORTED_VERSION);
}
+ return version;
+ }
- // Read options
- final int optionsFlags = source.readUnsignedShort();
+ /**
+ * Reads a header from a buffer.
+ * @param buffer the buffer to read.
+ * @throws IOException
+ * @throws UnsupportedFormatException
+ */
+ public static FileHeader readHeader(final FusionDictionaryBufferInterface buffer)
+ throws IOException, UnsupportedFormatException {
+ final int version = checkFormatVersion(buffer);
+ final int optionsFlags = buffer.readUnsignedShort();
- final long headerSize;
- final HashMap<String, String> options = new HashMap<String, String>();
- if (version < FIRST_VERSION_WITH_HEADER_SIZE) {
- headerSize = source.getFilePointer();
+ final HashMap<String, String> attributes = new HashMap<String, String>();
+ final int headerSize;
+ if (version < FormatSpec.FIRST_VERSION_WITH_HEADER_SIZE) {
+ headerSize = buffer.position();
} else {
- headerSize = (source.readUnsignedByte() << 24) + (source.readUnsignedByte() << 16)
- + (source.readUnsignedByte() << 8) + source.readUnsignedByte();
- while (source.getFilePointer() < headerSize) {
- final String key = CharEncoding.readString(source);
- final String value = CharEncoding.readString(source);
- options.put(key, value);
- }
- source.seek(headerSize);
+ headerSize = buffer.readInt();
+ populateOptions(buffer, headerSize, attributes);
+ buffer.position(headerSize);
+ }
+
+ if (headerSize < 0) {
+ throw new UnsupportedFormatException("header size can't be negative.");
+ }
+
+ final FileHeader header = new FileHeader(headerSize,
+ new FusionDictionary.DictionaryOptions(attributes,
+ 0 != (optionsFlags & FormatSpec.GERMAN_UMLAUT_PROCESSING_FLAG),
+ 0 != (optionsFlags & FormatSpec.FRENCH_LIGATURE_PROCESSING_FLAG)),
+ new FormatOptions(version,
+ 0 != (optionsFlags & FormatSpec.SUPPORTS_DYNAMIC_UPDATE)));
+ return header;
+ }
+
+ /**
+ * Reads options from a buffer and populate a map with their contents.
+ *
+ * The buffer is read at the current position, so the caller must take care the pointer
+ * is in the right place before calling this.
+ */
+ public static void populateOptions(final FusionDictionaryBufferInterface buffer,
+ final int headerSize, final HashMap<String, String> options) {
+ while (buffer.position() < headerSize) {
+ final String key = CharEncoding.readString(buffer);
+ final String value = CharEncoding.readString(buffer);
+ options.put(key, value);
}
+ }
+
+ /**
+ * Reads a buffer and returns the memory representation of the dictionary.
+ *
+ * This high-level method takes a buffer and reads its contents, populating a
+ * FusionDictionary structure. The optional dict argument is an existing dictionary to
+ * which words from the buffer should be added. If it is null, a new dictionary is created.
+ *
+ * @param buffer the buffer to read.
+ * @param dict an optional dictionary to add words to, or null.
+ * @return the created (or merged) dictionary.
+ */
+ public static FusionDictionary readDictionaryBinary(
+ final FusionDictionaryBufferInterface buffer, final FusionDictionary dict)
+ throws IOException, UnsupportedFormatException {
+ // clear cache
+ wordCache.clear();
+
+ // Read header
+ final FileHeader header = readHeader(buffer);
Map<Integer, Node> reverseNodeMapping = new TreeMap<Integer, Node>();
Map<Integer, CharGroup> reverseGroupMapping = new TreeMap<Integer, CharGroup>();
- final Node root = readNode(source, headerSize, reverseNodeMapping, reverseGroupMapping);
+ final Node root = readNode(buffer, header.mHeaderSize, reverseNodeMapping,
+ reverseGroupMapping, header.mFormatOptions);
- FusionDictionary newDict = new FusionDictionary(root,
- new FusionDictionary.DictionaryOptions(options,
- 0 != (optionsFlags & GERMAN_UMLAUT_PROCESSING_FLAG),
- 0 != (optionsFlags & FRENCH_LIGATURE_PROCESSING_FLAG)));
+ FusionDictionary newDict = new FusionDictionary(root, header.mDictionaryOptions);
if (null != dict) {
for (final Word w : dict) {
- newDict.add(w.mWord, w.mFrequency, w.mShortcutTargets);
+ if (w.mIsBlacklistEntry) {
+ newDict.addBlacklistEntry(w.mWord, w.mShortcutTargets, w.mIsNotAWord);
+ } else {
+ newDict.add(w.mWord, w.mFrequency, w.mShortcutTargets, w.mIsNotAWord);
+ }
}
for (final Word w : dict) {
// By construction a binary dictionary may not have bigrams pointing to
@@ -1400,14 +1663,45 @@ public class BinaryDictInputOutput {
* @return true if it's a binary dictionary, false otherwise
*/
public static boolean isBinaryDictionary(final String filename) {
+ FileInputStream inStream = null;
try {
- RandomAccessFile f = new RandomAccessFile(filename, "r");
- final int version = getFormatVersion(f);
- return (version >= MINIMUM_SUPPORTED_VERSION && version <= MAXIMUM_SUPPORTED_VERSION);
+ final File file = new File(filename);
+ inStream = new FileInputStream(file);
+ final ByteBuffer buffer = inStream.getChannel().map(
+ FileChannel.MapMode.READ_ONLY, 0, file.length());
+ final int version = getFormatVersion(new ByteBufferWrapper(buffer));
+ return (version >= FormatSpec.MINIMUM_SUPPORTED_VERSION
+ && version <= FormatSpec.MAXIMUM_SUPPORTED_VERSION);
} catch (FileNotFoundException e) {
return false;
} catch (IOException e) {
return false;
+ } finally {
+ if (inStream != null) {
+ try {
+ inStream.close();
+ } catch (IOException e) {
+ // do nothing
+ }
+ }
}
}
+
+ /**
+ * Calculate bigram frequency from compressed value
+ *
+ * @see #makeBigramFlags
+ *
+ * @param unigramFrequency
+ * @param bigramFrequency compressed frequency
+ * @return approximate bigram frequency
+ */
+ public static int reconstructBigramFrequency(final int unigramFrequency,
+ final int bigramFrequency) {
+ final float stepSize = (FormatSpec.MAX_TERMINAL_FREQUENCY - unigramFrequency)
+ / (1.5f + FormatSpec.MAX_BIGRAM_FREQUENCY);
+ final float resultFreqFloat = (float)unigramFrequency
+ + stepSize * (bigramFrequency + 1.0f);
+ return (int)resultFreqFloat;
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/makedict/CharGroupInfo.java b/java/src/com/android/inputmethod/latin/makedict/CharGroupInfo.java
index ef7dbb251..8e64082e6 100644
--- a/java/src/com/android/inputmethod/latin/makedict/CharGroupInfo.java
+++ b/java/src/com/android/inputmethod/latin/makedict/CharGroupInfo.java
@@ -23,7 +23,7 @@ import java.util.ArrayList;
/**
* Raw char group info straight out of a file. This will contain numbers for addresses.
*/
-public class CharGroupInfo {
+public final class CharGroupInfo {
public final int mOriginalAddress;
public final int mEndAddress;
@@ -31,18 +31,20 @@ public class CharGroupInfo {
public final int[] mCharacters;
public final int mFrequency;
public final int mChildrenAddress;
+ public final int mParentAddress;
public final ArrayList<WeightedString> mShortcutTargets;
public final ArrayList<PendingAttribute> mBigrams;
public CharGroupInfo(final int originalAddress, final int endAddress, final int flags,
- final int[] characters, final int frequency, final int childrenAddress,
- final ArrayList<WeightedString> shortcutTargets,
+ final int[] characters, final int frequency, final int parentAddress,
+ final int childrenAddress, final ArrayList<WeightedString> shortcutTargets,
final ArrayList<PendingAttribute> bigrams) {
mOriginalAddress = originalAddress;
mEndAddress = endAddress;
mFlags = flags;
mCharacters = characters;
mFrequency = frequency;
+ mParentAddress = parentAddress;
mChildrenAddress = childrenAddress;
mShortcutTargets = shortcutTargets;
mBigrams = bigrams;
diff --git a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
new file mode 100644
index 000000000..b3fbb9fb5
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
@@ -0,0 +1,258 @@
+/*
+ * 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.FusionDictionary.DictionaryOptions;
+
+/**
+ * Dictionary File Format Specification.
+ */
+public final class FormatSpec {
+
+ /*
+ * Array of Node(FusionDictionary.Node) layout is as follows:
+ *
+ * g |
+ * r | the number of groups, 1 or 2 bytes.
+ * o | 1 byte = bbbbbbbb match
+ * u | case 1xxxxxxx => xxxxxxx << 8 + next byte
+ * p | otherwise => bbbbbbbb
+ * c |
+ * ount
+ *
+ * g |
+ * r | sequence of groups,
+ * o | the layout of each group is described below.
+ * u |
+ * ps
+ *
+ * f |
+ * o | IF SUPPORTS_DYNAMIC_UPDATE (defined in the file header)
+ * r | forward link address, 3byte
+ * w | 1 byte = bbbbbbbb match
+ * a | case 1xxxxxxx => -((xxxxxxx << 16) + (next byte << 8) + next byte)
+ * r | otherwise => (xxxxxxx << 16) + (next byte << 8) + next byte
+ * d |
+ * linkaddress
+ */
+
+ /* Node(CharGroup) layout is as follows:
+ * | IF !SUPPORTS_DYNAMIC_UPDATE
+ * | addressType xx : mask with MASK_GROUP_ADDRESS_TYPE
+ * | 2 bits, 00 = no children : FLAG_GROUP_ADDRESS_TYPE_NOADDRESS
+ * f | 01 = 1 byte : FLAG_GROUP_ADDRESS_TYPE_ONEBYTE
+ * l | 10 = 2 bytes : FLAG_GROUP_ADDRESS_TYPE_TWOBYTES
+ * a | 11 = 3 bytes : FLAG_GROUP_ADDRESS_TYPE_THREEBYTES
+ * g | ELSE
+ * s | is moved ? 2 bits, 11 = no
+ * | 01 = yes
+ * | the new address is stored in the same place as the parent address
+ * | has several chars ? 1 bit, 1 = yes, 0 = no : FLAG_HAS_MULTIPLE_CHARS
+ * | has a terminal ? 1 bit, 1 = yes, 0 = no : FLAG_IS_TERMINAL
+ * | has shortcut targets ? 1 bit, 1 = yes, 0 = no : FLAG_HAS_SHORTCUT_TARGETS
+ * | has bigrams ? 1 bit, 1 = yes, 0 = no : FLAG_HAS_BIGRAMS
+ * | is not a word ? 1 bit, 1 = yes, 0 = no : FLAG_IS_NOT_A_WORD
+ * | is blacklisted ? 1 bit, 1 = yes, 0 = no : FLAG_IS_BLACKLISTED
+ *
+ * p |
+ * a | IF SUPPORTS_DYNAMIC_UPDATE (defined in the file header)
+ * r | parent address, 3byte
+ * e | 1 byte = bbbbbbbb match
+ * n | case 1xxxxxxx => -((0xxxxxxx << 16) + (next byte << 8) + next byte)
+ * t | otherwise => (bbbbbbbb << 16) + (next byte << 8) + next byte
+ * a |
+ * ddress
+ *
+ * c | IF FLAG_HAS_MULTIPLE_CHARS
+ * h | char, char, char, char n * (1 or 3 bytes) : use CharGroupInfo for i/o helpers
+ * a | end 1 byte, = 0
+ * r | ELSE
+ * s | char 1 or 3 bytes
+ * | END
+ *
+ * f |
+ * r | IF FLAG_IS_TERMINAL
+ * e | frequency 1 byte
+ * q |
+ *
+ * c | IF 00 = FLAG_GROUP_ADDRESS_TYPE_NOADDRESS = addressType
+ * h | // nothing
+ * i | ELSIF 01 = FLAG_GROUP_ADDRESS_TYPE_ONEBYTE == addressType
+ * l | children address, 1 byte
+ * d | ELSIF 10 = FLAG_GROUP_ADDRESS_TYPE_TWOBYTES == addressType
+ * r | children address, 2 bytes
+ * e | ELSE // 11 = FLAG_GROUP_ADDRESS_TYPE_THREEBYTES = addressType
+ * n | children address, 3 bytes
+ * A | END
+ * d
+ * dress
+ *
+ * | IF FLAG_IS_TERMINAL && FLAG_HAS_SHORTCUT_TARGETS
+ * | shortcut string list
+ * | IF FLAG_IS_TERMINAL && FLAG_HAS_BIGRAMS
+ * | bigrams address list
+ *
+ * Char format is:
+ * 1 byte = bbbbbbbb match
+ * case 000xxxxx: xxxxx << 16 + next byte << 8 + next byte
+ * else: if 00011111 (= 0x1F) : this is the terminator. This is a relevant choice because
+ * unicode code points range from 0 to 0x10FFFF, so any 3-byte value starting with
+ * 00011111 would be outside unicode.
+ * else: iso-latin-1 code
+ * This allows for the whole unicode range to be encoded, including chars outside of
+ * the BMP. Also everything in the iso-latin-1 charset is only 1 byte, except control
+ * characters which should never happen anyway (and still work, but take 3 bytes).
+ *
+ * bigram address list is:
+ * <flags> = | hasNext = 1 bit, 1 = yes, 0 = no : FLAG_ATTRIBUTE_HAS_NEXT
+ * | addressSign = 1 bit, : FLAG_ATTRIBUTE_OFFSET_NEGATIVE
+ * | 1 = must take -address, 0 = must take +address
+ * | xx : mask with MASK_ATTRIBUTE_ADDRESS_TYPE
+ * | addressFormat = 2 bits, 00 = unused : FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE
+ * | 01 = 1 byte : FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE
+ * | 10 = 2 bytes : FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES
+ * | 11 = 3 bytes : FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES
+ * | 4 bits : frequency : mask with FLAG_ATTRIBUTE_FREQUENCY
+ * <address> | IF (01 == FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE == addressFormat)
+ * | read 1 byte, add top 4 bits
+ * | ELSIF (10 == FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES == addressFormat)
+ * | read 2 bytes, add top 4 bits
+ * | ELSE // 11 == FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES == addressFormat
+ * | read 3 bytes, add top 4 bits
+ * | END
+ * | if (FLAG_ATTRIBUTE_OFFSET_NEGATIVE) then address = -address
+ * if (FLAG_ATTRIBUTE_HAS_NEXT) goto bigram_and_shortcut_address_list_is
+ *
+ * shortcut string list is:
+ * <byte size> = GROUP_SHORTCUT_LIST_SIZE_SIZE bytes, big-endian: size of the list, in bytes.
+ * <flags> = | hasNext = 1 bit, 1 = yes, 0 = no : FLAG_ATTRIBUTE_HAS_NEXT
+ * | reserved = 3 bits, must be 0
+ * | 4 bits : frequency : mask with FLAG_ATTRIBUTE_FREQUENCY
+ * <shortcut> = | string of characters at the char format described above, with the terminator
+ * | used to signal the end of the string.
+ * if (FLAG_ATTRIBUTE_HAS_NEXT goto flags
+ */
+
+ static final int VERSION_1_MAGIC_NUMBER = 0x78B1;
+ public static final int VERSION_2_MAGIC_NUMBER = 0x9BC13AFE;
+ static final int MINIMUM_SUPPORTED_VERSION = 1;
+ static final int MAXIMUM_SUPPORTED_VERSION = 3;
+ static final int NOT_A_VERSION_NUMBER = -1;
+ static final int FIRST_VERSION_WITH_HEADER_SIZE = 2;
+ static final int FIRST_VERSION_WITH_DYNAMIC_UPDATE = 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 SUPPORTS_DYNAMIC_UPDATE = 0x2;
+ static final int FRENCH_LIGATURE_PROCESSING_FLAG = 0x4;
+ static final int CONTAINS_BIGRAMS_FLAG = 0x8;
+
+ // 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;
+ static final int FLAG_GROUP_ADDRESS_TYPE_ONEBYTE = 0x40;
+ static final int FLAG_GROUP_ADDRESS_TYPE_TWOBYTES = 0x80;
+ static final int FLAG_GROUP_ADDRESS_TYPE_THREEBYTES = 0xC0;
+
+ static final int FLAG_HAS_MULTIPLE_CHARS = 0x20;
+
+ static final int FLAG_IS_TERMINAL = 0x10;
+ static final int FLAG_HAS_SHORTCUT_TARGETS = 0x08;
+ static final int FLAG_HAS_BIGRAMS = 0x04;
+ static final int FLAG_IS_NOT_A_WORD = 0x02;
+ static final int FLAG_IS_BLACKLISTED = 0x01;
+ static final int FLAG_IS_MOVED = 0x40;
+
+ static final int FLAG_ATTRIBUTE_HAS_NEXT = 0x80;
+ static final int FLAG_ATTRIBUTE_OFFSET_NEGATIVE = 0x40;
+ static final int MASK_ATTRIBUTE_ADDRESS_TYPE = 0x30;
+ static final int FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE = 0x10;
+ static final int FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES = 0x20;
+ static final int FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES = 0x30;
+ static final int FLAG_ATTRIBUTE_FREQUENCY = 0x0F;
+
+ static final int GROUP_CHARACTERS_TERMINATOR = 0x1F;
+
+ static final int GROUP_TERMINATOR_SIZE = 1;
+ static final int GROUP_FLAGS_SIZE = 1;
+ static final int GROUP_FREQUENCY_SIZE = 1;
+ static final int GROUP_MAX_ADDRESS_SIZE = 3;
+ static final int GROUP_ATTRIBUTE_FLAGS_SIZE = 1;
+ static final int GROUP_ATTRIBUTE_MAX_ADDRESS_SIZE = 3;
+ static final int GROUP_SHORTCUT_LIST_SIZE_SIZE = 2;
+
+ 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
+ static final int MAX_CHARGROUPS_IN_A_NODE = 0x7FFF; // 32767
+
+ 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;
+ static final int SIGNED_CHILDREN_ADDRESS_SIZE = 3;
+
+ /**
+ * Options about file format.
+ */
+ public static final class FormatOptions {
+ public final int mVersion;
+ public final boolean mSupportsDynamicUpdate;
+ public FormatOptions(final int version) {
+ this(version, false);
+ }
+ public FormatOptions(final int version, final boolean supportsDynamicUpdate) {
+ mVersion = version;
+ if (version < FIRST_VERSION_WITH_DYNAMIC_UPDATE && supportsDynamicUpdate) {
+ throw new RuntimeException("Dynamic updates are only supported with versions "
+ + FIRST_VERSION_WITH_DYNAMIC_UPDATE + " and ulterior.");
+ }
+ mSupportsDynamicUpdate = supportsDynamicUpdate;
+ }
+ }
+
+ /**
+ * Class representing file header.
+ */
+ static final class FileHeader {
+ public final int mHeaderSize;
+ public final DictionaryOptions mDictionaryOptions;
+ public final FormatOptions mFormatOptions;
+ public FileHeader(final int headerSize, final DictionaryOptions dictionaryOptions,
+ final FormatOptions formatOptions) {
+ mHeaderSize = headerSize;
+ mDictionaryOptions = dictionaryOptions;
+ mFormatOptions = formatOptions;
+ }
+ }
+
+ private FormatSpec() {
+ // This utility class is not publicly instantiable.
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java
index 8b53c9427..3193ef457 100644
--- a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java
+++ b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java
@@ -16,6 +16,8 @@
package com.android.inputmethod.latin.makedict;
+import com.android.inputmethod.latin.Constants;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -26,8 +28,7 @@ import java.util.LinkedList;
/**
* A dictionary that can fusion heads and tails of words for more compression.
*/
-public class FusionDictionary implements Iterable<Word> {
-
+public final class FusionDictionary implements Iterable<Word> {
private static final boolean DBG = MakedictLog.DBG;
/**
@@ -38,20 +39,18 @@ public class FusionDictionary implements Iterable<Word> {
* This class also contains fields to cache size and address, to help with binary
* generation.
*/
- public static class Node {
+ public static final class Node {
ArrayList<CharGroup> mData;
// To help with binary generation
- int mCachedSize;
- int mCachedAddress;
+ int mCachedSize = Integer.MIN_VALUE;
+ int mCachedAddress = Integer.MIN_VALUE;
+ int mCachedParentAddress = 0;
+
public Node() {
mData = new ArrayList<CharGroup>();
- mCachedSize = Integer.MIN_VALUE;
- mCachedAddress = Integer.MIN_VALUE;
}
public Node(ArrayList<CharGroup> data) {
mData = data;
- mCachedSize = Integer.MIN_VALUE;
- mCachedAddress = Integer.MIN_VALUE;
}
}
@@ -60,9 +59,9 @@ public class FusionDictionary implements Iterable<Word> {
*
* This represents an "attribute", that is either a bigram or a shortcut.
*/
- public static class WeightedString {
- final String mWord;
- int mFrequency;
+ public static final class WeightedString {
+ public final String mWord;
+ public int mFrequency;
public WeightedString(String word, int frequency) {
mWord = word;
mFrequency = frequency;
@@ -94,33 +93,41 @@ public class FusionDictionary implements Iterable<Word> {
* value is the frequency of this terminal. A terminal may have non-null shortcuts and/or
* bigrams, but a non-terminal may not. Moreover, children, if present, are null.
*/
- public static class CharGroup {
+ public static final class CharGroup {
public static final int NOT_A_TERMINAL = -1;
final int mChars[];
ArrayList<WeightedString> mShortcutTargets;
ArrayList<WeightedString> mBigrams;
int mFrequency; // NOT_A_TERMINAL == mFrequency indicates this is not a terminal.
Node mChildren;
+ boolean mIsNotAWord; // Only a shortcut
+ boolean mIsBlacklistEntry;
// The two following members to help with binary generation
int mCachedSize;
int mCachedAddress;
public CharGroup(final int[] chars, final ArrayList<WeightedString> shortcutTargets,
- final ArrayList<WeightedString> bigrams, final int frequency) {
+ final ArrayList<WeightedString> bigrams, final int frequency,
+ final boolean isNotAWord, final boolean isBlacklistEntry) {
mChars = chars;
mFrequency = frequency;
mShortcutTargets = shortcutTargets;
mBigrams = bigrams;
mChildren = null;
+ mIsNotAWord = isNotAWord;
+ mIsBlacklistEntry = isBlacklistEntry;
}
public CharGroup(final int[] chars, final ArrayList<WeightedString> shortcutTargets,
- final ArrayList<WeightedString> bigrams, final int frequency, final Node children) {
+ final ArrayList<WeightedString> bigrams, final int frequency,
+ final boolean isNotAWord, final boolean isBlacklistEntry, final Node children) {
mChars = chars;
mFrequency = frequency;
mShortcutTargets = shortcutTargets;
mBigrams = bigrams;
mChildren = children;
+ mIsNotAWord = isNotAWord;
+ mIsBlacklistEntry = isBlacklistEntry;
}
public void addChild(CharGroup n) {
@@ -197,8 +204,9 @@ public class FusionDictionary implements Iterable<Word> {
* the existing ones if any. Note: unigram, bigram, and shortcut frequencies are only
* updated if they are higher than the existing ones.
*/
- public void update(int frequency, ArrayList<WeightedString> shortcutTargets,
- ArrayList<WeightedString> bigrams) {
+ public void update(final int frequency, final ArrayList<WeightedString> shortcutTargets,
+ final ArrayList<WeightedString> bigrams,
+ final boolean isNotAWord, final boolean isBlacklistEntry) {
if (frequency > mFrequency) {
mFrequency = frequency;
}
@@ -234,6 +242,8 @@ public class FusionDictionary implements Iterable<Word> {
}
}
}
+ mIsNotAWord = isNotAWord;
+ mIsBlacklistEntry = isBlacklistEntry;
}
}
@@ -242,7 +252,7 @@ public class FusionDictionary implements Iterable<Word> {
*
* There are no options at the moment, so this class is empty.
*/
- public static class DictionaryOptions {
+ public static final class DictionaryOptions {
public final boolean mGermanUmlautProcessing;
public final boolean mFrenchLigatureProcessing;
public final HashMap<String, String> mAttributes;
@@ -296,10 +306,24 @@ public class FusionDictionary implements Iterable<Word> {
* @param word the word to add.
* @param frequency the frequency of the word, in the range [0..255].
* @param shortcutTargets a list of shortcut targets for this word, or null.
+ * @param isNotAWord true if this should not be considered a word (e.g. shortcut only)
*/
public void add(final String word, final int frequency,
- final ArrayList<WeightedString> shortcutTargets) {
- add(getCodePoints(word), frequency, shortcutTargets);
+ final ArrayList<WeightedString> shortcutTargets, final boolean isNotAWord) {
+ add(getCodePoints(word), frequency, shortcutTargets, isNotAWord,
+ false /* isBlacklistEntry */);
+ }
+
+ /**
+ * Helper method to add a blacklist entry as a string.
+ *
+ * @param word the word to add as a blacklist entry.
+ * @param shortcutTargets a list of shortcut targets for this word, or null.
+ * @param isNotAWord true if this is not a word for spellcheking purposes (shortcut only or so)
+ */
+ public void addBlacklistEntry(final String word,
+ final ArrayList<WeightedString> shortcutTargets, final boolean isNotAWord) {
+ add(getCodePoints(word), 0, shortcutTargets, isNotAWord, true /* isBlacklistEntry */);
}
/**
@@ -332,7 +356,8 @@ public class FusionDictionary implements Iterable<Word> {
if (charGroup != null) {
final CharGroup charGroup2 = findWordInTree(mRoot, word2);
if (charGroup2 == null) {
- add(getCodePoints(word2), 0, null);
+ add(getCodePoints(word2), 0, null, false /* isNotAWord */,
+ false /* isBlacklistEntry */);
}
charGroup.addBigram(word2, frequency);
} else {
@@ -349,10 +374,18 @@ public class FusionDictionary implements Iterable<Word> {
* @param word the word, as an int array.
* @param frequency the frequency of the word, in the range [0..255].
* @param shortcutTargets an optional list of shortcut targets for this word (null if none).
+ * @param isNotAWord true if this is not a word for spellcheking purposes (shortcut only or so)
+ * @param isBlacklistEntry true if this is a blacklisted word, false otherwise
*/
private void add(final int[] word, final int frequency,
- final ArrayList<WeightedString> shortcutTargets) {
+ final ArrayList<WeightedString> shortcutTargets,
+ final boolean isNotAWord, final boolean isBlacklistEntry) {
assert(frequency >= 0 && frequency <= 255);
+ if (word.length >= Constants.Dictionary.MAX_WORD_LENGTH) {
+ MakedictLog.w("Ignoring a word that is too long: word.length = " + word.length);
+ return;
+ }
+
Node currentNode = mRoot;
int charIndex = 0;
@@ -376,7 +409,7 @@ public class FusionDictionary implements Iterable<Word> {
final int insertionIndex = findInsertionIndex(currentNode, word[charIndex]);
final CharGroup newGroup = new CharGroup(
Arrays.copyOfRange(word, charIndex, word.length),
- shortcutTargets, null /* bigrams */, frequency);
+ shortcutTargets, null /* bigrams */, frequency, isNotAWord, isBlacklistEntry);
currentNode.mData.add(insertionIndex, newGroup);
if (DBG) checkStack(currentNode);
} else {
@@ -386,13 +419,15 @@ public class FusionDictionary implements Iterable<Word> {
// The new word is a prefix of an existing word, but the node on which it
// should end already exists as is. Since the old CharNode was not a terminal,
// make it one by filling in its frequency and other attributes
- currentGroup.update(frequency, shortcutTargets, null);
+ currentGroup.update(frequency, shortcutTargets, null, isNotAWord,
+ isBlacklistEntry);
} else {
// The new word matches the full old word and extends past it.
// We only have to create a new node and add it to the end of this.
final CharGroup newNode = new CharGroup(
Arrays.copyOfRange(word, charIndex + differentCharIndex, word.length),
- shortcutTargets, null /* bigrams */, frequency);
+ shortcutTargets, null /* bigrams */, frequency, isNotAWord,
+ isBlacklistEntry);
currentGroup.mChildren = new Node();
currentGroup.mChildren.mData.add(newNode);
}
@@ -400,7 +435,9 @@ public class FusionDictionary implements Iterable<Word> {
if (0 == differentCharIndex) {
// Exact same word. Update the frequency if higher. This will also add the
// new shortcuts to the existing shortcut list if it already exists.
- currentGroup.update(frequency, shortcutTargets, null);
+ currentGroup.update(frequency, shortcutTargets, null,
+ currentGroup.mIsNotAWord && isNotAWord,
+ currentGroup.mIsBlacklistEntry || isBlacklistEntry);
} else {
// Partial prefix match only. We have to replace the current node with a node
// containing the current prefix and create two new ones for the tails.
@@ -408,21 +445,26 @@ public class FusionDictionary implements Iterable<Word> {
final CharGroup newOldWord = new CharGroup(
Arrays.copyOfRange(currentGroup.mChars, differentCharIndex,
currentGroup.mChars.length), currentGroup.mShortcutTargets,
- currentGroup.mBigrams, currentGroup.mFrequency, currentGroup.mChildren);
+ currentGroup.mBigrams, currentGroup.mFrequency,
+ currentGroup.mIsNotAWord, currentGroup.mIsBlacklistEntry,
+ currentGroup.mChildren);
newChildren.mData.add(newOldWord);
final CharGroup newParent;
if (charIndex + differentCharIndex >= word.length) {
newParent = new CharGroup(
Arrays.copyOfRange(currentGroup.mChars, 0, differentCharIndex),
- shortcutTargets, null /* bigrams */, frequency, newChildren);
+ shortcutTargets, null /* bigrams */, frequency,
+ isNotAWord, isBlacklistEntry, newChildren);
} else {
newParent = new CharGroup(
Arrays.copyOfRange(currentGroup.mChars, 0, differentCharIndex),
- null /* shortcutTargets */, null /* bigrams */, -1, newChildren);
+ null /* shortcutTargets */, null /* bigrams */, -1,
+ false /* isNotAWord */, false /* isBlacklistEntry */, newChildren);
final CharGroup newWord = new CharGroup(Arrays.copyOfRange(word,
charIndex + differentCharIndex, word.length),
- shortcutTargets, null /* bigrams */, frequency);
+ shortcutTargets, null /* bigrams */, frequency,
+ isNotAWord, isBlacklistEntry);
final int addIndex = word[charIndex + differentCharIndex]
> currentGroup.mChars[differentCharIndex] ? 1 : 0;
newChildren.mData.add(addIndex, newWord);
@@ -468,7 +510,7 @@ public class FusionDictionary implements Iterable<Word> {
* is ignored.
* This comparator imposes orderings that are inconsistent with equals.
*/
- static private class CharGroupComparator implements java.util.Comparator<CharGroup> {
+ static private final class CharGroupComparator implements java.util.Comparator<CharGroup> {
@Override
public int compare(CharGroup c1, CharGroup c2) {
if (c1.mChars[0] == c2.mChars[0]) return 0;
@@ -483,7 +525,8 @@ public class FusionDictionary implements Iterable<Word> {
private static int findInsertionIndex(final Node node, int character) {
final ArrayList<CharGroup> data = node.mData;
final CharGroup reference = new CharGroup(new int[] { character },
- null /* shortcutTargets */, null /* bigrams */, 0);
+ null /* shortcutTargets */, null /* bigrams */, 0, false /* isNotAWord */,
+ false /* isBlacklistEntry */);
int result = Collections.binarySearch(data, reference, CHARGROUP_COMPARATOR);
return result >= 0 ? result : -result - 1;
}
@@ -512,17 +555,28 @@ 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;
currentGroup = node.mData.get(indexOfGroup);
+
+ if (s.length() - index < currentGroup.mChars.length) return null;
+ int newIndex = index;
+ while (newIndex < s.length() && newIndex - index < currentGroup.mChars.length) {
+ if (currentGroup.mChars[newIndex - index] != s.codePointAt(newIndex)) return null;
+ newIndex++;
+ }
+ index = newIndex;
+
if (DBG) checker.append(new String(currentGroup.mChars, 0, currentGroup.mChars.length));
- index += 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 < codePointCountInS) return null;
+ if (!currentGroup.isTerminal()) return null;
if (DBG && !s.equals(checker.toString())) return null;
return currentGroup;
}
@@ -679,7 +733,7 @@ public class FusionDictionary implements Iterable<Word> {
// StringBuilder s = new StringBuilder();
// for (CharGroup g : node.data) {
// s.append(g.frequency);
-// for (int ch : g.chars){
+// for (int ch : g.chars) {
// s.append(Character.toChars(ch));
// }
// }
@@ -691,9 +745,8 @@ public class FusionDictionary implements Iterable<Word> {
*
* This is purely for convenience.
*/
- public static class DictionaryIterator implements Iterator<Word> {
-
- private static class Position {
+ public static final class DictionaryIterator implements Iterator<Word> {
+ private static final class Position {
public Iterator<CharGroup> pos;
public int length;
public Position(ArrayList<CharGroup> groups) {
@@ -738,13 +791,14 @@ public class FusionDictionary implements Iterable<Word> {
}
if (currentGroup.mFrequency >= 0)
return new Word(mCurrentString.toString(), currentGroup.mFrequency,
- currentGroup.mShortcutTargets, currentGroup.mBigrams);
+ currentGroup.mShortcutTargets, currentGroup.mBigrams,
+ currentGroup.mIsNotAWord, currentGroup.mIsBlacklistEntry);
} else {
mPositions.removeLast();
currentPos = mPositions.getLast();
mCurrentString.setLength(mCurrentString.length() - mPositions.getLast().length);
}
- } while(true);
+ } while (true);
}
@Override
diff --git a/java/src/com/android/inputmethod/latin/makedict/MakedictLog.java b/java/src/com/android/inputmethod/latin/makedict/MakedictLog.java
index 3f0cd0796..6c6b00b6a 100644
--- a/java/src/com/android/inputmethod/latin/makedict/MakedictLog.java
+++ b/java/src/com/android/inputmethod/latin/makedict/MakedictLog.java
@@ -21,7 +21,7 @@ import android.util.Log;
/**
* Wrapper to redirect log events to the right output medium.
*/
-public class MakedictLog {
+public final class MakedictLog {
public static final boolean DBG = false;
private static final String TAG = MakedictLog.class.getSimpleName();
diff --git a/java/src/com/android/inputmethod/latin/makedict/PendingAttribute.java b/java/src/com/android/inputmethod/latin/makedict/PendingAttribute.java
index 5b41d27f2..5bb24da74 100644
--- a/java/src/com/android/inputmethod/latin/makedict/PendingAttribute.java
+++ b/java/src/com/android/inputmethod/latin/makedict/PendingAttribute.java
@@ -22,7 +22,7 @@ package com.android.inputmethod.latin.makedict;
* An attribute is either a bigram or a shortcut.
* All instances of this class are always immutable.
*/
-public class PendingAttribute {
+public final class PendingAttribute {
public final int mFrequency;
public final int mAddress;
public PendingAttribute(final int frequency, final int address) {
diff --git a/java/src/com/android/inputmethod/latin/makedict/UnsupportedFormatException.java b/java/src/com/android/inputmethod/latin/makedict/UnsupportedFormatException.java
index bd42fb8fa..dbb2ea870 100644
--- a/java/src/com/android/inputmethod/latin/makedict/UnsupportedFormatException.java
+++ b/java/src/com/android/inputmethod/latin/makedict/UnsupportedFormatException.java
@@ -19,7 +19,7 @@ package com.android.inputmethod.latin.makedict;
/**
* Simple exception thrown when a file format is not recognized.
*/
-public class UnsupportedFormatException extends Exception {
+public final class UnsupportedFormatException extends Exception {
public UnsupportedFormatException(String description) {
super(description);
}
diff --git a/java/src/com/android/inputmethod/latin/makedict/Word.java b/java/src/com/android/inputmethod/latin/makedict/Word.java
index d07826757..4c4f18f1a 100644
--- a/java/src/com/android/inputmethod/latin/makedict/Word.java
+++ b/java/src/com/android/inputmethod/latin/makedict/Word.java
@@ -26,21 +26,26 @@ import java.util.Arrays;
*
* This is chiefly used to iterate a dictionary.
*/
-public class Word implements Comparable<Word> {
- final String mWord;
- final int mFrequency;
- final ArrayList<WeightedString> mShortcutTargets;
- final ArrayList<WeightedString> mBigrams;
+public final class Word implements Comparable<Word> {
+ public final String mWord;
+ public final int mFrequency;
+ public final ArrayList<WeightedString> mShortcutTargets;
+ public final ArrayList<WeightedString> mBigrams;
+ public final boolean mIsNotAWord;
+ public final boolean mIsBlacklistEntry;
private int mHashCode = 0;
public Word(final String word, final int frequency,
final ArrayList<WeightedString> shortcutTargets,
- final ArrayList<WeightedString> bigrams) {
+ final ArrayList<WeightedString> bigrams,
+ final boolean isNotAWord, final boolean isBlacklistEntry) {
mWord = word;
mFrequency = frequency;
mShortcutTargets = shortcutTargets;
mBigrams = bigrams;
+ mIsNotAWord = isNotAWord;
+ mIsBlacklistEntry = isBlacklistEntry;
}
private static int computeHashCode(Word word) {
@@ -48,7 +53,9 @@ public class Word implements Comparable<Word> {
word.mWord,
word.mFrequency,
word.mShortcutTargets.hashCode(),
- word.mBigrams.hashCode()
+ word.mBigrams.hashCode(),
+ word.mIsNotAWord,
+ word.mIsBlacklistEntry
});
}
@@ -78,7 +85,9 @@ public class Word implements Comparable<Word> {
Word w = (Word)o;
return mFrequency == w.mFrequency && mWord.equals(w.mWord)
&& mShortcutTargets.equals(w.mShortcutTargets)
- && mBigrams.equals(w.mBigrams);
+ && mBigrams.equals(w.mBigrams)
+ && mIsNotAWord == w.mIsNotAWord
+ && mIsBlacklistEntry == w.mIsBlacklistEntry;
}
@Override
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
index ba974ff7f..5a11ae534 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
@@ -16,37 +16,26 @@
package com.android.inputmethod.latin.spellcheck;
-import android.content.ContentResolver;
import android.content.Intent;
import android.content.SharedPreferences;
-import android.database.ContentObserver;
import android.preference.PreferenceManager;
-import android.provider.UserDictionary.Words;
import android.service.textservice.SpellCheckerService;
-import android.text.TextUtils;
import android.util.Log;
-import android.util.LruCache;
-import android.view.textservice.SentenceSuggestionsInfo;
import android.view.textservice.SuggestionsInfo;
-import android.view.textservice.TextInfo;
-import com.android.inputmethod.compat.SuggestionsInfoCompatUtils;
import com.android.inputmethod.keyboard.ProximityInfo;
import com.android.inputmethod.latin.BinaryDictionary;
+import com.android.inputmethod.latin.CollectionUtils;
+import com.android.inputmethod.latin.ContactsBinaryDictionary;
import com.android.inputmethod.latin.Dictionary;
-import com.android.inputmethod.latin.Dictionary.WordCallback;
import com.android.inputmethod.latin.DictionaryCollection;
import com.android.inputmethod.latin.DictionaryFactory;
-import com.android.inputmethod.latin.LatinIME;
import com.android.inputmethod.latin.LocaleUtils;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.StringUtils;
import com.android.inputmethod.latin.SynchronouslyLoadedContactsBinaryDictionary;
-import com.android.inputmethod.latin.SynchronouslyLoadedContactsDictionary;
import com.android.inputmethod.latin.SynchronouslyLoadedUserBinaryDictionary;
-import com.android.inputmethod.latin.SynchronouslyLoadedUserDictionary;
-import com.android.inputmethod.latin.WhitelistDictionary;
-import com.android.inputmethod.latin.WordComposer;
+import com.android.inputmethod.latin.UserBinaryDictionary;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@@ -61,7 +50,7 @@ import java.util.TreeMap;
/**
* Service for spell checking, using LatinIME's dictionaries and mechanisms.
*/
-public class AndroidSpellCheckerService extends SpellCheckerService
+public final class AndroidSpellCheckerService extends SpellCheckerService
implements SharedPreferences.OnSharedPreferenceChangeListener {
private static final String TAG = AndroidSpellCheckerService.class.getSimpleName();
private static final boolean DBG = false;
@@ -69,18 +58,15 @@ public class AndroidSpellCheckerService extends SpellCheckerService
public static final String PREF_USE_CONTACTS_KEY = "pref_spellcheck_use_contacts";
- private static final int CAPITALIZE_NONE = 0; // No caps, or mixed case
- private static final int CAPITALIZE_FIRST = 1; // First only
- private static final int CAPITALIZE_ALL = 2; // All caps
+ public static final int CAPITALIZE_NONE = 0; // No caps, or mixed case
+ public static final int CAPITALIZE_FIRST = 1; // First only
+ public static final int CAPITALIZE_ALL = 2; // All caps
private final static String[] EMPTY_STRING_ARRAY = new String[0];
- private Map<String, DictionaryPool> mDictionaryPools =
- Collections.synchronizedMap(new TreeMap<String, DictionaryPool>());
- private Map<String, Dictionary> mUserDictionaries =
- Collections.synchronizedMap(new TreeMap<String, Dictionary>());
- private Map<String, Dictionary> mWhitelistDictionaries =
- Collections.synchronizedMap(new TreeMap<String, Dictionary>());
- private Dictionary mContactsDictionary;
+ private Map<String, DictionaryPool> mDictionaryPools = CollectionUtils.newSynchronizedTreeMap();
+ private Map<String, UserBinaryDictionary> mUserDictionaries =
+ CollectionUtils.newSynchronizedTreeMap();
+ private ContactsBinaryDictionary mContactsDictionary;
// The threshold for a candidate to be offered as a suggestion.
private float mSuggestionThreshold;
@@ -91,12 +77,12 @@ public class AndroidSpellCheckerService extends SpellCheckerService
private final Object mUseContactsLock = new Object();
private final HashSet<WeakReference<DictionaryCollection>> mDictionaryCollectionsList =
- new HashSet<WeakReference<DictionaryCollection>>();
+ CollectionUtils.newHashSet();
public static final int SCRIPT_LATIN = 0;
public static final int SCRIPT_CYRILLIC = 1;
- private static final String SINGLE_QUOTE = "\u0027";
- private static final String APOSTROPHE = "\u2019";
+ public static final String SINGLE_QUOTE = "\u0027";
+ public static final String APOSTROPHE = "\u2019";
private static final TreeMap<String, Integer> mLanguageToScript;
static {
// List of the supported languages and their associated script. We won't check
@@ -107,7 +93,7 @@ public class AndroidSpellCheckerService extends SpellCheckerService
// proximity to pass to the dictionary descent algorithm.
// IMPORTANT: this only contains languages - do not write countries in there.
// Only the language is searched from the map.
- mLanguageToScript = new TreeMap<String, Integer>();
+ mLanguageToScript = CollectionUtils.newTreeMap();
mLanguageToScript.put("en", SCRIPT_LATIN);
mLanguageToScript.put("fr", SCRIPT_LATIN);
mLanguageToScript.put("de", SCRIPT_LATIN);
@@ -133,7 +119,7 @@ public class AndroidSpellCheckerService extends SpellCheckerService
onSharedPreferenceChanged(prefs, PREF_USE_CONTACTS_KEY);
}
- private static int getScriptFromLocale(final Locale locale) {
+ public static int getScriptFromLocale(final Locale locale) {
final Integer script = mLanguageToScript.get(locale.getLanguage());
if (null == script) {
throw new RuntimeException("We have been called with an unsupported language: \""
@@ -157,13 +143,9 @@ public class AndroidSpellCheckerService extends SpellCheckerService
private void startUsingContactsDictionaryLocked() {
if (null == mContactsDictionary) {
- if (LatinIME.USE_BINARY_CONTACTS_DICTIONARY) {
- // TODO: use the right locale for each session
- mContactsDictionary =
- new SynchronouslyLoadedContactsBinaryDictionary(this, Locale.getDefault());
- } else {
- mContactsDictionary = new SynchronouslyLoadedContactsDictionary(this);
- }
+ // TODO: use the right locale for each session
+ mContactsDictionary =
+ new SynchronouslyLoadedContactsBinaryDictionary(this, Locale.getDefault());
}
final Iterator<WeakReference<DictionaryCollection>> iterator =
mDictionaryCollectionsList.iterator();
@@ -199,20 +181,28 @@ public class AndroidSpellCheckerService extends SpellCheckerService
@Override
public Session createSession() {
- return new AndroidSpellCheckerSession(this);
+ // Should not refer to AndroidSpellCheckerSession directly considering
+ // that AndroidSpellCheckerSession may be overlaid.
+ return AndroidSpellCheckerSessionFactory.newInstance(this);
}
- private static SuggestionsInfo getNotInDictEmptySuggestions() {
+ public static SuggestionsInfo getNotInDictEmptySuggestions() {
return new SuggestionsInfo(0, EMPTY_STRING_ARRAY);
}
- private static SuggestionsInfo getInDictEmptySuggestions() {
+ public static SuggestionsInfo getInDictEmptySuggestions() {
return new SuggestionsInfo(SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY,
EMPTY_STRING_ARRAY);
}
- private static class SuggestionsGatherer implements WordCallback {
- public static class Result {
+ public SuggestionsGatherer newSuggestionsGatherer(final String text, int maxLength) {
+ return new SuggestionsGatherer(
+ text, mSuggestionThreshold, mRecommendedThreshold, maxLength);
+ }
+
+ // TODO: remove this class and replace it by storage local to the session.
+ public static final class SuggestionsGatherer {
+ public static final class Result {
public final String[] mSuggestions;
public final boolean mHasRecommendedSuggestions;
public Result(final String[] gatheredSuggestions,
@@ -241,13 +231,12 @@ public class AndroidSpellCheckerService extends SpellCheckerService
mSuggestionThreshold = suggestionThreshold;
mRecommendedThreshold = recommendedThreshold;
mMaxLength = maxLength;
- mSuggestions = new ArrayList<CharSequence>(maxLength + 1);
+ mSuggestions = CollectionUtils.newArrayList(maxLength + 1);
mScores = new int[mMaxLength];
}
- @Override
- synchronized public boolean addWord(char[] word, int wordOffset, int wordLength, int score,
- int dicTypeId, int dataType) {
+ synchronized public boolean addWord(char[] word, int[] spaceIndices, int wordOffset,
+ int wordLength, int score) {
final int positionIndex = Arrays.binarySearch(mScores, 0, mLength, score);
// binarySearch returns the index if the element exists, and -<insertion index> - 1
// if it doesn't. See documentation for binarySearch.
@@ -370,11 +359,9 @@ public class AndroidSpellCheckerService extends SpellCheckerService
private void closeAllDictionaries() {
final Map<String, DictionaryPool> oldPools = mDictionaryPools;
- mDictionaryPools = Collections.synchronizedMap(new TreeMap<String, DictionaryPool>());
- final Map<String, Dictionary> oldUserDictionaries = mUserDictionaries;
- mUserDictionaries = Collections.synchronizedMap(new TreeMap<String, Dictionary>());
- final Map<String, Dictionary> oldWhitelistDictionaries = mWhitelistDictionaries;
- mWhitelistDictionaries = Collections.synchronizedMap(new TreeMap<String, Dictionary>());
+ mDictionaryPools = CollectionUtils.newSynchronizedTreeMap();
+ final Map<String, UserBinaryDictionary> oldUserDictionaries = mUserDictionaries;
+ mUserDictionaries = CollectionUtils.newSynchronizedTreeMap();
new Thread("spellchecker_close_dicts") {
@Override
public void run() {
@@ -384,15 +371,12 @@ public class AndroidSpellCheckerService extends SpellCheckerService
for (Dictionary dict : oldUserDictionaries.values()) {
dict.close();
}
- for (Dictionary dict : oldWhitelistDictionaries.values()) {
- dict.close();
- }
synchronized (mUseContactsLock) {
if (null != mContactsDictionary) {
// The synchronously loaded contacts dictionary should have been in one
// or several pools, but it is shielded against multiple closing and it's
// safe to call it several times.
- final Dictionary dictToClose = mContactsDictionary;
+ final ContactsBinaryDictionary dictToClose = mContactsDictionary;
// TODO: revert to the concrete type when USE_BINARY_CONTACTS_DICTIONARY
// is no longer needed
mContactsDictionary = null;
@@ -403,7 +387,7 @@ public class AndroidSpellCheckerService extends SpellCheckerService
}.start();
}
- private DictionaryPool getDictionaryPool(final String locale) {
+ public DictionaryPool getDictionaryPool(final String locale) {
DictionaryPool pool = mDictionaryPools.get(locale);
if (null == pool) {
final Locale localeObject = LocaleUtils.constructLocaleFromString(locale);
@@ -424,36 +408,20 @@ public class AndroidSpellCheckerService extends SpellCheckerService
DictionaryFactory.createMainDictionaryFromManager(this, locale,
true /* useFullEditDistance */);
final String localeStr = locale.toString();
- Dictionary userDictionary = mUserDictionaries.get(localeStr);
+ UserBinaryDictionary userDictionary = mUserDictionaries.get(localeStr);
if (null == userDictionary) {
- if (LatinIME.USE_BINARY_USER_DICTIONARY) {
- userDictionary = new SynchronouslyLoadedUserBinaryDictionary(this, localeStr, true);
- } else {
- userDictionary = new SynchronouslyLoadedUserDictionary(this, localeStr, true);
- }
+ userDictionary = new SynchronouslyLoadedUserBinaryDictionary(this, localeStr, true);
mUserDictionaries.put(localeStr, userDictionary);
}
dictionaryCollection.addDictionary(userDictionary);
- Dictionary whitelistDictionary = mWhitelistDictionaries.get(localeStr);
- if (null == whitelistDictionary) {
- whitelistDictionary = new WhitelistDictionary(this, locale);
- mWhitelistDictionaries.put(localeStr, whitelistDictionary);
- }
- dictionaryCollection.addDictionary(whitelistDictionary);
synchronized (mUseContactsLock) {
if (mUseContactsDictionary) {
if (null == mContactsDictionary) {
- // TODO: revert to the concrete type when USE_BINARY_CONTACTS_DICTIONARY is no
- // longer needed
- if (LatinIME.USE_BINARY_CONTACTS_DICTIONARY) {
- // TODO: use the right locale. We can't do it right now because the
- // spell checker is reusing the contacts dictionary across sessions
- // without regard for their locale, so we need to fix that first.
- mContactsDictionary = new SynchronouslyLoadedContactsBinaryDictionary(this,
- Locale.getDefault());
- } else {
- mContactsDictionary = new SynchronouslyLoadedContactsDictionary(this);
- }
+ // TODO: use the right locale. We can't do it right now because the
+ // spell checker is reusing the contacts dictionary across sessions
+ // without regard for their locale, so we need to fix that first.
+ mContactsDictionary = new SynchronouslyLoadedContactsBinaryDictionary(this,
+ Locale.getDefault());
}
}
dictionaryCollection.addDictionary(mContactsDictionary);
@@ -464,7 +432,7 @@ public class AndroidSpellCheckerService extends SpellCheckerService
}
// This method assumes the text is not empty or null.
- private static int getCapitalizationType(String text) {
+ public static int getCapitalizationType(String text) {
// If the first char is not uppercase, then the word is either all lower case,
// and in either case we return CAPITALIZE_NONE.
if (!Character.isUpperCase(text.codePointAt(0))) return CAPITALIZE_NONE;
@@ -481,378 +449,4 @@ public class AndroidSpellCheckerService extends SpellCheckerService
if (1 == capsCount) return CAPITALIZE_FIRST;
return (len == capsCount ? CAPITALIZE_ALL : CAPITALIZE_NONE);
}
-
- private static class AndroidSpellCheckerSession extends Session {
- // Immutable, but need the locale which is not available in the constructor yet
- private DictionaryPool mDictionaryPool;
- // Likewise
- private Locale mLocale;
- // Cache this for performance
- private int mScript; // One of SCRIPT_LATIN or SCRIPT_CYRILLIC for now.
-
- private final AndroidSpellCheckerService mService;
-
- private final SuggestionsCache mSuggestionsCache = new SuggestionsCache();
- private final ContentObserver mObserver;
-
- private static class SuggestionsParams {
- public final String[] mSuggestions;
- public final int mFlags;
- public SuggestionsParams(String[] suggestions, int flags) {
- mSuggestions = suggestions;
- mFlags = flags;
- }
- }
-
- private static class SuggestionsCache {
- private static final int MAX_CACHE_SIZE = 50;
- // TODO: support bigram
- private final LruCache<String, SuggestionsParams> mUnigramSuggestionsInfoCache =
- new LruCache<String, SuggestionsParams>(MAX_CACHE_SIZE);
-
- public SuggestionsParams getSuggestionsFromCache(String query) {
- return mUnigramSuggestionsInfoCache.get(query);
- }
-
- public void putSuggestionsToCache(String query, String[] suggestions, int flags) {
- if (suggestions == null || TextUtils.isEmpty(query)) {
- return;
- }
- mUnigramSuggestionsInfoCache.put(query, new SuggestionsParams(suggestions, flags));
- }
-
- public void clearCache() {
- mUnigramSuggestionsInfoCache.evictAll();
- }
- }
-
- AndroidSpellCheckerSession(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
- public void onCreate() {
- final String localeString = getLocale();
- mDictionaryPool = mService.getDictionaryPool(localeString);
- mLocale = LocaleUtils.constructLocaleFromString(localeString);
- mScript = 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.
- * The dictionaries supported by Latin IME are described in res/xml/spellchecker.xml
- * and is limited to EFIGS languages and Russian.
- * Hence at the moment this explicitly tests for Cyrillic characters or Latin characters
- * as appropriate, and explicitly excludes CJK, Arabic and Hebrew characters.
- */
- private static boolean isLetterCheckableByLanguage(final int codePoint,
- final int script) {
- switch (script) {
- case SCRIPT_LATIN:
- // Our supported latin script dictionaries (EFIGS) at the moment only include
- // characters in the C0, C1, Latin Extended A and B, IPA extensions unicode
- // blocks. As it happens, those are back-to-back in the code range 0x40 to 0x2AF,
- // so the below is a very efficient way to test for it. As for the 0-0x3F, it's
- // excluded from isLetter anyway.
- return codePoint <= 0x2AF && Character.isLetter(codePoint);
- case SCRIPT_CYRILLIC:
- // All Cyrillic characters are in the 400~52F block. There are some in the upper
- // Unicode range, but they are archaic characters that are not used in modern
- // russian and are not used by our dictionary.
- return codePoint >= 0x400 && codePoint <= 0x52F && Character.isLetter(codePoint);
- default:
- // Should never come here
- throw new RuntimeException("Impossible value of script: " + script);
- }
- }
-
- /**
- * Finds out whether a particular string should be filtered out of spell checking.
- *
- * This will loosely match URLs, numbers, symbols. To avoid always underlining words that
- * we know we will never recognize, this accepts a script identifier that should be one
- * of the SCRIPT_* constants defined above, to rule out quickly characters from very
- * different languages.
- *
- * @param text the string to evaluate.
- * @param script the identifier for the script this spell checker recognizes
- * @return true if we should filter this text out, false otherwise
- */
- private static boolean shouldFilterOut(final String text, final int script) {
- if (TextUtils.isEmpty(text) || text.length() <= 1) return true;
-
- // TODO: check if an equivalent processing can't be done more quickly with a
- // compiled regexp.
- // Filter by first letter
- final int firstCodePoint = text.codePointAt(0);
- // Filter out words that don't start with a letter or an apostrophe
- if (!isLetterCheckableByLanguage(firstCodePoint, script)
- && '\'' != firstCodePoint) return true;
-
- // Filter contents
- final int length = text.length();
- int letterCount = 0;
- for (int i = 0; i < length; i = text.offsetByCodePoints(i, 1)) {
- final int codePoint = text.codePointAt(i);
- // Any word containing a '@' is probably an e-mail address
- // Any word containing a '/' is probably either an ad-hoc combination of two
- // words or a URI - in either case we don't want to spell check that
- if ('@' == codePoint || '/' == codePoint) return true;
- if (isLetterCheckableByLanguage(codePoint, script)) ++letterCount;
- }
- // Guestimate heuristic: perform spell checking if at least 3/4 of the characters
- // in this word are letters
- return (letterCount * 4 < length * 3);
- }
-
- private SentenceSuggestionsInfo fixWronglyInvalidatedWordWithSingleQuote(
- TextInfo ti, SentenceSuggestionsInfo ssi) {
- final String typedText = ti.getText();
- if (!typedText.contains(SINGLE_QUOTE)) {
- return null;
- }
- final int N = ssi.getSuggestionsCount();
- final ArrayList<Integer> additionalOffsets = new ArrayList<Integer>();
- final ArrayList<Integer> additionalLengths = new ArrayList<Integer>();
- final ArrayList<SuggestionsInfo> additionalSuggestionsInfos =
- new ArrayList<SuggestionsInfo>();
- for (int i = 0; i < N; ++i) {
- final SuggestionsInfo si = ssi.getSuggestionsInfoAt(i);
- final int flags = si.getSuggestionsAttributes();
- if ((flags & SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY) == 0) {
- continue;
- }
- final int offset = ssi.getOffsetAt(i);
- final int length = ssi.getLengthAt(i);
- final String subText = typedText.substring(offset, offset + length);
- if (!subText.contains(SINGLE_QUOTE)) {
- continue;
- }
- final String[] splitTexts = subText.split(SINGLE_QUOTE, -1);
- if (splitTexts == null || splitTexts.length <= 1) {
- continue;
- }
- final int splitNum = splitTexts.length;
- for (int j = 0; j < splitNum; ++j) {
- final String splitText = splitTexts[j];
- if (TextUtils.isEmpty(splitText)) {
- continue;
- }
- if (mSuggestionsCache.getSuggestionsFromCache(splitText) == null) {
- continue;
- }
- final int newLength = splitText.length();
- // Neither RESULT_ATTR_IN_THE_DICTIONARY nor RESULT_ATTR_LOOKS_LIKE_TYPO
- final int newFlags = 0;
- final SuggestionsInfo newSi = new SuggestionsInfo(newFlags, EMPTY_STRING_ARRAY);
- newSi.setCookieAndSequence(si.getCookie(), si.getSequence());
- if (DBG) {
- Log.d(TAG, "Override and remove old span over: "
- + splitText + ", " + offset + "," + newLength);
- }
- additionalOffsets.add(offset);
- additionalLengths.add(newLength);
- additionalSuggestionsInfos.add(newSi);
- }
- }
- final int additionalSize = additionalOffsets.size();
- if (additionalSize <= 0) {
- return null;
- }
- final int suggestionsSize = N + additionalSize;
- final int[] newOffsets = new int[suggestionsSize];
- final int[] newLengths = new int[suggestionsSize];
- final SuggestionsInfo[] newSuggestionsInfos = new SuggestionsInfo[suggestionsSize];
- int i;
- for (i = 0; i < N; ++i) {
- newOffsets[i] = ssi.getOffsetAt(i);
- newLengths[i] = ssi.getLengthAt(i);
- newSuggestionsInfos[i] = ssi.getSuggestionsInfoAt(i);
- }
- for (; i < suggestionsSize; ++i) {
- newOffsets[i] = additionalOffsets.get(i - N);
- newLengths[i] = additionalLengths.get(i - N);
- newSuggestionsInfos[i] = additionalSuggestionsInfos.get(i - N);
- }
- return new SentenceSuggestionsInfo(newSuggestionsInfos, newOffsets, newLengths);
- }
-
- @Override
- public SentenceSuggestionsInfo[] onGetSentenceSuggestionsMultiple(
- TextInfo[] textInfos, int suggestionsLimit) {
- final SentenceSuggestionsInfo[] retval = super.onGetSentenceSuggestionsMultiple(
- textInfos, suggestionsLimit);
- if (retval == null || retval.length != textInfos.length) {
- return retval;
- }
- for (int i = 0; i < retval.length; ++i) {
- final SentenceSuggestionsInfo tempSsi =
- fixWronglyInvalidatedWordWithSingleQuote(textInfos[i], retval[i]);
- if (tempSsi != null) {
- retval[i] = tempSsi;
- }
- }
- return retval;
- }
-
- @Override
- public SuggestionsInfo[] onGetSuggestionsMultiple(TextInfo[] textInfos,
- int suggestionsLimit, boolean sequentialWords) {
- final int length = textInfos.length;
- final SuggestionsInfo[] retval = new SuggestionsInfo[length];
- for (int i = 0; i < length; ++i) {
- final String prevWord;
- if (sequentialWords && i > 0) {
- final String prevWordCandidate = textInfos[i - 1].getText();
- // Note that an empty string would be used to indicate the initial word
- // in the future.
- prevWord = TextUtils.isEmpty(prevWordCandidate) ? null : prevWordCandidate;
- } else {
- prevWord = null;
- }
- retval[i] = onGetSuggestions(textInfos[i], prevWord, suggestionsLimit);
- retval[i].setCookieAndSequence(
- textInfos[i].getCookie(), textInfos[i].getSequence());
- }
- return retval;
- }
-
- // Note : this must be reentrant
- /**
- * Gets a list of suggestions for a specific string. This returns a list of possible
- * corrections for the text passed as an argument. It may split or group words, and
- * even perform grammatical analysis.
- */
- @Override
- public SuggestionsInfo onGetSuggestions(final TextInfo textInfo,
- final int suggestionsLimit) {
- return onGetSuggestions(textInfo, null, suggestionsLimit);
- }
-
- private SuggestionsInfo onGetSuggestions(
- final TextInfo textInfo, final String prevWord, final int suggestionsLimit) {
- try {
- final String inText = textInfo.getText();
- final SuggestionsParams cachedSuggestionsParams =
- mSuggestionsCache.getSuggestionsFromCache(inText);
- if (cachedSuggestionsParams != null) {
- if (DBG) {
- Log.d(TAG, "Cache hit: " + inText + ", " + cachedSuggestionsParams.mFlags);
- }
- return new SuggestionsInfo(
- cachedSuggestionsParams.mFlags, cachedSuggestionsParams.mSuggestions);
- }
-
- if (shouldFilterOut(inText, mScript)) {
- DictAndProximity dictInfo = null;
- try {
- dictInfo = mDictionaryPool.takeOrGetNull();
- if (null == dictInfo) return getNotInDictEmptySuggestions();
- return dictInfo.mDictionary.isValidWord(inText) ?
- getInDictEmptySuggestions() : getNotInDictEmptySuggestions();
- } finally {
- if (null != dictInfo) {
- if (!mDictionaryPool.offer(dictInfo)) {
- Log.e(TAG, "Can't re-insert a dictionary into its pool");
- }
- }
- }
- }
- final String text = inText.replaceAll(APOSTROPHE, SINGLE_QUOTE);
-
- // TODO: Don't gather suggestions if the limit is <= 0 unless necessary
- final SuggestionsGatherer suggestionsGatherer = new SuggestionsGatherer(text,
- mService.mSuggestionThreshold, mService.mRecommendedThreshold,
- suggestionsLimit);
- final WordComposer composer = new WordComposer();
- final int length = text.length();
- for (int i = 0; i < length; i = text.offsetByCodePoints(i, 1)) {
- final int codePoint = text.codePointAt(i);
- // The getXYForCodePointAndScript method returns (Y << 16) + X
- final int xy = SpellCheckerProximityInfo.getXYForCodePointAndScript(
- codePoint, mScript);
- if (SpellCheckerProximityInfo.NOT_A_COORDINATE_PAIR == xy) {
- composer.add(codePoint, WordComposer.NOT_A_COORDINATE,
- WordComposer.NOT_A_COORDINATE, null);
- } else {
- composer.add(codePoint, xy & 0xFFFF, xy >> 16, null);
- }
- }
-
- final int capitalizeType = getCapitalizationType(text);
- boolean isInDict = true;
- DictAndProximity dictInfo = null;
- try {
- dictInfo = mDictionaryPool.takeOrGetNull();
- if (null == dictInfo) return getNotInDictEmptySuggestions();
- dictInfo.mDictionary.getWords(composer, prevWord, suggestionsGatherer,
- dictInfo.mProximityInfo);
- isInDict = dictInfo.mDictionary.isValidWord(text);
- if (!isInDict && CAPITALIZE_NONE != capitalizeType) {
- // We want to test the word again if it's all caps or first caps only.
- // If it's fully down, we already tested it, if it's mixed case, we don't
- // want to test a lowercase version of it.
- isInDict = dictInfo.mDictionary.isValidWord(text.toLowerCase(mLocale));
- }
- } finally {
- if (null != dictInfo) {
- if (!mDictionaryPool.offer(dictInfo)) {
- Log.e(TAG, "Can't re-insert a dictionary into its pool");
- }
- }
- }
-
- final SuggestionsGatherer.Result result = suggestionsGatherer.getResults(
- capitalizeType, mLocale);
-
- if (DBG) {
- Log.i(TAG, "Spell checking results for " + text + " with suggestion limit "
- + suggestionsLimit);
- Log.i(TAG, "IsInDict = " + isInDict);
- Log.i(TAG, "LooksLikeTypo = " + (!isInDict));
- Log.i(TAG, "HasRecommendedSuggestions = " + result.mHasRecommendedSuggestions);
- if (null != result.mSuggestions) {
- for (String suggestion : result.mSuggestions) {
- Log.i(TAG, suggestion);
- }
- }
- }
-
- final int flags =
- (isInDict ? SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY
- : SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO)
- | (result.mHasRecommendedSuggestions
- ? SuggestionsInfoCompatUtils
- .getValueOf_RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS()
- : 0);
- final SuggestionsInfo retval = new SuggestionsInfo(flags, result.mSuggestions);
- mSuggestionsCache.putSuggestionsToCache(text, result.mSuggestions, flags);
- return retval;
- } catch (RuntimeException e) {
- // Don't kill the keyboard if there is a bug in the spell checker
- if (DBG) {
- throw e;
- } else {
- Log.e(TAG, "Exception while spellcheking: " + e);
- return getNotInDictEmptySuggestions();
- }
- }
- }
- }
}
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java
new file mode 100644
index 000000000..668e7a641
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java
@@ -0,0 +1,154 @@
+/*
+ * 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.spellcheck;
+
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.textservice.SentenceSuggestionsInfo;
+import android.view.textservice.SuggestionsInfo;
+import android.view.textservice.TextInfo;
+
+import com.android.inputmethod.latin.CollectionUtils;
+
+import java.util.ArrayList;
+
+public final class AndroidSpellCheckerSession extends AndroidWordLevelSpellCheckerSession {
+ private static final String TAG = AndroidSpellCheckerSession.class.getSimpleName();
+ private static final boolean DBG = false;
+ private final static String[] EMPTY_STRING_ARRAY = new String[0];
+
+ public AndroidSpellCheckerSession(AndroidSpellCheckerService service) {
+ super(service);
+ }
+
+ private SentenceSuggestionsInfo fixWronglyInvalidatedWordWithSingleQuote(TextInfo ti,
+ SentenceSuggestionsInfo ssi) {
+ final String typedText = ti.getText();
+ if (!typedText.contains(AndroidSpellCheckerService.SINGLE_QUOTE)) {
+ return null;
+ }
+ final int N = ssi.getSuggestionsCount();
+ final ArrayList<Integer> additionalOffsets = CollectionUtils.newArrayList();
+ final ArrayList<Integer> additionalLengths = CollectionUtils.newArrayList();
+ final ArrayList<SuggestionsInfo> additionalSuggestionsInfos =
+ CollectionUtils.newArrayList();
+ String currentWord = null;
+ for (int i = 0; i < N; ++i) {
+ final SuggestionsInfo si = ssi.getSuggestionsInfoAt(i);
+ final int flags = si.getSuggestionsAttributes();
+ if ((flags & SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY) == 0) {
+ continue;
+ }
+ final int offset = ssi.getOffsetAt(i);
+ final int length = ssi.getLengthAt(i);
+ final String subText = typedText.substring(offset, offset + length);
+ final String prevWord = currentWord;
+ currentWord = subText;
+ if (!subText.contains(AndroidSpellCheckerService.SINGLE_QUOTE)) {
+ continue;
+ }
+ final String[] splitTexts =
+ subText.split(AndroidSpellCheckerService.SINGLE_QUOTE, -1);
+ if (splitTexts == null || splitTexts.length <= 1) {
+ continue;
+ }
+ final int splitNum = splitTexts.length;
+ for (int j = 0; j < splitNum; ++j) {
+ final String splitText = splitTexts[j];
+ if (TextUtils.isEmpty(splitText)) {
+ continue;
+ }
+ if (mSuggestionsCache.getSuggestionsFromCache(splitText, prevWord) == null) {
+ continue;
+ }
+ final int newLength = splitText.length();
+ // Neither RESULT_ATTR_IN_THE_DICTIONARY nor RESULT_ATTR_LOOKS_LIKE_TYPO
+ final int newFlags = 0;
+ final SuggestionsInfo newSi =
+ new SuggestionsInfo(newFlags, EMPTY_STRING_ARRAY);
+ newSi.setCookieAndSequence(si.getCookie(), si.getSequence());
+ if (DBG) {
+ Log.d(TAG, "Override and remove old span over: " + splitText + ", "
+ + offset + "," + newLength);
+ }
+ additionalOffsets.add(offset);
+ additionalLengths.add(newLength);
+ additionalSuggestionsInfos.add(newSi);
+ }
+ }
+ final int additionalSize = additionalOffsets.size();
+ if (additionalSize <= 0) {
+ return null;
+ }
+ final int suggestionsSize = N + additionalSize;
+ final int[] newOffsets = new int[suggestionsSize];
+ final int[] newLengths = new int[suggestionsSize];
+ final SuggestionsInfo[] newSuggestionsInfos = new SuggestionsInfo[suggestionsSize];
+ int i;
+ for (i = 0; i < N; ++i) {
+ newOffsets[i] = ssi.getOffsetAt(i);
+ newLengths[i] = ssi.getLengthAt(i);
+ newSuggestionsInfos[i] = ssi.getSuggestionsInfoAt(i);
+ }
+ for (; i < suggestionsSize; ++i) {
+ newOffsets[i] = additionalOffsets.get(i - N);
+ newLengths[i] = additionalLengths.get(i - N);
+ newSuggestionsInfos[i] = additionalSuggestionsInfos.get(i - N);
+ }
+ return new SentenceSuggestionsInfo(newSuggestionsInfos, newOffsets, newLengths);
+ }
+
+ @Override
+ public SentenceSuggestionsInfo[] onGetSentenceSuggestionsMultiple(TextInfo[] textInfos,
+ int suggestionsLimit) {
+ final SentenceSuggestionsInfo[] retval =
+ super.onGetSentenceSuggestionsMultiple(textInfos, suggestionsLimit);
+ if (retval == null || retval.length != textInfos.length) {
+ return retval;
+ }
+ for (int i = 0; i < retval.length; ++i) {
+ final SentenceSuggestionsInfo tempSsi =
+ fixWronglyInvalidatedWordWithSingleQuote(textInfos[i], retval[i]);
+ if (tempSsi != null) {
+ retval[i] = tempSsi;
+ }
+ }
+ return retval;
+ }
+
+ @Override
+ public SuggestionsInfo[] onGetSuggestionsMultiple(TextInfo[] textInfos,
+ int suggestionsLimit, boolean sequentialWords) {
+ final int length = textInfos.length;
+ final SuggestionsInfo[] retval = new SuggestionsInfo[length];
+ for (int i = 0; i < length; ++i) {
+ final String prevWord;
+ if (sequentialWords && i > 0) {
+ final String prevWordCandidate = textInfos[i - 1].getText();
+ // Note that an empty string would be used to indicate the initial word
+ // in the future.
+ prevWord = TextUtils.isEmpty(prevWordCandidate) ? null : prevWordCandidate;
+ } else {
+ prevWord = null;
+ }
+ retval[i] = onGetSuggestions(textInfos[i], prevWord, suggestionsLimit);
+ retval[i].setCookieAndSequence(textInfos[i].getCookie(),
+ textInfos[i].getSequence());
+ }
+ return retval;
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSessionFactory.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSessionFactory.java
new file mode 100644
index 000000000..8eb1eb68e
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSessionFactory.java
@@ -0,0 +1,25 @@
+/*
+ * 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.spellcheck;
+
+import android.service.textservice.SpellCheckerService.Session;
+
+public abstract class AndroidSpellCheckerSessionFactory {
+ public static Session newInstance(AndroidSpellCheckerService service) {
+ return new AndroidSpellCheckerSession(service);
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
new file mode 100644
index 000000000..53ed4d3c3
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
@@ -0,0 +1,326 @@
+/*
+ * 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.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;
+import android.util.LruCache;
+import android.view.textservice.SuggestionsInfo;
+import android.view.textservice.TextInfo;
+
+import com.android.inputmethod.compat.SuggestionsInfoCompatUtils;
+import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.latin.LocaleUtils;
+import com.android.inputmethod.latin.WordComposer;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+import com.android.inputmethod.latin.spellcheck.AndroidSpellCheckerService.SuggestionsGatherer;
+
+import java.util.ArrayList;
+import java.util.Locale;
+
+public abstract class AndroidWordLevelSpellCheckerSession extends Session {
+ private static final String TAG = AndroidWordLevelSpellCheckerSession.class.getSimpleName();
+ private static final boolean DBG = false;
+
+ // Immutable, but need the locale which is not available in the constructor yet
+ private DictionaryPool mDictionaryPool;
+ // Likewise
+ private Locale mLocale;
+ // Cache this for performance
+ 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 final class SuggestionsParams {
+ public final String[] mSuggestions;
+ public final int mFlags;
+ public SuggestionsParams(String[] suggestions, int flags) {
+ mSuggestions = suggestions;
+ mFlags = flags;
+ }
+ }
+
+ protected static final class SuggestionsCache {
+ private static final char CHAR_DELIMITER = '\uFFFC';
+ private static final int MAX_CACHE_SIZE = 50;
+ private final LruCache<String, SuggestionsParams> mUnigramSuggestionsInfoCache =
+ new LruCache<String, SuggestionsParams>(MAX_CACHE_SIZE);
+
+ // TODO: Support n-gram input
+ private static String generateKey(String query, String prevWord) {
+ if (TextUtils.isEmpty(query) || TextUtils.isEmpty(prevWord)) {
+ return query;
+ }
+ return query + CHAR_DELIMITER + prevWord;
+ }
+
+ // TODO: Support n-gram input
+ public SuggestionsParams getSuggestionsFromCache(String query, String prevWord) {
+ return mUnigramSuggestionsInfoCache.get(generateKey(query, prevWord));
+ }
+
+ // TODO: Support n-gram input
+ public void putSuggestionsToCache(
+ String query, String prevWord, String[] suggestions, int flags) {
+ if (suggestions == null || TextUtils.isEmpty(query)) {
+ return;
+ }
+ 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
+ public void onCreate() {
+ final String localeString = getLocale();
+ mDictionaryPool = mService.getDictionaryPool(localeString);
+ mLocale = LocaleUtils.constructLocaleFromString(localeString);
+ 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.
+ * The dictionaries supported by Latin IME are described in res/xml/spellchecker.xml
+ * and is limited to EFIGS languages and Russian.
+ * Hence at the moment this explicitly tests for Cyrillic characters or Latin characters
+ * as appropriate, and explicitly excludes CJK, Arabic and Hebrew characters.
+ */
+ private static boolean isLetterCheckableByLanguage(final int codePoint,
+ final int script) {
+ switch (script) {
+ case AndroidSpellCheckerService.SCRIPT_LATIN:
+ // Our supported latin script dictionaries (EFIGS) at the moment only include
+ // characters in the C0, C1, Latin Extended A and B, IPA extensions unicode
+ // blocks. As it happens, those are back-to-back in the code range 0x40 to 0x2AF,
+ // so the below is a very efficient way to test for it. As for the 0-0x3F, it's
+ // excluded from isLetter anyway.
+ return codePoint <= 0x2AF && Character.isLetter(codePoint);
+ case AndroidSpellCheckerService.SCRIPT_CYRILLIC:
+ // All Cyrillic characters are in the 400~52F block. There are some in the upper
+ // Unicode range, but they are archaic characters that are not used in modern
+ // russian and are not used by our dictionary.
+ return codePoint >= 0x400 && codePoint <= 0x52F && Character.isLetter(codePoint);
+ default:
+ // Should never come here
+ throw new RuntimeException("Impossible value of script: " + script);
+ }
+ }
+
+ /**
+ * Finds out whether a particular string should be filtered out of spell checking.
+ *
+ * This will loosely match URLs, numbers, symbols. To avoid always underlining words that
+ * we know we will never recognize, this accepts a script identifier that should be one
+ * of the SCRIPT_* constants defined above, to rule out quickly characters from very
+ * different languages.
+ *
+ * @param text the string to evaluate.
+ * @param script the identifier for the script this spell checker recognizes
+ * @return true if we should filter this text out, false otherwise
+ */
+ private static boolean shouldFilterOut(final String text, final int script) {
+ if (TextUtils.isEmpty(text) || text.length() <= 1) return true;
+
+ // TODO: check if an equivalent processing can't be done more quickly with a
+ // compiled regexp.
+ // Filter by first letter
+ final int firstCodePoint = text.codePointAt(0);
+ // Filter out words that don't start with a letter or an apostrophe
+ if (!isLetterCheckableByLanguage(firstCodePoint, script)
+ && '\'' != firstCodePoint) return true;
+
+ // Filter contents
+ final int length = text.length();
+ int letterCount = 0;
+ for (int i = 0; i < length; i = text.offsetByCodePoints(i, 1)) {
+ final int codePoint = text.codePointAt(i);
+ // Any word containing a '@' is probably an e-mail address
+ // Any word containing a '/' is probably either an ad-hoc combination of two
+ // words or a URI - in either case we don't want to spell check that
+ if ('@' == codePoint || '/' == codePoint) return true;
+ if (isLetterCheckableByLanguage(codePoint, script)) ++letterCount;
+ }
+ // Guestimate heuristic: perform spell checking if at least 3/4 of the characters
+ // in this word are letters
+ return (letterCount * 4 < length * 3);
+ }
+
+ // Note : this must be reentrant
+ /**
+ * Gets a list of suggestions for a specific string. This returns a list of possible
+ * corrections for the text passed as an argument. It may split or group words, and
+ * even perform grammatical analysis.
+ */
+ @Override
+ public SuggestionsInfo onGetSuggestions(final TextInfo textInfo,
+ final int suggestionsLimit) {
+ return onGetSuggestions(textInfo, null, suggestionsLimit);
+ }
+
+ protected SuggestionsInfo onGetSuggestions(
+ final TextInfo textInfo, final String prevWord, final int suggestionsLimit) {
+ try {
+ final String inText = textInfo.getText();
+ final SuggestionsParams cachedSuggestionsParams =
+ mSuggestionsCache.getSuggestionsFromCache(inText, prevWord);
+ if (cachedSuggestionsParams != null) {
+ if (DBG) {
+ Log.d(TAG, "Cache hit: " + inText + ", " + cachedSuggestionsParams.mFlags);
+ }
+ return new SuggestionsInfo(
+ cachedSuggestionsParams.mFlags, cachedSuggestionsParams.mSuggestions);
+ }
+
+ if (shouldFilterOut(inText, mScript)) {
+ DictAndProximity dictInfo = null;
+ try {
+ dictInfo = mDictionaryPool.pollWithDefaultTimeout();
+ if (!DictionaryPool.isAValidDictionary(dictInfo)) {
+ return AndroidSpellCheckerService.getNotInDictEmptySuggestions();
+ }
+ return dictInfo.mDictionary.isValidWord(inText)
+ ? AndroidSpellCheckerService.getInDictEmptySuggestions()
+ : AndroidSpellCheckerService.getNotInDictEmptySuggestions();
+ } finally {
+ if (null != dictInfo) {
+ if (!mDictionaryPool.offer(dictInfo)) {
+ Log.e(TAG, "Can't re-insert a dictionary into its pool");
+ }
+ }
+ }
+ }
+ final String text = inText.replaceAll(
+ AndroidSpellCheckerService.APOSTROPHE, AndroidSpellCheckerService.SINGLE_QUOTE);
+
+ // TODO: Don't gather suggestions if the limit is <= 0 unless necessary
+ //final SuggestionsGatherer suggestionsGatherer = new SuggestionsGatherer(text,
+ //mService.mSuggestionThreshold, mService.mRecommendedThreshold,
+ //suggestionsLimit);
+ final SuggestionsGatherer suggestionsGatherer = mService.newSuggestionsGatherer(
+ text, suggestionsLimit);
+ final WordComposer composer = new WordComposer();
+ final int length = text.length();
+ for (int i = 0; i < length; i = text.offsetByCodePoints(i, 1)) {
+ final int codePoint = text.codePointAt(i);
+ // The getXYForCodePointAndScript method returns (Y << 16) + X
+ final int xy = SpellCheckerProximityInfo.getXYForCodePointAndScript(
+ codePoint, mScript);
+ if (SpellCheckerProximityInfo.NOT_A_COORDINATE_PAIR == xy) {
+ composer.add(codePoint,
+ Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
+ } else {
+ composer.add(codePoint, xy & 0xFFFF, xy >> 16);
+ }
+ }
+
+ final int capitalizeType = AndroidSpellCheckerService.getCapitalizationType(text);
+ boolean isInDict = true;
+ DictAndProximity dictInfo = null;
+ try {
+ dictInfo = mDictionaryPool.pollWithDefaultTimeout();
+ if (!DictionaryPool.isAValidDictionary(dictInfo)) {
+ return AndroidSpellCheckerService.getNotInDictEmptySuggestions();
+ }
+ final ArrayList<SuggestedWordInfo> suggestions =
+ dictInfo.mDictionary.getSuggestions(composer, prevWord,
+ dictInfo.mProximityInfo);
+ for (final SuggestedWordInfo suggestion : suggestions) {
+ final String suggestionStr = suggestion.mWord.toString();
+ suggestionsGatherer.addWord(suggestionStr.toCharArray(), null, 0,
+ suggestionStr.length(), suggestion.mScore);
+ }
+ isInDict = dictInfo.mDictionary.isValidWord(text);
+ if (!isInDict && AndroidSpellCheckerService.CAPITALIZE_NONE != capitalizeType) {
+ // We want to test the word again if it's all caps or first caps only.
+ // If it's fully down, we already tested it, if it's mixed case, we don't
+ // want to test a lowercase version of it.
+ isInDict = dictInfo.mDictionary.isValidWord(text.toLowerCase(mLocale));
+ }
+ } finally {
+ if (null != dictInfo) {
+ if (!mDictionaryPool.offer(dictInfo)) {
+ Log.e(TAG, "Can't re-insert a dictionary into its pool");
+ }
+ }
+ }
+
+ final SuggestionsGatherer.Result result = suggestionsGatherer.getResults(
+ capitalizeType, mLocale);
+
+ if (DBG) {
+ Log.i(TAG, "Spell checking results for " + text + " with suggestion limit "
+ + suggestionsLimit);
+ Log.i(TAG, "IsInDict = " + isInDict);
+ Log.i(TAG, "LooksLikeTypo = " + (!isInDict));
+ Log.i(TAG, "HasRecommendedSuggestions = " + result.mHasRecommendedSuggestions);
+ if (null != result.mSuggestions) {
+ for (String suggestion : result.mSuggestions) {
+ Log.i(TAG, suggestion);
+ }
+ }
+ }
+
+ final int flags =
+ (isInDict ? SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY
+ : SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO)
+ | (result.mHasRecommendedSuggestions
+ ? SuggestionsInfoCompatUtils
+ .getValueOf_RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS()
+ : 0);
+ final SuggestionsInfo retval = new SuggestionsInfo(flags, result.mSuggestions);
+ mSuggestionsCache.putSuggestionsToCache(text, prevWord, result.mSuggestions, flags);
+ return retval;
+ } catch (RuntimeException e) {
+ // Don't kill the keyboard if there is a bug in the spell checker
+ if (DBG) {
+ throw e;
+ } else {
+ Log.e(TAG, "Exception while spellcheking: " + e);
+ return AndroidSpellCheckerService.getNotInDictEmptySuggestions();
+ }
+ }
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/DictAndProximity.java b/java/src/com/android/inputmethod/latin/spellcheck/DictAndProximity.java
index 3dbbd40cd..9d7c61a33 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/DictAndProximity.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/DictAndProximity.java
@@ -22,7 +22,7 @@ import com.android.inputmethod.keyboard.ProximityInfo;
/**
* A simple container for both a Dictionary and a ProximityInfo.
*/
-public class DictAndProximity {
+public final class DictAndProximity {
public final Dictionary mDictionary;
public final ProximityInfo mProximityInfo;
public DictAndProximity(final Dictionary dictionary, final ProximityInfo proximityInfo) {
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java
index 8fc632ee7..1fb2bbb6a 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java
@@ -16,19 +16,56 @@
package com.android.inputmethod.latin.spellcheck;
+import android.util.Log;
+
+import com.android.inputmethod.keyboard.ProximityInfo;
+import com.android.inputmethod.latin.CollectionUtils;
+import com.android.inputmethod.latin.Dictionary;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+import com.android.inputmethod.latin.WordComposer;
+
+import java.util.ArrayList;
import java.util.Locale;
import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
/**
* A blocking queue that creates dictionaries up to a certain limit as necessary.
+ * As a deadlock-detecting device, if waiting for more than TIMEOUT = 3 seconds, we
+ * will clear the queue and generate its contents again. This is transparent for
+ * the client code, but may help with sloppy clients.
*/
@SuppressWarnings("serial")
-public class DictionaryPool extends LinkedBlockingQueue<DictAndProximity> {
+public final class DictionaryPool extends LinkedBlockingQueue<DictAndProximity> {
+ private final static String TAG = DictionaryPool.class.getSimpleName();
+ // How many seconds we wait for a dictionary to become available. Past this delay, we give up in
+ // fear some bug caused a deadlock, and reset the whole pool.
+ private final static int TIMEOUT = 3;
private final AndroidSpellCheckerService mService;
private final int mMaxSize;
private final Locale mLocale;
private int mSize;
private volatile boolean mClosed;
+ final static ArrayList<SuggestedWordInfo> noSuggestions = CollectionUtils.newArrayList();
+ private final static DictAndProximity dummyDict = new DictAndProximity(
+ new Dictionary(Dictionary.TYPE_MAIN) {
+ @Override
+ public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
+ final CharSequence prevWord, final ProximityInfo proximityInfo) {
+ return noSuggestions;
+ }
+ @Override
+ public boolean isValidWord(CharSequence word) {
+ // This is never called. However if for some strange reason it ever gets
+ // called, returning true is less destructive (it will not underline the
+ // word in red).
+ return true;
+ }
+ }, null);
+
+ static public boolean isAValidDictionary(final DictAndProximity dictInfo) {
+ return null != dictInfo && dummyDict != dictInfo;
+ }
public DictionaryPool(final int maxSize, final AndroidSpellCheckerService service,
final Locale locale) {
@@ -41,13 +78,23 @@ public class DictionaryPool extends LinkedBlockingQueue<DictAndProximity> {
}
@Override
- public DictAndProximity take() throws InterruptedException {
+ public DictAndProximity poll(final long timeout, final TimeUnit unit)
+ throws InterruptedException {
final DictAndProximity dict = poll();
if (null != dict) return dict;
synchronized(this) {
if (mSize >= mMaxSize) {
- // Our pool is already full. Wait until some dictionary is ready.
- return super.take();
+ // Our pool is already full. Wait until some dictionary is ready, or TIMEOUT
+ // expires to avoid a deadlock.
+ final DictAndProximity result = super.poll(timeout, unit);
+ if (null == result) {
+ Log.e(TAG, "Deadlock detected ! Resetting dictionary pool");
+ clear();
+ mSize = 1;
+ return mService.createDictAndProximity(mLocale);
+ } else {
+ return result;
+ }
} else {
++mSize;
return mService.createDictAndProximity(mLocale);
@@ -56,9 +103,9 @@ public class DictionaryPool extends LinkedBlockingQueue<DictAndProximity> {
}
// Convenience method
- public DictAndProximity takeOrGetNull() {
+ public DictAndProximity pollWithDefaultTimeout() {
try {
- return take();
+ return poll(TIMEOUT, TimeUnit.SECONDS);
} catch (InterruptedException e) {
return null;
}
@@ -78,7 +125,7 @@ public class DictionaryPool extends LinkedBlockingQueue<DictAndProximity> {
public boolean offer(final DictAndProximity dict) {
if (mClosed) {
dict.mDictionary.close();
- return false;
+ return super.offer(dummyDict);
} else {
return super.offer(dict);
}
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java
index 0103e8423..11bb97031 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java
@@ -16,14 +16,15 @@
package com.android.inputmethod.latin.spellcheck;
-import com.android.inputmethod.keyboard.KeyDetector;
import com.android.inputmethod.keyboard.ProximityInfo;
+import com.android.inputmethod.latin.CollectionUtils;
+import com.android.inputmethod.latin.Constants;
import java.util.TreeMap;
-public class SpellCheckerProximityInfo {
+public final class SpellCheckerProximityInfo {
/* public for test */
- final public static int NUL = KeyDetector.NOT_A_CODE;
+ final public static int NUL = Constants.NOT_A_CODE;
// This must be the same as MAX_PROXIMITY_CHARS_SIZE else it will not work inside
// native code - this value is passed at creation of the binary object and reused
@@ -52,14 +53,14 @@ public class SpellCheckerProximityInfo {
return result;
}
- private static class Latin {
+ private static final class Latin {
// This is a map from the code point to the index in the PROXIMITY array.
// At the time the native code to read the binary dictionary needs the proximity info be
// passed as a flat array spaced by MAX_PROXIMITY_CHARS_SIZE columns, one for each input
// character.
// Since we need to build such an array, we want to be able to search in our big proximity
// data quickly by character, and a map is probably the best way to do this.
- final private static TreeMap<Integer, Integer> INDICES = new TreeMap<Integer, Integer>();
+ final private static TreeMap<Integer, Integer> INDICES = CollectionUtils.newTreeMap();
// The proximity here is the union of
// - the proximity for a QWERTY keyboard.
@@ -111,6 +112,7 @@ public class SpellCheckerProximityInfo {
NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
+ NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL,
};
static {
buildProximityIndices(PROXIMITY, INDICES);
@@ -120,8 +122,8 @@ public class SpellCheckerProximityInfo {
}
}
- private static class Cyrillic {
- final private static TreeMap<Integer, Integer> INDICES = new TreeMap<Integer, Integer>();
+ private static final class Cyrillic {
+ final private static TreeMap<Integer, Integer> INDICES = CollectionUtils.newTreeMap();
// TODO: The following table is solely based on the keyboard layout. Consult with Russian
// speakers on commonly misspelled words/letters.
final static int[] PROXIMITY = {
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsActivity.java b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsActivity.java
index e14db8797..e63dff312 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsActivity.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsActivity.java
@@ -23,7 +23,7 @@ import android.preference.PreferenceActivity;
/**
* Spell checker preference screen.
*/
-public class SpellCheckerSettingsActivity extends PreferenceActivity {
+public final class SpellCheckerSettingsActivity extends PreferenceActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsFragment.java b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsFragment.java
index 7056874a1..ef5123d68 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsFragment.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsFragment.java
@@ -24,7 +24,7 @@ import com.android.inputmethod.latin.R;
/**
* Preference screen.
*/
-public class SpellCheckerSettingsFragment extends PreferenceFragment {
+public final class SpellCheckerSettingsFragment extends PreferenceFragment {
/**
* Empty constructor for fragment generation.
*/
diff --git a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
index c6fe43b69..4e9fd1968 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
@@ -23,158 +23,166 @@ import android.graphics.drawable.Drawable;
import com.android.inputmethod.keyboard.Key;
import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.KeyboardSwitcher;
+import com.android.inputmethod.keyboard.internal.KeyboardBuilder;
import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
+import com.android.inputmethod.keyboard.internal.KeyboardParams;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.SuggestedWords;
import com.android.inputmethod.latin.Utils;
-public class MoreSuggestions extends Keyboard {
+public final class MoreSuggestions extends Keyboard {
public static final int SUGGESTION_CODE_BASE = 1024;
- MoreSuggestions(Builder.MoreSuggestionsParam params) {
+ MoreSuggestions(final MoreSuggestionsParam params) {
super(params);
}
- public static class Builder extends Keyboard.Builder<Builder.MoreSuggestionsParam> {
- private final MoreSuggestionsView mPaneView;
- private SuggestedWords mSuggestions;
- private int mFromPos;
- private int mToPos;
+ private static final class MoreSuggestionsParam extends KeyboardParams {
+ private final int[] mWidths = new int[SuggestionStripView.MAX_SUGGESTIONS];
+ private final int[] mRowNumbers = new int[SuggestionStripView.MAX_SUGGESTIONS];
+ private final int[] mColumnOrders = new int[SuggestionStripView.MAX_SUGGESTIONS];
+ private final int[] mNumColumnsInRow = new int[SuggestionStripView.MAX_SUGGESTIONS];
+ private static final int MAX_COLUMNS_IN_ROW = 3;
+ private int mNumRows;
+ public Drawable mDivider;
+ public int mDividerWidth;
+
+ public MoreSuggestionsParam() {
+ super();
+ }
- public static class MoreSuggestionsParam extends Keyboard.Params {
- private final int[] mWidths = new int[SuggestionsView.MAX_SUGGESTIONS];
- private final int[] mRowNumbers = new int[SuggestionsView.MAX_SUGGESTIONS];
- private final int[] mColumnOrders = new int[SuggestionsView.MAX_SUGGESTIONS];
- private final int[] mNumColumnsInRow = new int[SuggestionsView.MAX_SUGGESTIONS];
- private static final int MAX_COLUMNS_IN_ROW = 3;
- private int mNumRows;
- public Drawable mDivider;
- public int mDividerWidth;
-
- public int layout(SuggestedWords suggestions, int fromPos, int maxWidth, int minWidth,
- int maxRow, MoreSuggestionsView view) {
- clearKeys();
- final Resources res = view.getContext().getResources();
- mDivider = res.getDrawable(R.drawable.more_suggestions_divider);
- mDividerWidth = mDivider.getIntrinsicWidth();
- final int padding = (int) res.getDimension(
- R.dimen.more_suggestions_key_horizontal_padding);
- final Paint paint = view.newDefaultLabelPaint();
-
- int row = 0;
- int pos = fromPos, rowStartPos = fromPos;
- final int size = Math.min(suggestions.size(), SuggestionsView.MAX_SUGGESTIONS);
- while (pos < size) {
- final String word = suggestions.getWord(pos).toString();
- // TODO: Should take care of text x-scaling.
- mWidths[pos] = (int)view.getLabelWidth(word, paint) + padding;
- final int numColumn = pos - rowStartPos + 1;
- final int columnWidth =
- (maxWidth - mDividerWidth * (numColumn - 1)) / numColumn;
- if (numColumn > MAX_COLUMNS_IN_ROW
- || !fitInWidth(rowStartPos, pos + 1, columnWidth)) {
- if ((row + 1) >= maxRow) {
- break;
- }
- mNumColumnsInRow[row] = pos - rowStartPos;
- rowStartPos = pos;
- row++;
+ // TODO: Remove {@link MoreSuggestionsView} argument.
+ public int layout(final SuggestedWords suggestions, final int fromPos, final int maxWidth,
+ final int minWidth, final int maxRow, final MoreSuggestionsView view) {
+ clearKeys();
+ final Resources res = view.getResources();
+ mDivider = res.getDrawable(R.drawable.more_suggestions_divider);
+ mDividerWidth = mDivider.getIntrinsicWidth();
+ final int padding = (int) res.getDimension(
+ R.dimen.more_suggestions_key_horizontal_padding);
+ final Paint paint = view.newDefaultLabelPaint();
+
+ int row = 0;
+ int pos = fromPos, rowStartPos = fromPos;
+ final int size = Math.min(suggestions.size(), SuggestionStripView.MAX_SUGGESTIONS);
+ while (pos < size) {
+ final String word = suggestions.getWord(pos).toString();
+ // TODO: Should take care of text x-scaling.
+ mWidths[pos] = (int)view.getLabelWidth(word, paint) + padding;
+ final int numColumn = pos - rowStartPos + 1;
+ final int columnWidth =
+ (maxWidth - mDividerWidth * (numColumn - 1)) / numColumn;
+ if (numColumn > MAX_COLUMNS_IN_ROW
+ || !fitInWidth(rowStartPos, pos + 1, columnWidth)) {
+ if ((row + 1) >= maxRow) {
+ break;
}
- mColumnOrders[pos] = pos - rowStartPos;
- mRowNumbers[pos] = row;
- pos++;
+ mNumColumnsInRow[row] = pos - rowStartPos;
+ rowStartPos = pos;
+ row++;
}
- mNumColumnsInRow[row] = pos - rowStartPos;
- mNumRows = row + 1;
- mBaseWidth = mOccupiedWidth = Math.max(
- minWidth, calcurateMaxRowWidth(fromPos, pos));
- mBaseHeight = mOccupiedHeight = mNumRows * mDefaultRowHeight + mVerticalGap;
- return pos - fromPos;
+ mColumnOrders[pos] = pos - rowStartPos;
+ mRowNumbers[pos] = row;
+ pos++;
}
+ mNumColumnsInRow[row] = pos - rowStartPos;
+ mNumRows = row + 1;
+ mBaseWidth = mOccupiedWidth = Math.max(
+ minWidth, calcurateMaxRowWidth(fromPos, pos));
+ mBaseHeight = mOccupiedHeight = mNumRows * mDefaultRowHeight + mVerticalGap;
+ return pos - fromPos;
+ }
- private boolean fitInWidth(int startPos, int endPos, int width) {
- for (int pos = startPos; pos < endPos; pos++) {
- if (mWidths[pos] > width)
- return false;
- }
- return true;
+ private boolean fitInWidth(final int startPos, final int endPos, final int width) {
+ for (int pos = startPos; pos < endPos; pos++) {
+ if (mWidths[pos] > width)
+ return false;
}
+ return true;
+ }
- private int calcurateMaxRowWidth(int startPos, int endPos) {
- int maxRowWidth = 0;
- int pos = startPos;
- for (int row = 0; row < mNumRows; row++) {
- final int numColumnInRow = mNumColumnsInRow[row];
- int maxKeyWidth = 0;
- while (pos < endPos && mRowNumbers[pos] == row) {
- maxKeyWidth = Math.max(maxKeyWidth, mWidths[pos]);
- pos++;
- }
- maxRowWidth = Math.max(maxRowWidth,
- maxKeyWidth * numColumnInRow + mDividerWidth * (numColumnInRow - 1));
+ private int calcurateMaxRowWidth(final int startPos, final int endPos) {
+ int maxRowWidth = 0;
+ int pos = startPos;
+ for (int row = 0; row < mNumRows; row++) {
+ final int numColumnInRow = mNumColumnsInRow[row];
+ int maxKeyWidth = 0;
+ while (pos < endPos && mRowNumbers[pos] == row) {
+ maxKeyWidth = Math.max(maxKeyWidth, mWidths[pos]);
+ pos++;
}
- return maxRowWidth;
+ maxRowWidth = Math.max(maxRowWidth,
+ maxKeyWidth * numColumnInRow + mDividerWidth * (numColumnInRow - 1));
}
+ return maxRowWidth;
+ }
- private static final int[][] COLUMN_ORDER_TO_NUMBER = {
- { 0, },
- { 1, 0, },
- { 2, 0, 1},
- };
-
- public int getNumColumnInRow(int pos) {
- return mNumColumnsInRow[mRowNumbers[pos]];
- }
+ private static final int[][] COLUMN_ORDER_TO_NUMBER = {
+ { 0, },
+ { 1, 0, },
+ { 2, 0, 1},
+ };
- public int getColumnNumber(int pos) {
- final int columnOrder = mColumnOrders[pos];
- final int numColumn = getNumColumnInRow(pos);
- return COLUMN_ORDER_TO_NUMBER[numColumn - 1][columnOrder];
- }
+ public int getNumColumnInRow(final int pos) {
+ return mNumColumnsInRow[mRowNumbers[pos]];
+ }
- public int getX(int pos) {
- final int columnNumber = getColumnNumber(pos);
- return columnNumber * (getWidth(pos) + mDividerWidth);
- }
+ public int getColumnNumber(final int pos) {
+ final int columnOrder = mColumnOrders[pos];
+ final int numColumn = getNumColumnInRow(pos);
+ return COLUMN_ORDER_TO_NUMBER[numColumn - 1][columnOrder];
+ }
- public int getY(int pos) {
- final int row = mRowNumbers[pos];
- return (mNumRows -1 - row) * mDefaultRowHeight + mTopPadding;
- }
+ public int getX(final int pos) {
+ final int columnNumber = getColumnNumber(pos);
+ return columnNumber * (getWidth(pos) + mDividerWidth);
+ }
- public int getWidth(int pos) {
- final int numColumnInRow = getNumColumnInRow(pos);
- return (mOccupiedWidth - mDividerWidth * (numColumnInRow - 1)) / numColumnInRow;
- }
+ public int getY(final int pos) {
+ final int row = mRowNumbers[pos];
+ return (mNumRows -1 - row) * mDefaultRowHeight + mTopPadding;
+ }
- public void markAsEdgeKey(Key key, int pos) {
- final int row = mRowNumbers[pos];
- if (row == 0)
- key.markAsBottomEdge(this);
- if (row == mNumRows - 1)
- key.markAsTopEdge(this);
+ public int getWidth(final int pos) {
+ final int numColumnInRow = getNumColumnInRow(pos);
+ return (mOccupiedWidth - mDividerWidth * (numColumnInRow - 1)) / numColumnInRow;
+ }
- final int numColumnInRow = mNumColumnsInRow[row];
- final int column = getColumnNumber(pos);
- if (column == 0)
- key.markAsLeftEdge(this);
- if (column == numColumnInRow - 1)
- key.markAsRightEdge(this);
- }
+ public void markAsEdgeKey(final Key key, final int pos) {
+ final int row = mRowNumbers[pos];
+ if (row == 0)
+ key.markAsBottomEdge(this);
+ if (row == mNumRows - 1)
+ key.markAsTopEdge(this);
+
+ final int numColumnInRow = mNumColumnsInRow[row];
+ final int column = getColumnNumber(pos);
+ if (column == 0)
+ key.markAsLeftEdge(this);
+ if (column == numColumnInRow - 1)
+ key.markAsRightEdge(this);
}
+ }
- public Builder(MoreSuggestionsView paneView) {
+ public static final class Builder extends KeyboardBuilder<MoreSuggestionsParam> {
+ private final MoreSuggestionsView mPaneView;
+ private SuggestedWords mSuggestions;
+ private int mFromPos;
+ private int mToPos;
+
+ public Builder(final MoreSuggestionsView paneView) {
super(paneView.getContext(), new MoreSuggestionsParam());
mPaneView = paneView;
}
- public Builder layout(SuggestedWords suggestions, int fromPos, int maxWidth,
- int minWidth, int maxRow) {
+ public Builder layout(final SuggestedWords suggestions, final int fromPos,
+ final int maxWidth, final int minWidth, final int maxRow) {
final Keyboard keyboard = KeyboardSwitcher.getInstance().getKeyboard();
final int xmlId = R.xml.kbd_suggestions_pane_template;
load(xmlId, keyboard.mId);
mParams.mVerticalGap = mParams.mTopPadding = keyboard.mVerticalGap / 2;
+ mPaneView.updateKeyboardGeometry(mParams.mDefaultRowHeight);
final int count = mParams.layout(suggestions, fromPos, maxWidth, minWidth, maxRow,
mPaneView);
mFromPos = fromPos;
@@ -183,25 +191,6 @@ public class MoreSuggestions extends Keyboard {
return this;
}
- private static class Divider extends Key.Spacer {
- private final Drawable mIcon;
-
- public Divider(Keyboard.Params params, Drawable icon, int x, int y, int width,
- int height) {
- super(params, x, y, width, height);
- mIcon = icon;
- }
-
- @Override
- public Drawable getIcon(KeyboardIconsSet iconSet, int alpha) {
- // KeyboardIconsSet and alpha are unused. Use the icon that has been passed to the
- // constructor.
- // TODO: Drawable itself should have an alpha value.
- mIcon.setAlpha(128);
- return mIcon;
- }
- }
-
@Override
public MoreSuggestions build() {
final MoreSuggestionsParam params = mParams;
@@ -228,4 +217,23 @@ public class MoreSuggestions extends Keyboard {
return new MoreSuggestions(params);
}
}
+
+ private static final class Divider extends Key.Spacer {
+ private final Drawable mIcon;
+
+ public Divider(final KeyboardParams params, final Drawable icon, final int x,
+ final int y, final int width, final int height) {
+ super(params, x, y, width, height);
+ mIcon = icon;
+ }
+
+ @Override
+ public Drawable getIcon(final KeyboardIconsSet iconSet, final int alpha) {
+ // KeyboardIconsSet and alpha are unused. Use the icon that has been passed to the
+ // constructor.
+ // TODO: Drawable itself should have an alpha value.
+ mIcon.setAlpha(128);
+ return mIcon;
+ }
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
index 19287e3f3..03a2e73d1 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
@@ -40,7 +40,7 @@ import com.android.inputmethod.latin.R;
* A view that renders a virtual {@link MoreSuggestions}. It handles rendering of keys and detecting
* key presses and touch movements.
*/
-public class MoreSuggestionsView extends KeyboardView implements MoreKeysPanel {
+public final class MoreSuggestionsView extends KeyboardView implements MoreKeysPanel {
private final int[] mCoordinates = new int[2];
final KeyDetector mModalPanelKeyDetector;
@@ -68,7 +68,7 @@ public class MoreSuggestionsView extends KeyboardView implements MoreKeysPanel {
@Override
public void onCodeInput(int primaryCode, int x, int y) {
final int index = primaryCode - MoreSuggestions.SUGGESTION_CODE_BASE;
- if (index >= 0 && index < SuggestionsView.MAX_SUGGESTIONS) {
+ if (index >= 0 && index < SuggestionStripView.MAX_SUGGESTIONS) {
mListener.onCustomRequest(index);
}
}
@@ -105,6 +105,10 @@ public class MoreSuggestionsView extends KeyboardView implements MoreKeysPanel {
}
}
+ public void updateKeyboardGeometry(final int keyHeight) {
+ mKeyDrawParams.updateParams(keyHeight, mKeyVisualAttributes);
+ }
+
@Override
public void setKeyboard(Keyboard keyboard) {
super.setKeyboard(keyboard);
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionsView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
index e86390b11..e926fa209 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionsView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
@@ -57,22 +57,24 @@ import com.android.inputmethod.keyboard.KeyboardView;
import com.android.inputmethod.keyboard.MoreKeysPanel;
import com.android.inputmethod.keyboard.PointerTracker;
import com.android.inputmethod.keyboard.ViewLayoutUtils;
+import com.android.inputmethod.latin.AutoCorrection;
+import com.android.inputmethod.latin.CollectionUtils;
import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.ResearchLogger;
+import com.android.inputmethod.latin.ResourceUtils;
import com.android.inputmethod.latin.StaticInnerHandlerWrapper;
-import com.android.inputmethod.latin.Suggest;
import com.android.inputmethod.latin.SuggestedWords;
import com.android.inputmethod.latin.Utils;
import com.android.inputmethod.latin.define.ProductionFlag;
+import com.android.inputmethod.research.ResearchLogger;
import java.util.ArrayList;
-public class SuggestionsView extends RelativeLayout implements OnClickListener,
+public final class SuggestionStripView extends RelativeLayout implements OnClickListener,
OnLongClickListener {
public interface Listener {
- public boolean addWordToDictionary(String word);
- public void pickSuggestionManually(int index, CharSequence word, int x, int y);
+ public boolean addWordToUserDictionary(String word);
+ public void pickSuggestionManually(int index, CharSequence word);
}
// The maximum number of suggestions available. See {@link Suggest#mPrefMaxSuggestions}.
@@ -88,9 +90,9 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener,
private final MoreSuggestions.Builder mMoreSuggestionsBuilder;
private final PopupWindow mMoreSuggestionsWindow;
- private final ArrayList<TextView> mWords = new ArrayList<TextView>();
- private final ArrayList<TextView> mInfos = new ArrayList<TextView>();
- private final ArrayList<View> mDividers = new ArrayList<View>();
+ private final ArrayList<TextView> mWords = CollectionUtils.newArrayList();
+ private final ArrayList<TextView> mInfos = CollectionUtils.newArrayList();
+ private final ArrayList<View> mDividers = CollectionUtils.newArrayList();
private final PopupWindow mPreviewPopup;
private final TextView mPreviewText;
@@ -98,24 +100,24 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener,
private Listener mListener;
private SuggestedWords mSuggestedWords = SuggestedWords.EMPTY;
- private final SuggestionsViewParams mParams;
+ private final SuggestionStripViewParams mParams;
private static final float MIN_TEXT_XSCALE = 0.70f;
private final UiHandler mHandler = new UiHandler(this);
- private static class UiHandler extends StaticInnerHandlerWrapper<SuggestionsView> {
+ private static final class UiHandler extends StaticInnerHandlerWrapper<SuggestionStripView> {
private static final int MSG_HIDE_PREVIEW = 0;
- public UiHandler(SuggestionsView outerInstance) {
+ public UiHandler(SuggestionStripView outerInstance) {
super(outerInstance);
}
@Override
public void dispatchMessage(Message msg) {
- final SuggestionsView suggestionsView = getOuterInstance();
+ final SuggestionStripView suggestionStripView = getOuterInstance();
switch (msg.what) {
case MSG_HIDE_PREVIEW:
- suggestionsView.hidePreview();
+ suggestionStripView.hidePreview();
break;
}
}
@@ -129,9 +131,9 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener,
}
}
- private static class SuggestionsViewParams {
+ private static final class SuggestionStripViewParams {
private static final int DEFAULT_SUGGESTIONS_COUNT_IN_STRIP = 3;
- private static final int DEFAULT_CENTER_SUGGESTION_PERCENTILE = 40;
+ private static final float DEFAULT_CENTER_SUGGESTION_PERCENTILE = 0.40f;
private static final int DEFAULT_MAX_MORE_SUGGESTIONS_ROW = 2;
private static final int PUNCTUATIONS_IN_STRIP = 5;
@@ -167,7 +169,7 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener,
private final int mSuggestionStripOption;
- private final ArrayList<CharSequence> mTexts = new ArrayList<CharSequence>();
+ private final ArrayList<CharSequence> mTexts = CollectionUtils.newArrayList();
public boolean mMoreSuggestionsAvailable;
@@ -175,7 +177,7 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener,
private final TextView mLeftwardsArrowView;
private final TextView mHintToSaveView;
- public SuggestionsViewParams(Context context, AttributeSet attrs, int defStyle,
+ public SuggestionStripViewParams(Context context, AttributeSet attrs, int defStyle,
ArrayList<TextView> words, ArrayList<View> dividers, ArrayList<TextView> infos) {
mWords = words;
mDividers = dividers;
@@ -191,38 +193,39 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener,
final Resources res = word.getResources();
mSuggestionsStripHeight = res.getDimensionPixelSize(R.dimen.suggestions_strip_height);
- final TypedArray a = context.obtainStyledAttributes(
- attrs, R.styleable.SuggestionsView, defStyle, R.style.SuggestionsViewStyle);
- mSuggestionStripOption = a.getInt(R.styleable.SuggestionsView_suggestionStripOption, 0);
- final float alphaValidTypedWord = getPercent(a,
- R.styleable.SuggestionsView_alphaValidTypedWord, 100);
- final float alphaTypedWord = getPercent(a,
- R.styleable.SuggestionsView_alphaTypedWord, 100);
- final float alphaAutoCorrect = getPercent(a,
- R.styleable.SuggestionsView_alphaAutoCorrect, 100);
- final float alphaSuggested = getPercent(a,
- R.styleable.SuggestionsView_alphaSuggested, 100);
- mAlphaObsoleted = getPercent(a, R.styleable.SuggestionsView_alphaSuggested, 100);
- mColorValidTypedWord = applyAlpha(
- a.getColor(R.styleable.SuggestionsView_colorValidTypedWord, 0),
- alphaValidTypedWord);
- mColorTypedWord = applyAlpha(
- a.getColor(R.styleable.SuggestionsView_colorTypedWord, 0), alphaTypedWord);
- mColorAutoCorrect = applyAlpha(
- a.getColor(R.styleable.SuggestionsView_colorAutoCorrect, 0), alphaAutoCorrect);
- mColorSuggested = applyAlpha(
- a.getColor(R.styleable.SuggestionsView_colorSuggested, 0), alphaSuggested);
+ final TypedArray a = context.obtainStyledAttributes(attrs,
+ R.styleable.SuggestionStripView, defStyle, R.style.SuggestionStripViewStyle);
+ mSuggestionStripOption = a.getInt(
+ R.styleable.SuggestionStripView_suggestionStripOption, 0);
+ final float alphaValidTypedWord = ResourceUtils.getFraction(a,
+ R.styleable.SuggestionStripView_alphaValidTypedWord, 1.0f);
+ final float alphaTypedWord = ResourceUtils.getFraction(a,
+ R.styleable.SuggestionStripView_alphaTypedWord, 1.0f);
+ final float alphaAutoCorrect = ResourceUtils.getFraction(a,
+ R.styleable.SuggestionStripView_alphaAutoCorrect, 1.0f);
+ final float alphaSuggested = ResourceUtils.getFraction(a,
+ R.styleable.SuggestionStripView_alphaSuggested, 1.0f);
+ mAlphaObsoleted = ResourceUtils.getFraction(a,
+ R.styleable.SuggestionStripView_alphaSuggested, 1.0f);
+ mColorValidTypedWord = applyAlpha(a.getColor(
+ R.styleable.SuggestionStripView_colorValidTypedWord, 0), alphaValidTypedWord);
+ mColorTypedWord = applyAlpha(a.getColor(
+ R.styleable.SuggestionStripView_colorTypedWord, 0), alphaTypedWord);
+ mColorAutoCorrect = applyAlpha(a.getColor(
+ R.styleable.SuggestionStripView_colorAutoCorrect, 0), alphaAutoCorrect);
+ mColorSuggested = applyAlpha(a.getColor(
+ R.styleable.SuggestionStripView_colorSuggested, 0), alphaSuggested);
mSuggestionsCountInStrip = a.getInt(
- R.styleable.SuggestionsView_suggestionsCountInStrip,
+ R.styleable.SuggestionStripView_suggestionsCountInStrip,
DEFAULT_SUGGESTIONS_COUNT_IN_STRIP);
- mCenterSuggestionWeight = getPercent(a,
- R.styleable.SuggestionsView_centerSuggestionPercentile,
+ mCenterSuggestionWeight = ResourceUtils.getFraction(a,
+ R.styleable.SuggestionStripView_centerSuggestionPercentile,
DEFAULT_CENTER_SUGGESTION_PERCENTILE);
mMaxMoreSuggestionsRow = a.getInt(
- R.styleable.SuggestionsView_maxMoreSuggestionsRow,
+ R.styleable.SuggestionStripView_maxMoreSuggestionsRow,
DEFAULT_MAX_MORE_SUGGESTIONS_ROW);
- mMinMoreSuggestionsWidth = getRatio(a,
- R.styleable.SuggestionsView_minMoreSuggestionsWidth);
+ mMinMoreSuggestionsWidth = ResourceUtils.getFraction(a,
+ R.styleable.SuggestionStripView_minMoreSuggestionsWidth, 1.0f);
a.recycle();
mMoreSuggestionsHint = getMoreSuggestionsHint(res,
@@ -276,16 +279,6 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener,
return new BitmapDrawable(res, buffer);
}
- // Read integer value in TypedArray as percent.
- private static float getPercent(TypedArray a, int index, int defValue) {
- return a.getInt(index, defValue) / 100.0f;
- }
-
- // Read fraction value in TypedArray as float.
- private static float getRatio(TypedArray a, int index) {
- return a.getFraction(index, 1000, 1000, 1) / 1000.0f;
- }
-
private CharSequence getStyledSuggestionWord(SuggestedWords suggestedWords, int pos) {
final CharSequence word = suggestedWords.getWord(pos);
final boolean isAutoCorrect = pos == 1 && suggestedWords.willAutoCorrect();
@@ -336,8 +329,8 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener,
if (LatinImeLogger.sDBG && suggestedWords.size() > 1) {
// If we auto-correct, then the autocorrection is in slot 0 and the typed word
// is in slot 1.
- if (index == mCenterSuggestionIndex && suggestedWords.mHasAutoCorrectionCandidate
- && Suggest.shouldBlockAutoCorrectionBySafetyNet(
+ if (index == mCenterSuggestionIndex
+ && AutoCorrection.shouldBlockAutoCorrectionBySafetyNet(
suggestedWords.getWord(1).toString(), suggestedWords.getWord(0))) {
return 0xFFFF0000;
}
@@ -596,15 +589,15 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener,
}
/**
- * Construct a {@link SuggestionsView} for showing suggestions to be picked by the user.
+ * Construct a {@link SuggestionStripView} for showing suggestions to be picked by the user.
* @param context
* @param attrs
*/
- public SuggestionsView(Context context, AttributeSet attrs) {
- this(context, attrs, R.attr.suggestionsViewStyle);
+ public SuggestionStripView(Context context, AttributeSet attrs) {
+ this(context, attrs, R.attr.suggestionStripViewStyle);
}
- public SuggestionsView(Context context, AttributeSet attrs, int defStyle) {
+ public SuggestionStripView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
final LayoutInflater inflater = LayoutInflater.from(context);
@@ -631,7 +624,8 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener,
mInfos.add((TextView)inflater.inflate(R.layout.suggestion_info, null));
}
- mParams = new SuggestionsViewParams(context, attrs, defStyle, mWords, mDividers, mInfos);
+ mParams = new SuggestionStripViewParams(
+ context, attrs, defStyle, mWords, mDividers, mInfos);
mMoreSuggestionsContainer = inflater.inflate(R.layout.more_suggestions, null);
mMoreSuggestionsView = (MoreSuggestionsView)mMoreSuggestionsContainer
@@ -677,7 +671,7 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener,
mSuggestedWords = suggestedWords;
mParams.layout(mSuggestedWords, mSuggestionsStrip, this, getWidth());
if (ProductionFlag.IS_EXPERIMENTAL) {
- ResearchLogger.suggestionsView_setSuggestions(mSuggestedWords);
+ ResearchLogger.suggestionStripView_setSuggestions(mSuggestedWords);
}
}
@@ -718,19 +712,13 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener,
mPreviewPopup.dismiss();
}
- private void addToDictionary(CharSequence word) {
- mListener.addWordToDictionary(word.toString());
- }
-
private final KeyboardActionListener mMoreSuggestionsListener =
new KeyboardActionListener.Adapter() {
@Override
public boolean onCustomRequest(int requestCode) {
final int index = requestCode;
final CharSequence word = mSuggestedWords.getWord(index);
- // TODO: change caller path so coordinates are passed through here
- mListener.pickSuggestionManually(index, word, NOT_A_TOUCH_COORDINATE,
- NOT_A_TOUCH_COORDINATE);
+ mListener.pickSuggestionManually(index, word);
dismissMoreSuggestions();
return true;
}
@@ -763,7 +751,7 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener,
}
private boolean showMoreSuggestions() {
- final SuggestionsViewParams params = mParams;
+ final SuggestionStripViewParams params = mParams;
if (params.mMoreSuggestionsAvailable) {
final int stripWidth = getWidth();
final View container = mMoreSuggestionsContainer;
@@ -863,7 +851,7 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener,
@Override
public void onClick(View view) {
if (mParams.isAddToDictionaryShowing(view)) {
- addToDictionary(mParams.getAddToDictionaryWord());
+ mListener.addWordToUserDictionary(mParams.getAddToDictionaryWord().toString());
clear();
return;
}
@@ -876,7 +864,7 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener,
return;
final CharSequence word = mSuggestedWords.getWord(index);
- mListener.pickSuggestionManually(index, word, mLastX, mLastY);
+ mListener.pickSuggestionManually(index, word);
}
@Override
diff --git a/java/src/com/android/inputmethod/research/BootBroadcastReceiver.java b/java/src/com/android/inputmethod/research/BootBroadcastReceiver.java
new file mode 100644
index 000000000..5124a35a6
--- /dev/null
+++ b/java/src/com/android/inputmethod/research/BootBroadcastReceiver.java
@@ -0,0 +1,33 @@
+/*
+ * 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.research;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+/**
+ * Arrange for the uploading service to be run on regular intervals.
+ */
+public final class BootBroadcastReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
+ ResearchLogger.scheduleUploadingService(context);
+ }
+ }
+}
diff --git a/java/src/com/android/inputmethod/research/FeedbackActivity.java b/java/src/com/android/inputmethod/research/FeedbackActivity.java
new file mode 100644
index 000000000..11eae8813
--- /dev/null
+++ b/java/src/com/android/inputmethod/research/FeedbackActivity.java
@@ -0,0 +1,54 @@
+/*
+ * 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.research;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.CheckBox;
+
+import com.android.inputmethod.latin.R;
+
+public class FeedbackActivity extends Activity {
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.research_feedback_activity);
+ final FeedbackLayout layout = (FeedbackLayout) findViewById(R.id.research_feedback_layout);
+ final CheckBox checkbox = (CheckBox) findViewById(R.id.research_feedback_include_history);
+ final CharSequence cs = checkbox.getText();
+ final String actualString = String.format(cs.toString(),
+ ResearchLogger.FEEDBACK_WORD_BUFFER_SIZE);
+ checkbox.setText(actualString);
+ layout.setActivity(this);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ }
+
+ @Override
+ public void onBackPressed() {
+ ResearchLogger.getInstance().onLeavingSendFeedbackDialog();
+ super.onBackPressed();
+ }
+}
diff --git a/java/src/com/android/inputmethod/research/FeedbackFragment.java b/java/src/com/android/inputmethod/research/FeedbackFragment.java
new file mode 100644
index 000000000..a2e08e2b7
--- /dev/null
+++ b/java/src/com/android/inputmethod/research/FeedbackFragment.java
@@ -0,0 +1,73 @@
+/*
+ * 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.research;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.os.Bundle;
+import android.text.Editable;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.EditText;
+
+import com.android.inputmethod.latin.R;
+
+public class FeedbackFragment extends Fragment {
+ private EditText mEditText;
+ private CheckBox mCheckBox;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ final View view = inflater.inflate(R.layout.research_feedback_fragment_layout, container,
+ false);
+ mEditText = (EditText) view.findViewById(R.id.research_feedback_contents);
+ mCheckBox = (CheckBox) view.findViewById(R.id.research_feedback_include_history);
+
+ final Button sendButton = (Button) view.findViewById(
+ R.id.research_feedback_send_button);
+ sendButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ final Editable editable = mEditText.getText();
+ final String feedbackContents = editable.toString();
+ final boolean includeHistory = mCheckBox.isChecked();
+ ResearchLogger.getInstance().sendFeedback(feedbackContents, includeHistory);
+ final Activity activity = FeedbackFragment.this.getActivity();
+ activity.finish();
+ ResearchLogger.getInstance().onLeavingSendFeedbackDialog();
+ }
+ });
+
+ final Button cancelButton = (Button) view.findViewById(
+ R.id.research_feedback_cancel_button);
+ cancelButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ final Activity activity = FeedbackFragment.this.getActivity();
+ activity.finish();
+ ResearchLogger.getInstance().onLeavingSendFeedbackDialog();
+ }
+ });
+
+ return view;
+ }
+}
diff --git a/java/src/com/android/inputmethod/research/FeedbackLayout.java b/java/src/com/android/inputmethod/research/FeedbackLayout.java
new file mode 100644
index 000000000..f2cbfe308
--- /dev/null
+++ b/java/src/com/android/inputmethod/research/FeedbackLayout.java
@@ -0,0 +1,62 @@
+/*
+ * 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.research;
+
+import android.app.Activity;
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.widget.LinearLayout;
+
+public class FeedbackLayout extends LinearLayout {
+ private Activity mActivity;
+
+ public FeedbackLayout(Context context) {
+ super(context);
+ }
+
+ public FeedbackLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public FeedbackLayout(Context context, AttributeSet attrs, int defstyle) {
+ super(context, attrs, defstyle);
+ }
+
+ public void setActivity(Activity activity) {
+ mActivity = activity;
+ }
+
+ @Override
+ public boolean dispatchKeyEventPreIme(KeyEvent event) {
+ if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
+ KeyEvent.DispatcherState state = getKeyDispatcherState();
+ if (state != null) {
+ if (event.getAction() == KeyEvent.ACTION_DOWN
+ && event.getRepeatCount() == 0) {
+ state.startTracking(event, this);
+ return true;
+ } else if (event.getAction() == KeyEvent.ACTION_UP
+ && !event.isCanceled() && state.isTracking(event)) {
+ mActivity.onBackPressed();
+ return true;
+ }
+ }
+ }
+ return super.dispatchKeyEventPreIme(event);
+ }
+}
diff --git a/java/src/com/android/inputmethod/research/LogBuffer.java b/java/src/com/android/inputmethod/research/LogBuffer.java
new file mode 100644
index 000000000..ae7b1579a
--- /dev/null
+++ b/java/src/com/android/inputmethod/research/LogBuffer.java
@@ -0,0 +1,113 @@
+/*
+ * 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.research;
+
+import com.android.inputmethod.latin.CollectionUtils;
+
+import java.util.LinkedList;
+
+/**
+ * A buffer that holds a fixed number of LogUnits.
+ *
+ * LogUnits are added in and shifted out in temporal order. Only a subset of the LogUnits are
+ * actual words; the other LogUnits do not count toward the word limit. Once the buffer reaches
+ * capacity, adding another LogUnit that is a word evicts the oldest LogUnits out one at a time to
+ * stay under the capacity limit.
+ */
+public class LogBuffer {
+ protected final LinkedList<LogUnit> mLogUnits;
+ /* package for test */ int mWordCapacity;
+ // The number of members of mLogUnits that are actual words.
+ protected int mNumActualWords;
+
+ /**
+ * Create a new LogBuffer that can hold a fixed number of LogUnits that are words (and
+ * unlimited number of non-word LogUnits), and that outputs its result to a researchLog.
+ *
+ * @param wordCapacity maximum number of words
+ */
+ LogBuffer(final int wordCapacity) {
+ if (wordCapacity <= 0) {
+ throw new IllegalArgumentException("wordCapacity must be 1 or greater.");
+ }
+ mLogUnits = CollectionUtils.newLinkedList();
+ mWordCapacity = wordCapacity;
+ mNumActualWords = 0;
+ }
+
+ /**
+ * Adds a new LogUnit to the front of the LIFO queue, evicting existing LogUnit's
+ * (oldest first) if word capacity is reached.
+ */
+ public void shiftIn(LogUnit newLogUnit) {
+ if (newLogUnit.getWord() == null) {
+ // This LogUnit isn't a word, so it doesn't count toward the word-limit.
+ mLogUnits.add(newLogUnit);
+ return;
+ }
+ if (mNumActualWords == mWordCapacity) {
+ shiftOutThroughFirstWord();
+ }
+ mLogUnits.add(newLogUnit);
+ mNumActualWords++; // Must be a word, or we wouldn't be here.
+ }
+
+ private void shiftOutThroughFirstWord() {
+ while (!mLogUnits.isEmpty()) {
+ final LogUnit logUnit = mLogUnits.removeFirst();
+ onShiftOut(logUnit);
+ if (logUnit.hasWord()) {
+ // Successfully shifted out a word-containing LogUnit and made space for the new
+ // LogUnit.
+ mNumActualWords--;
+ break;
+ }
+ }
+ }
+
+ /**
+ * Removes all LogUnits from the buffer without calling onShiftOut().
+ */
+ public void clear() {
+ mLogUnits.clear();
+ mNumActualWords = 0;
+ }
+
+ /**
+ * Called when a LogUnit is removed from the LogBuffer as a result of a shiftIn. LogUnits are
+ * removed in the order entered. This method is not called when shiftOut is called directly.
+ *
+ * Base class does nothing; subclasses may override.
+ */
+ protected void onShiftOut(LogUnit logUnit) {
+ }
+
+ /**
+ * Called to deliberately remove the oldest LogUnit. Usually called when draining the
+ * LogBuffer.
+ */
+ public LogUnit shiftOut() {
+ if (mLogUnits.isEmpty()) {
+ return null;
+ }
+ final LogUnit logUnit = mLogUnits.removeFirst();
+ if (logUnit.hasWord()) {
+ mNumActualWords--;
+ }
+ return logUnit;
+ }
+}
diff --git a/java/src/com/android/inputmethod/research/LogUnit.java b/java/src/com/android/inputmethod/research/LogUnit.java
new file mode 100644
index 000000000..d8b3a29ff
--- /dev/null
+++ b/java/src/com/android/inputmethod/research/LogUnit.java
@@ -0,0 +1,83 @@
+/*
+ * 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.research;
+
+import com.android.inputmethod.latin.CollectionUtils;
+
+import java.util.ArrayList;
+
+/**
+ * A group of log statements related to each other.
+ *
+ * A LogUnit is collection of LogStatements, each of which is generated by at a particular point
+ * in the code. (There is no LogStatement class; the data is stored across the instance variables
+ * here.) A single LogUnit's statements can correspond to all the calls made while in the same
+ * composing region, or all the calls between committing the last composing region, and the first
+ * character of the next composing region.
+ *
+ * Individual statements in a log may be marked as potentially private. If so, then they are only
+ * published to a ResearchLog if the ResearchLogger determines that publishing the entire LogUnit
+ * will not violate the user's privacy. Checks for this may include whether other LogUnits have
+ * been published recently, or whether the LogUnit contains numbers, etc.
+ */
+/* package */ class LogUnit {
+ private final ArrayList<String[]> mKeysList = CollectionUtils.newArrayList();
+ private final ArrayList<Object[]> mValuesList = CollectionUtils.newArrayList();
+ private final ArrayList<Boolean> mIsPotentiallyPrivate = CollectionUtils.newArrayList();
+ private String mWord;
+ private boolean mContainsDigit;
+
+ public void addLogStatement(final String[] keys, final Object[] values,
+ final Boolean isPotentiallyPrivate) {
+ mKeysList.add(keys);
+ mValuesList.add(values);
+ mIsPotentiallyPrivate.add(isPotentiallyPrivate);
+ }
+
+ public void publishTo(final ResearchLog researchLog, final boolean isIncludingPrivateData) {
+ final int size = mKeysList.size();
+ for (int i = 0; i < size; i++) {
+ if (!mIsPotentiallyPrivate.get(i) || isIncludingPrivateData) {
+ researchLog.outputEvent(mKeysList.get(i), mValuesList.get(i));
+ }
+ }
+ }
+
+ public void setWord(String word) {
+ mWord = word;
+ }
+
+ public String getWord() {
+ return mWord;
+ }
+
+ public boolean hasWord() {
+ return mWord != null;
+ }
+
+ public void setContainsDigit() {
+ mContainsDigit = true;
+ }
+
+ public boolean hasDigit() {
+ return mContainsDigit;
+ }
+
+ public boolean isEmpty() {
+ return mKeysList.isEmpty();
+ }
+}
diff --git a/java/src/com/android/inputmethod/research/MainLogBuffer.java b/java/src/com/android/inputmethod/research/MainLogBuffer.java
new file mode 100644
index 000000000..745768d35
--- /dev/null
+++ b/java/src/com/android/inputmethod/research/MainLogBuffer.java
@@ -0,0 +1,127 @@
+/*
+ * 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.research;
+
+import com.android.inputmethod.latin.Dictionary;
+import com.android.inputmethod.latin.Suggest;
+
+import java.util.Random;
+
+public class MainLogBuffer extends LogBuffer {
+ // The size of the n-grams logged. E.g. N_GRAM_SIZE = 2 means to sample bigrams.
+ private static final int N_GRAM_SIZE = 2;
+ // The number of words between n-grams to omit from the log.
+ private static final int DEFAULT_NUMBER_OF_WORDS_BETWEEN_SAMPLES = 18;
+
+ private final ResearchLog mResearchLog;
+ private Suggest mSuggest;
+
+ // The minimum periodicity with which n-grams can be sampled. E.g. mWinWordPeriod is 10 if
+ // every 10th bigram is sampled, i.e., words 1-8 are not, but the bigram at words 9 and 10, etc.
+ // for 11-18, and the bigram at words 19 and 20. If an n-gram is not safe (e.g. it contains a
+ // number in the middle or an out-of-vocabulary word), then sampling is delayed until a safe
+ // n-gram does appear.
+ /* package for test */ int mMinWordPeriod;
+
+ // Counter for words left to suppress before an n-gram can be sampled. Reset to mMinWordPeriod
+ // after a sample is taken.
+ /* package for test */ int mWordsUntilSafeToSample;
+
+ public MainLogBuffer(final ResearchLog researchLog) {
+ super(N_GRAM_SIZE);
+ mResearchLog = researchLog;
+ mMinWordPeriod = DEFAULT_NUMBER_OF_WORDS_BETWEEN_SAMPLES + N_GRAM_SIZE;
+ final Random random = new Random();
+ mWordsUntilSafeToSample = random.nextInt(mMinWordPeriod);
+ }
+
+ public void setSuggest(Suggest suggest) {
+ mSuggest = suggest;
+ }
+
+ @Override
+ public void shiftIn(final LogUnit newLogUnit) {
+ super.shiftIn(newLogUnit);
+ if (newLogUnit.hasWord()) {
+ if (mWordsUntilSafeToSample > 0) {
+ mWordsUntilSafeToSample--;
+ }
+ }
+ }
+
+ public void resetWordCounter() {
+ mWordsUntilSafeToSample = mMinWordPeriod;
+ }
+
+ /**
+ * Determines whether the content of the MainLogBuffer can be safely uploaded in its complete
+ * form and still protect the user's privacy.
+ *
+ * The size of the MainLogBuffer is just enough to hold one n-gram, its corrections, and any
+ * non-character data that is typed between words. The decision about privacy is made based on
+ * the buffer's entire content. If it is decided that the privacy risks are too great to upload
+ * the contents of this buffer, a censored version of the LogItems may still be uploaded. E.g.,
+ * the screen orientation and other characteristics about the device can be uploaded without
+ * revealing much about the user.
+ */
+ public boolean isSafeToLog() {
+ // Check that we are not sampling too frequently. Having sampled recently might disclose
+ // too much of the user's intended meaning.
+ if (mWordsUntilSafeToSample > 0) {
+ return false;
+ }
+ if (mSuggest == null || !mSuggest.hasMainDictionary()) {
+ // Main dictionary is unavailable. Since we cannot check it, we cannot tell if a word
+ // is out-of-vocabulary or not. Therefore, we must judge the entire buffer contents to
+ // potentially pose a privacy risk.
+ return false;
+ }
+ // Reload the dictionary in case it has changed (e.g., because the user has changed
+ // languages).
+ final Dictionary dictionary = mSuggest.getMainDictionary();
+ if (dictionary == null) {
+ return false;
+ }
+ // Check each word in the buffer. If any word poses a privacy threat, we cannot upload the
+ // complete buffer contents in detail.
+ final int length = mLogUnits.size();
+ for (int i = 0; i < length; i++) {
+ final LogUnit logUnit = mLogUnits.get(i);
+ final String word = logUnit.getWord();
+ if (word == null) {
+ // Digits outside words are a privacy threat.
+ if (logUnit.hasDigit()) {
+ return false;
+ }
+ } else {
+ // Words not in the dictionary are a privacy threat.
+ if (!(dictionary.isValidWord(word))) {
+ return false;
+ }
+ }
+ }
+ // All checks have passed; this buffer's content can be safely uploaded.
+ return true;
+ }
+
+ @Override
+ protected void onShiftOut(LogUnit logUnit) {
+ if (mResearchLog != null) {
+ mResearchLog.publish(logUnit, false /* isIncludingPrivateData */);
+ }
+ }
+}
diff --git a/java/src/com/android/inputmethod/research/ResearchLog.java b/java/src/com/android/inputmethod/research/ResearchLog.java
new file mode 100644
index 000000000..70c38e909
--- /dev/null
+++ b/java/src/com/android/inputmethod/research/ResearchLog.java
@@ -0,0 +1,320 @@
+/*
+ * 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.research;
+
+import android.content.SharedPreferences;
+import android.os.SystemClock;
+import android.util.JsonWriter;
+import android.util.Log;
+import android.view.inputmethod.CompletionInfo;
+
+import com.android.inputmethod.keyboard.Key;
+import com.android.inputmethod.latin.SuggestedWords;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+import com.android.inputmethod.latin.define.ProductionFlag;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executors;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Logs the use of the LatinIME keyboard.
+ *
+ * This class logs operations on the IME keyboard, including what the user has typed.
+ * Data is stored locally in a file in app-specific storage.
+ *
+ * This functionality is off by default. See {@link ProductionFlag#IS_EXPERIMENTAL}.
+ */
+public class ResearchLog {
+ private static final String TAG = ResearchLog.class.getSimpleName();
+ private static final boolean DEBUG = false;
+ private static final long FLUSH_DELAY_IN_MS = 1000 * 5;
+ private static final int ABORT_TIMEOUT_IN_MS = 1000 * 4;
+
+ /* package */ final ScheduledExecutorService mExecutor;
+ /* package */ final File mFile;
+ private JsonWriter mJsonWriter = NULL_JSON_WRITER;
+ // true if at least one byte of data has been written out to the log file. This must be
+ // remembered because JsonWriter requires that calls matching calls to beginObject and
+ // endObject, as well as beginArray and endArray, and the file is opened lazily, only when
+ // it is certain that data will be written. Alternatively, the matching call exceptions
+ // could be caught, but this might suppress other errors.
+ private boolean mHasWrittenData = false;
+
+ private static final JsonWriter NULL_JSON_WRITER = new JsonWriter(
+ new OutputStreamWriter(new NullOutputStream()));
+ private static class NullOutputStream extends OutputStream {
+ /** {@inheritDoc} */
+ @Override
+ public void write(byte[] buffer, int offset, int count) {
+ // nop
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void write(byte[] buffer) {
+ // nop
+ }
+
+ @Override
+ public void write(int oneByte) {
+ }
+ }
+
+ public ResearchLog(final File outputFile) {
+ if (outputFile == null) {
+ throw new IllegalArgumentException();
+ }
+ mExecutor = Executors.newSingleThreadScheduledExecutor();
+ mFile = outputFile;
+ }
+
+ public synchronized void close(final Runnable onClosed) {
+ mExecutor.submit(new Callable<Object>() {
+ @Override
+ public Object call() throws Exception {
+ try {
+ if (mHasWrittenData) {
+ mJsonWriter.endArray();
+ mJsonWriter.flush();
+ mJsonWriter.close();
+ if (DEBUG) {
+ Log.d(TAG, "wrote log to " + mFile);
+ }
+ mHasWrittenData = false;
+ } else {
+ if (DEBUG) {
+ Log.d(TAG, "close() called, but no data, not outputting");
+ }
+ }
+ } catch (Exception e) {
+ Log.d(TAG, "error when closing ResearchLog:");
+ e.printStackTrace();
+ } finally {
+ if (mFile.exists()) {
+ mFile.setWritable(false, false);
+ }
+ if (onClosed != null) {
+ onClosed.run();
+ }
+ }
+ return null;
+ }
+ });
+ removeAnyScheduledFlush();
+ mExecutor.shutdown();
+ }
+
+ private boolean mIsAbortSuccessful;
+
+ public synchronized void abort() {
+ mExecutor.submit(new Callable<Object>() {
+ @Override
+ public Object call() throws Exception {
+ try {
+ if (mHasWrittenData) {
+ mJsonWriter.endArray();
+ mJsonWriter.close();
+ mHasWrittenData = false;
+ }
+ } finally {
+ mIsAbortSuccessful = mFile.delete();
+ }
+ return null;
+ }
+ });
+ removeAnyScheduledFlush();
+ mExecutor.shutdown();
+ }
+
+ public boolean blockingAbort() throws InterruptedException {
+ abort();
+ mExecutor.awaitTermination(ABORT_TIMEOUT_IN_MS, TimeUnit.MILLISECONDS);
+ return mIsAbortSuccessful;
+ }
+
+ public void awaitTermination(int delay, TimeUnit timeUnit) throws InterruptedException {
+ mExecutor.awaitTermination(delay, timeUnit);
+ }
+
+ /* package */ synchronized void flush() {
+ removeAnyScheduledFlush();
+ mExecutor.submit(mFlushCallable);
+ }
+
+ private final Callable<Object> mFlushCallable = new Callable<Object>() {
+ @Override
+ public Object call() throws Exception {
+ mJsonWriter.flush();
+ return null;
+ }
+ };
+
+ private ScheduledFuture<Object> mFlushFuture;
+
+ private void removeAnyScheduledFlush() {
+ if (mFlushFuture != null) {
+ mFlushFuture.cancel(false);
+ mFlushFuture = null;
+ }
+ }
+
+ private void scheduleFlush() {
+ removeAnyScheduledFlush();
+ mFlushFuture = mExecutor.schedule(mFlushCallable, FLUSH_DELAY_IN_MS, TimeUnit.MILLISECONDS);
+ }
+
+ public synchronized void publish(final LogUnit logUnit, final boolean isIncludingPrivateData) {
+ try {
+ mExecutor.submit(new Callable<Object>() {
+ @Override
+ public Object call() throws Exception {
+ logUnit.publishTo(ResearchLog.this, isIncludingPrivateData);
+ scheduleFlush();
+ return null;
+ }
+ });
+ } catch (RejectedExecutionException e) {
+ // TODO: Add code to record loss of data, and report.
+ }
+ }
+
+ private static final String CURRENT_TIME_KEY = "_ct";
+ private static final String UPTIME_KEY = "_ut";
+ private static final String EVENT_TYPE_KEY = "_ty";
+
+ void outputEvent(final String[] keys, final Object[] values) {
+ // Not thread safe.
+ if (keys.length == 0) {
+ return;
+ }
+ if (DEBUG) {
+ if (keys.length != values.length + 1) {
+ Log.d(TAG, "Key and Value list sizes do not match. " + keys[0]);
+ }
+ }
+ try {
+ if (mJsonWriter == NULL_JSON_WRITER) {
+ mJsonWriter = new JsonWriter(new BufferedWriter(new FileWriter(mFile)));
+ mJsonWriter.beginArray();
+ mHasWrittenData = true;
+ }
+ mJsonWriter.beginObject();
+ mJsonWriter.name(CURRENT_TIME_KEY).value(System.currentTimeMillis());
+ mJsonWriter.name(UPTIME_KEY).value(SystemClock.uptimeMillis());
+ mJsonWriter.name(EVENT_TYPE_KEY).value(keys[0]);
+ final int length = values.length;
+ for (int i = 0; i < length; i++) {
+ mJsonWriter.name(keys[i + 1]);
+ Object value = values[i];
+ if (value instanceof CharSequence) {
+ mJsonWriter.value(value.toString());
+ } else if (value instanceof Number) {
+ mJsonWriter.value((Number) value);
+ } else if (value instanceof Boolean) {
+ mJsonWriter.value((Boolean) value);
+ } else if (value instanceof CompletionInfo[]) {
+ CompletionInfo[] ci = (CompletionInfo[]) value;
+ mJsonWriter.beginArray();
+ for (int j = 0; j < ci.length; j++) {
+ mJsonWriter.value(ci[j].toString());
+ }
+ mJsonWriter.endArray();
+ } else if (value instanceof SharedPreferences) {
+ SharedPreferences prefs = (SharedPreferences) value;
+ mJsonWriter.beginObject();
+ for (Map.Entry<String,?> entry : prefs.getAll().entrySet()) {
+ mJsonWriter.name(entry.getKey());
+ final Object innerValue = entry.getValue();
+ if (innerValue == null) {
+ mJsonWriter.nullValue();
+ } else if (innerValue instanceof Boolean) {
+ mJsonWriter.value((Boolean) innerValue);
+ } else if (innerValue instanceof Number) {
+ mJsonWriter.value((Number) innerValue);
+ } else {
+ mJsonWriter.value(innerValue.toString());
+ }
+ }
+ mJsonWriter.endObject();
+ } else if (value instanceof Key[]) {
+ Key[] keyboardKeys = (Key[]) value;
+ mJsonWriter.beginArray();
+ for (Key keyboardKey : keyboardKeys) {
+ mJsonWriter.beginObject();
+ mJsonWriter.name("code").value(keyboardKey.mCode);
+ mJsonWriter.name("altCode").value(keyboardKey.getAltCode());
+ mJsonWriter.name("x").value(keyboardKey.mX);
+ mJsonWriter.name("y").value(keyboardKey.mY);
+ mJsonWriter.name("w").value(keyboardKey.mWidth);
+ mJsonWriter.name("h").value(keyboardKey.mHeight);
+ mJsonWriter.endObject();
+ }
+ mJsonWriter.endArray();
+ } else if (value instanceof SuggestedWords) {
+ SuggestedWords words = (SuggestedWords) value;
+ mJsonWriter.beginObject();
+ mJsonWriter.name("typedWordValid").value(words.mTypedWordValid);
+ mJsonWriter.name("willAutoCorrect").value(words.mWillAutoCorrect);
+ mJsonWriter.name("isPunctuationSuggestions")
+ .value(words.mIsPunctuationSuggestions);
+ mJsonWriter.name("isObsoleteSuggestions").value(words.mIsObsoleteSuggestions);
+ mJsonWriter.name("isPrediction").value(words.mIsPrediction);
+ mJsonWriter.name("words");
+ mJsonWriter.beginArray();
+ final int size = words.size();
+ for (int j = 0; j < size; j++) {
+ SuggestedWordInfo wordInfo = words.getWordInfo(j);
+ mJsonWriter.value(wordInfo.toString());
+ }
+ mJsonWriter.endArray();
+ mJsonWriter.endObject();
+ } else if (value == null) {
+ mJsonWriter.nullValue();
+ } else {
+ Log.w(TAG, "Unrecognized type to be logged: " +
+ (value == null ? "<null>" : value.getClass().getName()));
+ mJsonWriter.nullValue();
+ }
+ }
+ mJsonWriter.endObject();
+ } catch (IOException e) {
+ e.printStackTrace();
+ Log.w(TAG, "Error in JsonWriter; disabling logging");
+ try {
+ mJsonWriter.close();
+ } catch (IllegalStateException e1) {
+ // Assume that this is just the json not being terminated properly.
+ // Ignore
+ } catch (IOException e1) {
+ e1.printStackTrace();
+ } finally {
+ mJsonWriter = NULL_JSON_WRITER;
+ }
+ }
+ }
+}
diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java
new file mode 100644
index 000000000..763fd6e00
--- /dev/null
+++ b/java/src/com/android/inputmethod/research/ResearchLogger.java
@@ -0,0 +1,1315 @@
+/*
+ * 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.research;
+
+import static com.android.inputmethod.latin.Constants.Subtype.ExtraValue.KEYBOARD_LAYOUT_SET;
+
+import android.app.AlarmManager;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnCancelListener;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Paint.Style;
+import android.inputmethodservice.InputMethodService;
+import android.net.Uri;
+import android.os.Build;
+import android.os.IBinder;
+import android.os.SystemClock;
+import android.text.TextUtils;
+import android.text.format.DateUtils;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.Window;
+import android.view.WindowManager;
+import android.view.inputmethod.CompletionInfo;
+import android.view.inputmethod.CorrectionInfo;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.widget.Toast;
+
+import com.android.inputmethod.keyboard.Key;
+import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.keyboard.KeyboardView;
+import com.android.inputmethod.keyboard.MainKeyboardView;
+import com.android.inputmethod.latin.CollectionUtils;
+import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.latin.Dictionary;
+import com.android.inputmethod.latin.LatinIME;
+import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.RichInputConnection;
+import com.android.inputmethod.latin.RichInputConnection.Range;
+import com.android.inputmethod.latin.Suggest;
+import com.android.inputmethod.latin.SuggestedWords;
+import com.android.inputmethod.latin.define.ProductionFlag;
+
+import java.io.File;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.UUID;
+
+/**
+ * Logs the use of the LatinIME keyboard.
+ *
+ * This class logs operations on the IME keyboard, including what the user has typed.
+ * Data is stored locally in a file in app-specific storage.
+ *
+ * This functionality is off by default. See {@link ProductionFlag#IS_EXPERIMENTAL}.
+ */
+public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChangeListener {
+ private static final String TAG = ResearchLogger.class.getSimpleName();
+ private static final boolean DEBUG = false;
+ private static final boolean OUTPUT_ENTIRE_BUFFER = false; // true may disclose private info
+ public static final boolean DEFAULT_USABILITY_STUDY_MODE = false;
+ /* package */ static boolean sIsLogging = false;
+ private static final int OUTPUT_FORMAT_VERSION = 1;
+ private static final String PREF_USABILITY_STUDY_MODE = "usability_study_mode";
+ private static final String PREF_RESEARCH_HAS_SEEN_SPLASH = "pref_research_has_seen_splash";
+ /* package */ static final String FILENAME_PREFIX = "researchLog";
+ private static final String FILENAME_SUFFIX = ".txt";
+ private static final SimpleDateFormat TIMESTAMP_DATEFORMAT =
+ new SimpleDateFormat("yyyyMMddHHmmssS", Locale.US);
+ private static final boolean IS_SHOWING_INDICATOR = true;
+ private static final boolean IS_SHOWING_INDICATOR_CLEARLY = false;
+ public static final int FEEDBACK_WORD_BUFFER_SIZE = 5;
+
+ // constants related to specific log points
+ private static final String WHITESPACE_SEPARATORS = " \t\n\r";
+ private static final int MAX_INPUTVIEW_LENGTH_TO_CAPTURE = 8192; // must be >=1
+ private static final String PREF_RESEARCH_LOGGER_UUID_STRING = "pref_research_logger_uuid";
+
+ private static final ResearchLogger sInstance = new ResearchLogger();
+ // to write to a different filename, e.g., for testing, set mFile before calling start()
+ /* package */ File mFilesDir;
+ /* package */ String mUUIDString;
+ /* package */ ResearchLog mMainResearchLog;
+ // mFeedbackLog records all events for the session, private or not (excepting
+ // passwords). It is written to permanent storage only if the user explicitly commands
+ // the system to do so.
+ // LogUnits are queued in the LogBuffers and published to the ResearchLogs when words are
+ // complete.
+ /* package */ ResearchLog mFeedbackLog;
+ /* package */ MainLogBuffer mMainLogBuffer;
+ /* package */ LogBuffer mFeedbackLogBuffer;
+
+ private boolean mIsPasswordView = false;
+ private boolean mIsLoggingSuspended = false;
+ private SharedPreferences mPrefs;
+
+ // digits entered by the user are replaced with this codepoint.
+ /* package for test */ static final int DIGIT_REPLACEMENT_CODEPOINT =
+ Character.codePointAt("\uE000", 0); // U+E000 is in the "private-use area"
+ // U+E001 is in the "private-use area"
+ /* package for test */ static final String WORD_REPLACEMENT_STRING = "\uE001";
+ private static final String PREF_LAST_CLEANUP_TIME = "pref_last_cleanup_time";
+ private static final long DURATION_BETWEEN_DIR_CLEANUP_IN_MS = DateUtils.DAY_IN_MILLIS;
+ private static final long MAX_LOGFILE_AGE_IN_MS = DateUtils.DAY_IN_MILLIS;
+ protected static final int SUSPEND_DURATION_IN_MINUTES = 1;
+ // set when LatinIME should ignore an onUpdateSelection() callback that
+ // arises from operations in this class
+ private static boolean sLatinIMEExpectingUpdateSelection = false;
+
+ // used to check whether words are not unique
+ private Suggest mSuggest;
+ private Dictionary mDictionary;
+ private MainKeyboardView mMainKeyboardView;
+ private InputMethodService mInputMethodService;
+ private final Statistics mStatistics;
+
+ private Intent mUploadIntent;
+ private PendingIntent mUploadPendingIntent;
+
+ private LogUnit mCurrentLogUnit = new LogUnit();
+
+ private ResearchLogger() {
+ mStatistics = Statistics.getInstance();
+ }
+
+ public static ResearchLogger getInstance() {
+ return sInstance;
+ }
+
+ public void init(final InputMethodService ims, final SharedPreferences prefs) {
+ assert ims != null;
+ if (ims == null) {
+ Log.w(TAG, "IMS is null; logging is off");
+ } else {
+ mFilesDir = ims.getFilesDir();
+ if (mFilesDir == null || !mFilesDir.exists()) {
+ Log.w(TAG, "IME storage directory does not exist.");
+ }
+ }
+ if (prefs != null) {
+ mUUIDString = getUUID(prefs);
+ if (!prefs.contains(PREF_USABILITY_STUDY_MODE)) {
+ Editor e = prefs.edit();
+ e.putBoolean(PREF_USABILITY_STUDY_MODE, DEFAULT_USABILITY_STUDY_MODE);
+ e.apply();
+ }
+ sIsLogging = prefs.getBoolean(PREF_USABILITY_STUDY_MODE, false);
+ prefs.registerOnSharedPreferenceChangeListener(this);
+
+ final long lastCleanupTime = prefs.getLong(PREF_LAST_CLEANUP_TIME, 0L);
+ final long now = System.currentTimeMillis();
+ if (lastCleanupTime + DURATION_BETWEEN_DIR_CLEANUP_IN_MS < now) {
+ final long timeHorizon = now - MAX_LOGFILE_AGE_IN_MS;
+ cleanupLoggingDir(mFilesDir, timeHorizon);
+ Editor e = prefs.edit();
+ e.putLong(PREF_LAST_CLEANUP_TIME, now);
+ e.apply();
+ }
+ }
+ mInputMethodService = ims;
+ mPrefs = prefs;
+ mUploadIntent = new Intent(mInputMethodService, UploaderService.class);
+ mUploadPendingIntent = PendingIntent.getService(mInputMethodService, 0, mUploadIntent, 0);
+
+ if (ProductionFlag.IS_EXPERIMENTAL) {
+ scheduleUploadingService(mInputMethodService);
+ }
+ }
+
+ /**
+ * Arrange for the UploaderService to be run on a regular basis.
+ *
+ * Any existing scheduled invocation of UploaderService is removed and rescheduled. This may
+ * cause problems if this method is called often and frequent updates are required, but since
+ * the user will likely be sleeping at some point, if the interval is less that the expected
+ * sleep duration and this method is not called during that time, the service should be invoked
+ * at some point.
+ */
+ public static void scheduleUploadingService(Context context) {
+ final Intent intent = new Intent(context, UploaderService.class);
+ final PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, 0);
+ final AlarmManager manager =
+ (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+ manager.cancel(pendingIntent);
+ manager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ UploaderService.RUN_INTERVAL, UploaderService.RUN_INTERVAL, pendingIntent);
+ }
+
+ private void cleanupLoggingDir(final File dir, final long time) {
+ for (File file : dir.listFiles()) {
+ if (file.getName().startsWith(ResearchLogger.FILENAME_PREFIX) &&
+ file.lastModified() < time) {
+ file.delete();
+ }
+ }
+ }
+
+ public void mainKeyboardView_onAttachedToWindow(final MainKeyboardView mainKeyboardView) {
+ mMainKeyboardView = mainKeyboardView;
+ maybeShowSplashScreen();
+ }
+
+ public void mainKeyboardView_onDetachedFromWindow() {
+ mMainKeyboardView = null;
+ }
+
+ private boolean hasSeenSplash() {
+ return mPrefs.getBoolean(PREF_RESEARCH_HAS_SEEN_SPLASH, false);
+ }
+
+ private Dialog mSplashDialog = null;
+
+ private void maybeShowSplashScreen() {
+ if (hasSeenSplash()) {
+ return;
+ }
+ if (mSplashDialog != null && mSplashDialog.isShowing()) {
+ return;
+ }
+ final IBinder windowToken = mMainKeyboardView != null
+ ? mMainKeyboardView.getWindowToken() : null;
+ if (windowToken == null) {
+ return;
+ }
+ final AlertDialog.Builder builder = new AlertDialog.Builder(mInputMethodService)
+ .setTitle(R.string.research_splash_title)
+ .setMessage(R.string.research_splash_content)
+ .setPositiveButton(android.R.string.yes,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ onUserLoggingConsent();
+ mSplashDialog.dismiss();
+ }
+ })
+ .setNegativeButton(android.R.string.no,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ final String packageName = mInputMethodService.getPackageName();
+ final Uri packageUri = Uri.parse("package:" + packageName);
+ final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE,
+ packageUri);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mInputMethodService.startActivity(intent);
+ }
+ })
+ .setCancelable(true)
+ .setOnCancelListener(
+ new OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ mInputMethodService.requestHideSelf(0);
+ }
+ });
+ mSplashDialog = builder.create();
+ final Window w = mSplashDialog.getWindow();
+ final WindowManager.LayoutParams lp = w.getAttributes();
+ lp.token = windowToken;
+ lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
+ w.setAttributes(lp);
+ w.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
+ mSplashDialog.show();
+ }
+
+ public void onUserLoggingConsent() {
+ setLoggingAllowed(true);
+ if (mPrefs == null) {
+ return;
+ }
+ final Editor e = mPrefs.edit();
+ e.putBoolean(PREF_RESEARCH_HAS_SEEN_SPLASH, true);
+ e.apply();
+ restart();
+ }
+
+ private void setLoggingAllowed(boolean enableLogging) {
+ if (mPrefs == null) {
+ return;
+ }
+ Editor e = mPrefs.edit();
+ e.putBoolean(PREF_USABILITY_STUDY_MODE, enableLogging);
+ e.apply();
+ sIsLogging = enableLogging;
+ }
+
+ private File createLogFile(File filesDir) {
+ final StringBuilder sb = new StringBuilder();
+ sb.append(FILENAME_PREFIX).append('-');
+ sb.append(mUUIDString).append('-');
+ sb.append(TIMESTAMP_DATEFORMAT.format(new Date()));
+ sb.append(FILENAME_SUFFIX);
+ return new File(filesDir, sb.toString());
+ }
+
+ private void checkForEmptyEditor() {
+ if (mInputMethodService == null) {
+ return;
+ }
+ final InputConnection ic = mInputMethodService.getCurrentInputConnection();
+ if (ic == null) {
+ return;
+ }
+ final CharSequence textBefore = ic.getTextBeforeCursor(1, 0);
+ if (!TextUtils.isEmpty(textBefore)) {
+ mStatistics.setIsEmptyUponStarting(false);
+ return;
+ }
+ final CharSequence textAfter = ic.getTextAfterCursor(1, 0);
+ if (!TextUtils.isEmpty(textAfter)) {
+ mStatistics.setIsEmptyUponStarting(false);
+ return;
+ }
+ if (textBefore != null && textAfter != null) {
+ mStatistics.setIsEmptyUponStarting(true);
+ }
+ }
+
+ private void start() {
+ if (DEBUG) {
+ Log.d(TAG, "start called");
+ }
+ maybeShowSplashScreen();
+ updateSuspendedState();
+ requestIndicatorRedraw();
+ mStatistics.reset();
+ checkForEmptyEditor();
+ if (!isAllowedToLog()) {
+ // Log.w(TAG, "not in usability mode; not logging");
+ return;
+ }
+ if (mFilesDir == null || !mFilesDir.exists()) {
+ Log.w(TAG, "IME storage directory does not exist. Cannot start logging.");
+ return;
+ }
+ if (mMainLogBuffer == null) {
+ mMainResearchLog = new ResearchLog(createLogFile(mFilesDir));
+ mMainLogBuffer = new MainLogBuffer(mMainResearchLog);
+ mMainLogBuffer.setSuggest(mSuggest);
+ }
+ if (mFeedbackLogBuffer == null) {
+ mFeedbackLog = new ResearchLog(createLogFile(mFilesDir));
+ // LogBuffer is one more than FEEDBACK_WORD_BUFFER_SIZE, because it must also hold
+ // the feedback LogUnit itself.
+ mFeedbackLogBuffer = new LogBuffer(FEEDBACK_WORD_BUFFER_SIZE + 1);
+ }
+ }
+
+ /* package */ void stop() {
+ if (DEBUG) {
+ Log.d(TAG, "stop called");
+ }
+ logStatistics();
+ commitCurrentLogUnit();
+
+ if (mMainLogBuffer != null) {
+ publishLogBuffer(mMainLogBuffer, mMainResearchLog, false /* isIncludingPrivateData */);
+ mMainResearchLog.close(null /* callback */);
+ mMainLogBuffer = null;
+ }
+ if (mFeedbackLogBuffer != null) {
+ mFeedbackLog.close(null /* callback */);
+ mFeedbackLogBuffer = null;
+ }
+ }
+
+ public boolean abort() {
+ if (DEBUG) {
+ Log.d(TAG, "abort called");
+ }
+ boolean didAbortMainLog = false;
+ if (mMainLogBuffer != null) {
+ mMainLogBuffer.clear();
+ try {
+ didAbortMainLog = mMainResearchLog.blockingAbort();
+ } catch (InterruptedException e) {
+ // Don't know whether this succeeded or not. We assume not; this is reported
+ // to the caller.
+ }
+ mMainLogBuffer = null;
+ }
+ boolean didAbortFeedbackLog = false;
+ if (mFeedbackLogBuffer != null) {
+ mFeedbackLogBuffer.clear();
+ try {
+ didAbortFeedbackLog = mFeedbackLog.blockingAbort();
+ } catch (InterruptedException e) {
+ // Don't know whether this succeeded or not. We assume not; this is reported
+ // to the caller.
+ }
+ mFeedbackLogBuffer = null;
+ }
+ return didAbortMainLog && didAbortFeedbackLog;
+ }
+
+ private void restart() {
+ stop();
+ start();
+ }
+
+ private long mResumeTime = 0L;
+ private void suspendLoggingUntil(long time) {
+ mIsLoggingSuspended = true;
+ mResumeTime = time;
+ requestIndicatorRedraw();
+ }
+
+ private void resumeLogging() {
+ mResumeTime = 0L;
+ updateSuspendedState();
+ requestIndicatorRedraw();
+ if (isAllowedToLog()) {
+ restart();
+ }
+ }
+
+ private void updateSuspendedState() {
+ final long time = System.currentTimeMillis();
+ if (time > mResumeTime) {
+ mIsLoggingSuspended = false;
+ }
+ }
+
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
+ if (key == null || prefs == null) {
+ return;
+ }
+ sIsLogging = prefs.getBoolean(PREF_USABILITY_STUDY_MODE, false);
+ if (sIsLogging == false) {
+ abort();
+ }
+ requestIndicatorRedraw();
+ mPrefs = prefs;
+ prefsChanged(prefs);
+ }
+
+ public void onResearchKeySelected(final LatinIME latinIME) {
+ if (mInFeedbackDialog) {
+ Toast.makeText(latinIME, R.string.research_please_exit_feedback_form,
+ Toast.LENGTH_LONG).show();
+ return;
+ }
+ presentFeedbackDialog(latinIME);
+ }
+
+ // TODO: currently unreachable. Remove after being sure no menu is needed.
+ /*
+ public void presentResearchDialog(final LatinIME latinIME) {
+ final CharSequence title = latinIME.getString(R.string.english_ime_research_log);
+ final boolean showEnable = mIsLoggingSuspended || !sIsLogging;
+ final CharSequence[] items = new CharSequence[] {
+ latinIME.getString(R.string.research_feedback_menu_option),
+ showEnable ? latinIME.getString(R.string.research_enable_session_logging) :
+ latinIME.getString(R.string.research_do_not_log_this_session)
+ };
+ final DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface di, int position) {
+ di.dismiss();
+ switch (position) {
+ case 0:
+ presentFeedbackDialog(latinIME);
+ break;
+ case 1:
+ enableOrDisable(showEnable, latinIME);
+ break;
+ }
+ }
+
+ };
+ final AlertDialog.Builder builder = new AlertDialog.Builder(latinIME)
+ .setItems(items, listener)
+ .setTitle(title);
+ latinIME.showOptionDialog(builder.create());
+ }
+ */
+
+ private boolean mInFeedbackDialog = false;
+ public void presentFeedbackDialog(LatinIME latinIME) {
+ mInFeedbackDialog = true;
+ latinIME.launchKeyboardedDialogActivity(FeedbackActivity.class);
+ }
+
+ // TODO: currently unreachable. Remove after being sure enable/disable is
+ // not needed.
+ /*
+ public void enableOrDisable(final boolean showEnable, final LatinIME latinIME) {
+ if (showEnable) {
+ if (!sIsLogging) {
+ setLoggingAllowed(true);
+ }
+ resumeLogging();
+ Toast.makeText(latinIME,
+ R.string.research_notify_session_logging_enabled,
+ Toast.LENGTH_LONG).show();
+ } else {
+ Toast toast = Toast.makeText(latinIME,
+ R.string.research_notify_session_log_deleting,
+ Toast.LENGTH_LONG);
+ toast.show();
+ boolean isLogDeleted = abort();
+ final long currentTime = System.currentTimeMillis();
+ final long resumeTime = currentTime + 1000 * 60 *
+ SUSPEND_DURATION_IN_MINUTES;
+ suspendLoggingUntil(resumeTime);
+ toast.cancel();
+ Toast.makeText(latinIME, R.string.research_notify_logging_suspended,
+ Toast.LENGTH_LONG).show();
+ }
+ }
+ */
+
+ private static final String[] EVENTKEYS_FEEDBACK = {
+ "UserTimestamp", "contents"
+ };
+ public void sendFeedback(final String feedbackContents, final boolean includeHistory) {
+ if (mFeedbackLogBuffer == null) {
+ return;
+ }
+ if (includeHistory) {
+ commitCurrentLogUnit();
+ } else {
+ mFeedbackLogBuffer.clear();
+ }
+ final LogUnit feedbackLogUnit = new LogUnit();
+ final Object[] values = {
+ feedbackContents
+ };
+ feedbackLogUnit.addLogStatement(EVENTKEYS_FEEDBACK, values,
+ false /* isPotentiallyPrivate */);
+ mFeedbackLogBuffer.shiftIn(feedbackLogUnit);
+ publishLogBuffer(mFeedbackLogBuffer, mFeedbackLog, true /* isIncludingPrivateData */);
+ mFeedbackLog.close(new Runnable() {
+ @Override
+ public void run() {
+ uploadNow();
+ }
+ });
+ mFeedbackLog = new ResearchLog(createLogFile(mFilesDir));
+ }
+
+ public void uploadNow() {
+ if (DEBUG) {
+ Log.d(TAG, "calling uploadNow()");
+ }
+ mInputMethodService.startService(mUploadIntent);
+ }
+
+ public void onLeavingSendFeedbackDialog() {
+ mInFeedbackDialog = false;
+ }
+
+ public void initSuggest(Suggest suggest) {
+ mSuggest = suggest;
+ if (mMainLogBuffer != null) {
+ mMainLogBuffer.setSuggest(mSuggest);
+ }
+ }
+
+ private void setIsPasswordView(boolean isPasswordView) {
+ mIsPasswordView = isPasswordView;
+ }
+
+ private boolean isAllowedToLog() {
+ if (DEBUG) {
+ Log.d(TAG, "iatl: " +
+ "mipw=" + mIsPasswordView +
+ ", mils=" + mIsLoggingSuspended +
+ ", sil=" + sIsLogging +
+ ", mInFeedbackDialog=" + mInFeedbackDialog);
+ }
+ return !mIsPasswordView && !mIsLoggingSuspended && sIsLogging && !mInFeedbackDialog;
+ }
+
+ public void requestIndicatorRedraw() {
+ if (!IS_SHOWING_INDICATOR) {
+ return;
+ }
+ if (mMainKeyboardView == null) {
+ return;
+ }
+ mMainKeyboardView.invalidateAllKeys();
+ }
+
+
+ public void paintIndicator(KeyboardView view, Paint paint, Canvas canvas, int width,
+ int height) {
+ // TODO: Reimplement using a keyboard background image specific to the ResearchLogger
+ // and remove this method.
+ // The check for MainKeyboardView ensures that a red border is only placed around
+ // the main keyboard, not every keyboard.
+ if (IS_SHOWING_INDICATOR && isAllowedToLog() && view instanceof MainKeyboardView) {
+ final int savedColor = paint.getColor();
+ paint.setColor(Color.RED);
+ final Style savedStyle = paint.getStyle();
+ paint.setStyle(Style.STROKE);
+ final float savedStrokeWidth = paint.getStrokeWidth();
+ if (IS_SHOWING_INDICATOR_CLEARLY) {
+ paint.setStrokeWidth(5);
+ canvas.drawRect(0, 0, width, height, paint);
+ } else {
+ // Put a tiny red dot on the screen so a knowledgeable user can check whether
+ // it is enabled. The dot is actually a zero-width, zero-height rectangle,
+ // placed at the lower-right corner of the canvas, painted with a non-zero border
+ // width.
+ paint.setStrokeWidth(3);
+ canvas.drawRect(width, height, width, height, paint);
+ }
+ paint.setColor(savedColor);
+ paint.setStyle(savedStyle);
+ paint.setStrokeWidth(savedStrokeWidth);
+ }
+ }
+
+ private static final Object[] EVENTKEYS_NULLVALUES = {};
+
+ /**
+ * Buffer a research log event, flagging it as privacy-sensitive.
+ *
+ * This event contains potentially private information. If the word that this event is a part
+ * of is determined to be privacy-sensitive, then this event should not be included in the
+ * output log. The system waits to output until the containing word is known.
+ *
+ * @param keys an array containing a descriptive name for the event, followed by the keys
+ * @param values an array of values, either a String or Number. length should be one
+ * less than the keys array
+ */
+ private synchronized void enqueuePotentiallyPrivateEvent(final String[] keys,
+ final Object[] values) {
+ assert values.length + 1 == keys.length;
+ if (isAllowedToLog()) {
+ mCurrentLogUnit.addLogStatement(keys, values, true /* isPotentiallyPrivate */);
+ }
+ }
+
+ private void setCurrentLogUnitContainsDigitFlag() {
+ mCurrentLogUnit.setContainsDigit();
+ }
+
+ /**
+ * Buffer a research log event, flaggint it as not privacy-sensitive.
+ *
+ * This event contains no potentially private information. Even if the word that this event
+ * is privacy-sensitive, this event can still safely be sent to the output log. The system
+ * waits until the containing word is known so that this event can be written in the proper
+ * temporal order with other events that may be privacy sensitive.
+ *
+ * @param keys an array containing a descriptive name for the event, followed by the keys
+ * @param values an array of values, either a String or Number. length should be one
+ * less than the keys array
+ */
+ private synchronized void enqueueEvent(final String[] keys, final Object[] values) {
+ assert values.length + 1 == keys.length;
+ if (isAllowedToLog()) {
+ mCurrentLogUnit.addLogStatement(keys, values, false /* isPotentiallyPrivate */);
+ }
+ }
+
+ /* package for test */ void commitCurrentLogUnit() {
+ if (DEBUG) {
+ Log.d(TAG, "commitCurrentLogUnit");
+ }
+ if (!mCurrentLogUnit.isEmpty()) {
+ if (mMainLogBuffer != null) {
+ mMainLogBuffer.shiftIn(mCurrentLogUnit);
+ if (mMainLogBuffer.isSafeToLog() && mMainResearchLog != null) {
+ publishLogBuffer(mMainLogBuffer, mMainResearchLog,
+ true /* isIncludingPrivateData */);
+ mMainLogBuffer.resetWordCounter();
+ }
+ }
+ if (mFeedbackLogBuffer != null) {
+ mFeedbackLogBuffer.shiftIn(mCurrentLogUnit);
+ }
+ mCurrentLogUnit = new LogUnit();
+ Log.d(TAG, "commitCurrentLogUnit");
+ }
+ }
+
+ /* package for test */ void publishLogBuffer(final LogBuffer logBuffer,
+ final ResearchLog researchLog, final boolean isIncludingPrivateData) {
+ LogUnit logUnit;
+ while ((logUnit = logBuffer.shiftOut()) != null) {
+ researchLog.publish(logUnit, isIncludingPrivateData);
+ }
+ }
+
+ private boolean hasOnlyLetters(final String word) {
+ final int length = word.length();
+ for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) {
+ final int codePoint = word.codePointAt(i);
+ if (!Character.isLetter(codePoint)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private void onWordComplete(final String word) {
+ Log.d(TAG, "onWordComplete: " + word);
+ if (word != null && word.length() > 0 && hasOnlyLetters(word)) {
+ mCurrentLogUnit.setWord(word);
+ mStatistics.recordWordEntered();
+ }
+ commitCurrentLogUnit();
+ }
+
+ private static int scrubDigitFromCodePoint(int codePoint) {
+ return Character.isDigit(codePoint) ? DIGIT_REPLACEMENT_CODEPOINT : codePoint;
+ }
+
+ /* package for test */ static String scrubDigitsFromString(String s) {
+ StringBuilder sb = null;
+ final int length = s.length();
+ for (int i = 0; i < length; i = s.offsetByCodePoints(i, 1)) {
+ final int codePoint = Character.codePointAt(s, i);
+ if (Character.isDigit(codePoint)) {
+ if (sb == null) {
+ sb = new StringBuilder(length);
+ sb.append(s.substring(0, i));
+ }
+ sb.appendCodePoint(DIGIT_REPLACEMENT_CODEPOINT);
+ } else {
+ if (sb != null) {
+ sb.appendCodePoint(codePoint);
+ }
+ }
+ }
+ if (sb == null) {
+ return s;
+ } else {
+ return sb.toString();
+ }
+ }
+
+ private static String getUUID(final SharedPreferences prefs) {
+ String uuidString = prefs.getString(PREF_RESEARCH_LOGGER_UUID_STRING, null);
+ if (null == uuidString) {
+ UUID uuid = UUID.randomUUID();
+ uuidString = uuid.toString();
+ Editor editor = prefs.edit();
+ editor.putString(PREF_RESEARCH_LOGGER_UUID_STRING, uuidString);
+ editor.apply();
+ }
+ return uuidString;
+ }
+
+ private String scrubWord(String word) {
+ if (mDictionary == null) {
+ return WORD_REPLACEMENT_STRING;
+ }
+ if (mDictionary.isValidWord(word)) {
+ return word;
+ }
+ return WORD_REPLACEMENT_STRING;
+ }
+
+ private static final String[] EVENTKEYS_LATINIME_ONSTARTINPUTVIEWINTERNAL = {
+ "LatinIMEOnStartInputViewInternal", "uuid", "packageName", "inputType", "imeOptions",
+ "fieldId", "display", "model", "prefs", "versionCode", "versionName", "outputFormatVersion"
+ };
+ public static void latinIME_onStartInputViewInternal(final EditorInfo editorInfo,
+ final SharedPreferences prefs) {
+ final ResearchLogger researchLogger = getInstance();
+ researchLogger.start();
+ if (editorInfo != null) {
+ final Context context = researchLogger.mInputMethodService;
+ try {
+ final PackageInfo packageInfo;
+ packageInfo = context.getPackageManager().getPackageInfo(context.getPackageName(),
+ 0);
+ final Integer versionCode = packageInfo.versionCode;
+ final String versionName = packageInfo.versionName;
+ final Object[] values = {
+ researchLogger.mUUIDString, editorInfo.packageName,
+ Integer.toHexString(editorInfo.inputType),
+ Integer.toHexString(editorInfo.imeOptions), editorInfo.fieldId,
+ Build.DISPLAY, Build.MODEL, prefs, versionCode, versionName,
+ OUTPUT_FORMAT_VERSION
+ };
+ researchLogger.enqueueEvent(EVENTKEYS_LATINIME_ONSTARTINPUTVIEWINTERNAL, values);
+ } catch (NameNotFoundException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ public void latinIME_onFinishInputInternal() {
+ stop();
+ }
+
+ private static final String[] EVENTKEYS_USER_FEEDBACK = {
+ "UserFeedback", "FeedbackContents"
+ };
+
+ private static final String[] EVENTKEYS_PREFS_CHANGED = {
+ "PrefsChanged", "prefs"
+ };
+ public static void prefsChanged(final SharedPreferences prefs) {
+ final ResearchLogger researchLogger = getInstance();
+ final Object[] values = {
+ prefs
+ };
+ researchLogger.enqueueEvent(EVENTKEYS_PREFS_CHANGED, values);
+ }
+
+ // Regular logging methods
+
+ private static final String[] EVENTKEYS_MAINKEYBOARDVIEW_PROCESSMOTIONEVENT = {
+ "MainKeyboardViewProcessMotionEvent", "action", "eventTime", "id", "x", "y", "size",
+ "pressure"
+ };
+ public static void mainKeyboardView_processMotionEvent(final MotionEvent me, final int action,
+ final long eventTime, final int index, final int id, final int x, final int y) {
+ if (me != null) {
+ final String actionString;
+ switch (action) {
+ case MotionEvent.ACTION_CANCEL: actionString = "CANCEL"; break;
+ case MotionEvent.ACTION_UP: actionString = "UP"; break;
+ case MotionEvent.ACTION_DOWN: actionString = "DOWN"; break;
+ case MotionEvent.ACTION_POINTER_UP: actionString = "POINTER_UP"; break;
+ case MotionEvent.ACTION_POINTER_DOWN: actionString = "POINTER_DOWN"; break;
+ case MotionEvent.ACTION_MOVE: actionString = "MOVE"; break;
+ case MotionEvent.ACTION_OUTSIDE: actionString = "OUTSIDE"; break;
+ default: actionString = "ACTION_" + action; break;
+ }
+ final float size = me.getSize(index);
+ final float pressure = me.getPressure(index);
+ final Object[] values = {
+ actionString, eventTime, id, x, y, size, pressure
+ };
+ getInstance().enqueuePotentiallyPrivateEvent(
+ EVENTKEYS_MAINKEYBOARDVIEW_PROCESSMOTIONEVENT, values);
+ }
+ }
+
+ private static final String[] EVENTKEYS_LATINIME_ONCODEINPUT = {
+ "LatinIMEOnCodeInput", "code", "x", "y"
+ };
+ public static void latinIME_onCodeInput(final int code, final int x, final int y) {
+ final long time = SystemClock.uptimeMillis();
+ final ResearchLogger researchLogger = getInstance();
+ final Object[] values = {
+ Keyboard.printableCode(scrubDigitFromCodePoint(code)), x, y
+ };
+ researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_ONCODEINPUT, values);
+ if (Character.isDigit(code)) {
+ researchLogger.setCurrentLogUnitContainsDigitFlag();
+ }
+ researchLogger.mStatistics.recordChar(code, time);
+ }
+
+ private static final String[] EVENTKEYS_LATINIME_ONDISPLAYCOMPLETIONS = {
+ "LatinIMEOnDisplayCompletions", "applicationSpecifiedCompletions"
+ };
+ public static void latinIME_onDisplayCompletions(
+ final CompletionInfo[] applicationSpecifiedCompletions) {
+ final Object[] values = {
+ applicationSpecifiedCompletions
+ };
+ getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_ONDISPLAYCOMPLETIONS,
+ values);
+ }
+
+ public static boolean getAndClearLatinIMEExpectingUpdateSelection() {
+ boolean returnValue = sLatinIMEExpectingUpdateSelection;
+ sLatinIMEExpectingUpdateSelection = false;
+ return returnValue;
+ }
+
+ private static final String[] EVENTKEYS_LATINIME_ONWINDOWHIDDEN = {
+ "LatinIMEOnWindowHidden", "isTextTruncated", "text"
+ };
+ public static void latinIME_onWindowHidden(final int savedSelectionStart,
+ final int savedSelectionEnd, final InputConnection ic) {
+ if (ic != null) {
+ // Capture the TextView contents. This will trigger onUpdateSelection(), so we
+ // set sLatinIMEExpectingUpdateSelection so that when onUpdateSelection() is called,
+ // it can tell that it was generated by the logging code, and not by the user, and
+ // therefore keep user-visible state as is.
+ ic.beginBatchEdit();
+ ic.performContextMenuAction(android.R.id.selectAll);
+ CharSequence charSequence = ic.getSelectedText(0);
+ ic.setSelection(savedSelectionStart, savedSelectionEnd);
+ ic.endBatchEdit();
+ sLatinIMEExpectingUpdateSelection = true;
+ final Object[] values = new Object[2];
+ if (OUTPUT_ENTIRE_BUFFER) {
+ if (TextUtils.isEmpty(charSequence)) {
+ values[0] = false;
+ values[1] = "";
+ } else {
+ if (charSequence.length() > MAX_INPUTVIEW_LENGTH_TO_CAPTURE) {
+ int length = MAX_INPUTVIEW_LENGTH_TO_CAPTURE;
+ // do not cut in the middle of a supplementary character
+ final char c = charSequence.charAt(length - 1);
+ if (Character.isHighSurrogate(c)) {
+ length--;
+ }
+ final CharSequence truncatedCharSequence = charSequence.subSequence(0,
+ length);
+ values[0] = true;
+ values[1] = truncatedCharSequence.toString();
+ } else {
+ values[0] = false;
+ values[1] = charSequence.toString();
+ }
+ }
+ } else {
+ values[0] = true;
+ values[1] = "";
+ }
+ final ResearchLogger researchLogger = getInstance();
+ researchLogger.enqueueEvent(EVENTKEYS_LATINIME_ONWINDOWHIDDEN, values);
+ researchLogger.commitCurrentLogUnit();
+ getInstance().stop();
+ }
+ }
+
+ private static final String[] EVENTKEYS_LATINIME_ONUPDATESELECTION = {
+ "LatinIMEOnUpdateSelection", "lastSelectionStart", "lastSelectionEnd", "oldSelStart",
+ "oldSelEnd", "newSelStart", "newSelEnd", "composingSpanStart", "composingSpanEnd",
+ "expectingUpdateSelection", "expectingUpdateSelectionFromLogger", "context"
+ };
+ public static void latinIME_onUpdateSelection(final int lastSelectionStart,
+ final int lastSelectionEnd, final int oldSelStart, final int oldSelEnd,
+ final int newSelStart, final int newSelEnd, final int composingSpanStart,
+ final int composingSpanEnd, final boolean expectingUpdateSelection,
+ final boolean expectingUpdateSelectionFromLogger,
+ final RichInputConnection connection) {
+ String word = "";
+ if (connection != null) {
+ Range range = connection.getWordRangeAtCursor(WHITESPACE_SEPARATORS, 1);
+ if (range != null) {
+ word = range.mWord;
+ }
+ }
+ final ResearchLogger researchLogger = getInstance();
+ final String scrubbedWord = researchLogger.scrubWord(word);
+ final Object[] values = {
+ lastSelectionStart, lastSelectionEnd, oldSelStart, oldSelEnd, newSelStart,
+ newSelEnd, composingSpanStart, composingSpanEnd, expectingUpdateSelection,
+ expectingUpdateSelectionFromLogger, scrubbedWord
+ };
+ researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_ONUPDATESELECTION, values);
+ }
+
+ private static final String[] EVENTKEYS_LATINIME_PICKSUGGESTIONMANUALLY = {
+ "LatinIMEPickSuggestionManually", "replacedWord", "index", "suggestion", "x", "y"
+ };
+ public static void latinIME_pickSuggestionManually(final String replacedWord,
+ final int index, CharSequence suggestion) {
+ final Object[] values = {
+ scrubDigitsFromString(replacedWord), index,
+ (suggestion == null ? null : scrubDigitsFromString(suggestion.toString())),
+ Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE
+ };
+ final ResearchLogger researchLogger = getInstance();
+ researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_PICKSUGGESTIONMANUALLY,
+ values);
+ }
+
+ private static final String[] EVENTKEYS_LATINIME_PUNCTUATIONSUGGESTION = {
+ "LatinIMEPunctuationSuggestion", "index", "suggestion", "x", "y"
+ };
+ public static void latinIME_punctuationSuggestion(final int index,
+ final CharSequence suggestion) {
+ final Object[] values = {
+ index, suggestion,
+ Constants.SUGGESTION_STRIP_COORDINATE, Constants.SUGGESTION_STRIP_COORDINATE
+ };
+ getInstance().enqueueEvent(EVENTKEYS_LATINIME_PUNCTUATIONSUGGESTION, values);
+ }
+
+ private static final String[] EVENTKEYS_LATINIME_SENDKEYCODEPOINT = {
+ "LatinIMESendKeyCodePoint", "code"
+ };
+ public static void latinIME_sendKeyCodePoint(final int code) {
+ final Object[] values = {
+ Keyboard.printableCode(scrubDigitFromCodePoint(code))
+ };
+ final ResearchLogger researchLogger = getInstance();
+ researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_SENDKEYCODEPOINT, values);
+ if (Character.isDigit(code)) {
+ researchLogger.setCurrentLogUnitContainsDigitFlag();
+ }
+ }
+
+ private static final String[] EVENTKEYS_LATINIME_SWAPSWAPPERANDSPACE = {
+ "LatinIMESwapSwapperAndSpace"
+ };
+ public static void latinIME_swapSwapperAndSpace() {
+ getInstance().enqueueEvent(EVENTKEYS_LATINIME_SWAPSWAPPERANDSPACE, EVENTKEYS_NULLVALUES);
+ }
+
+ private static final String[] EVENTKEYS_MAINKEYBOARDVIEW_ONLONGPRESS = {
+ "MainKeyboardViewOnLongPress"
+ };
+ public static void mainKeyboardView_onLongPress() {
+ getInstance().enqueueEvent(EVENTKEYS_MAINKEYBOARDVIEW_ONLONGPRESS, EVENTKEYS_NULLVALUES);
+ }
+
+ private static final String[] EVENTKEYS_MAINKEYBOARDVIEW_SETKEYBOARD = {
+ "MainKeyboardViewSetKeyboard", "elementId", "locale", "orientation", "width",
+ "modeName", "action", "navigateNext", "navigatePrevious", "clobberSettingsKey",
+ "passwordInput", "shortcutKeyEnabled", "hasShortcutKey", "languageSwitchKeyEnabled",
+ "isMultiLine", "tw", "th", "keys"
+ };
+ public static void mainKeyboardView_setKeyboard(final Keyboard keyboard) {
+ if (keyboard != null) {
+ final KeyboardId kid = keyboard.mId;
+ final boolean isPasswordView = kid.passwordInput();
+ getInstance().setIsPasswordView(isPasswordView);
+ final Object[] values = {
+ KeyboardId.elementIdToName(kid.mElementId),
+ kid.mLocale + ":" + kid.mSubtype.getExtraValueOf(KEYBOARD_LAYOUT_SET),
+ kid.mOrientation,
+ kid.mWidth,
+ KeyboardId.modeName(kid.mMode),
+ kid.imeAction(),
+ kid.navigateNext(),
+ kid.navigatePrevious(),
+ kid.mClobberSettingsKey,
+ isPasswordView,
+ kid.mShortcutKeyEnabled,
+ kid.mHasShortcutKey,
+ kid.mLanguageSwitchKeyEnabled,
+ kid.isMultiLine(),
+ keyboard.mOccupiedWidth,
+ keyboard.mOccupiedHeight,
+ keyboard.mKeys
+ };
+ getInstance().setIsPasswordView(isPasswordView);
+ getInstance().enqueueEvent(EVENTKEYS_MAINKEYBOARDVIEW_SETKEYBOARD, values);
+ }
+ }
+
+ private static final String[] EVENTKEYS_LATINIME_REVERTCOMMIT = {
+ "LatinIMERevertCommit", "originallyTypedWord"
+ };
+ public static void latinIME_revertCommit(final String originallyTypedWord) {
+ final Object[] values = {
+ originallyTypedWord
+ };
+ getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_LATINIME_REVERTCOMMIT, values);
+ }
+
+ private static final String[] EVENTKEYS_POINTERTRACKER_CALLLISTENERONCANCELINPUT = {
+ "PointerTrackerCallListenerOnCancelInput"
+ };
+ public static void pointerTracker_callListenerOnCancelInput() {
+ getInstance().enqueueEvent(EVENTKEYS_POINTERTRACKER_CALLLISTENERONCANCELINPUT,
+ EVENTKEYS_NULLVALUES);
+ }
+
+ private static final String[] EVENTKEYS_POINTERTRACKER_CALLLISTENERONCODEINPUT = {
+ "PointerTrackerCallListenerOnCodeInput", "code", "outputText", "x", "y",
+ "ignoreModifierKey", "altersCode", "isEnabled"
+ };
+ public static void pointerTracker_callListenerOnCodeInput(final Key key, final int x,
+ final int y, final boolean ignoreModifierKey, final boolean altersCode,
+ final int code) {
+ if (key != null) {
+ String outputText = key.getOutputText();
+ final Object[] values = {
+ Keyboard.printableCode(scrubDigitFromCodePoint(code)), outputText == null ? null
+ : scrubDigitsFromString(outputText.toString()),
+ x, y, ignoreModifierKey, altersCode, key.isEnabled()
+ };
+ getInstance().enqueuePotentiallyPrivateEvent(
+ EVENTKEYS_POINTERTRACKER_CALLLISTENERONCODEINPUT, values);
+ }
+ }
+
+ private static final String[] EVENTKEYS_POINTERTRACKER_CALLLISTENERONRELEASE = {
+ "PointerTrackerCallListenerOnRelease", "code", "withSliding", "ignoreModifierKey",
+ "isEnabled"
+ };
+ public static void pointerTracker_callListenerOnRelease(final Key key, final int primaryCode,
+ final boolean withSliding, final boolean ignoreModifierKey) {
+ if (key != null) {
+ final Object[] values = {
+ Keyboard.printableCode(scrubDigitFromCodePoint(primaryCode)), withSliding,
+ ignoreModifierKey, key.isEnabled()
+ };
+ getInstance().enqueuePotentiallyPrivateEvent(
+ EVENTKEYS_POINTERTRACKER_CALLLISTENERONRELEASE, values);
+ }
+ }
+
+ private static final String[] EVENTKEYS_POINTERTRACKER_ONDOWNEVENT = {
+ "PointerTrackerOnDownEvent", "deltaT", "distanceSquared"
+ };
+ public static void pointerTracker_onDownEvent(long deltaT, int distanceSquared) {
+ final Object[] values = {
+ deltaT, distanceSquared
+ };
+ getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_POINTERTRACKER_ONDOWNEVENT, values);
+ }
+
+ private static final String[] EVENTKEYS_POINTERTRACKER_ONMOVEEVENT = {
+ "PointerTrackerOnMoveEvent", "x", "y", "lastX", "lastY"
+ };
+ public static void pointerTracker_onMoveEvent(final int x, final int y, final int lastX,
+ final int lastY) {
+ final Object[] values = {
+ x, y, lastX, lastY
+ };
+ getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_POINTERTRACKER_ONMOVEEVENT, values);
+ }
+
+ private static final String[] EVENTKEYS_RICHINPUTCONNECTION_COMMITCOMPLETION = {
+ "RichInputConnectionCommitCompletion", "completionInfo"
+ };
+ public static void richInputConnection_commitCompletion(final CompletionInfo completionInfo) {
+ final Object[] values = {
+ completionInfo
+ };
+ final ResearchLogger researchLogger = getInstance();
+ researchLogger.enqueuePotentiallyPrivateEvent(
+ EVENTKEYS_RICHINPUTCONNECTION_COMMITCOMPLETION, values);
+ }
+
+ // Disabled for privacy-protection reasons. Because this event comes after
+ // richInputConnection_commitText, which is the event used to separate LogUnits, the
+ // data in this event can be associated with the next LogUnit, revealing information
+ // about the current word even if it was supposed to be suppressed. The occurrance of
+ // autocorrection can be determined by examining the difference between the text strings in
+ // the last call to richInputConnection_setComposingText before
+ // richInputConnection_commitText, so it's not a data loss.
+ // TODO: Figure out how to log this event without loss of privacy.
+ /*
+ private static final String[] EVENTKEYS_RICHINPUTCONNECTION_COMMITCORRECTION = {
+ "RichInputConnectionCommitCorrection", "typedWord", "autoCorrection"
+ };
+ */
+ public static void richInputConnection_commitCorrection(CorrectionInfo correctionInfo) {
+ /*
+ final String typedWord = correctionInfo.getOldText().toString();
+ final String autoCorrection = correctionInfo.getNewText().toString();
+ final Object[] values = {
+ scrubDigitsFromString(typedWord), scrubDigitsFromString(autoCorrection)
+ };
+ final ResearchLogger researchLogger = getInstance();
+ researchLogger.enqueuePotentiallyPrivateEvent(
+ EVENTKEYS_RICHINPUTCONNECTION_COMMITCORRECTION, values);
+ */
+ }
+
+ private static final String[] EVENTKEYS_RICHINPUTCONNECTION_COMMITTEXT = {
+ "RichInputConnectionCommitText", "typedWord", "newCursorPosition"
+ };
+ public static void richInputConnection_commitText(final CharSequence typedWord,
+ final int newCursorPosition) {
+ final String scrubbedWord = scrubDigitsFromString(typedWord.toString());
+ final Object[] values = {
+ scrubbedWord, newCursorPosition
+ };
+ final ResearchLogger researchLogger = getInstance();
+ researchLogger.enqueuePotentiallyPrivateEvent(EVENTKEYS_RICHINPUTCONNECTION_COMMITTEXT,
+ values);
+ researchLogger.onWordComplete(scrubbedWord);
+ }
+
+ private static final String[] EVENTKEYS_RICHINPUTCONNECTION_DELETESURROUNDINGTEXT = {
+ "RichInputConnectionDeleteSurroundingText", "beforeLength", "afterLength"
+ };
+ public static void richInputConnection_deleteSurroundingText(final int beforeLength,
+ final int afterLength) {
+ final Object[] values = {
+ beforeLength, afterLength
+ };
+ getInstance().enqueuePotentiallyPrivateEvent(
+ EVENTKEYS_RICHINPUTCONNECTION_DELETESURROUNDINGTEXT, values);
+ }
+
+ private static final String[] EVENTKEYS_RICHINPUTCONNECTION_FINISHCOMPOSINGTEXT = {
+ "RichInputConnectionFinishComposingText"
+ };
+ public static void richInputConnection_finishComposingText() {
+ getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_FINISHCOMPOSINGTEXT,
+ EVENTKEYS_NULLVALUES);
+ }
+
+ private static final String[] EVENTKEYS_RICHINPUTCONNECTION_PERFORMEDITORACTION = {
+ "RichInputConnectionPerformEditorAction", "imeActionNext"
+ };
+ public static void richInputConnection_performEditorAction(final int imeActionNext) {
+ final Object[] values = {
+ imeActionNext
+ };
+ getInstance().enqueueEvent(EVENTKEYS_RICHINPUTCONNECTION_PERFORMEDITORACTION, values);
+ }
+
+ private static final String[] EVENTKEYS_RICHINPUTCONNECTION_SENDKEYEVENT = {
+ "RichInputConnectionSendKeyEvent", "eventTime", "action", "code"
+ };
+ public static void richInputConnection_sendKeyEvent(final KeyEvent keyEvent) {
+ final Object[] values = {
+ keyEvent.getEventTime(),
+ keyEvent.getAction(),
+ keyEvent.getKeyCode()
+ };
+ getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_RICHINPUTCONNECTION_SENDKEYEVENT,
+ values);
+ }
+
+ private static final String[] EVENTKEYS_RICHINPUTCONNECTION_SETCOMPOSINGTEXT = {
+ "RichInputConnectionSetComposingText", "text", "newCursorPosition"
+ };
+ public static void richInputConnection_setComposingText(final CharSequence text,
+ final int newCursorPosition) {
+ if (text == null) {
+ throw new RuntimeException("setComposingText is null");
+ }
+ final Object[] values = {
+ text, newCursorPosition
+ };
+ getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_RICHINPUTCONNECTION_SETCOMPOSINGTEXT,
+ values);
+ }
+
+ private static final String[] EVENTKEYS_RICHINPUTCONNECTION_SETSELECTION = {
+ "RichInputConnectionSetSelection", "from", "to"
+ };
+ public static void richInputConnection_setSelection(final int from, final int to) {
+ final Object[] values = {
+ from, to
+ };
+ getInstance().enqueuePotentiallyPrivateEvent(EVENTKEYS_RICHINPUTCONNECTION_SETSELECTION,
+ values);
+ }
+
+ private static final String[] EVENTKEYS_SUDDENJUMPINGTOUCHEVENTHANDLER_ONTOUCHEVENT = {
+ "SuddenJumpingTouchEventHandlerOnTouchEvent", "motionEvent"
+ };
+ public static void suddenJumpingTouchEventHandler_onTouchEvent(final MotionEvent me) {
+ if (me != null) {
+ final Object[] values = {
+ me.toString()
+ };
+ getInstance().enqueuePotentiallyPrivateEvent(
+ EVENTKEYS_SUDDENJUMPINGTOUCHEVENTHANDLER_ONTOUCHEVENT, values);
+ }
+ }
+
+ private static final String[] EVENTKEYS_SUGGESTIONSTRIPVIEW_SETSUGGESTIONS = {
+ "SuggestionStripViewSetSuggestions", "suggestedWords"
+ };
+ public static void suggestionStripView_setSuggestions(final SuggestedWords suggestedWords) {
+ if (suggestedWords != null) {
+ final Object[] values = {
+ suggestedWords
+ };
+ getInstance().enqueuePotentiallyPrivateEvent(
+ EVENTKEYS_SUGGESTIONSTRIPVIEW_SETSUGGESTIONS, values);
+ }
+ }
+
+ private static final String[] EVENTKEYS_USER_TIMESTAMP = {
+ "UserTimestamp"
+ };
+ public void userTimestamp() {
+ getInstance().enqueueEvent(EVENTKEYS_USER_TIMESTAMP, EVENTKEYS_NULLVALUES);
+ }
+
+ private static final String[] EVENTKEYS_STATISTICS = {
+ "Statistics", "charCount", "letterCount", "numberCount", "spaceCount", "deleteOpsCount",
+ "wordCount", "isEmptyUponStarting", "isEmptinessStateKnown", "averageTimeBetweenKeys",
+ "averageTimeBeforeDelete", "averageTimeDuringRepeatedDelete", "averageTimeAfterDelete"
+ };
+ private static void logStatistics() {
+ final ResearchLogger researchLogger = getInstance();
+ final Statistics statistics = researchLogger.mStatistics;
+ final Object[] values = {
+ statistics.mCharCount, statistics.mLetterCount, statistics.mNumberCount,
+ statistics.mSpaceCount, statistics.mDeleteKeyCount,
+ statistics.mWordCount, statistics.mIsEmptyUponStarting,
+ statistics.mIsEmptinessStateKnown, statistics.mKeyCounter.getAverageTime(),
+ statistics.mBeforeDeleteKeyCounter.getAverageTime(),
+ statistics.mDuringRepeatedDeleteKeysCounter.getAverageTime(),
+ statistics.mAfterDeleteKeyCounter.getAverageTime()
+ };
+ researchLogger.enqueueEvent(EVENTKEYS_STATISTICS, values);
+ }
+}
diff --git a/java/src/com/android/inputmethod/research/Statistics.java b/java/src/com/android/inputmethod/research/Statistics.java
new file mode 100644
index 000000000..eab465aa2
--- /dev/null
+++ b/java/src/com/android/inputmethod/research/Statistics.java
@@ -0,0 +1,146 @@
+/*
+ * 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.research;
+
+import com.android.inputmethod.keyboard.Keyboard;
+
+public class Statistics {
+ // Number of characters entered during a typing session
+ int mCharCount;
+ // Number of letter characters entered during a typing session
+ int mLetterCount;
+ // Number of number characters entered
+ int mNumberCount;
+ // Number of space characters entered
+ int mSpaceCount;
+ // Number of delete operations entered (taps on the backspace key)
+ int mDeleteKeyCount;
+ // Number of words entered during a session.
+ int mWordCount;
+ // Whether the text field was empty upon editing
+ boolean mIsEmptyUponStarting;
+ boolean mIsEmptinessStateKnown;
+
+ // Timers to count average time to enter a key, first press a delete key,
+ // between delete keys, and then to return typing after a delete key.
+ final AverageTimeCounter mKeyCounter = new AverageTimeCounter();
+ final AverageTimeCounter mBeforeDeleteKeyCounter = new AverageTimeCounter();
+ final AverageTimeCounter mDuringRepeatedDeleteKeysCounter = new AverageTimeCounter();
+ final AverageTimeCounter mAfterDeleteKeyCounter = new AverageTimeCounter();
+
+ static class AverageTimeCounter {
+ int mCount;
+ int mTotalTime;
+
+ public void reset() {
+ mCount = 0;
+ mTotalTime = 0;
+ }
+
+ public void add(long deltaTime) {
+ mCount++;
+ mTotalTime += deltaTime;
+ }
+
+ public int getAverageTime() {
+ if (mCount == 0) {
+ return 0;
+ }
+ return mTotalTime / mCount;
+ }
+ }
+
+ // To account for the interruptions when the user's attention is directed elsewhere, times
+ // longer than MIN_TYPING_INTERMISSION are not counted when estimating this statistic.
+ public static final int MIN_TYPING_INTERMISSION = 2 * 1000; // in milliseconds
+ public static final int MIN_DELETION_INTERMISSION = 10 * 1000; // in milliseconds
+
+ // The last time that a tap was performed
+ private long mLastTapTime;
+ // The type of the last keypress (delete key or not)
+ boolean mIsLastKeyDeleteKey;
+
+ private static final Statistics sInstance = new Statistics();
+
+ public static Statistics getInstance() {
+ return sInstance;
+ }
+
+ private Statistics() {
+ reset();
+ }
+
+ public void reset() {
+ mCharCount = 0;
+ mLetterCount = 0;
+ mNumberCount = 0;
+ mSpaceCount = 0;
+ mDeleteKeyCount = 0;
+ mWordCount = 0;
+ mIsEmptyUponStarting = true;
+ mIsEmptinessStateKnown = false;
+ mKeyCounter.reset();
+ mBeforeDeleteKeyCounter.reset();
+ mDuringRepeatedDeleteKeysCounter.reset();
+ mAfterDeleteKeyCounter.reset();
+
+ mLastTapTime = 0;
+ mIsLastKeyDeleteKey = false;
+ }
+
+ public void recordChar(int codePoint, long time) {
+ final long delta = time - mLastTapTime;
+ if (codePoint == Keyboard.CODE_DELETE) {
+ mDeleteKeyCount++;
+ if (delta < MIN_DELETION_INTERMISSION) {
+ if (mIsLastKeyDeleteKey) {
+ mDuringRepeatedDeleteKeysCounter.add(delta);
+ } else {
+ mBeforeDeleteKeyCounter.add(delta);
+ }
+ }
+ mIsLastKeyDeleteKey = true;
+ } else {
+ mCharCount++;
+ if (Character.isDigit(codePoint)) {
+ mNumberCount++;
+ }
+ if (Character.isLetter(codePoint)) {
+ mLetterCount++;
+ }
+ if (Character.isSpaceChar(codePoint)) {
+ mSpaceCount++;
+ }
+ if (mIsLastKeyDeleteKey && delta < MIN_DELETION_INTERMISSION) {
+ mAfterDeleteKeyCounter.add(delta);
+ } else if (!mIsLastKeyDeleteKey && delta < MIN_TYPING_INTERMISSION) {
+ mKeyCounter.add(delta);
+ }
+ mIsLastKeyDeleteKey = false;
+ }
+ mLastTapTime = time;
+ }
+
+ public void recordWordEntered() {
+ mWordCount++;
+ }
+
+ public void setIsEmptyUponStarting(final boolean isEmpty) {
+ mIsEmptyUponStarting = isEmpty;
+ mIsEmptinessStateKnown = true;
+ }
+}
diff --git a/java/src/com/android/inputmethod/research/UploaderService.java b/java/src/com/android/inputmethod/research/UploaderService.java
new file mode 100644
index 000000000..7a5749096
--- /dev/null
+++ b/java/src/com/android/inputmethod/research/UploaderService.java
@@ -0,0 +1,191 @@
+/*
+ * 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.research;
+
+import android.Manifest;
+import android.app.AlarmManager;
+import android.app.IntentService;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.os.BatteryManager;
+import android.os.Bundle;
+import android.util.Log;
+
+import com.android.inputmethod.latin.R;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+public final class UploaderService extends IntentService {
+ private static final String TAG = UploaderService.class.getSimpleName();
+ public static final long RUN_INTERVAL = AlarmManager.INTERVAL_HOUR;
+ private static final String EXTRA_UPLOAD_UNCONDITIONALLY = UploaderService.class.getName()
+ + ".extra.UPLOAD_UNCONDITIONALLY";
+ private static final int BUF_SIZE = 1024 * 8;
+ protected static final int TIMEOUT_IN_MS = 1000 * 4;
+
+ private boolean mCanUpload;
+ private File mFilesDir;
+ private URL mUrl;
+
+ public UploaderService() {
+ super("Research Uploader Service");
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ mCanUpload = false;
+ mFilesDir = null;
+ mUrl = null;
+
+ final PackageManager packageManager = getPackageManager();
+ final boolean hasPermission = packageManager.checkPermission(Manifest.permission.INTERNET,
+ getPackageName()) == PackageManager.PERMISSION_GRANTED;
+ if (!hasPermission) {
+ return;
+ }
+
+ try {
+ final String urlString = getString(R.string.research_logger_upload_url);
+ if (urlString == null || urlString.equals("")) {
+ return;
+ }
+ mFilesDir = getFilesDir();
+ mUrl = new URL(urlString);
+ mCanUpload = true;
+ } catch (MalformedURLException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ protected void onHandleIntent(Intent intent) {
+ if (!mCanUpload) {
+ return;
+ }
+ boolean isUploadingUnconditionally = false;
+ Bundle bundle = intent.getExtras();
+ if (bundle != null && bundle.containsKey(EXTRA_UPLOAD_UNCONDITIONALLY)) {
+ isUploadingUnconditionally = bundle.getBoolean(EXTRA_UPLOAD_UNCONDITIONALLY);
+ }
+ doUpload(isUploadingUnconditionally);
+ }
+
+ private boolean isExternallyPowered() {
+ final Intent intent = registerReceiver(null, new IntentFilter(
+ Intent.ACTION_BATTERY_CHANGED));
+ final int pluggedState = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
+ return pluggedState == BatteryManager.BATTERY_PLUGGED_AC
+ || pluggedState == BatteryManager.BATTERY_PLUGGED_USB;
+ }
+
+ private boolean hasWifiConnection() {
+ final ConnectivityManager manager =
+ (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
+ final NetworkInfo wifiInfo = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
+ return wifiInfo.isConnected();
+ }
+
+ private void doUpload(final boolean isUploadingUnconditionally) {
+ if (!isUploadingUnconditionally && (!isExternallyPowered() || !hasWifiConnection())) {
+ return;
+ }
+ if (mFilesDir == null) {
+ return;
+ }
+ final File[] files = mFilesDir.listFiles(new FileFilter() {
+ @Override
+ public boolean accept(File pathname) {
+ return pathname.getName().startsWith(ResearchLogger.FILENAME_PREFIX)
+ && !pathname.canWrite();
+ }
+ });
+ boolean success = true;
+ if (files.length == 0) {
+ success = false;
+ }
+ for (final File file : files) {
+ if (!uploadFile(file)) {
+ success = false;
+ }
+ }
+ }
+
+ private boolean uploadFile(File file) {
+ Log.d(TAG, "attempting upload of " + file.getAbsolutePath());
+ boolean success = false;
+ final int contentLength = (int) file.length();
+ HttpURLConnection connection = null;
+ InputStream fileInputStream = null;
+ try {
+ fileInputStream = new FileInputStream(file);
+ connection = (HttpURLConnection) mUrl.openConnection();
+ connection.setRequestMethod("PUT");
+ connection.setDoOutput(true);
+ connection.setFixedLengthStreamingMode(contentLength);
+ final OutputStream os = connection.getOutputStream();
+ final byte[] buf = new byte[BUF_SIZE];
+ int numBytesRead;
+ while ((numBytesRead = fileInputStream.read(buf)) != -1) {
+ os.write(buf, 0, numBytesRead);
+ }
+ if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
+ Log.d(TAG, "upload failed: " + connection.getResponseCode());
+ InputStream netInputStream = connection.getInputStream();
+ BufferedReader reader = new BufferedReader(new InputStreamReader(netInputStream));
+ String line;
+ while ((line = reader.readLine()) != null) {
+ Log.d(TAG, "| " + reader.readLine());
+ }
+ reader.close();
+ return success;
+ }
+ file.delete();
+ success = true;
+ Log.d(TAG, "upload successful");
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ if (fileInputStream != null) {
+ try {
+ fileInputStream.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ if (connection != null) {
+ connection.disconnect();
+ }
+ }
+ return success;
+ }
+}