aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xjava/Android.mk13
-rwxr-xr-xjava/AndroidManifest.xml10
-rw-r--r--java/proguard.flags5
-rwxr-xr-xjava/res/drawable-en-hdpi/sym_keyboard_feedback_delete.pngbin1278 -> 0 bytes
-rw-r--r--java/res/drawable-hdpi/btn_close_normal.pngbin0 -> 1884 bytes
-rw-r--r--java/res/drawable-hdpi/btn_close_pressed.pngbin0 -> 2737 bytes
-rw-r--r--java/res/drawable-hdpi/btn_close_selected.pngbin0 -> 2807 bytes
-rw-r--r--java/res/drawable-hdpi/btn_keyboard_key_normal_off_stone.9.pngbin0 -> 2348 bytes
-rw-r--r--java/res/drawable-hdpi/btn_keyboard_key_normal_on_stone.9.pngbin0 -> 2379 bytes
-rw-r--r--java/res/drawable-hdpi/btn_keyboard_key_normal_stone.9.pngbin0 -> 2211 bytes
-rw-r--r--java/res/drawable-hdpi/btn_keyboard_normal_metal.9.pngbin0 -> 7614 bytes
-rw-r--r--java/res/drawable-hdpi/btn_keyboard_toggle_off.pngbin0 -> 1425 bytes
-rw-r--r--java/res/drawable-hdpi/btn_keyboard_toggle_on.pngbin0 -> 1733 bytes
-rw-r--r--java/res/drawable-hdpi/btn_led_off.9.pngbin0 -> 1279 bytes
-rw-r--r--java/res/drawable-hdpi/btn_led_on.9.pngbin0 -> 1607 bytes
-rw-r--r--java/res/drawable-hdpi/dialog_top_dark_bottom_medium.9.pngbin0 -> 49933 bytes
-rwxr-xr-xjava/res/drawable-hdpi/dialog_top_dark_bottom_medium.pngbin1976 -> 0 bytes
-rw-r--r--java/res/drawable-hdpi/keyboard_key_feedback_background.9.pngbin0 -> 1372 bytes
-rw-r--r--java/res/drawable-hdpi/keyboard_key_feedback_more_background.9.pngbin0 -> 1637 bytes
-rw-r--r--java/res/drawable-hdpi/keyboard_popup_panel_background.9.pngbin0 -> 1443 bytes
-rw-r--r--java/res/drawable-hdpi/keyboard_popup_panel_trans_background.9.pngbin0 -> 1677 bytes
-rw-r--r--java/res/drawable-hdpi/sym_bkeyboard_123_mic.pngbin0 -> 2576 bytes
-rw-r--r--java/res/drawable-hdpi/sym_bkeyboard_delete.pngbin0 -> 2314 bytes
-rw-r--r--java/res/drawable-hdpi/sym_bkeyboard_done.pngbin0 -> 1588 bytes
-rw-r--r--java/res/drawable-hdpi/sym_bkeyboard_globe.pngbin0 -> 2136 bytes
-rw-r--r--java/res/drawable-hdpi/sym_bkeyboard_mic.pngbin0 -> 1410 bytes
-rw-r--r--java/res/drawable-hdpi/sym_bkeyboard_num0.pngbin0 -> 1903 bytes
-rw-r--r--java/res/drawable-hdpi/sym_bkeyboard_num1.pngbin0 -> 792 bytes
-rw-r--r--java/res/drawable-hdpi/sym_bkeyboard_num2.pngbin0 -> 3241 bytes
-rw-r--r--java/res/drawable-hdpi/sym_bkeyboard_num3.pngbin0 -> 2829 bytes
-rw-r--r--java/res/drawable-hdpi/sym_bkeyboard_num4.pngbin0 -> 2638 bytes
-rw-r--r--java/res/drawable-hdpi/sym_bkeyboard_num5.pngbin0 -> 2532 bytes
-rw-r--r--java/res/drawable-hdpi/sym_bkeyboard_num6.pngbin0 -> 3568 bytes
-rw-r--r--java/res/drawable-hdpi/sym_bkeyboard_num7.pngbin0 -> 3687 bytes
-rw-r--r--java/res/drawable-hdpi/sym_bkeyboard_num8.pngbin0 -> 2952 bytes
-rw-r--r--java/res/drawable-hdpi/sym_bkeyboard_num9.pngbin0 -> 3887 bytes
-rw-r--r--java/res/drawable-hdpi/sym_bkeyboard_numalt.pngbin0 -> 2971 bytes
-rw-r--r--java/res/drawable-hdpi/sym_bkeyboard_numpound.pngbin0 -> 1577 bytes
-rw-r--r--java/res/drawable-hdpi/sym_bkeyboard_numstar.pngbin0 -> 1742 bytes
-rw-r--r--java/res/drawable-hdpi/sym_bkeyboard_return.pngbin0 -> 1111 bytes
-rw-r--r--java/res/drawable-hdpi/sym_bkeyboard_search.pngbin0 -> 1612 bytes
-rw-r--r--java/res/drawable-hdpi/sym_bkeyboard_shift.pngbin0 -> 1474 bytes
-rw-r--r--java/res/drawable-hdpi/sym_bkeyboard_shift_locked.pngbin0 -> 1115 bytes
-rw-r--r--java/res/drawable-hdpi/sym_bkeyboard_space.pngbin0 -> 358 bytes
-rw-r--r--java/res/drawable-hdpi/sym_bkeyboard_tab.pngbin0 -> 1008 bytes
-rw-r--r--java/res/drawable-hdpi/sym_bkeyboard_tabprev.pngbin0 -> 1026 bytes
-rw-r--r--java/res/drawable-hdpi/voice_swipe_hint.pngbin0 -> 5965 bytes
-rw-r--r--java/res/drawable-land-hdpi/btn_keyboard_key_normal_off_stone.9.pngbin0 -> 2691 bytes
-rw-r--r--java/res/drawable-land-hdpi/btn_keyboard_key_normal_on_stone.9.pngbin0 -> 2720 bytes
-rw-r--r--java/res/drawable-land-hdpi/btn_keyboard_key_normal_stone.9.pngbin0 -> 2517 bytes
-rw-r--r--java/res/drawable-land-mdpi/btn_keyboard_key_normal_off.9.pngbin860 -> 0 bytes
-rw-r--r--java/res/drawable-land-mdpi/btn_keyboard_key_normal_off_stone.9.pngbin0 -> 2691 bytes
-rw-r--r--java/res/drawable-land-mdpi/btn_keyboard_key_normal_on.9.pngbin926 -> 0 bytes
-rw-r--r--java/res/drawable-land-mdpi/btn_keyboard_key_normal_on_stone.9.pngbin0 -> 2720 bytes
-rw-r--r--java/res/drawable-land-mdpi/btn_keyboard_key_normal_stone.9.pngbin0 -> 2517 bytes
-rw-r--r--java/res/drawable-land-mdpi/btn_keyboard_key_pressed_off.9.pngbin836 -> 0 bytes
-rw-r--r--java/res/drawable-land-mdpi/btn_keyboard_key_pressed_on.9.pngbin886 -> 0 bytes
-rw-r--r--java/res/drawable-mdpi/btn_close_normal.pngbin0 -> 1259 bytes
-rw-r--r--java/res/drawable-mdpi/btn_close_pressed.pngbin0 -> 1726 bytes
-rw-r--r--java/res/drawable-mdpi/btn_close_selected.pngbin0 -> 1716 bytes
-rw-r--r--java/res/drawable-mdpi/btn_keyboard_key_normal_off_stone.9.pngbin0 -> 2348 bytes
-rw-r--r--java/res/drawable-mdpi/btn_keyboard_key_normal_on_stone.9.pngbin0 -> 2379 bytes
-rw-r--r--java/res/drawable-mdpi/btn_keyboard_key_normal_stone.9.pngbin0 -> 2211 bytes
-rw-r--r--java/res/drawable-mdpi/btn_keyboard_normal_metal.9.pngbin0 -> 5069 bytes
-rw-r--r--java/res/drawable-mdpi/btn_keyboard_toggle_off.pngbin0 -> 693 bytes
-rw-r--r--java/res/drawable-mdpi/btn_keyboard_toggle_on.pngbin0 -> 812 bytes
-rw-r--r--java/res/drawable-mdpi/btn_led_off.9.pngbin0 -> 505 bytes
-rw-r--r--java/res/drawable-mdpi/btn_led_on.9.pngbin0 -> 575 bytes
-rw-r--r--java/res/drawable-mdpi/cancel.png (renamed from java/res/drawable/cancel.png)bin1259 -> 1259 bytes
-rw-r--r--java/res/drawable-mdpi/caution.png (renamed from java/res/drawable/caution.png)bin1100 -> 1100 bytes
-rw-r--r--java/res/drawable-mdpi/dialog_top_dark_bottom_medium.9.png (renamed from java/res/drawable/dialog_top_dark_bottom_medium.9.png)bin1574 -> 1574 bytes
-rw-r--r--java/res/drawable-mdpi/ic_dialog_alert_large.png (renamed from java/res/drawable/ic_dialog_alert_large.png)bin4089 -> 4089 bytes
-rw-r--r--java/res/drawable-mdpi/ic_dialog_voice_input.png (renamed from java/res/drawable/ic_dialog_voice_input.png)bin1040 -> 1040 bytes
-rw-r--r--java/res/drawable-mdpi/ic_dialog_wave_0_0.png (renamed from java/res/drawable/ic_dialog_wave_0_0.png)bin8471 -> 8471 bytes
-rw-r--r--java/res/drawable-mdpi/ic_dialog_wave_1_3.png (renamed from java/res/drawable/ic_dialog_wave_1_3.png)bin9017 -> 9017 bytes
-rw-r--r--java/res/drawable-mdpi/ic_dialog_wave_2_3.png (renamed from java/res/drawable/ic_dialog_wave_2_3.png)bin9614 -> 9614 bytes
-rw-r--r--java/res/drawable-mdpi/ic_dialog_wave_3_3.png (renamed from java/res/drawable/ic_dialog_wave_3_3.png)bin10089 -> 10089 bytes
-rw-r--r--java/res/drawable-mdpi/ic_dialog_wave_4_3.png (renamed from java/res/drawable/ic_dialog_wave_4_3.png)bin10514 -> 10514 bytes
-rw-r--r--java/res/drawable-mdpi/keyboard_key_feedback_background.9.pngbin0 -> 1182 bytes
-rwxr-xr-xjava/res/drawable-mdpi/keyboard_key_feedback_more_background.9.pngbin0 -> 1385 bytes
-rw-r--r--java/res/drawable-mdpi/keyboard_popup_panel_background.9.pngbin0 -> 996 bytes
-rw-r--r--java/res/drawable-mdpi/keyboard_popup_panel_trans_background.9.pngbin0 -> 3734 bytes
-rw-r--r--java/res/drawable-mdpi/mic_slash.png (renamed from java/res/drawable/mic_slash.png)bin3098 -> 3098 bytes
-rw-r--r--java/res/drawable-mdpi/ok_cancel.png (renamed from java/res/drawable/ok_cancel.png)bin8453 -> 8453 bytes
-rw-r--r--java/res/drawable-mdpi/speak_now_level0.png (renamed from java/res/drawable/speak_now_level0.png)bin3263 -> 3263 bytes
-rw-r--r--java/res/drawable-mdpi/speak_now_level1.png (renamed from java/res/drawable/speak_now_level1.png)bin3572 -> 3572 bytes
-rw-r--r--java/res/drawable-mdpi/speak_now_level2.png (renamed from java/res/drawable/speak_now_level2.png)bin3974 -> 3974 bytes
-rw-r--r--java/res/drawable-mdpi/speak_now_level3.png (renamed from java/res/drawable/speak_now_level3.png)bin4270 -> 4270 bytes
-rw-r--r--java/res/drawable-mdpi/speak_now_level4.png (renamed from java/res/drawable/speak_now_level4.png)bin4241 -> 4241 bytes
-rw-r--r--java/res/drawable-mdpi/speak_now_level5.png (renamed from java/res/drawable/speak_now_level5.png)bin4252 -> 4252 bytes
-rw-r--r--java/res/drawable-mdpi/speak_now_level6.png (renamed from java/res/drawable/speak_now_level6.png)bin2201 -> 2201 bytes
-rw-r--r--java/res/drawable-mdpi/sym_bkeyboard_123_mic.pngbin0 -> 1520 bytes
-rw-r--r--java/res/drawable-mdpi/sym_bkeyboard_delete.pngbin0 -> 800 bytes
-rw-r--r--java/res/drawable-mdpi/sym_bkeyboard_done.pngbin0 -> 775 bytes
-rw-r--r--java/res/drawable-mdpi/sym_bkeyboard_globe.pngbin0 -> 1303 bytes
-rw-r--r--java/res/drawable-mdpi/sym_bkeyboard_mic.pngbin0 -> 838 bytes
-rw-r--r--java/res/drawable-mdpi/sym_bkeyboard_num0.pngbin0 -> 1148 bytes
-rw-r--r--java/res/drawable-mdpi/sym_bkeyboard_num1.pngbin0 -> 493 bytes
-rw-r--r--java/res/drawable-mdpi/sym_bkeyboard_num2.pngbin0 -> 1785 bytes
-rw-r--r--java/res/drawable-mdpi/sym_bkeyboard_num3.pngbin0 -> 1675 bytes
-rw-r--r--java/res/drawable-mdpi/sym_bkeyboard_num4.pngbin0 -> 1530 bytes
-rw-r--r--java/res/drawable-mdpi/sym_bkeyboard_num5.pngbin0 -> 1411 bytes
-rw-r--r--java/res/drawable-mdpi/sym_bkeyboard_num6.pngbin0 -> 1943 bytes
-rw-r--r--java/res/drawable-mdpi/sym_bkeyboard_num7.pngbin0 -> 2040 bytes
-rw-r--r--java/res/drawable-mdpi/sym_bkeyboard_num8.pngbin0 -> 1618 bytes
-rw-r--r--java/res/drawable-mdpi/sym_bkeyboard_num9.pngbin0 -> 2167 bytes
-rw-r--r--java/res/drawable-mdpi/sym_bkeyboard_numalt.pngbin0 -> 1670 bytes
-rw-r--r--java/res/drawable-mdpi/sym_bkeyboard_numpound.pngbin0 -> 910 bytes
-rw-r--r--java/res/drawable-mdpi/sym_bkeyboard_numstar.pngbin0 -> 943 bytes
-rw-r--r--java/res/drawable-mdpi/sym_bkeyboard_return.pngbin0 -> 834 bytes
-rw-r--r--java/res/drawable-mdpi/sym_bkeyboard_search.pngbin0 -> 1042 bytes
-rw-r--r--java/res/drawable-mdpi/sym_bkeyboard_shift.pngbin0 -> 998 bytes
-rw-r--r--java/res/drawable-mdpi/sym_bkeyboard_shift_locked.pngbin0 -> 787 bytes
-rw-r--r--java/res/drawable-mdpi/sym_bkeyboard_space.pngbin0 -> 411 bytes
-rw-r--r--java/res/drawable-mdpi/sym_bkeyboard_tab.pngbin0 -> 627 bytes
-rw-r--r--java/res/drawable-mdpi/sym_bkeyboard_tabprev.pngbin0 -> 605 bytes
-rw-r--r--java/res/drawable-mdpi/voice_ime_background.9.png (renamed from java/res/drawable/voice_ime_background.9.png)bin20661 -> 20661 bytes
-rw-r--r--java/res/drawable-mdpi/voice_swipe_hint.png (renamed from java/res/drawable/voice_swipe_hint.png)bin3111 -> 3111 bytes
-rw-r--r--java/res/drawable-mdpi/working.png (renamed from java/res/drawable/working.png)bin33111 -> 33111 bytes
-rw-r--r--java/res/drawable/btn_close.xml27
-rw-r--r--java/res/drawable/btn_keyboard_key2.xml36
-rw-r--r--java/res/drawable/btn_keyboard_key3.xml36
-rw-r--r--java/res/drawable/btn_keyboard_key_stone.xml36
-rw-r--r--java/res/drawable/keyboard_key_feedback.xml22
-rwxr-xr-xjava/res/layout/input_basic.xml (renamed from java/res/layout/input.xml)6
-rwxr-xr-xjava/res/layout/input_basic_highcontrast.xml32
-rwxr-xr-xjava/res/layout/input_stone_bold.xml37
-rwxr-xr-xjava/res/layout/input_stone_normal.xml35
-rwxr-xr-xjava/res/layout/input_stone_popup.xml50
-rwxr-xr-xjava/res/layout/input_trans.xml6
-rw-r--r--java/res/layout/keyboard_key_preview.xml29
-rw-r--r--java/res/layout/keyboard_popup_keyboard.xml47
-rw-r--r--java/res/values-cs/strings.xml6
-rw-r--r--java/res/values-da/strings.xml6
-rw-r--r--java/res/values-de/strings.xml6
-rw-r--r--java/res/values-el/strings.xml6
-rw-r--r--java/res/values-es-rUS/strings.xml6
-rw-r--r--java/res/values-es/strings.xml6
-rw-r--r--java/res/values-fr/strings.xml6
-rw-r--r--java/res/values-it/strings.xml6
-rw-r--r--java/res/values-ja/strings.xml6
-rw-r--r--java/res/values-ko/strings.xml6
-rw-r--r--java/res/values-nb/strings.xml6
-rw-r--r--java/res/values-nl/strings.xml6
-rw-r--r--java/res/values-pl/strings.xml6
-rw-r--r--java/res/values-pt-rPT/strings.xml6
-rw-r--r--java/res/values-pt/strings.xml6
-rw-r--r--java/res/values-ru/strings.xml6
-rw-r--r--java/res/values-sr/strings.xml299
-rw-r--r--java/res/values-sv/strings.xml6
-rw-r--r--java/res/values-tr/strings.xml6
-rw-r--r--java/res/values-xlarge/dimens.xml26
-rw-r--r--java/res/values-zh-rCN/strings.xml6
-rw-r--r--java/res/values-zh-rTW/strings.xml6
-rw-r--r--java/res/values/attrs.xml69
-rw-r--r--java/res/values/bools.xml1
-rw-r--r--java/res/values/colors.xml10
-rw-r--r--java/res/values/dimens.xml7
-rw-r--r--java/res/values/donottranslate.xml4
-rw-r--r--java/res/values/strings.xml36
-rw-r--r--java/res/values/styles.xml35
-rw-r--r--java/res/xml-da/kbd_qwerty.xml213
-rwxr-xr-xjava/res/xml-de/kbd_qwerty_black.xml189
-rw-r--r--java/res/xml-fr/kbd_qwerty_black.xml192
-rwxr-xr-xjava/res/xml-iw/kbd_qwerty.xml164
-rwxr-xr-xjava/res/xml-iw/kbd_qwerty_black.xml164
-rw-r--r--java/res/xml-nb/kbd_qwerty.xml213
-rwxr-xr-xjava/res/xml-ru/kbd_qwerty_black.xml175
-rw-r--r--java/res/xml-sr/kbd_qwerty.xml171
-rw-r--r--java/res/xml-sv/kbd_qwerty_black.xml215
-rw-r--r--java/res/xml/dictionary.xml23
-rw-r--r--java/res/xml/kbd_alpha_black.xml106
-rwxr-xr-xjava/res/xml/kbd_phone_black.xml67
-rwxr-xr-xjava/res/xml/kbd_phone_symbols_black.xml70
-rwxr-xr-xjava/res/xml/kbd_qwerty_black.xml207
-rwxr-xr-xjava/res/xml/kbd_symbols_black.xml141
-rwxr-xr-xjava/res/xml/kbd_symbols_shift.xml5
-rwxr-xr-xjava/res/xml/kbd_symbols_shift_black.xml107
-rw-r--r--java/res/xml/prefs.xml34
-rw-r--r--java/src/com/android/inputmethod/latin/AutoDictionary.java4
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionary.java141
-rwxr-xr-xjava/src/com/android/inputmethod/latin/CandidateView.java55
-rw-r--r--java/src/com/android/inputmethod/latin/ContactsDictionary.java36
-rw-r--r--java/src/com/android/inputmethod/latin/Dictionary.java27
-rw-r--r--java/src/com/android/inputmethod/latin/EditingUtil.java (renamed from java/src/com/android/inputmethod/voice/EditingUtil.java)76
-rw-r--r--java/src/com/android/inputmethod/latin/ExpandableDictionary.java202
-rw-r--r--java/src/com/android/inputmethod/latin/InputLanguageSelection.java5
-rw-r--r--java/src/com/android/inputmethod/latin/KeyboardSwitcher.java205
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java691
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIMESettings.java38
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIMEUtil.java78
-rw-r--r--java/src/com/android/inputmethod/latin/LatinImeLogger.java847
-rw-r--r--java/src/com/android/inputmethod/latin/LatinKeyboard.java87
-rw-r--r--java/src/com/android/inputmethod/latin/LatinKeyboardBaseView.java1633
-rw-r--r--java/src/com/android/inputmethod/latin/LatinKeyboardView.java72
-rwxr-xr-xjava/src/com/android/inputmethod/latin/Suggest.java239
-rw-r--r--java/src/com/android/inputmethod/latin/TextEntryState.java61
-rw-r--r--java/src/com/android/inputmethod/latin/UserDictionary.java3
-rw-r--r--java/src/com/android/inputmethod/latin/WordComposer.java11
-rw-r--r--java/src/com/android/inputmethod/voice/LatinIMEWithVoice.java28
-rw-r--r--java/src/com/android/inputmethod/voice/LatinIMEWithVoiceSettings.java21
-rw-r--r--java/src/com/android/inputmethod/voice/VoiceInput.java76
-rw-r--r--java/src/com/google/android/voicesearch/LatinIMEWithVoice.java29
-rw-r--r--java/src/com/google/android/voicesearch/LatinIMEWithVoiceSettings.java5
-rw-r--r--native/Android.mk16
-rw-r--r--native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp105
-rw-r--r--native/src/char_utils.cpp899
-rw-r--r--native/src/char_utils.h26
-rw-r--r--native/src/dictionary.cpp317
-rw-r--r--native/src/dictionary.h27
-rw-r--r--tests/Android.mk17
-rw-r--r--tests/AndroidManifest.xml33
-rw-r--r--tests/data/bigramlist.xml36
-rw-r--r--tests/data/wordlist.xml244
-rw-r--r--tests/res/raw/test.dictbin0 -> 2829 bytes
-rw-r--r--tests/res/raw/testtext.txt24
-rw-r--r--tests/src/com/android/inputmethod/latin/ImeLoggerTests.java59
-rw-r--r--tests/src/com/android/inputmethod/latin/tests/SuggestHelper.java193
-rw-r--r--tests/src/com/android/inputmethod/latin/tests/SuggestPerformanceTests.java127
-rw-r--r--tests/src/com/android/inputmethod/latin/tests/SuggestTests.java172
220 files changed, 9768 insertions, 687 deletions
diff --git a/java/Android.mk b/java/Android.mk
index 4bb8986ee..0d5a93db0 100755
--- a/java/Android.mk
+++ b/java/Android.mk
@@ -14,9 +14,20 @@ LOCAL_JNI_SHARED_LIBRARIES := libjni_latinime
LOCAL_STATIC_JAVA_LIBRARIES := android-common
#LOCAL_AAPT_FLAGS := -0 .dict
+# The following flag is required because we use a different package name
+# com.google.android.inputmethod.latin than Java package name
+# com.android.inputmethod.latin
+LOCAL_AAPT_FLAGS := --custom-package com.android.inputmethod.latin
-LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := 8
LOCAL_PROGUARD_FLAGS := -include $(LOCAL_PATH)/proguard.flags
+# Define LOCAL_DICTIONARY_RESOURCE_DIR in order to overlay dictionaries.
+# The overlay dictionary resource directory should have dictionary files such
+# as raw-en/main.dict, raw-es/main.dict per locale.
+ifneq ($(strip $(LOCAL_DICTIONARY_RESOURCE_DIR)),)
+LOCAL_RESOURCE_DIR := $(LOCAL_DICTIONARY_RESOURCE_DIR) $(LOCAL_PATH)/res
+endif
+
include $(BUILD_PACKAGE)
diff --git a/java/AndroidManifest.xml b/java/AndroidManifest.xml
index e229bc76a..f87ce013b 100755
--- a/java/AndroidManifest.xml
+++ b/java/AndroidManifest.xml
@@ -1,5 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.inputmethod.latin">
+ package="com.google.android.inputmethod.latin">
<original-package android:name="com.android.inputmethod.latin" />
@@ -10,10 +10,10 @@
<uses-permission android:name="android.permission.READ_CONTACTS" />
<application android:label="@string/english_ime_name"
- android:backupAgent="LatinIMEBackupAgent"
+ android:backupAgent="com.android.inputmethod.latin.LatinIMEBackupAgent"
android:killAfterRestore="false">
- <service android:name="LatinIME"
+ <service android:name="com.android.inputmethod.latin.LatinIME"
android:label="@string/english_ime_name"
android:permission="android.permission.BIND_INPUT_METHOD">
<intent-filter>
@@ -22,13 +22,13 @@
<meta-data android:name="android.view.im" android:resource="@xml/method" />
</service>
- <activity android:name="LatinIMESettings" android:label="@string/english_ime_settings">
+ <activity android:name="com.android.inputmethod.latin.LatinIMESettings" android:label="@string/english_ime_settings">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
</intent-filter>
</activity>
- <activity android:name="InputLanguageSelection"
+ <activity android:name="com.android.inputmethod.latin.InputLanguageSelection"
android:label="@string/language_selection_title">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
diff --git a/java/proguard.flags b/java/proguard.flags
index 0a5d2dda9..829a096c0 100644
--- a/java/proguard.flags
+++ b/java/proguard.flags
@@ -1,3 +1,8 @@
-keep class com.android.inputmethod.latin.BinaryDictionary {
int mDictLength;
+ <init>(...);
+}
+
+-keep class com.android.inputmethod.latin.Suggest {
+ <init>(...);
}
diff --git a/java/res/drawable-en-hdpi/sym_keyboard_feedback_delete.png b/java/res/drawable-en-hdpi/sym_keyboard_feedback_delete.png
deleted file mode 100755
index ca7637552..000000000
--- a/java/res/drawable-en-hdpi/sym_keyboard_feedback_delete.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_close_normal.png b/java/res/drawable-hdpi/btn_close_normal.png
new file mode 100644
index 000000000..38b49f1a3
--- /dev/null
+++ b/java/res/drawable-hdpi/btn_close_normal.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_close_pressed.png b/java/res/drawable-hdpi/btn_close_pressed.png
new file mode 100644
index 000000000..aa9ea49f0
--- /dev/null
+++ b/java/res/drawable-hdpi/btn_close_pressed.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_close_selected.png b/java/res/drawable-hdpi/btn_close_selected.png
new file mode 100644
index 000000000..870c670f7
--- /dev/null
+++ b/java/res/drawable-hdpi/btn_close_selected.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_normal_off_stone.9.png b/java/res/drawable-hdpi/btn_keyboard_key_normal_off_stone.9.png
new file mode 100644
index 000000000..b67732cd4
--- /dev/null
+++ b/java/res/drawable-hdpi/btn_keyboard_key_normal_off_stone.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_normal_on_stone.9.png b/java/res/drawable-hdpi/btn_keyboard_key_normal_on_stone.9.png
new file mode 100644
index 000000000..534f1cdfd
--- /dev/null
+++ b/java/res/drawable-hdpi/btn_keyboard_key_normal_on_stone.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_normal_stone.9.png b/java/res/drawable-hdpi/btn_keyboard_key_normal_stone.9.png
new file mode 100644
index 000000000..fba10b888
--- /dev/null
+++ b/java/res/drawable-hdpi/btn_keyboard_key_normal_stone.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_normal_metal.9.png b/java/res/drawable-hdpi/btn_keyboard_normal_metal.9.png
new file mode 100644
index 000000000..b29d6d174
--- /dev/null
+++ b/java/res/drawable-hdpi/btn_keyboard_normal_metal.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_toggle_off.png b/java/res/drawable-hdpi/btn_keyboard_toggle_off.png
new file mode 100644
index 000000000..bfe78402f
--- /dev/null
+++ b/java/res/drawable-hdpi/btn_keyboard_toggle_off.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_toggle_on.png b/java/res/drawable-hdpi/btn_keyboard_toggle_on.png
new file mode 100644
index 000000000..0a1221e97
--- /dev/null
+++ b/java/res/drawable-hdpi/btn_keyboard_toggle_on.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_led_off.9.png b/java/res/drawable-hdpi/btn_led_off.9.png
new file mode 100644
index 000000000..a60f96539
--- /dev/null
+++ b/java/res/drawable-hdpi/btn_led_off.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_led_on.9.png b/java/res/drawable-hdpi/btn_led_on.9.png
new file mode 100644
index 000000000..c90260967
--- /dev/null
+++ b/java/res/drawable-hdpi/btn_led_on.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/dialog_top_dark_bottom_medium.9.png b/java/res/drawable-hdpi/dialog_top_dark_bottom_medium.9.png
new file mode 100644
index 000000000..ab6c036c3
--- /dev/null
+++ b/java/res/drawable-hdpi/dialog_top_dark_bottom_medium.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/dialog_top_dark_bottom_medium.png b/java/res/drawable-hdpi/dialog_top_dark_bottom_medium.png
deleted file mode 100755
index 7c79a4f90..000000000
--- a/java/res/drawable-hdpi/dialog_top_dark_bottom_medium.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/keyboard_key_feedback_background.9.png b/java/res/drawable-hdpi/keyboard_key_feedback_background.9.png
new file mode 100644
index 000000000..6ba42db82
--- /dev/null
+++ b/java/res/drawable-hdpi/keyboard_key_feedback_background.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/keyboard_key_feedback_more_background.9.png b/java/res/drawable-hdpi/keyboard_key_feedback_more_background.9.png
new file mode 100644
index 000000000..4d0b60109
--- /dev/null
+++ b/java/res/drawable-hdpi/keyboard_key_feedback_more_background.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/keyboard_popup_panel_background.9.png b/java/res/drawable-hdpi/keyboard_popup_panel_background.9.png
new file mode 100644
index 000000000..8e2461b3f
--- /dev/null
+++ b/java/res/drawable-hdpi/keyboard_popup_panel_background.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/keyboard_popup_panel_trans_background.9.png b/java/res/drawable-hdpi/keyboard_popup_panel_trans_background.9.png
new file mode 100644
index 000000000..fd7366e20
--- /dev/null
+++ b/java/res/drawable-hdpi/keyboard_popup_panel_trans_background.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_123_mic.png b/java/res/drawable-hdpi/sym_bkeyboard_123_mic.png
new file mode 100644
index 000000000..3e4eff698
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_123_mic.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_delete.png b/java/res/drawable-hdpi/sym_bkeyboard_delete.png
new file mode 100644
index 000000000..1d24cc85c
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_delete.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_done.png b/java/res/drawable-hdpi/sym_bkeyboard_done.png
new file mode 100644
index 000000000..b77803d21
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_done.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_globe.png b/java/res/drawable-hdpi/sym_bkeyboard_globe.png
new file mode 100644
index 000000000..f5dbe0cd1
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_globe.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_mic.png b/java/res/drawable-hdpi/sym_bkeyboard_mic.png
new file mode 100644
index 000000000..512f46080
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_mic.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_num0.png b/java/res/drawable-hdpi/sym_bkeyboard_num0.png
new file mode 100644
index 000000000..678a790de
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_num0.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_num1.png b/java/res/drawable-hdpi/sym_bkeyboard_num1.png
new file mode 100644
index 000000000..4e68e35b3
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_num1.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_num2.png b/java/res/drawable-hdpi/sym_bkeyboard_num2.png
new file mode 100644
index 000000000..546663fda
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_num2.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_num3.png b/java/res/drawable-hdpi/sym_bkeyboard_num3.png
new file mode 100644
index 000000000..57f9a8d8e
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_num3.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_num4.png b/java/res/drawable-hdpi/sym_bkeyboard_num4.png
new file mode 100644
index 000000000..de504388f
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_num4.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_num5.png b/java/res/drawable-hdpi/sym_bkeyboard_num5.png
new file mode 100644
index 000000000..1d2e1ef89
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_num5.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_num6.png b/java/res/drawable-hdpi/sym_bkeyboard_num6.png
new file mode 100644
index 000000000..39788b727
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_num6.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_num7.png b/java/res/drawable-hdpi/sym_bkeyboard_num7.png
new file mode 100644
index 000000000..fff6f27bf
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_num7.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_num8.png b/java/res/drawable-hdpi/sym_bkeyboard_num8.png
new file mode 100644
index 000000000..8cc1a955e
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_num8.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_num9.png b/java/res/drawable-hdpi/sym_bkeyboard_num9.png
new file mode 100644
index 000000000..021742509
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_num9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_numalt.png b/java/res/drawable-hdpi/sym_bkeyboard_numalt.png
new file mode 100644
index 000000000..200714f66
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_numalt.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_numpound.png b/java/res/drawable-hdpi/sym_bkeyboard_numpound.png
new file mode 100644
index 000000000..0a46122b2
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_numpound.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_numstar.png b/java/res/drawable-hdpi/sym_bkeyboard_numstar.png
new file mode 100644
index 000000000..ca22bd535
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_numstar.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_return.png b/java/res/drawable-hdpi/sym_bkeyboard_return.png
new file mode 100644
index 000000000..426e1599e
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_return.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_search.png b/java/res/drawable-hdpi/sym_bkeyboard_search.png
new file mode 100644
index 000000000..1b6f884fa
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_search.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_shift.png b/java/res/drawable-hdpi/sym_bkeyboard_shift.png
new file mode 100644
index 000000000..5a22dd309
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_shift.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_shift_locked.png b/java/res/drawable-hdpi/sym_bkeyboard_shift_locked.png
new file mode 100644
index 000000000..566449126
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_shift_locked.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_space.png b/java/res/drawable-hdpi/sym_bkeyboard_space.png
new file mode 100644
index 000000000..cd0ebe2f4
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_space.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_tab.png b/java/res/drawable-hdpi/sym_bkeyboard_tab.png
new file mode 100644
index 000000000..3466e1271
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_tab.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_tabprev.png b/java/res/drawable-hdpi/sym_bkeyboard_tabprev.png
new file mode 100644
index 000000000..eb4f0eb68
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_tabprev.png
Binary files differ
diff --git a/java/res/drawable-hdpi/voice_swipe_hint.png b/java/res/drawable-hdpi/voice_swipe_hint.png
new file mode 100644
index 000000000..130f83a9c
--- /dev/null
+++ b/java/res/drawable-hdpi/voice_swipe_hint.png
Binary files differ
diff --git a/java/res/drawable-land-hdpi/btn_keyboard_key_normal_off_stone.9.png b/java/res/drawable-land-hdpi/btn_keyboard_key_normal_off_stone.9.png
new file mode 100644
index 000000000..67a204f85
--- /dev/null
+++ b/java/res/drawable-land-hdpi/btn_keyboard_key_normal_off_stone.9.png
Binary files differ
diff --git a/java/res/drawable-land-hdpi/btn_keyboard_key_normal_on_stone.9.png b/java/res/drawable-land-hdpi/btn_keyboard_key_normal_on_stone.9.png
new file mode 100644
index 000000000..63cbe60a3
--- /dev/null
+++ b/java/res/drawable-land-hdpi/btn_keyboard_key_normal_on_stone.9.png
Binary files differ
diff --git a/java/res/drawable-land-hdpi/btn_keyboard_key_normal_stone.9.png b/java/res/drawable-land-hdpi/btn_keyboard_key_normal_stone.9.png
new file mode 100644
index 000000000..0dd33b429
--- /dev/null
+++ b/java/res/drawable-land-hdpi/btn_keyboard_key_normal_stone.9.png
Binary files differ
diff --git a/java/res/drawable-land-mdpi/btn_keyboard_key_normal_off.9.png b/java/res/drawable-land-mdpi/btn_keyboard_key_normal_off.9.png
deleted file mode 100644
index bda9b8394..000000000
--- a/java/res/drawable-land-mdpi/btn_keyboard_key_normal_off.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-mdpi/btn_keyboard_key_normal_off_stone.9.png b/java/res/drawable-land-mdpi/btn_keyboard_key_normal_off_stone.9.png
new file mode 100644
index 000000000..67a204f85
--- /dev/null
+++ b/java/res/drawable-land-mdpi/btn_keyboard_key_normal_off_stone.9.png
Binary files differ
diff --git a/java/res/drawable-land-mdpi/btn_keyboard_key_normal_on.9.png b/java/res/drawable-land-mdpi/btn_keyboard_key_normal_on.9.png
deleted file mode 100644
index 0c16ed509..000000000
--- a/java/res/drawable-land-mdpi/btn_keyboard_key_normal_on.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-mdpi/btn_keyboard_key_normal_on_stone.9.png b/java/res/drawable-land-mdpi/btn_keyboard_key_normal_on_stone.9.png
new file mode 100644
index 000000000..63cbe60a3
--- /dev/null
+++ b/java/res/drawable-land-mdpi/btn_keyboard_key_normal_on_stone.9.png
Binary files differ
diff --git a/java/res/drawable-land-mdpi/btn_keyboard_key_normal_stone.9.png b/java/res/drawable-land-mdpi/btn_keyboard_key_normal_stone.9.png
new file mode 100644
index 000000000..0dd33b429
--- /dev/null
+++ b/java/res/drawable-land-mdpi/btn_keyboard_key_normal_stone.9.png
Binary files differ
diff --git a/java/res/drawable-land-mdpi/btn_keyboard_key_pressed_off.9.png b/java/res/drawable-land-mdpi/btn_keyboard_key_pressed_off.9.png
deleted file mode 100644
index bdcf06e1b..000000000
--- a/java/res/drawable-land-mdpi/btn_keyboard_key_pressed_off.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-land-mdpi/btn_keyboard_key_pressed_on.9.png b/java/res/drawable-land-mdpi/btn_keyboard_key_pressed_on.9.png
deleted file mode 100644
index 79621a9e6..000000000
--- a/java/res/drawable-land-mdpi/btn_keyboard_key_pressed_on.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_close_normal.png b/java/res/drawable-mdpi/btn_close_normal.png
new file mode 100644
index 000000000..4c6e79dc8
--- /dev/null
+++ b/java/res/drawable-mdpi/btn_close_normal.png
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_close_pressed.png b/java/res/drawable-mdpi/btn_close_pressed.png
new file mode 100644
index 000000000..fc983afdc
--- /dev/null
+++ b/java/res/drawable-mdpi/btn_close_pressed.png
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_close_selected.png b/java/res/drawable-mdpi/btn_close_selected.png
new file mode 100644
index 000000000..f2bf91a2d
--- /dev/null
+++ b/java/res/drawable-mdpi/btn_close_selected.png
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_keyboard_key_normal_off_stone.9.png b/java/res/drawable-mdpi/btn_keyboard_key_normal_off_stone.9.png
new file mode 100644
index 000000000..b67732cd4
--- /dev/null
+++ b/java/res/drawable-mdpi/btn_keyboard_key_normal_off_stone.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_keyboard_key_normal_on_stone.9.png b/java/res/drawable-mdpi/btn_keyboard_key_normal_on_stone.9.png
new file mode 100644
index 000000000..534f1cdfd
--- /dev/null
+++ b/java/res/drawable-mdpi/btn_keyboard_key_normal_on_stone.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_keyboard_key_normal_stone.9.png b/java/res/drawable-mdpi/btn_keyboard_key_normal_stone.9.png
new file mode 100644
index 000000000..fba10b888
--- /dev/null
+++ b/java/res/drawable-mdpi/btn_keyboard_key_normal_stone.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_keyboard_normal_metal.9.png b/java/res/drawable-mdpi/btn_keyboard_normal_metal.9.png
new file mode 100644
index 000000000..f4fe0a8a0
--- /dev/null
+++ b/java/res/drawable-mdpi/btn_keyboard_normal_metal.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_keyboard_toggle_off.png b/java/res/drawable-mdpi/btn_keyboard_toggle_off.png
new file mode 100644
index 000000000..21399a4f3
--- /dev/null
+++ b/java/res/drawable-mdpi/btn_keyboard_toggle_off.png
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_keyboard_toggle_on.png b/java/res/drawable-mdpi/btn_keyboard_toggle_on.png
new file mode 100644
index 000000000..22d5683e2
--- /dev/null
+++ b/java/res/drawable-mdpi/btn_keyboard_toggle_on.png
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_led_off.9.png b/java/res/drawable-mdpi/btn_led_off.9.png
new file mode 100644
index 000000000..68ce7a67a
--- /dev/null
+++ b/java/res/drawable-mdpi/btn_led_off.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_led_on.9.png b/java/res/drawable-mdpi/btn_led_on.9.png
new file mode 100644
index 000000000..fe77abb08
--- /dev/null
+++ b/java/res/drawable-mdpi/btn_led_on.9.png
Binary files differ
diff --git a/java/res/drawable/cancel.png b/java/res/drawable-mdpi/cancel.png
index 081532bec..081532bec 100644
--- a/java/res/drawable/cancel.png
+++ b/java/res/drawable-mdpi/cancel.png
Binary files differ
diff --git a/java/res/drawable/caution.png b/java/res/drawable-mdpi/caution.png
index eaef53425..eaef53425 100644
--- a/java/res/drawable/caution.png
+++ b/java/res/drawable-mdpi/caution.png
Binary files differ
diff --git a/java/res/drawable/dialog_top_dark_bottom_medium.9.png b/java/res/drawable-mdpi/dialog_top_dark_bottom_medium.9.png
index cf7ecaf1e..cf7ecaf1e 100644
--- a/java/res/drawable/dialog_top_dark_bottom_medium.9.png
+++ b/java/res/drawable-mdpi/dialog_top_dark_bottom_medium.9.png
Binary files differ
diff --git a/java/res/drawable/ic_dialog_alert_large.png b/java/res/drawable-mdpi/ic_dialog_alert_large.png
index 2d4a164a7..2d4a164a7 100644
--- a/java/res/drawable/ic_dialog_alert_large.png
+++ b/java/res/drawable-mdpi/ic_dialog_alert_large.png
Binary files differ
diff --git a/java/res/drawable/ic_dialog_voice_input.png b/java/res/drawable-mdpi/ic_dialog_voice_input.png
index d28914132..d28914132 100644
--- a/java/res/drawable/ic_dialog_voice_input.png
+++ b/java/res/drawable-mdpi/ic_dialog_voice_input.png
Binary files differ
diff --git a/java/res/drawable/ic_dialog_wave_0_0.png b/java/res/drawable-mdpi/ic_dialog_wave_0_0.png
index 9c3c28f37..9c3c28f37 100644
--- a/java/res/drawable/ic_dialog_wave_0_0.png
+++ b/java/res/drawable-mdpi/ic_dialog_wave_0_0.png
Binary files differ
diff --git a/java/res/drawable/ic_dialog_wave_1_3.png b/java/res/drawable-mdpi/ic_dialog_wave_1_3.png
index d33bd0d9b..d33bd0d9b 100644
--- a/java/res/drawable/ic_dialog_wave_1_3.png
+++ b/java/res/drawable-mdpi/ic_dialog_wave_1_3.png
Binary files differ
diff --git a/java/res/drawable/ic_dialog_wave_2_3.png b/java/res/drawable-mdpi/ic_dialog_wave_2_3.png
index 5094a6e6c..5094a6e6c 100644
--- a/java/res/drawable/ic_dialog_wave_2_3.png
+++ b/java/res/drawable-mdpi/ic_dialog_wave_2_3.png
Binary files differ
diff --git a/java/res/drawable/ic_dialog_wave_3_3.png b/java/res/drawable-mdpi/ic_dialog_wave_3_3.png
index 69917564d..69917564d 100644
--- a/java/res/drawable/ic_dialog_wave_3_3.png
+++ b/java/res/drawable-mdpi/ic_dialog_wave_3_3.png
Binary files differ
diff --git a/java/res/drawable/ic_dialog_wave_4_3.png b/java/res/drawable-mdpi/ic_dialog_wave_4_3.png
index af5a84c31..af5a84c31 100644
--- a/java/res/drawable/ic_dialog_wave_4_3.png
+++ b/java/res/drawable-mdpi/ic_dialog_wave_4_3.png
Binary files differ
diff --git a/java/res/drawable-mdpi/keyboard_key_feedback_background.9.png b/java/res/drawable-mdpi/keyboard_key_feedback_background.9.png
new file mode 100644
index 000000000..2a80f096d
--- /dev/null
+++ b/java/res/drawable-mdpi/keyboard_key_feedback_background.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/keyboard_key_feedback_more_background.9.png b/java/res/drawable-mdpi/keyboard_key_feedback_more_background.9.png
new file mode 100755
index 000000000..29aa285bd
--- /dev/null
+++ b/java/res/drawable-mdpi/keyboard_key_feedback_more_background.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/keyboard_popup_panel_background.9.png b/java/res/drawable-mdpi/keyboard_popup_panel_background.9.png
new file mode 100644
index 000000000..36d75df6f
--- /dev/null
+++ b/java/res/drawable-mdpi/keyboard_popup_panel_background.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/keyboard_popup_panel_trans_background.9.png b/java/res/drawable-mdpi/keyboard_popup_panel_trans_background.9.png
new file mode 100644
index 000000000..4ba2a4908
--- /dev/null
+++ b/java/res/drawable-mdpi/keyboard_popup_panel_trans_background.9.png
Binary files differ
diff --git a/java/res/drawable/mic_slash.png b/java/res/drawable-mdpi/mic_slash.png
index 0b0fb5803..0b0fb5803 100644
--- a/java/res/drawable/mic_slash.png
+++ b/java/res/drawable-mdpi/mic_slash.png
Binary files differ
diff --git a/java/res/drawable/ok_cancel.png b/java/res/drawable-mdpi/ok_cancel.png
index 0601d3231..0601d3231 100644
--- a/java/res/drawable/ok_cancel.png
+++ b/java/res/drawable-mdpi/ok_cancel.png
Binary files differ
diff --git a/java/res/drawable/speak_now_level0.png b/java/res/drawable-mdpi/speak_now_level0.png
index abc845466..abc845466 100644
--- a/java/res/drawable/speak_now_level0.png
+++ b/java/res/drawable-mdpi/speak_now_level0.png
Binary files differ
diff --git a/java/res/drawable/speak_now_level1.png b/java/res/drawable-mdpi/speak_now_level1.png
index 67cb235bf..67cb235bf 100644
--- a/java/res/drawable/speak_now_level1.png
+++ b/java/res/drawable-mdpi/speak_now_level1.png
Binary files differ
diff --git a/java/res/drawable/speak_now_level2.png b/java/res/drawable-mdpi/speak_now_level2.png
index 1e07f26c6..1e07f26c6 100644
--- a/java/res/drawable/speak_now_level2.png
+++ b/java/res/drawable-mdpi/speak_now_level2.png
Binary files differ
diff --git a/java/res/drawable/speak_now_level3.png b/java/res/drawable-mdpi/speak_now_level3.png
index 31991daee..31991daee 100644
--- a/java/res/drawable/speak_now_level3.png
+++ b/java/res/drawable-mdpi/speak_now_level3.png
Binary files differ
diff --git a/java/res/drawable/speak_now_level4.png b/java/res/drawable-mdpi/speak_now_level4.png
index 7363ca892..7363ca892 100644
--- a/java/res/drawable/speak_now_level4.png
+++ b/java/res/drawable-mdpi/speak_now_level4.png
Binary files differ
diff --git a/java/res/drawable/speak_now_level5.png b/java/res/drawable-mdpi/speak_now_level5.png
index 9034908f4..9034908f4 100644
--- a/java/res/drawable/speak_now_level5.png
+++ b/java/res/drawable-mdpi/speak_now_level5.png
Binary files differ
diff --git a/java/res/drawable/speak_now_level6.png b/java/res/drawable-mdpi/speak_now_level6.png
index 3eaa9bdad..3eaa9bdad 100644
--- a/java/res/drawable/speak_now_level6.png
+++ b/java/res/drawable-mdpi/speak_now_level6.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_123_mic.png b/java/res/drawable-mdpi/sym_bkeyboard_123_mic.png
new file mode 100644
index 000000000..0749b5fc6
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_123_mic.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_delete.png b/java/res/drawable-mdpi/sym_bkeyboard_delete.png
new file mode 100644
index 000000000..1a5ff439e
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_delete.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_done.png b/java/res/drawable-mdpi/sym_bkeyboard_done.png
new file mode 100644
index 000000000..05ce7c643
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_done.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_globe.png b/java/res/drawable-mdpi/sym_bkeyboard_globe.png
new file mode 100644
index 000000000..c6595cf62
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_globe.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_mic.png b/java/res/drawable-mdpi/sym_bkeyboard_mic.png
new file mode 100644
index 000000000..a6cb1cc01
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_mic.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_num0.png b/java/res/drawable-mdpi/sym_bkeyboard_num0.png
new file mode 100644
index 000000000..7188f9ca5
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_num0.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_num1.png b/java/res/drawable-mdpi/sym_bkeyboard_num1.png
new file mode 100644
index 000000000..2a31bd458
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_num1.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_num2.png b/java/res/drawable-mdpi/sym_bkeyboard_num2.png
new file mode 100644
index 000000000..c1e9cc9b1
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_num2.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_num3.png b/java/res/drawable-mdpi/sym_bkeyboard_num3.png
new file mode 100644
index 000000000..e9987668c
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_num3.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_num4.png b/java/res/drawable-mdpi/sym_bkeyboard_num4.png
new file mode 100644
index 000000000..7f0f3cccc
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_num4.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_num5.png b/java/res/drawable-mdpi/sym_bkeyboard_num5.png
new file mode 100644
index 000000000..5f748b416
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_num5.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_num6.png b/java/res/drawable-mdpi/sym_bkeyboard_num6.png
new file mode 100644
index 000000000..78aae74a0
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_num6.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_num7.png b/java/res/drawable-mdpi/sym_bkeyboard_num7.png
new file mode 100644
index 000000000..5bb874c47
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_num7.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_num8.png b/java/res/drawable-mdpi/sym_bkeyboard_num8.png
new file mode 100644
index 000000000..6b58fdc8a
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_num8.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_num9.png b/java/res/drawable-mdpi/sym_bkeyboard_num9.png
new file mode 100644
index 000000000..f348c92af
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_num9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_numalt.png b/java/res/drawable-mdpi/sym_bkeyboard_numalt.png
new file mode 100644
index 000000000..4fa410b62
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_numalt.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_numpound.png b/java/res/drawable-mdpi/sym_bkeyboard_numpound.png
new file mode 100644
index 000000000..9126eed0d
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_numpound.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_numstar.png b/java/res/drawable-mdpi/sym_bkeyboard_numstar.png
new file mode 100644
index 000000000..9b9f1b986
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_numstar.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_return.png b/java/res/drawable-mdpi/sym_bkeyboard_return.png
new file mode 100644
index 000000000..e76225d0f
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_return.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_search.png b/java/res/drawable-mdpi/sym_bkeyboard_search.png
new file mode 100644
index 000000000..1f180155d
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_search.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_shift.png b/java/res/drawable-mdpi/sym_bkeyboard_shift.png
new file mode 100644
index 000000000..c981188dd
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_shift.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_shift_locked.png b/java/res/drawable-mdpi/sym_bkeyboard_shift_locked.png
new file mode 100644
index 000000000..b8cebd060
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_shift_locked.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_space.png b/java/res/drawable-mdpi/sym_bkeyboard_space.png
new file mode 100644
index 000000000..4da7ee86e
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_space.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_tab.png b/java/res/drawable-mdpi/sym_bkeyboard_tab.png
new file mode 100644
index 000000000..2cb991cbf
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_tab.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_tabprev.png b/java/res/drawable-mdpi/sym_bkeyboard_tabprev.png
new file mode 100644
index 000000000..5298291d5
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_tabprev.png
Binary files differ
diff --git a/java/res/drawable/voice_ime_background.9.png b/java/res/drawable-mdpi/voice_ime_background.9.png
index 67802492a..67802492a 100644
--- a/java/res/drawable/voice_ime_background.9.png
+++ b/java/res/drawable-mdpi/voice_ime_background.9.png
Binary files differ
diff --git a/java/res/drawable/voice_swipe_hint.png b/java/res/drawable-mdpi/voice_swipe_hint.png
index bb8873251..bb8873251 100644
--- a/java/res/drawable/voice_swipe_hint.png
+++ b/java/res/drawable-mdpi/voice_swipe_hint.png
Binary files differ
diff --git a/java/res/drawable/working.png b/java/res/drawable-mdpi/working.png
index 6246a6d1c..6246a6d1c 100644
--- a/java/res/drawable/working.png
+++ b/java/res/drawable-mdpi/working.png
Binary files differ
diff --git a/java/res/drawable/btn_close.xml b/java/res/drawable/btn_close.xml
new file mode 100644
index 000000000..ee5813898
--- /dev/null
+++ b/java/res/drawable/btn_close.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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_pressed="false" android:state_focused="false"
+ android:drawable="@drawable/btn_close_normal" />
+
+ <item android:state_pressed="true"
+ android:drawable="@drawable/btn_close_pressed" />
+
+ <item android:state_focused="true"
+ android:drawable="@drawable/btn_close_selected" />
+</selector>
diff --git a/java/res/drawable/btn_keyboard_key2.xml b/java/res/drawable/btn_keyboard_key2.xml
new file mode 100644
index 000000000..bd745b76e
--- /dev/null
+++ b/java/res/drawable/btn_keyboard_key2.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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">
+
+ <!-- Toggle keys. Use checkable/checked state. -->
+
+ <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"
+ android:drawable="@drawable/btn_keyboard_key_fulltrans_pressed" />
+ <item android:state_checkable="true" android:state_checked="true"
+ android:drawable="@drawable/btn_keyboard_key_normal_on" />
+ <item android:state_checkable="true"
+ android:drawable="@drawable/btn_keyboard_key_fulltrans_normal" />
+
+ <!-- Normal keys -->
+
+ <item android:state_pressed="true"
+ android:drawable="@drawable/btn_keyboard_key_fulltrans_pressed" />
+ <item android:drawable="@drawable/btn_keyboard_key_fulltrans_normal" />
+</selector>
diff --git a/java/res/drawable/btn_keyboard_key3.xml b/java/res/drawable/btn_keyboard_key3.xml
new file mode 100644
index 000000000..dbe82d5fd
--- /dev/null
+++ b/java/res/drawable/btn_keyboard_key3.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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">
+
+ <!-- Toggle keys. Use checkable/checked state. -->
+
+ <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"
+ android:drawable="@drawable/btn_keyboard_key_fulltrans_normal" />
+ <item android:state_checkable="true" android:state_checked="true"
+ android:drawable="@drawable/btn_keyboard_key_normal_on" />
+ <item android:state_checkable="true"
+ android:drawable="@drawable/btn_keyboard_key_fulltrans_pressed" />
+
+ <!-- Normal keys -->
+
+ <item android:state_pressed="true"
+ android:drawable="@drawable/btn_keyboard_key_fulltrans_normal" />
+ <item android:drawable="@drawable/btn_keyboard_key_fulltrans_pressed" />
+</selector>
diff --git a/java/res/drawable/btn_keyboard_key_stone.xml b/java/res/drawable/btn_keyboard_key_stone.xml
new file mode 100644
index 000000000..a6040a04e
--- /dev/null
+++ b/java/res/drawable/btn_keyboard_key_stone.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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">
+
+ <!-- Toggle keys. Use checkable/checked state. -->
+
+ <item android:state_checkable="true" android:state_checked="true"
+ android:state_pressed="true"
+ android:drawable="@drawable/btn_keyboard_key_normal_on_stone" />
+ <item android:state_checkable="true" android:state_pressed="true"
+ android:drawable="@drawable/btn_keyboard_key_normal_off_stone" />
+ <item android:state_checkable="true" android:state_checked="true"
+ android:drawable="@drawable/btn_keyboard_key_normal_on_stone" />
+ <item android:state_checkable="true"
+ android:drawable="@drawable/btn_keyboard_key_normal_off_stone" />
+
+ <!-- Normal keys -->
+
+ <item android:state_pressed="true"
+ android:drawable="@drawable/btn_keyboard_key_fulltrans_pressed" />
+ <item android:drawable="@drawable/btn_keyboard_key_normal_stone" />
+</selector>
diff --git a/java/res/drawable/keyboard_key_feedback.xml b/java/res/drawable/keyboard_key_feedback.xml
new file mode 100644
index 000000000..159ba8686
--- /dev/null
+++ b/java/res/drawable/keyboard_key_feedback.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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_more_background" />
+
+ <item android:drawable="@drawable/keyboard_key_feedback_background" />
+</selector>
diff --git a/java/res/layout/input.xml b/java/res/layout/input_basic.xml
index 1d7c6f746..ffa59543b 100755
--- a/java/res/layout/input.xml
+++ b/java/res/layout/input_basic.xml
@@ -20,10 +20,12 @@
<com.android.inputmethod.latin.LatinKeyboardView
xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@android:id/keyboardView"
+ xmlns:latin="http://schemas.android.com/apk/res/com.google.android.inputmethod.latin"
+ android:id="@+id/LatinkeyboardBaseView"
android:layout_alignParentBottom="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/keyboard_background"
- android:keyBackground="@drawable/btn_keyboard_key"
+
+ latin:keyBackground="@drawable/btn_keyboard_key"
/>
diff --git a/java/res/layout/input_basic_highcontrast.xml b/java/res/layout/input_basic_highcontrast.xml
new file mode 100755
index 000000000..dc4d09736
--- /dev/null
+++ b/java/res/layout/input_basic_highcontrast.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2010, 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.latin.LatinKeyboardView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:latin="http://schemas.android.com/apk/res/com.google.android.inputmethod.latin"
+
+ android:id="@+id/LatinkeyboardBaseView"
+ android:layout_alignParentBottom="true"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@android:color/black"
+
+ latin:keyBackground="@drawable/btn_keyboard_key3"
+ />
diff --git a/java/res/layout/input_stone_bold.xml b/java/res/layout/input_stone_bold.xml
new file mode 100755
index 000000000..d6c03cc20
--- /dev/null
+++ b/java/res/layout/input_stone_bold.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2010, 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.latin.LatinKeyboardView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:latin="http://schemas.android.com/apk/res/com.google.android.inputmethod.latin"
+ android:id="@+id/LatinkeyboardBaseView"
+ android:layout_alignParentBottom="true"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@drawable/keyboard_background"
+ android:textStyle="bold"
+
+ latin:keyBackground="@drawable/btn_keyboard_key_stone"
+ latin:keyTextColor="@color/latinkeyboard_key_color_black"
+ latin:shadowColor="@color/latinkeyboard_key_color_white"
+ latin:keyTextStyle="bold"
+ latin:symbolColorScheme="black"
+ latin:popupLayout="@layout/input_stone_popup"
+ />
diff --git a/java/res/layout/input_stone_normal.xml b/java/res/layout/input_stone_normal.xml
new file mode 100755
index 000000000..2c39bb137
--- /dev/null
+++ b/java/res/layout/input_stone_normal.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2010, 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.latin.LatinKeyboardView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:latin="http://schemas.android.com/apk/res/com.google.android.inputmethod.latin"
+ android:id="@+id/LatinkeyboardBaseView"
+ android:layout_alignParentBottom="true"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@drawable/keyboard_background"
+
+ latin:keyBackground="@drawable/btn_keyboard_key_stone"
+ latin:keyTextColor="@color/latinkeyboard_key_color_black"
+ latin:shadowColor="@color/latinkeyboard_key_color_white"
+ latin:symbolColorScheme="black"
+ latin:popupLayout="@layout/input_stone_popup"
+ />
diff --git a/java/res/layout/input_stone_popup.xml b/java/res/layout/input_stone_popup.xml
new file mode 100755
index 000000000..b6894b6e0
--- /dev/null
+++ b/java/res/layout/input_stone_popup.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2010, 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="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:background="@drawable/keyboard_popup_panel_background"
+ >
+ <com.android.inputmethod.latin.LatinKeyboardBaseView
+ xmlns:latin="http://schemas.android.com/apk/res/com.google.android.inputmethod.latin"
+ android:id="@+id/LatinKeyboardBaseView"
+ android:layout_alignParentBottom="true"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@drawable/keyboard_background"
+
+ latin:keyBackground="@drawable/btn_keyboard_key_stone"
+ latin:keyTextColor="@color/latinkeyboard_key_color_black"
+ latin:shadowColor="@color/latinkeyboard_key_color_white"
+ latin:popupLayout="@layout/input_stone_popup"
+ />
+ <ImageButton android:id="@+id/closeButton"
+ android:background="@android:color/transparent"
+ android:src="@drawable/btn_close"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_marginLeft="8dp"
+ android:clickable="true"
+ />
+</LinearLayout>
diff --git a/java/res/layout/input_trans.xml b/java/res/layout/input_trans.xml
index 94806f7e3..ee5f85ea1 100755
--- a/java/res/layout/input_trans.xml
+++ b/java/res/layout/input_trans.xml
@@ -20,11 +20,13 @@
<com.android.inputmethod.latin.LatinKeyboardView
xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:latin="http://schemas.android.com/apk/res/com.google.android.inputmethod.latin"
android:id="@android:id/keyboardView"
android:layout_alignParentBottom="true"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:background="#A0000000"
+ android:background="@color/latinkeyboard_extension_background"
android:verticalCorrection="0dip"
- android:keyBackground="@drawable/btn_keyboard_key_fulltrans"
+
+ latin:keyBackground="@drawable/btn_keyboard_key_fulltrans"
/>
diff --git a/java/res/layout/keyboard_key_preview.xml b/java/res/layout/keyboard_key_preview.xml
new file mode 100644
index 000000000..64eaa6579
--- /dev/null
+++ b/java/res/layout/keyboard_key_preview.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2010, 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.
+*/
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="80sp"
+ android:textSize="40sp"
+ android:textColor="?android:attr/textColorPrimaryInverse"
+ android:minWidth="32dip"
+ android:gravity="center"
+ android:background="@drawable/keyboard_key_feedback"
+ />
diff --git a/java/res/layout/keyboard_popup_keyboard.xml b/java/res/layout/keyboard_popup_keyboard.xml
new file mode 100644
index 000000000..5f8a03d46
--- /dev/null
+++ b/java/res/layout/keyboard_popup_keyboard.xml
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2010, 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="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:background="@drawable/keyboard_popup_panel_background"
+ >
+ <com.android.inputmethod.latin.LatinKeyboardBaseView
+ xmlns:latin="http://schemas.android.com/apk/res/com.google.android.inputmethod.latin"
+ android:id="@+id/LatinKeyboardBaseView"
+ android:layout_alignParentBottom="true"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@android:color/transparent"
+
+ latin:keyPreviewLayout="@layout/keyboard_key_preview"
+ latin:popupLayout="@layout/keyboard_popup_keyboard"
+ />
+ <ImageButton android:id="@+id/closeButton"
+ android:background="@android:color/transparent"
+ android:src="@drawable/btn_close"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_marginLeft="8dp"
+ android:clickable="true"
+ />
+</LinearLayout>
diff --git a/java/res/values-cs/strings.xml b/java/res/values-cs/strings.xml
index 4687406f3..4bc1f5543 100644
--- a/java/res/values-cs/strings.xml
+++ b/java/res/values-cs/strings.xml
@@ -39,7 +39,8 @@
<string name="auto_cap" msgid="1719746674854628252">"Velká písmena automaticky"</string>
<string name="auto_cap_summary" msgid="3260681697600786825">"Zahájit větu velkým písmenem"</string>
<string name="auto_punctuate" msgid="7276672334264521751">"Automatická interpunkce"</string>
- <string name="auto_punctuate_summary" msgid="6589441565817502132"></string>
+ <!-- no translation found for auto_punctuate_summary (6589441565817502132) -->
+ <skip />
<string name="quick_fixes" msgid="5353213327680897927">"Rychlé opravy"</string>
<string name="quick_fixes_summary" msgid="3405028402510332373">"Opravuje nejčastější chyby při psaní"</string>
<string name="show_suggestions" msgid="507074425254289133">"Zobrazit návrhy"</string>
@@ -88,7 +89,8 @@
<string name="voice_hint_dialog_message" msgid="6892342981545727994">"Chcete-li použít hlasový vstup, stiskněte tlačítko mikrofonu nebo přejeďte prstem přes klávesnici na obrazovce."</string>
<string name="voice_listening" msgid="467518160751321844">"Mluvte"</string>
<string name="voice_working" msgid="6666937792815731889">"Probíhá zpracování"</string>
- <string name="voice_initializing" msgid="661962047129906646"></string>
+ <!-- no translation found for voice_initializing (661962047129906646) -->
+ <skip />
<string name="voice_error" msgid="5140896300312186162">"Chyba. Zkuste to prosím znovu."</string>
<string name="voice_network_error" msgid="6649556447401862563">"Připojení se nezdařilo."</string>
<string name="voice_too_much_speech" msgid="5746973620134227376">"Chyba, řeč je příliš dlouhá."</string>
diff --git a/java/res/values-da/strings.xml b/java/res/values-da/strings.xml
index 7a2c59f76..750d67b20 100644
--- a/java/res/values-da/strings.xml
+++ b/java/res/values-da/strings.xml
@@ -39,7 +39,8 @@
<string name="auto_cap" msgid="1719746674854628252">"Skriv aut. med stort"</string>
<string name="auto_cap_summary" msgid="3260681697600786825">"Første bogstav i en sætning skrives med stort"</string>
<string name="auto_punctuate" msgid="7276672334264521751">"Foretag automatisk tegnsætning"</string>
- <string name="auto_punctuate_summary" msgid="6589441565817502132"></string>
+ <!-- no translation found for auto_punctuate_summary (6589441565817502132) -->
+ <skip />
<string name="quick_fixes" msgid="5353213327680897927">"Hurtige løsninger"</string>
<string name="quick_fixes_summary" msgid="3405028402510332373">"Retter almindelige stavefejl"</string>
<string name="show_suggestions" msgid="507074425254289133">"Vis forslag"</string>
@@ -88,7 +89,8 @@
<string name="voice_hint_dialog_message" msgid="6892342981545727994">"For at bruge stemme-input skal du trykke på knappen mikrofon eller lade glide fingeren hen over skærmtastaturet."</string>
<string name="voice_listening" msgid="467518160751321844">"Tal nu"</string>
<string name="voice_working" msgid="6666937792815731889">"Arbejder"</string>
- <string name="voice_initializing" msgid="661962047129906646"></string>
+ <!-- no translation found for voice_initializing (661962047129906646) -->
+ <skip />
<string name="voice_error" msgid="5140896300312186162">"Fejl. Prøv igen."</string>
<string name="voice_network_error" msgid="6649556447401862563">"Kunne ikke oprette forbindelse"</string>
<string name="voice_too_much_speech" msgid="5746973620134227376">"Fejl. For meget tale."</string>
diff --git a/java/res/values-de/strings.xml b/java/res/values-de/strings.xml
index 9295f2332..ed6408dde 100644
--- a/java/res/values-de/strings.xml
+++ b/java/res/values-de/strings.xml
@@ -39,7 +39,8 @@
<string name="auto_cap" msgid="1719746674854628252">"Autom. Groß-/Kleinschr."</string>
<string name="auto_cap_summary" msgid="3260681697600786825">"Sätze mit Großbuchstaben beginnen"</string>
<string name="auto_punctuate" msgid="7276672334264521751">"Autom. Zeichensetzung"</string>
- <string name="auto_punctuate_summary" msgid="6589441565817502132"></string>
+ <!-- no translation found for auto_punctuate_summary (6589441565817502132) -->
+ <skip />
<string name="quick_fixes" msgid="5353213327680897927">"Quick Fixes"</string>
<string name="quick_fixes_summary" msgid="3405028402510332373">"Korrigiert gängige Tippfehler"</string>
<string name="show_suggestions" msgid="507074425254289133">"Vorschläge anzeigen"</string>
@@ -88,7 +89,8 @@
<string name="voice_hint_dialog_message" msgid="6892342981545727994">"Um die Spracheingabe zu verwenden, drücken Sie die Mikrofontaste oder ziehen Sie Ihren Finger über die Bildschirmtastatur."</string>
<string name="voice_listening" msgid="467518160751321844">"Jetzt sprechen"</string>
<string name="voice_working" msgid="6666937792815731889">"Vorgang läuft"</string>
- <string name="voice_initializing" msgid="661962047129906646"></string>
+ <!-- no translation found for voice_initializing (661962047129906646) -->
+ <skip />
<string name="voice_error" msgid="5140896300312186162">"Fehler. Versuchen Sie es erneut.."</string>
<string name="voice_network_error" msgid="6649556447401862563">"Keine Verbindung"</string>
<string name="voice_too_much_speech" msgid="5746973620134227376">"Fehler – Text zu lang"</string>
diff --git a/java/res/values-el/strings.xml b/java/res/values-el/strings.xml
index 8c78e5cf1..c4a50771a 100644
--- a/java/res/values-el/strings.xml
+++ b/java/res/values-el/strings.xml
@@ -39,7 +39,8 @@
<string name="auto_cap" msgid="1719746674854628252">"Αυτόματη χρήση κεφαλαίων"</string>
<string name="auto_cap_summary" msgid="3260681697600786825">"Κεφαλαίο το πρώτο γράμμα της πρότασης"</string>
<string name="auto_punctuate" msgid="7276672334264521751">"Αυτόματος τονισμός"</string>
- <string name="auto_punctuate_summary" msgid="6589441565817502132"></string>
+ <!-- no translation found for auto_punctuate_summary (6589441565817502132) -->
+ <skip />
<string name="quick_fixes" msgid="5353213327680897927">"Γρήγορες διορθώσεις"</string>
<string name="quick_fixes_summary" msgid="3405028402510332373">"Διορθώνει συνηθισμένα λάθη πληκτρολόγησης"</string>
<string name="show_suggestions" msgid="507074425254289133">"Εμφάνιση υποδείξεων"</string>
@@ -88,7 +89,8 @@
<string name="voice_hint_dialog_message" msgid="6892342981545727994">"Για να χρησιμοποιήσετε τις φωνητικές εντολές, πιέστε το κουμπί μικροφώνου ή σύρετε το δάχτυλό σας κατά μήκος του πληκτρολογίου της οθόνης."</string>
<string name="voice_listening" msgid="467518160751321844">"Μιλήστε τώρα"</string>
<string name="voice_working" msgid="6666937792815731889">"Σε λειτουργία"</string>
- <string name="voice_initializing" msgid="661962047129906646"></string>
+ <!-- no translation found for voice_initializing (661962047129906646) -->
+ <skip />
<string name="voice_error" msgid="5140896300312186162">"Σφάλμα. Δοκιμάστε ξανά."</string>
<string name="voice_network_error" msgid="6649556447401862563">"Δεν ήταν δυνατή η σύνδεση"</string>
<string name="voice_too_much_speech" msgid="5746973620134227376">"Σφάλμα, πολλές λέξεις."</string>
diff --git a/java/res/values-es-rUS/strings.xml b/java/res/values-es-rUS/strings.xml
index 4fd2e10ce..cd17dba31 100644
--- a/java/res/values-es-rUS/strings.xml
+++ b/java/res/values-es-rUS/strings.xml
@@ -39,7 +39,8 @@
<string name="auto_cap" msgid="1719746674854628252">"Mayúsculas automáticas"</string>
<string name="auto_cap_summary" msgid="3260681697600786825">"Poner en mayúscula el inicio de una oración"</string>
<string name="auto_punctuate" msgid="7276672334264521751">"Puntuación automática"</string>
- <string name="auto_punctuate_summary" msgid="6589441565817502132"></string>
+ <!-- no translation found for auto_punctuate_summary (6589441565817502132) -->
+ <skip />
<string name="quick_fixes" msgid="5353213327680897927">"Arreglos rápidos"</string>
<string name="quick_fixes_summary" msgid="3405028402510332373">"Corrige errores de escritura comunes"</string>
<string name="show_suggestions" msgid="507074425254289133">"Mostrar sugerencias"</string>
@@ -88,7 +89,8 @@
<string name="voice_hint_dialog_message" msgid="6892342981545727994">"Para realizar entrada por voz, presiona el botón del micrófono o desliza tus dedos por el teclado en pantalla."</string>
<string name="voice_listening" msgid="467518160751321844">"Habla ahora"</string>
<string name="voice_working" msgid="6666937792815731889">"Procesando"</string>
- <string name="voice_initializing" msgid="661962047129906646"></string>
+ <!-- no translation found for voice_initializing (661962047129906646) -->
+ <skip />
<string name="voice_error" msgid="5140896300312186162">"Error. Vuelve a intentarlo."</string>
<string name="voice_network_error" msgid="6649556447401862563">"No se pudo establecer la conexión."</string>
<string name="voice_too_much_speech" msgid="5746973620134227376">"Error, demasiado discurso."</string>
diff --git a/java/res/values-es/strings.xml b/java/res/values-es/strings.xml
index 7789a39d3..fbe3ad347 100644
--- a/java/res/values-es/strings.xml
+++ b/java/res/values-es/strings.xml
@@ -39,7 +39,8 @@
<string name="auto_cap" msgid="1719746674854628252">"Uso de mayúsculas auto."</string>
<string name="auto_cap_summary" msgid="3260681697600786825">"Escribir en mayúscula el principio de la frase"</string>
<string name="auto_punctuate" msgid="7276672334264521751">"Puntuación automática"</string>
- <string name="auto_punctuate_summary" msgid="6589441565817502132"></string>
+ <!-- no translation found for auto_punctuate_summary (6589441565817502132) -->
+ <skip />
<string name="quick_fixes" msgid="5353213327680897927">"Correcciones rápidas"</string>
<string name="quick_fixes_summary" msgid="3405028402510332373">"Corrige los errores tipográficos que se cometen con más frecuencia."</string>
<string name="show_suggestions" msgid="507074425254289133">"Mostrar sugerencias"</string>
@@ -88,7 +89,8 @@
<string name="voice_hint_dialog_message" msgid="6892342981545727994">"Para utilizar la función de introducción de voz, pulsa el botón de micrófono o desliza el dedo por el teclado en pantalla."</string>
<string name="voice_listening" msgid="467518160751321844">"Hablar ahora"</string>
<string name="voice_working" msgid="6666937792815731889">"En curso"</string>
- <string name="voice_initializing" msgid="661962047129906646"></string>
+ <!-- no translation found for voice_initializing (661962047129906646) -->
+ <skip />
<string name="voice_error" msgid="5140896300312186162">"Se ha producido un error. Inténtalo de nuevo."</string>
<string name="voice_network_error" msgid="6649556447401862563">"No se ha podido establecer conexión."</string>
<string name="voice_too_much_speech" msgid="5746973620134227376">"Se ha producido un error debido a un exceso de introducción de datos de voz."</string>
diff --git a/java/res/values-fr/strings.xml b/java/res/values-fr/strings.xml
index 5589c677f..2cabe40d9 100644
--- a/java/res/values-fr/strings.xml
+++ b/java/res/values-fr/strings.xml
@@ -39,7 +39,8 @@
<string name="auto_cap" msgid="1719746674854628252">"Majuscules auto"</string>
<string name="auto_cap_summary" msgid="3260681697600786825">"Mettre en majuscule la première lettre de chaque phrase"</string>
<string name="auto_punctuate" msgid="7276672334264521751">"Ponctuation automatique"</string>
- <string name="auto_punctuate_summary" msgid="6589441565817502132"></string>
+ <!-- no translation found for auto_punctuate_summary (6589441565817502132) -->
+ <skip />
<string name="quick_fixes" msgid="5353213327680897927">"Corrections rapides"</string>
<string name="quick_fixes_summary" msgid="3405028402510332373">"Corrige les fautes de frappe courantes"</string>
<string name="show_suggestions" msgid="507074425254289133">"Afficher les suggestions"</string>
@@ -88,7 +89,8 @@
<string name="voice_hint_dialog_message" msgid="6892342981545727994">"Pour utiliser la saisie vocale, appuyez sur la touche du microphone ou faites glisser votre doigt sur le clavier à l\'écran."</string>
<string name="voice_listening" msgid="467518160751321844">"Parlez maintenant"</string>
<string name="voice_working" msgid="6666937792815731889">"Traitement en cours"</string>
- <string name="voice_initializing" msgid="661962047129906646"></string>
+ <!-- no translation found for voice_initializing (661962047129906646) -->
+ <skip />
<string name="voice_error" msgid="5140896300312186162">"Erreur. Veuillez réessayer."</string>
<string name="voice_network_error" msgid="6649556447401862563">"Connexion impossible"</string>
<string name="voice_too_much_speech" msgid="5746973620134227376">"Erreur, discours trop long."</string>
diff --git a/java/res/values-it/strings.xml b/java/res/values-it/strings.xml
index 7c4ffbeb0..8100175b3 100644
--- a/java/res/values-it/strings.xml
+++ b/java/res/values-it/strings.xml
@@ -39,7 +39,8 @@
<string name="auto_cap" msgid="1719746674854628252">"Maiuscole automatiche"</string>
<string name="auto_cap_summary" msgid="3260681697600786825">"Rendi maiuscole le iniziali delle frasi"</string>
<string name="auto_punctuate" msgid="7276672334264521751">"Punteggiatura automat."</string>
- <string name="auto_punctuate_summary" msgid="6589441565817502132"></string>
+ <!-- no translation found for auto_punctuate_summary (6589441565817502132) -->
+ <skip />
<string name="quick_fixes" msgid="5353213327680897927">"Correzioni veloci"</string>
<string name="quick_fixes_summary" msgid="3405028402510332373">"Corregge gli errori di digitazione più comuni"</string>
<string name="show_suggestions" msgid="507074425254289133">"Mostra suggerimenti"</string>
@@ -88,7 +89,8 @@
<string name="voice_hint_dialog_message" msgid="6892342981545727994">"Per utilizzare i comandi vocali, premi il pulsante del microfono o fai scorrere il dito sulla tastiera sullo schermo."</string>
<string name="voice_listening" msgid="467518160751321844">"Parla ora"</string>
<string name="voice_working" msgid="6666937792815731889">"Elaborazione in corso"</string>
- <string name="voice_initializing" msgid="661962047129906646"></string>
+ <!-- no translation found for voice_initializing (661962047129906646) -->
+ <skip />
<string name="voice_error" msgid="5140896300312186162">"Errore. Riprova più tardi."</string>
<string name="voice_network_error" msgid="6649556447401862563">"Impossibile connettersi."</string>
<string name="voice_too_much_speech" msgid="5746973620134227376">"Errore: conversazione troppo lunga."</string>
diff --git a/java/res/values-ja/strings.xml b/java/res/values-ja/strings.xml
index 1412c854f..7867684cb 100644
--- a/java/res/values-ja/strings.xml
+++ b/java/res/values-ja/strings.xml
@@ -39,7 +39,8 @@
<string name="auto_cap" msgid="1719746674854628252">"自動大文字変換"</string>
<string name="auto_cap_summary" msgid="3260681697600786825">"英字入力で文頭文字を大文字にする"</string>
<string name="auto_punctuate" msgid="7276672334264521751">"句読点を自動入力"</string>
- <string name="auto_punctuate_summary" msgid="6589441565817502132"></string>
+ <!-- no translation found for auto_punctuate_summary (6589441565817502132) -->
+ <skip />
<string name="quick_fixes" msgid="5353213327680897927">"クイックフィックス"</string>
<string name="quick_fixes_summary" msgid="3405028402510332373">"よくある誤字・脱字を修正します"</string>
<string name="show_suggestions" msgid="507074425254289133">"入力候補を表示"</string>
@@ -88,7 +89,8 @@
<string name="voice_hint_dialog_message" msgid="6892342981545727994">"音声入力するには、マイクボタンを押すか画面キーボードをスワイプしてください。"</string>
<string name="voice_listening" msgid="467518160751321844">"お話しください"</string>
<string name="voice_working" msgid="6666937792815731889">"処理中"</string>
- <string name="voice_initializing" msgid="661962047129906646"></string>
+ <!-- no translation found for voice_initializing (661962047129906646) -->
+ <skip />
<string name="voice_error" msgid="5140896300312186162">"エラーです。もう一度お試しください。"</string>
<string name="voice_network_error" msgid="6649556447401862563">"接続できませんでした"</string>
<string name="voice_too_much_speech" msgid="5746973620134227376">"音声が長すぎてエラーになりました。"</string>
diff --git a/java/res/values-ko/strings.xml b/java/res/values-ko/strings.xml
index a574a6b8d..94d1ff4ef 100644
--- a/java/res/values-ko/strings.xml
+++ b/java/res/values-ko/strings.xml
@@ -39,7 +39,8 @@
<string name="auto_cap" msgid="1719746674854628252">"자동 대문자화"</string>
<string name="auto_cap_summary" msgid="3260681697600786825">"문장의 첫 글자를 대문자로 표시"</string>
<string name="auto_punctuate" msgid="7276672334264521751">"자동 구두점 입력"</string>
- <string name="auto_punctuate_summary" msgid="6589441565817502132"></string>
+ <!-- no translation found for auto_punctuate_summary (6589441565817502132) -->
+ <skip />
<string name="quick_fixes" msgid="5353213327680897927">"빠른 수정"</string>
<string name="quick_fixes_summary" msgid="3405028402510332373">"자주 발생하는 오타를 수정합니다."</string>
<string name="show_suggestions" msgid="507074425254289133">"추천 단어 표시"</string>
@@ -88,7 +89,8 @@
<string name="voice_hint_dialog_message" msgid="6892342981545727994">"음성 입력을 사용하려면 마이크 버튼을 누르거나 터치 키보드 위로 손가락을 미끄러지듯 움직이세요."</string>
<string name="voice_listening" msgid="467518160751321844">"지금 말하세요."</string>
<string name="voice_working" msgid="6666937792815731889">"인식 중"</string>
- <string name="voice_initializing" msgid="661962047129906646"></string>
+ <!-- no translation found for voice_initializing (661962047129906646) -->
+ <skip />
<string name="voice_error" msgid="5140896300312186162">"오류가 발생했습니다. 다시 시도해 보세요."</string>
<string name="voice_network_error" msgid="6649556447401862563">"연결할 수 없습니다."</string>
<string name="voice_too_much_speech" msgid="5746973620134227376">"음성을 너무 많이 입력했습니다."</string>
diff --git a/java/res/values-nb/strings.xml b/java/res/values-nb/strings.xml
index 60089941b..041d07eb3 100644
--- a/java/res/values-nb/strings.xml
+++ b/java/res/values-nb/strings.xml
@@ -39,7 +39,8 @@
<string name="auto_cap" msgid="1719746674854628252">"Stor forbokstav"</string>
<string name="auto_cap_summary" msgid="3260681697600786825">"Start automatisk setninger med stor bokstav"</string>
<string name="auto_punctuate" msgid="7276672334264521751">"Automatisk punktum"</string>
- <string name="auto_punctuate_summary" msgid="6589441565817502132"></string>
+ <!-- no translation found for auto_punctuate_summary (6589441565817502132) -->
+ <skip />
<string name="quick_fixes" msgid="5353213327680897927">"Autokorrektur"</string>
<string name="quick_fixes_summary" msgid="3405028402510332373">"Retter vanlige stavefeil"</string>
<string name="show_suggestions" msgid="507074425254289133">"Vis forslag"</string>
@@ -88,7 +89,8 @@
<string name="voice_hint_dialog_message" msgid="6892342981545727994">"Du bruker talekommandoer ved å trykke på mikrofonknappen eller skyve fingeren over tastaturet på skjermen."</string>
<string name="voice_listening" msgid="467518160751321844">"Snakk nå"</string>
<string name="voice_working" msgid="6666937792815731889">"Arbeider"</string>
- <string name="voice_initializing" msgid="661962047129906646"></string>
+ <!-- no translation found for voice_initializing (661962047129906646) -->
+ <skip />
<string name="voice_error" msgid="5140896300312186162">"Feil. Prøv på nytt."</string>
<string name="voice_network_error" msgid="6649556447401862563">"Kunne ikke koble til"</string>
<string name="voice_too_much_speech" msgid="5746973620134227376">"Feil – for mye tale"</string>
diff --git a/java/res/values-nl/strings.xml b/java/res/values-nl/strings.xml
index 27147b8fe..00b197bf1 100644
--- a/java/res/values-nl/strings.xml
+++ b/java/res/values-nl/strings.xml
@@ -39,7 +39,8 @@
<string name="auto_cap" msgid="1719746674854628252">"Auto-hoofdlettergebruik"</string>
<string name="auto_cap_summary" msgid="3260681697600786825">"Hoofdletter gebruiken aan het begin van een zin"</string>
<string name="auto_punctuate" msgid="7276672334264521751">"Automatische interpunctie"</string>
- <string name="auto_punctuate_summary" msgid="6589441565817502132"></string>
+ <!-- no translation found for auto_punctuate_summary (6589441565817502132) -->
+ <skip />
<string name="quick_fixes" msgid="5353213327680897927">"Snelle oplossingen"</string>
<string name="quick_fixes_summary" msgid="3405028402510332373">"Hiermee worden veelvoorkomende typefouten gecorrigeerd"</string>
<string name="show_suggestions" msgid="507074425254289133">"Suggesties weergeven"</string>
@@ -88,7 +89,8 @@
<string name="voice_hint_dialog_message" msgid="6892342981545727994">"Als u spraakinvoer gebruikt, drukt u op de microfoonknop of schuift u uw vinger over het schermtoetsenbord."</string>
<string name="voice_listening" msgid="467518160751321844">"Nu spreken"</string>
<string name="voice_working" msgid="6666937792815731889">"Wordt uitgevoerd"</string>
- <string name="voice_initializing" msgid="661962047129906646"></string>
+ <!-- no translation found for voice_initializing (661962047129906646) -->
+ <skip />
<string name="voice_error" msgid="5140896300312186162">"Fout. Probeer het opnieuw."</string>
<string name="voice_network_error" msgid="6649556447401862563">"Kan geen verbinding maken"</string>
<string name="voice_too_much_speech" msgid="5746973620134227376">"Fout, te lange spraakinvoer."</string>
diff --git a/java/res/values-pl/strings.xml b/java/res/values-pl/strings.xml
index 1d42efd98..0c72727ce 100644
--- a/java/res/values-pl/strings.xml
+++ b/java/res/values-pl/strings.xml
@@ -39,7 +39,8 @@
<string name="auto_cap" msgid="1719746674854628252">"Wstawiaj wielkie litery"</string>
<string name="auto_cap_summary" msgid="3260681697600786825">"Zamieniaj na wielką pierwszą literę zdania"</string>
<string name="auto_punctuate" msgid="7276672334264521751">"Automatyczna interpunkcja"</string>
- <string name="auto_punctuate_summary" msgid="6589441565817502132"></string>
+ <!-- no translation found for auto_punctuate_summary (6589441565817502132) -->
+ <skip />
<string name="quick_fixes" msgid="5353213327680897927">"Szybkie poprawki"</string>
<string name="quick_fixes_summary" msgid="3405028402510332373">"Poprawia częste błędy wpisywania"</string>
<string name="show_suggestions" msgid="507074425254289133">"Pokaż sugestie"</string>
@@ -88,7 +89,8 @@
<string name="voice_hint_dialog_message" msgid="6892342981545727994">"Aby skorzystać z wprowadzania głosowego, naciśnij przycisk mikrofonu lub przesuń palcem po klawiaturze ekranowej."</string>
<string name="voice_listening" msgid="467518160751321844">"Mów teraz"</string>
<string name="voice_working" msgid="6666937792815731889">"W toku"</string>
- <string name="voice_initializing" msgid="661962047129906646"></string>
+ <!-- no translation found for voice_initializing (661962047129906646) -->
+ <skip />
<string name="voice_error" msgid="5140896300312186162">"Błąd. Spróbuj ponownie."</string>
<string name="voice_network_error" msgid="6649556447401862563">"Nie można nawiązać połączenia"</string>
<string name="voice_too_much_speech" msgid="5746973620134227376">"Błąd, zbyt długa wypowiedź."</string>
diff --git a/java/res/values-pt-rPT/strings.xml b/java/res/values-pt-rPT/strings.xml
index fac0e2dc0..35a9cb715 100644
--- a/java/res/values-pt-rPT/strings.xml
+++ b/java/res/values-pt-rPT/strings.xml
@@ -39,7 +39,8 @@
<string name="auto_cap" msgid="1719746674854628252">"Letras maiúsculas automáticas"</string>
<string name="auto_cap_summary" msgid="3260681697600786825">"Colocar inicial maiúscula no início de uma frase"</string>
<string name="auto_punctuate" msgid="7276672334264521751">"Pontuação automática"</string>
- <string name="auto_punctuate_summary" msgid="6589441565817502132"></string>
+ <!-- no translation found for auto_punctuate_summary (6589441565817502132) -->
+ <skip />
<string name="quick_fixes" msgid="5353213327680897927">"Correcções rápidas"</string>
<string name="quick_fixes_summary" msgid="3405028402510332373">"Corrige os erros de escrita comuns"</string>
<string name="show_suggestions" msgid="507074425254289133">"Mostrar sugestões"</string>
@@ -88,7 +89,8 @@
<string name="voice_hint_dialog_message" msgid="6892342981545727994">"Para utilizar a entrada de voz, prima o botão do microfone ou deslize o dedo no teclado do ecrã."</string>
<string name="voice_listening" msgid="467518160751321844">"Falar agora"</string>
<string name="voice_working" msgid="6666937792815731889">"A executar"</string>
- <string name="voice_initializing" msgid="661962047129906646"></string>
+ <!-- no translation found for voice_initializing (661962047129906646) -->
+ <skip />
<string name="voice_error" msgid="5140896300312186162">"Erro. Tente novamente."</string>
<string name="voice_network_error" msgid="6649556447401862563">"Não foi possível ligar"</string>
<string name="voice_too_much_speech" msgid="5746973620134227376">"Erro, discurso demasiado longo."</string>
diff --git a/java/res/values-pt/strings.xml b/java/res/values-pt/strings.xml
index ceab82a31..235fd65ac 100644
--- a/java/res/values-pt/strings.xml
+++ b/java/res/values-pt/strings.xml
@@ -39,7 +39,8 @@
<string name="auto_cap" msgid="1719746674854628252">"Capitaliz. automática"</string>
<string name="auto_cap_summary" msgid="3260681697600786825">"Colocar em maiúscula o início de uma frase"</string>
<string name="auto_punctuate" msgid="7276672334264521751">"Pontuação automática"</string>
- <string name="auto_punctuate_summary" msgid="6589441565817502132"></string>
+ <!-- no translation found for auto_punctuate_summary (6589441565817502132) -->
+ <skip />
<string name="quick_fixes" msgid="5353213327680897927">"Reparos rápidos"</string>
<string name="quick_fixes_summary" msgid="3405028402510332373">"Corrige erros comuns de digitação"</string>
<string name="show_suggestions" msgid="507074425254289133">"Mostrar sugestões"</string>
@@ -88,7 +89,8 @@
<string name="voice_hint_dialog_message" msgid="6892342981545727994">"Para usar a entrada de voz, pressione o botão com o microfone ou deslize o dedo sobre o teclado na tela."</string>
<string name="voice_listening" msgid="467518160751321844">"Fale agora"</string>
<string name="voice_working" msgid="6666937792815731889">"Trabalhando"</string>
- <string name="voice_initializing" msgid="661962047129906646"></string>
+ <!-- no translation found for voice_initializing (661962047129906646) -->
+ <skip />
<string name="voice_error" msgid="5140896300312186162">"Erro. Tente novamente."</string>
<string name="voice_network_error" msgid="6649556447401862563">"Não foi possível conectar"</string>
<string name="voice_too_much_speech" msgid="5746973620134227376">"Erro, fala muito longa."</string>
diff --git a/java/res/values-ru/strings.xml b/java/res/values-ru/strings.xml
index 9e5c59b58..e27402c7f 100644
--- a/java/res/values-ru/strings.xml
+++ b/java/res/values-ru/strings.xml
@@ -39,7 +39,8 @@
<string name="auto_cap" msgid="1719746674854628252">"Автоподст. заглавных"</string>
<string name="auto_cap_summary" msgid="3260681697600786825">"Делать заглавной первую букву предложения"</string>
<string name="auto_punctuate" msgid="7276672334264521751">"Автопунктуация"</string>
- <string name="auto_punctuate_summary" msgid="6589441565817502132"></string>
+ <!-- no translation found for auto_punctuate_summary (6589441565817502132) -->
+ <skip />
<string name="quick_fixes" msgid="5353213327680897927">"Быстрое исправление"</string>
<string name="quick_fixes_summary" msgid="3405028402510332373">"Исправлять распространенные опечатки"</string>
<string name="show_suggestions" msgid="507074425254289133">"Предлагать варианты"</string>
@@ -88,7 +89,8 @@
<string name="voice_hint_dialog_message" msgid="6892342981545727994">"Чтобы использовать голосовой ввод, нажмите кнопку микрофона или проведите пальцем по экранной клавиатуре."</string>
<string name="voice_listening" msgid="467518160751321844">"Говорите"</string>
<string name="voice_working" msgid="6666937792815731889">"Выполняется обработка"</string>
- <string name="voice_initializing" msgid="661962047129906646"></string>
+ <!-- no translation found for voice_initializing (661962047129906646) -->
+ <skip />
<string name="voice_error" msgid="5140896300312186162">"Ошибка. Повторите попытку."</string>
<string name="voice_network_error" msgid="6649556447401862563">"Ошибка подключения"</string>
<string name="voice_too_much_speech" msgid="5746973620134227376">"Слишком длинная фраза"</string>
diff --git a/java/res/values-sr/strings.xml b/java/res/values-sr/strings.xml
new file mode 100644
index 000000000..f706ebc3d
--- /dev/null
+++ b/java/res/values-sr/strings.xml
@@ -0,0 +1,299 @@
+<?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:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Title for Latin keyboard -->
+ <string name="english_ime_name">Андроидова тастатура</string>
+ <!-- Title for Latin keyboard settings activity / dialog -->
+ <string name="english_ime_settings">Подешавања андроидове тастатуре</string>
+
+ <!-- Option to provide vibrate/haptic feedback on keypress -->
+ <string name="vibrate_on_keypress">Вибрације при притиску</string>
+ <!-- Option to play back sound on keypress in soft keyboard -->
+ <string name="sound_on_keypress">Звук при притиску</string>
+
+ <!-- Option to enable using nearby keys when correcting/predicting -->
+ <string name="hit_correction">Исправљање грешака</string>
+
+ <!-- Description for hit_correction -->
+ <string name="hit_correction_summary">Исправљање грешака при уносу</string>
+
+ <!-- Option to enable using nearby keys when correcting/predicting in landscape-->
+ <string name="hit_correction_land">Грешке при водоравној оријентацији</string>
+
+ <!-- Description for hit_correction in landscape -->
+ <string name="hit_correction_land_summary">Исправљање грешака при уносу при
+ водоравном положају</string>
+
+ <!-- Option to automatically correct word on hitting space -->
+ <string name="auto_correction">Предлози речи</string>
+
+ <!-- Description for auto_correction -->
+ <string name="auto_correction_summary">Аутоматска исправка претходно унесене речи</string>
+
+ <!-- Option to enable text prediction -->
+ <string name="prediction">Предлози речи</string>
+ <!-- Category title for text prediction -->
+ <string name="prediction_category">Подешавања за предлоге речи</string>
+ <!-- Description for text prediction -->
+ <string name="prediction_summary">Укључи аутоматске наставке при уносу</string>
+
+ <!-- Dialog title for auto complete choices -->
+ <string name="auto_complete_dialog_title">Аутоматски наставци</string>
+
+ <!-- Option to enable text prediction in landscape -->
+ <string name="prediction_landscape">Увећан поље за унос текста</string>
+ <!-- Description for text prediction -->
+ <string name="prediction_landscape_summary">Сакриј предлоге речи при водоравном положају</string>
+
+ <!-- Option to enable auto capitalization of sentences -->
+ <string name="auto_cap">Аутоматска величина слова</string>
+ <!-- Description for auto cap -->
+ <string name="auto_cap_summary">Велико слово на почетку реченице</string>
+ <!-- Option to enable auto punctuate -->
+ <string name="auto_punctuate">Аутоматска интерпункција</string>
+ <!-- Description for auto punctuate -->
+ <string name="auto_punctuate_summary">Аутоматско постављање интерпункцијских знака при уносу.</string>
+
+ <!-- Option to enable quick fixes -->
+ <string name="quick_fixes">Брзе исправке</string>
+ <!-- Description for quick fixes -->
+ <string name="quick_fixes_summary">Аутоматска исправка честих грешака</string>
+
+ <!-- Option to enable showing suggestions -->
+ <string name="show_suggestions">Приказ предлога</string>
+ <!-- Description for show suggestions -->
+ <string name="show_suggestions_summary">Приказује предлоге речи током уноса</string>
+
+ <!-- Option to enable auto completion -->
+ <string name="auto_complete">Аутоматска допуна</string>
+ <!-- Description for auto completion -->
+ <string name="auto_complete_summary">Размакница и интерпункција аутоматски убацују означену реч.</string>
+
+ <!-- Array of prediction modes -->
+ <string-array name="prediction_modes">
+ <item>Искључено</item>
+ <item>Основно</item>
+ <item>Напредно</item>
+ </string-array>
+
+ <string-array name="prediction_modes_values" translatable="false">
+ <item>@string/prediction_none</item>
+ <item>@string/prediction_basic</item>
+ <item>@string/prediction_full</item>
+ </string-array>
+
+ <!-- Indicates that a word has been added to the dictionary -->
+ <string name="added_word"><xliff:g id="word">%s</xliff:g> : Saved</string>
+ <!-- Tip to long press on keys -->
+ <string name="tip_long_press">Дуги притисак на тастере открива проширене знаке (ø, ö, итд.)</string>
+ <!-- Tip to dismiss keyboard -->
+ <string name="tip_dismiss">Притисните тастер за назад \u21B6 како бисте затворили тастатуру</string>
+ <!-- Tip to press ?123 to access numbers and symbols -->
+ <string name="tip_access_symbols">Приступ бројевима и симболима</string>
+ <!-- Tip to long press on typed word to add to dictionary -->
+ <string name="tip_add_to_dictionary">Притисните и држите притиснуту реч са крајње леве стране
+ како бисте је додали у речник</string>
+
+ <!-- Instruction to touch the bubble to continue -->
+ <string name="touch_to_continue">Притисните овај подсетник да наставите »</string>
+
+ <!-- Instruction to touch the bubble to start typing -->
+ <string name="touch_to_finish">Притисните овде да бисте затворили подсетник и наставили унос!</string>
+
+ <!-- Tutorial tip 1 - The keyboard opens any time you touch a text field -->
+ <string name="tip_to_open_keyboard"><b>Тастатура се отвара кад год је потребно да унесете текст</b></string>
+
+ <!-- Tutorial tip 2 - Touch and hold a key to view accents (examples) -->
+ <string name="tip_to_view_accents"><b>Притисните и држите тастер како бисте видели проширене
+ знаке\n(„, ‟, итд.)</b>
+ </string>
+
+ <!-- Tutorial tip 3 - How to switch to number/symbol keyboard -->
+ <string name="tip_to_open_symbols"><b>Пребаците се на бројеве и симболе притиском на овај тастер
+ </b></string>
+
+ <!-- Tutorial tip 4 - How to switch back to alphabet keyboard -->
+ <string name="tip_to_close_symbols"><b>Вратите се назад на слова притиском на овај тастер</b></string>
+
+ <!-- Tutorial tip 5 - How to launch keyboard settings -->
+ <string name="tip_to_launch_settings"><b>Притисните и држите притиснут овај тастер да бисте променили
+ подешавања тастатуре, попут аутоматског настављања</b></string>
+
+ <!-- Tutorial tip 6 - Done with the tutorial -->
+ <string name="tip_to_start_typing"><b>Пробајте сами!</b></string>
+
+
+ <!-- Label for soft enter key when it performs GO action. Must be short to fit on key! -->
+ <string name="label_go_key">Иди</string>
+ <!-- Label for soft enter key when it performs NEXT action. Must be short to fit on key! -->
+ <string name="label_next_key">Даље</string>
+ <!-- Label for soft enter key when it performs DONE action. Must be short to fit on key! -->
+ <string name="label_done_key">Крај</string>
+ <!-- Label for soft enter key when it performs SEND action. Must be short to fit on key! -->
+ <string name="label_send_key">Шаљи</string>
+ <!-- Label for "switch to symbols" key. Must be short to fit on key! -->
+ <string name="label_symbol_key">\?123</string>
+ <!-- Label for "switch to numeric" key. Must be short to fit on key! -->
+ <string name="label_phone_key">123</string>
+ <!-- Label for "switch to alphabetic" key. Must be short to fit on key! -->
+ <string name="label_alpha_key">АБВ</string>
+ <!-- Label for ALT modifier key. Must be short to fit on key! -->
+ <string name="label_alt_key">ALT</string>
+
+ <!-- Voice related labels -->
+
+ <!-- Title of the warning dialog that shows when a user initiates voice input for
+ the first time. -->
+ <string name="voice_warning_title">Говорни унос</string>
+
+ <!-- Message that gets put at the top of the warning dialog if the user is attempting to use
+ voice input in a currently unsupported locale. Voice input will work for such a user,
+ but it will only recognize them in English. -->
+ <string name="voice_warning_locale_not_supported">Говорни унос није тренутно подржан на Вашем језику,
+ али ради на енглеском.</string>
+
+ <!-- Message of the warning dialog that shows when a user initiates voice input for
+ the first time, or turns it on in settings. -->
+ <string name="voice_warning_may_not_understand">Говорни унос је експериментална могућност која користи
+ Google-ово мрежно препознавање говора.</string>
+
+ <!-- An additional part of the warning dialog for voice input that only shows when the user
+ actually initiates voice input, rather than just turning it on in settings. -->
+ <string name="voice_warning_how_to_turn_off">Како бисте искључили говорни унос, изаберите подешавања
+ тастатуре.</string>
+
+ <!-- Message to show when user clicks the swiping hint (which says
+ "Swipe across keyboard to speak"). Also shown when enabling settings. -->
+ <string name="voice_hint_dialog_message">Како бисте укључили говорни унос, притисните дугме са сличицом
+ микрофона или превуците прстом преко целе дужине тастатуре.</string>
+
+ <!-- Short message to tell the user the system is ready for them to speak. -->
+ <string name="voice_listening">Говорите сада</string>
+
+ <!-- Short message shown after the user finishes speaking. -->
+ <string name="voice_working">Обрада је у току</string>
+
+ <!-- Short message shown before the user should speak. -->
+ <string name="voice_initializing"></string>
+
+ <!-- Short message shown when a generic error occurs. -->
+ <string name="voice_error">Грешка. Молимо пробајте поново.</string>
+
+ <!-- Short message shown for a network error. -->
+ <string name="voice_network_error">Повезивање није успело</string>
+
+ <!-- Short message shown for a network error where the utterance was really long,
+ in which case we should suggest that the user speak less. -->
+ <string name="voice_too_much_speech">Грешка, говор је предугачак.</string>
+
+ <!-- Short message shown for an audio error. -->
+ <string name="voice_audio_error">Проблем са звуком</string>
+
+ <!-- Short message shown for an error with the voice server. -->
+ <string name="voice_server_error">Грешка на серверу</string>
+
+ <!-- Short message shown when no speech is heard. -->
+ <string name="voice_speech_timeout">Говор није снимљен</string>
+
+ <!-- Short message shown when the server couldn't parse any speech. -->
+ <string name="voice_no_match">Нема погодака</string>
+
+ <!-- Short message shown when the user initiates voice and voice
+ search is not installed. -->
+ <string name="voice_not_installed">Говорна претрага није инсталирана</string>
+
+ <!-- Short hint shown in candidate view to explain voice input. -->
+ <string name="voice_swipe_hint"><b>Савет:</b> Превуците прстом преко тастатуре а онда говорите.</string>
+
+ <!-- Short hint shown in candidate view to explain that user can speak punctuation. -->
+ <string name="voice_punctuation_hint"><b>Савет:</b> Следећи пут, изговорите назив интерпункције,
+ попут „тачка“, „запета“ или „знак питања“.</string>
+
+ <!-- Label on button to stop recognition. Must be short to fit on button. -->
+ <string name="cancel">Откажи</string>
+
+ <!-- Label on button when an error occurs -->
+ <string name="ok">У реду</string>
+
+ <!-- Preferences item for enabling speech input -->
+ <string name="voice_input">Говорни унос</string>
+
+ <!-- Array of Voice Input modes -->
+ <string-array name="voice_input_modes">
+ <item>На главној тастатури</item>
+ <item>На симболичкој тастатури</item>
+ <item>Искључен</item>
+ </string-array>
+
+ <string-array name="voice_input_modes_values" translatable="false">
+ <item>@string/voice_mode_main</item>
+ <item>@string/voice_mode_symbols</item>
+ <item>@string/voice_mode_off</item>
+ </string-array>
+
+ <!-- Array of Voice Input modes summary -->
+ <string-array name="voice_input_modes_summary">
+ <item>Микрофон на главној тастатури</item>
+ <item>Микрофон на симболичкој тастатури</item>
+ <item>Говорни унос је искључен</item>
+ </string-array>
+
+ <!-- Press the "enter" key after the user speaks. Option on settings.-->
+ <string name="auto_submit">Аутоматско слање по говорном уносу</string>
+
+ <!-- Press the "enter" key after the user speaks. Summary of option in settings.-->
+ <string name="auto_submit_summary">Дугме за претрагу се аутоматски притиска при претрази или преласку
+ на следеће поље за унос.</string>
+
+ <!-- IME Tutorial screen (ROMAN) --><skip />
+ <!-- appears above image showing the user to click on a TextView to show the IME -->
+ <string name="open_the_keyboard"><font size="17"><b>Отварање тастатуре\n</b></font><font size="3">\n</font>Touch any text field.</string>
+
+ <!-- appears above the image showing the back button used to close the keyboard -->
+ <string name="close_the_keyboard"><font size="17"><b>Затварање тастатуре\n</b></font><font size="3">\n</font>Press the Back key.</string>
+
+ <!-- appears above image showing how to use touch and hold -->
+ <string name="touch_and_hold"><font size="17"><b>Притисните \u0026 и држите пристиснут тастер за опције\n</b></font><font size="3">\n</font>Приступ акцентима и интерпункцији.</string>
+
+ <!-- appears above image showing how to access keyboard settings -->
+ <string name="keyboard_settings"><font size="17"><b>Подешавање тастатуре\n</b></font><font size="3">\n</font>Притисните \u0026 и држите тастер <b>\?123\</b>.</string>
+
+ <!-- popular web domains for the locale - most popular, displayed on the keyboard -->
+ <string name="popular_domain_0">".rs"</string>
+ <!-- popular web domains for the locale - item 1, displayed in the popup -->
+ <string name="popular_domain_1">".com"</string>
+ <!-- popular web domains for the locale - item 2, displayed in the popup -->
+ <string name="popular_domain_2">".net"</string>
+ <!-- popular web domains for the locale - item 3, displayed in the popup -->
+ <string name="popular_domain_3">".org"</string>
+ <!-- popular web domains for the locale - item 4, displayed in the popup -->
+ <string name="popular_domain_4">".edu"</string>
+
+ <!-- Menu item for launching Input method switcher -->
+ <string name="inputMethod">Метод за унос</string>
+
+ <!-- Title for input language selection screen -->
+ <string name="language_selection_title">Језици за унос</string>
+ <!-- Title summary for input language selection screen -->
+ <string name="language_selection_summary">Превуците прстом по размакници за промену језика</string>
+
+ <!-- Add to dictionary hint -->
+ <string name="hint_add_to_dictionary">\u2190 Притисните опет да бисте сачували</string>
+</resources>
diff --git a/java/res/values-sv/strings.xml b/java/res/values-sv/strings.xml
index bb1d47cf6..9c6c22159 100644
--- a/java/res/values-sv/strings.xml
+++ b/java/res/values-sv/strings.xml
@@ -39,7 +39,8 @@
<string name="auto_cap" msgid="1719746674854628252">"Automatiska versaler"</string>
<string name="auto_cap_summary" msgid="3260681697600786825">"Använd versal i början av mening"</string>
<string name="auto_punctuate" msgid="7276672334264521751">"Automatiska punkter"</string>
- <string name="auto_punctuate_summary" msgid="6589441565817502132"></string>
+ <!-- no translation found for auto_punctuate_summary (6589441565817502132) -->
+ <skip />
<string name="quick_fixes" msgid="5353213327680897927">"Snabba lösningar"</string>
<string name="quick_fixes_summary" msgid="3405028402510332373">"Åtgärdar automatiskt vanliga misstag"</string>
<string name="show_suggestions" msgid="507074425254289133">"Visa förslag"</string>
@@ -88,7 +89,8 @@
<string name="voice_hint_dialog_message" msgid="6892342981545727994">"Om du vill använda röstinmatning trycker du på mikrofonknappen eller drar fingret över tangentbordet på skärmen."</string>
<string name="voice_listening" msgid="467518160751321844">"Tala nu"</string>
<string name="voice_working" msgid="6666937792815731889">"Fungerar"</string>
- <string name="voice_initializing" msgid="661962047129906646"></string>
+ <!-- no translation found for voice_initializing (661962047129906646) -->
+ <skip />
<string name="voice_error" msgid="5140896300312186162">"Fel. Försök igen."</string>
<string name="voice_network_error" msgid="6649556447401862563">"Det gick inte att ansluta"</string>
<string name="voice_too_much_speech" msgid="5746973620134227376">"Fel, för mycket tal."</string>
diff --git a/java/res/values-tr/strings.xml b/java/res/values-tr/strings.xml
index 9baa1bfe2..0fbdc7da3 100644
--- a/java/res/values-tr/strings.xml
+++ b/java/res/values-tr/strings.xml
@@ -39,7 +39,8 @@
<string name="auto_cap" msgid="1719746674854628252">"Otomatik olarak büyük harf yap"</string>
<string name="auto_cap_summary" msgid="3260681697600786825">"Cümlenin baş harfini büyük yap"</string>
<string name="auto_punctuate" msgid="7276672334264521751">"Otomatik noktalama"</string>
- <string name="auto_punctuate_summary" msgid="6589441565817502132"></string>
+ <!-- no translation found for auto_punctuate_summary (6589441565817502132) -->
+ <skip />
<string name="quick_fixes" msgid="5353213327680897927">"Hızlı onarımlar"</string>
<string name="quick_fixes_summary" msgid="3405028402510332373">"Yaygın olarak yapılan yazım hatalarını düzeltir"</string>
<string name="show_suggestions" msgid="507074425254289133">"Önerileri göster"</string>
@@ -88,7 +89,8 @@
<string name="voice_hint_dialog_message" msgid="6892342981545727994">"Ses girişini kullanmak için mikrofon düğmesine basın veya parmağınızı dokunmatik klavye üzerinde kaydırın."</string>
<string name="voice_listening" msgid="467518160751321844">"Şimdi konuşun"</string>
<string name="voice_working" msgid="6666937792815731889">"Çalışıyor"</string>
- <string name="voice_initializing" msgid="661962047129906646"></string>
+ <!-- no translation found for voice_initializing (661962047129906646) -->
+ <skip />
<string name="voice_error" msgid="5140896300312186162">"Hata. Lütfen tekrar deneyin."</string>
<string name="voice_network_error" msgid="6649556447401862563">"Bağlanamadı"</string>
<string name="voice_too_much_speech" msgid="5746973620134227376">"Hata, çok uzun konuşma."</string>
diff --git a/java/res/values-xlarge/dimens.xml b/java/res/values-xlarge/dimens.xml
new file mode 100644
index 000000000..4e1c52877
--- /dev/null
+++ b/java/res/values-xlarge/dimens.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2010, 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>
+ <dimen name="key_height">72dip</dimen>
+ <dimen name="candidate_strip_height">46dip</dimen>
+ <dimen name="spacebar_vertical_correction">0dip</dimen>
+ <dimen name="key_text_size">35sp</dimen>
+</resources>
diff --git a/java/res/values-zh-rCN/strings.xml b/java/res/values-zh-rCN/strings.xml
index dd64bed97..9c9b25708 100644
--- a/java/res/values-zh-rCN/strings.xml
+++ b/java/res/values-zh-rCN/strings.xml
@@ -39,7 +39,8 @@
<string name="auto_cap" msgid="1719746674854628252">"自动大写"</string>
<string name="auto_cap_summary" msgid="3260681697600786825">"句首字母大写"</string>
<string name="auto_punctuate" msgid="7276672334264521751">"自动加标点"</string>
- <string name="auto_punctuate_summary" msgid="6589441565817502132"></string>
+ <!-- no translation found for auto_punctuate_summary (6589441565817502132) -->
+ <skip />
<string name="quick_fixes" msgid="5353213327680897927">"快速纠正"</string>
<string name="quick_fixes_summary" msgid="3405028402510332373">"纠正常见的输入错误"</string>
<string name="show_suggestions" msgid="507074425254289133">"显示建议"</string>
@@ -88,7 +89,8 @@
<string name="voice_hint_dialog_message" msgid="6892342981545727994">"要使用语音输入,请按麦克风按钮或者在屏幕键盘上滑动手指。"</string>
<string name="voice_listening" msgid="467518160751321844">"请开始说话"</string>
<string name="voice_working" msgid="6666937792815731889">"正在处理"</string>
- <string name="voice_initializing" msgid="661962047129906646"></string>
+ <!-- no translation found for voice_initializing (661962047129906646) -->
+ <skip />
<string name="voice_error" msgid="5140896300312186162">"出错,请重试。"</string>
<string name="voice_network_error" msgid="6649556447401862563">"无法连接"</string>
<string name="voice_too_much_speech" msgid="5746973620134227376">"出错,语音过长。"</string>
diff --git a/java/res/values-zh-rTW/strings.xml b/java/res/values-zh-rTW/strings.xml
index 5c54cc884..4f83be405 100644
--- a/java/res/values-zh-rTW/strings.xml
+++ b/java/res/values-zh-rTW/strings.xml
@@ -39,7 +39,8 @@
<string name="auto_cap" msgid="1719746674854628252">"自動大寫"</string>
<string name="auto_cap_summary" msgid="3260681697600786825">"句首字母大寫"</string>
<string name="auto_punctuate" msgid="7276672334264521751">"自動標點"</string>
- <string name="auto_punctuate_summary" msgid="6589441565817502132"></string>
+ <!-- no translation found for auto_punctuate_summary (6589441565817502132) -->
+ <skip />
<string name="quick_fixes" msgid="5353213327680897927">"快速修正"</string>
<string name="quick_fixes_summary" msgid="3405028402510332373">"修正一般打字錯誤"</string>
<string name="show_suggestions" msgid="507074425254289133">"顯示建議"</string>
@@ -88,7 +89,8 @@
<string name="voice_hint_dialog_message" msgid="6892342981545727994">"如要使用語音輸入,按下 [麥克風] 按鈕,或將手指滑過螢幕小鍵盤即可。"</string>
<string name="voice_listening" msgid="467518160751321844">"請說話"</string>
<string name="voice_working" msgid="6666937792815731889">"辨識中"</string>
- <string name="voice_initializing" msgid="661962047129906646"></string>
+ <!-- no translation found for voice_initializing (661962047129906646) -->
+ <skip />
<string name="voice_error" msgid="5140896300312186162">"發生錯誤,請再試一次。"</string>
<string name="voice_network_error" msgid="6649556447401862563">"無法連線"</string>
<string name="voice_too_much_speech" msgid="5746973620134227376">"錯誤:語音內容過長。"</string>
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml
new file mode 100644
index 000000000..e3171eb33
--- /dev/null
+++ b/java/res/values/attrs.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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>
+
+ <declare-styleable name="LatinKeyboardBaseView">
+ <!-- Default KeyboardView style. -->
+ <attr name="keyboardViewStyle" format="reference" />
+
+ <!-- Image for the key. This image needs to be a StateListDrawable, with the following
+ possible states: normal, pressed, checkable, checkable+pressed, checkable+checked,
+ checkable+checked+pressed. -->
+ <attr name="keyBackground" format="reference" />
+
+ <!-- Size of the text for character keys. -->
+ <attr name="keyTextSize" format="dimension" />
+
+ <!-- Size of the text for custom keys with some text and no icon. -->
+ <attr name="labelTextSize" format="dimension" />
+
+ <!-- Color to use for the label in a key. -->
+ <attr name="keyTextColor" format="color" />
+
+ <!-- Layout resource for key press feedback.-->
+ <attr name="keyPreviewLayout" format="reference" />
+
+ <!-- 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" />
+
+ <!-- Amount to offset the touch Y coordinate by, for bias correction. -->
+ <attr name="verticalCorrection" format="dimension" />
+
+ <!-- Layout resource for popup keyboards. -->
+ <attr name="popupLayout" format="reference" />
+
+ <attr name="shadowColor" format="color" />
+ <attr name="shadowRadius" format="float" />
+ <attr name="backgroundDimAmount" format="float" />
+
+ <attr name="keyTextStyle">
+ <flag name="normal" value="0" />
+ <flag name="bold" value="1" />
+ <flag name="italic" value="2" />
+ </attr>
+
+ <attr name="symbolColorScheme">
+ <flag name="white" value="0" />
+ <flag name="black" value="1" />
+ </attr>
+
+ </declare-styleable>
+
+</resources>
diff --git a/java/res/values/bools.xml b/java/res/values/bools.xml
index ebe2f04e5..a0cebbb94 100644
--- a/java/res/values/bools.xml
+++ b/java/res/values/bools.xml
@@ -25,4 +25,5 @@
<bool name="im_is_default">false</bool>
<!-- Whether or not voice input is enabled by default. -->
<bool name="voice_input_default">true</bool>
+ <bool name="config_swipeDisambiguation">true</bool>
</resources>
diff --git a/java/res/values/colors.xml b/java/res/values/colors.xml
index c90d9f6af..343a9405d 100644
--- a/java/res/values/colors.xml
+++ b/java/res/values/colors.xml
@@ -21,4 +21,12 @@
<color name="candidate_normal">#FF000000</color>
<color name="candidate_recommended">#FFE35900</color>
<color name="candidate_other">#ff808080</color>
-</resources> \ No newline at end of file
+ <color name="latinkeyboard_transparent">#00000000</color>
+ <color name="latinkeyboard_bar_language_shadow_white">#80000000</color>
+ <color name="latinkeyboard_bar_language_shadow_black">#80FFFFFF</color>
+ <color name="latinkeyboard_bar_language_text">#FF808080</color>
+ <color name="latinkeyboard_extension_background">#A0000000</color>
+ <color name="latinkeyboard_text_color">#FF000000</color>
+ <color name="latinkeyboard_key_color_white">#FFFFFFFF</color>
+ <color name="latinkeyboard_key_color_black">#FF000000</color>
+</resources>
diff --git a/java/res/values/dimens.xml b/java/res/values/dimens.xml
index 5b2095c0e..39dce9db0 100644
--- a/java/res/values/dimens.xml
+++ b/java/res/values/dimens.xml
@@ -23,4 +23,9 @@
<dimen name="bubble_pointer_offset">22dip</dimen>
<dimen name="candidate_strip_height">42dip</dimen>
<dimen name="spacebar_vertical_correction">4dip</dimen>
-</resources> \ No newline at end of file
+ <!-- If the screen height in landscape is larger than the below value, then the keyboard
+ will not go into extract (fullscreen) mode. -->
+ <dimen name="max_height_for_fullscreen">2.5in</dimen>
+ <dimen name="key_text_size">22sp</dimen>
+ <dimen name="key_debounce_hysteresis_distance">0.05in</dimen>
+</resources>
diff --git a/java/res/values/donottranslate.xml b/java/res/values/donottranslate.xml
index d5017353d..b7bfd9c3a 100644
--- a/java/res/values/donottranslate.xml
+++ b/java/res/values/donottranslate.xml
@@ -21,9 +21,9 @@
<!-- Symbols that are commonly considered word separators in this language -->
<string name="word_separators">.\u0009\u0020,;:!?\n()[]*&amp;@{}/&lt;&gt;_+=|\u0022</string>
<!-- Symbols that are sentence separators, for purposes of making it hug the last sentence. -->
- <string name="sentence_separators">.,!?</string>
+ <string name="sentence_separators">.,!?)</string>
<!-- Symbols that are suggested between words -->
- <string name="suggested_punctuations">!?,@_</string>
+ <string name="suggested_punctuations">!?,\u0022\u0027:()-/@_</string>
<!-- Accented characters related to "d" -->
<string name="alternates_for_d"></string>
<!-- Accented characters related to "r" -->
diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml
index 35dd3e089..e77155d50 100644
--- a/java/res/values/strings.xml
+++ b/java/res/values/strings.xml
@@ -25,9 +25,10 @@
<!-- Option to provide vibrate/haptic feedback on keypress -->
<string name="vibrate_on_keypress">Vibrate on keypress</string>
+
<!-- Option to play back sound on keypress in soft keyboard -->
<string name="sound_on_keypress">Sound on keypress</string>
-
+
<!-- Option to enable using nearby keys when correcting/predicting -->
<string name="hit_correction">Correct typing errors</string>
@@ -85,6 +86,11 @@
<!-- Description for auto completion -->
<string name="auto_complete_summary">Spacebar and punctuation automatically insert highlighted word</string>
+ <!-- Option to enable bigram completion -->
+ <string name="bigram_suggestion">Bigram Suggestions</string>
+ <!-- Description for auto completion -->
+ <string name="bigram_suggestion_summary">Use previous word to improve suggestion</string>
+
<!-- Array of prediction modes -->
<string-array name="prediction_modes">
<item>None</item>
@@ -322,4 +328,32 @@
<!-- Inform the user that a particular language has an available dictionary -->
<string name="has_dictionary">Dictionary available</string>
+
+ <!-- Option to send logs -->
+ <string name="prefs_enable_log">Enable user feedback</string>
+ <!-- Description for sending logs -->
+ <string name="prefs_description_log">Help improve this input method editor by automatically sending usage statistics and crash reports to Google.</string>
+
+ <string name="keyboard_layout">Keyboard Theme</string>
+ <string name="layout_basic" translatable="false">Basic</string>
+ <string name="layout_high_contrast" translatable="false">Basic (High Contrast)</string>
+ <string name="layout_stone_bold" translatable="false">Default (bold)</string>
+ <string name="layout_stone_normal" translatable="false">Default (normal)</string>
+
+ <string-array name="keyboard_layout_modes" translatable="false">
+ <item>@string/layout_basic</item>
+ <item>@string/layout_high_contrast</item>
+ <item>@string/layout_stone_normal</item>
+ <item>@string/layout_stone_bold</item>
+ </string-array>
+
+ <string-array name="keyboard_layout_modes_values" translatable="false">
+ <item>0</item>
+ <item>1</item>
+ <item>2</item>
+ <item>3</item>
+ </string-array>
+
+ <string name="prefs_debug_mode">Debug (Temporary)</string>
+
</resources>
diff --git a/java/res/values/styles.xml b/java/res/values/styles.xml
new file mode 100644
index 000000000..24fee02d8
--- /dev/null
+++ b/java/res/values/styles.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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>
+ <style name="LatinKeyboardBaseView">
+ <item name="android:background">@drawable/keyboard_background</item>
+
+ <item name="keyBackground">@drawable/btn_keyboard_key</item>
+ <item name="keyTextSize">@dimen/key_text_size</item>
+ <item name="keyTextColor">#FFFFFFFF</item>
+ <item name="keyPreviewLayout">@layout/keyboard_key_preview</item>
+ <item name="keyPreviewOffset">-12dip</item>
+ <item name="keyPreviewHeight">80dip</item>
+ <item name="labelTextSize">14sp</item>
+ <item name="popupLayout">@layout/keyboard_popup_keyboard</item>
+ <item name="verticalCorrection">-10dip</item>
+ <item name="shadowColor">#BB000000</item>
+ <item name="shadowRadius">2.75</item>
+ <item name="backgroundDimAmount">0.5</item>
+ <item name="symbolColorScheme">white</item>
+ </style>
+</resources>
diff --git a/java/res/xml-da/kbd_qwerty.xml b/java/res/xml-da/kbd_qwerty.xml
new file mode 100644
index 000000000..472f8be55
--- /dev/null
+++ b/java/res/xml-da/kbd_qwerty.xml
@@ -0,0 +1,213 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2010, 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.
+*/
+-->
+
+<!--
+ Danish Keyboard Layout
+
+ Just a copy of the Norwegian layout, with æ/ø switched.
+-->
+
+<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
+ android:keyWidth="9.09%p"
+ android:horizontalGap="0px"
+ android:verticalGap="0px"
+ android:keyHeight="@dimen/key_height"
+ >
+
+ <Row>
+ <Key android:codes="113" android:keyLabel="q"
+ android:keyWidth="8.75%p" android:keyEdgeFlags="left"/>
+ <Key android:codes="119" android:keyLabel="w"/>
+ <Key android:codes="101" android:keyLabel="e"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="éèêëę€"/>
+ <Key android:codes="114" android:keyLabel="r"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="ř"/>
+ <Key android:codes="116" android:keyLabel="t"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="ťþ"/>
+ <Key android:codes="121" android:keyLabel="y"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="ýÿü"/>
+ <Key android:codes="117" android:keyLabel="u"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="úùûū"/>
+ <Key android:codes="105" android:keyLabel="i"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="íìîï"/>
+ <Key android:codes="111" android:keyLabel="o"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="óòôõ"/>
+ <Key android:codes="112" android:keyLabel="p"/>
+ <Key android:keyLabel="å"
+ android:keyWidth="8.75%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row>
+ <Key android:codes="97" android:keyLabel="a"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="áàâąã"
+ android:keyWidth="8.75%p" android:keyEdgeFlags="left"/>
+ <Key android:codes="115" android:keyLabel="s"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="śšşß"/>
+ <Key android:codes="100" android:keyLabel="d"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="ðď"/>
+ <Key android:codes="102" android:keyLabel="f"/>
+ <Key android:codes="103" android:keyLabel="g"/>
+ <Key android:codes="104" android:keyLabel="h"/>
+ <Key android:codes="106" android:keyLabel="j"/>
+ <Key android:codes="107" android:keyLabel="k"/>
+ <Key android:codes="108" android:keyLabel="l"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="ł"/>
+ <Key android:keyLabel="æ"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="ä"/>
+ <Key android:keyLabel="ø"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="öœ"
+ android:keyWidth="8.75%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyWidth="10%p">
+ <Key android:codes="-1" android:keyIcon="@drawable/sym_keyboard_shift"
+ android:keyWidth="15%p" android:isModifier="true"
+ android:iconPreview="@drawable/sym_keyboard_feedback_shift"
+ android:isSticky="true" android:keyEdgeFlags="left"/>
+ <Key android:codes="122" android:keyLabel="z"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="źžż"/>
+ <Key android:codes="120" android:keyLabel="x"/>
+ <Key android:codes="99" android:keyLabel="c"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="çćč"/>
+ <Key android:codes="118" android:keyLabel="v"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="w"/>
+ <Key android:codes="98" android:keyLabel="b"/>
+ <Key android:codes="110" android:keyLabel="n"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="ńñň"/>
+ <Key android:codes="109" android:keyLabel="m"/>
+ <Key android:codes="-5" android:keyIcon="@drawable/sym_keyboard_delete"
+ android:keyWidth="15%p" android:keyEdgeFlags="right"
+ android:iconPreview="@drawable/sym_keyboard_feedback_delete"
+ android:isRepeatable="true"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_normal" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_keyboard_mic"
+ android:iconPreview="@drawable/sym_keyboard_feedback_mic"
+ android:keyWidth="10%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="40%p" android:isRepeatable="true"/>
+ <Key android:codes="46" android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+ android:keyWidth="10%p"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_url" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="/" android:keyWidth="10%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="40%p" android:isRepeatable="true"/>
+ <!--Key android:keyLabel="@string/popular_domain_0"
+ android:keyOutputText="@string/popular_domain_0"
+ android:popupKeyboard="@xml/popup_domains"
+ android:keyWidth="20%p"/-->
+ <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+ android:keyWidth="10%p"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_email" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="\@"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="40%p" android:isRepeatable="true"/>
+ <!--Key android:keyLabel="@string/popular_domain_0"
+ android:keyOutputText="@string/popular_domain_0"
+ android:popupKeyboard="@xml/popup_domains"
+ android:keyWidth="20%p"/-->
+ <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_im" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_keyboard_mic"
+ android:iconPreview="@drawable/sym_keyboard_feedback_mic"
+ android:keyWidth="10%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="40%p" android:isRepeatable="true"/>
+ <Key android:codes="46" android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+ android:keyWidth="10%p"/>
+ <Key android:keyLabel=":-)" android:keyOutputText=":-) "
+ android:popupKeyboard="@xml/popup_smileys"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_webentry" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_keyboard_mic"
+ android:iconPreview="@drawable/sym_keyboard_feedback_mic"
+ android:keyWidth="10%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="20%p" android:isRepeatable="true"/>
+ <Key android:codes="9" android:keyIcon="@drawable/sym_keyboard_tab"
+ android:iconPreview="@drawable/sym_keyboard_feedback_tab"
+ android:keyWidth="20%p"/>
+ <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+</Keyboard>
diff --git a/java/res/xml-de/kbd_qwerty_black.xml b/java/res/xml-de/kbd_qwerty_black.xml
new file mode 100755
index 000000000..366f87134
--- /dev/null
+++ b/java/res/xml-de/kbd_qwerty_black.xml
@@ -0,0 +1,189 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2010, 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:android="http://schemas.android.com/apk/res/android"
+ android:keyWidth="10%p"
+ android:horizontalGap="0px"
+ android:verticalGap="0px"
+ android:keyHeight="@dimen/key_height"
+ >
+
+ <Row>
+ <Key android:codes="113" android:keyLabel="q" android:keyEdgeFlags="left"/>
+ <Key android:codes="119" android:keyLabel="w"/>
+ <Key android:codes="101" android:keyLabel="e"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_e"
+ />
+ <Key android:codes="114" android:keyLabel="r"/>
+ <Key android:codes="116" android:keyLabel="t"/>
+ <Key android:codes="122" android:keyLabel="z" />
+ <Key android:codes="117" android:keyLabel="u"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_u"
+ />
+ <Key android:codes="105" android:keyLabel="i"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_i"
+ />
+ <Key android:codes="111" android:keyLabel="o"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_o"
+ />
+ <Key android:codes="112" android:keyLabel="p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row>
+ <Key android:codes="97" android:keyLabel="a" android:horizontalGap="5%p"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_a"
+ android:keyEdgeFlags="left"/>
+ <Key android:codes="115" android:keyLabel="s"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_s"
+ />
+ <Key android:codes="100" android:keyLabel="d"/>
+ <Key android:codes="102" android:keyLabel="f"/>
+ <Key android:codes="103" android:keyLabel="g"/>
+ <Key android:codes="104" android:keyLabel="h"/>
+ <Key android:codes="106" android:keyLabel="j"/>
+ <Key android:codes="107" android:keyLabel="k"/>
+ <Key android:codes="108" android:keyLabel="l" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row>
+ <Key android:codes="-1" android:keyIcon="@drawable/sym_bkeyboard_shift"
+ android:keyWidth="15%p" android:isModifier="true"
+ android:iconPreview="@drawable/sym_keyboard_feedback_shift"
+ android:isSticky="true" android:keyEdgeFlags="left"/>
+ <Key android:codes="121" android:keyLabel="y"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_y"
+ />
+ <Key android:codes="120" android:keyLabel="x"/>
+ <Key android:codes="99" android:keyLabel="c"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_c"
+ />
+ <Key android:codes="118" android:keyLabel="v"/>
+ <Key android:codes="98" android:keyLabel="b"/>
+ <Key android:codes="110" android:keyLabel="n"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_n"
+ />
+ <Key android:codes="109" android:keyLabel="m"/>
+ <Key android:codes="-5" android:keyIcon="@drawable/sym_bkeyboard_delete"
+ android:keyWidth="15%p" android:keyEdgeFlags="right"
+ android:iconPreview="@drawable/sym_keyboard_feedback_delete"
+ android:isRepeatable="true"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_normal" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:codes="@integer/key_f1"
+ android:keyWidth="10%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="40%p" android:isRepeatable="true"/>
+ <Key android:codes="46" android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+ android:keyWidth="10%p"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_url" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="/" android:keyWidth="10%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="40%p" android:isRepeatable="true"/>
+ <!--Key android:keyLabel="@string/popular_domain_0"
+ android:keyOutputText="@string/popular_domain_0"
+ android:popupKeyboard="@xml/popup_domains"
+ android:keyWidth="20%p"/-->
+ <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+ android:keyWidth="10%p"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_email" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="\@"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="40%p" android:isRepeatable="true"/>
+ <!--Key android:keyLabel="@string/popular_domain_0"
+ android:keyOutputText="@string/popular_domain_0"
+ android:popupKeyboard="@xml/popup_domains"
+ android:keyWidth="20%p"/-->
+ <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_im" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_bkeyboard_globe"
+ android:keyWidth="10%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="40%p" android:isRepeatable="true"/>
+ <Key android:codes="46" android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+ android:keyWidth="10%p"/>
+ <Key android:keyLabel=":-)" android:keyOutputText=":-) "
+ android:popupKeyboard="@xml/popup_smileys"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_webentry" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_bkeyboard_globe"
+ android:keyWidth="10%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="20%p" android:isRepeatable="true"/>
+ <Key android:codes="9" android:keyIcon="@drawable/sym_bkeyboard_tab"
+ android:iconPreview="@drawable/sym_keyboard_feedback_tab"
+ android:keyWidth="20%p"/>
+ <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+</Keyboard>
diff --git a/java/res/xml-fr/kbd_qwerty_black.xml b/java/res/xml-fr/kbd_qwerty_black.xml
new file mode 100644
index 000000000..1b799a51d
--- /dev/null
+++ b/java/res/xml-fr/kbd_qwerty_black.xml
@@ -0,0 +1,192 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2010, 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:android="http://schemas.android.com/apk/res/android"
+ android:keyWidth="10%p"
+ android:horizontalGap="0px"
+ android:verticalGap="0px"
+ android:keyHeight="@dimen/key_height"
+ >
+
+ <Row>
+ <Key android:codes="97" android:keyLabel="a"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_a"
+ android:keyEdgeFlags="left"/>
+ <Key android:codes="122" android:keyLabel="z"/>
+ <Key android:codes="101" android:keyLabel="e"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_e"
+ />
+ <Key android:codes="114" android:keyLabel="r"/>
+ <Key android:codes="116" android:keyLabel="t"/>
+ <Key android:codes="121" android:keyLabel="y"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_y"
+ />
+ <Key android:codes="117" android:keyLabel="u"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_u"
+ />
+ <Key android:codes="105" android:keyLabel="i"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_i"
+ />
+ <Key android:codes="111" android:keyLabel="o"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_o"
+ />
+ <Key android:codes="112" android:keyLabel="p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row>
+ <Key android:codes="113" android:keyLabel="q" android:keyEdgeFlags="left"/>
+ <Key android:codes="115" android:keyLabel="s"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_s"
+ />
+ <Key android:codes="100" android:keyLabel="d"/>
+ <Key android:codes="102" android:keyLabel="f"/>
+ <Key android:codes="103" android:keyLabel="g"/>
+ <Key android:codes="104" android:keyLabel="h"/>
+ <Key android:codes="106" android:keyLabel="j"/>
+ <Key android:codes="107" android:keyLabel="k"/>
+ <Key android:codes="108" android:keyLabel="l"/>
+ <Key android:codes="109" android:keyLabel="m" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row>
+ <Key android:codes="-1" android:keyIcon="@drawable/sym_bkeyboard_shift"
+ android:keyWidth="15%p" android:isModifier="true"
+ android:iconPreview="@drawable/sym_keyboard_feedback_shift"
+ android:isSticky="true" android:keyEdgeFlags="left"/>
+ <Key android:codes="119" android:keyLabel="w"/>
+ <Key android:codes="120" android:keyLabel="x"/>
+ <Key android:codes="99" android:keyLabel="c"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_c"
+ />
+ <Key android:codes="118" android:keyLabel="v"/>
+ <Key android:codes="98" android:keyLabel="b"/>
+ <Key android:codes="110" android:keyLabel="n"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_n"
+ />
+ <!--Key android:codes="233,224,232,234" android:keyLabel="é"/-->
+ <Key android:keyLabel="\'"/>
+ <Key android:codes="-5" android:keyIcon="@drawable/sym_bkeyboard_delete"
+ android:keyWidth="15%p" android:keyEdgeFlags="right"
+ android:iconPreview="@drawable/sym_keyboard_feedback_delete"
+ android:isRepeatable="true"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_normal" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_bkeyboard_globe"
+ android:keyWidth="10%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="40%p" android:isRepeatable="true"/>
+ <Key android:codes="46" android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+ android:keyWidth="10%p"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_url" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="/" android:keyWidth="10%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="40%p" android:isRepeatable="true"/>
+ <!--Key android:keyLabel="@string/popular_domain_0"
+ android:keyOutputText="@string/popular_domain_0"
+ android:popupKeyboard="@xml/popup_domains"
+ android:keyWidth="20%p"/-->
+ <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+ android:keyWidth="10%p"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_email" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="\@"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="40%p" android:isRepeatable="true"/>
+ <!--Key android:keyLabel="@string/popular_domain_0"
+ android:keyOutputText="@string/popular_domain_0"
+ android:popupKeyboard="@xml/popup_domains"
+ android:keyWidth="20%p"/-->
+ <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_im" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_bkeyboard_globe"
+ android:keyWidth="10%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="40%p" android:isRepeatable="true"/>
+ <Key android:codes="46" android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+ android:keyWidth="10%p"/>
+ <Key android:keyLabel=":-)" android:keyOutputText=":-) "
+ android:popupKeyboard="@xml/popup_smileys"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_webentry" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_bkeyboard_globe"
+ android:keyWidth="10%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="20%p" android:isRepeatable="true"/>
+ <Key android:codes="9" android:keyIcon="@drawable/sym_bkeyboard_tab"
+ android:iconPreview="@drawable/sym_keyboard_feedback_tab"
+ android:keyWidth="20%p"/>
+ <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+</Keyboard>
diff --git a/java/res/xml-iw/kbd_qwerty.xml b/java/res/xml-iw/kbd_qwerty.xml
new file mode 100755
index 000000000..b893f1a62
--- /dev/null
+++ b/java/res/xml-iw/kbd_qwerty.xml
@@ -0,0 +1,164 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2010, 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:android="http://schemas.android.com/apk/res/android"
+ android:keyWidth="10%p"
+ android:horizontalGap="0px"
+ android:verticalGap="0px"
+ android:keyHeight="@dimen/key_height"
+ >
+
+ <Row>
+ <Key android:keyLabel="ק"
+ android:horizontalGap="5%p"
+ android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="ר"/>
+ <Key android:keyLabel="א"/>
+ <Key android:keyLabel="ט"/>
+ <Key android:keyLabel="ו"/>
+ <Key android:keyLabel="ן"/>
+ <Key android:keyLabel="ם"/>
+ <Key android:keyLabel="פ"/>
+ <Key android:codes="-5"
+ android:horizontalGap="1.25%p"
+ android:keyIcon="@drawable/sym_keyboard_delete"
+ android:keyWidth="13.75%p" android:keyEdgeFlags="right"
+ android:iconPreview="@drawable/sym_keyboard_feedback_delete"
+ android:isRepeatable="true"/>
+ </Row>
+
+ <Row>
+ <Key android:keyLabel="ש" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="ד"/>
+ <Key android:keyLabel="ג"/>
+ <Key android:keyLabel="כ"/>
+ <Key android:keyLabel="ע"/>
+ <Key android:keyLabel="י"/>
+ <Key android:keyLabel="ח"/>
+ <Key android:keyLabel="ל"/>
+ <Key android:keyLabel="ך"/>
+ <Key android:keyLabel="ף" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row>
+ <Key android:keyLabel="ז" android:horizontalGap="5%p" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="ס"/>
+ <Key android:keyLabel="ב"/>
+ <Key android:keyLabel="ה"/>
+ <Key android:keyLabel="נ"/>
+ <Key android:keyLabel="מ"/>
+ <Key android:keyLabel="צ"/>
+ <Key android:keyLabel="ת"/>
+ <Key android:keyLabel="ץ" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_normal" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_keyboard_globe"
+ android:keyWidth="10%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="40%p" android:isRepeatable="true"/>
+ <Key android:codes="46" android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+ android:keyWidth="10%p"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_url" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="/" android:keyWidth="10%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="40%p" android:isRepeatable="true"/>
+ <!--Key android:keyLabel="@string/popular_domain_0"
+ android:keyOutputText="@string/popular_domain_0"
+ android:popupKeyboard="@xml/popup_domains"
+ android:keyWidth="20%p"/-->
+ <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+ android:keyWidth="10%p"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_email" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="\@" android:keyWidth="10%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="40%p" android:isRepeatable="true"/>
+ <!--Key android:keyLabel="@string/popular_domain_0"
+ android:keyOutputText="@string/popular_domain_0"
+ android:popupKeyboard="@xml/popup_domains"
+ android:keyWidth="20%p"/-->
+ <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_im" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_keyboard_globe"
+ android:keyWidth="10%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="40%p" android:isRepeatable="true"/>
+ <Key android:codes="46" android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+ android:keyWidth="10%p"/>
+ <Key android:keyLabel=":-)" android:keyOutputText=":-) "
+ android:popupKeyboard="@xml/popup_smileys"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_webentry" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_keyboard_globe"
+ android:keyWidth="10%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="20%p" android:isRepeatable="true"/>
+ <Key android:codes="9" android:keyIcon="@drawable/sym_keyboard_tab"
+ android:iconPreview="@drawable/sym_keyboard_feedback_tab"
+ android:keyWidth="20%p"/>
+ <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+</Keyboard>
+
diff --git a/java/res/xml-iw/kbd_qwerty_black.xml b/java/res/xml-iw/kbd_qwerty_black.xml
new file mode 100755
index 000000000..0dcf513e3
--- /dev/null
+++ b/java/res/xml-iw/kbd_qwerty_black.xml
@@ -0,0 +1,164 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2010, 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:android="http://schemas.android.com/apk/res/android"
+ android:keyWidth="10%p"
+ android:horizontalGap="0px"
+ android:verticalGap="0px"
+ android:keyHeight="@dimen/key_height"
+ >
+
+ <Row>
+ <Key android:keyLabel="ק"
+ android:horizontalGap="5%p"
+ android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="ר"/>
+ <Key android:keyLabel="א"/>
+ <Key android:keyLabel="ט"/>
+ <Key android:keyLabel="ו"/>
+ <Key android:keyLabel="ן"/>
+ <Key android:keyLabel="ם"/>
+ <Key android:keyLabel="פ"/>
+ <Key android:codes="-5"
+ android:horizontalGap="1.25%p"
+ android:keyIcon="@drawable/sym_bkeyboard_delete"
+ android:keyWidth="13.75%p" android:keyEdgeFlags="right"
+ android:iconPreview="@drawable/sym_keyboard_feedback_delete"
+ android:isRepeatable="true"/>
+ </Row>
+
+ <Row>
+ <Key android:keyLabel="ש" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="ד"/>
+ <Key android:keyLabel="ג"/>
+ <Key android:keyLabel="כ"/>
+ <Key android:keyLabel="ע"/>
+ <Key android:keyLabel="י"/>
+ <Key android:keyLabel="ח"/>
+ <Key android:keyLabel="ל"/>
+ <Key android:keyLabel="ך"/>
+ <Key android:keyLabel="ף" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row>
+ <Key android:keyLabel="ז" android:horizontalGap="5%p" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="ס"/>
+ <Key android:keyLabel="ב"/>
+ <Key android:keyLabel="ה"/>
+ <Key android:keyLabel="נ"/>
+ <Key android:keyLabel="מ"/>
+ <Key android:keyLabel="צ"/>
+ <Key android:keyLabel="ת"/>
+ <Key android:keyLabel="ץ" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_normal" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_bkeyboard_globe"
+ android:keyWidth="10%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="40%p" android:isRepeatable="true"/>
+ <Key android:codes="46" android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+ android:keyWidth="10%p"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_url" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="/" android:keyWidth="10%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="40%p" android:isRepeatable="true"/>
+ <!--Key android:keyLabel="@string/popular_domain_0"
+ android:keyOutputText="@string/popular_domain_0"
+ android:popupKeyboard="@xml/popup_domains"
+ android:keyWidth="20%p"/-->
+ <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+ android:keyWidth="10%p"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_email" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="\@" android:keyWidth="10%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="40%p" android:isRepeatable="true"/>
+ <!--Key android:keyLabel="@string/popular_domain_0"
+ android:keyOutputText="@string/popular_domain_0"
+ android:popupKeyboard="@xml/popup_domains"
+ android:keyWidth="20%p"/-->
+ <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_im" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_bkeyboard_globe"
+ android:keyWidth="10%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="40%p" android:isRepeatable="true"/>
+ <Key android:codes="46" android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+ android:keyWidth="10%p"/>
+ <Key android:keyLabel=":-)" android:keyOutputText=":-) "
+ android:popupKeyboard="@xml/popup_smileys"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_webentry" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_bkeyboard_globe"
+ android:keyWidth="10%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="20%p" android:isRepeatable="true"/>
+ <Key android:codes="9" android:keyIcon="@drawable/sym_bkeyboard_tab"
+ android:iconPreview="@drawable/sym_keyboard_feedback_tab"
+ android:keyWidth="20%p"/>
+ <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+</Keyboard>
+
diff --git a/java/res/xml-nb/kbd_qwerty.xml b/java/res/xml-nb/kbd_qwerty.xml
new file mode 100644
index 000000000..554bb00eb
--- /dev/null
+++ b/java/res/xml-nb/kbd_qwerty.xml
@@ -0,0 +1,213 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2010, 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.
+*/
+-->
+
+<!--
+ Norwegian Keyboard Layout
+
+ Just a copy of the Swedish layout, with ä/æ and ö/ø switched.
+-->
+
+<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
+ android:keyWidth="9.09%p"
+ android:horizontalGap="0px"
+ android:verticalGap="0px"
+ android:keyHeight="@dimen/key_height"
+ >
+
+ <Row>
+ <Key android:codes="113" android:keyLabel="q"
+ android:keyWidth="8.75%p" android:keyEdgeFlags="left"/>
+ <Key android:codes="119" android:keyLabel="w"/>
+ <Key android:codes="101" android:keyLabel="e"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="éèêëę€"/>
+ <Key android:codes="114" android:keyLabel="r"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="ř"/>
+ <Key android:codes="116" android:keyLabel="t"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="ťþ"/>
+ <Key android:codes="121" android:keyLabel="y"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="ýÿü"/>
+ <Key android:codes="117" android:keyLabel="u"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="úùûū"/>
+ <Key android:codes="105" android:keyLabel="i"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="íìîï"/>
+ <Key android:codes="111" android:keyLabel="o"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="óòôõ"/>
+ <Key android:codes="112" android:keyLabel="p"/>
+ <Key android:keyLabel="å"
+ android:keyWidth="8.75%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row>
+ <Key android:codes="97" android:keyLabel="a"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="áàâąã"
+ android:keyWidth="8.75%p" android:keyEdgeFlags="left"/>
+ <Key android:codes="115" android:keyLabel="s"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="śšşß"/>
+ <Key android:codes="100" android:keyLabel="d"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="ðď"/>
+ <Key android:codes="102" android:keyLabel="f"/>
+ <Key android:codes="103" android:keyLabel="g"/>
+ <Key android:codes="104" android:keyLabel="h"/>
+ <Key android:codes="106" android:keyLabel="j"/>
+ <Key android:codes="107" android:keyLabel="k"/>
+ <Key android:codes="108" android:keyLabel="l"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="ł"/>
+ <Key android:keyLabel="ø"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="öœ"/>
+ <Key android:keyLabel="æ"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="ä"
+ android:keyWidth="8.75%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyWidth="10%p">
+ <Key android:codes="-1" android:keyIcon="@drawable/sym_keyboard_shift"
+ android:keyWidth="15%p" android:isModifier="true"
+ android:iconPreview="@drawable/sym_keyboard_feedback_shift"
+ android:isSticky="true" android:keyEdgeFlags="left"/>
+ <Key android:codes="122" android:keyLabel="z"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="źžż"/>
+ <Key android:codes="120" android:keyLabel="x"/>
+ <Key android:codes="99" android:keyLabel="c"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="çćč"/>
+ <Key android:codes="118" android:keyLabel="v"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="w"/>
+ <Key android:codes="98" android:keyLabel="b"/>
+ <Key android:codes="110" android:keyLabel="n"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="ńñň"/>
+ <Key android:codes="109" android:keyLabel="m"/>
+ <Key android:codes="-5" android:keyIcon="@drawable/sym_keyboard_delete"
+ android:keyWidth="15%p" android:keyEdgeFlags="right"
+ android:iconPreview="@drawable/sym_keyboard_feedback_delete"
+ android:isRepeatable="true"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_normal" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_keyboard_mic"
+ android:iconPreview="@drawable/sym_keyboard_feedback_mic"
+ android:keyWidth="10%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="40%p" android:isRepeatable="true"/>
+ <Key android:codes="46" android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+ android:keyWidth="10%p"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_url" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="/" android:keyWidth="10%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="40%p" android:isRepeatable="true"/>
+ <!--Key android:keyLabel="@string/popular_domain_0"
+ android:keyOutputText="@string/popular_domain_0"
+ android:popupKeyboard="@xml/popup_domains"
+ android:keyWidth="20%p"/-->
+ <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+ android:keyWidth="10%p"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_email" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="\@"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="40%p" android:isRepeatable="true"/>
+ <!--Key android:keyLabel="@string/popular_domain_0"
+ android:keyOutputText="@string/popular_domain_0"
+ android:popupKeyboard="@xml/popup_domains"
+ android:keyWidth="20%p"/-->
+ <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_im" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_keyboard_mic"
+ android:iconPreview="@drawable/sym_keyboard_feedback_mic"
+ android:keyWidth="10%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="40%p" android:isRepeatable="true"/>
+ <Key android:codes="46" android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+ android:keyWidth="10%p"/>
+ <Key android:keyLabel=":-)" android:keyOutputText=":-) "
+ android:popupKeyboard="@xml/popup_smileys"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_webentry" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_keyboard_mic"
+ android:iconPreview="@drawable/sym_keyboard_feedback_mic"
+ android:keyWidth="10%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="20%p" android:isRepeatable="true"/>
+ <Key android:codes="9" android:keyIcon="@drawable/sym_keyboard_tab"
+ android:iconPreview="@drawable/sym_keyboard_feedback_tab"
+ android:keyWidth="20%p"/>
+ <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+</Keyboard>
diff --git a/java/res/xml-ru/kbd_qwerty_black.xml b/java/res/xml-ru/kbd_qwerty_black.xml
new file mode 100755
index 000000000..4923be01a
--- /dev/null
+++ b/java/res/xml-ru/kbd_qwerty_black.xml
@@ -0,0 +1,175 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2010, 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:android="http://schemas.android.com/apk/res/android"
+ android:keyWidth="9.09%p"
+ android:horizontalGap="0px"
+ android:verticalGap="0px"
+ android:keyHeight="@dimen/key_height"
+ >
+
+ <Row>
+ <Key android:keyLabel="й" android:keyWidth="8.75%p"
+ android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="ц"/>
+ <Key android:keyLabel="у"/>
+ <Key android:keyLabel="к"/>
+ <Key android:keyLabel="е"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="ё" />
+ <Key android:keyLabel="н"/>
+ <Key android:keyLabel="г"/>
+ <Key android:keyLabel="ш"/>
+ <Key android:keyLabel="щ"/>
+ <Key android:keyLabel="з"/>
+ <Key android:keyLabel="х" android:keyWidth="8.75%p"
+ android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row>
+ <Key android:keyLabel="ф" android:keyWidth="8.75%p"
+ android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="ы"/>
+ <Key android:keyLabel="в"/>
+ <Key android:keyLabel="а"/>
+ <Key android:keyLabel="п"/>
+ <Key android:keyLabel="р"/>
+ <Key android:keyLabel="о"/>
+ <Key android:keyLabel="л"/>
+ <Key android:keyLabel="д"/>
+ <Key android:keyLabel="ж"/>
+ <Key android:keyLabel="э" android:keyWidth="8.75%p"
+ android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyWidth="8.5%p">
+ <Key android:codes="-1" android:keyIcon="@drawable/sym_bkeyboard_shift"
+ android:keyWidth="11.75%p" android:isModifier="true"
+ android:iconPreview="@drawable/sym_keyboard_feedback_shift"
+ android:isSticky="true" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="я"/>
+ <Key android:keyLabel="ч"/>
+ <Key android:keyLabel="с"/>
+ <Key android:keyLabel="м"/>
+ <Key android:keyLabel="и"/>
+ <Key android:keyLabel="т"/>
+ <Key android:keyLabel="ь"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="ъ" />
+ <Key android:keyLabel="б"/>
+ <Key android:keyLabel="ю"/>
+ <Key android:codes="-5" android:keyIcon="@drawable/sym_bkeyboard_delete"
+ android:keyWidth="11.75%p" android:keyEdgeFlags="right"
+ android:iconPreview="@drawable/sym_keyboard_feedback_delete"
+ android:isRepeatable="true"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_normal" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_bkeyboard_globe"
+ android:keyWidth="10%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="40%p" android:isRepeatable="true"/>
+ <Key android:codes="46" android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+ android:keyWidth="10%p"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_url" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="/" android:keyWidth="10%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="40%p" android:isRepeatable="true"/>
+ <!--Key android:keyLabel="@string/popular_domain_0"
+ android:keyOutputText="@string/popular_domain_0"
+ android:popupKeyboard="@xml/popup_domains"
+ android:keyWidth="20%p"/-->
+ <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+ android:keyWidth="10%p"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_email" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="\@" android:keyWidth="10%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="40%p" android:isRepeatable="true"/>
+ <!--Key android:keyLabel="@string/popular_domain_0"
+ android:keyOutputText="@string/popular_domain_0"
+ android:popupKeyboard="@xml/popup_domains"
+ android:keyWidth="20%p"/-->
+ <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_im" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_bkeyboard_globe"
+ android:keyWidth="10%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="40%p" android:isRepeatable="true"/>
+ <Key android:codes="46" android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+ android:keyWidth="10%p"/>
+ <Key android:keyLabel=":-)" android:keyOutputText=":-) "
+ android:popupKeyboard="@xml/popup_smileys"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_webentry" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_bkeyboard_globe"
+ android:keyWidth="10%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="20%p" android:isRepeatable="true"/>
+ <Key android:codes="9" android:keyIcon="@drawable/sym_bkeyboard_tab"
+ android:iconPreview="@drawable/sym_keyboard_feedback_tab"
+ android:keyWidth="20%p"/>
+ <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+</Keyboard>
diff --git a/java/res/xml-sr/kbd_qwerty.xml b/java/res/xml-sr/kbd_qwerty.xml
new file mode 100644
index 000000000..e4884a8a6
--- /dev/null
+++ b/java/res/xml-sr/kbd_qwerty.xml
@@ -0,0 +1,171 @@
+<?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.
+*/
+-->
+
+<!-- Serbian keyboard layout, based on the X11 layout for Serbian -->
+<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
+ android:keyWidth="9.09%p"
+ android:horizontalGap="0px"
+ android:verticalGap="0px"
+ android:keyHeight="@dimen/key_height"
+ >
+
+ <Row>
+ <Key android:keyLabel="љ"
+ android:keyEdgeFlags="left" />
+ <Key android:keyLabel="њ" />
+ <Key android:keyLabel="е" />
+ <Key android:keyLabel="р" />
+ <Key android:keyLabel="т" />
+ <Key android:keyLabel="з" />
+ <Key android:keyLabel="у" />
+ <Key android:keyLabel="и" />
+ <Key android:keyLabel="о" />
+ <Key android:keyLabel="п" />
+ <Key android:keyLabel="ш"
+ android:keyEdgeFlags="right" />
+ </Row>
+
+ <Row>
+ <Key android:keyLabel="а"
+ android:keyEdgeFlags="left" />
+ <Key android:keyLabel="с" />
+ <Key android:keyLabel="д" />
+ <Key android:keyLabel="ф" />
+ <Key android:keyLabel="г" />
+ <Key android:keyLabel="х" />
+ <Key android:keyLabel="ј" />
+ <Key android:keyLabel="к" />
+ <Key android:keyLabel="л" />
+ <Key android:keyLabel="ч" />
+ <Key android:keyLabel="ћ" />
+ <Key android:keyLabel="ђ"
+ android:keyEdgeFlags="right" />
+ </Row>
+
+ <Row android:keyWidth="8.5%p">
+ <Key android:codes="-1" android:keyIcon="@drawable/sym_keyboard_shift"
+ android:keyWidth="11.75%p" android:isModifier="true"
+ android:iconPreview="@drawable/sym_keyboard_feedback_shift"
+ android:isSticky="true" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="ж" />
+ <Key android:keyLabel="џ" />
+ <Key android:keyLabel="ц" />
+ <Key android:keyLabel="в" />
+ <Key android:keyLabel="б" />
+ <Key android:keyLabel="н" />
+ <Key android:keyLabel="м" />
+ <Key android:codes="-5" android:keyIcon="@drawable/sym_keyboard_delete"
+ android:keyWidth="11.75%p" android:keyEdgeFlags="right"
+ android:iconPreview="@drawable/sym_keyboard_feedback_delete"
+ android:isRepeatable="true"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_normal" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_keyboard_globe"
+ android:keyWidth="10%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="40%p" android:isRepeatable="true"/>
+ <Key android:codes="46" android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+ android:keyWidth="10%p"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_url" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="/" android:keyWidth="10%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="20%p" android:isRepeatable="true"/>
+ <Key android:keyLabel="@string/popular_domain_0"
+ android:keyOutputText="@string/popular_domain_0"
+ android:popupKeyboard="@xml/popup_domains"
+ android:keyWidth="20%p"/>
+ <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+ android:keyWidth="10%p"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_email" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="\@" android:keyWidth="10%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="20%p" android:isRepeatable="true"/>
+ <Key android:keyLabel="@string/popular_domain_0"
+ android:keyOutputText="@string/popular_domain_0"
+ android:popupKeyboard="@xml/popup_domains"
+ android:keyWidth="20%p"/>
+ <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_im" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_keyboard_globe"
+ android:keyWidth="10%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="40%p" android:isRepeatable="true"/>
+ <Key android:codes="46" android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+ android:keyWidth="10%p"/>
+ <Key android:keyLabel=":-)" android:keyOutputText=":-) "
+ android:popupKeyboard="@xml/popup_smileys"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_webentry" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_keyboard_globe"
+ android:keyWidth="10%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="20%p" android:isRepeatable="true"/>
+ <Key android:codes="9" android:keyIcon="@drawable/sym_keyboard_tab"
+ android:iconPreview="@drawable/sym_keyboard_feedback_tab"
+ android:keyWidth="20%p"/>
+ <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+</Keyboard>
diff --git a/java/res/xml-sv/kbd_qwerty_black.xml b/java/res/xml-sv/kbd_qwerty_black.xml
new file mode 100644
index 000000000..6604bc87c
--- /dev/null
+++ b/java/res/xml-sv/kbd_qwerty_black.xml
@@ -0,0 +1,215 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2010, 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.
+*/
+-->
+
+<!--
+ Swedish Keyboard Layout
+
+ Key positioning: Svensk standard SS 66 22 41
+ Foreign letters: Svenska skrivregler (2:a uppl.) §302
+ Local additions: ۧ
+-->
+
+<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
+ android:keyWidth="9.09%p"
+ android:horizontalGap="0px"
+ android:verticalGap="0px"
+ android:keyHeight="@dimen/key_height"
+ >
+
+ <Row>
+ <Key android:codes="113" android:keyLabel="q"
+ android:keyWidth="8.75%p" android:keyEdgeFlags="left"/>
+ <Key android:codes="119" android:keyLabel="w"/>
+ <Key android:codes="101" android:keyLabel="e"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="éèêëę€"/>
+ <Key android:codes="114" android:keyLabel="r"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="ř"/>
+ <Key android:codes="116" android:keyLabel="t"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="ťþ"/>
+ <Key android:codes="121" android:keyLabel="y"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="ýÿü"/>
+ <Key android:codes="117" android:keyLabel="u"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="úùûū"/>
+ <Key android:codes="105" android:keyLabel="i"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="íìîï"/>
+ <Key android:codes="111" android:keyLabel="o"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="óòôõ"/>
+ <Key android:codes="112" android:keyLabel="p"/>
+ <Key android:keyLabel="å"
+ android:keyWidth="8.75%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row>
+ <Key android:codes="97" android:keyLabel="a"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="áàâąã"
+ android:keyWidth="8.75%p" android:keyEdgeFlags="left"/>
+ <Key android:codes="115" android:keyLabel="s"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="śšşß"/>
+ <Key android:codes="100" android:keyLabel="d"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="ðď"/>
+ <Key android:codes="102" android:keyLabel="f"/>
+ <Key android:codes="103" android:keyLabel="g"/>
+ <Key android:codes="104" android:keyLabel="h"/>
+ <Key android:codes="106" android:keyLabel="j"/>
+ <Key android:codes="107" android:keyLabel="k"/>
+ <Key android:codes="108" android:keyLabel="l"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="ł"/>
+ <Key android:keyLabel="ö"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="øœ"/>
+ <Key android:keyLabel="ä"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="æ"
+ android:keyWidth="8.75%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyWidth="10%p">
+ <Key android:codes="-1" android:keyIcon="@drawable/sym_bkeyboard_shift"
+ android:keyWidth="15%p" android:isModifier="true"
+ android:iconPreview="@drawable/sym_keyboard_feedback_shift"
+ android:isSticky="true" android:keyEdgeFlags="left"/>
+ <Key android:codes="122" android:keyLabel="z"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="źžż"/>
+ <Key android:codes="120" android:keyLabel="x"/>
+ <Key android:codes="99" android:keyLabel="c"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="çćč"/>
+ <Key android:codes="118" android:keyLabel="v"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="w"/>
+ <Key android:codes="98" android:keyLabel="b"/>
+ <Key android:codes="110" android:keyLabel="n"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="ńñň"/>
+ <Key android:codes="109" android:keyLabel="m"/>
+ <Key android:codes="-5" android:keyIcon="@drawable/sym_bkeyboard_delete"
+ android:keyWidth="15%p" android:keyEdgeFlags="right"
+ android:iconPreview="@drawable/sym_keyboard_feedback_delete"
+ android:isRepeatable="true"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_normal" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_bkeyboard_mic"
+ android:iconPreview="@drawable/sym_keyboard_feedback_mic"
+ android:keyWidth="10%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="40%p" android:isRepeatable="true"/>
+ <Key android:codes="46" android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+ android:keyWidth="10%p"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_url" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="/" android:keyWidth="10%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="40%p" android:isRepeatable="true"/>
+ <!--Key android:keyLabel="@string/popular_domain_0"
+ android:keyOutputText="@string/popular_domain_0"
+ android:popupKeyboard="@xml/popup_domains"
+ android:keyWidth="20%p"/-->
+ <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+ android:keyWidth="10%p"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_email" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="\@"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="40%p" android:isRepeatable="true"/>
+ <!--Key android:keyLabel="@string/popular_domain_0"
+ android:keyOutputText="@string/popular_domain_0"
+ android:popupKeyboard="@xml/popup_domains"
+ android:keyWidth="20%p"/-->
+ <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_im" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_bkeyboard_mic"
+ android:iconPreview="@drawable/sym_keyboard_feedback_mic"
+ android:keyWidth="10%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="40%p" android:isRepeatable="true"/>
+ <Key android:codes="46" android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+ android:keyWidth="10%p"/>
+ <Key android:keyLabel=":-)" android:keyOutputText=":-) "
+ android:popupKeyboard="@xml/popup_smileys"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_webentry" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_bkeyboard_mic"
+ android:iconPreview="@drawable/sym_keyboard_feedback_mic"
+ android:keyWidth="10%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="20%p" android:isRepeatable="true"/>
+ <Key android:codes="9" android:keyIcon="@drawable/sym_bkeyboard_tab"
+ android:iconPreview="@drawable/sym_keyboard_feedback_tab"
+ android:keyWidth="20%p"/>
+ <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+</Keyboard>
diff --git a/java/res/xml/dictionary.xml b/java/res/xml/dictionary.xml
new file mode 100644
index 000000000..7b770a8b4
--- /dev/null
+++ b/java/res/xml/dictionary.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2010, 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.
+*/
+-->
+
+<dictionary>
+ <part name = "main" />
+</dictionary> \ No newline at end of file
diff --git a/java/res/xml/kbd_alpha_black.xml b/java/res/xml/kbd_alpha_black.xml
new file mode 100644
index 000000000..108e466b8
--- /dev/null
+++ b/java/res/xml/kbd_alpha_black.xml
@@ -0,0 +1,106 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2010, 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:android="http://schemas.android.com/apk/res/android"
+ android:keyWidth="10%p"
+ android:horizontalGap="0px"
+ android:verticalGap="0px"
+ android:keyHeight="@dimen/key_height"
+ >
+
+ <Row>
+ <Key android:keyLabel="a"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_a"
+ android:keyEdgeFlags="left" />
+ <Key android:keyLabel="b" />
+ <Key android:keyLabel="c"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_c" />
+ <Key android:keyLabel="d" />
+ <Key android:keyLabel="e"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_e" />
+ <Key android:keyLabel="f" />
+ <Key android:keyLabel="g" />
+ <Key android:keyLabel="h" />
+ <Key android:keyLabel="i"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_i" />
+ <Key android:keyLabel="j" android:keyEdgeFlags="right" />
+ </Row>
+ <Row>
+ <Key android:keyLabel="k" android:keyEdgeFlags="left" />
+ <Key android:keyLabel="l" />
+ <Key android:keyLabel="m" />
+ <Key android:keyLabel="n"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_n" />
+ <Key android:keyLabel="o"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_o" />
+ <Key android:keyLabel="p" />
+ <Key android:keyLabel="q" />
+ <Key android:keyLabel="r" />
+ <Key android:keyLabel="s"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_s" />
+ <Key android:keyLabel="t" android:keyEdgeFlags="right" />
+ </Row>
+
+ <Row>
+ <Key android:codes="-1" android:keyIcon="@drawable/sym_bkeyboard_shift"
+ android:keyWidth="15%p" android:isModifier="true"
+ android:iconPreview="@drawable/sym_keyboard_feedback_shift"
+ android:isSticky="true" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="u"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_u" />
+ <Key android:keyLabel="v"/>
+ <Key android:keyLabel="w"/>
+ <Key android:keyLabel="x"/>
+ <Key android:keyLabel="y"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_y"
+ />
+ <Key android:keyLabel="z"/>
+ <Key android:keyLabel=","/>
+ <Key android:codes="-5" android:keyIcon="@drawable/sym_bkeyboard_delete"
+ android:keyWidth="15%p" android:keyEdgeFlags="right"
+ android:iconPreview="@drawable/sym_keyboard_feedback_delete"
+ android:isRepeatable="true"/>
+ </Row>
+
+ <Row android:rowEdgeFlags="bottom">
+ <Key android:codes="-3" android:keyIcon="@drawable/sym_bkeyboard_done"
+ android:iconPreview="@drawable/sym_keyboard_feedback_done"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:codes="-2" android:keyLabel="123" android:keyWidth="15%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="30%p" android:isRepeatable="true"/>
+ <Key android:codes="46" android:keyLabel="."
+ android:popupKeyboard="@xml/popup_punctuation"
+ android:keyWidth="15%p"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+</Keyboard>
diff --git a/java/res/xml/kbd_phone_black.xml b/java/res/xml/kbd_phone_black.xml
new file mode 100755
index 000000000..b7f9096bd
--- /dev/null
+++ b/java/res/xml/kbd_phone_black.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2010, 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:android="http://schemas.android.com/apk/res/android"
+ android:keyWidth="26.67%p"
+ android:horizontalGap="0px"
+ android:verticalGap="0px"
+ android:keyHeight="@dimen/key_height"
+ >
+
+ <Row>
+ <Key android:codes="49" android:keyIcon="@drawable/sym_bkeyboard_num1" android:keyEdgeFlags="left"/>
+ <Key android:codes="50" android:keyIcon="@drawable/sym_bkeyboard_num2"/>
+ <Key android:codes="51" android:keyIcon="@drawable/sym_bkeyboard_num3"/>
+ <Key android:keyLabel="-" android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row>
+ <Key android:codes="52" android:keyIcon="@drawable/sym_bkeyboard_num4" android:keyEdgeFlags="left"/>
+ <Key android:codes="53" android:keyIcon="@drawable/sym_bkeyboard_num5"/>
+ <Key android:codes="54" android:keyIcon="@drawable/sym_bkeyboard_num6"/>
+ <Key android:keyLabel="." android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row>
+ <Key android:codes="55" android:keyIcon="@drawable/sym_bkeyboard_num7" android:keyEdgeFlags="left"/>
+ <Key android:codes="56" android:keyIcon="@drawable/sym_bkeyboard_num8"/>
+ <Key android:codes="57" android:keyIcon="@drawable/sym_bkeyboard_num9"/>
+ <Key android:codes="-5" android:keyIcon="@drawable/sym_bkeyboard_delete"
+ android:iconPreview="@drawable/sym_keyboard_feedback_delete"
+ android:keyWidth="20%p"
+ android:isRepeatable="true" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyIcon="@drawable/sym_bkeyboard_numalt"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:iconPreview="@drawable/sym_keyboard_feedback_numalt"/>
+
+ <Key android:codes="48" android:keyIcon="@drawable/sym_bkeyboard_num0"/>
+
+ <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:isRepeatable="true"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+ android:keyWidth="20%p"
+ android:keyEdgeFlags="right"/>
+ </Row>
+
+</Keyboard>
diff --git a/java/res/xml/kbd_phone_symbols_black.xml b/java/res/xml/kbd_phone_symbols_black.xml
new file mode 100755
index 000000000..c73e5faa4
--- /dev/null
+++ b/java/res/xml/kbd_phone_symbols_black.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2010, 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:android="http://schemas.android.com/apk/res/android"
+ android:keyWidth="26.67%p"
+ android:horizontalGap="0px"
+ android:verticalGap="0px"
+ android:keyHeight="@dimen/key_height"
+ >
+
+ <Row>
+ <Key android:keyLabel="(" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="/"/>
+ <Key android:keyLabel=")"/>
+ <Key android:keyLabel="-" android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row>
+ <Key android:keyLabel="N" android:keyEdgeFlags="left"/>
+ <!-- Pause is a comma.
+ Check PhoneNumberUtils.java to see if this has changed. -->
+ <Key android:codes="44" android:keyLabel="Pause"/>
+ <Key android:keyLabel=","/>
+ <Key android:keyLabel="." android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row>
+ <Key android:codes="42" android:keyIcon="@drawable/sym_bkeyboard_numstar"
+ android:keyEdgeFlags="left"/>
+ <!-- Wait is a semicolon. -->
+ <Key android:codes="59" android:keyLabel="Wait"/>
+ <Key android:codes="35" android:keyIcon="@drawable/sym_bkeyboard_numpound"/>
+ <Key android:codes="-5" android:keyIcon="@drawable/sym_bkeyboard_delete"
+ android:iconPreview="@drawable/sym_keyboard_feedback_delete"
+ android:keyWidth="20%p"
+ android:isRepeatable="true" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_phone_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="+"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:isRepeatable="true"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+ android:keyWidth="20%p"
+ android:keyEdgeFlags="right"/>
+ </Row>
+
+</Keyboard>
diff --git a/java/res/xml/kbd_qwerty_black.xml b/java/res/xml/kbd_qwerty_black.xml
new file mode 100755
index 000000000..d013ae01b
--- /dev/null
+++ b/java/res/xml/kbd_qwerty_black.xml
@@ -0,0 +1,207 @@
+<?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.
+*/
+-->
+
+<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
+ android:keyWidth="10%p"
+ android:horizontalGap="0px"
+ android:verticalGap="0px"
+ android:keyHeight="@dimen/key_height"
+ >
+
+ <Row>
+ <Key android:codes="113" android:keyLabel="q" android:keyEdgeFlags="left"/>
+ <Key android:codes="119" android:keyLabel="w"/>
+ <Key android:codes="101" android:keyLabel="e"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_e"
+ />
+ <Key android:codes="114" android:keyLabel="r"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_r"/>
+ <Key android:codes="116" android:keyLabel="t"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_t"/>
+ <Key android:codes="121" android:keyLabel="y"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_y"
+ />
+ <Key android:codes="117" android:keyLabel="u"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_u"
+ />
+ <Key android:codes="105" android:keyLabel="i"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_i"
+ />
+ <Key android:codes="111" android:keyLabel="o"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_o"
+ />
+ <Key android:codes="112" android:keyLabel="p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row>
+ <Key android:codes="97" android:keyLabel="a" android:horizontalGap="5%p"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_a"
+ android:keyEdgeFlags="left"/>
+ <Key android:codes="115" android:keyLabel="s"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_s"
+ />
+ <Key android:codes="100" android:keyLabel="d"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_d"/>
+ <Key android:codes="102" android:keyLabel="f"/>
+ <Key android:codes="103" android:keyLabel="g"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_g"
+ />
+ <Key android:codes="104" android:keyLabel="h"/>
+ <Key android:codes="106" android:keyLabel="j"/>
+ <Key android:codes="107" android:keyLabel="k"/>
+ <Key android:codes="108" android:keyLabel="l"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_l"
+ android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row>
+ <Key android:codes="-1" android:keyIcon="@drawable/sym_bkeyboard_shift"
+ android:keyWidth="15%p" android:isModifier="true"
+ android:iconPreview="@drawable/sym_keyboard_feedback_shift"
+ android:isSticky="true" android:keyEdgeFlags="left"/>
+ <Key android:codes="122" android:keyLabel="z"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_z"/>
+ <Key android:codes="120" android:keyLabel="x"/>
+ <Key android:codes="99" android:keyLabel="c"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_c"
+ />
+ <Key android:codes="118" android:keyLabel="v"/>
+ <Key android:codes="98" android:keyLabel="b"/>
+ <Key android:codes="110" android:keyLabel="n"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="@string/alternates_for_n"
+ />
+ <Key android:codes="109" android:keyLabel="m"/>
+ <Key android:codes="-5" android:keyIcon="@drawable/sym_bkeyboard_delete"
+ android:keyWidth="15%p" android:keyEdgeFlags="right"
+ android:iconPreview="@drawable/sym_keyboard_feedback_delete"
+ android:isRepeatable="true"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_normal" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_bkeyboard_mic"
+ android:iconPreview="@drawable/sym_keyboard_feedback_mic"
+ android:keyWidth="10%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="40%p" android:isRepeatable="true"/>
+ <Key android:codes="46" android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+ android:keyWidth="10%p"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_url" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="/" android:keyWidth="10%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="40%p" android:isRepeatable="true"/>
+ <!--Key android:keyLabel="@string/popular_domain_0"
+ android:keyOutputText="@string/popular_domain_0"
+ android:popupKeyboard="@xml/popup_domains"
+ android:keyWidth="20%p"/-->
+ <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+ android:keyWidth="10%p"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_email" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="\@"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="40%p" android:isRepeatable="true"/>
+ <!--Key android:keyLabel="@string/popular_domain_0"
+ android:keyOutputText="@string/popular_domain_0"
+ android:popupKeyboard="@xml/popup_domains"
+ android:keyWidth="20%p"/-->
+ <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_im" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_bkeyboard_mic"
+ android:iconPreview="@drawable/sym_keyboard_feedback_mic"
+ android:keyWidth="10%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="40%p" android:isRepeatable="true"/>
+ <Key android:codes="46" android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+ android:keyWidth="10%p"/>
+ <Key android:keyLabel=":-)" android:keyOutputText=":-) "
+ android:popupKeyboard="@xml/popup_smileys"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row android:keyboardMode="@+id/mode_webentry" android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_bkeyboard_mic"
+ android:iconPreview="@drawable/sym_keyboard_feedback_mic"
+ android:keyWidth="10%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:keyWidth="20%p" android:isRepeatable="true"/>
+ <Key android:codes="9" android:keyIcon="@drawable/sym_bkeyboard_tab"
+ android:iconPreview="@drawable/sym_keyboard_feedback_tab"
+ android:keyWidth="20%p"/>
+ <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+ </Row>
+
+</Keyboard>
diff --git a/java/res/xml/kbd_symbols_black.xml b/java/res/xml/kbd_symbols_black.xml
new file mode 100755
index 000000000..5652f7fca
--- /dev/null
+++ b/java/res/xml/kbd_symbols_black.xml
@@ -0,0 +1,141 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2010, 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:android="http://schemas.android.com/apk/res/android"
+ android:keyWidth="10%p"
+ android:horizontalGap="0px"
+ android:verticalGap="0px"
+ android:keyHeight="@dimen/key_height"
+ >
+
+ <Row>
+ <Key android:codes="49" android:keyLabel="1" android:keyEdgeFlags="left"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="¹½⅓¼⅛"
+ />
+ <Key android:codes="50" android:keyLabel="2"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="²⅔"
+ />
+ <Key android:codes="51" android:keyLabel="3"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="³¾⅜"
+ />
+ <Key android:codes="52" android:keyLabel="4"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="⁴"
+ />
+ <Key android:codes="53" android:keyLabel="5"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="⅝"
+ />
+ <Key android:codes="54" android:keyLabel="6"/>
+ <Key android:codes="55" android:keyLabel="7"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="⅞"
+ />
+ <Key android:codes="56" android:keyLabel="8"/>
+ <Key android:codes="57" android:keyLabel="9"/>
+ <Key android:codes="48" android:keyLabel="0"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="ⁿ∅"
+ android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row>
+ <Key android:codes="64" android:keyLabel="\@" android:keyEdgeFlags="left"/>
+ <Key android:codes="35" android:keyLabel="\#"/>
+ <Key android:codes="36" android:keyLabel="$"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="¢£€¥₣₤₱"
+ />
+ <Key android:codes="37" android:keyLabel="%"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="‰"
+ />
+ <Key android:codes="38" android:keyLabel="&amp;"/>
+ <Key android:codes="42" android:keyLabel="*"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="†‡★"
+ />
+ <Key android:codes="45" android:keyLabel="-"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_–—"
+ />
+ <Key android:keyLabel="+"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="±"
+ />
+ <Key android:codes="40" android:keyLabel="("
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="[{&lt;"
+ />
+ <Key android:codes="41" android:keyLabel=")" android:keyEdgeFlags="right"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="]}&gt;"
+ />
+ </Row>
+
+ <Row>
+ <Key android:codes="-1" android:keyLabel="@string/label_alt_key"
+ android:keyWidth="15%p" android:isModifier="true"
+ android:isSticky="true" android:keyEdgeFlags="left"/>
+ <Key android:codes="33" android:keyLabel="!"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="¡"
+ />
+ <Key android:codes="34" android:keyLabel="&quot;"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="“”«»˝"
+ />
+ <Key android:codes="39" android:keyLabel="\'"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="‘’"
+ />
+ <Key android:codes="58" android:keyLabel=":"/>
+ <Key android:codes="59" android:keyLabel=";"/>
+ <Key android:codes="47" android:keyLabel="/" />
+ <Key android:codes="63" android:keyLabel="\?"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="¿"
+ />
+ <Key android:codes="-5" android:keyIcon="@drawable/sym_bkeyboard_delete" android:keyWidth="15%p" android:keyEdgeFlags="right"
+ android:iconPreview="@drawable/sym_keyboard_feedback_delete"
+ android:isRepeatable="true"/>
+ </Row>
+
+ <Row android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_alpha_key"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+ <Key android:codes="@integer/key_f1" android:keyWidth="10%p"/>
+ <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+ android:keyWidth="40%p"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:isRepeatable="true"/>
+ <Key android:codes="46" android:keyLabel="."
+ android:popupKeyboard="@xml/popup_punctuation"
+ android:keyWidth="10%p"/>
+ <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return" android:keyWidth="20%p" android:keyEdgeFlags="right"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ />
+ </Row>
+</Keyboard>
diff --git a/java/res/xml/kbd_symbols_shift.xml b/java/res/xml/kbd_symbols_shift.xml
index 09b5c3f9d..ca431fc8c 100755
--- a/java/res/xml/kbd_symbols_shift.xml
+++ b/java/res/xml/kbd_symbols_shift.xml
@@ -34,7 +34,10 @@
android:popupCharacters="♪♥♠♦♣"
/>
<Key android:keyLabel="√"/>
- <Key android:keyLabel="π"/>
+ <Key android:keyLabel="π"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="Π"
+ />
<Key android:keyLabel="÷"/>
<Key android:keyLabel="×"/>
<Key android:keyLabel="{"/>
diff --git a/java/res/xml/kbd_symbols_shift_black.xml b/java/res/xml/kbd_symbols_shift_black.xml
new file mode 100755
index 000000000..a8acb9d00
--- /dev/null
+++ b/java/res/xml/kbd_symbols_shift_black.xml
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2010, 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:android="http://schemas.android.com/apk/res/android"
+ android:keyWidth="10%p"
+ android:horizontalGap="0px"
+ android:verticalGap="0px"
+ android:keyHeight="@dimen/key_height"
+ >
+
+ <Row>
+ <Key android:keyLabel="~" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="`"/>
+ <Key android:keyLabel="|"/>
+ <Key android:keyLabel="•"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="♪♥♠♦♣"
+ />
+ <Key android:keyLabel="√"/>
+ <Key android:keyLabel="π"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="Π"
+ />
+ <Key android:keyLabel="÷"/>
+ <Key android:keyLabel="×"/>
+ <Key android:keyLabel="{"/>
+ <Key android:keyLabel="}" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row>
+ <Key android:codes="9" android:keyLabel="\u21E5" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="£"/>
+ <Key android:keyLabel="¢"/>
+ <Key android:keyLabel="€"/>
+ <Key android:keyLabel="°"/>
+ <Key android:keyLabel="^"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="↑↓←→"
+ />
+ <Key android:keyLabel="_"/>
+ <Key android:keyLabel="="
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="≠≈∞"
+ />
+ <Key android:keyLabel="["/>
+ <Key android:keyLabel="]" android:keyEdgeFlags="right"/>
+ </Row>
+
+ <Row>
+ <Key android:codes="-1" android:keyLabel="@string/label_alt_key"
+ android:keyWidth="15%p" android:isModifier="true"
+ android:isSticky="true" android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="™"/>
+ <Key android:keyLabel="®"/>
+ <Key android:keyLabel="©"/>
+ <Key android:keyLabel="¶"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="§"
+ />
+ <Key android:keyLabel="\\"/>
+ <Key android:keyLabel="&lt;"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="≤«‹"
+ />
+ <Key android:keyLabel="&gt;"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="≥»›"
+ />
+ <Key android:codes="-5" android:keyIcon="@drawable/sym_bkeyboard_delete" android:keyWidth="15%p" android:keyEdgeFlags="right"
+ android:iconPreview="@drawable/sym_keyboard_feedback_delete"
+ android:isRepeatable="true"/>
+ </Row>
+
+ <Row android:rowEdgeFlags="bottom">
+ <Key android:codes="-2" android:keyLabel="@string/label_alpha_key" android:keyWidth="20%p"
+ android:popupKeyboard="@xml/kbd_popup_template"
+ android:popupCharacters="_"
+ android:keyEdgeFlags="left"/>
+ <Key android:keyLabel="„" android:keyWidth="10%p" />
+ <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+ android:keyWidth="40%p"
+ android:iconPreview="@drawable/sym_keyboard_feedback_space"
+ android:isRepeatable="true"/>
+ <Key android:keyLabel="…" android:keyWidth="10%p" />
+ <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+ android:keyWidth="20%p" android:keyEdgeFlags="right"
+ android:iconPreview="@drawable/sym_keyboard_feedback_return"
+ />
+ </Row>
+</Keyboard>
diff --git a/java/res/xml/prefs.xml b/java/res/xml/prefs.xml
index 535b63f3b..e4c689aa8 100644
--- a/java/res/xml/prefs.xml
+++ b/java/res/xml/prefs.xml
@@ -37,6 +37,14 @@
android:defaultValue="true"
/>
+ <CheckBoxPreference
+ android:key="enable_logging"
+ android:title="@string/prefs_enable_log"
+ android:summary="@string/prefs_description_log"
+ android:persistent="true"
+ android:defaultValue="false"
+ />
+
<ListPreference
android:key="voice_mode"
android:title="@string/voice_input"
@@ -46,6 +54,15 @@
android:defaultValue="@string/voice_mode_main"
/>
+ <ListPreference
+ android:key="keyboard_layout"
+ android:title="@string/keyboard_layout"
+ android:persistent="true"
+ android:entryValues="@array/keyboard_layout_modes_values"
+ android:entries="@array/keyboard_layout_modes"
+ android:defaultValue="3"
+ />
+
<PreferenceScreen
android:title="@string/language_selection_title"
android:summary="@string/language_selection_summary">
@@ -81,6 +98,21 @@
android:defaultValue="@bool/enable_autocorrect"
android:dependency="show_suggestions"
/>
-
+
+ <CheckBoxPreference
+ android:key="bigram_suggestion"
+ android:title="@string/bigram_suggestion"
+ android:summary="@string/bigram_suggestion_summary"
+ android:persistent="true"
+ android:defaultValue="true"
+ android:dependency="auto_complete"
+ />
</PreferenceCategory>
+
+<CheckBoxPreference
+ android:key="debug_mode"
+ android:title="@string/prefs_debug_mode"
+ android:persistent="true"
+ android:defaultValue="false"
+ />
</PreferenceScreen>
diff --git a/java/src/com/android/inputmethod/latin/AutoDictionary.java b/java/src/com/android/inputmethod/latin/AutoDictionary.java
index 93f1985ca..94331d3f2 100644
--- a/java/src/com/android/inputmethod/latin/AutoDictionary.java
+++ b/java/src/com/android/inputmethod/latin/AutoDictionary.java
@@ -85,8 +85,8 @@ public class AutoDictionary extends ExpandableDictionary {
private static DatabaseHelper mOpenHelper = null;
- public AutoDictionary(Context context, LatinIME ime, String locale) {
- super(context);
+ public AutoDictionary(Context context, LatinIME ime, String locale, int dicTypeId) {
+ super(context, dicTypeId);
mIme = ime;
mLocale = locale;
if (mOpenHelper == null) {
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index 87de94b76..e2c0c4ccc 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -16,10 +16,15 @@
package com.android.inputmethod.latin;
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.channels.Channels;
import java.util.Arrays;
import android.content.Context;
-import android.content.res.AssetManager;
import android.util.Log;
/**
@@ -27,18 +32,33 @@ import android.util.Log;
*/
public class BinaryDictionary extends Dictionary {
- public static final int MAX_WORD_LENGTH = 48;
+ /**
+ * There is difference between what java and native code can handle.
+ * This value should only be used in BinaryDictionary.java
+ * It is necessary to keep it at this value because some languages e.g. German have
+ * really long words.
+ */
+ protected static final int MAX_WORD_LENGTH = 48;
+
+ private static final String TAG = "BinaryDictionary";
private static final int MAX_ALTERNATIVES = 16;
private static final int MAX_WORDS = 16;
+ private static final int MAX_BIGRAMS = 255; // TODO Probably don't need all 255
private static final int TYPED_LETTER_MULTIPLIER = 2;
private static final boolean ENABLE_MISSED_CHARACTERS = true;
+ private int mDicTypeId;
private int mNativeDict;
- private int mDictLength; // This value is set from native code, don't change the name!!!!
+ private int mDictLength;
private int[] mInputCodes = new int[MAX_WORD_LENGTH * MAX_ALTERNATIVES];
private char[] mOutputChars = new char[MAX_WORD_LENGTH * MAX_WORDS];
+ private char[] mOutputChars_bigrams = new char[MAX_WORD_LENGTH * MAX_BIGRAMS];
private int[] mFrequencies = new int[MAX_WORDS];
+ private int[] mFrequencies_bigrams = new int[MAX_BIGRAMS];
+ // Keep a reference to the native dict direct buffer in Java to avoid
+ // unexpected deallocation of the direct buffer.
+ private ByteBuffer mNativeDictDirectBuffer;
static {
try {
@@ -53,32 +73,120 @@ public class BinaryDictionary extends Dictionary {
* @param context application context for reading resources
* @param resId the resource containing the raw binary dictionary
*/
- public BinaryDictionary(Context context, int resId) {
- if (resId != 0) {
+ public BinaryDictionary(Context context, int[] resId, int dicTypeId) {
+ if (resId != null && resId.length > 0 && resId[0] != 0) {
loadDictionary(context, resId);
}
+ mDicTypeId = dicTypeId;
+ }
+
+ /**
+ * Create a dictionary from a byte buffer. This is used for testing.
+ * @param context application context for reading resources
+ * @param byteBuffer a ByteBuffer containing the binary dictionary
+ */
+ public BinaryDictionary(Context context, ByteBuffer byteBuffer, int dicTypeId) {
+ if (byteBuffer != null) {
+ if (byteBuffer.isDirect()) {
+ mNativeDictDirectBuffer = byteBuffer;
+ } else {
+ mNativeDictDirectBuffer = ByteBuffer.allocateDirect(byteBuffer.capacity());
+ byteBuffer.rewind();
+ mNativeDictDirectBuffer.put(byteBuffer);
+ }
+ mDictLength = byteBuffer.capacity();
+ mNativeDict = openNative(mNativeDictDirectBuffer,
+ TYPED_LETTER_MULTIPLIER, FULL_WORD_FREQ_MULTIPLIER);
+ }
+ mDicTypeId = dicTypeId;
}
- private native int openNative(AssetManager am, String resourcePath, int typedLetterMultiplier,
+ private native int openNative(ByteBuffer bb, int typedLetterMultiplier,
int fullWordMultiplier);
private native void closeNative(int dict);
private native boolean isValidWordNative(int nativeData, char[] word, int wordLength);
private native int getSuggestionsNative(int dict, int[] inputCodes, int codesSize,
- char[] outputChars, int[] frequencies,
- int maxWordLength, int maxWords, int maxAlternatives, int skipPos,
- int[] nextLettersFrequencies, int nextLettersSize);
-
- private final void loadDictionary(Context context, int resId) {
- AssetManager am = context.getResources().getAssets();
- String assetName = context.getResources().getString(resId);
- mNativeDict = openNative(am, assetName, TYPED_LETTER_MULTIPLIER, FULL_WORD_FREQ_MULTIPLIER);
+ char[] outputChars, int[] frequencies, int maxWordLength, int maxWords,
+ int maxAlternatives, int skipPos, int[] nextLettersFrequencies, int nextLettersSize);
+ private native int getBigramsNative(int dict, char[] prevWord, int prevWordLength,
+ int[] inputCodes, int inputCodesLength, char[] outputChars, int[] frequencies,
+ int maxWordLength, int maxBigrams, int maxAlternatives);
+
+ private final void loadDictionary(Context context, int[] resId) {
+ InputStream[] is = null;
+ try {
+ // merging separated dictionary into one if dictionary is separated
+ int total = 0;
+ is = new InputStream[resId.length];
+ for (int i = 0; i < resId.length; i++) {
+ is[i] = context.getResources().openRawResource(resId[i]);
+ total += is[i].available();
+ }
+
+ mNativeDictDirectBuffer =
+ ByteBuffer.allocateDirect(total).order(ByteOrder.nativeOrder());
+ int got = 0;
+ for (int i = 0; i < resId.length; i++) {
+ got += Channels.newChannel(is[i]).read(mNativeDictDirectBuffer);
+ }
+ if (got != total) {
+ Log.e(TAG, "Read " + got + " bytes, expected " + total);
+ } else {
+ mNativeDict = openNative(mNativeDictDirectBuffer,
+ TYPED_LETTER_MULTIPLIER, FULL_WORD_FREQ_MULTIPLIER);
+ mDictLength = total;
+ }
+ } catch (IOException e) {
+ Log.w(TAG, "No available memory for binary dictionary");
+ } finally {
+ try {
+ for (int i = 0;i < is.length; i++) {
+ is[i].close();
+ }
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to close input stream");
+ }
+ }
+ }
+
+
+ @Override
+ public void getBigrams(final WordComposer codes, final CharSequence previousWord,
+ final WordCallback callback, int[] nextLettersFrequencies) {
+
+ char[] chars = previousWord.toString().toCharArray();
+ Arrays.fill(mOutputChars_bigrams, (char) 0);
+ Arrays.fill(mFrequencies_bigrams, 0);
+
+ int codesSize = codes.size();
+ Arrays.fill(mInputCodes, -1);
+ int[] alternatives = codes.getCodesAt(0);
+ System.arraycopy(alternatives, 0, mInputCodes, 0,
+ Math.min(alternatives.length, MAX_ALTERNATIVES));
+
+ int count = getBigramsNative(mNativeDict, chars, chars.length, mInputCodes, codesSize,
+ mOutputChars_bigrams, mFrequencies_bigrams, MAX_WORD_LENGTH, MAX_BIGRAMS,
+ MAX_ALTERNATIVES);
+
+ for (int j = 0; j < count; j++) {
+ if (mFrequencies_bigrams[j] < 1) break;
+ int start = j * MAX_WORD_LENGTH;
+ int len = 0;
+ while (mOutputChars_bigrams[start + len] != 0) {
+ len++;
+ }
+ if (len > 0) {
+ callback.addWord(mOutputChars_bigrams, start, len, mFrequencies_bigrams[j],
+ mDicTypeId, DataType.BIGRAM);
+ }
+ }
}
@Override
public void getWords(final WordComposer codes, final WordCallback callback,
int[] nextLettersFrequencies) {
final int codesSize = codes.size();
- // Wont deal with really long words.
+ // Won't deal with really long words.
if (codesSize > MAX_WORD_LENGTH - 1) return;
Arrays.fill(mInputCodes, -1);
@@ -119,7 +227,8 @@ public class BinaryDictionary extends Dictionary {
len++;
}
if (len > 0) {
- callback.addWord(mOutputChars, start, len, mFrequencies[j]);
+ callback.addWord(mOutputChars, start, len, mFrequencies[j], mDicTypeId,
+ DataType.UNIGRAM);
}
}
}
diff --git a/java/src/com/android/inputmethod/latin/CandidateView.java b/java/src/com/android/inputmethod/latin/CandidateView.java
index 3a199bbaf..faf72c996 100755
--- a/java/src/com/android/inputmethod/latin/CandidateView.java
+++ b/java/src/com/android/inputmethod/latin/CandidateView.java
@@ -83,7 +83,6 @@ public class CandidateView extends View {
private int mDescent;
private boolean mScrolled;
private boolean mShowingAddToDictionary;
- private CharSequence mWordToAddToDictionary;
private CharSequence mAddToDictionaryHint;
private int mTargetScrollX;
@@ -144,9 +143,13 @@ public class CandidateView extends View {
mPaint.setStrokeWidth(0);
mPaint.setTextAlign(Align.CENTER);
mDescent = (int) mPaint.descent();
- // 80 pixels for a 160dpi device would mean half an inch
+ // 50 pixels for a 160dpi device would mean about 0.3 inch
mMinTouchableWidth = (int) (getResources().getDisplayMetrics().density * 50);
+ // Slightly reluctant to scroll to be able to easily choose the suggestion
+ // 50 pixels for a 160dpi device would mean about 0.3 inch
+ final int touchSlop = (int) (getResources().getDisplayMetrics().density * 50);
+ final int touchSlopSquare = touchSlop * touchSlop;
mGestureDetector = new GestureDetector(new GestureDetector.SimpleOnGestureListener() {
@Override
public void onLongPress(MotionEvent me) {
@@ -160,6 +163,13 @@ public class CandidateView extends View {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
+ final int deltaX = (int) (e2.getX() - e1.getX());
+ final int deltaY = (int) (e2.getY() - e1.getY());
+ final int distance = (deltaX * deltaX) + (deltaY * deltaY);
+ if (distance < touchSlopSquare) {
+ return false;
+ }
+
final int width = getWidth();
mScrolled = true;
int scrollX = getScrollX();
@@ -167,7 +177,7 @@ public class CandidateView extends View {
if (scrollX < 0) {
scrollX = 0;
}
- if (distanceX > 0 && scrollX + width > mTotalWidth) {
+ if (distanceX > 0 && scrollX + width > mTotalWidth) {
scrollX -= (int) distanceX;
}
mTargetScrollX = scrollX;
@@ -219,8 +229,7 @@ public class CandidateView extends View {
mDivider.getIntrinsicHeight());
}
int x = 0;
- final int count = mSuggestions.size();
- final int width = getWidth();
+ final int count = Math.min(mSuggestions.size(), MAX_SUGGESTIONS);
final Rect bgPadding = mBgPadding;
final Paint paint = mPaint;
final int touchX = mTouchX;
@@ -325,7 +334,6 @@ public class CandidateView extends View {
}
public void showAddToDictionaryHint(CharSequence word) {
- mWordToAddToDictionary = word;
ArrayList<CharSequence> suggestions = new ArrayList<CharSequence>();
suggestions.add(word);
suggestions.add(mAddToDictionaryHint);
@@ -335,7 +343,7 @@ public class CandidateView extends View {
public void scrollPrev() {
int i = 0;
- final int count = mSuggestions.size();
+ final int count = Math.min(mSuggestions.size(), MAX_SUGGESTIONS);
int firstItem = 0; // Actually just before the first item, if at the boundary
while (i < count) {
if (mWordX[i] < getScrollX()
@@ -354,7 +362,7 @@ public class CandidateView extends View {
int i = 0;
int scrollX = getScrollX();
int targetX = scrollX;
- final int count = mSuggestions.size();
+ final int count = Math.min(mSuggestions.size(), MAX_SUGGESTIONS);
int rightEdge = scrollX + getWidth();
while (i < count) {
if (mWordX[i] <= rightEdge &&
@@ -376,8 +384,14 @@ public class CandidateView extends View {
mScrolled = true;
}
}
-
+
+ /* package */ List<CharSequence> getSuggestions() {
+ return mSuggestions;
+ }
+
public void clear() {
+ // Don't call mSuggestions.clear() because it's being used for logging
+ // in LatinIME.pickSuggestionManually().
mSuggestions = EMPTY_LIST;
mTouchX = OUT_OF_BOUNDS;
mSelectedString = null;
@@ -412,7 +426,11 @@ public class CandidateView extends View {
if (y <= 0) {
// Fling up!?
if (mSelectedString != null) {
+ // If there are completions from the application, we don't change the state to
+ // STATE_PICKED_SUGGESTION
if (!mShowingCompletions) {
+ // This "acceptedSuggestion" will not be counted as a word because
+ // it will be counted in pickSuggestion instead.
TextEntryState.acceptedSuggestion(mSuggestions.get(0),
mSelectedString);
}
@@ -447,25 +465,6 @@ public class CandidateView extends View {
}
return true;
}
-
- /**
- * For flick through from keyboard, call this method with the x coordinate of the flick
- * gesture.
- * @param x
- */
- public void takeSuggestionAt(float x) {
- mTouchX = (int) x;
- // To detect candidate
- onDraw(null);
- if (mSelectedString != null) {
- if (!mShowingCompletions) {
- TextEntryState.acceptedSuggestion(mSuggestions.get(0), mSelectedString);
- }
- mService.pickSuggestionManually(mSelectedIndex, mSelectedString);
- }
- invalidate();
- mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_REMOVE_THROUGH_PREVIEW), 200);
- }
private void hidePreview() {
mCurrentWordIndex = OUT_OF_BOUNDS;
diff --git a/java/src/com/android/inputmethod/latin/ContactsDictionary.java b/java/src/com/android/inputmethod/latin/ContactsDictionary.java
index 15edb706a..756782887 100644
--- a/java/src/com/android/inputmethod/latin/ContactsDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ContactsDictionary.java
@@ -20,9 +20,10 @@ import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.database.Cursor;
-import android.os.AsyncTask;
import android.os.SystemClock;
import android.provider.ContactsContract.Contacts;
+import android.text.TextUtils;
+import android.util.Log;
public class ContactsDictionary extends ExpandableDictionary {
@@ -31,27 +32,35 @@ public class ContactsDictionary extends ExpandableDictionary {
Contacts.DISPLAY_NAME,
};
+ /**
+ * Frequency for contacts information into the dictionary
+ */
+ private static final int FREQUENCY_FOR_CONTACTS = 128;
+ private static final int FREQUENCY_FOR_CONTACTS_BIGRAM = 90;
+
private static final int INDEX_NAME = 1;
private ContentObserver mObserver;
private long mLastLoadedContacts;
- public ContactsDictionary(Context context) {
- super(context);
+ public ContactsDictionary(Context context, int dicTypeId) {
+ super(context, dicTypeId);
// Perform a managed query. The Activity will handle closing and requerying the cursor
// when needed.
ContentResolver cres = context.getContentResolver();
- cres.registerContentObserver(Contacts.CONTENT_URI, true, mObserver = new ContentObserver(null) {
- @Override
- public void onChange(boolean self) {
- setRequiresReload(true);
- }
- });
+ cres.registerContentObserver(
+ Contacts.CONTENT_URI, true,mObserver = new ContentObserver(null) {
+ @Override
+ public void onChange(boolean self) {
+ setRequiresReload(true);
+ }
+ });
loadDictionary();
}
+ @Override
public synchronized void close() {
if (mObserver != null) {
getContext().getContentResolver().unregisterContentObserver(mObserver);
@@ -89,6 +98,7 @@ public class ContactsDictionary extends ExpandableDictionary {
if (name != null) {
int len = name.length();
+ String prevWord = null;
// TODO: Better tokenization for non-Latin writing systems
for (int i = 0; i < len; i++) {
@@ -112,7 +122,13 @@ public class ContactsDictionary extends ExpandableDictionary {
// capitalization of i.
final int wordLen = word.length();
if (wordLen < maxWordLength && wordLen > 1) {
- super.addWord(word, 128);
+ super.addWord(word, FREQUENCY_FOR_CONTACTS);
+ if (!TextUtils.isEmpty(prevWord)) {
+ // TODO Do not add email address
+ super.addBigrams(prevWord, word,
+ FREQUENCY_FOR_CONTACTS_BIGRAM);
+ }
+ prevWord = word;
}
}
}
diff --git a/java/src/com/android/inputmethod/latin/Dictionary.java b/java/src/com/android/inputmethod/latin/Dictionary.java
index e7b526663..d04bf57a7 100644
--- a/java/src/com/android/inputmethod/latin/Dictionary.java
+++ b/java/src/com/android/inputmethod/latin/Dictionary.java
@@ -21,7 +21,6 @@ package com.android.inputmethod.latin;
* strokes.
*/
abstract public class Dictionary {
-
/**
* Whether or not to replicate the typed word in the suggested list, even if it's valid.
*/
@@ -31,7 +30,11 @@ abstract public class Dictionary {
* The weight to give to a word if it's length is the same as the number of typed characters.
*/
protected static final int FULL_WORD_FREQ_MULTIPLIER = 2;
-
+
+ public static enum DataType {
+ UNIGRAM, BIGRAM
+ }
+
/**
* Interface to be implemented by classes requesting words to be fetched from the dictionary.
* @see #getWords(WordComposer, WordCallback)
@@ -45,9 +48,12 @@ abstract public class Dictionary {
* @param wordLength length of valid characters in the character array
* @param frequency the frequency of occurence. 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
* @return true if the word was added, false if no more words are required
*/
- boolean addWord(char[] word, int wordOffset, int wordLength, int frequency);
+ boolean addWord(char[] word, int wordOffset, int wordLength, int frequency, int dicTypeId,
+ DataType dataType);
}
/**
@@ -65,6 +71,21 @@ abstract public class Dictionary {
int[] nextLettersFrequencies);
/**
+ * 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 callback the callback object to send possible word following previous word
+ * @param nextLettersFrequencies array of frequencies of next letters that could follow the
+ * word so far. For instance, "bracke" can be followed by "t", so array['t'] will have
+ * a non-zero value on returning from this method.
+ * Pass in null if you don't want the dictionary to look up next letters.
+ */
+ public void getBigrams(final WordComposer composer, final CharSequence previousWord,
+ final WordCallback callback, int[] nextLettersFrequencies) {
+ // empty base implementation
+ }
+
+ /**
* Checks if the given word occurs in the dictionary
* @param word the word to search for. The search should be case-insensitive.
* @return true if the word exists, false otherwise
diff --git a/java/src/com/android/inputmethod/voice/EditingUtil.java b/java/src/com/android/inputmethod/latin/EditingUtil.java
index 6316d8ccf..0c87f8d58 100644
--- a/java/src/com/android/inputmethod/voice/EditingUtil.java
+++ b/java/src/com/android/inputmethod/latin/EditingUtil.java
@@ -14,7 +14,9 @@
* the License.
*/
-package com.android.inputmethod.voice;
+package com.android.inputmethod.latin;
+
+import java.util.regex.Pattern;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
@@ -24,6 +26,11 @@ import android.view.inputmethod.InputConnection;
* Utility methods to deal with editing text through an InputConnection.
*/
public class EditingUtil {
+ /**
+ * Number of characters we want to look back in order to identify the previous word
+ */
+ private static final int LOOKBACK_CHARACTER_NUM = 15;
+
private EditingUtil() {};
/**
@@ -75,9 +82,21 @@ public class EditingUtil {
* represents the cursor, then "hello " will be returned.
*/
public static String getWordAtCursor(
- InputConnection connection, String separators) {
- Range range = getWordRangeAtCursor(connection, separators);
- return (range == null) ? null : range.word;
+ InputConnection connection, String separators) {
+ return getWordAtCursor(connection, separators, null);
+ }
+
+ /**
+ * @param connection connection to the current text field.
+ * @param sep 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, Range range) {
+ Range r = getWordRangeAtCursor(connection, separators, range);
+ return (r == null) ? null : r.word;
}
/**
@@ -87,7 +106,7 @@ public class EditingUtil {
public static void deleteWordAtCursor(
InputConnection connection, String separators) {
- Range range = getWordRangeAtCursor(connection, separators);
+ Range range = getWordRangeAtCursor(connection, separators, null);
if (range == null) return;
connection.finishComposingText();
@@ -101,18 +120,20 @@ public class EditingUtil {
/**
* Represents a range of text, relative to the current cursor position.
*/
- private static class Range {
+ public static class Range {
/** Characters before selection start */
- int charsBefore;
+ public int charsBefore;
/**
* Characters after selection start, including one trailing word
* separator.
*/
- int charsAfter;
+ public int charsAfter;
/** The actual characters that make up a word */
- String word;
+ public String word;
+
+ public Range() {}
public Range(int charsBefore, int charsAfter, String word) {
if (charsBefore < 0 || charsAfter < 0) {
@@ -125,7 +146,7 @@ public class EditingUtil {
}
private static Range getWordRangeAtCursor(
- InputConnection connection, String sep) {
+ InputConnection connection, String sep, Range range) {
if (connection == null || sep == null) {
return null;
}
@@ -137,20 +158,22 @@ public class EditingUtil {
// Find first word separator before the cursor
int start = before.length();
- while (--start > 0 && !isWhitespace(before.charAt(start - 1), sep));
+ 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));
- if (end < after.length() - 1) {
- end++; // Include trailing space, if it exists, in word
- }
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);
+ + after.toString().substring(0, end);
+
+ Range returnRange = range != null? range : new Range();
+ returnRange.charsBefore = before.length() - start;
+ returnRange.charsAfter = end;
+ returnRange.word = word;
+ return returnRange;
}
return null;
@@ -159,4 +182,25 @@ public class EditingUtil {
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!
+ CharSequence prev = connection.getTextBeforeCursor(LOOKBACK_CHARACTER_NUM, 0);
+ if (prev == null) {
+ return null;
+ }
+ String[] w = spaceRegex.split(prev);
+ if (w.length >= 2 && w[w.length-2].length() > 0) {
+ 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];
+ } else {
+ return null;
+ }
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
index 46bc41c42..53f9ed8c8 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
@@ -16,24 +16,32 @@
package com.android.inputmethod.latin;
-import com.android.inputmethod.latin.Dictionary.WordCallback;
+import java.util.LinkedList;
import android.content.Context;
import android.os.AsyncTask;
import android.os.SystemClock;
+import android.util.Log;
/**
* Base class for an in-memory dictionary that can grow dynamically and can
* be searched for suggestions and valid words.
*/
public class ExpandableDictionary extends Dictionary {
+ /**
+ * There is difference between what java and native code can handle.
+ * It uses 32 because Java stack overflows when greater value is used.
+ */
+ protected static final int MAX_WORD_LENGTH = 32;
+
private Context mContext;
private char[] mWordBuilder = new char[MAX_WORD_LENGTH];
+ private int mDicTypeId;
private int mMaxDepth;
private int mInputLength;
private int[] mNextLettersFrequencies;
+ private StringBuilder sb = new StringBuilder(MAX_WORD_LENGTH);
- public static final int MAX_WORD_LENGTH = 32;
private static final char QUOTE = '\'';
private boolean mRequiresReload;
@@ -47,7 +55,9 @@ public class ExpandableDictionary extends Dictionary {
char code;
int frequency;
boolean terminal;
+ Node parent;
NodeArray children;
+ LinkedList<NextWord> ngrams; // Supports ngram
}
static class NodeArray {
@@ -71,14 +81,27 @@ public class ExpandableDictionary extends Dictionary {
}
}
+ static class NextWord {
+ Node word;
+ NextWord nextWord;
+ int frequency;
+
+ NextWord(Node word, int frequency) {
+ this.word = word;
+ this.frequency = frequency;
+ }
+ }
+
+
private NodeArray mRoots;
private int[][] mCodes;
- ExpandableDictionary(Context context) {
+ ExpandableDictionary(Context context, int dicTypeId) {
mContext = context;
clearDictionary();
mCodes = new int[MAX_WORD_LENGTH][];
+ mDicTypeId = dicTypeId;
}
public void loadDictionary() {
@@ -118,12 +141,11 @@ public class ExpandableDictionary extends Dictionary {
}
public void addWord(String word, int frequency) {
- addWordRec(mRoots, word, 0, frequency);
+ addWordRec(mRoots, word, 0, frequency, null);
}
- private void addWordRec(NodeArray children, final String word,
- final int depth, final int frequency) {
-
+ private void addWordRec(NodeArray children, final String word, final int depth,
+ final int frequency, Node parentNode) {
final int wordLength = word.length();
final char c = word.charAt(depth);
// Does children have the current character?
@@ -140,6 +162,7 @@ public class ExpandableDictionary extends Dictionary {
if (!found) {
childNode = new Node();
childNode.code = c;
+ childNode.parent = parentNode;
children.add(childNode);
}
if (wordLength == depth + 1) {
@@ -152,7 +175,7 @@ public class ExpandableDictionary extends Dictionary {
if (childNode.children == null) {
childNode.children = new NodeArray();
}
- addWordRec(childNode.children, word, depth + 1, frequency);
+ addWordRec(childNode.children, word, depth + 1, frequency, childNode);
}
@Override
@@ -186,7 +209,7 @@ public class ExpandableDictionary extends Dictionary {
if (mRequiresReload) startDictionaryLoadingTaskLocked();
if (mUpdatingDictionary) return false;
}
- final int freq = getWordFrequencyRec(mRoots, word, 0, word.length());
+ final int freq = getWordFrequency(word);
return freq > -1;
}
@@ -194,32 +217,8 @@ public class ExpandableDictionary extends Dictionary {
* Returns the word's frequency or -1 if not found
*/
public int getWordFrequency(CharSequence word) {
- return getWordFrequencyRec(mRoots, word, 0, word.length());
- }
-
- /**
- * Returns the word's frequency or -1 if not found
- */
- private int getWordFrequencyRec(final NodeArray children, final CharSequence word,
- final int offset, final int length) {
- final int count = children.length;
- char currentChar = word.charAt(offset);
- for (int j = 0; j < count; j++) {
- final Node node = children.data[j];
- if (node.code == currentChar) {
- if (offset == length - 1) {
- if (node.terminal) {
- return node.frequency;
- }
- } else {
- if (node.children != null) {
- int freq = getWordFrequencyRec(node.children, word, offset + 1, length);
- if (freq > -1) return freq;
- }
- }
- }
- }
- return -1;
+ Node node = searchNode(mRoots, word, 0, word.length());
+ return (node == null) ? -1 : node.frequency;
}
/**
@@ -267,7 +266,8 @@ public class ExpandableDictionary extends Dictionary {
if (completion) {
word[depth] = c;
if (terminal) {
- if (!callback.addWord(word, 0, depth + 1, freq * snr)) {
+ if (!callback.addWord(word, 0, depth + 1, freq * snr, mDicTypeId,
+ DataType.UNIGRAM)) {
return;
}
// Add to frequency of next letters for predictive correction
@@ -305,7 +305,8 @@ public class ExpandableDictionary extends Dictionary {
|| !same(word, depth + 1, codes.getTypedWord())) {
int finalFreq = freq * snr * addedAttenuation;
if (skipPos < 0) finalFreq *= FULL_WORD_FREQ_MULTIPLIER;
- callback.addWord(word, 0, depth + 1, finalFreq);
+ callback.addWord(word, 0, depth + 1, finalFreq, mDicTypeId,
+ DataType.UNIGRAM);
}
}
if (children != null) {
@@ -324,6 +325,133 @@ public class ExpandableDictionary extends Dictionary {
}
}
+ /**
+ * Adds bigrams to the in-memory trie structure that is being used to retrieve any word
+ * @param addFrequency adding frequency of the pair
+ * @return returns the final frequency
+ */
+ protected int addBigrams(String word1, String word2, int addFrequency) {
+ Node firstWord = searchWord(mRoots, word1, 0, null);
+ Node secondWord = searchWord(mRoots, word2, 0, null);
+ LinkedList<NextWord> bigram = firstWord.ngrams;
+ if (bigram == null || bigram.size() == 0) {
+ firstWord.ngrams = new LinkedList<NextWord>();
+ bigram = firstWord.ngrams;
+ } else {
+ for (NextWord nw : bigram) {
+ if (nw.word == secondWord) {
+ nw.frequency += addFrequency;
+ return nw.frequency;
+ }
+ }
+ }
+ NextWord nw = new NextWord(secondWord, addFrequency);
+ firstWord.ngrams.add(nw);
+ return addFrequency;
+ }
+
+ /**
+ * Searches for the word and add the word if it does not exist.
+ * @return Returns the terminal node of the word we are searching for.
+ */
+ private Node searchWord(NodeArray children, String word, int depth, Node parentNode) {
+ final int wordLength = word.length();
+ final char c = word.charAt(depth);
+ // Does children have the current character?
+ final int childrenLength = children.length;
+ Node childNode = null;
+ boolean found = false;
+ for (int i = 0; i < childrenLength; i++) {
+ childNode = children.data[i];
+ if (childNode.code == c) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ childNode = new Node();
+ childNode.code = c;
+ childNode.parent = parentNode;
+ children.add(childNode);
+ }
+ if (wordLength == depth + 1) {
+ // Terminate this word
+ childNode.terminal = true;
+ return childNode;
+ }
+ if (childNode.children == null) {
+ childNode.children = new NodeArray();
+ }
+ return searchWord(childNode.children, word, depth + 1, childNode);
+ }
+
+ @Override
+ public void getBigrams(final WordComposer codes, final CharSequence previousWord,
+ final WordCallback callback, int[] nextLettersFrequencies) {
+ 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;
+ }
+
+ Node prevWord = searchNode(mRoots, previousWord, 0, previousWord.length());
+ if (prevWord != null && prevWord.ngrams != null) {
+ reverseLookUp(prevWord.ngrams, callback);
+ }
+ }
+
+ /**
+ * reverseLookUp retrieves the full word given a list of terminal nodes and adds those words
+ * through callback.
+ * @param terminalNodes list of terminal nodes we want to add
+ */
+ private void reverseLookUp(LinkedList<NextWord> terminalNodes,
+ final WordCallback callback) {
+ Node node;
+ int freq;
+ for (NextWord nextWord : terminalNodes) {
+ node = nextWord.word;
+ freq = nextWord.frequency;
+ sb.setLength(0);
+ do {
+ sb.insert(0, node.code);
+ node = node.parent;
+ } while(node != null);
+
+ // TODO better way to feed char array?
+ callback.addWord(sb.toString().toCharArray(), 0, sb.length(), freq, mDicTypeId,
+ DataType.BIGRAM);
+ }
+ }
+
+ /**
+ * Search for the terminal node of the word
+ * @return Returns the terminal node of the word if the word exists
+ */
+ private Node searchNode(final NodeArray children, final CharSequence word, final int offset,
+ final int length) {
+ // TODO Consider combining with addWordRec
+ final int count = children.length;
+ char currentChar = word.charAt(offset);
+ for (int j = 0; j < count; j++) {
+ final Node node = children.data[j];
+ if (node.code == currentChar) {
+ if (offset == length - 1) {
+ if (node.terminal) {
+ return node;
+ }
+ } else {
+ if (node.children != null) {
+ Node returnNode = searchNode(node.children, word, offset + 1, length);
+ if (returnNode != null) return returnNode;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
protected void clearDictionary() {
mRoots = new NodeArray();
}
diff --git a/java/src/com/android/inputmethod/latin/InputLanguageSelection.java b/java/src/com/android/inputmethod/latin/InputLanguageSelection.java
index 5e835e543..923dce359 100644
--- a/java/src/com/android/inputmethod/latin/InputLanguageSelection.java
+++ b/java/src/com/android/inputmethod/latin/InputLanguageSelection.java
@@ -99,7 +99,10 @@ public class InputLanguageSelection extends PreferenceActivity {
boolean haveDictionary = false;
conf.locale = locale;
res.updateConfiguration(conf, res.getDisplayMetrics());
- BinaryDictionary bd = new BinaryDictionary(this, R.raw.main);
+
+ int[] dictionaries = LatinIME.getDictionary(res, this.getPackageName());
+ BinaryDictionary bd = new BinaryDictionary(this, dictionaries, Suggest.DIC_MAIN);
+
// Is the dictionary larger than a placeholder? Arbitrarily chose a lower limit of
// 4000-5000 words, whereas the LARGE_DICTIONARY is about 20000+ words.
if (bd.getSize() > Suggest.LARGE_DICTIONARY_THRESHOLD / 4) {
diff --git a/java/src/com/android/inputmethod/latin/KeyboardSwitcher.java b/java/src/com/android/inputmethod/latin/KeyboardSwitcher.java
index 1a196448f..bcf4902f2 100644
--- a/java/src/com/android/inputmethod/latin/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/latin/KeyboardSwitcher.java
@@ -21,12 +21,15 @@ import java.util.Locale;
import java.util.Map;
import android.content.Context;
+import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.content.res.Resources;
-import android.inputmethodservice.InputMethodService;
+import android.preference.PreferenceManager;
+import android.view.InflateException;
-public class KeyboardSwitcher {
+public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceChangeListener {
+ public static final int MODE_NONE = 0;
public static final int MODE_TEXT = 1;
public static final int MODE_SYMBOLS = 2;
public static final int MODE_PHONE = 3;
@@ -45,6 +48,27 @@ public class KeyboardSwitcher {
public static final int KEYBOARDMODE_IM = R.id.mode_im;
public static final int KEYBOARDMODE_WEB = R.id.mode_webentry;
+ public static final String DEFAULT_LAYOUT_ID = "3";
+ public static final String PREF_KEYBOARD_LAYOUT = "keyboard_layout";
+ private static final int[] THEMES = new int [] {
+ R.layout.input_basic, R.layout.input_basic_highcontrast, R.layout.input_stone_normal,
+ R.layout.input_stone_bold};
+
+ // Ids for each characters' color in the keyboard
+ private static final int CHAR_THEME_COLOR_WHITE = 0;
+ private static final int CHAR_THEME_COLOR_BLACK = 1;
+
+ // Tables which contains resource ids for each character theme color
+ private static final int[] KBD_ALPHA = new int[] {R.xml.kbd_alpha, R.xml.kbd_alpha_black};
+ private static final int[] KBD_PHONE = new int[] {R.xml.kbd_phone, R.xml.kbd_phone_black};
+ private static final int[] KBD_PHONE_SYMBOLS = new int[] {
+ R.xml.kbd_phone_symbols, R.xml.kbd_phone_symbols_black};
+ private static final int[] KBD_SYMBOLS = new int[] {
+ R.xml.kbd_symbols, R.xml.kbd_symbols_black};
+ private static final int[] KBD_SYMBOLS_SHIFT = new int[] {
+ R.xml.kbd_symbols_shift, R.xml.kbd_symbols_shift_black};
+ private static final int[] KBD_QWERTY = new int[] {R.xml.kbd_qwerty, R.xml.kbd_qwerty_black};
+
private static final int SYMBOLS_MODE_STATE_NONE = 0;
private static final int SYMBOLS_MODE_STATE_BEGIN = 1;
private static final int SYMBOLS_MODE_STATE_SYMBOL = 2;
@@ -57,9 +81,8 @@ public class KeyboardSwitcher {
KEYBOARDMODE_IM,
KEYBOARDMODE_WEB};
- //LatinIME mContext;
Context mContext;
- InputMethodService mInputMethodService;
+ LatinIME mInputMethodService;
private KeyboardId mSymbolsId;
private KeyboardId mSymbolsShiftedId;
@@ -67,7 +90,7 @@ public class KeyboardSwitcher {
private KeyboardId mCurrentId;
private Map<KeyboardId, LatinKeyboard> mKeyboards;
- private int mMode; /** One of the MODE_XXX values */
+ private int mMode = MODE_NONE; /** One of the MODE_XXX values */
private int mImeOptions;
private int mTextMode = MODE_TEXT_QWERTY;
private boolean mIsSymbols;
@@ -79,13 +102,19 @@ public class KeyboardSwitcher {
private int mLastDisplayWidth;
private LanguageSwitcher mLanguageSwitcher;
private Locale mInputLocale;
- private boolean mEnableMultipleLanguages;
- KeyboardSwitcher(Context context, InputMethodService ims) {
+ private int mLayoutId;
+
+ KeyboardSwitcher(Context context, LatinIME ims) {
mContext = context;
+
+ final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ims);
+ mLayoutId = Integer.valueOf(prefs.getString(PREF_KEYBOARD_LAYOUT, DEFAULT_LAYOUT_ID));
+ prefs.registerOnSharedPreferenceChangeListener(this);
+
mKeyboards = new HashMap<KeyboardId, LatinKeyboard>();
- mSymbolsId = new KeyboardId(R.xml.kbd_symbols, false);
- mSymbolsShiftedId = new KeyboardId(R.xml.kbd_symbols_shift, false);
+ mSymbolsId = makeSymbolsId(false);
+ mSymbolsShiftedId = makeSymbolsShiftedId(false);
mInputMethodService = ims;
}
@@ -98,14 +127,24 @@ public class KeyboardSwitcher {
void setLanguageSwitcher(LanguageSwitcher languageSwitcher) {
mLanguageSwitcher = languageSwitcher;
mInputLocale = mLanguageSwitcher.getInputLocale();
- mEnableMultipleLanguages = mLanguageSwitcher.getLocaleCount() > 1;
}
void setInputView(LatinKeyboardView inputView) {
mInputView = inputView;
}
-
+
+ private KeyboardId makeSymbolsId(boolean hasVoice) {
+ return new KeyboardId(KBD_SYMBOLS[getCharColorId()], hasVoice);
+ }
+
+ private KeyboardId makeSymbolsShiftedId(boolean hasVoice) {
+ return new KeyboardId(KBD_SYMBOLS_SHIFT[getCharColorId()], hasVoice);
+ }
+
void makeKeyboards(boolean forceCreate) {
+ mSymbolsId = makeSymbolsId(mHasVoice && !mVoiceOnPrimary);
+ mSymbolsShiftedId = makeSymbolsShiftedId(mHasVoice && !mVoiceOnPrimary);
+
if (forceCreate) mKeyboards.clear();
// Configuration change is coming after the keyboard gets recreated. So don't rely on that.
// If keyboards have already been made, check if we have a screen width change and
@@ -114,9 +153,6 @@ public class KeyboardSwitcher {
if (displayWidth == mLastDisplayWidth) return;
mLastDisplayWidth = displayWidth;
if (!forceCreate) mKeyboards.clear();
- mSymbolsId = new KeyboardId(R.xml.kbd_symbols, mHasVoice && !mVoiceOnPrimary);
- mSymbolsShiftedId = new KeyboardId(R.xml.kbd_symbols_shift,
- mHasVoice && !mVoiceOnPrimary);
}
/**
@@ -140,6 +176,7 @@ public class KeyboardSwitcher {
this(xml, 0, false, hasVoice);
}
+ @Override
public boolean equals(Object other) {
return other instanceof KeyboardId && equals((KeyboardId) other);
}
@@ -150,6 +187,7 @@ public class KeyboardSwitcher {
&& other.mEnableShiftLock == this.mEnableShiftLock;
}
+ @Override
public int hashCode() {
return (mXml + 1) * (mKeyboardMode + 1) * (mEnableShiftLock ? 2 : 1)
* (mHasVoice ? 4 : 8);
@@ -173,8 +211,14 @@ public class KeyboardSwitcher {
void setKeyboardMode(int mode, int imeOptions, boolean enableVoice) {
mSymbolsModeState = SYMBOLS_MODE_STATE_NONE;
mPreferSymbols = mode == MODE_SYMBOLS;
- setKeyboardMode(mode == MODE_SYMBOLS ? MODE_TEXT : mode, imeOptions, enableVoice,
- mPreferSymbols);
+ if (mode == MODE_SYMBOLS) {
+ mode = MODE_TEXT;
+ }
+ try {
+ setKeyboardMode(mode, imeOptions, enableVoice, mPreferSymbols);
+ } catch (RuntimeException e) {
+ LatinImeLogger.logOnException(mode + "," + imeOptions + "," + mPreferSymbols, e);
+ }
}
void setKeyboardMode(int mode, int imeOptions, boolean enableVoice, boolean isSymbols) {
@@ -188,8 +232,8 @@ public class KeyboardSwitcher {
mInputView.setPreviewEnabled(true);
KeyboardId id = getKeyboardId(mode, imeOptions, isSymbols);
-
- LatinKeyboard keyboard = getKeyboard(id);
+ LatinKeyboard keyboard = null;
+ keyboard = getKeyboard(id);
if (mode == MODE_PHONE) {
mInputView.setPhoneKeyboard(keyboard);
@@ -201,6 +245,7 @@ public class KeyboardSwitcher {
keyboard.setShifted(false);
keyboard.setShiftLocked(keyboard.isShiftLocked());
keyboard.setImeOptions(mContext.getResources(), mMode, imeOptions);
+ keyboard.setBlackFlag(isBlackSym());
}
private LatinKeyboard getKeyboard(KeyboardId id) {
@@ -212,8 +257,10 @@ public class KeyboardSwitcher {
orig.updateConfiguration(conf, null);
LatinKeyboard keyboard = new LatinKeyboard(
mContext, id.mXml, id.mKeyboardMode);
- keyboard.setVoiceMode(hasVoiceButton(id.mXml == R.xml.kbd_symbols), mHasVoice);
+ keyboard.setVoiceMode(hasVoiceButton(id.mXml == R.xml.kbd_symbols
+ || id.mXml == R.xml.kbd_symbols_black), mHasVoice);
keyboard.setLanguageSwitcher(mLanguageSwitcher);
+ keyboard.setBlackFlag(isBlackSym());
if (id.mKeyboardMode == KEYBOARDMODE_NORMAL
|| id.mKeyboardMode == KEYBOARDMODE_URL
|| id.mKeyboardMode == KEYBOARDMODE_IM
@@ -236,31 +283,40 @@ public class KeyboardSwitcher {
private KeyboardId getKeyboardId(int mode, int imeOptions, boolean isSymbols) {
boolean hasVoice = hasVoiceButton(isSymbols);
+ int charColorId = getCharColorId();
+ // TODO: generalize for any KeyboardId
+ int keyboardRowsResId = KBD_QWERTY[charColorId];
if (isSymbols) {
- return (mode == MODE_PHONE)
- ? new KeyboardId(R.xml.kbd_phone_symbols, hasVoice)
- : new KeyboardId(R.xml.kbd_symbols, hasVoice);
+ if (mode == MODE_PHONE) {
+ return new KeyboardId(KBD_PHONE_SYMBOLS[charColorId], hasVoice);
+ } else {
+ return new KeyboardId(KBD_SYMBOLS[charColorId], hasVoice);
+ }
}
switch (mode) {
+ case MODE_NONE:
+ LatinImeLogger.logOnWarning(
+ "getKeyboardId:" + mode + "," + imeOptions + "," + isSymbols);
+ /* fall through */
case MODE_TEXT:
- if (mTextMode == MODE_TEXT_QWERTY) {
- return new KeyboardId(R.xml.kbd_qwerty, KEYBOARDMODE_NORMAL, true, hasVoice);
- } else if (mTextMode == MODE_TEXT_ALPHA) {
- return new KeyboardId(R.xml.kbd_alpha, KEYBOARDMODE_NORMAL, true, hasVoice);
+ if (mTextMode == MODE_TEXT_ALPHA) {
+ return new KeyboardId(
+ KBD_ALPHA[charColorId], KEYBOARDMODE_NORMAL, true, hasVoice);
}
- break;
+ // Normally mTextMode should be MODE_TEXT_QWERTY.
+ return new KeyboardId(keyboardRowsResId, KEYBOARDMODE_NORMAL, true, hasVoice);
case MODE_SYMBOLS:
- return new KeyboardId(R.xml.kbd_symbols, hasVoice);
+ return new KeyboardId(KBD_SYMBOLS[charColorId], hasVoice);
case MODE_PHONE:
- return new KeyboardId(R.xml.kbd_phone, hasVoice);
+ return new KeyboardId(KBD_PHONE[charColorId], hasVoice);
case MODE_URL:
- return new KeyboardId(R.xml.kbd_qwerty, KEYBOARDMODE_URL, true, hasVoice);
+ return new KeyboardId(keyboardRowsResId, KEYBOARDMODE_URL, true, hasVoice);
case MODE_EMAIL:
- return new KeyboardId(R.xml.kbd_qwerty, KEYBOARDMODE_EMAIL, true, hasVoice);
+ return new KeyboardId(keyboardRowsResId, KEYBOARDMODE_EMAIL, true, hasVoice);
case MODE_IM:
- return new KeyboardId(R.xml.kbd_qwerty, KEYBOARDMODE_IM, true, hasVoice);
+ return new KeyboardId(keyboardRowsResId, KEYBOARDMODE_IM, true, hasVoice);
case MODE_WEB:
- return new KeyboardId(R.xml.kbd_qwerty, KEYBOARDMODE_WEB, true, hasVoice);
+ return new KeyboardId(keyboardRowsResId, KEYBOARDMODE_WEB, true, hasVoice);
}
return null;
}
@@ -273,19 +329,6 @@ public class KeyboardSwitcher {
return mMode == MODE_TEXT;
}
- int getTextMode() {
- return mTextMode;
- }
-
- void setTextMode(int position) {
- if (position < MODE_TEXT_COUNT && position >= 0) {
- mTextMode = position;
- }
- if (isTextMode()) {
- setKeyboardMode(MODE_TEXT, mImeOptions, mHasVoice);
- }
- }
-
int getTextModeCount() {
return MODE_TEXT_COUNT;
}
@@ -314,7 +357,7 @@ public class KeyboardSwitcher {
LatinKeyboard symbolsShiftedKeyboard = getKeyboard(mSymbolsShiftedId);
symbolsShiftedKeyboard.setShifted(false);
mCurrentId = mSymbolsId;
- mInputView.setKeyboard(getKeyboard(mSymbolsId));
+ mInputView.setKeyboard(symbolsKeyboard);
symbolsKeyboard.setShifted(false);
symbolsKeyboard.setImeOptions(mContext.getResources(), mMode, mImeOptions);
}
@@ -348,4 +391,72 @@ public class KeyboardSwitcher {
}
return false;
}
+
+ public LatinKeyboardView getInputView() {
+ return mInputView;
+ }
+
+ public void recreateInputView() {
+ changeLatinKeyboardView(mLayoutId, true);
+ }
+
+ private void changeLatinKeyboardView(int newLayout, boolean forceReset) {
+ if (mLayoutId != newLayout || mInputView == null || forceReset) {
+ if (mInputView != null) {
+ mInputView.closing();
+ }
+ if (THEMES.length <= newLayout) {
+ newLayout = Integer.valueOf(DEFAULT_LAYOUT_ID);
+ }
+
+ LatinIMEUtil.GCUtils.getInstance().reset();
+ boolean tryGC = true;
+ for (int i = 0; i < LatinIMEUtil.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) {
+ try {
+ mInputView = (LatinKeyboardView) mInputMethodService.getLayoutInflater(
+ ).inflate(THEMES[newLayout], null);
+ tryGC = false;
+ } catch (OutOfMemoryError e) {
+ tryGC = LatinIMEUtil.GCUtils.getInstance().tryGCOrWait(
+ mLayoutId + "," + newLayout, e);
+ } catch (InflateException e) {
+ tryGC = LatinIMEUtil.GCUtils.getInstance().tryGCOrWait(
+ mLayoutId + "," + newLayout, e);
+ }
+ }
+ mInputView.setExtentionLayoutResId(THEMES[newLayout]);
+ mInputView.setOnKeyboardActionListener(mInputMethodService);
+ mLayoutId = newLayout;
+ }
+ mInputMethodService.mHandler.post(new Runnable() {
+ public void run() {
+ if (mInputView != null) {
+ mInputMethodService.setInputView(mInputView);
+ }
+ mInputMethodService.updateInputViewShown();
+ }});
+ }
+
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+ if (PREF_KEYBOARD_LAYOUT.equals(key)) {
+ changeLatinKeyboardView(
+ Integer.valueOf(sharedPreferences.getString(key, DEFAULT_LAYOUT_ID)), false);
+ }
+ }
+
+ public boolean isBlackSym () {
+ if (mInputView != null && mInputView.getSymbolColorSheme() == 1) {
+ return true;
+ }
+ return false;
+ }
+
+ private int getCharColorId () {
+ if (isBlackSym()) {
+ return CHAR_THEME_COLOR_BLACK;
+ } else {
+ return CHAR_THEME_COLOR_WHITE;
+ }
+ }
+
}
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index b4ed80c1f..bbca316a4 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -16,11 +16,12 @@
package com.android.inputmethod.latin;
-import com.android.inputmethod.voice.EditingUtil;
import com.android.inputmethod.voice.FieldContext;
import com.android.inputmethod.voice.SettingsUtil;
import com.android.inputmethod.voice.VoiceInput;
+import org.xmlpull.v1.XmlPullParserException;
+
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -30,9 +31,9 @@ import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
import android.inputmethodservice.InputMethodService;
import android.inputmethodservice.Keyboard;
-import android.inputmethodservice.KeyboardView;
import android.media.AudioManager;
import android.os.Debug;
import android.os.Handler;
@@ -40,9 +41,9 @@ import android.os.Message;
import android.os.SystemClock;
import android.preference.PreferenceManager;
import android.speech.SpeechRecognizer;
-import android.text.AutoText;
import android.text.ClipboardManager;
import android.text.TextUtils;
+import android.util.DisplayMetrics;
import android.util.Log;
import android.util.PrintWriterPrinter;
import android.util.Printer;
@@ -50,8 +51,8 @@ import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
-import android.view.ViewParent;
import android.view.ViewGroup;
+import android.view.ViewParent;
import android.view.Window;
import android.view.WindowManager;
import android.view.inputmethod.CompletionInfo;
@@ -62,6 +63,7 @@ import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import java.io.FileDescriptor;
+import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
@@ -74,14 +76,16 @@ import java.util.Map;
* Input method implementation for Qwerty'ish keyboard.
*/
public class LatinIME extends InputMethodService
- implements KeyboardView.OnKeyboardActionListener,
+ implements LatinKeyboardBaseView.OnKeyboardActionListener,
VoiceInput.UiListener,
SharedPreferences.OnSharedPreferenceChangeListener {
private static final String TAG = "LatinIME";
+ private static final boolean PERF_DEBUG = false;
static final boolean DEBUG = false;
static final boolean TRACE = false;
static final boolean VOICE_INSTALLED = true;
static final boolean ENABLE_VOICE_BUTTON = true;
+ private static final boolean MODIFY_TEXT_FOR_CORRECTION = false;
private static final String PREF_VIBRATE_ON = "vibrate_on";
private static final String PREF_SOUND_ON = "sound_on";
@@ -89,6 +93,7 @@ public class LatinIME extends InputMethodService
private static final String PREF_QUICK_FIXES = "quick_fixes";
private static final String PREF_SHOW_SUGGESTIONS = "show_suggestions";
private static final String PREF_AUTO_COMPLETE = "auto_complete";
+ private static final String PREF_BIGRAM_SUGGESTIONS = "bigram_suggestion";
private static final String PREF_VOICE_MODE = "voice_mode";
// Whether or not the user has used voice input before (and thus, whether to show the
@@ -127,6 +132,7 @@ public class LatinIME extends InputMethodService
private static final int MSG_UPDATE_SHIFT_STATE = 2;
private static final int MSG_VOICE_RESULTS = 3;
private static final int MSG_START_LISTENING_AFTER_SWIPE = 4;
+ private static final int MSG_UPDATE_OLD_SUGGESTIONS = 5;
// If we detect a swipe gesture within N ms of typing, then swipe is
// ignored, since it may in fact be two key presses in quick succession.
@@ -145,7 +151,7 @@ public class LatinIME extends InputMethodService
private static final int POS_SETTINGS = 0;
private static final int POS_METHOD = 1;
- private LatinKeyboardView mInputView;
+ //private LatinKeyboardView mInputView;
private CandidateViewContainer mCandidateViewContainer;
private CandidateView mCandidateView;
private Suggest mSuggest;
@@ -157,6 +163,8 @@ public class LatinIME extends InputMethodService
KeyboardSwitcher mKeyboardSwitcher;
private UserDictionary mUserDictionary;
+ // User Bigram is disabled for now
+ //private UserBigramDictionary mUserBigramDictionary;
private ContactsDictionary mContactsDictionary;
private AutoDictionary mAutoDictionary;
@@ -176,7 +184,6 @@ public class LatinIME extends InputMethodService
private boolean mAfterVoiceInput;
private boolean mImmediatelyAfterVoiceInput;
private boolean mShowingVoiceSuggestions;
- private boolean mImmediatelyAfterVoiceSuggestions;
private boolean mVoiceInputHighlighted;
private boolean mEnableVoiceButton;
private CharSequence mBestWord;
@@ -186,10 +193,10 @@ public class LatinIME extends InputMethodService
private boolean mAutoSpace;
private boolean mJustAddedAutoSpace;
private boolean mAutoCorrectEnabled;
+ private boolean mBigramSuggestionEnabled;
private boolean mAutoCorrectOn;
private boolean mCapsLock;
private boolean mPasswordText;
- private boolean mEmailText;
private boolean mVibrateOn;
private boolean mSoundOn;
private boolean mAutoCap;
@@ -198,13 +205,18 @@ public class LatinIME extends InputMethodService
private boolean mHasUsedVoiceInputUnsupportedLocale;
private boolean mLocaleSupportedForVoiceInput;
private boolean mShowSuggestions;
- private boolean mSuggestionShouldReplaceCurrentWord;
private boolean mIsShowingHint;
private int mCorrectionMode;
private boolean mEnableVoice = true;
private boolean mVoiceOnPrimary;
private int mOrientation;
private List<CharSequence> mSuggestPuncList;
+ // Keep track of the last selection range to decide if we need to show word alternatives
+ private int mLastSelectionStart;
+ private int mLastSelectionEnd;
+
+ // Input type is such that we should not auto-correct
+ private boolean mInputTypeNoAutoCorrect;
// Indicates whether the suggestion strip is to be on in landscape
private boolean mJustAccepted;
@@ -219,8 +231,9 @@ public class LatinIME extends InputMethodService
private final float FX_VOLUME = -1.0f;
private boolean mSilentMode;
- private String mWordSeparators;
+ /* package */ String mWordSeparators;
private String mSentenceSeparators;
+ private String mSuggestPuncs;
private VoiceInput mVoiceInput;
private VoiceResults mVoiceResults = new VoiceResults();
private long mSwipeTriggerTimeMillis;
@@ -228,17 +241,66 @@ public class LatinIME extends InputMethodService
// Keeps track of most recently inserted text (multi-character key) for reverting
private CharSequence mEnteredText;
+ private boolean mRefreshKeyboardRequired;
// For each word, a list of potential replacements, usually from voice.
private Map<String, List<CharSequence>> mWordToSuggestions =
new HashMap<String, List<CharSequence>>();
+ private ArrayList<WordAlternatives> mWordHistory = new ArrayList<WordAlternatives>();
+
private class VoiceResults {
List<String> candidates;
Map<String, List<CharSequence>> alternatives;
}
+
+ public abstract static class WordAlternatives {
+ protected CharSequence mChosenWord;
- private boolean mRefreshKeyboardRequired;
+ public WordAlternatives() {
+ // Nothing
+ }
+
+ public WordAlternatives(CharSequence chosenWord) {
+ mChosenWord = chosenWord;
+ }
+
+ @Override
+ public int hashCode() {
+ return mChosenWord.hashCode();
+ }
+
+ public abstract CharSequence getOriginalWord();
+
+ public CharSequence getChosenWord() {
+ return mChosenWord;
+ }
+
+ public abstract List<CharSequence> getAlternatives();
+ }
+
+ public class TypedWordAlternatives extends WordAlternatives {
+ private WordComposer word;
+
+ public TypedWordAlternatives() {
+ // Nothing
+ }
+
+ public TypedWordAlternatives(CharSequence chosenWord, WordComposer wordComposer) {
+ super(chosenWord);
+ word = wordComposer;
+ }
+
+ @Override
+ public CharSequence getOriginalWord() {
+ return word.getTypedWord();
+ }
+
+ @Override
+ public List<CharSequence> getAlternatives() {
+ return getTypedSuggestions(word);
+ }
+ }
Handler mHandler = new Handler() {
@Override
@@ -247,10 +309,14 @@ public class LatinIME extends InputMethodService
case MSG_UPDATE_SUGGESTIONS:
updateSuggestions();
break;
+ case MSG_UPDATE_OLD_SUGGESTIONS:
+ setOldSuggestions();
+ break;
case MSG_START_TUTORIAL:
if (mTutorial == null) {
- if (mInputView.isShown()) {
- mTutorial = new Tutorial(LatinIME.this, mInputView);
+ if (mKeyboardSwitcher.getInputView().isShown()) {
+ mTutorial = new Tutorial(
+ LatinIME.this, mKeyboardSwitcher.getInputView());
mTutorial.start();
} else {
// Try again soon if the view is not yet showing
@@ -273,6 +339,7 @@ public class LatinIME extends InputMethodService
};
@Override public void onCreate() {
+ LatinImeLogger.init(this);
super.onCreate();
//setStatusIcon(R.drawable.ime_qwerty);
mResources = getResources();
@@ -288,7 +355,18 @@ public class LatinIME extends InputMethodService
if (inputLanguage == null) {
inputLanguage = conf.locale.toString();
}
- initSuggest(inputLanguage);
+
+ LatinIMEUtil.GCUtils.getInstance().reset();
+ boolean tryGC = true;
+ for (int i = 0; i < LatinIMEUtil.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) {
+ try {
+ initSuggest(inputLanguage);
+ tryGC = false;
+ } catch (OutOfMemoryError e) {
+ tryGC = LatinIMEUtil.GCUtils.getInstance().tryGCOrWait(inputLanguage, e);
+ }
+ }
+
mOrientation = conf.orientation;
initSuggestPuncList();
@@ -311,6 +389,45 @@ public class LatinIME extends InputMethodService
prefs.registerOnSharedPreferenceChangeListener(this);
}
+ /**
+ * Loads a dictionary or multiple separated dictionary
+ * @return returns array of dictionary resource ids
+ */
+ static int[] getDictionary(Resources res, String packageName) {
+ XmlResourceParser xrp = res.getXml(R.xml.dictionary);
+ int dictionaryCount = 0;
+ ArrayList<Integer> dictionaries = new ArrayList<Integer>();
+
+ try {
+ int current = xrp.getEventType();
+ while (current != XmlResourceParser.END_DOCUMENT) {
+ if (current == XmlResourceParser.START_TAG) {
+ String tag = xrp.getName();
+ if (tag != null) {
+ if (tag.equals("part")) {
+ String dictFileName = xrp.getAttributeValue(null, "name");
+ dictionaries.add(res.getIdentifier(dictFileName, "raw", packageName));
+ }
+ }
+ }
+ xrp.next();
+ current = xrp.getEventType();
+ }
+ } catch (XmlPullParserException e) {
+ Log.e(TAG, "Dictionary XML parsing failure");
+ } catch (IOException e) {
+ Log.e(TAG, "Dictionary XML IOException");
+ }
+
+ int count = dictionaries.size();
+ int[] dict = new int[count];
+ for (int i = 0; i < count; i++) {
+ dict[i] = dictionaries.get(i);
+ }
+
+ return dict;
+ }
+
private void initSuggest(String locale) {
mInputLocale = locale;
@@ -324,17 +441,28 @@ public class LatinIME extends InputMethodService
}
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
mQuickFixes = sp.getBoolean(PREF_QUICK_FIXES, true);
- mSuggest = new Suggest(this, R.raw.main);
+
+ int[] dictionaries = getDictionary(orig, this.getPackageName());
+ mSuggest = new Suggest(this, dictionaries);
updateAutoTextEnabled(saveLocale);
if (mUserDictionary != null) mUserDictionary.close();
mUserDictionary = new UserDictionary(this, mInputLocale);
if (mContactsDictionary == null) {
- mContactsDictionary = new ContactsDictionary(this);
+ mContactsDictionary = new ContactsDictionary(this, Suggest.DIC_CONTACTS);
}
if (mAutoDictionary != null) {
mAutoDictionary.close();
}
- mAutoDictionary = new AutoDictionary(this, this, mInputLocale);
+ mAutoDictionary = new AutoDictionary(this, this, mInputLocale, Suggest.DIC_AUTO);
+ // User Bigram is disabled for now
+ /*
+ if (mUserBigramDictionary != null) {
+ mUserBigramDictionary.close();
+ }
+ mUserBigramDictionary = new UserBigramDictionary(this, this, mInputLocale,
+ Suggest.DIC_USERBIGRAM);
+ mSuggest.setUserBigramDictionary(mUserBigramDictionary);
+ */
mSuggest.setUserDictionary(mUserDictionary);
mSuggest.setContactsDictionary(mContactsDictionary);
mSuggest.setAutoDictionary(mAutoDictionary);
@@ -348,12 +476,18 @@ public class LatinIME extends InputMethodService
@Override
public void onDestroy() {
- mUserDictionary.close();
- mContactsDictionary.close();
+ if (mUserDictionary != null) {
+ mUserDictionary.close();
+ }
+ if (mContactsDictionary != null) {
+ mContactsDictionary.close();
+ }
unregisterReceiver(mReceiver);
- if (VOICE_INSTALLED) {
+ if (VOICE_INSTALLED && mVoiceInput != null) {
mVoiceInput.destroy();
}
+ LatinImeLogger.commit();
+ LatinImeLogger.onDestroy();
super.onDestroy();
}
@@ -393,15 +527,12 @@ public class LatinIME extends InputMethodService
@Override
public View onCreateInputView() {
- mInputView = (LatinKeyboardView) getLayoutInflater().inflate(
- R.layout.input, null);
- mKeyboardSwitcher.setInputView(mInputView);
+ mKeyboardSwitcher.recreateInputView();
mKeyboardSwitcher.makeKeyboards(true);
- mInputView.setOnKeyboardActionListener(this);
mKeyboardSwitcher.setKeyboardMode(
KeyboardSwitcher.MODE_TEXT, 0,
shouldShowVoiceButton(makeFieldContext(), getCurrentInputEditorInfo()));
- return mInputView;
+ return mKeyboardSwitcher.getInputView();
}
@Override
@@ -418,8 +549,9 @@ public class LatinIME extends InputMethodService
@Override
public void onStartInputView(EditorInfo attribute, boolean restarting) {
+ LatinKeyboardView inputView = mKeyboardSwitcher.getInputView();
// In landscape mode, this method gets called without the input view being created.
- if (mInputView == null) {
+ if (inputView == null) {
return;
}
@@ -448,15 +580,12 @@ public class LatinIME extends InputMethodService
mAfterVoiceInput = false;
mImmediatelyAfterVoiceInput = false;
mShowingVoiceSuggestions = false;
- mImmediatelyAfterVoiceSuggestions = false;
mVoiceInputHighlighted = false;
- mWordToSuggestions.clear();
mInputTypeNoAutoCorrect = false;
mPredictionOn = false;
mCompletionOn = false;
mCompletions = null;
mCapsLock = false;
- mEmailText = false;
mEnteredText = null;
switch (attribute.inputType & EditorInfo.TYPE_MASK_CLASS) {
@@ -479,9 +608,6 @@ public class LatinIME extends InputMethodService
variation == EditorInfo.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD ) {
mPredictionOn = false;
}
- if (variation == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS) {
- mEmailText = true;
- }
if (variation == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS
|| variation == EditorInfo.TYPE_TEXT_VARIATION_PERSON_NAME) {
mAutoSpace = false;
@@ -532,7 +658,7 @@ public class LatinIME extends InputMethodService
attribute.imeOptions, enableVoiceButton);
updateShiftKeyState(attribute);
}
- mInputView.closing();
+ inputView.closing();
mComposing.setLength(0);
mPredicting = false;
mDeleteCount = 0;
@@ -548,7 +674,7 @@ public class LatinIME extends InputMethodService
updateCorrectionMode();
- mInputView.setProximityCorrectionEnabled(true);
+ inputView.setProximityCorrectionEnabled(true);
mPredictionOn = mPredictionOn && (mCorrectionMode > 0 || mShowSuggestions);
checkTutorial(attribute.privateImeOptions);
if (TRACE) Debug.startMethodTracing("/data/trace/latinime");
@@ -558,6 +684,8 @@ public class LatinIME extends InputMethodService
public void onFinishInput() {
super.onFinishInput();
+ LatinImeLogger.commit();
+
if (VOICE_INSTALLED && !mConfigurationChanging) {
if (mAfterVoiceInput) {
mVoiceInput.flushAllTextModificationCounters();
@@ -566,10 +694,12 @@ public class LatinIME extends InputMethodService
mVoiceInput.flushLogs();
mVoiceInput.cancel();
}
- if (mInputView != null) {
- mInputView.closing();
+ if (mKeyboardSwitcher.getInputView() != null) {
+ mKeyboardSwitcher.getInputView().closing();
}
if (mAutoDictionary != null) mAutoDictionary.flushPendingWrites();
+ // User Bigram is disabled for now
+ //if (mUserBigramDictionary != null) mUserBigramDictionary.flushPendingWrites();
}
@Override
@@ -605,15 +735,15 @@ public class LatinIME extends InputMethodService
mVoiceInput.setSelectionSpan(newSelEnd - newSelStart);
}
- mSuggestionShouldReplaceCurrentWord = false;
// If the current selection in the text view changes, we should
// clear whatever candidate text we have.
if ((((mComposing.length() > 0 && mPredicting) || mVoiceInputHighlighted)
&& (newSelStart != candidatesEnd
- || newSelEnd != candidatesEnd))) {
+ || newSelEnd != candidatesEnd)
+ && mLastSelectionStart != newSelStart)) {
mComposing.setLength(0);
mPredicting = false;
- updateSuggestions();
+ postUpdateSuggestions();
TextEntryState.reset();
InputConnection ic = getCurrentInputConnection();
if (ic != null) {
@@ -633,32 +763,29 @@ public class LatinIME extends InputMethodService
mJustAccepted = false;
postUpdateShiftKeyState();
- if (VOICE_INSTALLED) {
- if (mShowingVoiceSuggestions) {
- if (mImmediatelyAfterVoiceSuggestions) {
- mImmediatelyAfterVoiceSuggestions = false;
- } else {
- updateSuggestions();
- mShowingVoiceSuggestions = false;
- }
- }
- if (VoiceInput.ENABLE_WORD_CORRECTIONS) {
- // If we have alternatives for the current word, then show them.
- String word = EditingUtil.getWordAtCursor(
- getCurrentInputConnection(), getWordSeparators());
- if (word != null && mWordToSuggestions.containsKey(word.trim())) {
- mSuggestionShouldReplaceCurrentWord = true;
- final List<CharSequence> suggestions = mWordToSuggestions.get(word.trim());
+ // Make a note of the cursor position
+ mLastSelectionStart = newSelStart;
+ mLastSelectionEnd = newSelEnd;
- setSuggestions(suggestions, false, true, true);
- setCandidatesViewShown(true);
- }
+
+ // TODO: Uncomment this block when we enable re-editing feature
+ // If a word is selected
+ if (isPredictionOn() && mJustRevertedSeparator == null
+ && (candidatesStart == candidatesEnd || newSelStart != oldSelStart)
+ && (newSelStart < newSelEnd - 1 || (!mPredicting))
+ && !mVoiceInputHighlighted) {
+ if (isCursorTouchingWord() || mLastSelectionStart < mLastSelectionEnd) {
+ postUpdateOldSuggestions();
+ } else {
+ abortCorrection(false);
}
}
}
@Override
public void hideWindow() {
+ LatinImeLogger.commit();
+
if (TRACE) Debug.stopMethodTracing();
if (mOptionsDialog != null && mOptionsDialog.isShowing()) {
mOptionsDialog.dismiss();
@@ -675,13 +802,15 @@ public class LatinIME extends InputMethodService
mVoiceInput.cancel();
}
}
+ mWordToSuggestions.clear();
+ mWordHistory.clear();
super.hideWindow();
TextEntryState.endSession();
}
@Override
public void onDisplayCompletions(CompletionInfo[] completions) {
- if (false) {
+ if (DEBUG) {
Log.i("foo", "Received completions:");
for (int i=0; i<(completions != null ? completions.length : 0); i++) {
Log.i("foo", " #" + i + ": " + completions[i]);
@@ -699,7 +828,7 @@ public class LatinIME extends InputMethodService
CompletionInfo ci = completions[i];
if (ci != null) stringList.add(ci.getText());
}
- //CharSequence typedWord = mWord.getTypedWord();
+ // When in fullscreen mode, show completions generated by the application
setSuggestions(stringList, true, true, true);
mBestWord = null;
setCandidatesViewShown(isCandidateStripVisible() || mCompletionOn);
@@ -711,7 +840,8 @@ public class LatinIME extends InputMethodService
// TODO: Remove this if we support candidates with hard keyboard
if (onEvaluateInputViewShown()) {
// Show the candidates view only if input view is showing
- super.setCandidatesViewShown(shown && mInputView != null && mInputView.isShown());
+ super.setCandidatesViewShown(shown && mKeyboardSwitcher.getInputView() != null
+ && mKeyboardSwitcher.getInputView().isShown());
}
}
@@ -724,11 +854,24 @@ public class LatinIME extends InputMethodService
}
@Override
+ public boolean onEvaluateFullscreenMode() {
+ DisplayMetrics dm = getResources().getDisplayMetrics();
+ float displayHeight = dm.heightPixels;
+ // If the display is more than X inches high, don't go to fullscreen mode
+ float dimen = getResources().getDimension(R.dimen.max_height_for_fullscreen);
+ if (displayHeight > dimen) {
+ return false;
+ } else {
+ return super.onEvaluateFullscreenMode();
+ }
+ }
+
+ @Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_BACK:
- if (event.getRepeatCount() == 0 && mInputView != null) {
- if (mInputView.handleBack()) {
+ if (event.getRepeatCount() == 0 && mKeyboardSwitcher.getInputView() != null) {
+ if (mKeyboardSwitcher.getInputView().handleBack()) {
return true;
} else if (mTutorial != null) {
mTutorial.close();
@@ -760,8 +903,10 @@ public class LatinIME extends InputMethodService
if (mTutorial != null) {
return true;
}
+ LatinKeyboardView inputView = mKeyboardSwitcher.getInputView();
// Enable shift key and DPAD to do selections
- if (mInputView != null && mInputView.isShown() && mInputView.isShifted()) {
+ if (inputView != null && inputView.isShown()
+ && inputView.isShifted()) {
event = new KeyEvent(event.getDownTime(), event.getEventTime(),
event.getAction(), event.getKeyCode(), event.getRepeatCount(),
event.getDeviceId(), event.getScanCode(),
@@ -794,7 +939,8 @@ public class LatinIME extends InputMethodService
mKeyboardSwitcher = new KeyboardSwitcher(this, this);
}
mKeyboardSwitcher.setLanguageSwitcher(mLanguageSwitcher);
- if (mInputView != null) {
+ if (mKeyboardSwitcher.getInputView() != null
+ && mKeyboardSwitcher.getKeyboardMode() != KeyboardSwitcher.MODE_NONE) {
mKeyboardSwitcher.setVoiceMode(mEnableVoice && mEnableVoiceButton, mVoiceOnPrimary);
}
mKeyboardSwitcher.makeKeyboards(true);
@@ -809,7 +955,7 @@ public class LatinIME extends InputMethodService
}
mCommittedLength = mComposing.length();
TextEntryState.acceptedTyped(mComposing);
- checkAddToDictionary(mComposing, AutoDictionary.FREQUENCY_FOR_TYPED);
+ addToDictionaries(mComposing, AutoDictionary.FREQUENCY_FOR_TYPED);
}
updateSuggestions();
}
@@ -822,9 +968,10 @@ public class LatinIME extends InputMethodService
public void updateShiftKeyState(EditorInfo attr) {
InputConnection ic = getCurrentInputConnection();
- if (attr != null && mInputView != null && mKeyboardSwitcher.isAlphabetMode()
- && ic != null) {
- mInputView.setShifted(mCapsLock || getCursorCapsMode(ic, attr) != 0);
+ if (attr != null && mKeyboardSwitcher.getInputView() != null
+ && mKeyboardSwitcher.isAlphabetMode() && ic != null) {
+ mKeyboardSwitcher.getInputView().setShifted(
+ mCapsLock || getCursorCapsMode(ic, attr) != 0);
}
}
@@ -937,6 +1084,7 @@ public class LatinIME extends InputMethodService
case Keyboard.KEYCODE_DELETE:
handleBackspace();
mDeleteCount++;
+ LatinImeLogger.logOnDelete();
break;
case Keyboard.KEYCODE_SHIFT:
handleShift();
@@ -977,6 +1125,7 @@ public class LatinIME extends InputMethodService
if (primaryCode != KEYCODE_ENTER) {
mJustAddedAutoSpace = false;
}
+ LatinImeLogger.logOnInputChar((char)primaryCode);
if (isWordSeparator(primaryCode)) {
handleSeparator(primaryCode);
} else {
@@ -998,6 +1147,7 @@ public class LatinIME extends InputMethodService
}
InputConnection ic = getCurrentInputConnection();
if (ic == null) return;
+ abortCorrection(false);
ic.beginBatchEdit();
if (mPredicting) {
commitTyped(ic);
@@ -1022,6 +1172,8 @@ public class LatinIME extends InputMethodService
InputConnection ic = getCurrentInputConnection();
if (ic == null) return;
+ ic.beginBatchEdit();
+
if (mAfterVoiceInput) {
// Don't log delete if the user is pressing delete at
// the beginning of the text box (hence not deleting anything)
@@ -1054,6 +1206,7 @@ public class LatinIME extends InputMethodService
TextEntryState.backspace();
if (TextEntryState.getState() == TextEntryState.STATE_UNDO_COMMIT) {
revertLastWord(deleteChar);
+ ic.endBatchEdit();
return;
} else if (mEnteredText != null && sameAsTextBeforeCursor(ic, mEnteredText)) {
ic.deleteSurroundingText(mEnteredText.length(), 0);
@@ -1064,6 +1217,7 @@ public class LatinIME extends InputMethodService
}
}
mJustRevertedSeparator = null;
+ ic.endBatchEdit();
}
private void handleShift() {
@@ -1071,12 +1225,20 @@ public class LatinIME extends InputMethodService
if (mKeyboardSwitcher.isAlphabetMode()) {
// Alphabet keyboard
checkToggleCapsLock();
- mInputView.setShifted(mCapsLock || !mInputView.isShifted());
+ mKeyboardSwitcher.getInputView().setShifted(mCapsLock
+ || !mKeyboardSwitcher.getInputView().isShifted());
} else {
mKeyboardSwitcher.toggleShift();
}
}
+ private void abortCorrection(boolean force) {
+ if (force || TextEntryState.isCorrecting()) {
+ getCurrentInputConnection().finishComposingText();
+ setSuggestions(null, false, false, false);
+ }
+ }
+
private void handleCharacter(int primaryCode, int[] keyCodes) {
if (VOICE_INSTALLED && mVoiceInputHighlighted) {
commitVoiceInput();
@@ -1086,24 +1248,31 @@ public class LatinIME extends InputMethodService
// Assume input length is 1. This assumption fails for smiley face insertions.
mVoiceInput.incrementTextModificationInsertCount(1);
}
+ abortCorrection(false);
if (isAlphabet(primaryCode) && isPredictionOn() && !isCursorTouchingWord()) {
if (!mPredicting) {
mPredicting = true;
mComposing.setLength(0);
+ saveWordInHistory(mBestWord);
mWord.reset();
}
}
- if (mInputView.isShifted()) {
- // TODO: This doesn't work with ß, need to fix it in the next release.
+ if (mKeyboardSwitcher.getInputView().isShifted()) {
+ // TODO: This doesn't work with [beta], need to fix it in the next release.
if (keyCodes == null || keyCodes[0] < Character.MIN_CODE_POINT
|| keyCodes[0] > Character.MAX_CODE_POINT) {
return;
}
- primaryCode = new String(keyCodes, 0, 1).toUpperCase().charAt(0);
+ primaryCode = keyCodes[0];
+ if (mKeyboardSwitcher.isAlphabetMode()) {
+ primaryCode = Character.toUpperCase(primaryCode);
+ }
}
if (mPredicting) {
- if (mInputView.isShifted() && mComposing.length() == 0) {
+ if (mKeyboardSwitcher.getInputView().isShifted()
+ && mKeyboardSwitcher.isAlphabetMode()
+ && mComposing.length() == 0) {
mWord.setCapitalized(true);
}
mComposing.append((char) primaryCode);
@@ -1122,7 +1291,7 @@ public class LatinIME extends InputMethodService
sendKeyChar((char)primaryCode);
}
updateShiftKeyState(getCurrentInputEditorInfo());
- measureCps();
+ if (LatinIME.PERF_DEBUG) measureCps();
TextEntryState.typedCharacter((char) primaryCode, isWordSeparator(primaryCode));
}
@@ -1141,6 +1310,7 @@ public class LatinIME extends InputMethodService
InputConnection ic = getCurrentInputConnection();
if (ic != null) {
ic.beginBatchEdit();
+ abortCorrection(false);
}
if (mPredicting) {
// In certain languages where single quote is a separator, it's better
@@ -1151,8 +1321,7 @@ public class LatinIME extends InputMethodService
(mJustRevertedSeparator == null
|| mJustRevertedSeparator.length() == 0
|| mJustRevertedSeparator.charAt(0) != primaryCode)) {
- pickDefaultSuggestion();
- pickedDefault = true;
+ pickedDefault = pickDefaultSuggestion();
// Picked the suggestion by the space key. We consider this
// as "added an auto space".
if (primaryCode == KEYCODE_SPACE) {
@@ -1180,11 +1349,10 @@ public class LatinIME extends InputMethodService
&& primaryCode != KEYCODE_ENTER) {
swapPunctuationAndSpace();
} else if (isPredictionOn() && primaryCode == KEYCODE_SPACE) {
- //else if (TextEntryState.STATE_SPACE_AFTER_ACCEPTED) {
doubleSpace();
}
- if (pickedDefault && mBestWord != null) {
- TextEntryState.acceptedDefault(mWord.getTypedWord(), mBestWord);
+ if (pickedDefault) {
+ TextEntryState.backToAcceptedDefault(mWord.getTypedWord());
}
updateShiftKeyState(getCurrentInputEditorInfo());
if (ic != null) {
@@ -1198,12 +1366,24 @@ public class LatinIME extends InputMethodService
mVoiceInput.cancel();
}
requestHideSelf(0);
- mInputView.closing();
+ mKeyboardSwitcher.getInputView().closing();
TextEntryState.endSession();
}
+ private void saveWordInHistory(CharSequence result) {
+ if (mWord.size() <= 1) {
+ mWord.reset();
+ return;
+ }
+ // Make a copy of the CharSequence, since it is/could be a mutable CharSequence
+ final String resultCopy = result.toString();
+ TypedWordAlternatives entry = new TypedWordAlternatives(resultCopy,
+ new WordComposer(mWord));
+ mWordHistory.add(entry);
+ }
+
private void checkToggleCapsLock() {
- if (mInputView.getKeyboard().isShifted()) {
+ if (mKeyboardSwitcher.getInputView().getKeyboard().isShifted()) {
toggleCapsLock();
}
}
@@ -1211,7 +1391,8 @@ public class LatinIME extends InputMethodService
private void toggleCapsLock() {
mCapsLock = !mCapsLock;
if (mKeyboardSwitcher.isAlphabetMode()) {
- ((LatinKeyboard) mInputView.getKeyboard()).setShiftLocked(mCapsLock);
+ ((LatinKeyboard) mKeyboardSwitcher.getInputView().getKeyboard()).setShiftLocked(
+ mCapsLock);
}
}
@@ -1220,6 +1401,11 @@ public class LatinIME extends InputMethodService
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_UPDATE_SUGGESTIONS), 100);
}
+ private void postUpdateOldSuggestions() {
+ mHandler.removeMessages(MSG_UPDATE_OLD_SUGGESTIONS);
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_UPDATE_OLD_SUGGESTIONS), 300);
+ }
+
private boolean isPredictionOn() {
boolean predictionOn = mPredictionOn;
return predictionOn;
@@ -1239,8 +1425,8 @@ public class LatinIME extends InputMethodService
mHandler.post(new Runnable() {
public void run() {
mRecognizing = false;
- if (mInputView != null) {
- setInputView(mInputView);
+ if (mKeyboardSwitcher.getInputView() != null) {
+ setInputView(mKeyboardSwitcher.getInputView());
}
updateInputViewShown();
}});
@@ -1339,7 +1525,7 @@ public class LatinIME extends InputMethodService
Window window = mVoiceWarningDialog.getWindow();
WindowManager.LayoutParams lp = window.getAttributes();
- lp.token = mInputView.getWindowToken();
+ lp.token = mKeyboardSwitcher.getInputView().getWindowToken();
lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
window.setAttributes(lp);
window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
@@ -1375,7 +1561,8 @@ public class LatinIME extends InputMethodService
final List<CharSequence> nBest = new ArrayList<CharSequence>();
boolean capitalizeFirstWord = preferCapitalization()
- || (mKeyboardSwitcher.isAlphabetMode() && mInputView.isShifted());
+ || (mKeyboardSwitcher.isAlphabetMode()
+ && mKeyboardSwitcher.getInputView().isShifted());
for (String c : mVoiceResults.candidates) {
if (capitalizeFirstWord) {
c = Character.toUpperCase(c.charAt(0)) + c.substring(1, c.length());
@@ -1400,13 +1587,6 @@ public class LatinIME extends InputMethodService
if (ic != null) ic.endBatchEdit();
- // Show N-Best alternates, if there is more than one choice.
- if (nBest.size() > 1) {
- mImmediatelyAfterVoiceSuggestions = true;
- mShowingVoiceSuggestions = true;
- setSuggestions(nBest.subList(1, nBest.size()), false, true, true);
- setCandidatesViewShown(true);
- }
mVoiceInputHighlighted = true;
mWordToSuggestions.putAll(mVoiceResults.alternatives);
@@ -1431,9 +1611,8 @@ public class LatinIME extends InputMethodService
}
private void updateSuggestions() {
- mSuggestionShouldReplaceCurrentWord = false;
-
- ((LatinKeyboard) mInputView.getKeyboard()).setPreferredLetters(null);
+ LatinKeyboardView inputView = mKeyboardSwitcher.getInputView();
+ ((LatinKeyboard) inputView.getKeyboard()).setPreferredLetters(null);
// Check if we have a suggestion engine attached.
if ((mSuggest == null || !isPredictionOn()) && !mVoiceInputHighlighted) {
@@ -1444,24 +1623,56 @@ public class LatinIME extends InputMethodService
setNextSuggestions();
return;
}
+ showSuggestions(mWord);
+ }
+
+ private List<CharSequence> getTypedSuggestions(WordComposer word) {
+ List<CharSequence> stringList = mSuggest.getSuggestions(
+ mKeyboardSwitcher.getInputView(), word, false, null);
+ return stringList;
+ }
+
+ private void showCorrections(WordAlternatives alternatives) {
+ List<CharSequence> stringList = alternatives.getAlternatives();
+ ((LatinKeyboard) mKeyboardSwitcher.getInputView().getKeyboard()).setPreferredLetters(null);
+ showSuggestions(stringList, alternatives.getOriginalWord(), false, false);
+ }
+
+ private void showSuggestions(WordComposer word) {
+ //long startTime = System.currentTimeMillis(); // TIME MEASUREMENT!
+ // TODO Maybe need better way of retrieving previous word
+ CharSequence prevWord = EditingUtil.getPreviousWord(getCurrentInputConnection(),
+ mWordSeparators);
+ List<CharSequence> stringList = mSuggest.getSuggestions(
+ mKeyboardSwitcher.getInputView(), word, false, prevWord);
+ //long stopTime = System.currentTimeMillis(); // TIME MEASUREMENT!
+ //Log.d("LatinIME","Suggest Total Time - " + (stopTime - startTime));
- List<CharSequence> stringList = mSuggest.getSuggestions(mInputView, mWord, false);
int[] nextLettersFrequencies = mSuggest.getNextLettersFrequencies();
- ((LatinKeyboard) mInputView.getKeyboard()).setPreferredLetters(nextLettersFrequencies);
+ ((LatinKeyboard) mKeyboardSwitcher.getInputView().getKeyboard()).setPreferredLetters(
+ nextLettersFrequencies);
boolean correctionAvailable = !mInputTypeNoAutoCorrect && mSuggest.hasMinimalCorrection();
//|| mCorrectionMode == mSuggest.CORRECTION_FULL;
- CharSequence typedWord = mWord.getTypedWord();
+ CharSequence typedWord = word.getTypedWord();
// If we're in basic correct
boolean typedWordValid = mSuggest.isValidWord(typedWord) ||
- (preferCapitalization() && mSuggest.isValidWord(typedWord.toString().toLowerCase()));
- if (mCorrectionMode == Suggest.CORRECTION_FULL) {
+ (preferCapitalization()
+ && mSuggest.isValidWord(typedWord.toString().toLowerCase()));
+ if (mCorrectionMode == Suggest.CORRECTION_FULL
+ || mCorrectionMode == Suggest.CORRECTION_FULL_BIGRAM) {
correctionAvailable |= typedWordValid;
}
// Don't auto-correct words with multiple capital letter
- correctionAvailable &= !mWord.isMostlyCaps();
+ correctionAvailable &= !word.isMostlyCaps();
+ correctionAvailable &= !TextEntryState.isCorrecting();
+
+ showSuggestions(stringList, typedWord, typedWordValid, correctionAvailable);
+ }
+ private void showSuggestions(List<CharSequence> stringList, CharSequence typedWord,
+ boolean typedWordValid, boolean correctionAvailable) {
setSuggestions(stringList, false, typedWordValid, correctionAvailable);
if (stringList.size() > 0) {
if (correctionAvailable && !typedWordValid && stringList.size() > 1) {
@@ -1475,7 +1686,7 @@ public class LatinIME extends InputMethodService
setCandidatesViewShown(isCandidateStripVisible() || mCompletionOn);
}
- private void pickDefaultSuggestion() {
+ private boolean pickDefaultSuggestion() {
// Complete any pending candidate query first
if (mHandler.hasMessages(MSG_UPDATE_SUGGESTIONS)) {
mHandler.removeMessages(MSG_UPDATE_SUGGESTIONS);
@@ -1484,14 +1695,18 @@ public class LatinIME extends InputMethodService
if (mBestWord != null && mBestWord.length() > 0) {
TextEntryState.acceptedDefault(mWord.getTypedWord(), mBestWord);
mJustAccepted = true;
- pickSuggestion(mBestWord);
+ pickSuggestion(mBestWord, false);
// Add the word to the auto dictionary if it's not a known word
- checkAddToDictionary(mBestWord, AutoDictionary.FREQUENCY_FOR_TYPED);
+ addToDictionaries(mBestWord, AutoDictionary.FREQUENCY_FOR_TYPED);
+ return true;
+
}
+ return false;
}
public void pickSuggestionManually(int index, CharSequence suggestion) {
if (mAfterVoiceInput && mShowingVoiceSuggestions) mVoiceInput.logNBestChoose(index);
+ List<CharSequence> suggestions = mCandidateView.getSuggestions();
if (mAfterVoiceInput && !mShowingVoiceSuggestions) {
mVoiceInput.flushAllTextModificationCounters();
@@ -1499,6 +1714,7 @@ public class LatinIME extends InputMethodService
mVoiceInput.logTextModifiedByChooseSuggestion(suggestion.length());
}
+ final boolean correcting = TextEntryState.isCorrecting();
InputConnection ic = getCurrentInputConnection();
if (ic != null) {
ic.beginBatchEdit();
@@ -1521,7 +1737,12 @@ public class LatinIME extends InputMethodService
}
// If this is a punctuation, apply it through the normal key press
- if (suggestion.length() == 1 && isWordSeparator(suggestion.charAt(0))) {
+ if (suggestion.length() == 1 && (isWordSeparator(suggestion.charAt(0))
+ || isSuggestedPunctuation(suggestion.charAt(0)))) {
+ // Word separators are suggested before the user inputs something.
+ // So, LatinImeLogger logs "" as a user's input.
+ LatinImeLogger.logOnManualSuggestion(
+ "", suggestion.toString(), index, suggestions);
onKey(suggestion.charAt(0), null);
if (ic != null) {
ic.endBatchEdit();
@@ -1529,20 +1750,26 @@ public class LatinIME extends InputMethodService
return;
}
mJustAccepted = true;
- pickSuggestion(suggestion);
+ pickSuggestion(suggestion, correcting);
// Add the word to the auto dictionary if it's not a known word
if (index == 0) {
- checkAddToDictionary(suggestion, AutoDictionary.FREQUENCY_FOR_PICKED);
+ addToDictionaries(suggestion, AutoDictionary.FREQUENCY_FOR_PICKED);
+ } else {
+ addToBigramDictionary(suggestion, 1);
}
+ LatinImeLogger.logOnManualSuggestion(mComposing.toString(), suggestion.toString(),
+ index, suggestions);
TextEntryState.acceptedSuggestion(mComposing.toString(), suggestion);
// Follow it with a space
- if (mAutoSpace) {
+ if (mAutoSpace && !correcting) {
sendSpace();
mJustAddedAutoSpace = true;
}
+
// Fool the state watcher so that a subsequent backspace will not do a revert
TextEntryState.typedCharacter((char) KEYCODE_SPACE, true);
- if (index == 0 && mCorrectionMode > 0 && !mSuggest.isValidWord(suggestion)) {
+ if (index == 0 && mCorrectionMode > 0 && !mSuggest.isValidWord(suggestion)
+ && !mSuggest.isValidWord(suggestion.toString().toLowerCase())) {
mCandidateView.showAddToDictionaryHint(suggestion);
}
if (ic != null) {
@@ -1550,43 +1777,221 @@ public class LatinIME extends InputMethodService
}
}
- private void pickSuggestion(CharSequence suggestion) {
+ private void rememberReplacedWord(CharSequence suggestion) {
+ if (mShowingVoiceSuggestions) {
+ // Retain the replaced word in the alternatives array.
+ EditingUtil.Range range = new EditingUtil.Range();
+ String wordToBeReplaced = EditingUtil.getWordAtCursor(getCurrentInputConnection(),
+ mWordSeparators, range);
+ if (!mWordToSuggestions.containsKey(wordToBeReplaced)) {
+ wordToBeReplaced = wordToBeReplaced.toLowerCase();
+ }
+ if (mWordToSuggestions.containsKey(wordToBeReplaced)) {
+ List<CharSequence> suggestions = mWordToSuggestions.get(wordToBeReplaced);
+ if (suggestions.contains(suggestion)) {
+ suggestions.remove(suggestion);
+ }
+ suggestions.add(wordToBeReplaced);
+ mWordToSuggestions.remove(wordToBeReplaced);
+ mWordToSuggestions.put(suggestion.toString(), suggestions);
+ }
+ }
+ // TODO: implement rememberReplacedWord for typed words
+ }
+
+ /**
+ * Commits the chosen word to the text field and saves it for later
+ * retrieval.
+ * @param suggestion the suggestion picked by the user to be committed to
+ * the text field
+ * @param correcting whether this is due to a correction of an existing
+ * word.
+ */
+ private void pickSuggestion(CharSequence suggestion, boolean correcting) {
+ LatinKeyboardView inputView = mKeyboardSwitcher.getInputView();
if (mCapsLock) {
suggestion = suggestion.toString().toUpperCase();
} else if (preferCapitalization()
- || (mKeyboardSwitcher.isAlphabetMode() && mInputView.isShifted())) {
+ || (mKeyboardSwitcher.isAlphabetMode()
+ && inputView.isShifted())) {
suggestion = suggestion.toString().toUpperCase().charAt(0)
+ suggestion.subSequence(1, suggestion.length()).toString();
}
InputConnection ic = getCurrentInputConnection();
if (ic != null) {
- if (mSuggestionShouldReplaceCurrentWord) {
+ rememberReplacedWord(suggestion);
+ // If text is in correction mode and we're not using composing
+ // text to underline, then the word at the cursor position needs
+ // to be removed before committing the correction
+ if (correcting && !MODIFY_TEXT_FOR_CORRECTION) {
+ if (mLastSelectionStart < mLastSelectionEnd) {
+ ic.setSelection(mLastSelectionStart, mLastSelectionStart);
+ }
EditingUtil.deleteWordAtCursor(ic, getWordSeparators());
}
- if (!VoiceInput.DELETE_SYMBOL.equals(suggestion)) {
- ic.commitText(suggestion, 1);
- }
+
+ ic.commitText(suggestion, 1);
}
+ saveWordInHistory(suggestion);
mPredicting = false;
mCommittedLength = suggestion.length();
- ((LatinKeyboard) mInputView.getKeyboard()).setPreferredLetters(null);
+ ((LatinKeyboard) inputView.getKeyboard()).setPreferredLetters(null);
setNextSuggestions();
updateShiftKeyState(getCurrentInputEditorInfo());
}
+ private void setOldSuggestions() {
+ // TODO: Inefficient to check if touching word and then get the touching word. Do it
+ // in one go.
+ mShowingVoiceSuggestions = false;
+ InputConnection ic = getCurrentInputConnection();
+ if (ic == null) return;
+ ic.beginBatchEdit();
+ // If there is a selection, then undo the selection first. Unfortunately this causes
+ // a flicker. TODO: Add getSelectionText() to InputConnection API.
+ if (mLastSelectionStart < mLastSelectionEnd) {
+ ic.setSelection(mLastSelectionStart, mLastSelectionStart);
+ }
+ if (!mPredicting && isCursorTouchingWord()) {
+ EditingUtil.Range range = new EditingUtil.Range();
+ CharSequence touching = EditingUtil.getWordAtCursor(getCurrentInputConnection(),
+ mWordSeparators, range);
+ if (touching != null && touching.length() > 1) {
+ if (mWordSeparators.indexOf(touching.charAt(touching.length() - 1)) > 0) {
+ touching = touching.toString().substring(0, touching.length() - 1);
+ }
+
+ // Search for result in spoken word alternatives
+ // TODO: possibly combine the spoken suggestions with the typed suggestions.
+ String selectedWord = touching.toString().trim();
+ if (!mWordToSuggestions.containsKey(selectedWord)){
+ selectedWord = selectedWord.toLowerCase();
+ }
+ if (mWordToSuggestions.containsKey(selectedWord)){
+ mShowingVoiceSuggestions = true;
+ underlineWord(touching, range.charsBefore, range.charsAfter);
+ List<CharSequence> suggestions = mWordToSuggestions.get(selectedWord);
+ // If the first letter of touching is capitalized, make all the suggestions
+ // start with a capital letter.
+ if (Character.isUpperCase((char) touching.charAt(0))) {
+ for (int i=0; i< suggestions.size(); i++) {
+ String origSugg = (String) suggestions.get(i);
+ String capsSugg = origSugg.toUpperCase().charAt(0)
+ + origSugg.subSequence(1, origSugg.length()).toString();
+ suggestions.set(i,capsSugg);
+ }
+ }
+ setSuggestions(suggestions, false, true, true);
+ setCandidatesViewShown(true);
+ TextEntryState.selectedForCorrection();
+ ic.endBatchEdit();
+ return;
+ }
+ // If we didn't find a match, search for result in word history
+ WordComposer foundWord = null;
+ WordAlternatives alternatives = null;
+ for (WordAlternatives entry : mWordHistory) {
+ if (TextUtils.equals(entry.getChosenWord(), touching)) {
+ if (entry instanceof TypedWordAlternatives) {
+ foundWord = ((TypedWordAlternatives)entry).word;
+ }
+ alternatives = entry;
+ break;
+ }
+ }
+ // If we didn't find a match, at least suggest completions
+ if (foundWord == null && mSuggest.isValidWord(touching)) {
+ foundWord = new WordComposer();
+ for (int i = 0; i < touching.length(); i++) {
+ foundWord.add(touching.charAt(i), new int[] { touching.charAt(i) });
+ }
+ }
+ // Found a match, show suggestions
+ if (foundWord != null || alternatives != null) {
+ underlineWord(touching, range.charsBefore, range.charsAfter);
+ TextEntryState.selectedForCorrection();
+ if (alternatives == null) alternatives = new TypedWordAlternatives(touching,
+ foundWord);
+ showCorrections(alternatives);
+ if (foundWord != null) {
+ mWord = new WordComposer(foundWord);
+ } else {
+ mWord.reset();
+ }
+ // Revert the selection
+ if (mLastSelectionStart < mLastSelectionEnd) {
+ ic.setSelection(mLastSelectionStart, mLastSelectionEnd);
+ }
+ ic.endBatchEdit();
+ return;
+ }
+ abortCorrection(true);
+ } else {
+ abortCorrection(true);
+ setNextSuggestions();
+ }
+ } else {
+ abortCorrection(true);
+ }
+ // Revert the selection
+ if (mLastSelectionStart < mLastSelectionEnd) {
+ ic.setSelection(mLastSelectionStart, mLastSelectionEnd);
+ }
+ ic.endBatchEdit();
+ }
+
private void setNextSuggestions() {
setSuggestions(mSuggestPuncList, false, false, false);
}
- private void checkAddToDictionary(CharSequence suggestion, int frequencyDelta) {
+ private void underlineWord(CharSequence word, int left, int right) {
+ InputConnection ic = getCurrentInputConnection();
+ if (ic == null) return;
+ if (MODIFY_TEXT_FOR_CORRECTION) {
+ ic.finishComposingText();
+ ic.deleteSurroundingText(left, right);
+ ic.setComposingText(word, 1);
+ }
+ ic.setSelection(mLastSelectionStart, mLastSelectionStart);
+ }
+
+ private void addToDictionaries(CharSequence suggestion, int frequencyDelta) {
+ checkAddToDictionary(suggestion, frequencyDelta, false);
+ }
+
+ private void addToBigramDictionary(CharSequence suggestion, int frequencyDelta) {
+ checkAddToDictionary(suggestion, frequencyDelta, true);
+ }
+
+ /**
+ * Adds to the UserBigramDictionary and/or AutoDictionary
+ * @param addToBigramDictionary true if it should be added to bigram dictionary if possible
+ */
+ private void checkAddToDictionary(CharSequence suggestion, int frequencyDelta,
+ boolean addToBigramDictionary) {
+ if (suggestion == null || suggestion.length() < 1) return;
// 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)) return;
- if (mAutoDictionary.isValidWord(suggestion)
- || (!mSuggest.isValidWord(suggestion.toString())
+ if (!(mCorrectionMode == Suggest.CORRECTION_FULL
+ || mCorrectionMode == Suggest.CORRECTION_FULL_BIGRAM)) {
+ return;
+ }
+ if (suggestion != null) {
+ if (!addToBigramDictionary && mAutoDictionary.isValidWord(suggestion)
+ || (!mSuggest.isValidWord(suggestion.toString())
&& !mSuggest.isValidWord(suggestion.toString().toLowerCase()))) {
- mAutoDictionary.addWord(suggestion.toString(), frequencyDelta);
+ mAutoDictionary.addWord(suggestion.toString(), frequencyDelta);
+ }
+ // User Bigram is disabled for now
+ /*
+ if (mUserBigramDictionary != null) {
+ CharSequence prevWord = EditingUtil.getPreviousWord(getCurrentInputConnection());
+ if (!TextUtils.isEmpty(prevWord)) {
+ mUserBigramDictionary.addBigrams(prevWord.toString(), suggestion.toString(), 1);
+ }
+ }
+ */
}
}
@@ -1616,7 +2021,6 @@ public class LatinIME extends InputMethodService
if (!mPredicting && length > 0) {
final InputConnection ic = getCurrentInputConnection();
mPredicting = true;
- ic.beginBatchEdit();
mJustRevertedSeparator = ic.getTextBeforeCursor(1, 0);
if (deleteChar) ic.deleteSurroundingText(1, 0);
int toDelete = mCommittedLength;
@@ -1628,7 +2032,6 @@ public class LatinIME extends InputMethodService
ic.deleteSurroundingText(toDelete, 0);
ic.setComposingText(mComposing, 1);
TextEntryState.backspace();
- ic.endBatchEdit();
postUpdateSuggestions();
} else {
sendDownUpKeyEvents(KeyEvent.KEYCODE_DEL);
@@ -1645,7 +2048,7 @@ public class LatinIME extends InputMethodService
return separators.contains(String.valueOf((char)code));
}
- public boolean isSentenceSeparator(int code) {
+ private boolean isSentenceSeparator(int code) {
return mSentenceSeparators.contains(String.valueOf((char)code));
}
@@ -1669,7 +2072,7 @@ public class LatinIME extends InputMethodService
ClipboardManager cm = ((ClipboardManager)getSystemService(CLIPBOARD_SERVICE));
CharSequence text = cm.getText();
if (!TextUtils.isEmpty(text)) {
- mInputView.startPlaying(text.toString());
+ mKeyboardSwitcher.getInputView().startPlaying(text.toString());
}
}
}
@@ -1720,7 +2123,7 @@ public class LatinIME extends InputMethodService
public void onRelease(int primaryCode) {
// Reset any drag flags in the keyboard
- ((LatinKeyboard) mInputView.getKeyboard()).keyReleased();
+ ((LatinKeyboard) mKeyboardSwitcher.getInputView().getKeyboard()).keyReleased();
//vibrate();
}
@@ -1772,7 +2175,7 @@ public class LatinIME extends InputMethodService
// if mAudioManager is null, we don't have the ringer state yet
// mAudioManager will be set by updateRingerMode
if (mAudioManager == null) {
- if (mInputView != null) {
+ if (mKeyboardSwitcher.getInputView() != null) {
updateRingerMode();
}
}
@@ -1799,8 +2202,9 @@ public class LatinIME extends InputMethodService
if (!mVibrateOn) {
return;
}
- if (mInputView != null) {
- mInputView.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP,
+ if (mKeyboardSwitcher.getInputView() != null) {
+ mKeyboardSwitcher.getInputView().performHapticFeedback(
+ HapticFeedbackConstants.KEYBOARD_TAP,
HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
}
}
@@ -1842,6 +2246,8 @@ public class LatinIME extends InputMethodService
mCorrectionMode = (mAutoCorrectOn && mAutoCorrectEnabled)
? Suggest.CORRECTION_FULL
: (mAutoCorrectOn ? Suggest.CORRECTION_BASIC : Suggest.CORRECTION_NONE);
+ mCorrectionMode = (mBigramSuggestionEnabled && mAutoCorrectOn && mAutoCorrectEnabled)
+ ? Suggest.CORRECTION_FULL_BIGRAM : mCorrectionMode;
if (mSuggest != null) {
mSuggest.setCorrectionMode(mCorrectionMode);
}
@@ -1858,7 +2264,7 @@ public class LatinIME extends InputMethodService
launchSettings(LatinIMESettings.class);
}
- protected void launchSettings(Class settingsClass) {
+ protected void launchSettings(Class<LatinIMESettings> settingsClass) {
handleClose();
Intent intent = new Intent();
intent.setClass(LatinIME.this, settingsClass);
@@ -1908,6 +2314,7 @@ public class LatinIME extends InputMethodService
}
mAutoCorrectEnabled = sp.getBoolean(PREF_AUTO_COMPLETE,
mResources.getBoolean(R.bool.enable_autocorrect)) & mShowSuggestions;
+ mBigramSuggestionEnabled = sp.getBoolean(PREF_BIGRAM_SUGGESTIONS, true) & mShowSuggestions;
updateCorrectionMode();
updateAutoTextEnabled(mResources.getConfiguration().locale);
mLanguageSwitcher.loadLocales(sp);
@@ -1915,14 +2322,18 @@ public class LatinIME extends InputMethodService
private void initSuggestPuncList() {
mSuggestPuncList = new ArrayList<CharSequence>();
- String suggestPuncs = mResources.getString(R.string.suggested_punctuations);
- if (suggestPuncs != null) {
- for (int i = 0; i < suggestPuncs.length(); i++) {
- mSuggestPuncList.add(suggestPuncs.subSequence(i, i + 1));
+ mSuggestPuncs = mResources.getString(R.string.suggested_punctuations);
+ if (mSuggestPuncs != null) {
+ for (int i = 0; i < mSuggestPuncs.length(); i++) {
+ mSuggestPuncList.add(mSuggestPuncs.subSequence(i, i + 1));
}
}
}
+ private boolean isSuggestedPunctuation(int code) {
+ return mSuggestPuncs.contains(String.valueOf((char)code));
+ }
+
private void showOptionsMenu() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setCancelable(true);
@@ -1951,7 +2362,7 @@ public class LatinIME extends InputMethodService
mOptionsDialog = builder.create();
Window window = mOptionsDialog.getWindow();
WindowManager.LayoutParams lp = window.getAttributes();
- lp.token = mInputView.getWindowToken();
+ lp.token = mKeyboardSwitcher.getInputView().getWindowToken();
lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
window.setAttributes(lp);
window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
@@ -1961,7 +2372,8 @@ public class LatinIME extends InputMethodService
private void changeKeyboardMode() {
mKeyboardSwitcher.toggleSymbols();
if (mCapsLock && mKeyboardSwitcher.isAlphabetMode()) {
- ((LatinKeyboard) mInputView.getKeyboard()).setShiftLocked(mCapsLock);
+ ((LatinKeyboard) mKeyboardSwitcher.getInputView().getKeyboard()).setShiftLocked(
+ mCapsLock);
}
updateShiftKeyState(getCurrentInputEditorInfo());
@@ -1995,15 +2407,12 @@ public class LatinIME extends InputMethodService
// Characters per second measurement
- private static final boolean PERF_DEBUG = false;
private long mLastCpsTime;
private static final int CPS_BUFFER_SIZE = 16;
private long[] mCpsIntervals = new long[CPS_BUFFER_SIZE];
private int mCpsIndex;
- private boolean mInputTypeNoAutoCorrect;
private void measureCps() {
- if (!LatinIME.PERF_DEBUG) return;
long now = System.currentTimeMillis();
if (mLastCpsTime == 0) mLastCpsTime = now - 100; // Initial
mCpsIntervals[mCpsIndex] = now - mLastCpsTime;
diff --git a/java/src/com/android/inputmethod/latin/LatinIMESettings.java b/java/src/com/android/inputmethod/latin/LatinIMESettings.java
index 21b967420..806ef00af 100644
--- a/java/src/com/android/inputmethod/latin/LatinIMESettings.java
+++ b/java/src/com/android/inputmethod/latin/LatinIMESettings.java
@@ -24,13 +24,13 @@ import android.app.Dialog;
import android.app.backup.BackupManager;
import android.content.DialogInterface;
import android.content.SharedPreferences;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.ListPreference;
-import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceGroup;
-import android.preference.Preference.OnPreferenceClickListener;
import android.speech.SpeechRecognizer;
import android.text.AutoText;
import android.util.Log;
@@ -43,11 +43,9 @@ public class LatinIMESettings extends PreferenceActivity
DialogInterface.OnDismissListener {
private static final String QUICK_FIXES_KEY = "quick_fixes";
- private static final String SHOW_SUGGESTIONS_KEY = "show_suggestions";
private static final String PREDICTION_SETTINGS_KEY = "prediction_settings";
private static final String VOICE_SETTINGS_KEY = "voice_mode";
- private static final String VOICE_ON_PRIMARY_KEY = "voice_on_main";
- private static final String VOICE_SERVER_KEY = "voice_server_url";
+ private static final String DEBUG_MODE_KEY = "debug_mode";
private static final String TAG = "LatinIMESettings";
@@ -55,7 +53,7 @@ public class LatinIMESettings extends PreferenceActivity
private static final int VOICE_INPUT_CONFIRM_DIALOG = 0;
private CheckBoxPreference mQuickFixes;
- private CheckBoxPreference mShowSuggestions;
+ private CheckBoxPreference mDebugMode;
private ListPreference mVoicePreference;
private boolean mVoiceOn;
@@ -69,7 +67,6 @@ public class LatinIMESettings extends PreferenceActivity
super.onCreate(icicle);
addPreferencesFromResource(R.xml.prefs);
mQuickFixes = (CheckBoxPreference) findPreference(QUICK_FIXES_KEY);
- mShowSuggestions = (CheckBoxPreference) findPreference(SHOW_SUGGESTIONS_KEY);
mVoicePreference = (ListPreference) findPreference(VOICE_SETTINGS_KEY);
SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
prefs.registerOnSharedPreferenceChangeListener(this);
@@ -77,6 +74,9 @@ public class LatinIMESettings extends PreferenceActivity
mVoiceModeOff = getString(R.string.voice_mode_off);
mVoiceOn = !(prefs.getString(VOICE_SETTINGS_KEY, mVoiceModeOff).equals(mVoiceModeOff));
mLogger = VoiceInputLogger.getLogger(this);
+
+ mDebugMode = (CheckBoxPreference) findPreference(DEBUG_MODE_KEY);
+ updateDebugMode(mDebugMode.isChecked());
}
@Override
@@ -110,11 +110,35 @@ public class LatinIMESettings extends PreferenceActivity
.equals(mVoiceModeOff)) {
showVoiceConfirmation();
}
+ } else if (key.equals(DEBUG_MODE_KEY)) {
+ updateDebugMode(prefs.getBoolean(DEBUG_MODE_KEY, false));
}
mVoiceOn = !(prefs.getString(VOICE_SETTINGS_KEY, mVoiceModeOff).equals(mVoiceModeOff));
updateVoiceModeSummary();
}
+ private void updateDebugMode(boolean isDebugMode) {
+ if (mDebugMode == null) {
+ return;
+ }
+ String version = "";
+ try {
+ PackageInfo info = getPackageManager().getPackageInfo(getPackageName(), 0);
+ version = "Version " + info.versionName;
+ } catch (NameNotFoundException e) {
+ Log.e(TAG, "Could not find version info.");
+ }
+ if (!isDebugMode) {
+ mDebugMode.setEnabled(false);
+ mDebugMode.setTitle(version);
+ mDebugMode.setSummary("");
+ } else {
+ mDebugMode.setEnabled(true);
+ mDebugMode.setTitle(getResources().getString(R.string.prefs_debug_mode));
+ mDebugMode.setSummary(version);
+ }
+ }
+
private void showVoiceConfirmation() {
mOkClicked = false;
showDialog(VOICE_INPUT_CONFIRM_DIALOG);
diff --git a/java/src/com/android/inputmethod/latin/LatinIMEUtil.java b/java/src/com/android/inputmethod/latin/LatinIMEUtil.java
new file mode 100644
index 000000000..838b4fe10
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/LatinIMEUtil.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2010 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.os.AsyncTask;
+import android.text.format.DateUtils;
+import android.util.Log;
+
+public class LatinIMEUtil {
+
+ /**
+ * Cancel an {@link AsyncTask}.
+ *
+ * @param mayInterruptIfRunning <tt>true</tt> if the thread executing this
+ * task should be interrupted; otherwise, in-progress tasks are allowed
+ * to complete.
+ */
+ public static void cancelTask(AsyncTask<?, ?, ?> task, boolean mayInterruptIfRunning) {
+ if (task != null && task.getStatus() != AsyncTask.Status.FINISHED) {
+ task.cancel(mayInterruptIfRunning);
+ }
+ }
+
+ public static class GCUtils {
+ private static final String TAG = "GCUtils";
+ 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 (LatinImeLogger.sDBG) {
+ Log.d(TAG, "Encountered Exception or Error. Try GC.");
+ }
+ 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(TAG, "Sleep was interrupted.");
+ LatinImeLogger.logOnException(metaData, t);
+ return false;
+ }
+ }
+ }
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/LatinImeLogger.java b/java/src/com/android/inputmethod/latin/LatinImeLogger.java
new file mode 100644
index 000000000..19eead0a0
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/LatinImeLogger.java
@@ -0,0 +1,847 @@
+/*
+ * Copyright (C) 2010 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.Dictionary.DataType;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.AsyncTask;
+import android.os.DropBoxManager;
+import android.preference.PreferenceManager;
+import android.text.TextUtils;
+import android.text.format.DateUtils;
+import android.util.Log;
+import android.util.Pair;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+
+public class LatinImeLogger implements SharedPreferences.OnSharedPreferenceChangeListener {
+ private static final String TAG = "LatinIMELogs";
+ public static boolean sDBG = false;
+ private static boolean sPRINTLOGGING = false;
+ // SUPPRESS_EXCEPTION should be true when released to public.
+ private static final boolean SUPPRESS_EXCEPTION = true;
+ // DEFAULT_LOG_ENABLED should be false when released to public.
+ private static final boolean DEFAULT_LOG_ENABLED = false;
+
+ private static final long MINIMUMSENDINTERVAL = 300 * DateUtils.SECOND_IN_MILLIS; // 300 sec
+ private static final long MINIMUMCOUNTINTERVAL = 20 * DateUtils.SECOND_IN_MILLIS; // 20 sec
+ private static final long MINIMUMSENDSIZE = 40;
+ private static final char SEPARATER = ';';
+ private static final char NULL_CHAR = '\uFFFC';
+ private static final int EXCEPTION_MAX_LENGTH = 400;
+
+ // ID_MANUALSUGGESTION has been replaced by ID_MANUALSUGGESTION_WITH_DATATYPE
+ // private static final int ID_MANUALSUGGESTION = 0;
+ private static final int ID_AUTOSUGGESTIONCANCELLED = 1;
+ private static final int ID_AUTOSUGGESTION = 2;
+ private static final int ID_INPUT_COUNT = 3;
+ private static final int ID_DELETE_COUNT = 4;
+ private static final int ID_WORD_COUNT = 5;
+ private static final int ID_ACTUAL_CHAR_COUNT = 6;
+ private static final int ID_THEME_ID = 7;
+ private static final int ID_SETTING_AUTO_COMPLETE = 8;
+ private static final int ID_VERSION = 9;
+ private static final int ID_EXCEPTION = 10;
+ private static final int ID_MANUALSUGGESTIONCOUNT = 11;
+ private static final int ID_AUTOSUGGESTIONCANCELLEDCOUNT = 12;
+ private static final int ID_AUTOSUGGESTIONCOUNT = 13;
+ private static final int ID_LANGUAGES = 14;
+ private static final int ID_MANUALSUGGESTION_WITH_DATATYPE = 15;
+
+ private static final String PREF_ENABLE_LOG = "enable_logging";
+ private static final String PREF_DEBUG_MODE = "debug_mode";
+ private static final String PREF_AUTO_COMPLETE = "auto_complete";
+
+ public static boolean sLogEnabled = true;
+ /* package */ static LatinImeLogger sLatinImeLogger = new LatinImeLogger();
+ // Store the last auto suggested word.
+ // This is required for a cancellation log of auto suggestion of that word.
+ /* package */ static String sLastAutoSuggestBefore;
+ /* package */ static String sLastAutoSuggestAfter;
+ /* package */ static String sLastAutoSuggestSeparator;
+ // This value holds MAIN, USER, AUTO, etc...
+ private static int sLastAutoSuggestDicTypeId;
+ // This value holds 0 (= unigram), 1 (= bigram) etc...
+ private static int sLastAutoSuggestDataType;
+ private static HashMap<String, Pair<Integer, Integer>> sSuggestDicMap
+ = new HashMap<String, Pair<Integer, Integer>>();
+ private static String[] sPreviousWords;
+ private static DebugKeyEnabler sDebugKeyEnabler = new DebugKeyEnabler();
+
+ private ArrayList<LogEntry> mLogBuffer = null;
+ private ArrayList<LogEntry> mPrivacyLogBuffer = null;
+ /* package */ RingCharBuffer mRingCharBuffer = null;
+
+ private Context mContext = null;
+ private DropBoxManager mDropBox = null;
+ private AddTextToDropBoxTask mAddTextToDropBoxTask;
+ private long mLastTimeActive;
+ private long mLastTimeSend;
+ private long mLastTimeCountEntry;
+
+ private String mThemeId;
+ private String mSelectedLanguages;
+ private String mCurrentLanguage;
+ private int mDeleteCount;
+ private int mInputCount;
+ private int mWordCount;
+ private int[] mAutoSuggestCountPerDic = new int[Suggest.DIC_TYPE_LAST_ID + 1];
+ private int[] mManualSuggestCountPerDic = new int[Suggest.DIC_TYPE_LAST_ID + 1];
+ private int[] mAutoCancelledCountPerDic = new int[Suggest.DIC_TYPE_LAST_ID + 1];
+ private int mActualCharCount;
+
+ private static class LogEntry implements Comparable<LogEntry> {
+ public final int mTag;
+ public final String[] mData;
+ public long mTime;
+
+ public LogEntry (long time, int tag, String[] data) {
+ mTag = tag;
+ mTime = time;
+ mData = data;
+ }
+
+ public int compareTo(LogEntry log2) {
+ if (mData.length == 0 && log2.mData.length == 0) {
+ return 0;
+ } else if (mData.length == 0) {
+ return 1;
+ } else if (log2.mData.length == 0) {
+ return -1;
+ }
+ return log2.mData[0].compareTo(mData[0]);
+ }
+ }
+
+ private class AddTextToDropBoxTask extends AsyncTask<Void, Void, Void> {
+ private final DropBoxManager mDropBox;
+ private final long mTime;
+ private final String mData;
+ public AddTextToDropBoxTask(DropBoxManager db, long time, String data) {
+ mDropBox = db;
+ mTime = time;
+ mData = data;
+ }
+ @Override
+ protected Void doInBackground(Void... params) {
+ if (sPRINTLOGGING) {
+ Log.d(TAG, "Commit log: " + mData);
+ }
+ mDropBox.addText(TAG, mData);
+ return null;
+ }
+ @Override
+ protected void onPostExecute(Void v) {
+ mLastTimeSend = mTime;
+ }
+ }
+
+ private void initInternal(Context context) {
+ mContext = context;
+ mDropBox = (DropBoxManager) mContext.getSystemService(Context.DROPBOX_SERVICE);
+ mLastTimeSend = System.currentTimeMillis();
+ mLastTimeActive = mLastTimeSend;
+ mLastTimeCountEntry = mLastTimeSend;
+ mDeleteCount = 0;
+ mInputCount = 0;
+ mWordCount = 0;
+ mActualCharCount = 0;
+ Arrays.fill(mAutoSuggestCountPerDic, 0);
+ Arrays.fill(mManualSuggestCountPerDic, 0);
+ Arrays.fill(mAutoCancelledCountPerDic, 0);
+ mLogBuffer = new ArrayList<LogEntry>();
+ mPrivacyLogBuffer = new ArrayList<LogEntry>();
+ mRingCharBuffer = new RingCharBuffer(context);
+ final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ sLogEnabled = prefs.getBoolean(PREF_ENABLE_LOG, DEFAULT_LOG_ENABLED);
+ mThemeId = prefs.getString(KeyboardSwitcher.PREF_KEYBOARD_LAYOUT,
+ KeyboardSwitcher.DEFAULT_LAYOUT_ID);
+ mSelectedLanguages = prefs.getString(LatinIME.PREF_SELECTED_LANGUAGES, "");
+ mCurrentLanguage = prefs.getString(LatinIME.PREF_INPUT_LANGUAGE, "");
+ sPRINTLOGGING = prefs.getBoolean(PREF_DEBUG_MODE, sPRINTLOGGING);
+ sDBG = sPRINTLOGGING;
+ prefs.registerOnSharedPreferenceChangeListener(this);
+ }
+
+ /**
+ * Clear all logged data
+ */
+ private void reset() {
+ mDeleteCount = 0;
+ mInputCount = 0;
+ mWordCount = 0;
+ mActualCharCount = 0;
+ Arrays.fill(mAutoSuggestCountPerDic, 0);
+ Arrays.fill(mManualSuggestCountPerDic, 0);
+ Arrays.fill(mAutoCancelledCountPerDic, 0);
+ mLogBuffer.clear();
+ mPrivacyLogBuffer.clear();
+ mRingCharBuffer.reset();
+ }
+
+ public void destroy() {
+ LatinIMEUtil.cancelTask(mAddTextToDropBoxTask, false);
+ }
+
+ /**
+ * Check if the input string is safe as an entry or not.
+ */
+ private static boolean checkStringDataSafe(String s) {
+ if (sDBG) {
+ Log.d(TAG, "Check String safety: " + s);
+ }
+ for (int i = 0; i < s.length(); ++i) {
+ if (Character.isDigit(s.charAt(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private void addCountEntry(long time) {
+ if (sPRINTLOGGING) {
+ Log.d(TAG, "Log counts. (4)");
+ }
+ mLogBuffer.add(new LogEntry (time, ID_DELETE_COUNT,
+ new String[] {String.valueOf(mDeleteCount)}));
+ mLogBuffer.add(new LogEntry (time, ID_INPUT_COUNT,
+ new String[] {String.valueOf(mInputCount)}));
+ mLogBuffer.add(new LogEntry (time, ID_WORD_COUNT,
+ new String[] {String.valueOf(mWordCount)}));
+ mLogBuffer.add(new LogEntry (time, ID_ACTUAL_CHAR_COUNT,
+ new String[] {String.valueOf(mActualCharCount)}));
+ mDeleteCount = 0;
+ mInputCount = 0;
+ mWordCount = 0;
+ mActualCharCount = 0;
+ mLastTimeCountEntry = time;
+ }
+
+ private void addSuggestionCountEntry(long time) {
+ if (sPRINTLOGGING) {
+ Log.d(TAG, "log suggest counts. (1)");
+ }
+ String[] s = new String[mAutoSuggestCountPerDic.length];
+ for (int i = 0; i < s.length; ++i) {
+ s[i] = String.valueOf(mAutoSuggestCountPerDic[i]);
+ }
+ mLogBuffer.add(new LogEntry(time, ID_AUTOSUGGESTIONCOUNT, s));
+
+ s = new String[mAutoCancelledCountPerDic.length];
+ for (int i = 0; i < s.length; ++i) {
+ s[i] = String.valueOf(mAutoCancelledCountPerDic[i]);
+ }
+ mLogBuffer.add(new LogEntry(time, ID_AUTOSUGGESTIONCANCELLEDCOUNT, s));
+
+ s = new String[mManualSuggestCountPerDic.length];
+ for (int i = 0; i < s.length; ++i) {
+ s[i] = String.valueOf(mManualSuggestCountPerDic[i]);
+ }
+ mLogBuffer.add(new LogEntry(time, ID_MANUALSUGGESTIONCOUNT, s));
+
+ Arrays.fill(mAutoSuggestCountPerDic, 0);
+ Arrays.fill(mManualSuggestCountPerDic, 0);
+ Arrays.fill(mAutoCancelledCountPerDic, 0);
+ }
+
+ private void addThemeIdEntry(long time) {
+ if (sPRINTLOGGING) {
+ Log.d(TAG, "Log theme Id. (1)");
+ }
+ // TODO: Not to convert theme ID here. Currently "2" is treated as "6" in a log server.
+ if (mThemeId.equals("2")) {
+ mThemeId = "6";
+ } else if (mThemeId.equals("3")) {
+ mThemeId = "7";
+ }
+ mLogBuffer.add(new LogEntry (time, ID_THEME_ID,
+ new String[] {mThemeId}));
+ }
+
+ private void addLanguagesEntry(long time) {
+ if (sPRINTLOGGING) {
+ Log.d(TAG, "Log language settings. (1)");
+ }
+ // CurrentLanguage and SelectedLanguages will be blank if user doesn't use multi-language
+ // switching.
+ if (TextUtils.isEmpty(mCurrentLanguage)) {
+ mCurrentLanguage = mContext.getResources().getConfiguration().locale.toString();
+ }
+ mLogBuffer.add(new LogEntry (time, ID_LANGUAGES,
+ new String[] {mCurrentLanguage , mSelectedLanguages}));
+ }
+
+ private void addSettingsEntry(long time) {
+ if (sPRINTLOGGING) {
+ Log.d(TAG, "Log settings. (1)");
+ }
+ final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
+ mLogBuffer.add(new LogEntry (time, ID_SETTING_AUTO_COMPLETE,
+ new String[] {String.valueOf(prefs.getBoolean(PREF_AUTO_COMPLETE,
+ mContext.getResources().getBoolean(R.bool.enable_autocorrect)))}));
+ }
+
+ private void addVersionNameEntry(long time) {
+ if (sPRINTLOGGING) {
+ Log.d(TAG, "Log Version. (1)");
+ }
+ try {
+ PackageInfo info = mContext.getPackageManager().getPackageInfo(
+ mContext.getPackageName(), 0);
+ mLogBuffer.add(new LogEntry (time, ID_VERSION,
+ new String[] {String.valueOf(info.versionCode), info.versionName}));
+ } catch (NameNotFoundException e) {
+ Log.e(TAG, "Could not find version name.");
+ }
+ }
+
+ private void addExceptionEntry(long time, String[] data) {
+ if (sPRINTLOGGING) {
+ Log.d(TAG, "Log Exception. (1)");
+ }
+ mLogBuffer.add(new LogEntry(time, ID_EXCEPTION, data));
+ }
+
+ private void flushPrivacyLogSafely() {
+ if (sPRINTLOGGING) {
+ Log.d(TAG, "Log obfuscated data. (" + mPrivacyLogBuffer.size() + ")");
+ }
+ long now = System.currentTimeMillis();
+ Collections.sort(mPrivacyLogBuffer);
+ for (LogEntry l: mPrivacyLogBuffer) {
+ l.mTime = now;
+ mLogBuffer.add(l);
+ }
+ mPrivacyLogBuffer.clear();
+ }
+
+ /**
+ * Add an entry
+ * @param tag
+ * @param data
+ */
+ private void addData(int tag, Object data) {
+ switch (tag) {
+ case ID_DELETE_COUNT:
+ if (((mLastTimeActive - mLastTimeCountEntry) > MINIMUMCOUNTINTERVAL)
+ || (mDeleteCount == 0 && mInputCount == 0)) {
+ addCountEntry(mLastTimeActive);
+ }
+ mDeleteCount += (Integer)data;
+ break;
+ case ID_INPUT_COUNT:
+ if (((mLastTimeActive - mLastTimeCountEntry) > MINIMUMCOUNTINTERVAL)
+ || (mDeleteCount == 0 && mInputCount == 0)) {
+ addCountEntry(mLastTimeActive);
+ }
+ mInputCount += (Integer)data;
+ break;
+ case ID_MANUALSUGGESTION_WITH_DATATYPE:
+ case ID_AUTOSUGGESTION:
+ ++mWordCount;
+ String[] dataStrings = (String[]) data;
+ if (dataStrings.length < 2) {
+ if (sDBG) {
+ Log.e(TAG, "The length of logged string array is invalid.");
+ }
+ break;
+ }
+ mActualCharCount += dataStrings[1].length();
+ if (checkStringDataSafe(dataStrings[0]) && checkStringDataSafe(dataStrings[1])) {
+ mPrivacyLogBuffer.add(
+ new LogEntry (System.currentTimeMillis(), tag, dataStrings));
+ } else {
+ if (sDBG) {
+ Log.d(TAG, "Skipped to add an entry because data is unsafe.");
+ }
+ }
+ break;
+ case ID_AUTOSUGGESTIONCANCELLED:
+ --mWordCount;
+ dataStrings = (String[]) data;
+ if (dataStrings.length < 2) {
+ if (sDBG) {
+ Log.e(TAG, "The length of logged string array is invalid.");
+ }
+ break;
+ }
+ mActualCharCount -= dataStrings[1].length();
+ if (checkStringDataSafe(dataStrings[0]) && checkStringDataSafe(dataStrings[1])) {
+ mPrivacyLogBuffer.add(
+ new LogEntry (System.currentTimeMillis(), tag, dataStrings));
+ } else {
+ if (sDBG) {
+ Log.d(TAG, "Skipped to add an entry because data is unsafe.");
+ }
+ }
+ break;
+ case ID_EXCEPTION:
+ dataStrings = (String[]) data;
+ if (dataStrings.length < 2) {
+ if (sDBG) {
+ Log.e(TAG, "The length of logged string array is invalid.");
+ }
+ break;
+ }
+ addExceptionEntry(System.currentTimeMillis(), dataStrings);
+ break;
+ default:
+ if (sDBG) {
+ Log.e(TAG, "Log Tag is not entried.");
+ }
+ break;
+ }
+ }
+
+ private void commitInternal() {
+ // if there is no log entry in mLogBuffer, will not send logs to DropBox.
+ if (!mLogBuffer.isEmpty() && (mAddTextToDropBoxTask == null
+ || mAddTextToDropBoxTask.getStatus() == AsyncTask.Status.FINISHED)) {
+ if (sPRINTLOGGING) {
+ Log.d(TAG, "Commit (" + mLogBuffer.size() + ")");
+ }
+ flushPrivacyLogSafely();
+ long now = System.currentTimeMillis();
+ addCountEntry(now);
+ addThemeIdEntry(now);
+ addLanguagesEntry(now);
+ addSettingsEntry(now);
+ addVersionNameEntry(now);
+ addSuggestionCountEntry(now);
+ String s = LogSerializer.createStringFromEntries(mLogBuffer);
+ reset();
+ mAddTextToDropBoxTask = (AddTextToDropBoxTask) new AddTextToDropBoxTask(
+ mDropBox, now, s).execute();
+ }
+ }
+
+ private void commitInternalAndStopSelf() {
+ if (sDBG) {
+ Log.e(TAG, "Exception was thrown and let's die.");
+ }
+ commitInternal();
+ LatinIME ime = ((LatinIME) mContext);
+ ime.hideWindow();
+ ime.stopSelf();
+ }
+
+ private synchronized void sendLogToDropBox(int tag, Object s) {
+ long now = System.currentTimeMillis();
+ if (sDBG) {
+ String out = "";
+ if (s instanceof String[]) {
+ for (String str: ((String[]) s)) {
+ out += str + ",";
+ }
+ } else if (s instanceof Integer) {
+ out += (Integer) s;
+ }
+ Log.d(TAG, "SendLog: " + tag + ";" + out + ", will be sent after "
+ + (- (now - mLastTimeSend - MINIMUMSENDINTERVAL) / 1000) + " sec.");
+ }
+ if (now - mLastTimeActive > MINIMUMSENDINTERVAL) {
+ // Send a log before adding an log entry if the last data is too old.
+ commitInternal();
+ addData(tag, s);
+ } else if (now - mLastTimeSend > MINIMUMSENDINTERVAL) {
+ // Send a log after adding an log entry.
+ addData(tag, s);
+ commitInternal();
+ } else {
+ addData(tag, s);
+ }
+ mLastTimeActive = now;
+ }
+
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+ if (PREF_ENABLE_LOG.equals(key)) {
+ if (sharedPreferences.getBoolean(key, DEFAULT_LOG_ENABLED)) {
+ sLogEnabled = (mContext != null);
+ } else {
+ sLogEnabled = false;
+ }
+ if (sDebugKeyEnabler.check()) {
+ sharedPreferences.edit().putBoolean(PREF_DEBUG_MODE, true).commit();
+ }
+ } else if (KeyboardSwitcher.PREF_KEYBOARD_LAYOUT.equals(key)) {
+ mThemeId = sharedPreferences.getString(KeyboardSwitcher.PREF_KEYBOARD_LAYOUT,
+ KeyboardSwitcher.DEFAULT_LAYOUT_ID);
+ addThemeIdEntry(mLastTimeActive);
+ } else if (PREF_DEBUG_MODE.equals(key)) {
+ sPRINTLOGGING = sharedPreferences.getBoolean(PREF_DEBUG_MODE, sPRINTLOGGING);
+ sDBG = sPRINTLOGGING;
+ } else if (LatinIME.PREF_INPUT_LANGUAGE.equals(key)) {
+ mCurrentLanguage = sharedPreferences.getString(LatinIME.PREF_INPUT_LANGUAGE, "");
+ addLanguagesEntry(mLastTimeActive);
+ } else if (LatinIME.PREF_INPUT_LANGUAGE.equals(key)) {
+ mSelectedLanguages = sharedPreferences.getString(LatinIME.PREF_SELECTED_LANGUAGES, "");
+ }
+ }
+
+ public static void init(Context context) {
+ sLatinImeLogger.initInternal(context);
+ }
+
+ public static void commit() {
+ if (sLogEnabled) {
+ if (System.currentTimeMillis() - sLatinImeLogger.mLastTimeActive > MINIMUMCOUNTINTERVAL
+ || (sLatinImeLogger.mLogBuffer.size()
+ + sLatinImeLogger.mPrivacyLogBuffer.size() > MINIMUMSENDSIZE)) {
+ sLatinImeLogger.commitInternal();
+ }
+ }
+ }
+
+ public static void onDestroy() {
+ sLatinImeLogger.commitInternal();
+ sLatinImeLogger.destroy();
+ }
+
+ // TODO: Handle CharSequence instead of String
+ public static void logOnManualSuggestion(String before, String after, int position
+ , List<CharSequence> suggestions) {
+ if (sLogEnabled) {
+ // log punctuation
+ if (before.length() == 0 && after.length() == 1) {
+ sLatinImeLogger.sendLogToDropBox(ID_MANUALSUGGESTION_WITH_DATATYPE, new String[] {
+ before, after, String.valueOf(position), ""});
+ } else if (!sSuggestDicMap.containsKey(after)) {
+ if (sDBG) {
+ Log.e(TAG, "logOnManualSuggestion was cancelled: from unknown dic.");
+ }
+ } else {
+ int dicTypeId = sSuggestDicMap.get(after).first;
+ sLatinImeLogger.mManualSuggestCountPerDic[dicTypeId]++;
+ if (dicTypeId != Suggest.DIC_MAIN) {
+ if (sDBG) {
+ Log.d(TAG, "logOnManualSuggestion was cancelled: not from main dic.");
+ }
+ before = "";
+ after = "";
+ sPreviousWords = null;
+ }
+ // TODO: Don't send a log if this doesn't come from Main Dictionary.
+ {
+ if (before.equals(after)) {
+ before = "";
+ after = "";
+ }
+
+ /* Example:
+ * When user typed "Illegal imm" and picked "immigrants",
+ * the suggestion list has "immigrants, immediate, immigrant".
+ * At this time, the log strings will be something like below:
+ * strings[0 = COLUMN_BEFORE_ID] = imm
+ * strings[1 = COLUMN_AFTER_ID] = immigrants
+ * strings[2 = COLUMN_PICKED_POSITION_ID] = 0
+ * strings[3 = COLUMN_SUGGESTION_LENGTH_ID] = 3
+ * strings[4 = COLUMN_PREVIOUS_WORDS_COUNT_ID] = 1
+ * strings[5] = immigrants
+ * strings[6] = immediate
+ * strings[7] = immigrant
+ * strings[8] = 1 (= bigram)
+ * strings[9] = 0 (= unigram)
+ * strings[10] = 1 (= bigram)
+ * strings[11] = Illegal
+ */
+
+ // 0 for unigram, 1 for bigram, 2 for trigram...
+ int previousWordsLength = (sPreviousWords == null) ? 0 : sPreviousWords.length;
+ int suggestionLength = suggestions.size();
+
+ final int COLUMN_BEFORE_ID = 0;
+ final int COLUMN_AFTER_ID = 1;
+ final int COLUMN_PICKED_POSITION_ID = 2;
+ final int COLUMN_SUGGESTION_LENGTH_ID = 3;
+ final int COLUMN_PREVIOUS_WORDS_COUNT_ID = 4;
+ final int BASE_COLUMN_SIZE = 5;
+
+ String[] strings =
+ new String[BASE_COLUMN_SIZE + suggestionLength * 2 + previousWordsLength];
+ strings[COLUMN_BEFORE_ID] = before;
+ strings[COLUMN_AFTER_ID] = after;
+ strings[COLUMN_PICKED_POSITION_ID] = String.valueOf(position);
+ strings[COLUMN_SUGGESTION_LENGTH_ID] = String.valueOf(suggestionLength);
+ strings[COLUMN_PREVIOUS_WORDS_COUNT_ID] = String.valueOf(previousWordsLength);
+
+ for (int i = 0; i < suggestionLength; ++i) {
+ String s = suggestions.get(i).toString();
+ if (sSuggestDicMap.containsKey(s)) {
+ strings[BASE_COLUMN_SIZE + i] = s;
+ strings[BASE_COLUMN_SIZE + suggestionLength + i]
+ = sSuggestDicMap.get(s).second.toString();
+ } else {
+ strings[BASE_COLUMN_SIZE + i] = "";
+ strings[BASE_COLUMN_SIZE + suggestionLength + i] = "";
+ }
+ }
+
+ for (int i = 0; i < previousWordsLength; ++i) {
+ strings[BASE_COLUMN_SIZE + suggestionLength * 2 + i] = sPreviousWords[i];
+ }
+
+ sLatinImeLogger.sendLogToDropBox(ID_MANUALSUGGESTION_WITH_DATATYPE, strings);
+ }
+ }
+ sSuggestDicMap.clear();
+ }
+ }
+
+ public static void logOnAutoSuggestion(String before, String after) {
+ if (sLogEnabled) {
+ if (!sSuggestDicMap.containsKey(after)) {
+ if (sDBG) {
+ Log.e(TAG, "logOnAutoSuggestion was cancelled: from unknown dic.");
+ }
+ } else {
+ String separator = String.valueOf(sLatinImeLogger.mRingCharBuffer.getLastChar());
+ sLastAutoSuggestDicTypeId = sSuggestDicMap.get(after).first;
+ sLastAutoSuggestDataType = sSuggestDicMap.get(after).second;
+ sLatinImeLogger.mAutoSuggestCountPerDic[sLastAutoSuggestDicTypeId]++;
+ if (sLastAutoSuggestDicTypeId != Suggest.DIC_MAIN) {
+ if (sDBG) {
+ Log.d(TAG, "logOnAutoSuggestion was cancelled: not from main dic.");
+ }
+ before = "";
+ after = "";
+ sPreviousWords = null;
+ }
+ // TODO: Not to send a log if this doesn't come from Main Dictionary.
+ {
+ if (before.equals(after)) {
+ before = "";
+ after = "";
+ }
+ int previousWordsLength = (sPreviousWords == null) ? 0 : sPreviousWords.length;
+
+ final int COLUMN_BEFORE_ID = 0;
+ final int COLUMN_AFTER_ID = 1;
+ final int COLUMN_SEPARATOR_ID = 2;
+ final int COLUMN_DATA_TYPE_ID = 3;
+ final int BASE_COLUMN_SIZE = 4;
+
+ String[] strings = new String[4 + previousWordsLength];
+ strings[COLUMN_BEFORE_ID] = before;
+ strings[COLUMN_AFTER_ID] = after;
+ strings[COLUMN_SEPARATOR_ID] = separator;
+ strings[COLUMN_DATA_TYPE_ID] = String.valueOf(sLastAutoSuggestDataType);
+ for (int i = 0; i < previousWordsLength; ++i) {
+ strings[BASE_COLUMN_SIZE + i] = sPreviousWords[i];
+ }
+ sLatinImeLogger.sendLogToDropBox(ID_AUTOSUGGESTION, strings);
+ }
+ synchronized (LatinImeLogger.class) {
+ sLastAutoSuggestBefore = before;
+ sLastAutoSuggestAfter = after;
+ sLastAutoSuggestSeparator = separator;
+ }
+ }
+ sSuggestDicMap.clear();
+ }
+ }
+
+ public static void logOnAutoSuggestionCanceled() {
+ if (sLogEnabled) {
+ sLatinImeLogger.mAutoCancelledCountPerDic[sLastAutoSuggestDicTypeId]++;
+ if (sLastAutoSuggestBefore != null && sLastAutoSuggestAfter != null) {
+ String[] strings = new String[] {
+ sLastAutoSuggestBefore, sLastAutoSuggestAfter, sLastAutoSuggestSeparator};
+ sLatinImeLogger.sendLogToDropBox(ID_AUTOSUGGESTIONCANCELLED, strings);
+ }
+ synchronized (LatinImeLogger.class) {
+ sLastAutoSuggestBefore = "";
+ sLastAutoSuggestAfter = "";
+ sLastAutoSuggestSeparator = "";
+ }
+ }
+ }
+
+ public static void logOnDelete() {
+ if (sLogEnabled) {
+ String mLastWord = sLatinImeLogger.mRingCharBuffer.getLastString();
+ if (!TextUtils.isEmpty(mLastWord)
+ && mLastWord.equalsIgnoreCase(sLastAutoSuggestBefore)) {
+ logOnAutoSuggestionCanceled();
+ }
+ sLatinImeLogger.mRingCharBuffer.pop();
+ sLatinImeLogger.sendLogToDropBox(ID_DELETE_COUNT, 1);
+ }
+ }
+
+ public static void logOnInputChar(char c) {
+ if (sLogEnabled) {
+ sLatinImeLogger.mRingCharBuffer.push(c);
+ sLatinImeLogger.sendLogToDropBox(ID_INPUT_COUNT, 1);
+ }
+ }
+
+ public static void logOnException(String metaData, Throwable e) {
+ if (sLogEnabled) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ PrintStream ps = new PrintStream(baos);
+ e.printStackTrace(ps);
+ String exceptionString = URLEncoder.encode(new String(baos.toByteArray(), 0,
+ Math.min(EXCEPTION_MAX_LENGTH, baos.size())));
+ sLatinImeLogger.sendLogToDropBox(
+ ID_EXCEPTION, new String[] {metaData, exceptionString});
+ if (sDBG) {
+ Log.e(TAG, "Exception: " + new String(baos.toByteArray())+ ":" + exceptionString);
+ }
+ if (SUPPRESS_EXCEPTION) {
+ sLatinImeLogger.commitInternalAndStopSelf();
+ } else {
+ sLatinImeLogger.commitInternal();
+ if (e instanceof RuntimeException) {
+ throw (RuntimeException) e;
+ } else if (e instanceof Error) {
+ throw (Error) e;
+ }
+ }
+ }
+ }
+
+ public static void logOnWarning(String warning) {
+ if (sLogEnabled) {
+ sLatinImeLogger.sendLogToDropBox(
+ ID_EXCEPTION, new String[] {warning, ""});
+ }
+ }
+
+ // TODO: This code supports only Bigram.
+ public static void onStartSuggestion(CharSequence previousWords) {
+ if (sLogEnabled) {
+ sSuggestDicMap.clear();
+ sPreviousWords = new String[] {
+ (previousWords == null) ? "" : previousWords.toString()};
+ }
+ }
+
+ public static void onAddSuggestedWord(String word, int typeId, DataType dataType) {
+ if (sLogEnabled) {
+ sSuggestDicMap.put(word, new Pair<Integer, Integer>(typeId, dataType.ordinal()));
+ }
+ }
+
+ private static class LogSerializer {
+ private static void appendWithLength(StringBuffer sb, String data) {
+ sb.append(data.length());
+ sb.append(SEPARATER);
+ sb.append(data);
+ sb.append(SEPARATER);
+ }
+
+ private static void appendLogEntry(StringBuffer sb, String time, String tag,
+ String[] data) {
+ if (data.length > 0) {
+ appendWithLength(sb, String.valueOf(data.length + 2));
+ appendWithLength(sb, time);
+ appendWithLength(sb, tag);
+ for (String s: data) {
+ appendWithLength(sb, s);
+ }
+ }
+ }
+
+ public static String createStringFromEntries(ArrayList<LogEntry> logs) {
+ StringBuffer sb = new StringBuffer();
+ for (LogEntry log: logs) {
+ appendLogEntry(sb, String.valueOf(log.mTime), String.valueOf(log.mTag), log.mData);
+ }
+ return sb.toString();
+ }
+ }
+
+ /* package */ static class RingCharBuffer {
+ final int BUFSIZE = 20;
+ private Context mContext;
+ private int mEnd = 0;
+ /* package */ int length = 0;
+ private char[] mCharBuf = new char[BUFSIZE];
+
+ public RingCharBuffer(Context context) {
+ mContext = context;
+ }
+
+ private int normalize(int in) {
+ int ret = in % BUFSIZE;
+ return ret < 0 ? ret + BUFSIZE : ret;
+ }
+ public void push(char c) {
+ mCharBuf[mEnd] = c;
+ mEnd = normalize(mEnd + 1);
+ if (length < BUFSIZE) {
+ ++length;
+ }
+ }
+ public char pop() {
+ if (length < 1) {
+ return NULL_CHAR;
+ } else {
+ mEnd = normalize(mEnd - 1);
+ --length;
+ return mCharBuf[mEnd];
+ }
+ }
+ public char getLastChar() {
+ if (length < 1) {
+ return NULL_CHAR;
+ } else {
+ return mCharBuf[normalize(mEnd - 1)];
+ }
+ }
+ public String getLastString() {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < length; ++i) {
+ char c = mCharBuf[normalize(mEnd - 1 - i)];
+ if (!((LatinIME)mContext).isWordSeparator(c)) {
+ sb.append(c);
+ } else {
+ break;
+ }
+ }
+ return sb.reverse().toString();
+ }
+ public void reset() {
+ length = 0;
+ }
+ }
+
+ private static class DebugKeyEnabler {
+ private int mCounter = 0;
+ private long mLastTime = 0;
+ public boolean check() {
+ if (System.currentTimeMillis() - mLastTime > 10 * 1000) {
+ mCounter = 0;
+ mLastTime = System.currentTimeMillis();
+ } else if (++mCounter >= 10) {
+ return true;
+ }
+ return false;
+ }
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/LatinKeyboard.java b/java/src/com/android/inputmethod/latin/LatinKeyboard.java
index 6aea5d13a..db4d167d4 100644
--- a/java/src/com/android/inputmethod/latin/LatinKeyboard.java
+++ b/java/src/com/android/inputmethod/latin/LatinKeyboard.java
@@ -47,7 +47,6 @@ public class LatinKeyboard extends Keyboard {
private Drawable mShiftLockIcon;
private Drawable mShiftLockPreviewIcon;
private Drawable mOldShiftIcon;
- private Drawable mOldShiftPreviewIcon;
private Drawable mSpaceIcon;
private Drawable mSpacePreviewIcon;
private Drawable mMicIcon;
@@ -68,7 +67,6 @@ public class LatinKeyboard extends Keyboard {
private LanguageSwitcher mLanguageSwitcher;
private Resources mRes;
private Context mContext;
- private int mMode;
// Whether this keyboard has voice icon on it
private boolean mHasVoiceButton;
// Whether voice icon is enabled at all
@@ -77,16 +75,16 @@ public class LatinKeyboard extends Keyboard {
private CharSequence m123Label;
private boolean mCurrentlyInSpace;
private SlidingLocaleDrawable mSlidingLocaleIcon;
- private Rect mBounds = new Rect();
private int[] mPrefLetterFrequencies;
- private boolean mPreemptiveCorrection;
private int mPrefLetter;
private int mPrefLetterX;
private int mPrefLetterY;
private int mPrefDistance;
- private int mExtensionResId;
-
+ private int mExtensionResId;
+ // TODO: generalize for any keyboardId
+ private boolean mIsBlackSym;
+
private static final int SHIFT_OFF = 0;
private static final int SHIFT_ON = 1;
private static final int SHIFT_LOCKED = 2;
@@ -107,7 +105,6 @@ public class LatinKeyboard extends Keyboard {
super(context, xmlLayoutResId, mode);
final Resources res = context.getResources();
mContext = context;
- mMode = mode;
mRes = res;
mShiftLockIcon = res.getDrawable(R.drawable.sym_keyboard_shift_locked);
mShiftLockPreviewIcon = res.getDrawable(R.drawable.sym_keyboard_feedback_shift_locked);
@@ -126,7 +123,8 @@ public class LatinKeyboard extends Keyboard {
setDefaultBounds(m123MicPreviewIcon);
sSpacebarVerticalCorrection = res.getDimensionPixelOffset(
R.dimen.spacebar_vertical_correction);
- mIsAlphaKeyboard = xmlLayoutResId == R.xml.kbd_qwerty;
+ mIsAlphaKeyboard = xmlLayoutResId == R.xml.kbd_qwerty
+ || xmlLayoutResId == R.xml.kbd_qwerty_black;
mSpaceKeyIndex = indexOf((int) ' ');
}
@@ -182,8 +180,8 @@ public class LatinKeyboard extends Keyboard {
case EditorInfo.IME_ACTION_SEARCH:
mEnterKey.iconPreview = res.getDrawable(
R.drawable.sym_keyboard_feedback_search);
- mEnterKey.icon = res.getDrawable(
- R.drawable.sym_keyboard_search);
+ mEnterKey.icon = res.getDrawable(mIsBlackSym ?
+ R.drawable.sym_bkeyboard_search : R.drawable.sym_keyboard_search);
mEnterKey.label = null;
break;
case EditorInfo.IME_ACTION_SEND:
@@ -201,8 +199,8 @@ public class LatinKeyboard extends Keyboard {
} else {
mEnterKey.iconPreview = res.getDrawable(
R.drawable.sym_keyboard_feedback_return);
- mEnterKey.icon = res.getDrawable(
- R.drawable.sym_keyboard_return);
+ mEnterKey.icon = res.getDrawable(mIsBlackSym ?
+ R.drawable.sym_bkeyboard_return : R.drawable.sym_keyboard_return);
mEnterKey.label = null;
}
break;
@@ -224,7 +222,6 @@ public class LatinKeyboard extends Keyboard {
((LatinKey)mShiftKey).enableShiftLock();
}
mOldShiftIcon = mShiftKey.icon;
- mOldShiftPreviewIcon = mShiftKey.iconPreview;
}
}
@@ -277,6 +274,10 @@ public class LatinKeyboard extends Keyboard {
}
}
+ /* package */ boolean isAlphaKeyboard() {
+ return mIsAlphaKeyboard;
+ }
+
public void setExtension(int resId) {
mExtensionResId = resId;
}
@@ -285,6 +286,26 @@ public class LatinKeyboard extends Keyboard {
return mExtensionResId;
}
+ public void setBlackFlag(boolean f) {
+ mIsBlackSym = f;
+ if (f) {
+ mShiftLockIcon = mRes.getDrawable(R.drawable.sym_bkeyboard_shift_locked);
+ mSpaceIcon = mRes.getDrawable(R.drawable.sym_bkeyboard_space);
+ mMicIcon = mRes.getDrawable(R.drawable.sym_bkeyboard_mic);
+ m123MicIcon = mRes.getDrawable(R.drawable.sym_bkeyboard_123_mic);
+ } else {
+ mShiftLockIcon = mRes.getDrawable(R.drawable.sym_keyboard_shift_locked);
+ mSpaceIcon = mRes.getDrawable(R.drawable.sym_keyboard_space);
+ mMicIcon = mRes.getDrawable(R.drawable.sym_keyboard_mic);
+ m123MicIcon = mRes.getDrawable(R.drawable.sym_keyboard_123_mic);
+ }
+ updateF1Key();
+ if (mSpaceKey != null) {
+ mSpaceKey.icon = mSpaceIcon;
+ updateSpaceBarForLocale(f);
+ }
+ }
+
private void setDefaultBounds(Drawable drawable) {
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
}
@@ -322,39 +343,40 @@ public class LatinKeyboard extends Keyboard {
}
}
- private void updateSpaceBarForLocale() {
+ private void updateSpaceBarForLocale(boolean isBlack) {
if (mLocale != null) {
// Create the graphic for spacebar
Bitmap buffer = Bitmap.createBitmap(mSpaceKey.width, mSpaceIcon.getIntrinsicHeight(),
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(buffer);
- drawSpaceBar(canvas, buffer.getWidth(), buffer.getHeight(), 255);
+ drawSpaceBar(canvas, buffer.getWidth(), buffer.getHeight(), 255, isBlack);
mSpaceKey.icon = new BitmapDrawable(mRes, buffer);
mSpaceKey.repeatable = mLanguageSwitcher.getLocaleCount() < 2;
} else {
- mSpaceKey.icon = mRes.getDrawable(R.drawable.sym_keyboard_space);
+ mSpaceKey.icon = isBlack ? mRes.getDrawable(R.drawable.sym_bkeyboard_space)
+ : mRes.getDrawable(R.drawable.sym_keyboard_space);
mSpaceKey.repeatable = true;
}
}
- private void drawSpaceBar(Canvas canvas, int width, int height, int opacity) {
- canvas.drawColor(0x00000000, PorterDuff.Mode.CLEAR);
+ private void drawSpaceBar(Canvas canvas, int width, int height, int opacity, boolean isBlack) {
+ canvas.drawColor(mRes.getColor(R.color.latinkeyboard_transparent), PorterDuff.Mode.CLEAR);
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setAlpha(opacity);
// Get the text size from the theme
paint.setTextSize(getTextSizeFromTheme(android.R.style.TextAppearance_Small, 14));
paint.setTextAlign(Align.CENTER);
- //// Draw a drop shadow for the text
- //paint.setShadowLayer(2f, 0, 0, 0xFF000000);
final String language = getInputLanguage(mSpaceKey.width, paint);
final int ascent = (int) -paint.ascent();
- paint.setColor(0x80000000);
- canvas.drawText(language,
- width / 2, ascent - 1, paint);
- paint.setColor(0xFF808080);
- canvas.drawText(language,
- width / 2, ascent, paint);
+
+ int shadowColor = isBlack ? mRes.getColor(R.color.latinkeyboard_bar_language_shadow_black)
+ : mRes.getColor(R.color.latinkeyboard_bar_language_shadow_white);
+
+ paint.setColor(shadowColor);
+ canvas.drawText(language, width / 2, ascent - 1, paint);
+ paint.setColor(mRes.getColor(R.color.latinkeyboard_bar_language_text));
+ canvas.drawText(language, width / 2, ascent, paint);
// Put arrows on either side of the text
if (mLanguageSwitcher.getLocaleCount() > 1) {
Rect bounds = new Rect();
@@ -439,7 +461,7 @@ public class LatinKeyboard extends Keyboard {
}
if (mLocale != null && mLocale.equals(locale)) return;
mLocale = locale;
- updateSpaceBarForLocale();
+ updateSpaceBarForLocale(mIsBlackSym);
}
boolean isCurrentlyInSpace() {
@@ -503,9 +525,10 @@ public class LatinKeyboard extends Keyboard {
// Handle preferred next letter
final int[] pref = mPrefLetterFrequencies;
if (mPrefLetter > 0) {
- if (DEBUG_PREFERRED_LETTER && mPrefLetter == code
- && !key.isInsideSuper(x, y)) {
- Log.d(TAG, "CORRECTED !!!!!!");
+ if (DEBUG_PREFERRED_LETTER) {
+ if (mPrefLetter == code && !key.isInsideSuper(x, y)) {
+ Log.d(TAG, "CORRECTED !!!!!!");
+ }
}
return mPrefLetter == code;
} else {
@@ -684,7 +707,7 @@ public class LatinKeyboard extends Keyboard {
mTextPaint = new TextPaint();
int textSize = getTextSizeFromTheme(android.R.style.TextAppearance_Medium, 18);
mTextPaint.setTextSize(textSize);
- mTextPaint.setColor(0);
+ mTextPaint.setColor(R.color.latinkeyboard_transparent);
mTextPaint.setTextAlign(Align.CENTER);
mTextPaint.setAlpha(255);
mTextPaint.setAntiAlias(true);
@@ -718,7 +741,7 @@ public class LatinKeyboard extends Keyboard {
public void draw(Canvas canvas) {
canvas.save();
if (mHitThreshold) {
- mTextPaint.setColor(0xFF000000);
+ mTextPaint.setColor(mRes.getColor(R.color.latinkeyboard_text_color));
canvas.clipRect(0, 0, mWidth, mHeight);
if (mCurrentLanguage == null) {
mCurrentLanguage = getInputLanguage(mWidth, mTextPaint);
diff --git a/java/src/com/android/inputmethod/latin/LatinKeyboardBaseView.java b/java/src/com/android/inputmethod/latin/LatinKeyboardBaseView.java
new file mode 100644
index 000000000..665c641c2
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/LatinKeyboardBaseView.java
@@ -0,0 +1,1633 @@
+/*
+ * Copyright (C) 2010 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.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.Rect;
+import android.graphics.Typeface;
+import android.graphics.Paint.Align;
+import android.graphics.Region.Op;
+import android.graphics.drawable.Drawable;
+import android.inputmethodservice.Keyboard;
+import android.inputmethodservice.Keyboard.Key;
+import android.os.Handler;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.GestureDetector;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.PopupWindow;
+import android.widget.TextView;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A view that renders a virtual {@link LatinKeyboard}. It handles rendering of keys and
+ * detecting key presses and touch movements.
+ *
+ * @attr ref R.styleable#LatinKeyboardBaseView_keyBackground
+ * @attr ref R.styleable#LatinKeyboardBaseView_keyPreviewLayout
+ * @attr ref R.styleable#LatinKeyboardBaseView_keyPreviewOffset
+ * @attr ref R.styleable#LatinKeyboardBaseView_labelTextSize
+ * @attr ref R.styleable#LatinKeyboardBaseView_keyTextSize
+ * @attr ref R.styleable#LatinKeyboardBaseView_keyTextColor
+ * @attr ref R.styleable#LatinKeyboardBaseView_verticalCorrection
+ * @attr ref R.styleable#LatinKeyboardBaseView_popupLayout
+ */
+public class LatinKeyboardBaseView extends View implements View.OnClickListener {
+
+ public interface OnKeyboardActionListener {
+
+ /**
+ * Called when the user presses a key. This is sent before the
+ * {@link #onKey} is called. For keys that repeat, this is only
+ * called once.
+ *
+ * @param primaryCode
+ * the unicode of the key being pressed. If the touch is
+ * not on a valid key, the value will be zero.
+ */
+ void onPress(int primaryCode);
+
+ /**
+ * Called when the user releases a key. This is sent after the
+ * {@link #onKey} is called. For keys that repeat, this is only
+ * called once.
+ *
+ * @param primaryCode
+ * the code of the key that was released
+ */
+ void onRelease(int primaryCode);
+
+ /**
+ * Send a key press to the listener.
+ *
+ * @param primaryCode
+ * this is the key that was pressed
+ * @param keyCodes
+ * the codes for all the possible alternative keys with
+ * the primary code being the first. If the primary key
+ * code is a single character such as an alphabet or
+ * number or symbol, the alternatives will include other
+ * characters that may be on the same key or adjacent
+ * keys. These codes are useful to correct for
+ * accidental presses of a key adjacent to the intended
+ * key.
+ */
+ void onKey(int primaryCode, int[] keyCodes);
+
+ /**
+ * Sends a sequence of characters to the listener.
+ *
+ * @param text
+ * the sequence of characters to be displayed.
+ */
+ void onText(CharSequence text);
+
+ /**
+ * Called when the user quickly moves the finger from right to
+ * left.
+ */
+ void swipeLeft();
+
+ /**
+ * Called when the user quickly moves the finger from left to
+ * right.
+ */
+ void swipeRight();
+
+ /**
+ * Called when the user quickly moves the finger from up to down.
+ */
+ void swipeDown();
+
+ /**
+ * Called when the user quickly moves the finger from down to up.
+ */
+ void swipeUp();
+ }
+
+ private static final boolean DEBUG = false;
+ private static final int NOT_A_KEY = -1;
+ private static final int[] KEY_DELETE = { Keyboard.KEYCODE_DELETE };
+ private static final int[] LONG_PRESSABLE_STATE_SET = { android.R.attr.state_long_pressable };
+
+ private Keyboard mKeyboard;
+ private int mCurrentKeyIndex = NOT_A_KEY;
+ private int mLabelTextSize;
+ private int mKeyTextSize;
+ private int mKeyTextColor;
+ private float mShadowRadius;
+ private int mShadowColor;
+ private float mBackgroundDimAmount;
+
+ private TextView mPreviewText;
+ private PopupWindow mPreviewPopup;
+ private int mPreviewTextSizeLarge;
+ private int mPreviewOffset;
+ private int mPreviewHeight;
+ private int[] mOffsetInWindow;
+
+ private PopupWindow mPopupKeyboard;
+ private View mMiniKeyboardContainer;
+ private LatinKeyboardBaseView mMiniKeyboard;
+ private boolean mMiniKeyboardOnScreen;
+ private View mPopupParent;
+ private int mMiniKeyboardOffsetX;
+ private int mMiniKeyboardOffsetY;
+ private Map<Key,View> mMiniKeyboardCache;
+ private int[] mWindowOffset;
+ private Key[] mKeys;
+ private Typeface mKeyTextStyle = Typeface.DEFAULT;
+ private int mSymbolColorScheme = 0;
+
+ /** Listener for {@link OnKeyboardActionListener}. */
+ private OnKeyboardActionListener mKeyboardActionListener;
+
+ private static final int DELAY_BEFORE_PREVIEW = 0;
+ private static final int DELAY_AFTER_PREVIEW = 70;
+ private static final int DEBOUNCE_TIME = 70;
+
+ private int mVerticalCorrection;
+ private int mProximityThreshold;
+
+ private boolean mPreviewCentered = false;
+ private boolean mShowPreview = true;
+ private boolean mShowTouchPoints = true;
+ private int mPopupPreviewX;
+ private int mPopupPreviewY;
+ private int mWindowY;
+
+ private boolean mProximityCorrectOn;
+
+ private Paint mPaint;
+ private Rect mPadding;
+
+ private int mCurrentKey = NOT_A_KEY;
+ private int mDownKey = NOT_A_KEY;
+ private int mStartX;
+ private int mStartY;
+
+ private KeyDebouncer mDebouncer;
+
+ private GestureDetector mGestureDetector;
+ private int mPopupX;
+ private int mPopupY;
+ private int mRepeatKeyIndex = NOT_A_KEY;
+ private int mPopupLayout;
+ private boolean mAbortKey;
+ private Key mInvalidatedKey;
+ private Rect mClipRegion = new Rect(0, 0, 0, 0);
+ private boolean mPossiblePoly;
+ private SwipeTracker mSwipeTracker = new SwipeTracker();
+ private int mSwipeThreshold;
+ private boolean mDisambiguateSwipe;
+
+ // Variables for dealing with multiple pointers
+ private int mOldPointerCount = 1;
+ private float mOldPointerX;
+ private float mOldPointerY;
+
+ private Drawable mKeyBackground;
+
+ private static final int REPEAT_INTERVAL = 50; // ~20 keys per second
+ private static final int REPEAT_START_DELAY = 400;
+ private static final int LONGPRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout();
+
+ private static int MAX_NEARBY_KEYS = 12;
+ private int[] mDistances = new int[MAX_NEARBY_KEYS];
+
+ // For multi-tap
+ private int mLastSentIndex;
+ private int mTapCount;
+ private long mLastTapTime;
+ private boolean mInMultiTap;
+ private static final int MULTITAP_INTERVAL = 800; // milliseconds
+ private StringBuilder mPreviewLabel = new StringBuilder(1);
+
+ /** Whether the keyboard bitmap needs to be redrawn before it's blitted. **/
+ private boolean mDrawPending;
+ /** The dirty region in the keyboard bitmap */
+ private Rect mDirtyRect = new Rect();
+ /** The keyboard bitmap for faster updates */
+ private Bitmap mBuffer;
+ /** Notes if the keyboard just changed, so that we could possibly reallocate the mBuffer. */
+ private boolean mKeyboardChanged;
+ /** The canvas for the above mutable keyboard bitmap */
+ private Canvas mCanvas;
+
+ UIHandler mHandler = new UIHandler();
+
+ class UIHandler extends Handler {
+ private static final int MSG_POPUP_PREVIEW = 1;
+ private static final int MSG_DISMISS_PREVIEW = 2;
+ private static final int MSG_REPEAT_KEY = 3;
+ private static final int MSG_LOGPRESS_KEY = 4;
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_POPUP_PREVIEW:
+ showKey(msg.arg1);
+ break;
+ case MSG_DISMISS_PREVIEW:
+ mPreviewText.setVisibility(INVISIBLE);
+ break;
+ case MSG_REPEAT_KEY:
+ if (repeatKey()) {
+ startKeyRepeatTimer(REPEAT_INTERVAL);
+ }
+ break;
+ case MSG_LOGPRESS_KEY:
+ openPopupIfRequired((MotionEvent) msg.obj);
+ break;
+ }
+ }
+
+ public void popupPreview(int keyIndex, long delay) {
+ removeMessages(MSG_POPUP_PREVIEW);
+ sendMessageDelayed(obtainMessage(MSG_POPUP_PREVIEW, keyIndex, 0), delay);
+ }
+
+ public void cancelPopupPreview() {
+ removeMessages(MSG_POPUP_PREVIEW);
+ }
+
+ public void dismissPreview(long delay) {
+ sendMessageDelayed(obtainMessage(MSG_DISMISS_PREVIEW), delay);
+ }
+
+ public void cancelDismissPreview() {
+ removeMessages(MSG_DISMISS_PREVIEW);
+ }
+
+ public void startKeyRepeatTimer(long delay) {
+ sendMessageDelayed(obtainMessage(MSG_REPEAT_KEY), delay);
+ }
+
+ public void startLongPressTimer(MotionEvent me, long delay) {
+ sendMessageDelayed(obtainMessage(MSG_LOGPRESS_KEY, me), delay);
+ }
+
+ public void cancelLongPressTimer() {
+ removeMessages(MSG_LOGPRESS_KEY);
+ }
+
+ public void cancelKeyTimers() {
+ removeMessages(MSG_REPEAT_KEY);
+ removeMessages(MSG_LOGPRESS_KEY);
+ }
+
+ public void cancelKeyTimersAndPopupPreview() {
+ removeMessages(MSG_REPEAT_KEY);
+ removeMessages(MSG_LOGPRESS_KEY);
+ removeMessages(MSG_POPUP_PREVIEW);
+ }
+
+ public void cancelAllMessages() {
+ removeMessages(MSG_REPEAT_KEY);
+ removeMessages(MSG_LOGPRESS_KEY);
+ removeMessages(MSG_POPUP_PREVIEW);
+ removeMessages(MSG_DISMISS_PREVIEW);
+ }
+ };
+
+ static class KeyDebouncer {
+ private final Key[] mKeys;
+ private final int mKeyDebounceThresholdSquared;
+
+ // for move de-bouncing
+ private int mLastCodeX;
+ private int mLastCodeY;
+ private int mLastX;
+ private int mLastY;
+
+ // for time de-bouncing
+ private int mLastKey;
+ private long mLastKeyTime;
+ private long mLastMoveTime;
+ private long mCurrentKeyTime;
+
+ KeyDebouncer(Key[] keys, float hysteresisPixel) {
+ if (keys == null || hysteresisPixel < 1.0f)
+ throw new IllegalArgumentException();
+ mKeys = keys;
+ mKeyDebounceThresholdSquared = (int)(hysteresisPixel * hysteresisPixel);
+ }
+
+ public int getLastCodeX() {
+ return mLastCodeX;
+ }
+
+ public int getLastCodeY() {
+ return mLastCodeY;
+ }
+
+ public int getLastX() {
+ return mLastX;
+ }
+
+ public int getLastY() {
+ return mLastY;
+ }
+
+ public int getLastKey() {
+ return mLastKey;
+ }
+
+ public void startMoveDebouncing(int x, int y) {
+ mLastCodeX = x;
+ mLastCodeY = y;
+ }
+
+ public void updateMoveDebouncing(int x, int y) {
+ mLastX = x;
+ mLastY = y;
+ }
+
+ public void resetMoveDebouncing() {
+ mLastCodeX = mLastX;
+ mLastCodeY = mLastY;
+ }
+
+ public boolean isMinorMoveBounce(int x, int y, int newKey, int curKey) {
+ if (newKey == curKey) {
+ return true;
+ } else if (curKey >= 0 && curKey < mKeys.length) {
+ return getSquareDistanceToKeyEdge(x, y, mKeys[curKey])
+ < mKeyDebounceThresholdSquared;
+ } else {
+ return false;
+ }
+ }
+
+ private static int getSquareDistanceToKeyEdge(int x, int y, Key key) {
+ final int left = key.x;
+ final int right = key.x + key.width;
+ final int top = key.y;
+ final int bottom = key.y + key.height;
+ final int edgeX = x < left ? left : (x > right ? right : x);
+ final int edgeY = y < top ? top : (y > bottom ? bottom : y);
+ final int dx = x - edgeX;
+ final int dy = y - edgeY;
+ return dx * dx + dy * dy;
+ }
+
+ public void startTimeDebouncing(long eventTime) {
+ mLastKey = NOT_A_KEY;
+ mLastKeyTime = 0;
+ mCurrentKeyTime = 0;
+ mLastMoveTime = eventTime;
+ }
+
+ public void updateTimeDebouncing(long eventTime) {
+ mCurrentKeyTime += eventTime - mLastMoveTime;
+ mLastMoveTime = eventTime;
+ }
+
+ public void resetTimeDebouncing(long eventTime, int currentKey) {
+ mLastKey = currentKey;
+ mLastKeyTime = mCurrentKeyTime + eventTime - mLastMoveTime;
+ mCurrentKeyTime = 0;
+ mLastMoveTime = eventTime;
+ }
+
+ public boolean isMinorTimeBounce() {
+ return mCurrentKeyTime < mLastKeyTime && mCurrentKeyTime < DEBOUNCE_TIME
+ && mLastKey != NOT_A_KEY;
+ }
+ }
+
+ public LatinKeyboardBaseView(Context context, AttributeSet attrs) {
+ this(context, attrs, R.attr.keyboardViewStyle);
+ }
+
+ public LatinKeyboardBaseView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ TypedArray a = context.obtainStyledAttributes(
+ attrs, R.styleable.LatinKeyboardBaseView, defStyle, R.style.LatinKeyboardBaseView);
+ LayoutInflater inflate =
+ (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ int previewLayout = 0;
+ int keyTextSize = 0;
+
+ int n = a.getIndexCount();
+
+ for (int i = 0; i < n; i++) {
+ int attr = a.getIndex(i);
+
+ switch (attr) {
+ case R.styleable.LatinKeyboardBaseView_keyBackground:
+ mKeyBackground = a.getDrawable(attr);
+ break;
+ case R.styleable.LatinKeyboardBaseView_verticalCorrection:
+ mVerticalCorrection = a.getDimensionPixelOffset(attr, 0);
+ break;
+ case R.styleable.LatinKeyboardBaseView_keyPreviewLayout:
+ previewLayout = a.getResourceId(attr, 0);
+ break;
+ case R.styleable.LatinKeyboardBaseView_keyPreviewOffset:
+ mPreviewOffset = a.getDimensionPixelOffset(attr, 0);
+ break;
+ case R.styleable.LatinKeyboardBaseView_keyPreviewHeight:
+ mPreviewHeight = a.getDimensionPixelSize(attr, 80);
+ break;
+ case R.styleable.LatinKeyboardBaseView_keyTextSize:
+ mKeyTextSize = a.getDimensionPixelSize(attr, 18);
+ break;
+ case R.styleable.LatinKeyboardBaseView_keyTextColor:
+ mKeyTextColor = a.getColor(attr, 0xFF000000);
+ break;
+ case R.styleable.LatinKeyboardBaseView_labelTextSize:
+ mLabelTextSize = a.getDimensionPixelSize(attr, 14);
+ break;
+ case R.styleable.LatinKeyboardBaseView_popupLayout:
+ mPopupLayout = a.getResourceId(attr, 0);
+ break;
+ case R.styleable.LatinKeyboardBaseView_shadowColor:
+ mShadowColor = a.getColor(attr, 0);
+ break;
+ case R.styleable.LatinKeyboardBaseView_shadowRadius:
+ mShadowRadius = a.getFloat(attr, 0f);
+ break;
+ // TODO: Use Theme (android.R.styleable.Theme_backgroundDimAmount)
+ case R.styleable.LatinKeyboardBaseView_backgroundDimAmount:
+ mBackgroundDimAmount = a.getFloat(attr, 0.5f);
+ break;
+ //case android.R.styleable.
+ case R.styleable.LatinKeyboardBaseView_keyTextStyle:
+ int textStyle = a.getInt(attr, 0);
+ switch (textStyle) {
+ case 0:
+ mKeyTextStyle = Typeface.DEFAULT;
+ break;
+ case 1:
+ mKeyTextStyle = Typeface.DEFAULT_BOLD;
+ break;
+ default:
+ mKeyTextStyle = Typeface.defaultFromStyle(textStyle);
+ break;
+ }
+ break;
+ case R.styleable.LatinKeyboardBaseView_symbolColorScheme:
+ mSymbolColorScheme = a.getInt(attr, 0);
+ break;
+ }
+ }
+
+ mPreviewPopup = new PopupWindow(context);
+ if (previewLayout != 0) {
+ mPreviewText = (TextView) inflate.inflate(previewLayout, null);
+ mPreviewTextSizeLarge = (int) mPreviewText.getTextSize();
+ mPreviewPopup.setContentView(mPreviewText);
+ mPreviewPopup.setBackgroundDrawable(null);
+ } else {
+ mShowPreview = false;
+ }
+
+ mPreviewPopup.setTouchable(false);
+
+ mPopupKeyboard = new PopupWindow(context);
+ mPopupKeyboard.setBackgroundDrawable(null);
+ //mPopupKeyboard.setClippingEnabled(false);
+
+ mPopupParent = this;
+ //mPredicting = true;
+
+ mPaint = new Paint();
+ mPaint.setAntiAlias(true);
+ mPaint.setTextSize(keyTextSize);
+ mPaint.setTextAlign(Align.CENTER);
+ mPaint.setAlpha(255);
+
+ mPadding = new Rect(0, 0, 0, 0);
+ mMiniKeyboardCache = new HashMap<Key,View>();
+ mKeyBackground.getPadding(mPadding);
+
+ mSwipeThreshold = (int) (500 * getResources().getDisplayMetrics().density);
+ // TODO: Refer frameworks/base/core/res/res/values/config.xml
+ mDisambiguateSwipe = getResources().getBoolean(R.bool.config_swipeDisambiguation);
+ resetMultiTap();
+ initGestureDetector();
+ }
+
+ private void initGestureDetector() {
+ mGestureDetector = new GestureDetector(
+ getContext(), new GestureDetector.SimpleOnGestureListener() {
+ @Override
+ public boolean onFling(MotionEvent me1, MotionEvent me2,
+ float velocityX, float velocityY) {
+ if (mPossiblePoly) return false;
+ final float absX = Math.abs(velocityX);
+ final float absY = Math.abs(velocityY);
+ float deltaX = me2.getX() - me1.getX();
+ float deltaY = me2.getY() - me1.getY();
+ int travelX = getWidth() / 2; // Half the keyboard width
+ int travelY = getHeight() / 2; // Half the keyboard height
+ mSwipeTracker.computeCurrentVelocity(1000);
+ final float endingVelocityX = mSwipeTracker.getXVelocity();
+ final float endingVelocityY = mSwipeTracker.getYVelocity();
+ boolean sendDownKey = false;
+ if (velocityX > mSwipeThreshold && absY < absX && deltaX > travelX) {
+ if (mDisambiguateSwipe && endingVelocityX < velocityX / 4) {
+ sendDownKey = true;
+ } else {
+ swipeRight();
+ return true;
+ }
+ } else if (velocityX < -mSwipeThreshold && absY < absX && deltaX < -travelX) {
+ if (mDisambiguateSwipe && endingVelocityX > velocityX / 4) {
+ sendDownKey = true;
+ } else {
+ swipeLeft();
+ return true;
+ }
+ } else if (velocityY < -mSwipeThreshold && absX < absY && deltaY < -travelY) {
+ if (mDisambiguateSwipe && endingVelocityY > velocityY / 4) {
+ sendDownKey = true;
+ } else {
+ swipeUp();
+ return true;
+ }
+ } else if (velocityY > mSwipeThreshold && absX < absY / 2 && deltaY > travelY) {
+ if (mDisambiguateSwipe && endingVelocityY < velocityY / 4) {
+ sendDownKey = true;
+ } else {
+ swipeDown();
+ return true;
+ }
+ }
+
+ if (sendDownKey) {
+ detectAndSendKey(mDownKey, mStartX, mStartY, me1.getEventTime());
+ }
+ return false;
+ }
+ });
+
+ mGestureDetector.setIsLongpressEnabled(false);
+ }
+
+ public void setOnKeyboardActionListener(OnKeyboardActionListener listener) {
+ mKeyboardActionListener = listener;
+ }
+
+ /**
+ * Returns the {@link OnKeyboardActionListener} object.
+ * @return the listener attached to this keyboard
+ */
+ protected OnKeyboardActionListener getOnKeyboardActionListener() {
+ return mKeyboardActionListener;
+ }
+
+ /**
+ * Attaches a keyboard to this view. The keyboard can be switched at any time and the
+ * view will re-layout itself to accommodate the keyboard.
+ * @see Keyboard
+ * @see #getKeyboard()
+ * @param keyboard the keyboard to display in this view
+ */
+ public void setKeyboard(Keyboard keyboard) {
+ if (mKeyboard != null) {
+ showPreview(NOT_A_KEY);
+ }
+ // Remove any pending messages, except dismissing preview
+ mHandler.cancelKeyTimersAndPopupPreview();
+ mKeyboard = keyboard;
+ List<Key> keys = mKeyboard.getKeys();
+ mKeys = keys.toArray(new Key[keys.size()]);
+ requestLayout();
+ // Hint to reallocate the buffer if the size changed
+ mKeyboardChanged = true;
+ invalidateAllKeys();
+ computeProximityThreshold(keyboard);
+ mMiniKeyboardCache.clear();
+ // Not really necessary to do every time, but will free up views
+ // Switching to a different keyboard should abort any pending keys so that the key up
+ // doesn't get delivered to the old or new keyboard
+ mAbortKey = true; // Until the next ACTION_DOWN
+ }
+
+ /**
+ * Returns the current keyboard being displayed by this view.
+ * @return the currently attached keyboard
+ * @see #setKeyboard(Keyboard)
+ */
+ public Keyboard getKeyboard() {
+ return mKeyboard;
+ }
+
+ /**
+ * Sets the state of the shift key of the keyboard, if any.
+ * @param shifted whether or not to enable the state of the shift key
+ * @return true if the shift key state changed, false if there was no change
+ */
+ public boolean setShifted(boolean shifted) {
+ if (mKeyboard != null) {
+ if (mKeyboard.setShifted(shifted)) {
+ // The whole keyboard probably needs to be redrawn
+ invalidateAllKeys();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns the state of the shift key of the keyboard, if any.
+ * @return true if the shift is in a pressed state, false otherwise. If there is
+ * no shift key on the keyboard or there is no keyboard attached, it returns false.
+ */
+ public boolean isShifted() {
+ if (mKeyboard != null) {
+ return mKeyboard.isShifted();
+ }
+ return false;
+ }
+
+ /**
+ * Enables or disables the key feedback popup. This is a popup that shows a magnified
+ * version of the depressed key. By default the preview is enabled.
+ * @param previewEnabled whether or not to enable the key feedback popup
+ * @see #isPreviewEnabled()
+ */
+ public void setPreviewEnabled(boolean previewEnabled) {
+ mShowPreview = previewEnabled;
+ }
+
+ /**
+ * Returns the enabled state of the key feedback popup.
+ * @return whether or not the key feedback popup is enabled
+ * @see #setPreviewEnabled(boolean)
+ */
+ public boolean isPreviewEnabled() {
+ return mShowPreview;
+ }
+
+ public int getSymbolColorSheme() {
+ return mSymbolColorScheme;
+ }
+
+ public void setVerticalCorrection(int verticalOffset) {
+ }
+
+ public void setPopupParent(View v) {
+ mPopupParent = v;
+ }
+
+ public void setPopupOffset(int x, int y) {
+ mMiniKeyboardOffsetX = x;
+ mMiniKeyboardOffsetY = y;
+ if (mPreviewPopup.isShowing()) {
+ mPreviewPopup.dismiss();
+ }
+ }
+
+ /**
+ * When enabled, calls to {@link OnKeyboardActionListener#onKey} 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) {
+ mProximityCorrectOn = enabled;
+ }
+
+ /**
+ * Returns true if proximity correction is enabled.
+ */
+ public boolean isProximityCorrectionEnabled() {
+ return mProximityCorrectOn;
+ }
+
+ /**
+ * Popup keyboard close button clicked.
+ * @hide
+ */
+ public void onClick(View v) {
+ dismissPopupKeyboard();
+ }
+
+ protected CharSequence adjustCase(CharSequence label) {
+ if (mKeyboard.isShifted() && label != null && label.length() < 3
+ && Character.isLowerCase(label.charAt(0))) {
+ label = label.toString().toUpperCase();
+ }
+ return label;
+ }
+
+ @Override
+ public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ // Round up a little
+ if (mKeyboard == null) {
+ setMeasuredDimension(
+ getPaddingLeft() + getPaddingRight(), getPaddingTop() + getPaddingBottom());
+ } else {
+ int width = mKeyboard.getMinWidth() + getPaddingLeft() + getPaddingRight();
+ if (MeasureSpec.getSize(widthMeasureSpec) < width + 10) {
+ width = MeasureSpec.getSize(widthMeasureSpec);
+ }
+ setMeasuredDimension(
+ width, mKeyboard.getHeight() + getPaddingTop() + getPaddingBottom());
+ }
+ }
+
+ /**
+ * Compute the average distance between adjacent keys (horizontally and vertically)
+ * and square it to get the proximity threshold. We use a square here and in computing
+ * the touch distance from a key's center to avoid taking a square root.
+ * @param keyboard
+ */
+ private void computeProximityThreshold(Keyboard keyboard) {
+ if (keyboard == null) return;
+ final Key[] keys = mKeys;
+ if (keys == null) return;
+ int length = keys.length;
+ int dimensionSum = 0;
+ for (int i = 0; i < length; i++) {
+ Key key = keys[i];
+ dimensionSum += Math.min(key.width, key.height) + key.gap;
+ }
+ if (dimensionSum < 0 || length == 0) return;
+ mProximityThreshold = (int) (dimensionSum * 1.4f / length);
+ mProximityThreshold *= mProximityThreshold; // Square it
+
+ final float hysteresisPixel = getContext().getResources()
+ .getDimension(R.dimen.key_debounce_hysteresis_distance);
+ mDebouncer = new KeyDebouncer(keys, hysteresisPixel);
+ }
+
+ @Override
+ public void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+ // Release the buffer, if any and it will be reallocated on the next draw
+ mBuffer = null;
+ }
+
+ @Override
+ public void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+ if (mDrawPending || mBuffer == null || mKeyboardChanged) {
+ onBufferDraw();
+ }
+ canvas.drawBitmap(mBuffer, 0, 0, null);
+ }
+
+ private void onBufferDraw() {
+ if (mBuffer == null || mKeyboardChanged) {
+ if (mBuffer == null || mKeyboardChanged &&
+ (mBuffer.getWidth() != getWidth() || mBuffer.getHeight() != getHeight())) {
+ // Make sure our bitmap is at least 1x1
+ final int width = Math.max(1, getWidth());
+ final int height = Math.max(1, getHeight());
+ mBuffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ mCanvas = new Canvas(mBuffer);
+ }
+ invalidateAllKeys();
+ mKeyboardChanged = false;
+ }
+ final Canvas canvas = mCanvas;
+ canvas.clipRect(mDirtyRect, Op.REPLACE);
+
+ if (mKeyboard == null) return;
+
+ final Paint paint = mPaint;
+ final Drawable keyBackground = mKeyBackground;
+ final Rect clipRegion = mClipRegion;
+ final Rect padding = mPadding;
+ final int kbdPaddingLeft = getPaddingLeft();
+ final int kbdPaddingTop = getPaddingTop();
+ final Key[] keys = mKeys;
+ final Key invalidKey = mInvalidatedKey;
+
+ paint.setColor(mKeyTextColor);
+ boolean drawSingleKey = false;
+ if (invalidKey != null && canvas.getClipBounds(clipRegion)) {
+ // Is clipRegion completely contained within the invalidated key?
+ if (invalidKey.x + kbdPaddingLeft - 1 <= clipRegion.left &&
+ invalidKey.y + kbdPaddingTop - 1 <= clipRegion.top &&
+ invalidKey.x + invalidKey.width + kbdPaddingLeft + 1 >= clipRegion.right &&
+ invalidKey.y + invalidKey.height + kbdPaddingTop + 1 >= clipRegion.bottom) {
+ drawSingleKey = true;
+ }
+ }
+ canvas.drawColor(0x00000000, PorterDuff.Mode.CLEAR);
+ final int keyCount = keys.length;
+ for (int i = 0; i < keyCount; i++) {
+ final Key key = keys[i];
+ if (drawSingleKey && invalidKey != key) {
+ continue;
+ }
+ int[] drawableState = key.getCurrentDrawableState();
+ keyBackground.setState(drawableState);
+
+ // Switch the character to uppercase if shift is pressed
+ String label = key.label == null? null : adjustCase(key.label).toString();
+
+ final Rect bounds = keyBackground.getBounds();
+ if (key.width != bounds.right ||
+ key.height != bounds.bottom) {
+ keyBackground.setBounds(0, 0, key.width, key.height);
+ }
+ canvas.translate(key.x + kbdPaddingLeft, key.y + kbdPaddingTop);
+ keyBackground.draw(canvas);
+
+ if (label != null) {
+ // For characters, use large font. For labels like "Done", use small font.
+ if (label.length() > 1 && key.codes.length < 2) {
+ paint.setTextSize(mLabelTextSize);
+ paint.setTypeface(Typeface.DEFAULT_BOLD);
+ } else {
+ paint.setTextSize(mKeyTextSize);
+ paint.setTypeface(mKeyTextStyle);
+ }
+ // Draw a drop shadow for the text
+ paint.setShadowLayer(mShadowRadius, 0, 0, mShadowColor);
+ // Draw the text
+ canvas.drawText(label,
+ (key.width - padding.left - padding.right) / 2
+ + padding.left,
+ (key.height - padding.top - padding.bottom) / 2
+ + (paint.getTextSize() - paint.descent()) / 2 + padding.top,
+ paint);
+ // Turn off drop shadow
+ paint.setShadowLayer(0, 0, 0, 0);
+ } else if (key.icon != null) {
+ final int drawableX = (key.width - padding.left - padding.right
+ - key.icon.getIntrinsicWidth()) / 2 + padding.left;
+ final int drawableY = (key.height - padding.top - padding.bottom
+ - key.icon.getIntrinsicHeight()) / 2 + padding.top;
+ canvas.translate(drawableX, drawableY);
+ key.icon.setBounds(0, 0,
+ key.icon.getIntrinsicWidth(), key.icon.getIntrinsicHeight());
+ key.icon.draw(canvas);
+ canvas.translate(-drawableX, -drawableY);
+ }
+ canvas.translate(-key.x - kbdPaddingLeft, -key.y - kbdPaddingTop);
+ }
+ mInvalidatedKey = null;
+ // Overlay a dark rectangle to dim the keyboard
+ if (mMiniKeyboardOnScreen) {
+ paint.setColor((int) (mBackgroundDimAmount * 0xFF) << 24);
+ canvas.drawRect(0, 0, getWidth(), getHeight(), paint);
+ }
+
+ if (DEBUG) {
+ if (mShowTouchPoints) {
+ int lastX = mDebouncer.getLastX();
+ int lastY = mDebouncer.getLastY();
+ paint.setAlpha(128);
+ paint.setColor(0xFFFF0000);
+ canvas.drawCircle(mStartX, mStartY, 3, paint);
+ canvas.drawLine(mStartX, mStartY, lastX, lastY, paint);
+ paint.setColor(0xFF0000FF);
+ canvas.drawCircle(lastX, lastY, 3, paint);
+ paint.setColor(0xFF00FF00);
+ canvas.drawCircle((mStartX + lastX) / 2, (mStartY + lastY) / 2, 2, paint);
+ }
+ }
+
+ mDrawPending = false;
+ mDirtyRect.setEmpty();
+ }
+
+ private int getKeyIndexAndNearbyCodes(int x, int y, int[] allKeys) {
+ final Key[] keys = mKeys;
+ int primaryIndex = NOT_A_KEY;
+ int closestKey = NOT_A_KEY;
+ int closestKeyDist = mProximityThreshold + 1;
+ Arrays.fill(mDistances, Integer.MAX_VALUE);
+ int [] nearestKeyIndices = mKeyboard.getNearestKeys(x, y);
+ final int keyCount = nearestKeyIndices.length;
+ for (int i = 0; i < keyCount; i++) {
+ final Key key = keys[nearestKeyIndices[i]];
+ int dist = 0;
+ boolean isInside = key.isInside(x,y);
+ if (isInside) {
+ primaryIndex = nearestKeyIndices[i];
+ }
+
+ if (((mProximityCorrectOn
+ && (dist = key.squaredDistanceFrom(x, y)) < mProximityThreshold)
+ || isInside)
+ && key.codes[0] > 32) {
+ // Find insertion point
+ final int nCodes = key.codes.length;
+ if (dist < closestKeyDist) {
+ closestKeyDist = dist;
+ closestKey = nearestKeyIndices[i];
+ }
+
+ if (allKeys == null) continue;
+
+ for (int j = 0; j < mDistances.length; j++) {
+ if (mDistances[j] > dist) {
+ // Make space for nCodes codes
+ System.arraycopy(mDistances, j, mDistances, j + nCodes,
+ mDistances.length - j - nCodes);
+ System.arraycopy(allKeys, j, allKeys, j + nCodes,
+ allKeys.length - j - nCodes);
+ System.arraycopy(key.codes, 0, allKeys, j, nCodes);
+ Arrays.fill(mDistances, j, j + nCodes, dist);
+ break;
+ }
+ }
+ }
+ }
+ if (primaryIndex == NOT_A_KEY) {
+ primaryIndex = closestKey;
+ }
+ return primaryIndex;
+ }
+
+ private void detectAndSendKey(int index, int x, int y, long eventTime) {
+ if (index != NOT_A_KEY && index < mKeys.length) {
+ final Key key = mKeys[index];
+ if (key.text != null) {
+ mKeyboardActionListener.onText(key.text);
+ mKeyboardActionListener.onRelease(NOT_A_KEY);
+ } else {
+ int code = key.codes[0];
+ //TextEntryState.keyPressedAt(key, x, y);
+ int[] codes = new int[MAX_NEARBY_KEYS];
+ Arrays.fill(codes, NOT_A_KEY);
+ getKeyIndexAndNearbyCodes(x, y, codes);
+ // Multi-tap
+ if (mInMultiTap) {
+ if (mTapCount != -1) {
+ mKeyboardActionListener.onKey(Keyboard.KEYCODE_DELETE, KEY_DELETE);
+ } else {
+ mTapCount = 0;
+ }
+ code = key.codes[mTapCount];
+ }
+ /*
+ * Swap the first and second values in the codes array if the primary code is not
+ * the first value but the second value in the array. This happens when key
+ * debouncing is in effect.
+ */
+ if (codes.length >= 2 && codes[0] != code && codes[1] == code) {
+ codes[1] = codes[0];
+ codes[0] = code;
+ }
+ mKeyboardActionListener.onKey(code, codes);
+ mKeyboardActionListener.onRelease(code);
+ }
+ mLastSentIndex = index;
+ mLastTapTime = eventTime;
+ }
+ }
+
+ /**
+ * Handle multi-tap keys by producing the key label for the current multi-tap state.
+ */
+ private CharSequence getPreviewText(Key key) {
+ if (mInMultiTap) {
+ // Multi-tap
+ mPreviewLabel.setLength(0);
+ mPreviewLabel.append((char) key.codes[mTapCount < 0 ? 0 : mTapCount]);
+ return adjustCase(mPreviewLabel);
+ } else {
+ return adjustCase(key.label);
+ }
+ }
+
+ private void showPreview(int keyIndex) {
+ int oldKeyIndex = mCurrentKeyIndex;
+ final PopupWindow previewPopup = mPreviewPopup;
+
+ mCurrentKeyIndex = keyIndex;
+ // Release the old key and press the new key
+ final Key[] keys = mKeys;
+ if (oldKeyIndex != mCurrentKeyIndex) {
+ if (oldKeyIndex != NOT_A_KEY && keys.length > oldKeyIndex) {
+ keys[oldKeyIndex].onReleased(mCurrentKeyIndex == NOT_A_KEY);
+ invalidateKey(oldKeyIndex);
+ }
+ if (mCurrentKeyIndex != NOT_A_KEY && keys.length > mCurrentKeyIndex) {
+ keys[mCurrentKeyIndex].onPressed();
+ invalidateKey(mCurrentKeyIndex);
+ }
+ }
+ // If key changed and preview is on ...
+ if (oldKeyIndex != mCurrentKeyIndex && mShowPreview) {
+ if (keyIndex == NOT_A_KEY) {
+ mHandler.cancelPopupPreview();
+ if (previewPopup.isShowing()) {
+ mHandler.dismissPreview(DELAY_AFTER_PREVIEW);
+ }
+ } else {
+ if (previewPopup.isShowing() && mPreviewText.getVisibility() == VISIBLE) {
+ // Show right away, if it's already visible and finger is moving around
+ showKey(keyIndex);
+ } else {
+ mHandler.popupPreview(keyIndex, DELAY_BEFORE_PREVIEW);
+ }
+ }
+ }
+ }
+
+ private void showKey(final int keyIndex) {
+ final PopupWindow previewPopup = mPreviewPopup;
+ final Key[] keys = mKeys;
+ if (keyIndex < 0 || keyIndex >= mKeys.length) return;
+ Key key = keys[keyIndex];
+ if (key.icon != null) {
+ mPreviewText.setCompoundDrawables(null, null, null,
+ key.iconPreview != null ? key.iconPreview : key.icon);
+ mPreviewText.setText(null);
+ } else {
+ mPreviewText.setCompoundDrawables(null, null, null, null);
+ mPreviewText.setText(getPreviewText(key));
+ if (key.label.length() > 1 && key.codes.length < 2) {
+ mPreviewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mKeyTextSize);
+ mPreviewText.setTypeface(Typeface.DEFAULT_BOLD);
+ } else {
+ mPreviewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mPreviewTextSizeLarge);
+ mPreviewText.setTypeface(mKeyTextStyle);
+ }
+ }
+ mPreviewText.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
+ int popupWidth = Math.max(mPreviewText.getMeasuredWidth(), key.width
+ + mPreviewText.getPaddingLeft() + mPreviewText.getPaddingRight());
+ final int popupHeight = mPreviewHeight;
+ LayoutParams lp = mPreviewText.getLayoutParams();
+ if (lp != null) {
+ lp.width = popupWidth;
+ lp.height = popupHeight;
+ }
+ if (!mPreviewCentered) {
+ mPopupPreviewX = key.x - mPreviewText.getPaddingLeft() + getPaddingLeft();
+ mPopupPreviewY = key.y - popupHeight + mPreviewOffset;
+ } else {
+ // TODO: Fix this if centering is brought back
+ mPopupPreviewX = 160 - mPreviewText.getMeasuredWidth() / 2;
+ mPopupPreviewY = - mPreviewText.getMeasuredHeight();
+ }
+ mHandler.cancelDismissPreview();
+ if (mOffsetInWindow == null) {
+ mOffsetInWindow = new int[2];
+ getLocationInWindow(mOffsetInWindow);
+ mOffsetInWindow[0] += mMiniKeyboardOffsetX; // Offset may be zero
+ mOffsetInWindow[1] += mMiniKeyboardOffsetY; // Offset may be zero
+ int[] mWindowLocation = new int[2];
+ getLocationOnScreen(mWindowLocation);
+ mWindowY = mWindowLocation[1];
+ }
+ // Set the preview background state
+ mPreviewText.getBackground().setState(
+ key.popupResId != 0 ? LONG_PRESSABLE_STATE_SET : EMPTY_STATE_SET);
+ mPopupPreviewX += mOffsetInWindow[0];
+ mPopupPreviewY += mOffsetInWindow[1];
+
+ // If the popup cannot be shown above the key, put it on the side
+ if (mPopupPreviewY + mWindowY < 0) {
+ // If the key you're pressing is on the left side of the keyboard, show the popup on
+ // the right, offset by enough to see at least one key to the left/right.
+ if (key.x + key.width <= getWidth() / 2) {
+ mPopupPreviewX += (int) (key.width * 2.5);
+ } else {
+ mPopupPreviewX -= (int) (key.width * 2.5);
+ }
+ mPopupPreviewY += popupHeight;
+ }
+
+ if (previewPopup.isShowing()) {
+ previewPopup.update(mPopupPreviewX, mPopupPreviewY,
+ popupWidth, popupHeight);
+ } else {
+ previewPopup.setWidth(popupWidth);
+ previewPopup.setHeight(popupHeight);
+ previewPopup.showAtLocation(mPopupParent, Gravity.NO_GRAVITY,
+ mPopupPreviewX, mPopupPreviewY);
+ }
+ mPreviewText.setVisibility(VISIBLE);
+ }
+
+ /**
+ * Requests a redraw of the entire keyboard. Calling {@link #invalidate} is not sufficient
+ * because the keyboard renders the keys to an off-screen buffer and an invalidate() only
+ * draws the cached buffer.
+ * @see #invalidateKey(int)
+ */
+ public void invalidateAllKeys() {
+ mDirtyRect.union(0, 0, getWidth(), getHeight());
+ mDrawPending = true;
+ invalidate();
+ }
+
+ /**
+ * Invalidates a key so that it will be redrawn on the next repaint. Use this method if only
+ * one key is changing it's content. Any changes that affect the position or size of the key
+ * may not be honored.
+ * @param keyIndex the index of the key in the attached {@link Keyboard}.
+ * @see #invalidateAllKeys
+ */
+ public void invalidateKey(int keyIndex) {
+ if (mKeys == null) return;
+ if (keyIndex < 0 || keyIndex >= mKeys.length) {
+ return;
+ }
+ final Key key = mKeys[keyIndex];
+ mInvalidatedKey = key;
+ mDirtyRect.union(key.x + getPaddingLeft(), key.y + getPaddingTop(),
+ key.x + key.width + getPaddingLeft(), key.y + key.height + getPaddingTop());
+ onBufferDraw();
+ invalidate(key.x + getPaddingLeft(), key.y + getPaddingTop(),
+ key.x + key.width + getPaddingLeft(), key.y + key.height + getPaddingTop());
+ }
+
+ private boolean openPopupIfRequired(MotionEvent me) {
+ // Check if we have a popup layout specified first.
+ if (mPopupLayout == 0) {
+ return false;
+ }
+ if (mCurrentKey < 0 || mCurrentKey >= mKeys.length) {
+ return false;
+ }
+
+ Key popupKey = mKeys[mCurrentKey];
+ boolean result = onLongPress(popupKey);
+ if (result) {
+ mAbortKey = true;
+ showPreview(NOT_A_KEY);
+ }
+ return result;
+ }
+
+ /**
+ * Called when a key is long pressed. By default this will open any popup keyboard associated
+ * with this key through the attributes popupLayout and popupCharacters.
+ * @param popupKey the key that was long pressed
+ * @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 popupKey) {
+ int popupKeyboardId = popupKey.popupResId;
+
+ if (popupKeyboardId != 0) {
+ mMiniKeyboardContainer = mMiniKeyboardCache.get(popupKey);
+ if (mMiniKeyboardContainer == null) {
+ LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ mMiniKeyboardContainer = inflater.inflate(mPopupLayout, null);
+ mMiniKeyboard = (LatinKeyboardBaseView) mMiniKeyboardContainer.findViewById(
+ R.id.LatinKeyboardBaseView);
+ View closeButton = mMiniKeyboardContainer.findViewById(
+ R.id.closeButton);
+ if (closeButton != null) closeButton.setOnClickListener(this);
+ mMiniKeyboard.setOnKeyboardActionListener(new OnKeyboardActionListener() {
+ public void onKey(int primaryCode, int[] keyCodes) {
+ mKeyboardActionListener.onKey(primaryCode, keyCodes);
+ dismissPopupKeyboard();
+ }
+
+ public void onText(CharSequence text) {
+ mKeyboardActionListener.onText(text);
+ dismissPopupKeyboard();
+ }
+
+ public void swipeLeft() { }
+ public void swipeRight() { }
+ public void swipeUp() { }
+ public void swipeDown() { }
+ public void onPress(int primaryCode) {
+ mKeyboardActionListener.onPress(primaryCode);
+ }
+ public void onRelease(int primaryCode) {
+ mKeyboardActionListener.onRelease(primaryCode);
+ }
+ });
+ //mInputView.setSuggest(mSuggest);
+ Keyboard keyboard;
+ if (popupKey.popupCharacters != null) {
+ keyboard = new Keyboard(getContext(), popupKeyboardId,
+ popupKey.popupCharacters, -1, getPaddingLeft() + getPaddingRight());
+ } else {
+ keyboard = new Keyboard(getContext(), popupKeyboardId);
+ }
+ mMiniKeyboard.setKeyboard(keyboard);
+ mMiniKeyboard.setPopupParent(this);
+ mMiniKeyboardContainer.measure(
+ MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST),
+ MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.AT_MOST));
+
+ mMiniKeyboardCache.put(popupKey, mMiniKeyboardContainer);
+ } else {
+ mMiniKeyboard = (LatinKeyboardBaseView) mMiniKeyboardContainer.findViewById(
+ R.id.LatinKeyboardBaseView);
+ }
+ if (mWindowOffset == null) {
+ mWindowOffset = new int[2];
+ getLocationInWindow(mWindowOffset);
+ }
+ mPopupX = popupKey.x + getPaddingLeft();
+ mPopupY = popupKey.y + getPaddingTop();
+ mPopupX = mPopupX + popupKey.width - mMiniKeyboardContainer.getMeasuredWidth();
+ mPopupY = mPopupY - mMiniKeyboardContainer.getMeasuredHeight();
+ final int x = mPopupX + mMiniKeyboardContainer.getPaddingRight() + mWindowOffset[0];
+ final int y = mPopupY + mMiniKeyboardContainer.getPaddingBottom() + mWindowOffset[1];
+ mMiniKeyboard.setPopupOffset(x < 0 ? 0 : x, y);
+ mMiniKeyboard.setShifted(isShifted());
+ mPopupKeyboard.setContentView(mMiniKeyboardContainer);
+ mPopupKeyboard.setWidth(mMiniKeyboardContainer.getMeasuredWidth());
+ mPopupKeyboard.setHeight(mMiniKeyboardContainer.getMeasuredHeight());
+ mPopupKeyboard.showAtLocation(this, Gravity.NO_GRAVITY, x, y);
+ mMiniKeyboardOnScreen = true;
+ //mMiniKeyboard.onTouchEvent(getTranslatedEvent(me));
+ invalidateAllKeys();
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent me) {
+ // Convert multi-pointer up/down events to single up/down events to
+ // deal with the typical multi-pointer behavior of two-thumb typing
+ final int pointerCount = me.getPointerCount();
+ final int action = me.getAction();
+ boolean result = false;
+ final long now = me.getEventTime();
+
+ if (pointerCount != mOldPointerCount) {
+ if (pointerCount == 1) {
+ // Send a down event for the latest pointer
+ MotionEvent down = MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN,
+ me.getX(), me.getY(), me.getMetaState());
+ result = onModifiedTouchEvent(down, false);
+ down.recycle();
+ // If it's an up action, then deliver the up as well.
+ if (action == MotionEvent.ACTION_UP) {
+ result = onModifiedTouchEvent(me, true);
+ }
+ } else {
+ // Send an up event for the last pointer
+ MotionEvent up = MotionEvent.obtain(now, now, MotionEvent.ACTION_UP,
+ mOldPointerX, mOldPointerY, me.getMetaState());
+ result = onModifiedTouchEvent(up, true);
+ up.recycle();
+ }
+ } else {
+ if (pointerCount == 1) {
+ result = onModifiedTouchEvent(me, false);
+ mOldPointerX = me.getX();
+ mOldPointerY = me.getY();
+ } else {
+ // Don't do anything when 2 pointers are down and moving.
+ result = true;
+ }
+ }
+ mOldPointerCount = pointerCount;
+
+ return result;
+ }
+
+ private boolean onModifiedTouchEvent(MotionEvent me, boolean possiblePoly) {
+ int touchX = (int) me.getX() - getPaddingLeft();
+ int touchY = (int) me.getY() + mVerticalCorrection - getPaddingTop();
+ final int action = me.getAction();
+ final long eventTime = me.getEventTime();
+ int keyIndex = getKeyIndexAndNearbyCodes(touchX, touchY, null);
+ mPossiblePoly = possiblePoly;
+
+ // Track the last few movements to look for spurious swipes.
+ if (action == MotionEvent.ACTION_DOWN) mSwipeTracker.clear();
+ mSwipeTracker.addMovement(me);
+
+ // Ignore all motion events until a DOWN.
+ if (mAbortKey
+ && action != MotionEvent.ACTION_DOWN && action != MotionEvent.ACTION_CANCEL) {
+ return true;
+ }
+
+ if (mGestureDetector.onTouchEvent(me)) {
+ showPreview(NOT_A_KEY);
+ mHandler.cancelKeyTimers();
+ return true;
+ }
+
+ // Needs to be called after the gesture detector gets a turn, as it may have
+ // displayed the mini keyboard
+ if (mMiniKeyboardOnScreen && action != MotionEvent.ACTION_CANCEL) {
+ return true;
+ }
+
+ switch (action) {
+ case MotionEvent.ACTION_DOWN:
+ mAbortKey = false;
+ mCurrentKey = keyIndex;
+ mDownKey = keyIndex;
+ mStartX = touchX;
+ mStartY = touchY;
+ mDebouncer.startMoveDebouncing(touchX, touchY);
+ mDebouncer.startTimeDebouncing(eventTime);
+ checkMultiTap(eventTime, keyIndex);
+ mKeyboardActionListener.onPress(keyIndex != NOT_A_KEY ?
+ mKeys[keyIndex].codes[0] : 0);
+ if (mCurrentKey >= 0 && mKeys[mCurrentKey].repeatable) {
+ mRepeatKeyIndex = mCurrentKey;
+ mHandler.startKeyRepeatTimer(REPEAT_START_DELAY);
+ repeatKey();
+ // Delivering the key could have caused an abort
+ if (mAbortKey) {
+ mRepeatKeyIndex = NOT_A_KEY;
+ break;
+ }
+ }
+ if (mCurrentKey != NOT_A_KEY) {
+ mHandler.startLongPressTimer(me, LONGPRESS_TIMEOUT);
+ }
+ showPreview(keyIndex);
+ break;
+
+ case MotionEvent.ACTION_MOVE:
+ boolean continueLongPress = false;
+ if (keyIndex != NOT_A_KEY) {
+ if (mCurrentKey == NOT_A_KEY) {
+ mCurrentKey = keyIndex;
+ mDebouncer.updateTimeDebouncing(eventTime);
+ } else if (mDebouncer.isMinorMoveBounce(touchX, touchY, keyIndex,
+ mCurrentKey)) {
+ mDebouncer.updateTimeDebouncing(eventTime);
+ continueLongPress = true;
+ } else if (mRepeatKeyIndex == NOT_A_KEY) {
+ resetMultiTap();
+ mDebouncer.resetTimeDebouncing(eventTime, mCurrentKey);
+ mDebouncer.resetMoveDebouncing();
+ mCurrentKey = keyIndex;
+ }
+ }
+ if (!continueLongPress) {
+ // Cancel old longpress
+ mHandler.cancelLongPressTimer();
+ // Start new longpress if key has changed
+ if (keyIndex != NOT_A_KEY) {
+ mHandler.startLongPressTimer(me, LONGPRESS_TIMEOUT);
+ }
+ }
+ /*
+ * While time debouncing is in effect, mCurrentKey holds the new key and mDebouncer
+ * holds the last key. At ACTION_UP event if time debouncing will be in effect
+ * eventually, the last key should be sent as the result. In such case mCurrentKey
+ * should not be showed as popup preview.
+ */
+ showPreview(mDebouncer.isMinorTimeBounce() ? mDebouncer.getLastKey() : mCurrentKey);
+ break;
+
+ case MotionEvent.ACTION_UP:
+ mHandler.cancelKeyTimersAndPopupPreview();
+ if (mDebouncer.isMinorMoveBounce(touchX, touchY, keyIndex, mCurrentKey)) {
+ mDebouncer.updateTimeDebouncing(eventTime);
+ } else {
+ resetMultiTap();
+ mDebouncer.resetTimeDebouncing(eventTime, mCurrentKey);
+ mCurrentKey = keyIndex;
+ }
+ if (mDebouncer.isMinorTimeBounce()) {
+ mCurrentKey = mDebouncer.getLastKey();
+ touchX = mDebouncer.getLastCodeX();
+ touchY = mDebouncer.getLastCodeY();
+ }
+ showPreview(NOT_A_KEY);
+ // If we're not on a repeating key (which sends on a DOWN event)
+ if (mRepeatKeyIndex == NOT_A_KEY && !mMiniKeyboardOnScreen && !mAbortKey) {
+ detectAndSendKey(mCurrentKey, touchX, touchY, eventTime);
+ }
+ invalidateKey(keyIndex);
+ mRepeatKeyIndex = NOT_A_KEY;
+ break;
+
+ case MotionEvent.ACTION_CANCEL:
+ mHandler.cancelKeyTimersAndPopupPreview();
+ dismissPopupKeyboard();
+ mAbortKey = true;
+ showPreview(NOT_A_KEY);
+ invalidateKey(mCurrentKey);
+ break;
+ }
+ mDebouncer.updateMoveDebouncing(touchX, touchY);
+ return true;
+ }
+
+ private boolean repeatKey() {
+ Key key = mKeys[mRepeatKeyIndex];
+ detectAndSendKey(mCurrentKey, key.x, key.y, mLastTapTime);
+ return true;
+ }
+
+ protected void swipeRight() {
+ mKeyboardActionListener.swipeRight();
+ }
+
+ protected void swipeLeft() {
+ mKeyboardActionListener.swipeLeft();
+ }
+
+ protected void swipeUp() {
+ mKeyboardActionListener.swipeUp();
+ }
+
+ protected void swipeDown() {
+ mKeyboardActionListener.swipeDown();
+ }
+
+ public void closing() {
+ if (mPreviewPopup.isShowing()) {
+ mPreviewPopup.dismiss();
+ }
+ mHandler.cancelAllMessages();
+
+ dismissPopupKeyboard();
+ mBuffer = null;
+ mCanvas = null;
+ mMiniKeyboardCache.clear();
+ }
+
+ @Override
+ public void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ closing();
+ }
+
+ private void dismissPopupKeyboard() {
+ if (mPopupKeyboard.isShowing()) {
+ mPopupKeyboard.dismiss();
+ mMiniKeyboardOnScreen = false;
+ invalidateAllKeys();
+ }
+ }
+
+ public boolean handleBack() {
+ if (mPopupKeyboard.isShowing()) {
+ dismissPopupKeyboard();
+ return true;
+ }
+ return false;
+ }
+
+ private void resetMultiTap() {
+ mLastSentIndex = NOT_A_KEY;
+ mTapCount = 0;
+ mLastTapTime = -1;
+ mInMultiTap = false;
+ }
+
+ private void checkMultiTap(long eventTime, int keyIndex) {
+ if (keyIndex == NOT_A_KEY) return;
+ Key key = mKeys[keyIndex];
+ if (key.codes.length > 1) {
+ mInMultiTap = true;
+ if (eventTime < mLastTapTime + MULTITAP_INTERVAL
+ && keyIndex == mLastSentIndex) {
+ mTapCount = (mTapCount + 1) % key.codes.length;
+ return;
+ } else {
+ mTapCount = -1;
+ return;
+ }
+ }
+ if (eventTime > mLastTapTime + MULTITAP_INTERVAL || keyIndex != mLastSentIndex) {
+ resetMultiTap();
+ }
+ }
+
+ private static class SwipeTracker {
+
+ static final int NUM_PAST = 4;
+ static final int LONGEST_PAST_TIME = 200;
+
+ final float mPastX[] = new float[NUM_PAST];
+ final float mPastY[] = new float[NUM_PAST];
+ final long mPastTime[] = new long[NUM_PAST];
+
+ float mYVelocity;
+ float mXVelocity;
+
+ public void clear() {
+ mPastTime[0] = 0;
+ }
+
+ public void addMovement(MotionEvent ev) {
+ long time = ev.getEventTime();
+ final int N = ev.getHistorySize();
+ for (int i=0; i<N; i++) {
+ addPoint(ev.getHistoricalX(i), ev.getHistoricalY(i),
+ ev.getHistoricalEventTime(i));
+ }
+ addPoint(ev.getX(), ev.getY(), time);
+ }
+
+ private void addPoint(float x, float y, long time) {
+ int drop = -1;
+ int i;
+ final long[] pastTime = mPastTime;
+ for (i=0; i<NUM_PAST; i++) {
+ if (pastTime[i] == 0) {
+ break;
+ } else if (pastTime[i] < time-LONGEST_PAST_TIME) {
+ drop = i;
+ }
+ }
+ if (i == NUM_PAST && drop < 0) {
+ drop = 0;
+ }
+ if (drop == i) drop--;
+ final float[] pastX = mPastX;
+ final float[] pastY = mPastY;
+ if (drop >= 0) {
+ final int start = drop+1;
+ final int count = NUM_PAST-drop-1;
+ System.arraycopy(pastX, start, pastX, 0, count);
+ System.arraycopy(pastY, start, pastY, 0, count);
+ System.arraycopy(pastTime, start, pastTime, 0, count);
+ i -= (drop+1);
+ }
+ pastX[i] = x;
+ pastY[i] = y;
+ pastTime[i] = time;
+ i++;
+ if (i < NUM_PAST) {
+ pastTime[i] = 0;
+ }
+ }
+
+ public void computeCurrentVelocity(int units) {
+ computeCurrentVelocity(units, Float.MAX_VALUE);
+ }
+
+ public void computeCurrentVelocity(int units, float maxVelocity) {
+ final float[] pastX = mPastX;
+ final float[] pastY = mPastY;
+ final long[] pastTime = mPastTime;
+
+ final float oldestX = pastX[0];
+ final float oldestY = pastY[0];
+ final long oldestTime = pastTime[0];
+ float accumX = 0;
+ float accumY = 0;
+ int N=0;
+ while (N < NUM_PAST) {
+ if (pastTime[N] == 0) {
+ break;
+ }
+ N++;
+ }
+
+ for (int i=1; i < N; i++) {
+ final int dur = (int)(pastTime[i] - oldestTime);
+ if (dur == 0) continue;
+ float dist = pastX[i] - oldestX;
+ float vel = (dist/dur) * units; // pixels/frame.
+ if (accumX == 0) accumX = vel;
+ else accumX = (accumX + vel) * .5f;
+
+ dist = pastY[i] - oldestY;
+ vel = (dist/dur) * units; // pixels/frame.
+ if (accumY == 0) accumY = vel;
+ else accumY = (accumY + vel) * .5f;
+ }
+ mXVelocity = accumX < 0.0f ? Math.max(accumX, -maxVelocity)
+ : Math.min(accumX, maxVelocity);
+ mYVelocity = accumY < 0.0f ? Math.max(accumY, -maxVelocity)
+ : Math.min(accumY, maxVelocity);
+ }
+
+ public float getXVelocity() {
+ return mXVelocity;
+ }
+
+ public float getYVelocity() {
+ return mYVelocity;
+ }
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/LatinKeyboardView.java b/java/src/com/android/inputmethod/latin/LatinKeyboardView.java
index 74fc475e6..bce2cde25 100644
--- a/java/src/com/android/inputmethod/latin/LatinKeyboardView.java
+++ b/java/src/com/android/inputmethod/latin/LatinKeyboardView.java
@@ -22,8 +22,6 @@ import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.inputmethodservice.Keyboard;
-import android.inputmethodservice.KeyboardView;
-import android.inputmethodservice.KeyboardView.OnKeyboardActionListener;
import android.inputmethodservice.Keyboard.Key;
import android.os.Handler;
import android.os.Message;
@@ -33,7 +31,7 @@ import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.widget.PopupWindow;
-public class LatinKeyboardView extends KeyboardView {
+public class LatinKeyboardView extends LatinKeyboardBaseView {
static final int KEYCODE_OPTIONS = -100;
static final int KEYCODE_SHIFT_LONGPRESS = -101;
@@ -66,6 +64,8 @@ public class LatinKeyboardView extends KeyboardView {
/** The y coordinate of the last row */
private int mLastRowY;
+ private int mExtensionLayoutResId = 0;
+
public LatinKeyboardView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@@ -78,6 +78,10 @@ public class LatinKeyboardView extends KeyboardView {
mPhoneKeyboard = phoneKeyboard;
}
+ public void setExtentionLayoutResId (int id) {
+ mExtensionLayoutResId = id;
+ }
+
@Override
public void setKeyboard(Keyboard k) {
super.setKeyboard(k);
@@ -107,6 +111,19 @@ public class LatinKeyboardView extends KeyboardView {
}
}
+ @Override
+ protected CharSequence adjustCase(CharSequence label) {
+ Keyboard keyboard = getKeyboard();
+ if (keyboard.isShifted()
+ && keyboard instanceof LatinKeyboard
+ && ((LatinKeyboard) keyboard).isAlphaKeyboard()
+ && label != null && label.length() < 3
+ && Character.isLowerCase(label.charAt(0))) {
+ label = label.toString().toUpperCase();
+ }
+ return label;
+ }
+
/**
* This function checks to see if we need to handle any sudden jumps in the pointer location
* that could be due to a multi-touch being treated as a move by the firmware or hardware.
@@ -295,7 +312,8 @@ public class LatinKeyboardView extends KeyboardView {
mExtensionPopup.setBackgroundDrawable(null);
LayoutInflater li = (LayoutInflater) getContext().getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
- mExtension = (LatinKeyboardView) li.inflate(R.layout.input_trans, null);
+ mExtension = (LatinKeyboardView) li.inflate(mExtensionLayoutResId == 0 ?
+ R.layout.input_trans : mExtensionLayoutResId, null);
mExtension.setExtensionType(true);
mExtension.setOnKeyboardActionListener(
new ExtensionKeyboardListener(getOnKeyboardActionListener()));
@@ -452,27 +470,39 @@ public class LatinKeyboardView extends KeyboardView {
}
}
}
-
- void startPlaying(String s) {
- if (!DEBUG_AUTO_PLAY) return;
- if (s == null) return;
- mStringToPlay = s.toLowerCase();
- mPlaying = true;
- mDownDelivered = false;
- mStringIndex = 0;
- mHandler2.sendEmptyMessageDelayed(MSG_TOUCH_DOWN, 10);
+
+ public void startPlaying(String s) {
+ if (DEBUG_AUTO_PLAY) {
+ if (s == null) return;
+ mStringToPlay = s.toLowerCase();
+ mPlaying = true;
+ mDownDelivered = false;
+ mStringIndex = 0;
+ mHandler2.sendEmptyMessageDelayed(MSG_TOUCH_DOWN, 10);
+ }
}
@Override
public void draw(Canvas c) {
- super.draw(c);
- if (DEBUG_AUTO_PLAY && mPlaying) {
- mHandler2.removeMessages(MSG_TOUCH_DOWN);
- mHandler2.removeMessages(MSG_TOUCH_UP);
- if (mDownDelivered) {
- mHandler2.sendEmptyMessageDelayed(MSG_TOUCH_UP, 20);
- } else {
- mHandler2.sendEmptyMessageDelayed(MSG_TOUCH_DOWN, 20);
+ LatinIMEUtil.GCUtils.getInstance().reset();
+ boolean tryGC = true;
+ for (int i = 0; i < LatinIMEUtil.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) {
+ try {
+ super.draw(c);
+ tryGC = false;
+ } catch (OutOfMemoryError e) {
+ tryGC = LatinIMEUtil.GCUtils.getInstance().tryGCOrWait("LatinKeyboardView", e);
+ }
+ }
+ if (DEBUG_AUTO_PLAY) {
+ if (mPlaying) {
+ mHandler2.removeMessages(MSG_TOUCH_DOWN);
+ mHandler2.removeMessages(MSG_TOUCH_UP);
+ if (mDownDelivered) {
+ mHandler2.sendEmptyMessageDelayed(MSG_TOUCH_UP, 20);
+ } else {
+ mHandler2.sendEmptyMessageDelayed(MSG_TOUCH_DOWN, 20);
+ }
}
}
if (DEBUG_LINE) {
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index a70bea003..cfb691021 100755
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -16,18 +16,17 @@
package com.android.inputmethod.latin;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
import android.content.Context;
import android.text.AutoText;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import com.android.inputmethod.latin.WordComposer;
-
/**
* This class loads a dictionary and provides a list of suggestions for a given sequence of
* characters. This includes corrections and completions.
@@ -35,9 +34,36 @@ import com.android.inputmethod.latin.WordComposer;
*/
public class Suggest implements Dictionary.WordCallback {
+ private static final String TAG = "Suggest";
+
+ public static final int APPROX_MAX_WORD_LENGTH = 32;
+
public static final int CORRECTION_NONE = 0;
public static final int CORRECTION_BASIC = 1;
public static final int CORRECTION_FULL = 2;
+ public static final int CORRECTION_FULL_BIGRAM = 3;
+
+ /**
+ * Words that appear in both bigram and unigram data gets multiplier ranging from
+ * BIGRAM_MULTIPLIER_MIN to BIGRAM_MULTIPLIER_MAX depending on the frequency score from
+ * bigram data.
+ */
+ public static final double BIGRAM_MULTIPLIER_MIN = 1.2;
+ public static final double BIGRAM_MULTIPLIER_MAX = 1.5;
+
+ /**
+ * Maximum possible bigram frequency. Will depend on how many bits are being used in data
+ * structure. Maximum bigram freqeuncy will get the BIGRAM_MULTIPLIER_MAX as the multiplier.
+ */
+ public static final int MAXIMUM_BIGRAM_FREQUENCY = 127;
+
+ 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_AUTO = 3;
+ public static final int DIC_CONTACTS = 4;
+ // If you add a type of dictionary, increment DIC_TYPE_LAST_ID
+ public static final int DIC_TYPE_LAST_ID = 4;
static final int LARGE_DICTIONARY_THRESHOLD = 200 * 1000;
@@ -49,11 +75,16 @@ public class Suggest implements Dictionary.WordCallback {
private Dictionary mContactsDictionary;
+ private Dictionary mUserBigramDictionary;
+
private int mPrefMaxSuggestions = 12;
+ private int mPrefMaxBigrams = 255;
private boolean mAutoTextEnabled;
private int[] mPriorities = new int[mPrefMaxSuggestions];
+ private int[] mBigramPriorities = new int[mPrefMaxBigrams];
+
// Handle predictive correction for only the first 1280 characters for performance reasons
// If we support scripts that need latin characters beyond that, we should probably use some
// kind of a sparse array or language specific list with a mapping lookup table.
@@ -61,6 +92,7 @@ public class Suggest implements Dictionary.WordCallback {
// latin characters.
private int[] mNextLettersFrequencies = new int[1280];
private ArrayList<CharSequence> mSuggestions = new ArrayList<CharSequence>();
+ private ArrayList<CharSequence> mBigramSuggestions = new ArrayList<CharSequence>();
private ArrayList<CharSequence> mStringPool = new ArrayList<CharSequence>();
private boolean mHaveCorrection;
private CharSequence mOriginalWord;
@@ -69,11 +101,19 @@ public class Suggest implements Dictionary.WordCallback {
private int mCorrectionMode = CORRECTION_BASIC;
+ public Suggest(Context context, int[] dictionaryResId) {
+ mMainDict = new BinaryDictionary(context, dictionaryResId, DIC_MAIN);
+ initPool();
+ }
+
+ public Suggest(Context context, ByteBuffer byteBuffer) {
+ mMainDict = new BinaryDictionary(context, byteBuffer, DIC_MAIN);
+ initPool();
+ }
- public Suggest(Context context, int dictionaryResId) {
- mMainDict = new BinaryDictionary(context, dictionaryResId);
+ private void initPool() {
for (int i = 0; i < mPrefMaxSuggestions; i++) {
- StringBuilder sb = new StringBuilder(32);
+ StringBuilder sb = new StringBuilder(getApproxMaxWordLength());
mStringPool.add(sb);
}
}
@@ -94,6 +134,10 @@ public class Suggest implements Dictionary.WordCallback {
return mMainDict.getSize() > LARGE_DICTIONARY_THRESHOLD;
}
+ public int getApproxMaxWordLength() {
+ return APPROX_MAX_WORD_LENGTH;
+ }
+
/**
* Sets an optional user dictionary resource to be loaded. The user dictionary is consulted
* before the main dictionary, if set.
@@ -113,6 +157,10 @@ public class Suggest implements Dictionary.WordCallback {
mAutoDictionary = autoDictionary;
}
+ public void setUserBigramDictionary(Dictionary userBigramDictionary) {
+ mUserBigramDictionary = userBigramDictionary;
+ }
+
/**
* Number of suggestions to generate from the input key sequence. This has
* to be a number between 1 and 100 (inclusive).
@@ -125,9 +173,10 @@ public class Suggest implements Dictionary.WordCallback {
}
mPrefMaxSuggestions = maxSuggestions;
mPriorities = new int[mPrefMaxSuggestions];
- collectGarbage();
+ mBigramPriorities = new int[mPrefMaxBigrams];
+ collectGarbage(mSuggestions, mPrefMaxSuggestions);
while (mStringPool.size() < mPrefMaxSuggestions) {
- StringBuilder sb = new StringBuilder(32);
+ StringBuilder sb = new StringBuilder(getApproxMaxWordLength());
mStringPool.add(sb);
}
}
@@ -162,30 +211,77 @@ public class Suggest implements Dictionary.WordCallback {
/**
* Returns a list of words that match the list of character codes passed in.
* This list will be overwritten the next time this function is called.
- * @param a view for retrieving the context for AutoText
- * @param codes the list of codes. Each list item contains an array of character codes
- * in order of probability where the character at index 0 in the array has the highest
- * probability.
+ * @param view a view for retrieving the context for AutoText
+ * @param wordComposer contains what is currently being typed
+ * @param prevWordForBigram previous word (used only for bigram)
* @return list of suggestions.
*/
public List<CharSequence> getSuggestions(View view, WordComposer wordComposer,
- boolean includeTypedWordIfValid) {
+ boolean includeTypedWordIfValid, CharSequence prevWordForBigram) {
+ LatinImeLogger.onStartSuggestion(prevWordForBigram);
mHaveCorrection = false;
mCapitalize = wordComposer.isCapitalized();
- collectGarbage();
+ collectGarbage(mSuggestions, mPrefMaxSuggestions);
Arrays.fill(mPriorities, 0);
Arrays.fill(mNextLettersFrequencies, 0);
// Save a lowercase version of the original word
mOriginalWord = wordComposer.getTypedWord();
if (mOriginalWord != null) {
- mOriginalWord = mOriginalWord.toString();
- mLowerOriginalWord = mOriginalWord.toString().toLowerCase();
+ final String mOriginalWordString = mOriginalWord.toString();
+ mOriginalWord = mOriginalWordString;
+ mLowerOriginalWord = mOriginalWordString.toLowerCase();
+ // Treating USER_TYPED as UNIGRAM suggestion for logging now.
+ LatinImeLogger.onAddSuggestedWord(mOriginalWordString, Suggest.DIC_USER_TYPED,
+ Dictionary.DataType.UNIGRAM);
} else {
mLowerOriginalWord = "";
}
- // Search the dictionary only if there are at least 2 characters
- if (wordComposer.size() > 1) {
+
+ if (wordComposer.size() == 1 && (mCorrectionMode == CORRECTION_FULL_BIGRAM
+ || mCorrectionMode == CORRECTION_BASIC)) {
+ // At first character typed, search only the bigrams
+ Arrays.fill(mBigramPriorities, 0);
+ collectGarbage(mBigramSuggestions, mPrefMaxBigrams);
+
+ if (!TextUtils.isEmpty(prevWordForBigram)) {
+ CharSequence lowerPrevWord = prevWordForBigram.toString().toLowerCase();
+ if (mMainDict.isValidWord(lowerPrevWord)) {
+ prevWordForBigram = lowerPrevWord;
+ }
+ if (mUserBigramDictionary != null) {
+ mUserBigramDictionary.getBigrams(wordComposer, prevWordForBigram, this,
+ mNextLettersFrequencies);
+ }
+ if (mContactsDictionary != null) {
+ mContactsDictionary.getBigrams(wordComposer, prevWordForBigram, this,
+ mNextLettersFrequencies);
+ }
+ if (mMainDict != null) {
+ mMainDict.getBigrams(wordComposer, prevWordForBigram, this,
+ mNextLettersFrequencies);
+ }
+ char currentChar = wordComposer.getTypedWord().charAt(0);
+ char currentCharUpper = Character.toUpperCase(currentChar);
+ int count = 0;
+ int bigramSuggestionSize = mBigramSuggestions.size();
+ for (int i = 0; i < bigramSuggestionSize; i++) {
+ if (mBigramSuggestions.get(i).charAt(0) == currentChar
+ || mBigramSuggestions.get(i).charAt(0) == currentCharUpper) {
+ int poolSize = mStringPool.size();
+ StringBuilder sb = poolSize > 0 ?
+ (StringBuilder) mStringPool.remove(poolSize - 1)
+ : new StringBuilder(getApproxMaxWordLength());
+ sb.setLength(0);
+ sb.append(mBigramSuggestions.get(i));
+ mSuggestions.add(count++, sb);
+ if (count > mPrefMaxSuggestions) break;
+ }
+ }
+ }
+
+ } else if (wordComposer.size() > 1) {
+ // At second character typed, search the unigrams (scores being affected by bigrams)
if (mUserDictionary != null || mContactsDictionary != null) {
if (mUserDictionary != null) {
mUserDictionary.getWords(wordComposer, this, mNextLettersFrequencies);
@@ -195,26 +291,29 @@ public class Suggest implements Dictionary.WordCallback {
}
if (mSuggestions.size() > 0 && isValidWord(mOriginalWord)
- && mCorrectionMode == CORRECTION_FULL) {
+ && (mCorrectionMode == CORRECTION_FULL
+ || mCorrectionMode == CORRECTION_FULL_BIGRAM)) {
mHaveCorrection = true;
}
}
mMainDict.getWords(wordComposer, this, mNextLettersFrequencies);
- if (mCorrectionMode == CORRECTION_FULL && mSuggestions.size() > 0) {
+ if ((mCorrectionMode == CORRECTION_FULL || mCorrectionMode == CORRECTION_FULL_BIGRAM)
+ && mSuggestions.size() > 0) {
mHaveCorrection = true;
}
}
if (mOriginalWord != null) {
mSuggestions.add(0, mOriginalWord.toString());
}
-
+
// Check if the first suggestion has a minimum number of characters in common
- if (mCorrectionMode == CORRECTION_FULL && mSuggestions.size() > 1) {
+ if (wordComposer.size() > 1 && mSuggestions.size() > 1
+ && (mCorrectionMode == CORRECTION_FULL
+ || mCorrectionMode == CORRECTION_FULL_BIGRAM)) {
if (!haveSufficientCommonality(mLowerOriginalWord, mSuggestions.get(1))) {
mHaveCorrection = false;
}
}
-
if (mAutoTextEnabled) {
int i = 0;
int max = 6;
@@ -240,7 +339,6 @@ public class Suggest implements Dictionary.WordCallback {
i++;
}
}
-
removeDupes();
return mSuggestions;
}
@@ -294,35 +392,68 @@ public class Suggest implements Dictionary.WordCallback {
return false;
}
- public boolean addWord(final char[] word, final int offset, final int length, final int freq) {
+ public boolean addWord(final char[] word, final int offset, final int length, int freq,
+ final int dicTypeId, final Dictionary.DataType dataType) {
+ Dictionary.DataType dataTypeForLog = dataType;
+ ArrayList<CharSequence> suggestions;
+ int[] priorities;
+ int prefMaxSuggestions;
+ if(dataType == Dictionary.DataType.BIGRAM) {
+ suggestions = mBigramSuggestions;
+ priorities = mBigramPriorities;
+ prefMaxSuggestions = mPrefMaxBigrams;
+ } else {
+ suggestions = mSuggestions;
+ priorities = mPriorities;
+ prefMaxSuggestions = mPrefMaxSuggestions;
+ }
+
int pos = 0;
- final int[] priorities = mPriorities;
- final int prefMaxSuggestions = mPrefMaxSuggestions;
+
// Check if it's the same word, only caps are different
if (compareCaseInsensitive(mLowerOriginalWord, word, offset, length)) {
pos = 0;
} else {
+ if (dataType == Dictionary.DataType.UNIGRAM) {
+ // Check if the word was already added before (by bigram data)
+ int bigramSuggestion = searchBigramSuggestion(word,offset,length);
+ if(bigramSuggestion >= 0) {
+ dataTypeForLog = Dictionary.DataType.BIGRAM;
+ // turn freq from bigram into multiplier specified above
+ double multiplier = (((double) mBigramPriorities[bigramSuggestion])
+ / MAXIMUM_BIGRAM_FREQUENCY)
+ * (BIGRAM_MULTIPLIER_MAX - BIGRAM_MULTIPLIER_MIN)
+ + BIGRAM_MULTIPLIER_MIN;
+ /* Log.d(TAG,"bigram num: " + bigramSuggestion
+ + " wordB: " + mBigramSuggestions.get(bigramSuggestion).toString()
+ + " currentPriority: " + freq + " bigramPriority: "
+ + mBigramPriorities[bigramSuggestion]
+ + " multiplier: " + multiplier); */
+ freq = (int)Math.round((freq * multiplier));
+ }
+ }
+
// Check the last one's priority and bail
if (priorities[prefMaxSuggestions - 1] >= freq) return true;
while (pos < prefMaxSuggestions) {
if (priorities[pos] < freq
- || (priorities[pos] == freq && length < mSuggestions
- .get(pos).length())) {
+ || (priorities[pos] == freq && length < suggestions.get(pos).length())) {
break;
}
pos++;
}
}
-
+
if (pos >= prefMaxSuggestions) {
return true;
}
+
System.arraycopy(priorities, pos, priorities, pos + 1,
prefMaxSuggestions - pos - 1);
priorities[pos] = freq;
int poolSize = mStringPool.size();
StringBuilder sb = poolSize > 0 ? (StringBuilder) mStringPool.remove(poolSize - 1)
- : new StringBuilder(32);
+ : new StringBuilder(getApproxMaxWordLength());
sb.setLength(0);
if (mCapitalize) {
sb.append(Character.toUpperCase(word[offset]));
@@ -332,16 +463,38 @@ public class Suggest implements Dictionary.WordCallback {
} else {
sb.append(word, offset, length);
}
- mSuggestions.add(pos, sb);
- if (mSuggestions.size() > prefMaxSuggestions) {
- CharSequence garbage = mSuggestions.remove(prefMaxSuggestions);
+ suggestions.add(pos, sb);
+ if (suggestions.size() > prefMaxSuggestions) {
+ CharSequence garbage = suggestions.remove(prefMaxSuggestions);
if (garbage instanceof StringBuilder) {
mStringPool.add(garbage);
}
+ } else {
+ LatinImeLogger.onAddSuggestedWord(sb.toString(), dicTypeId, dataTypeForLog);
}
return true;
}
+ private int searchBigramSuggestion(final char[] word, final int offset, final int length) {
+ // TODO This is almost O(n^2). Might need fix.
+ // search whether the word appeared in bigram data
+ int bigramSuggestSize = mBigramSuggestions.size();
+ for(int i = 0; i < bigramSuggestSize; i++) {
+ if(mBigramSuggestions.get(i).length() == length) {
+ boolean chk = true;
+ for(int j = 0; j < length; j++) {
+ if(mBigramSuggestions.get(i).charAt(j) != word[offset+j]) {
+ chk = false;
+ break;
+ }
+ }
+ if(chk) return i;
+ }
+ }
+
+ return -1;
+ }
+
public boolean isValidWord(final CharSequence word) {
if (word == null || word.length() == 0) {
return false;
@@ -352,21 +505,21 @@ public class Suggest implements Dictionary.WordCallback {
|| (mContactsDictionary != null && mContactsDictionary.isValidWord(word));
}
- private void collectGarbage() {
+ private void collectGarbage(ArrayList<CharSequence> suggestions, int prefMaxSuggestions) {
int poolSize = mStringPool.size();
- int garbageSize = mSuggestions.size();
- while (poolSize < mPrefMaxSuggestions && garbageSize > 0) {
- CharSequence garbage = mSuggestions.get(garbageSize - 1);
+ int garbageSize = suggestions.size();
+ while (poolSize < prefMaxSuggestions && garbageSize > 0) {
+ CharSequence garbage = suggestions.get(garbageSize - 1);
if (garbage != null && garbage instanceof StringBuilder) {
mStringPool.add(garbage);
poolSize++;
}
garbageSize--;
}
- if (poolSize == mPrefMaxSuggestions + 1) {
+ if (poolSize == prefMaxSuggestions + 1) {
Log.w("Suggest", "String pool got too big: " + poolSize);
}
- mSuggestions.clear();
+ suggestions.clear();
}
public void close() {
diff --git a/java/src/com/android/inputmethod/latin/TextEntryState.java b/java/src/com/android/inputmethod/latin/TextEntryState.java
index d056ceb16..bc7bf3f71 100644
--- a/java/src/com/android/inputmethod/latin/TextEntryState.java
+++ b/java/src/com/android/inputmethod/latin/TextEntryState.java
@@ -43,22 +43,9 @@ public class TextEntryState {
private static int sSessionCount = 0;
private static int sTypedChars;
-
+
private static int sActualChars;
-
- private static final String[] STATES = {
- "Unknown",
- "Start",
- "In word",
- "Accepted default",
- "Picked suggestion",
- "Punc. after word",
- "Punc. after accepted",
- "Space after accepted",
- "Space after picked",
- "Undo commit"
- };
-
+
public static final int STATE_UNKNOWN = 0;
public static final int STATE_START = 1;
public static final int STATE_IN_WORD = 2;
@@ -69,9 +56,11 @@ public class TextEntryState {
public static final int STATE_SPACE_AFTER_ACCEPTED = 7;
public static final int STATE_SPACE_AFTER_PICKED = 8;
public static final int STATE_UNDO_COMMIT = 9;
-
+ public static final int STATE_CORRECTING = 10;
+ public static final int STATE_PICKED_CORRECTION = 11;
+
private static int sState = STATE_UNKNOWN;
-
+
private static FileOutputStream sKeyLocationFile;
private static FileOutputStream sUserActionFile;
@@ -130,8 +119,23 @@ public class TextEntryState {
sTypedChars += typedWord.length();
sActualChars += actualWord.length();
sState = STATE_ACCEPTED_DEFAULT;
+ LatinImeLogger.logOnAutoSuggestion(typedWord.toString(), actualWord.toString());
}
-
+
+ // STATE_ACCEPTED_DEFAULT will be changed to other sub-states
+ // (see "case STATE_ACCEPTED_DEFAULT" in typedCharacter() below),
+ // and should be restored back to STATE_ACCEPTED_DEFAULT after processing for each sub-state.
+ public static void backToAcceptedDefault(CharSequence typedWord) {
+ if (typedWord == null) return;
+ switch (sState) {
+ case STATE_SPACE_AFTER_ACCEPTED:
+ case STATE_PUNCTUATION_AFTER_ACCEPTED:
+ case STATE_IN_WORD:
+ sState = STATE_ACCEPTED_DEFAULT;
+ break;
+ }
+ }
+
public static void acceptedTyped(CharSequence typedWord) {
sWordNotInDictionaryCount++;
sState = STATE_PICKED_SUGGESTION;
@@ -139,12 +143,17 @@ public class TextEntryState {
public static void acceptedSuggestion(CharSequence typedWord, CharSequence actualWord) {
sManualSuggestCount++;
+ int oldState = sState;
if (typedWord.equals(actualWord)) {
acceptedTyped(typedWord);
}
- sState = STATE_PICKED_SUGGESTION;
+ sState = oldState == STATE_CORRECTING ? STATE_PICKED_CORRECTION : STATE_PICKED_SUGGESTION;
}
-
+
+ public static void selectedForCorrection() {
+ sState = STATE_CORRECTING;
+ }
+
public static void typedCharacter(char c, boolean isSeparator) {
boolean isSpace = c == ' ';
switch (sState) {
@@ -166,6 +175,7 @@ public class TextEntryState {
}
break;
case STATE_PICKED_SUGGESTION:
+ case STATE_PICKED_CORRECTION:
if (isSpace) {
sState = STATE_SPACE_AFTER_PICKED;
} else if (isSeparator) {
@@ -192,6 +202,10 @@ public class TextEntryState {
} else {
sState = STATE_IN_WORD;
}
+ break;
+ case STATE_CORRECTING:
+ sState = STATE_START;
+ break;
}
}
@@ -199,6 +213,7 @@ public class TextEntryState {
if (sState == STATE_ACCEPTED_DEFAULT) {
sState = STATE_UNDO_COMMIT;
sAutoSuggestUndoneCount++;
+ LatinImeLogger.logOnAutoSuggestionCanceled();
} else if (sState == STATE_UNDO_COMMIT) {
sState = STATE_IN_WORD;
}
@@ -212,7 +227,11 @@ public class TextEntryState {
public static int getState() {
return sState;
}
-
+
+ public static boolean isCorrecting() {
+ return sState == STATE_CORRECTING || sState == STATE_PICKED_CORRECTION;
+ }
+
public static void keyPressedAt(Key key, int x, int y) {
if (LOGGING && sKeyLocationFile != null && key.codes[0] >= 32) {
String out =
diff --git a/java/src/com/android/inputmethod/latin/UserDictionary.java b/java/src/com/android/inputmethod/latin/UserDictionary.java
index e8ca33af3..3315cf6c9 100644
--- a/java/src/com/android/inputmethod/latin/UserDictionary.java
+++ b/java/src/com/android/inputmethod/latin/UserDictionary.java
@@ -38,7 +38,7 @@ public class UserDictionary extends ExpandableDictionary {
private String mLocale;
public UserDictionary(Context context, String locale) {
- super(context);
+ super(context, Suggest.DIC_USER);
mLocale = locale;
// Perform a managed query. The Activity will handle closing and requerying the cursor
// when needed.
@@ -54,6 +54,7 @@ public class UserDictionary extends ExpandableDictionary {
loadDictionary();
}
+ @Override
public synchronized void close() {
if (mObserver != null) {
getContext().getContentResolver().unregisterContentObserver(mObserver);
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
index 19f714ae7..1ea74847a 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -44,11 +44,20 @@ public class WordComposer {
*/
private boolean mIsCapitalized;
- WordComposer() {
+ public WordComposer() {
mCodes = new ArrayList<int[]>(12);
mTypedWord = new StringBuilder(20);
}
+ WordComposer(WordComposer copy) {
+ mCodes = (ArrayList<int[]>) copy.mCodes.clone();
+ mPreferredWord = copy.mPreferredWord;
+ mTypedWord = new StringBuilder(copy.mTypedWord);
+ mCapsCount = copy.mCapsCount;
+ mAutoCapitalized = copy.mAutoCapitalized;
+ mIsCapitalized = copy.mIsCapitalized;
+ }
+
/**
* Clear out the keys registered so far.
*/
diff --git a/java/src/com/android/inputmethod/voice/LatinIMEWithVoice.java b/java/src/com/android/inputmethod/voice/LatinIMEWithVoice.java
deleted file mode 100644
index ccbf5b6bc..000000000
--- a/java/src/com/android/inputmethod/voice/LatinIMEWithVoice.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2009 Google Inc.
- *
- * 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.voice;
-
-import android.content.Intent;
-
-import com.android.inputmethod.latin.LatinIME;
-
-public class LatinIMEWithVoice extends LatinIME {
- @Override
- protected void launchSettings() {
- launchSettings(LatinIMEWithVoiceSettings.class);
- }
-}
diff --git a/java/src/com/android/inputmethod/voice/LatinIMEWithVoiceSettings.java b/java/src/com/android/inputmethod/voice/LatinIMEWithVoiceSettings.java
deleted file mode 100644
index 13a58e14d..000000000
--- a/java/src/com/android/inputmethod/voice/LatinIMEWithVoiceSettings.java
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright (C) 2009 Google Inc.
- *
- * 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.voice;
-
-import com.android.inputmethod.latin.LatinIMESettings;
-
-public class LatinIMEWithVoiceSettings extends LatinIMESettings {}
diff --git a/java/src/com/android/inputmethod/voice/VoiceInput.java b/java/src/com/android/inputmethod/voice/VoiceInput.java
index ac06ab50d..f24c180d0 100644
--- a/java/src/com/android/inputmethod/voice/VoiceInput.java
+++ b/java/src/com/android/inputmethod/voice/VoiceInput.java
@@ -25,6 +25,7 @@ import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
+import android.os.Parcelable;
import android.speech.RecognitionListener;
import android.speech.SpeechRecognizer;
import android.speech.RecognizerIntent;
@@ -52,6 +53,8 @@ public class VoiceInput implements OnClickListener {
private static final String EXTRA_RECOGNITION_CONTEXT =
"android.speech.extras.RECOGNITION_CONTEXT";
private static final String EXTRA_CALLING_PACKAGE = "calling_package";
+ private static final String EXTRA_ALTERNATES = "android.speech.extra.ALTERNATES";
+ private static final int MAX_ALT_LIST_LENGTH = 6;
private static final String DEFAULT_RECOMMENDED_PACKAGES =
"com.android.mms " +
@@ -63,7 +66,7 @@ public class VoiceInput implements OnClickListener {
// WARNING! Before enabling this, fix the problem with calling getExtractedText() in
// landscape view. It causes Extracted text updates to be rejected due to a token mismatch
- public static boolean ENABLE_WORD_CORRECTIONS = false;
+ public static boolean ENABLE_WORD_CORRECTIONS = true;
// Dummy word suggestion which means "delete current word"
public static final String DELETE_SYMBOL = " \u00D7 "; // times symbol
@@ -73,6 +76,25 @@ public class VoiceInput implements OnClickListener {
private VoiceInputLogger mLogger;
+ // Names of a few extras defined in VoiceSearch's RecognitionController
+ // Note, the version of voicesearch that shipped in Froyo returns the raw
+ // RecognitionClientAlternates protocol buffer under the key "alternates",
+ // so a VS market update must be installed on Froyo devices in order to see
+ // alternatives.
+ private static final String ALTERNATES_BUNDLE = "alternates_bundle";
+
+ // This is copied from the VoiceSearch app.
+ private static final class AlternatesBundleKeys {
+ public static final String ALTERNATES = "alternates";
+ public static final String CONFIDENCE = "confidence";
+ public static final String LENGTH = "length";
+ public static final String MAX_SPAN_LENGTH = "max_span_length";
+ public static final String SPANS = "spans";
+ public static final String SPAN_KEY_DELIMITER = ":";
+ public static final String START = "start";
+ public static final String TEXT = "text";
+ }
+
// Names of a few intent extras defined in VoiceSearch's RecognitionService.
// These let us tweak the endpointer parameters.
private static final String EXTRA_SPEECH_MINIMUM_LENGTH_MILLIS =
@@ -304,12 +326,12 @@ public class VoiceInput implements OnClickListener {
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, "");
intent.putExtra(EXTRA_RECOGNITION_CONTEXT, context.getBundle());
intent.putExtra(EXTRA_CALLING_PACKAGE, "VoiceIME");
+ intent.putExtra(EXTRA_ALTERNATES, true);
intent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS,
SettingsUtil.getSettingsInt(
mContext.getContentResolver(),
SettingsUtil.LATIN_IME_MAX_VOICE_RESULTS,
1));
-
// Get endpointer params from Gservices.
// TODO: Consider caching these values for improved performance on slower devices.
final ContentResolver cr = mContext.getContentResolver();
@@ -563,26 +585,42 @@ public class VoiceInput implements OnClickListener {
public void onResults(Bundle resultsBundle) {
List<String> results = resultsBundle
.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
+ // VS Market update is needed for IME froyo clients to access the alternatesBundle
+ // TODO: verify this.
+ Bundle alternatesBundle = resultsBundle.getBundle(ALTERNATES_BUNDLE);
mState = DEFAULT;
final Map<String, List<CharSequence>> alternatives =
- new HashMap<String, List<CharSequence>>();
- if (results.size() >= 2 && ENABLE_WORD_CORRECTIONS) {
- final String[][] words = new String[results.size()][];
- for (int i = 0; i < words.length; i++) {
- words[i] = results.get(i).split(" ");
- }
-
- for (int key = 0; key < words[0].length; key++) {
- alternatives.put(words[0][key], new ArrayList<CharSequence>());
- for (int alt = 1; alt < words.length; alt++) {
- int keyBegin = key * words[alt].length / words[0].length;
- int keyEnd = (key + 1) * words[alt].length / words[0].length;
-
- for (int i = keyBegin; i < Math.min(words[alt].length, keyEnd); i++) {
- List<CharSequence> altList = alternatives.get(words[0][key]);
- if (!altList.contains(words[alt][i]) && altList.size() < 6) {
- altList.add(words[alt][i]);
+ new HashMap<String, List<CharSequence>>();
+
+ if (ENABLE_WORD_CORRECTIONS && alternatesBundle != null && results.size() > 0) {
+ // Use the top recognition result to map each alternative's start:length to a word.
+ String[] words = results.get(0).split(" ");
+ Bundle spansBundle = alternatesBundle.getBundle(AlternatesBundleKeys.SPANS);
+ for (String key : spansBundle.keySet()) {
+ // Get the word for which these alternates correspond to.
+ Bundle spanBundle = spansBundle.getBundle(key);
+ int start = spanBundle.getInt(AlternatesBundleKeys.START);
+ int length = spanBundle.getInt(AlternatesBundleKeys.LENGTH);
+ // Only keep single-word based alternatives.
+ if (length == 1 && start < words.length) {
+ // Get the alternatives associated with the span.
+ // If a word appears twice in a recognition result,
+ // concatenate the alternatives for the word.
+ List<CharSequence> altList = alternatives.get(words[start]);
+ if (altList == null) {
+ altList = new ArrayList<CharSequence>();
+ alternatives.put(words[start], altList);
+ }
+ Parcelable[] alternatesArr = spanBundle
+ .getParcelableArray(AlternatesBundleKeys.ALTERNATES);
+ for (int j = 0; j < alternatesArr.length &&
+ altList.size() < MAX_ALT_LIST_LENGTH; j++) {
+ Bundle alternateBundle = (Bundle) alternatesArr[j];
+ String alternate = alternateBundle.getString(AlternatesBundleKeys.TEXT);
+ // Don't allow duplicates in the alternates list.
+ if (!altList.contains(alternate)) {
+ altList.add(alternate);
}
}
}
diff --git a/java/src/com/google/android/voicesearch/LatinIMEWithVoice.java b/java/src/com/google/android/voicesearch/LatinIMEWithVoice.java
deleted file mode 100644
index 8a339d14a..000000000
--- a/java/src/com/google/android/voicesearch/LatinIMEWithVoice.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- *
- * Copyright (C) 2009 Google Inc.
- *
- * 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.google.android.voicesearch;
-
-import android.content.Intent;
-
-import com.android.inputmethod.latin.LatinIME;
-
-public class LatinIMEWithVoice extends LatinIME {
- @Override
- protected void launchSettings() {
- launchSettings(LatinIMEWithVoiceSettings.class);
- }
-}
diff --git a/java/src/com/google/android/voicesearch/LatinIMEWithVoiceSettings.java b/java/src/com/google/android/voicesearch/LatinIMEWithVoiceSettings.java
deleted file mode 100644
index a53cebfd9..000000000
--- a/java/src/com/google/android/voicesearch/LatinIMEWithVoiceSettings.java
+++ /dev/null
@@ -1,5 +0,0 @@
-package com.google.android.voicesearch;
-
-import com.android.inputmethod.latin.LatinIMESettings;
-
-public class LatinIMEWithVoiceSettings extends LatinIMESettings {}
diff --git a/native/Android.mk b/native/Android.mk
index 12b6964b9..b2944699c 100644
--- a/native/Android.mk
+++ b/native/Android.mk
@@ -5,19 +5,11 @@ LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
LOCAL_SRC_FILES := \
jni/com_android_inputmethod_latin_BinaryDictionary.cpp \
- src/dictionary.cpp
+ src/dictionary.cpp \
+ src/char_utils.cpp
-LOCAL_C_INCLUDES += \
- external/icu4c/common \
- $(JNI_H_INCLUDE)
-
-LOCAL_LDLIBS := -lm
-
-LOCAL_SHARED_LIBRARIES := \
- libandroid_runtime \
- libcutils \
- libutils \
- libicuuc
+LOCAL_NDK_VERSION := 4
+LOCAL_SDK_VERSION := 8
LOCAL_MODULE := libjni_latinime
diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
index d068f3faf..bf7ec0d1a 100644
--- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
+++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
@@ -15,31 +15,18 @@
** limitations under the License.
*/
-#define LOG_TAG "BinaryDictionary"
-#include "utils/Log.h"
-
#include <stdio.h>
#include <assert.h>
#include <unistd.h>
#include <fcntl.h>
-#include <nativehelper/jni.h>
-#include "utils/AssetManager.h"
-#include "utils/Asset.h"
-
+#include <jni.h>
#include "dictionary.h"
// ----------------------------------------------------------------------------
using namespace latinime;
-using namespace android;
-
-static jfieldID sDescriptorField;
-static jfieldID sAssetManagerNativeField;
-static jmethodID sAddWordMethod;
-static jfieldID sDictLength;
-
//
// helper function to throw an exception
//
@@ -54,35 +41,15 @@ static void throwException(JNIEnv *env, const char* ex, const char* fmt, int dat
}
static jint latinime_BinaryDictionary_open
- (JNIEnv *env, jobject object, jobject assetManager, jstring resourceString,
+ (JNIEnv *env, jobject object, jobject dictDirectBuffer,
jint typedLetterMultiplier, jint fullWordMultiplier)
{
- // Get the native file descriptor from the FileDescriptor object
- AssetManager *am = (AssetManager*) env->GetIntField(assetManager, sAssetManagerNativeField);
- if (!am) {
- LOGE("DICT: Couldn't get AssetManager native peer\n");
- return 0;
- }
- const char *resourcePath = env->GetStringUTFChars(resourceString, NULL);
-
- Asset *dictAsset = am->openNonAsset(resourcePath, Asset::ACCESS_BUFFER);
- if (dictAsset == NULL) {
- LOGE("DICT: Couldn't get asset %s\n", resourcePath);
- env->ReleaseStringUTFChars(resourceString, resourcePath);
- return 0;
- }
-
- void *dict = (void*) dictAsset->getBuffer(false);
+ void *dict = env->GetDirectBufferAddress(dictDirectBuffer);
if (dict == NULL) {
- LOGE("DICT: Dictionary buffer is null\n");
- env->ReleaseStringUTFChars(resourceString, resourcePath);
+ fprintf(stderr, "DICT: Dictionary buffer is null\n");
return 0;
}
Dictionary *dictionary = new Dictionary(dict, typedLetterMultiplier, fullWordMultiplier);
- dictionary->setAsset(dictAsset);
- env->SetIntField(object, sDictLength, (jint) dictAsset->getLength());
-
- env->ReleaseStringUTFChars(resourceString, resourcePath);
return (jint) dictionary;
}
@@ -92,8 +59,7 @@ static int latinime_BinaryDictionary_getSuggestions(
jint maxAlternatives, jint skipPos, jintArray nextLettersArray, jint nextLettersSize)
{
Dictionary *dictionary = (Dictionary*) dict;
- if (dictionary == NULL)
- return 0;
+ if (dictionary == NULL) return 0;
int *frequencies = env->GetIntArrayElements(frequencyArray, NULL);
int *inputCodes = env->GetIntArrayElements(inputArray, NULL);
@@ -101,8 +67,9 @@ static int latinime_BinaryDictionary_getSuggestions(
int *nextLetters = nextLettersArray != NULL ? env->GetIntArrayElements(nextLettersArray, NULL)
: NULL;
- int count = dictionary->getSuggestions(inputCodes, arraySize, (unsigned short*) outputChars, frequencies,
- maxWordLength, maxWords, maxAlternatives, skipPos, nextLetters, nextLettersSize);
+ int count = dictionary->getSuggestions(inputCodes, arraySize, (unsigned short*) outputChars,
+ frequencies, maxWordLength, maxWords, maxAlternatives, skipPos, nextLetters,
+ nextLettersSize);
env->ReleaseIntArrayElements(frequencyArray, frequencies, 0);
env->ReleaseIntArrayElements(inputArray, inputCodes, JNI_ABORT);
@@ -114,6 +81,32 @@ static int latinime_BinaryDictionary_getSuggestions(
return count;
}
+static int latinime_BinaryDictionary_getBigrams
+ (JNIEnv *env, jobject object, jint dict, jcharArray prevWordArray, jint prevWordLength,
+ jintArray inputArray, jint inputArraySize, jcharArray outputArray,
+ jintArray frequencyArray, jint maxWordLength, jint maxBigrams, jint maxAlternatives)
+{
+ Dictionary *dictionary = (Dictionary*) dict;
+ if (dictionary == NULL) return 0;
+
+ jchar *prevWord = env->GetCharArrayElements(prevWordArray, NULL);
+ int *inputCodes = env->GetIntArrayElements(inputArray, NULL);
+ jchar *outputChars = env->GetCharArrayElements(outputArray, NULL);
+ int *frequencies = env->GetIntArrayElements(frequencyArray, NULL);
+
+ int count = dictionary->getBigrams((unsigned short*) prevWord, prevWordLength, inputCodes,
+ inputArraySize, (unsigned short*) outputChars, frequencies, maxWordLength, maxBigrams,
+ maxAlternatives);
+
+ env->ReleaseCharArrayElements(prevWordArray, prevWord, JNI_ABORT);
+ env->ReleaseIntArrayElements(inputArray, inputCodes, JNI_ABORT);
+ env->ReleaseCharArrayElements(outputArray, outputChars, 0);
+ env->ReleaseIntArrayElements(frequencyArray, frequencies, 0);
+
+ return count;
+}
+
+
static jboolean latinime_BinaryDictionary_isValidWord
(JNIEnv *env, jobject object, jint dict, jcharArray wordArray, jint wordLength)
{
@@ -131,18 +124,18 @@ static void latinime_BinaryDictionary_close
(JNIEnv *env, jobject object, jint dict)
{
Dictionary *dictionary = (Dictionary*) dict;
- ((Asset*) dictionary->getAsset())->close();
delete (Dictionary*) dict;
}
// ----------------------------------------------------------------------------
static JNINativeMethod gMethods[] = {
- {"openNative", "(Landroid/content/res/AssetManager;Ljava/lang/String;II)I",
+ {"openNative", "(Ljava/nio/ByteBuffer;II)I",
(void*)latinime_BinaryDictionary_open},
{"closeNative", "(I)V", (void*)latinime_BinaryDictionary_close},
{"getSuggestionsNative", "(I[II[C[IIIII[II)I", (void*)latinime_BinaryDictionary_getSuggestions},
- {"isValidWordNative", "(I[CI)Z", (void*)latinime_BinaryDictionary_isValidWord}
+ {"isValidWordNative", "(I[CI)Z", (void*)latinime_BinaryDictionary_isValidWord},
+ {"getBigramsNative", "(I[CI[II[C[IIII)I", (void*)latinime_BinaryDictionary_getBigrams}
};
static int registerNativeMethods(JNIEnv* env, const char* className,
@@ -167,30 +160,6 @@ static int registerNativeMethods(JNIEnv* env, const char* className,
static int registerNatives(JNIEnv *env)
{
const char* const kClassPathName = "com/android/inputmethod/latin/BinaryDictionary";
- jclass clazz;
-
- clazz = env->FindClass("java/io/FileDescriptor");
- if (clazz == NULL) {
- LOGE("Can't find %s", "java/io/FileDescriptor");
- return -1;
- }
- sDescriptorField = env->GetFieldID(clazz, "descriptor", "I");
-
- clazz = env->FindClass("android/content/res/AssetManager");
- if (clazz == NULL) {
- LOGE("Can't find %s", "java/io/FileDescriptor");
- return -1;
- }
- sAssetManagerNativeField = env->GetFieldID(clazz, "mObject", "I");
-
- // Get the field pointer for the dictionary length
- clazz = env->FindClass(kClassPathName);
- if (clazz == NULL) {
- LOGE("Can't find %s", kClassPathName);
- return -1;
- }
- sDictLength = env->GetFieldID(clazz, "mDictLength", "I");
-
return registerNativeMethods(env,
kClassPathName, gMethods, sizeof(gMethods) / sizeof(gMethods[0]));
}
diff --git a/native/src/char_utils.cpp b/native/src/char_utils.cpp
new file mode 100644
index 000000000..a31a0632c
--- /dev/null
+++ b/native/src/char_utils.cpp
@@ -0,0 +1,899 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+
+namespace latinime {
+
+struct LatinCapitalSmallPair {
+ unsigned short capital;
+ unsigned short small;
+};
+
+// Generated from http://unicode.org/Public/UNIDATA/UnicodeData.txt
+//
+// 1. Run the following code. Bascially taken from
+// Dictionary::toLowerCase(unsigned short c) in dictionary.cpp.
+// Then, get the list of chars where cc != ccc.
+//
+// unsigned short c, cc, ccc, ccc2;
+// for (c = 0; c < 0xFFFF ; c++) {
+// if (c < sizeof(BASE_CHARS) / sizeof(BASE_CHARS[0])) {
+// cc = BASE_CHARS[c];
+// } else {
+// cc = c;
+// }
+//
+// // tolower
+// int isBase = 0;
+// if (cc >='A' && cc <= 'Z') {
+// ccc = (cc | 0x20);
+// ccc2 = ccc;
+// isBase = 1;
+// } else if (cc > 0x7F) {
+// ccc = u_tolower(cc);
+// ccc2 = latin_tolower(cc);
+// } else {
+// ccc = cc;
+// ccc2 = ccc;
+// }
+// if (!isBase && cc != ccc) {
+// wprintf(L" 0x%04X => 0x%04X => 0x%04X %lc => %lc => %lc \n",
+// c, cc, ccc, c, cc, ccc);
+// //assert(ccc == ccc2);
+// }
+// }
+//
+// Initially, started with an empty latin_tolower() as below.
+//
+// unsigned short latin_tolower(unsigned short c) {
+// return c;
+// }
+//
+//
+// 2. Process the list obtained by 1 by the following perl script and apply
+// 'sort -u' as well. Get the SORTED_CHAR_MAP[].
+// Note that '$1' in the perl script is 'cc' in the above C code.
+//
+// while(<>) {
+// / 0x\w* => 0x(\w*) =/;
+// open(HDL, "grep -iw ^" . $1 . " UnicodeData.txt | ");
+// $line = <HDL>;
+// chomp $line;
+// @cols = split(/;/, $line);
+// print " { 0x$1, 0x$cols[13] }, // $cols[1]\n";
+// }
+//
+//
+// 3. Update the latin_tolower() function above with SORTED_CHAR_MAP. Enable
+// the assert(ccc == ccc2) above and confirm the function exits successfully.
+//
+static const struct LatinCapitalSmallPair SORTED_CHAR_MAP[] = {
+ { 0x00C4, 0x00E4 }, // LATIN CAPITAL LETTER A WITH DIAERESIS
+ { 0x00C5, 0x00E5 }, // LATIN CAPITAL LETTER A WITH RING ABOVE
+ { 0x00C6, 0x00E6 }, // LATIN CAPITAL LETTER AE
+ { 0x00D0, 0x00F0 }, // LATIN CAPITAL LETTER ETH
+ { 0x00D5, 0x00F5 }, // LATIN CAPITAL LETTER O WITH TILDE
+ { 0x00D6, 0x00F6 }, // LATIN CAPITAL LETTER O WITH DIAERESIS
+ { 0x00D8, 0x00F8 }, // LATIN CAPITAL LETTER O WITH STROKE
+ { 0x00DC, 0x00FC }, // LATIN CAPITAL LETTER U WITH DIAERESIS
+ { 0x00DE, 0x00FE }, // LATIN CAPITAL LETTER THORN
+ { 0x0110, 0x0111 }, // LATIN CAPITAL LETTER D WITH STROKE
+ { 0x0126, 0x0127 }, // LATIN CAPITAL LETTER H WITH STROKE
+ { 0x0141, 0x0142 }, // LATIN CAPITAL LETTER L WITH STROKE
+ { 0x014A, 0x014B }, // LATIN CAPITAL LETTER ENG
+ { 0x0152, 0x0153 }, // LATIN CAPITAL LIGATURE OE
+ { 0x0166, 0x0167 }, // LATIN CAPITAL LETTER T WITH STROKE
+ { 0x0181, 0x0253 }, // LATIN CAPITAL LETTER B WITH HOOK
+ { 0x0182, 0x0183 }, // LATIN CAPITAL LETTER B WITH TOPBAR
+ { 0x0184, 0x0185 }, // LATIN CAPITAL LETTER TONE SIX
+ { 0x0186, 0x0254 }, // LATIN CAPITAL LETTER OPEN O
+ { 0x0187, 0x0188 }, // LATIN CAPITAL LETTER C WITH HOOK
+ { 0x0189, 0x0256 }, // LATIN CAPITAL LETTER AFRICAN D
+ { 0x018A, 0x0257 }, // LATIN CAPITAL LETTER D WITH HOOK
+ { 0x018B, 0x018C }, // LATIN CAPITAL LETTER D WITH TOPBAR
+ { 0x018E, 0x01DD }, // LATIN CAPITAL LETTER REVERSED E
+ { 0x018F, 0x0259 }, // LATIN CAPITAL LETTER SCHWA
+ { 0x0190, 0x025B }, // LATIN CAPITAL LETTER OPEN E
+ { 0x0191, 0x0192 }, // LATIN CAPITAL LETTER F WITH HOOK
+ { 0x0193, 0x0260 }, // LATIN CAPITAL LETTER G WITH HOOK
+ { 0x0194, 0x0263 }, // LATIN CAPITAL LETTER GAMMA
+ { 0x0196, 0x0269 }, // LATIN CAPITAL LETTER IOTA
+ { 0x0197, 0x0268 }, // LATIN CAPITAL LETTER I WITH STROKE
+ { 0x0198, 0x0199 }, // LATIN CAPITAL LETTER K WITH HOOK
+ { 0x019C, 0x026F }, // LATIN CAPITAL LETTER TURNED M
+ { 0x019D, 0x0272 }, // LATIN CAPITAL LETTER N WITH LEFT HOOK
+ { 0x019F, 0x0275 }, // LATIN CAPITAL LETTER O WITH MIDDLE TILDE
+ { 0x01A2, 0x01A3 }, // LATIN CAPITAL LETTER OI
+ { 0x01A4, 0x01A5 }, // LATIN CAPITAL LETTER P WITH HOOK
+ { 0x01A6, 0x0280 }, // LATIN LETTER YR
+ { 0x01A7, 0x01A8 }, // LATIN CAPITAL LETTER TONE TWO
+ { 0x01A9, 0x0283 }, // LATIN CAPITAL LETTER ESH
+ { 0x01AC, 0x01AD }, // LATIN CAPITAL LETTER T WITH HOOK
+ { 0x01AE, 0x0288 }, // LATIN CAPITAL LETTER T WITH RETROFLEX HOOK
+ { 0x01B1, 0x028A }, // LATIN CAPITAL LETTER UPSILON
+ { 0x01B2, 0x028B }, // LATIN CAPITAL LETTER V WITH HOOK
+ { 0x01B3, 0x01B4 }, // LATIN CAPITAL LETTER Y WITH HOOK
+ { 0x01B5, 0x01B6 }, // LATIN CAPITAL LETTER Z WITH STROKE
+ { 0x01B7, 0x0292 }, // LATIN CAPITAL LETTER EZH
+ { 0x01B8, 0x01B9 }, // LATIN CAPITAL LETTER EZH REVERSED
+ { 0x01BC, 0x01BD }, // LATIN CAPITAL LETTER TONE FIVE
+ { 0x01E4, 0x01E5 }, // LATIN CAPITAL LETTER G WITH STROKE
+ { 0x01EA, 0x01EB }, // LATIN CAPITAL LETTER O WITH OGONEK
+ { 0x01F6, 0x0195 }, // LATIN CAPITAL LETTER HWAIR
+ { 0x01F7, 0x01BF }, // LATIN CAPITAL LETTER WYNN
+ { 0x021C, 0x021D }, // LATIN CAPITAL LETTER YOGH
+ { 0x0220, 0x019E }, // LATIN CAPITAL LETTER N WITH LONG RIGHT LEG
+ { 0x0222, 0x0223 }, // LATIN CAPITAL LETTER OU
+ { 0x0224, 0x0225 }, // LATIN CAPITAL LETTER Z WITH HOOK
+ { 0x0226, 0x0227 }, // LATIN CAPITAL LETTER A WITH DOT ABOVE
+ { 0x022E, 0x022F }, // LATIN CAPITAL LETTER O WITH DOT ABOVE
+ { 0x023A, 0x2C65 }, // LATIN CAPITAL LETTER A WITH STROKE
+ { 0x023B, 0x023C }, // LATIN CAPITAL LETTER C WITH STROKE
+ { 0x023D, 0x019A }, // LATIN CAPITAL LETTER L WITH BAR
+ { 0x023E, 0x2C66 }, // LATIN CAPITAL LETTER T WITH DIAGONAL STROKE
+ { 0x0241, 0x0242 }, // LATIN CAPITAL LETTER GLOTTAL STOP
+ { 0x0243, 0x0180 }, // LATIN CAPITAL LETTER B WITH STROKE
+ { 0x0244, 0x0289 }, // LATIN CAPITAL LETTER U BAR
+ { 0x0245, 0x028C }, // LATIN CAPITAL LETTER TURNED V
+ { 0x0246, 0x0247 }, // LATIN CAPITAL LETTER E WITH STROKE
+ { 0x0248, 0x0249 }, // LATIN CAPITAL LETTER J WITH STROKE
+ { 0x024A, 0x024B }, // LATIN CAPITAL LETTER SMALL Q WITH HOOK TAIL
+ { 0x024C, 0x024D }, // LATIN CAPITAL LETTER R WITH STROKE
+ { 0x024E, 0x024F }, // LATIN CAPITAL LETTER Y WITH STROKE
+ { 0x0370, 0x0371 }, // GREEK CAPITAL LETTER HETA
+ { 0x0372, 0x0373 }, // GREEK CAPITAL LETTER ARCHAIC SAMPI
+ { 0x0376, 0x0377 }, // GREEK CAPITAL LETTER PAMPHYLIAN DIGAMMA
+ { 0x0391, 0x03B1 }, // GREEK CAPITAL LETTER ALPHA
+ { 0x0392, 0x03B2 }, // GREEK CAPITAL LETTER BETA
+ { 0x0393, 0x03B3 }, // GREEK CAPITAL LETTER GAMMA
+ { 0x0394, 0x03B4 }, // GREEK CAPITAL LETTER DELTA
+ { 0x0395, 0x03B5 }, // GREEK CAPITAL LETTER EPSILON
+ { 0x0396, 0x03B6 }, // GREEK CAPITAL LETTER ZETA
+ { 0x0397, 0x03B7 }, // GREEK CAPITAL LETTER ETA
+ { 0x0398, 0x03B8 }, // GREEK CAPITAL LETTER THETA
+ { 0x0399, 0x03B9 }, // GREEK CAPITAL LETTER IOTA
+ { 0x039A, 0x03BA }, // GREEK CAPITAL LETTER KAPPA
+ { 0x039B, 0x03BB }, // GREEK CAPITAL LETTER LAMDA
+ { 0x039C, 0x03BC }, // GREEK CAPITAL LETTER MU
+ { 0x039D, 0x03BD }, // GREEK CAPITAL LETTER NU
+ { 0x039E, 0x03BE }, // GREEK CAPITAL LETTER XI
+ { 0x039F, 0x03BF }, // GREEK CAPITAL LETTER OMICRON
+ { 0x03A0, 0x03C0 }, // GREEK CAPITAL LETTER PI
+ { 0x03A1, 0x03C1 }, // GREEK CAPITAL LETTER RHO
+ { 0x03A3, 0x03C3 }, // GREEK CAPITAL LETTER SIGMA
+ { 0x03A4, 0x03C4 }, // GREEK CAPITAL LETTER TAU
+ { 0x03A5, 0x03C5 }, // GREEK CAPITAL LETTER UPSILON
+ { 0x03A6, 0x03C6 }, // GREEK CAPITAL LETTER PHI
+ { 0x03A7, 0x03C7 }, // GREEK CAPITAL LETTER CHI
+ { 0x03A8, 0x03C8 }, // GREEK CAPITAL LETTER PSI
+ { 0x03A9, 0x03C9 }, // GREEK CAPITAL LETTER OMEGA
+ { 0x03CF, 0x03D7 }, // GREEK CAPITAL KAI SYMBOL
+ { 0x03D8, 0x03D9 }, // GREEK LETTER ARCHAIC KOPPA
+ { 0x03DA, 0x03DB }, // GREEK LETTER STIGMA
+ { 0x03DC, 0x03DD }, // GREEK LETTER DIGAMMA
+ { 0x03DE, 0x03DF }, // GREEK LETTER KOPPA
+ { 0x03E0, 0x03E1 }, // GREEK LETTER SAMPI
+ { 0x03E2, 0x03E3 }, // COPTIC CAPITAL LETTER SHEI
+ { 0x03E4, 0x03E5 }, // COPTIC CAPITAL LETTER FEI
+ { 0x03E6, 0x03E7 }, // COPTIC CAPITAL LETTER KHEI
+ { 0x03E8, 0x03E9 }, // COPTIC CAPITAL LETTER HORI
+ { 0x03EA, 0x03EB }, // COPTIC CAPITAL LETTER GANGIA
+ { 0x03EC, 0x03ED }, // COPTIC CAPITAL LETTER SHIMA
+ { 0x03EE, 0x03EF }, // COPTIC CAPITAL LETTER DEI
+ { 0x03F7, 0x03F8 }, // GREEK CAPITAL LETTER SHO
+ { 0x03FA, 0x03FB }, // GREEK CAPITAL LETTER SAN
+ { 0x03FD, 0x037B }, // GREEK CAPITAL REVERSED LUNATE SIGMA SYMBOL
+ { 0x03FE, 0x037C }, // GREEK CAPITAL DOTTED LUNATE SIGMA SYMBOL
+ { 0x03FF, 0x037D }, // GREEK CAPITAL REVERSED DOTTED LUNATE SIGMA SYMBOL
+ { 0x0402, 0x0452 }, // CYRILLIC CAPITAL LETTER DJE
+ { 0x0404, 0x0454 }, // CYRILLIC CAPITAL LETTER UKRAINIAN IE
+ { 0x0405, 0x0455 }, // CYRILLIC CAPITAL LETTER DZE
+ { 0x0406, 0x0456 }, // CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I
+ { 0x0408, 0x0458 }, // CYRILLIC CAPITAL LETTER JE
+ { 0x0409, 0x0459 }, // CYRILLIC CAPITAL LETTER LJE
+ { 0x040A, 0x045A }, // CYRILLIC CAPITAL LETTER NJE
+ { 0x040B, 0x045B }, // CYRILLIC CAPITAL LETTER TSHE
+ { 0x040F, 0x045F }, // CYRILLIC CAPITAL LETTER DZHE
+ { 0x0410, 0x0430 }, // CYRILLIC CAPITAL LETTER A
+ { 0x0411, 0x0431 }, // CYRILLIC CAPITAL LETTER BE
+ { 0x0412, 0x0432 }, // CYRILLIC CAPITAL LETTER VE
+ { 0x0413, 0x0433 }, // CYRILLIC CAPITAL LETTER GHE
+ { 0x0414, 0x0434 }, // CYRILLIC CAPITAL LETTER DE
+ { 0x0415, 0x0435 }, // CYRILLIC CAPITAL LETTER IE
+ { 0x0416, 0x0436 }, // CYRILLIC CAPITAL LETTER ZHE
+ { 0x0417, 0x0437 }, // CYRILLIC CAPITAL LETTER ZE
+ { 0x0418, 0x0438 }, // CYRILLIC CAPITAL LETTER I
+ { 0x041A, 0x043A }, // CYRILLIC CAPITAL LETTER KA
+ { 0x041B, 0x043B }, // CYRILLIC CAPITAL LETTER EL
+ { 0x041C, 0x043C }, // CYRILLIC CAPITAL LETTER EM
+ { 0x041D, 0x043D }, // CYRILLIC CAPITAL LETTER EN
+ { 0x041E, 0x043E }, // CYRILLIC CAPITAL LETTER O
+ { 0x041F, 0x043F }, // CYRILLIC CAPITAL LETTER PE
+ { 0x0420, 0x0440 }, // CYRILLIC CAPITAL LETTER ER
+ { 0x0421, 0x0441 }, // CYRILLIC CAPITAL LETTER ES
+ { 0x0422, 0x0442 }, // CYRILLIC CAPITAL LETTER TE
+ { 0x0423, 0x0443 }, // CYRILLIC CAPITAL LETTER U
+ { 0x0424, 0x0444 }, // CYRILLIC CAPITAL LETTER EF
+ { 0x0425, 0x0445 }, // CYRILLIC CAPITAL LETTER HA
+ { 0x0426, 0x0446 }, // CYRILLIC CAPITAL LETTER TSE
+ { 0x0427, 0x0447 }, // CYRILLIC CAPITAL LETTER CHE
+ { 0x0428, 0x0448 }, // CYRILLIC CAPITAL LETTER SHA
+ { 0x0429, 0x0449 }, // CYRILLIC CAPITAL LETTER SHCHA
+ { 0x042A, 0x044A }, // CYRILLIC CAPITAL LETTER HARD SIGN
+ { 0x042B, 0x044B }, // CYRILLIC CAPITAL LETTER YERU
+ { 0x042C, 0x044C }, // CYRILLIC CAPITAL LETTER SOFT SIGN
+ { 0x042D, 0x044D }, // CYRILLIC CAPITAL LETTER E
+ { 0x042E, 0x044E }, // CYRILLIC CAPITAL LETTER YU
+ { 0x042F, 0x044F }, // CYRILLIC CAPITAL LETTER YA
+ { 0x0460, 0x0461 }, // CYRILLIC CAPITAL LETTER OMEGA
+ { 0x0462, 0x0463 }, // CYRILLIC CAPITAL LETTER YAT
+ { 0x0464, 0x0465 }, // CYRILLIC CAPITAL LETTER IOTIFIED E
+ { 0x0466, 0x0467 }, // CYRILLIC CAPITAL LETTER LITTLE YUS
+ { 0x0468, 0x0469 }, // CYRILLIC CAPITAL LETTER IOTIFIED LITTLE YUS
+ { 0x046A, 0x046B }, // CYRILLIC CAPITAL LETTER BIG YUS
+ { 0x046C, 0x046D }, // CYRILLIC CAPITAL LETTER IOTIFIED BIG YUS
+ { 0x046E, 0x046F }, // CYRILLIC CAPITAL LETTER KSI
+ { 0x0470, 0x0471 }, // CYRILLIC CAPITAL LETTER PSI
+ { 0x0472, 0x0473 }, // CYRILLIC CAPITAL LETTER FITA
+ { 0x0474, 0x0475 }, // CYRILLIC CAPITAL LETTER IZHITSA
+ { 0x0478, 0x0479 }, // CYRILLIC CAPITAL LETTER UK
+ { 0x047A, 0x047B }, // CYRILLIC CAPITAL LETTER ROUND OMEGA
+ { 0x047C, 0x047D }, // CYRILLIC CAPITAL LETTER OMEGA WITH TITLO
+ { 0x047E, 0x047F }, // CYRILLIC CAPITAL LETTER OT
+ { 0x0480, 0x0481 }, // CYRILLIC CAPITAL LETTER KOPPA
+ { 0x048A, 0x048B }, // CYRILLIC CAPITAL LETTER SHORT I WITH TAIL
+ { 0x048C, 0x048D }, // CYRILLIC CAPITAL LETTER SEMISOFT SIGN
+ { 0x048E, 0x048F }, // CYRILLIC CAPITAL LETTER ER WITH TICK
+ { 0x0490, 0x0491 }, // CYRILLIC CAPITAL LETTER GHE WITH UPTURN
+ { 0x0492, 0x0493 }, // CYRILLIC CAPITAL LETTER GHE WITH STROKE
+ { 0x0494, 0x0495 }, // CYRILLIC CAPITAL LETTER GHE WITH MIDDLE HOOK
+ { 0x0496, 0x0497 }, // CYRILLIC CAPITAL LETTER ZHE WITH DESCENDER
+ { 0x0498, 0x0499 }, // CYRILLIC CAPITAL LETTER ZE WITH DESCENDER
+ { 0x049A, 0x049B }, // CYRILLIC CAPITAL LETTER KA WITH DESCENDER
+ { 0x049C, 0x049D }, // CYRILLIC CAPITAL LETTER KA WITH VERTICAL STROKE
+ { 0x049E, 0x049F }, // CYRILLIC CAPITAL LETTER KA WITH STROKE
+ { 0x04A0, 0x04A1 }, // CYRILLIC CAPITAL LETTER BASHKIR KA
+ { 0x04A2, 0x04A3 }, // CYRILLIC CAPITAL LETTER EN WITH DESCENDER
+ { 0x04A4, 0x04A5 }, // CYRILLIC CAPITAL LIGATURE EN GHE
+ { 0x04A6, 0x04A7 }, // CYRILLIC CAPITAL LETTER PE WITH MIDDLE HOOK
+ { 0x04A8, 0x04A9 }, // CYRILLIC CAPITAL LETTER ABKHASIAN HA
+ { 0x04AA, 0x04AB }, // CYRILLIC CAPITAL LETTER ES WITH DESCENDER
+ { 0x04AC, 0x04AD }, // CYRILLIC CAPITAL LETTER TE WITH DESCENDER
+ { 0x04AE, 0x04AF }, // CYRILLIC CAPITAL LETTER STRAIGHT U
+ { 0x04B0, 0x04B1 }, // CYRILLIC CAPITAL LETTER STRAIGHT U WITH STROKE
+ { 0x04B2, 0x04B3 }, // CYRILLIC CAPITAL LETTER HA WITH DESCENDER
+ { 0x04B4, 0x04B5 }, // CYRILLIC CAPITAL LIGATURE TE TSE
+ { 0x04B6, 0x04B7 }, // CYRILLIC CAPITAL LETTER CHE WITH DESCENDER
+ { 0x04B8, 0x04B9 }, // CYRILLIC CAPITAL LETTER CHE WITH VERTICAL STROKE
+ { 0x04BA, 0x04BB }, // CYRILLIC CAPITAL LETTER SHHA
+ { 0x04BC, 0x04BD }, // CYRILLIC CAPITAL LETTER ABKHASIAN CHE
+ { 0x04BE, 0x04BF }, // CYRILLIC CAPITAL LETTER ABKHASIAN CHE WITH DESCENDER
+ { 0x04C0, 0x04CF }, // CYRILLIC LETTER PALOCHKA
+ { 0x04C3, 0x04C4 }, // CYRILLIC CAPITAL LETTER KA WITH HOOK
+ { 0x04C5, 0x04C6 }, // CYRILLIC CAPITAL LETTER EL WITH TAIL
+ { 0x04C7, 0x04C8 }, // CYRILLIC CAPITAL LETTER EN WITH HOOK
+ { 0x04C9, 0x04CA }, // CYRILLIC CAPITAL LETTER EN WITH TAIL
+ { 0x04CB, 0x04CC }, // CYRILLIC CAPITAL LETTER KHAKASSIAN CHE
+ { 0x04CD, 0x04CE }, // CYRILLIC CAPITAL LETTER EM WITH TAIL
+ { 0x04D4, 0x04D5 }, // CYRILLIC CAPITAL LIGATURE A IE
+ { 0x04D8, 0x04D9 }, // CYRILLIC CAPITAL LETTER SCHWA
+ { 0x04E0, 0x04E1 }, // CYRILLIC CAPITAL LETTER ABKHASIAN DZE
+ { 0x04E8, 0x04E9 }, // CYRILLIC CAPITAL LETTER BARRED O
+ { 0x04F6, 0x04F7 }, // CYRILLIC CAPITAL LETTER GHE WITH DESCENDER
+ { 0x04FA, 0x04FB }, // CYRILLIC CAPITAL LETTER GHE WITH STROKE AND HOOK
+ { 0x04FC, 0x04FD }, // CYRILLIC CAPITAL LETTER HA WITH HOOK
+ { 0x04FE, 0x04FF }, // CYRILLIC CAPITAL LETTER HA WITH STROKE
+ { 0x0500, 0x0501 }, // CYRILLIC CAPITAL LETTER KOMI DE
+ { 0x0502, 0x0503 }, // CYRILLIC CAPITAL LETTER KOMI DJE
+ { 0x0504, 0x0505 }, // CYRILLIC CAPITAL LETTER KOMI ZJE
+ { 0x0506, 0x0507 }, // CYRILLIC CAPITAL LETTER KOMI DZJE
+ { 0x0508, 0x0509 }, // CYRILLIC CAPITAL LETTER KOMI LJE
+ { 0x050A, 0x050B }, // CYRILLIC CAPITAL LETTER KOMI NJE
+ { 0x050C, 0x050D }, // CYRILLIC CAPITAL LETTER KOMI SJE
+ { 0x050E, 0x050F }, // CYRILLIC CAPITAL LETTER KOMI TJE
+ { 0x0510, 0x0511 }, // CYRILLIC CAPITAL LETTER REVERSED ZE
+ { 0x0512, 0x0513 }, // CYRILLIC CAPITAL LETTER EL WITH HOOK
+ { 0x0514, 0x0515 }, // CYRILLIC CAPITAL LETTER LHA
+ { 0x0516, 0x0517 }, // CYRILLIC CAPITAL LETTER RHA
+ { 0x0518, 0x0519 }, // CYRILLIC CAPITAL LETTER YAE
+ { 0x051A, 0x051B }, // CYRILLIC CAPITAL LETTER QA
+ { 0x051C, 0x051D }, // CYRILLIC CAPITAL LETTER WE
+ { 0x051E, 0x051F }, // CYRILLIC CAPITAL LETTER ALEUT KA
+ { 0x0520, 0x0521 }, // CYRILLIC CAPITAL LETTER EL WITH MIDDLE HOOK
+ { 0x0522, 0x0523 }, // CYRILLIC CAPITAL LETTER EN WITH MIDDLE HOOK
+ { 0x0524, 0x0525 }, // CYRILLIC CAPITAL LETTER PE WITH DESCENDER
+ { 0x0531, 0x0561 }, // ARMENIAN CAPITAL LETTER AYB
+ { 0x0532, 0x0562 }, // ARMENIAN CAPITAL LETTER BEN
+ { 0x0533, 0x0563 }, // ARMENIAN CAPITAL LETTER GIM
+ { 0x0534, 0x0564 }, // ARMENIAN CAPITAL LETTER DA
+ { 0x0535, 0x0565 }, // ARMENIAN CAPITAL LETTER ECH
+ { 0x0536, 0x0566 }, // ARMENIAN CAPITAL LETTER ZA
+ { 0x0537, 0x0567 }, // ARMENIAN CAPITAL LETTER EH
+ { 0x0538, 0x0568 }, // ARMENIAN CAPITAL LETTER ET
+ { 0x0539, 0x0569 }, // ARMENIAN CAPITAL LETTER TO
+ { 0x053A, 0x056A }, // ARMENIAN CAPITAL LETTER ZHE
+ { 0x053B, 0x056B }, // ARMENIAN CAPITAL LETTER INI
+ { 0x053C, 0x056C }, // ARMENIAN CAPITAL LETTER LIWN
+ { 0x053D, 0x056D }, // ARMENIAN CAPITAL LETTER XEH
+ { 0x053E, 0x056E }, // ARMENIAN CAPITAL LETTER CA
+ { 0x053F, 0x056F }, // ARMENIAN CAPITAL LETTER KEN
+ { 0x0540, 0x0570 }, // ARMENIAN CAPITAL LETTER HO
+ { 0x0541, 0x0571 }, // ARMENIAN CAPITAL LETTER JA
+ { 0x0542, 0x0572 }, // ARMENIAN CAPITAL LETTER GHAD
+ { 0x0543, 0x0573 }, // ARMENIAN CAPITAL LETTER CHEH
+ { 0x0544, 0x0574 }, // ARMENIAN CAPITAL LETTER MEN
+ { 0x0545, 0x0575 }, // ARMENIAN CAPITAL LETTER YI
+ { 0x0546, 0x0576 }, // ARMENIAN CAPITAL LETTER NOW
+ { 0x0547, 0x0577 }, // ARMENIAN CAPITAL LETTER SHA
+ { 0x0548, 0x0578 }, // ARMENIAN CAPITAL LETTER VO
+ { 0x0549, 0x0579 }, // ARMENIAN CAPITAL LETTER CHA
+ { 0x054A, 0x057A }, // ARMENIAN CAPITAL LETTER PEH
+ { 0x054B, 0x057B }, // ARMENIAN CAPITAL LETTER JHEH
+ { 0x054C, 0x057C }, // ARMENIAN CAPITAL LETTER RA
+ { 0x054D, 0x057D }, // ARMENIAN CAPITAL LETTER SEH
+ { 0x054E, 0x057E }, // ARMENIAN CAPITAL LETTER VEW
+ { 0x054F, 0x057F }, // ARMENIAN CAPITAL LETTER TIWN
+ { 0x0550, 0x0580 }, // ARMENIAN CAPITAL LETTER REH
+ { 0x0551, 0x0581 }, // ARMENIAN CAPITAL LETTER CO
+ { 0x0552, 0x0582 }, // ARMENIAN CAPITAL LETTER YIWN
+ { 0x0553, 0x0583 }, // ARMENIAN CAPITAL LETTER PIWR
+ { 0x0554, 0x0584 }, // ARMENIAN CAPITAL LETTER KEH
+ { 0x0555, 0x0585 }, // ARMENIAN CAPITAL LETTER OH
+ { 0x0556, 0x0586 }, // ARMENIAN CAPITAL LETTER FEH
+ { 0x10A0, 0x2D00 }, // GEORGIAN CAPITAL LETTER AN
+ { 0x10A1, 0x2D01 }, // GEORGIAN CAPITAL LETTER BAN
+ { 0x10A2, 0x2D02 }, // GEORGIAN CAPITAL LETTER GAN
+ { 0x10A3, 0x2D03 }, // GEORGIAN CAPITAL LETTER DON
+ { 0x10A4, 0x2D04 }, // GEORGIAN CAPITAL LETTER EN
+ { 0x10A5, 0x2D05 }, // GEORGIAN CAPITAL LETTER VIN
+ { 0x10A6, 0x2D06 }, // GEORGIAN CAPITAL LETTER ZEN
+ { 0x10A7, 0x2D07 }, // GEORGIAN CAPITAL LETTER TAN
+ { 0x10A8, 0x2D08 }, // GEORGIAN CAPITAL LETTER IN
+ { 0x10A9, 0x2D09 }, // GEORGIAN CAPITAL LETTER KAN
+ { 0x10AA, 0x2D0A }, // GEORGIAN CAPITAL LETTER LAS
+ { 0x10AB, 0x2D0B }, // GEORGIAN CAPITAL LETTER MAN
+ { 0x10AC, 0x2D0C }, // GEORGIAN CAPITAL LETTER NAR
+ { 0x10AD, 0x2D0D }, // GEORGIAN CAPITAL LETTER ON
+ { 0x10AE, 0x2D0E }, // GEORGIAN CAPITAL LETTER PAR
+ { 0x10AF, 0x2D0F }, // GEORGIAN CAPITAL LETTER ZHAR
+ { 0x10B0, 0x2D10 }, // GEORGIAN CAPITAL LETTER RAE
+ { 0x10B1, 0x2D11 }, // GEORGIAN CAPITAL LETTER SAN
+ { 0x10B2, 0x2D12 }, // GEORGIAN CAPITAL LETTER TAR
+ { 0x10B3, 0x2D13 }, // GEORGIAN CAPITAL LETTER UN
+ { 0x10B4, 0x2D14 }, // GEORGIAN CAPITAL LETTER PHAR
+ { 0x10B5, 0x2D15 }, // GEORGIAN CAPITAL LETTER KHAR
+ { 0x10B6, 0x2D16 }, // GEORGIAN CAPITAL LETTER GHAN
+ { 0x10B7, 0x2D17 }, // GEORGIAN CAPITAL LETTER QAR
+ { 0x10B8, 0x2D18 }, // GEORGIAN CAPITAL LETTER SHIN
+ { 0x10B9, 0x2D19 }, // GEORGIAN CAPITAL LETTER CHIN
+ { 0x10BA, 0x2D1A }, // GEORGIAN CAPITAL LETTER CAN
+ { 0x10BB, 0x2D1B }, // GEORGIAN CAPITAL LETTER JIL
+ { 0x10BC, 0x2D1C }, // GEORGIAN CAPITAL LETTER CIL
+ { 0x10BD, 0x2D1D }, // GEORGIAN CAPITAL LETTER CHAR
+ { 0x10BE, 0x2D1E }, // GEORGIAN CAPITAL LETTER XAN
+ { 0x10BF, 0x2D1F }, // GEORGIAN CAPITAL LETTER JHAN
+ { 0x10C0, 0x2D20 }, // GEORGIAN CAPITAL LETTER HAE
+ { 0x10C1, 0x2D21 }, // GEORGIAN CAPITAL LETTER HE
+ { 0x10C2, 0x2D22 }, // GEORGIAN CAPITAL LETTER HIE
+ { 0x10C3, 0x2D23 }, // GEORGIAN CAPITAL LETTER WE
+ { 0x10C4, 0x2D24 }, // GEORGIAN CAPITAL LETTER HAR
+ { 0x10C5, 0x2D25 }, // GEORGIAN CAPITAL LETTER HOE
+ { 0x1E00, 0x1E01 }, // LATIN CAPITAL LETTER A WITH RING BELOW
+ { 0x1E02, 0x1E03 }, // LATIN CAPITAL LETTER B WITH DOT ABOVE
+ { 0x1E04, 0x1E05 }, // LATIN CAPITAL LETTER B WITH DOT BELOW
+ { 0x1E06, 0x1E07 }, // LATIN CAPITAL LETTER B WITH LINE BELOW
+ { 0x1E08, 0x1E09 }, // LATIN CAPITAL LETTER C WITH CEDILLA AND ACUTE
+ { 0x1E0A, 0x1E0B }, // LATIN CAPITAL LETTER D WITH DOT ABOVE
+ { 0x1E0C, 0x1E0D }, // LATIN CAPITAL LETTER D WITH DOT BELOW
+ { 0x1E0E, 0x1E0F }, // LATIN CAPITAL LETTER D WITH LINE BELOW
+ { 0x1E10, 0x1E11 }, // LATIN CAPITAL LETTER D WITH CEDILLA
+ { 0x1E12, 0x1E13 }, // LATIN CAPITAL LETTER D WITH CIRCUMFLEX BELOW
+ { 0x1E14, 0x1E15 }, // LATIN CAPITAL LETTER E WITH MACRON AND GRAVE
+ { 0x1E16, 0x1E17 }, // LATIN CAPITAL LETTER E WITH MACRON AND ACUTE
+ { 0x1E18, 0x1E19 }, // LATIN CAPITAL LETTER E WITH CIRCUMFLEX BELOW
+ { 0x1E1A, 0x1E1B }, // LATIN CAPITAL LETTER E WITH TILDE BELOW
+ { 0x1E1C, 0x1E1D }, // LATIN CAPITAL LETTER E WITH CEDILLA AND BREVE
+ { 0x1E1E, 0x1E1F }, // LATIN CAPITAL LETTER F WITH DOT ABOVE
+ { 0x1E20, 0x1E21 }, // LATIN CAPITAL LETTER G WITH MACRON
+ { 0x1E22, 0x1E23 }, // LATIN CAPITAL LETTER H WITH DOT ABOVE
+ { 0x1E24, 0x1E25 }, // LATIN CAPITAL LETTER H WITH DOT BELOW
+ { 0x1E26, 0x1E27 }, // LATIN CAPITAL LETTER H WITH DIAERESIS
+ { 0x1E28, 0x1E29 }, // LATIN CAPITAL LETTER H WITH CEDILLA
+ { 0x1E2A, 0x1E2B }, // LATIN CAPITAL LETTER H WITH BREVE BELOW
+ { 0x1E2C, 0x1E2D }, // LATIN CAPITAL LETTER I WITH TILDE BELOW
+ { 0x1E2E, 0x1E2F }, // LATIN CAPITAL LETTER I WITH DIAERESIS AND ACUTE
+ { 0x1E30, 0x1E31 }, // LATIN CAPITAL LETTER K WITH ACUTE
+ { 0x1E32, 0x1E33 }, // LATIN CAPITAL LETTER K WITH DOT BELOW
+ { 0x1E34, 0x1E35 }, // LATIN CAPITAL LETTER K WITH LINE BELOW
+ { 0x1E36, 0x1E37 }, // LATIN CAPITAL LETTER L WITH DOT BELOW
+ { 0x1E38, 0x1E39 }, // LATIN CAPITAL LETTER L WITH DOT BELOW AND MACRON
+ { 0x1E3A, 0x1E3B }, // LATIN CAPITAL LETTER L WITH LINE BELOW
+ { 0x1E3C, 0x1E3D }, // LATIN CAPITAL LETTER L WITH CIRCUMFLEX BELOW
+ { 0x1E3E, 0x1E3F }, // LATIN CAPITAL LETTER M WITH ACUTE
+ { 0x1E40, 0x1E41 }, // LATIN CAPITAL LETTER M WITH DOT ABOVE
+ { 0x1E42, 0x1E43 }, // LATIN CAPITAL LETTER M WITH DOT BELOW
+ { 0x1E44, 0x1E45 }, // LATIN CAPITAL LETTER N WITH DOT ABOVE
+ { 0x1E46, 0x1E47 }, // LATIN CAPITAL LETTER N WITH DOT BELOW
+ { 0x1E48, 0x1E49 }, // LATIN CAPITAL LETTER N WITH LINE BELOW
+ { 0x1E4A, 0x1E4B }, // LATIN CAPITAL LETTER N WITH CIRCUMFLEX BELOW
+ { 0x1E4C, 0x1E4D }, // LATIN CAPITAL LETTER O WITH TILDE AND ACUTE
+ { 0x1E4E, 0x1E4F }, // LATIN CAPITAL LETTER O WITH TILDE AND DIAERESIS
+ { 0x1E50, 0x1E51 }, // LATIN CAPITAL LETTER O WITH MACRON AND GRAVE
+ { 0x1E52, 0x1E53 }, // LATIN CAPITAL LETTER O WITH MACRON AND ACUTE
+ { 0x1E54, 0x1E55 }, // LATIN CAPITAL LETTER P WITH ACUTE
+ { 0x1E56, 0x1E57 }, // LATIN CAPITAL LETTER P WITH DOT ABOVE
+ { 0x1E58, 0x1E59 }, // LATIN CAPITAL LETTER R WITH DOT ABOVE
+ { 0x1E5A, 0x1E5B }, // LATIN CAPITAL LETTER R WITH DOT BELOW
+ { 0x1E5C, 0x1E5D }, // LATIN CAPITAL LETTER R WITH DOT BELOW AND MACRON
+ { 0x1E5E, 0x1E5F }, // LATIN CAPITAL LETTER R WITH LINE BELOW
+ { 0x1E60, 0x1E61 }, // LATIN CAPITAL LETTER S WITH DOT ABOVE
+ { 0x1E62, 0x1E63 }, // LATIN CAPITAL LETTER S WITH DOT BELOW
+ { 0x1E64, 0x1E65 }, // LATIN CAPITAL LETTER S WITH ACUTE AND DOT ABOVE
+ { 0x1E66, 0x1E67 }, // LATIN CAPITAL LETTER S WITH CARON AND DOT ABOVE
+ { 0x1E68, 0x1E69 }, // LATIN CAPITAL LETTER S WITH DOT BELOW AND DOT ABOVE
+ { 0x1E6A, 0x1E6B }, // LATIN CAPITAL LETTER T WITH DOT ABOVE
+ { 0x1E6C, 0x1E6D }, // LATIN CAPITAL LETTER T WITH DOT BELOW
+ { 0x1E6E, 0x1E6F }, // LATIN CAPITAL LETTER T WITH LINE BELOW
+ { 0x1E70, 0x1E71 }, // LATIN CAPITAL LETTER T WITH CIRCUMFLEX BELOW
+ { 0x1E72, 0x1E73 }, // LATIN CAPITAL LETTER U WITH DIAERESIS BELOW
+ { 0x1E74, 0x1E75 }, // LATIN CAPITAL LETTER U WITH TILDE BELOW
+ { 0x1E76, 0x1E77 }, // LATIN CAPITAL LETTER U WITH CIRCUMFLEX BELOW
+ { 0x1E78, 0x1E79 }, // LATIN CAPITAL LETTER U WITH TILDE AND ACUTE
+ { 0x1E7A, 0x1E7B }, // LATIN CAPITAL LETTER U WITH MACRON AND DIAERESIS
+ { 0x1E7C, 0x1E7D }, // LATIN CAPITAL LETTER V WITH TILDE
+ { 0x1E7E, 0x1E7F }, // LATIN CAPITAL LETTER V WITH DOT BELOW
+ { 0x1E80, 0x1E81 }, // LATIN CAPITAL LETTER W WITH GRAVE
+ { 0x1E82, 0x1E83 }, // LATIN CAPITAL LETTER W WITH ACUTE
+ { 0x1E84, 0x1E85 }, // LATIN CAPITAL LETTER W WITH DIAERESIS
+ { 0x1E86, 0x1E87 }, // LATIN CAPITAL LETTER W WITH DOT ABOVE
+ { 0x1E88, 0x1E89 }, // LATIN CAPITAL LETTER W WITH DOT BELOW
+ { 0x1E8A, 0x1E8B }, // LATIN CAPITAL LETTER X WITH DOT ABOVE
+ { 0x1E8C, 0x1E8D }, // LATIN CAPITAL LETTER X WITH DIAERESIS
+ { 0x1E8E, 0x1E8F }, // LATIN CAPITAL LETTER Y WITH DOT ABOVE
+ { 0x1E90, 0x1E91 }, // LATIN CAPITAL LETTER Z WITH CIRCUMFLEX
+ { 0x1E92, 0x1E93 }, // LATIN CAPITAL LETTER Z WITH DOT BELOW
+ { 0x1E94, 0x1E95 }, // LATIN CAPITAL LETTER Z WITH LINE BELOW
+ { 0x1E9E, 0x00DF }, // LATIN CAPITAL LETTER SHARP S
+ { 0x1EA0, 0x1EA1 }, // LATIN CAPITAL LETTER A WITH DOT BELOW
+ { 0x1EA2, 0x1EA3 }, // LATIN CAPITAL LETTER A WITH HOOK ABOVE
+ { 0x1EA4, 0x1EA5 }, // LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND ACUTE
+ { 0x1EA6, 0x1EA7 }, // LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND GRAVE
+ { 0x1EA8, 0x1EA9 }, // LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND HOOK ABOVE
+ { 0x1EAA, 0x1EAB }, // LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND TILDE
+ { 0x1EAC, 0x1EAD }, // LATIN CAPITAL LETTER A WITH CIRCUMFLEX AND DOT BELOW
+ { 0x1EAE, 0x1EAF }, // LATIN CAPITAL LETTER A WITH BREVE AND ACUTE
+ { 0x1EB0, 0x1EB1 }, // LATIN CAPITAL LETTER A WITH BREVE AND GRAVE
+ { 0x1EB2, 0x1EB3 }, // LATIN CAPITAL LETTER A WITH BREVE AND HOOK ABOVE
+ { 0x1EB4, 0x1EB5 }, // LATIN CAPITAL LETTER A WITH BREVE AND TILDE
+ { 0x1EB6, 0x1EB7 }, // LATIN CAPITAL LETTER A WITH BREVE AND DOT BELOW
+ { 0x1EB8, 0x1EB9 }, // LATIN CAPITAL LETTER E WITH DOT BELOW
+ { 0x1EBA, 0x1EBB }, // LATIN CAPITAL LETTER E WITH HOOK ABOVE
+ { 0x1EBC, 0x1EBD }, // LATIN CAPITAL LETTER E WITH TILDE
+ { 0x1EBE, 0x1EBF }, // LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND ACUTE
+ { 0x1EC0, 0x1EC1 }, // LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND GRAVE
+ { 0x1EC2, 0x1EC3 }, // LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND HOOK ABOVE
+ { 0x1EC4, 0x1EC5 }, // LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND TILDE
+ { 0x1EC6, 0x1EC7 }, // LATIN CAPITAL LETTER E WITH CIRCUMFLEX AND DOT BELOW
+ { 0x1EC8, 0x1EC9 }, // LATIN CAPITAL LETTER I WITH HOOK ABOVE
+ { 0x1ECA, 0x1ECB }, // LATIN CAPITAL LETTER I WITH DOT BELOW
+ { 0x1ECC, 0x1ECD }, // LATIN CAPITAL LETTER O WITH DOT BELOW
+ { 0x1ECE, 0x1ECF }, // LATIN CAPITAL LETTER O WITH HOOK ABOVE
+ { 0x1ED0, 0x1ED1 }, // LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND ACUTE
+ { 0x1ED2, 0x1ED3 }, // LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND GRAVE
+ { 0x1ED4, 0x1ED5 }, // LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND HOOK ABOVE
+ { 0x1ED6, 0x1ED7 }, // LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND TILDE
+ { 0x1ED8, 0x1ED9 }, // LATIN CAPITAL LETTER O WITH CIRCUMFLEX AND DOT BELOW
+ { 0x1EDA, 0x1EDB }, // LATIN CAPITAL LETTER O WITH HORN AND ACUTE
+ { 0x1EDC, 0x1EDD }, // LATIN CAPITAL LETTER O WITH HORN AND GRAVE
+ { 0x1EDE, 0x1EDF }, // LATIN CAPITAL LETTER O WITH HORN AND HOOK ABOVE
+ { 0x1EE0, 0x1EE1 }, // LATIN CAPITAL LETTER O WITH HORN AND TILDE
+ { 0x1EE2, 0x1EE3 }, // LATIN CAPITAL LETTER O WITH HORN AND DOT BELOW
+ { 0x1EE4, 0x1EE5 }, // LATIN CAPITAL LETTER U WITH DOT BELOW
+ { 0x1EE6, 0x1EE7 }, // LATIN CAPITAL LETTER U WITH HOOK ABOVE
+ { 0x1EE8, 0x1EE9 }, // LATIN CAPITAL LETTER U WITH HORN AND ACUTE
+ { 0x1EEA, 0x1EEB }, // LATIN CAPITAL LETTER U WITH HORN AND GRAVE
+ { 0x1EEC, 0x1EED }, // LATIN CAPITAL LETTER U WITH HORN AND HOOK ABOVE
+ { 0x1EEE, 0x1EEF }, // LATIN CAPITAL LETTER U WITH HORN AND TILDE
+ { 0x1EF0, 0x1EF1 }, // LATIN CAPITAL LETTER U WITH HORN AND DOT BELOW
+ { 0x1EF2, 0x1EF3 }, // LATIN CAPITAL LETTER Y WITH GRAVE
+ { 0x1EF4, 0x1EF5 }, // LATIN CAPITAL LETTER Y WITH DOT BELOW
+ { 0x1EF6, 0x1EF7 }, // LATIN CAPITAL LETTER Y WITH HOOK ABOVE
+ { 0x1EF8, 0x1EF9 }, // LATIN CAPITAL LETTER Y WITH TILDE
+ { 0x1EFA, 0x1EFB }, // LATIN CAPITAL LETTER MIDDLE-WELSH LL
+ { 0x1EFC, 0x1EFD }, // LATIN CAPITAL LETTER MIDDLE-WELSH V
+ { 0x1EFE, 0x1EFF }, // LATIN CAPITAL LETTER Y WITH LOOP
+ { 0x1F08, 0x1F00 }, // GREEK CAPITAL LETTER ALPHA WITH PSILI
+ { 0x1F09, 0x1F01 }, // GREEK CAPITAL LETTER ALPHA WITH DASIA
+ { 0x1F0A, 0x1F02 }, // GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA
+ { 0x1F0B, 0x1F03 }, // GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA
+ { 0x1F0C, 0x1F04 }, // GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA
+ { 0x1F0D, 0x1F05 }, // GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA
+ { 0x1F0E, 0x1F06 }, // GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI
+ { 0x1F0F, 0x1F07 }, // GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI
+ { 0x1F18, 0x1F10 }, // GREEK CAPITAL LETTER EPSILON WITH PSILI
+ { 0x1F19, 0x1F11 }, // GREEK CAPITAL LETTER EPSILON WITH DASIA
+ { 0x1F1A, 0x1F12 }, // GREEK CAPITAL LETTER EPSILON WITH PSILI AND VARIA
+ { 0x1F1B, 0x1F13 }, // GREEK CAPITAL LETTER EPSILON WITH DASIA AND VARIA
+ { 0x1F1C, 0x1F14 }, // GREEK CAPITAL LETTER EPSILON WITH PSILI AND OXIA
+ { 0x1F1D, 0x1F15 }, // GREEK CAPITAL LETTER EPSILON WITH DASIA AND OXIA
+ { 0x1F28, 0x1F20 }, // GREEK CAPITAL LETTER ETA WITH PSILI
+ { 0x1F29, 0x1F21 }, // GREEK CAPITAL LETTER ETA WITH DASIA
+ { 0x1F2A, 0x1F22 }, // GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA
+ { 0x1F2B, 0x1F23 }, // GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA
+ { 0x1F2C, 0x1F24 }, // GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA
+ { 0x1F2D, 0x1F25 }, // GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA
+ { 0x1F2E, 0x1F26 }, // GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI
+ { 0x1F2F, 0x1F27 }, // GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI
+ { 0x1F38, 0x1F30 }, // GREEK CAPITAL LETTER IOTA WITH PSILI
+ { 0x1F39, 0x1F31 }, // GREEK CAPITAL LETTER IOTA WITH DASIA
+ { 0x1F3A, 0x1F32 }, // GREEK CAPITAL LETTER IOTA WITH PSILI AND VARIA
+ { 0x1F3B, 0x1F33 }, // GREEK CAPITAL LETTER IOTA WITH DASIA AND VARIA
+ { 0x1F3C, 0x1F34 }, // GREEK CAPITAL LETTER IOTA WITH PSILI AND OXIA
+ { 0x1F3D, 0x1F35 }, // GREEK CAPITAL LETTER IOTA WITH DASIA AND OXIA
+ { 0x1F3E, 0x1F36 }, // GREEK CAPITAL LETTER IOTA WITH PSILI AND PERISPOMENI
+ { 0x1F3F, 0x1F37 }, // GREEK CAPITAL LETTER IOTA WITH DASIA AND PERISPOMENI
+ { 0x1F48, 0x1F40 }, // GREEK CAPITAL LETTER OMICRON WITH PSILI
+ { 0x1F49, 0x1F41 }, // GREEK CAPITAL LETTER OMICRON WITH DASIA
+ { 0x1F4A, 0x1F42 }, // GREEK CAPITAL LETTER OMICRON WITH PSILI AND VARIA
+ { 0x1F4B, 0x1F43 }, // GREEK CAPITAL LETTER OMICRON WITH DASIA AND VARIA
+ { 0x1F4C, 0x1F44 }, // GREEK CAPITAL LETTER OMICRON WITH PSILI AND OXIA
+ { 0x1F4D, 0x1F45 }, // GREEK CAPITAL LETTER OMICRON WITH DASIA AND OXIA
+ { 0x1F59, 0x1F51 }, // GREEK CAPITAL LETTER UPSILON WITH DASIA
+ { 0x1F5B, 0x1F53 }, // GREEK CAPITAL LETTER UPSILON WITH DASIA AND VARIA
+ { 0x1F5D, 0x1F55 }, // GREEK CAPITAL LETTER UPSILON WITH DASIA AND OXIA
+ { 0x1F5F, 0x1F57 }, // GREEK CAPITAL LETTER UPSILON WITH DASIA AND PERISPOMENI
+ { 0x1F68, 0x1F60 }, // GREEK CAPITAL LETTER OMEGA WITH PSILI
+ { 0x1F69, 0x1F61 }, // GREEK CAPITAL LETTER OMEGA WITH DASIA
+ { 0x1F6A, 0x1F62 }, // GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA
+ { 0x1F6B, 0x1F63 }, // GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA
+ { 0x1F6C, 0x1F64 }, // GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA
+ { 0x1F6D, 0x1F65 }, // GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA
+ { 0x1F6E, 0x1F66 }, // GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI
+ { 0x1F6F, 0x1F67 }, // GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI
+ { 0x1F88, 0x1F80 }, // GREEK CAPITAL LETTER ALPHA WITH PSILI AND PROSGEGRAMMENI
+ { 0x1F89, 0x1F81 }, // GREEK CAPITAL LETTER ALPHA WITH DASIA AND PROSGEGRAMMENI
+ { 0x1F8A, 0x1F82 }, // GREEK CAPITAL LETTER ALPHA WITH PSILI AND VARIA AND PROSGEGRAMMENI
+ { 0x1F8B, 0x1F83 }, // GREEK CAPITAL LETTER ALPHA WITH DASIA AND VARIA AND PROSGEGRAMMENI
+ { 0x1F8C, 0x1F84 }, // GREEK CAPITAL LETTER ALPHA WITH PSILI AND OXIA AND PROSGEGRAMMENI
+ { 0x1F8D, 0x1F85 }, // GREEK CAPITAL LETTER ALPHA WITH DASIA AND OXIA AND PROSGEGRAMMENI
+ { 0x1F8E, 0x1F86 }, // GREEK CAPITAL LETTER ALPHA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI
+ { 0x1F8F, 0x1F87 }, // GREEK CAPITAL LETTER ALPHA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI
+ { 0x1F98, 0x1F90 }, // GREEK CAPITAL LETTER ETA WITH PSILI AND PROSGEGRAMMENI
+ { 0x1F99, 0x1F91 }, // GREEK CAPITAL LETTER ETA WITH DASIA AND PROSGEGRAMMENI
+ { 0x1F9A, 0x1F92 }, // GREEK CAPITAL LETTER ETA WITH PSILI AND VARIA AND PROSGEGRAMMENI
+ { 0x1F9B, 0x1F93 }, // GREEK CAPITAL LETTER ETA WITH DASIA AND VARIA AND PROSGEGRAMMENI
+ { 0x1F9C, 0x1F94 }, // GREEK CAPITAL LETTER ETA WITH PSILI AND OXIA AND PROSGEGRAMMENI
+ { 0x1F9D, 0x1F95 }, // GREEK CAPITAL LETTER ETA WITH DASIA AND OXIA AND PROSGEGRAMMENI
+ { 0x1F9E, 0x1F96 }, // GREEK CAPITAL LETTER ETA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI
+ { 0x1F9F, 0x1F97 }, // GREEK CAPITAL LETTER ETA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI
+ { 0x1FA8, 0x1FA0 }, // GREEK CAPITAL LETTER OMEGA WITH PSILI AND PROSGEGRAMMENI
+ { 0x1FA9, 0x1FA1 }, // GREEK CAPITAL LETTER OMEGA WITH DASIA AND PROSGEGRAMMENI
+ { 0x1FAA, 0x1FA2 }, // GREEK CAPITAL LETTER OMEGA WITH PSILI AND VARIA AND PROSGEGRAMMENI
+ { 0x1FAB, 0x1FA3 }, // GREEK CAPITAL LETTER OMEGA WITH DASIA AND VARIA AND PROSGEGRAMMENI
+ { 0x1FAC, 0x1FA4 }, // GREEK CAPITAL LETTER OMEGA WITH PSILI AND OXIA AND PROSGEGRAMMENI
+ { 0x1FAD, 0x1FA5 }, // GREEK CAPITAL LETTER OMEGA WITH DASIA AND OXIA AND PROSGEGRAMMENI
+ { 0x1FAE, 0x1FA6 }, // GREEK CAPITAL LETTER OMEGA WITH PSILI AND PERISPOMENI AND PROSGEGRAMMENI
+ { 0x1FAF, 0x1FA7 }, // GREEK CAPITAL LETTER OMEGA WITH DASIA AND PERISPOMENI AND PROSGEGRAMMENI
+ { 0x1FB8, 0x1FB0 }, // GREEK CAPITAL LETTER ALPHA WITH VRACHY
+ { 0x1FB9, 0x1FB1 }, // GREEK CAPITAL LETTER ALPHA WITH MACRON
+ { 0x1FBA, 0x1F70 }, // GREEK CAPITAL LETTER ALPHA WITH VARIA
+ { 0x1FBB, 0x1F71 }, // GREEK CAPITAL LETTER ALPHA WITH OXIA
+ { 0x1FBC, 0x1FB3 }, // GREEK CAPITAL LETTER ALPHA WITH PROSGEGRAMMENI
+ { 0x1FC8, 0x1F72 }, // GREEK CAPITAL LETTER EPSILON WITH VARIA
+ { 0x1FC9, 0x1F73 }, // GREEK CAPITAL LETTER EPSILON WITH OXIA
+ { 0x1FCA, 0x1F74 }, // GREEK CAPITAL LETTER ETA WITH VARIA
+ { 0x1FCB, 0x1F75 }, // GREEK CAPITAL LETTER ETA WITH OXIA
+ { 0x1FCC, 0x1FC3 }, // GREEK CAPITAL LETTER ETA WITH PROSGEGRAMMENI
+ { 0x1FD8, 0x1FD0 }, // GREEK CAPITAL LETTER IOTA WITH VRACHY
+ { 0x1FD9, 0x1FD1 }, // GREEK CAPITAL LETTER IOTA WITH MACRON
+ { 0x1FDA, 0x1F76 }, // GREEK CAPITAL LETTER IOTA WITH VARIA
+ { 0x1FDB, 0x1F77 }, // GREEK CAPITAL LETTER IOTA WITH OXIA
+ { 0x1FE8, 0x1FE0 }, // GREEK CAPITAL LETTER UPSILON WITH VRACHY
+ { 0x1FE9, 0x1FE1 }, // GREEK CAPITAL LETTER UPSILON WITH MACRON
+ { 0x1FEA, 0x1F7A }, // GREEK CAPITAL LETTER UPSILON WITH VARIA
+ { 0x1FEB, 0x1F7B }, // GREEK CAPITAL LETTER UPSILON WITH OXIA
+ { 0x1FEC, 0x1FE5 }, // GREEK CAPITAL LETTER RHO WITH DASIA
+ { 0x1FF8, 0x1F78 }, // GREEK CAPITAL LETTER OMICRON WITH VARIA
+ { 0x1FF9, 0x1F79 }, // GREEK CAPITAL LETTER OMICRON WITH OXIA
+ { 0x1FFA, 0x1F7C }, // GREEK CAPITAL LETTER OMEGA WITH VARIA
+ { 0x1FFB, 0x1F7D }, // GREEK CAPITAL LETTER OMEGA WITH OXIA
+ { 0x1FFC, 0x1FF3 }, // GREEK CAPITAL LETTER OMEGA WITH PROSGEGRAMMENI
+ { 0x2126, 0x03C9 }, // OHM SIGN
+ { 0x212A, 0x006B }, // KELVIN SIGN
+ { 0x212B, 0x00E5 }, // ANGSTROM SIGN
+ { 0x2132, 0x214E }, // TURNED CAPITAL F
+ { 0x2160, 0x2170 }, // ROMAN NUMERAL ONE
+ { 0x2161, 0x2171 }, // ROMAN NUMERAL TWO
+ { 0x2162, 0x2172 }, // ROMAN NUMERAL THREE
+ { 0x2163, 0x2173 }, // ROMAN NUMERAL FOUR
+ { 0x2164, 0x2174 }, // ROMAN NUMERAL FIVE
+ { 0x2165, 0x2175 }, // ROMAN NUMERAL SIX
+ { 0x2166, 0x2176 }, // ROMAN NUMERAL SEVEN
+ { 0x2167, 0x2177 }, // ROMAN NUMERAL EIGHT
+ { 0x2168, 0x2178 }, // ROMAN NUMERAL NINE
+ { 0x2169, 0x2179 }, // ROMAN NUMERAL TEN
+ { 0x216A, 0x217A }, // ROMAN NUMERAL ELEVEN
+ { 0x216B, 0x217B }, // ROMAN NUMERAL TWELVE
+ { 0x216C, 0x217C }, // ROMAN NUMERAL FIFTY
+ { 0x216D, 0x217D }, // ROMAN NUMERAL ONE HUNDRED
+ { 0x216E, 0x217E }, // ROMAN NUMERAL FIVE HUNDRED
+ { 0x216F, 0x217F }, // ROMAN NUMERAL ONE THOUSAND
+ { 0x2183, 0x2184 }, // ROMAN NUMERAL REVERSED ONE HUNDRED
+ { 0x24B6, 0x24D0 }, // CIRCLED LATIN CAPITAL LETTER A
+ { 0x24B7, 0x24D1 }, // CIRCLED LATIN CAPITAL LETTER B
+ { 0x24B8, 0x24D2 }, // CIRCLED LATIN CAPITAL LETTER C
+ { 0x24B9, 0x24D3 }, // CIRCLED LATIN CAPITAL LETTER D
+ { 0x24BA, 0x24D4 }, // CIRCLED LATIN CAPITAL LETTER E
+ { 0x24BB, 0x24D5 }, // CIRCLED LATIN CAPITAL LETTER F
+ { 0x24BC, 0x24D6 }, // CIRCLED LATIN CAPITAL LETTER G
+ { 0x24BD, 0x24D7 }, // CIRCLED LATIN CAPITAL LETTER H
+ { 0x24BE, 0x24D8 }, // CIRCLED LATIN CAPITAL LETTER I
+ { 0x24BF, 0x24D9 }, // CIRCLED LATIN CAPITAL LETTER J
+ { 0x24C0, 0x24DA }, // CIRCLED LATIN CAPITAL LETTER K
+ { 0x24C1, 0x24DB }, // CIRCLED LATIN CAPITAL LETTER L
+ { 0x24C2, 0x24DC }, // CIRCLED LATIN CAPITAL LETTER M
+ { 0x24C3, 0x24DD }, // CIRCLED LATIN CAPITAL LETTER N
+ { 0x24C4, 0x24DE }, // CIRCLED LATIN CAPITAL LETTER O
+ { 0x24C5, 0x24DF }, // CIRCLED LATIN CAPITAL LETTER P
+ { 0x24C6, 0x24E0 }, // CIRCLED LATIN CAPITAL LETTER Q
+ { 0x24C7, 0x24E1 }, // CIRCLED LATIN CAPITAL LETTER R
+ { 0x24C8, 0x24E2 }, // CIRCLED LATIN CAPITAL LETTER S
+ { 0x24C9, 0x24E3 }, // CIRCLED LATIN CAPITAL LETTER T
+ { 0x24CA, 0x24E4 }, // CIRCLED LATIN CAPITAL LETTER U
+ { 0x24CB, 0x24E5 }, // CIRCLED LATIN CAPITAL LETTER V
+ { 0x24CC, 0x24E6 }, // CIRCLED LATIN CAPITAL LETTER W
+ { 0x24CD, 0x24E7 }, // CIRCLED LATIN CAPITAL LETTER X
+ { 0x24CE, 0x24E8 }, // CIRCLED LATIN CAPITAL LETTER Y
+ { 0x24CF, 0x24E9 }, // CIRCLED LATIN CAPITAL LETTER Z
+ { 0x2C00, 0x2C30 }, // GLAGOLITIC CAPITAL LETTER AZU
+ { 0x2C01, 0x2C31 }, // GLAGOLITIC CAPITAL LETTER BUKY
+ { 0x2C02, 0x2C32 }, // GLAGOLITIC CAPITAL LETTER VEDE
+ { 0x2C03, 0x2C33 }, // GLAGOLITIC CAPITAL LETTER GLAGOLI
+ { 0x2C04, 0x2C34 }, // GLAGOLITIC CAPITAL LETTER DOBRO
+ { 0x2C05, 0x2C35 }, // GLAGOLITIC CAPITAL LETTER YESTU
+ { 0x2C06, 0x2C36 }, // GLAGOLITIC CAPITAL LETTER ZHIVETE
+ { 0x2C07, 0x2C37 }, // GLAGOLITIC CAPITAL LETTER DZELO
+ { 0x2C08, 0x2C38 }, // GLAGOLITIC CAPITAL LETTER ZEMLJA
+ { 0x2C09, 0x2C39 }, // GLAGOLITIC CAPITAL LETTER IZHE
+ { 0x2C0A, 0x2C3A }, // GLAGOLITIC CAPITAL LETTER INITIAL IZHE
+ { 0x2C0B, 0x2C3B }, // GLAGOLITIC CAPITAL LETTER I
+ { 0x2C0C, 0x2C3C }, // GLAGOLITIC CAPITAL LETTER DJERVI
+ { 0x2C0D, 0x2C3D }, // GLAGOLITIC CAPITAL LETTER KAKO
+ { 0x2C0E, 0x2C3E }, // GLAGOLITIC CAPITAL LETTER LJUDIJE
+ { 0x2C0F, 0x2C3F }, // GLAGOLITIC CAPITAL LETTER MYSLITE
+ { 0x2C10, 0x2C40 }, // GLAGOLITIC CAPITAL LETTER NASHI
+ { 0x2C11, 0x2C41 }, // GLAGOLITIC CAPITAL LETTER ONU
+ { 0x2C12, 0x2C42 }, // GLAGOLITIC CAPITAL LETTER POKOJI
+ { 0x2C13, 0x2C43 }, // GLAGOLITIC CAPITAL LETTER RITSI
+ { 0x2C14, 0x2C44 }, // GLAGOLITIC CAPITAL LETTER SLOVO
+ { 0x2C15, 0x2C45 }, // GLAGOLITIC CAPITAL LETTER TVRIDO
+ { 0x2C16, 0x2C46 }, // GLAGOLITIC CAPITAL LETTER UKU
+ { 0x2C17, 0x2C47 }, // GLAGOLITIC CAPITAL LETTER FRITU
+ { 0x2C18, 0x2C48 }, // GLAGOLITIC CAPITAL LETTER HERU
+ { 0x2C19, 0x2C49 }, // GLAGOLITIC CAPITAL LETTER OTU
+ { 0x2C1A, 0x2C4A }, // GLAGOLITIC CAPITAL LETTER PE
+ { 0x2C1B, 0x2C4B }, // GLAGOLITIC CAPITAL LETTER SHTA
+ { 0x2C1C, 0x2C4C }, // GLAGOLITIC CAPITAL LETTER TSI
+ { 0x2C1D, 0x2C4D }, // GLAGOLITIC CAPITAL LETTER CHRIVI
+ { 0x2C1E, 0x2C4E }, // GLAGOLITIC CAPITAL LETTER SHA
+ { 0x2C1F, 0x2C4F }, // GLAGOLITIC CAPITAL LETTER YERU
+ { 0x2C20, 0x2C50 }, // GLAGOLITIC CAPITAL LETTER YERI
+ { 0x2C21, 0x2C51 }, // GLAGOLITIC CAPITAL LETTER YATI
+ { 0x2C22, 0x2C52 }, // GLAGOLITIC CAPITAL LETTER SPIDERY HA
+ { 0x2C23, 0x2C53 }, // GLAGOLITIC CAPITAL LETTER YU
+ { 0x2C24, 0x2C54 }, // GLAGOLITIC CAPITAL LETTER SMALL YUS
+ { 0x2C25, 0x2C55 }, // GLAGOLITIC CAPITAL LETTER SMALL YUS WITH TAIL
+ { 0x2C26, 0x2C56 }, // GLAGOLITIC CAPITAL LETTER YO
+ { 0x2C27, 0x2C57 }, // GLAGOLITIC CAPITAL LETTER IOTATED SMALL YUS
+ { 0x2C28, 0x2C58 }, // GLAGOLITIC CAPITAL LETTER BIG YUS
+ { 0x2C29, 0x2C59 }, // GLAGOLITIC CAPITAL LETTER IOTATED BIG YUS
+ { 0x2C2A, 0x2C5A }, // GLAGOLITIC CAPITAL LETTER FITA
+ { 0x2C2B, 0x2C5B }, // GLAGOLITIC CAPITAL LETTER IZHITSA
+ { 0x2C2C, 0x2C5C }, // GLAGOLITIC CAPITAL LETTER SHTAPIC
+ { 0x2C2D, 0x2C5D }, // GLAGOLITIC CAPITAL LETTER TROKUTASTI A
+ { 0x2C2E, 0x2C5E }, // GLAGOLITIC CAPITAL LETTER LATINATE MYSLITE
+ { 0x2C60, 0x2C61 }, // LATIN CAPITAL LETTER L WITH DOUBLE BAR
+ { 0x2C62, 0x026B }, // LATIN CAPITAL LETTER L WITH MIDDLE TILDE
+ { 0x2C63, 0x1D7D }, // LATIN CAPITAL LETTER P WITH STROKE
+ { 0x2C64, 0x027D }, // LATIN CAPITAL LETTER R WITH TAIL
+ { 0x2C67, 0x2C68 }, // LATIN CAPITAL LETTER H WITH DESCENDER
+ { 0x2C69, 0x2C6A }, // LATIN CAPITAL LETTER K WITH DESCENDER
+ { 0x2C6B, 0x2C6C }, // LATIN CAPITAL LETTER Z WITH DESCENDER
+ { 0x2C6D, 0x0251 }, // LATIN CAPITAL LETTER ALPHA
+ { 0x2C6E, 0x0271 }, // LATIN CAPITAL LETTER M WITH HOOK
+ { 0x2C6F, 0x0250 }, // LATIN CAPITAL LETTER TURNED A
+ { 0x2C70, 0x0252 }, // LATIN CAPITAL LETTER TURNED ALPHA
+ { 0x2C72, 0x2C73 }, // LATIN CAPITAL LETTER W WITH HOOK
+ { 0x2C75, 0x2C76 }, // LATIN CAPITAL LETTER HALF H
+ { 0x2C7E, 0x023F }, // LATIN CAPITAL LETTER S WITH SWASH TAIL
+ { 0x2C7F, 0x0240 }, // LATIN CAPITAL LETTER Z WITH SWASH TAIL
+ { 0x2C80, 0x2C81 }, // COPTIC CAPITAL LETTER ALFA
+ { 0x2C82, 0x2C83 }, // COPTIC CAPITAL LETTER VIDA
+ { 0x2C84, 0x2C85 }, // COPTIC CAPITAL LETTER GAMMA
+ { 0x2C86, 0x2C87 }, // COPTIC CAPITAL LETTER DALDA
+ { 0x2C88, 0x2C89 }, // COPTIC CAPITAL LETTER EIE
+ { 0x2C8A, 0x2C8B }, // COPTIC CAPITAL LETTER SOU
+ { 0x2C8C, 0x2C8D }, // COPTIC CAPITAL LETTER ZATA
+ { 0x2C8E, 0x2C8F }, // COPTIC CAPITAL LETTER HATE
+ { 0x2C90, 0x2C91 }, // COPTIC CAPITAL LETTER THETHE
+ { 0x2C92, 0x2C93 }, // COPTIC CAPITAL LETTER IAUDA
+ { 0x2C94, 0x2C95 }, // COPTIC CAPITAL LETTER KAPA
+ { 0x2C96, 0x2C97 }, // COPTIC CAPITAL LETTER LAULA
+ { 0x2C98, 0x2C99 }, // COPTIC CAPITAL LETTER MI
+ { 0x2C9A, 0x2C9B }, // COPTIC CAPITAL LETTER NI
+ { 0x2C9C, 0x2C9D }, // COPTIC CAPITAL LETTER KSI
+ { 0x2C9E, 0x2C9F }, // COPTIC CAPITAL LETTER O
+ { 0x2CA0, 0x2CA1 }, // COPTIC CAPITAL LETTER PI
+ { 0x2CA2, 0x2CA3 }, // COPTIC CAPITAL LETTER RO
+ { 0x2CA4, 0x2CA5 }, // COPTIC CAPITAL LETTER SIMA
+ { 0x2CA6, 0x2CA7 }, // COPTIC CAPITAL LETTER TAU
+ { 0x2CA8, 0x2CA9 }, // COPTIC CAPITAL LETTER UA
+ { 0x2CAA, 0x2CAB }, // COPTIC CAPITAL LETTER FI
+ { 0x2CAC, 0x2CAD }, // COPTIC CAPITAL LETTER KHI
+ { 0x2CAE, 0x2CAF }, // COPTIC CAPITAL LETTER PSI
+ { 0x2CB0, 0x2CB1 }, // COPTIC CAPITAL LETTER OOU
+ { 0x2CB2, 0x2CB3 }, // COPTIC CAPITAL LETTER DIALECT-P ALEF
+ { 0x2CB4, 0x2CB5 }, // COPTIC CAPITAL LETTER OLD COPTIC AIN
+ { 0x2CB6, 0x2CB7 }, // COPTIC CAPITAL LETTER CRYPTOGRAMMIC EIE
+ { 0x2CB8, 0x2CB9 }, // COPTIC CAPITAL LETTER DIALECT-P KAPA
+ { 0x2CBA, 0x2CBB }, // COPTIC CAPITAL LETTER DIALECT-P NI
+ { 0x2CBC, 0x2CBD }, // COPTIC CAPITAL LETTER CRYPTOGRAMMIC NI
+ { 0x2CBE, 0x2CBF }, // COPTIC CAPITAL LETTER OLD COPTIC OOU
+ { 0x2CC0, 0x2CC1 }, // COPTIC CAPITAL LETTER SAMPI
+ { 0x2CC2, 0x2CC3 }, // COPTIC CAPITAL LETTER CROSSED SHEI
+ { 0x2CC4, 0x2CC5 }, // COPTIC CAPITAL LETTER OLD COPTIC SHEI
+ { 0x2CC6, 0x2CC7 }, // COPTIC CAPITAL LETTER OLD COPTIC ESH
+ { 0x2CC8, 0x2CC9 }, // COPTIC CAPITAL LETTER AKHMIMIC KHEI
+ { 0x2CCA, 0x2CCB }, // COPTIC CAPITAL LETTER DIALECT-P HORI
+ { 0x2CCC, 0x2CCD }, // COPTIC CAPITAL LETTER OLD COPTIC HORI
+ { 0x2CCE, 0x2CCF }, // COPTIC CAPITAL LETTER OLD COPTIC HA
+ { 0x2CD0, 0x2CD1 }, // COPTIC CAPITAL LETTER L-SHAPED HA
+ { 0x2CD2, 0x2CD3 }, // COPTIC CAPITAL LETTER OLD COPTIC HEI
+ { 0x2CD4, 0x2CD5 }, // COPTIC CAPITAL LETTER OLD COPTIC HAT
+ { 0x2CD6, 0x2CD7 }, // COPTIC CAPITAL LETTER OLD COPTIC GANGIA
+ { 0x2CD8, 0x2CD9 }, // COPTIC CAPITAL LETTER OLD COPTIC DJA
+ { 0x2CDA, 0x2CDB }, // COPTIC CAPITAL LETTER OLD COPTIC SHIMA
+ { 0x2CDC, 0x2CDD }, // COPTIC CAPITAL LETTER OLD NUBIAN SHIMA
+ { 0x2CDE, 0x2CDF }, // COPTIC CAPITAL LETTER OLD NUBIAN NGI
+ { 0x2CE0, 0x2CE1 }, // COPTIC CAPITAL LETTER OLD NUBIAN NYI
+ { 0x2CE2, 0x2CE3 }, // COPTIC CAPITAL LETTER OLD NUBIAN WAU
+ { 0x2CEB, 0x2CEC }, // COPTIC CAPITAL LETTER CRYPTOGRAMMIC SHEI
+ { 0x2CED, 0x2CEE }, // COPTIC CAPITAL LETTER CRYPTOGRAMMIC GANGIA
+ { 0xA640, 0xA641 }, // CYRILLIC CAPITAL LETTER ZEMLYA
+ { 0xA642, 0xA643 }, // CYRILLIC CAPITAL LETTER DZELO
+ { 0xA644, 0xA645 }, // CYRILLIC CAPITAL LETTER REVERSED DZE
+ { 0xA646, 0xA647 }, // CYRILLIC CAPITAL LETTER IOTA
+ { 0xA648, 0xA649 }, // CYRILLIC CAPITAL LETTER DJERV
+ { 0xA64A, 0xA64B }, // CYRILLIC CAPITAL LETTER MONOGRAPH UK
+ { 0xA64C, 0xA64D }, // CYRILLIC CAPITAL LETTER BROAD OMEGA
+ { 0xA64E, 0xA64F }, // CYRILLIC CAPITAL LETTER NEUTRAL YER
+ { 0xA650, 0xA651 }, // CYRILLIC CAPITAL LETTER YERU WITH BACK YER
+ { 0xA652, 0xA653 }, // CYRILLIC CAPITAL LETTER IOTIFIED YAT
+ { 0xA654, 0xA655 }, // CYRILLIC CAPITAL LETTER REVERSED YU
+ { 0xA656, 0xA657 }, // CYRILLIC CAPITAL LETTER IOTIFIED A
+ { 0xA658, 0xA659 }, // CYRILLIC CAPITAL LETTER CLOSED LITTLE YUS
+ { 0xA65A, 0xA65B }, // CYRILLIC CAPITAL LETTER BLENDED YUS
+ { 0xA65C, 0xA65D }, // CYRILLIC CAPITAL LETTER IOTIFIED CLOSED LITTLE YUS
+ { 0xA65E, 0xA65F }, // CYRILLIC CAPITAL LETTER YN
+ { 0xA662, 0xA663 }, // CYRILLIC CAPITAL LETTER SOFT DE
+ { 0xA664, 0xA665 }, // CYRILLIC CAPITAL LETTER SOFT EL
+ { 0xA666, 0xA667 }, // CYRILLIC CAPITAL LETTER SOFT EM
+ { 0xA668, 0xA669 }, // CYRILLIC CAPITAL LETTER MONOCULAR O
+ { 0xA66A, 0xA66B }, // CYRILLIC CAPITAL LETTER BINOCULAR O
+ { 0xA66C, 0xA66D }, // CYRILLIC CAPITAL LETTER DOUBLE MONOCULAR O
+ { 0xA680, 0xA681 }, // CYRILLIC CAPITAL LETTER DWE
+ { 0xA682, 0xA683 }, // CYRILLIC CAPITAL LETTER DZWE
+ { 0xA684, 0xA685 }, // CYRILLIC CAPITAL LETTER ZHWE
+ { 0xA686, 0xA687 }, // CYRILLIC CAPITAL LETTER CCHE
+ { 0xA688, 0xA689 }, // CYRILLIC CAPITAL LETTER DZZE
+ { 0xA68A, 0xA68B }, // CYRILLIC CAPITAL LETTER TE WITH MIDDLE HOOK
+ { 0xA68C, 0xA68D }, // CYRILLIC CAPITAL LETTER TWE
+ { 0xA68E, 0xA68F }, // CYRILLIC CAPITAL LETTER TSWE
+ { 0xA690, 0xA691 }, // CYRILLIC CAPITAL LETTER TSSE
+ { 0xA692, 0xA693 }, // CYRILLIC CAPITAL LETTER TCHE
+ { 0xA694, 0xA695 }, // CYRILLIC CAPITAL LETTER HWE
+ { 0xA696, 0xA697 }, // CYRILLIC CAPITAL LETTER SHWE
+ { 0xA722, 0xA723 }, // LATIN CAPITAL LETTER EGYPTOLOGICAL ALEF
+ { 0xA724, 0xA725 }, // LATIN CAPITAL LETTER EGYPTOLOGICAL AIN
+ { 0xA726, 0xA727 }, // LATIN CAPITAL LETTER HENG
+ { 0xA728, 0xA729 }, // LATIN CAPITAL LETTER TZ
+ { 0xA72A, 0xA72B }, // LATIN CAPITAL LETTER TRESILLO
+ { 0xA72C, 0xA72D }, // LATIN CAPITAL LETTER CUATRILLO
+ { 0xA72E, 0xA72F }, // LATIN CAPITAL LETTER CUATRILLO WITH COMMA
+ { 0xA732, 0xA733 }, // LATIN CAPITAL LETTER AA
+ { 0xA734, 0xA735 }, // LATIN CAPITAL LETTER AO
+ { 0xA736, 0xA737 }, // LATIN CAPITAL LETTER AU
+ { 0xA738, 0xA739 }, // LATIN CAPITAL LETTER AV
+ { 0xA73A, 0xA73B }, // LATIN CAPITAL LETTER AV WITH HORIZONTAL BAR
+ { 0xA73C, 0xA73D }, // LATIN CAPITAL LETTER AY
+ { 0xA73E, 0xA73F }, // LATIN CAPITAL LETTER REVERSED C WITH DOT
+ { 0xA740, 0xA741 }, // LATIN CAPITAL LETTER K WITH STROKE
+ { 0xA742, 0xA743 }, // LATIN CAPITAL LETTER K WITH DIAGONAL STROKE
+ { 0xA744, 0xA745 }, // LATIN CAPITAL LETTER K WITH STROKE AND DIAGONAL STROKE
+ { 0xA746, 0xA747 }, // LATIN CAPITAL LETTER BROKEN L
+ { 0xA748, 0xA749 }, // LATIN CAPITAL LETTER L WITH HIGH STROKE
+ { 0xA74A, 0xA74B }, // LATIN CAPITAL LETTER O WITH LONG STROKE OVERLAY
+ { 0xA74C, 0xA74D }, // LATIN CAPITAL LETTER O WITH LOOP
+ { 0xA74E, 0xA74F }, // LATIN CAPITAL LETTER OO
+ { 0xA750, 0xA751 }, // LATIN CAPITAL LETTER P WITH STROKE THROUGH DESCENDER
+ { 0xA752, 0xA753 }, // LATIN CAPITAL LETTER P WITH FLOURISH
+ { 0xA754, 0xA755 }, // LATIN CAPITAL LETTER P WITH SQUIRREL TAIL
+ { 0xA756, 0xA757 }, // LATIN CAPITAL LETTER Q WITH STROKE THROUGH DESCENDER
+ { 0xA758, 0xA759 }, // LATIN CAPITAL LETTER Q WITH DIAGONAL STROKE
+ { 0xA75A, 0xA75B }, // LATIN CAPITAL LETTER R ROTUNDA
+ { 0xA75C, 0xA75D }, // LATIN CAPITAL LETTER RUM ROTUNDA
+ { 0xA75E, 0xA75F }, // LATIN CAPITAL LETTER V WITH DIAGONAL STROKE
+ { 0xA760, 0xA761 }, // LATIN CAPITAL LETTER VY
+ { 0xA762, 0xA763 }, // LATIN CAPITAL LETTER VISIGOTHIC Z
+ { 0xA764, 0xA765 }, // LATIN CAPITAL LETTER THORN WITH STROKE
+ { 0xA766, 0xA767 }, // LATIN CAPITAL LETTER THORN WITH STROKE THROUGH DESCENDER
+ { 0xA768, 0xA769 }, // LATIN CAPITAL LETTER VEND
+ { 0xA76A, 0xA76B }, // LATIN CAPITAL LETTER ET
+ { 0xA76C, 0xA76D }, // LATIN CAPITAL LETTER IS
+ { 0xA76E, 0xA76F }, // LATIN CAPITAL LETTER CON
+ { 0xA779, 0xA77A }, // LATIN CAPITAL LETTER INSULAR D
+ { 0xA77B, 0xA77C }, // LATIN CAPITAL LETTER INSULAR F
+ { 0xA77D, 0x1D79 }, // LATIN CAPITAL LETTER INSULAR G
+ { 0xA77E, 0xA77F }, // LATIN CAPITAL LETTER TURNED INSULAR G
+ { 0xA780, 0xA781 }, // LATIN CAPITAL LETTER TURNED L
+ { 0xA782, 0xA783 }, // LATIN CAPITAL LETTER INSULAR R
+ { 0xA784, 0xA785 }, // LATIN CAPITAL LETTER INSULAR S
+ { 0xA786, 0xA787 }, // LATIN CAPITAL LETTER INSULAR T
+ { 0xA78B, 0xA78C }, // LATIN CAPITAL LETTER SALTILLO
+ { 0xFF21, 0xFF41 }, // FULLWIDTH LATIN CAPITAL LETTER A
+ { 0xFF22, 0xFF42 }, // FULLWIDTH LATIN CAPITAL LETTER B
+ { 0xFF23, 0xFF43 }, // FULLWIDTH LATIN CAPITAL LETTER C
+ { 0xFF24, 0xFF44 }, // FULLWIDTH LATIN CAPITAL LETTER D
+ { 0xFF25, 0xFF45 }, // FULLWIDTH LATIN CAPITAL LETTER E
+ { 0xFF26, 0xFF46 }, // FULLWIDTH LATIN CAPITAL LETTER F
+ { 0xFF27, 0xFF47 }, // FULLWIDTH LATIN CAPITAL LETTER G
+ { 0xFF28, 0xFF48 }, // FULLWIDTH LATIN CAPITAL LETTER H
+ { 0xFF29, 0xFF49 }, // FULLWIDTH LATIN CAPITAL LETTER I
+ { 0xFF2A, 0xFF4A }, // FULLWIDTH LATIN CAPITAL LETTER J
+ { 0xFF2B, 0xFF4B }, // FULLWIDTH LATIN CAPITAL LETTER K
+ { 0xFF2C, 0xFF4C }, // FULLWIDTH LATIN CAPITAL LETTER L
+ { 0xFF2D, 0xFF4D }, // FULLWIDTH LATIN CAPITAL LETTER M
+ { 0xFF2E, 0xFF4E }, // FULLWIDTH LATIN CAPITAL LETTER N
+ { 0xFF2F, 0xFF4F }, // FULLWIDTH LATIN CAPITAL LETTER O
+ { 0xFF30, 0xFF50 }, // FULLWIDTH LATIN CAPITAL LETTER P
+ { 0xFF31, 0xFF51 }, // FULLWIDTH LATIN CAPITAL LETTER Q
+ { 0xFF32, 0xFF52 }, // FULLWIDTH LATIN CAPITAL LETTER R
+ { 0xFF33, 0xFF53 }, // FULLWIDTH LATIN CAPITAL LETTER S
+ { 0xFF34, 0xFF54 }, // FULLWIDTH LATIN CAPITAL LETTER T
+ { 0xFF35, 0xFF55 }, // FULLWIDTH LATIN CAPITAL LETTER U
+ { 0xFF36, 0xFF56 }, // FULLWIDTH LATIN CAPITAL LETTER V
+ { 0xFF37, 0xFF57 }, // FULLWIDTH LATIN CAPITAL LETTER W
+ { 0xFF38, 0xFF58 }, // FULLWIDTH LATIN CAPITAL LETTER X
+ { 0xFF39, 0xFF59 }, // FULLWIDTH LATIN CAPITAL LETTER Y
+ { 0xFF3A, 0xFF5A } // FULLWIDTH LATIN CAPITAL LETTER Z
+};
+
+static int compare_pair_capital(const void *a, const void *b) {
+ return (int)(*(unsigned short *)a)
+ - (int)((struct LatinCapitalSmallPair*)b)->capital;
+}
+
+unsigned short latin_tolower(unsigned short c) {
+ struct LatinCapitalSmallPair *p =
+ (struct LatinCapitalSmallPair *)bsearch(&c, SORTED_CHAR_MAP,
+ sizeof(SORTED_CHAR_MAP) / sizeof(SORTED_CHAR_MAP[0]),
+ sizeof(SORTED_CHAR_MAP[0]),
+ compare_pair_capital);
+ return p ? p->small : c;
+}
+
+} // namespace latinime
diff --git a/native/src/char_utils.h b/native/src/char_utils.h
new file mode 100644
index 000000000..921ecb4a5
--- /dev/null
+++ b/native/src/char_utils.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef LATINIME_CHAR_UTILS_H
+#define LATINIME_CHAR_UTILS_H
+
+namespace latinime {
+
+unsigned short latin_tolower(unsigned short c);
+
+}; // namespace latinime
+
+#endif // LATINIME_CHAR_UTILS_H
diff --git a/native/src/dictionary.cpp b/native/src/dictionary.cpp
index 6e6f44182..1a39f585b4 100644
--- a/native/src/dictionary.cpp
+++ b/native/src/dictionary.cpp
@@ -19,21 +19,18 @@
#include <fcntl.h>
#include <sys/mman.h>
#include <string.h>
-#include <cutils/log.h>
-
-#include <unicode/uchar.h>
-
-//#define USE_ASSET_MANAGER
-
-#ifdef USE_ASSET_MANAGER
-#include <utils/AssetManager.h>
-#include <utils/Asset.h>
-#endif
+//#define LOG_TAG "dictionary.cpp"
+//#include <cutils/log.h>
+#define LOGI
#include "dictionary.h"
#include "basechars.h"
+#include "char_utils.h"
#define DEBUG_DICT 0
+#define DICTIONARY_VERSION_MIN 200
+#define DICTIONARY_HEADER_SIZE 2
+#define NOT_VALID_WORD -99
namespace latinime {
@@ -42,6 +39,7 @@ Dictionary::Dictionary(void *dict, int typedLetterMultiplier, int fullWordMultip
mDict = (unsigned char*) dict;
mTypedLetterMultiplier = typedLetterMultiplier;
mFullWordMultiplier = fullWordMultiplier;
+ getVersionNumber();
}
Dictionary::~Dictionary()
@@ -65,7 +63,11 @@ int Dictionary::getSuggestions(int *codes, int codesSize, unsigned short *outWor
mNextLettersFrequencies = nextLetters;
mNextLettersSize = nextLettersSize;
- getWordsRec(0, 0, mInputLength * 3, false, 1, 0, 0);
+ if (checkIfDictVersionIsLatest()) {
+ getWordsRec(DICTIONARY_HEADER_SIZE, 0, mInputLength * 3, false, 1, 0, 0);
+ } else {
+ getWordsRec(0, 0, mInputLength * 3, false, 1, 0, 0);
+ }
// Get the word count
suggWords = 0;
@@ -92,6 +94,21 @@ Dictionary::registerNextLetter(unsigned short c)
}
}
+void
+Dictionary::getVersionNumber()
+{
+ mVersion = (mDict[0] & 0xFF);
+ mBigram = (mDict[1] & 0xFF);
+ LOGI("IN NATIVE SUGGEST Version: %d Bigram : %d \n", mVersion, mBigram);
+}
+
+// Checks whether it has the latest dictionary or the old dictionary
+bool
+Dictionary::checkIfDictVersionIsLatest()
+{
+ return (mVersion >= DICTIONARY_VERSION_MIN) && (mBigram == 1 || mBigram == 0);
+}
+
unsigned short
Dictionary::getChar(int *pos)
{
@@ -120,6 +137,28 @@ Dictionary::getAddress(int *pos)
}
int
+Dictionary::getFreq(int *pos)
+{
+ int freq = mDict[(*pos)++] & 0xFF;
+
+ if (checkIfDictVersionIsLatest()) {
+ // skipping bigram
+ int bigramExist = (mDict[*pos] & FLAG_BIGRAM_READ);
+ if (bigramExist > 0) {
+ int nextBigramExist = 1;
+ while (nextBigramExist > 0) {
+ (*pos) += 3;
+ nextBigramExist = (mDict[(*pos)++] & FLAG_BIGRAM_CONTINUED);
+ }
+ } else {
+ (*pos)++;
+ }
+ }
+
+ return freq;
+}
+
+int
Dictionary::wideStrLen(unsigned short *str)
{
if (!str) return 0;
@@ -168,6 +207,46 @@ Dictionary::addWord(unsigned short *word, int length, int frequency)
return false;
}
+bool
+Dictionary::addWordBigram(unsigned short *word, int length, int frequency)
+{
+ word[length] = 0;
+ if (DEBUG_DICT) {
+ char s[length + 1];
+ for (int i = 0; i <= length; i++) s[i] = word[i];
+ LOGI("Bigram: Found word = %s, freq = %d : \n", s, frequency);
+ }
+
+ // Find the right insertion point
+ int insertAt = 0;
+ while (insertAt < mMaxBigrams) {
+ if (frequency > mBigramFreq[insertAt]
+ || (mBigramFreq[insertAt] == frequency
+ && length < wideStrLen(mBigramChars + insertAt * mMaxWordLength))) {
+ break;
+ }
+ insertAt++;
+ }
+ LOGI("Bigram: InsertAt -> %d maxBigrams: %d\n", insertAt, mMaxBigrams);
+ if (insertAt < mMaxBigrams) {
+ memmove((char*) mBigramFreq + (insertAt + 1) * sizeof(mBigramFreq[0]),
+ (char*) mBigramFreq + insertAt * sizeof(mBigramFreq[0]),
+ (mMaxBigrams - insertAt - 1) * sizeof(mBigramFreq[0]));
+ mBigramFreq[insertAt] = frequency;
+ memmove((char*) mBigramChars + (insertAt + 1) * mMaxWordLength * sizeof(short),
+ (char*) mBigramChars + (insertAt ) * mMaxWordLength * sizeof(short),
+ (mMaxBigrams - insertAt - 1) * sizeof(short) * mMaxWordLength);
+ unsigned short *dest = mBigramChars + (insertAt ) * mMaxWordLength;
+ while (length--) {
+ *dest++ = *word++;
+ }
+ *dest = 0; // NULL terminate
+ if (DEBUG_DICT) LOGI("Bigram: Added word at %d\n", insertAt);
+ return true;
+ }
+ return false;
+}
+
unsigned short
Dictionary::toLowerCase(unsigned short c) {
if (c < sizeof(BASE_CHARS) / sizeof(BASE_CHARS[0])) {
@@ -176,7 +255,7 @@ Dictionary::toLowerCase(unsigned short c) {
if (c >='A' && c <= 'Z') {
c |= 32;
} else if (c > 127) {
- c = u_tolower(c);
+ c = latin_tolower(c);
}
return c;
}
@@ -220,12 +299,17 @@ Dictionary::getWordsRec(int pos, int depth, int maxDepth, bool completion, int s
}
for (int i = 0; i < count; i++) {
+ // -- at char
unsigned short c = getChar(&pos);
+ // -- at flag/add
unsigned short lowerC = toLowerCase(c);
bool terminal = getTerminal(&pos);
int childrenAddress = getAddress(&pos);
+ // -- after address or flag
int freq = 1;
if (terminal) freq = getFreq(&pos);
+ // -- after add or freq
+
// If we are only doing completions, no need to look at the typed characters.
if (completion) {
mWord[depth] = c;
@@ -239,7 +323,7 @@ Dictionary::getWordsRec(int pos, int depth, int maxDepth, bool completion, int s
getWordsRec(childrenAddress, depth + 1, maxDepth,
completion, snr, inputIndex, diffs);
}
- } else if (c == QUOTE && currentChars[0] != QUOTE || mSkipPos == depth) {
+ } else if ((c == QUOTE && currentChars[0] != QUOTE) || mSkipPos == depth) {
// Skip the ' or other letter and continue deeper
mWord[depth] = c;
if (childrenAddress != 0) {
@@ -277,14 +361,208 @@ Dictionary::getWordsRec(int pos, int depth, int maxDepth, bool completion, int s
}
}
+int
+Dictionary::getBigramAddress(int *pos, bool advance)
+{
+ int address = 0;
+
+ address += (mDict[*pos] & 0x3F) << 16;
+ address += (mDict[*pos + 1] & 0xFF) << 8;
+ address += (mDict[*pos + 2] & 0xFF);
+
+ if (advance) {
+ *pos += 3;
+ }
+
+ return address;
+}
+
+int
+Dictionary::getBigramFreq(int *pos)
+{
+ int freq = mDict[(*pos)++] & FLAG_BIGRAM_FREQ;
+
+ return freq;
+}
+
+
+int
+Dictionary::getBigrams(unsigned short *prevWord, int prevWordLength, int *codes, int codesSize,
+ unsigned short *bigramChars, int *bigramFreq, int maxWordLength, int maxBigrams,
+ int maxAlternatives)
+{
+ mBigramFreq = bigramFreq;
+ mBigramChars = bigramChars;
+ mInputCodes = codes;
+ mInputLength = codesSize;
+ mMaxWordLength = maxWordLength;
+ mMaxBigrams = maxBigrams;
+ mMaxAlternatives = maxAlternatives;
+
+ if (mBigram == 1 && checkIfDictVersionIsLatest()) {
+ int pos = isValidWordRec(DICTIONARY_HEADER_SIZE, prevWord, 0, prevWordLength);
+ LOGI("Pos -> %d\n", pos);
+ if (pos < 0) {
+ return 0;
+ }
+
+ int bigramCount = 0;
+ int bigramExist = (mDict[pos] & FLAG_BIGRAM_READ);
+ if (bigramExist > 0) {
+ int nextBigramExist = 1;
+ while (nextBigramExist > 0 && bigramCount < maxBigrams) {
+ int bigramAddress = getBigramAddress(&pos, true);
+ int frequency = (FLAG_BIGRAM_FREQ & mDict[pos]);
+ // search for all bigrams and store them
+ searchForTerminalNode(bigramAddress, frequency);
+ nextBigramExist = (mDict[pos++] & FLAG_BIGRAM_CONTINUED);
+ bigramCount++;
+ }
+ }
+
+ return bigramCount;
+ }
+ return 0;
+}
+
+void
+Dictionary::searchForTerminalNode(int addressLookingFor, int frequency)
+{
+ // track word with such address and store it in an array
+ unsigned short word[mMaxWordLength];
+
+ int pos;
+ int followDownBranchAddress = DICTIONARY_HEADER_SIZE;
+ bool found = false;
+ char followingChar = ' ';
+ int depth = -1;
+
+ while(!found) {
+ bool followDownAddressSearchStop = false;
+ bool firstAddress = true;
+ bool haveToSearchAll = true;
+
+ if (depth >= 0) {
+ word[depth] = (unsigned short) followingChar;
+ }
+ pos = followDownBranchAddress; // pos start at count
+ int count = mDict[pos] & 0xFF;
+ LOGI("count - %d\n",count);
+ pos++;
+ for (int i = 0; i < count; i++) {
+ // pos at data
+ pos++;
+ // pos now at flag
+ if (!getFirstBitOfByte(&pos)) { // non-terminal
+ if (!followDownAddressSearchStop) {
+ int addr = getBigramAddress(&pos, false);
+ if (addr > addressLookingFor) {
+ followDownAddressSearchStop = true;
+ if (firstAddress) {
+ firstAddress = false;
+ haveToSearchAll = true;
+ } else if (!haveToSearchAll) {
+ break;
+ }
+ } else {
+ followDownBranchAddress = addr;
+ followingChar = (char)(0xFF & mDict[pos-1]);
+ if (firstAddress) {
+ firstAddress = false;
+ haveToSearchAll = false;
+ }
+ }
+ }
+ pos += 3;
+ } else if (getFirstBitOfByte(&pos)) { // terminal
+ if (addressLookingFor == (pos-1)) { // found !!
+ depth++;
+ word[depth] = (0xFF & mDict[pos-1]);
+ found = true;
+ break;
+ }
+ if (getSecondBitOfByte(&pos)) { // address + freq (4 byte)
+ if (!followDownAddressSearchStop) {
+ int addr = getBigramAddress(&pos, false);
+ if (addr > addressLookingFor) {
+ followDownAddressSearchStop = true;
+ if (firstAddress) {
+ firstAddress = false;
+ haveToSearchAll = true;
+ } else if (!haveToSearchAll) {
+ break;
+ }
+ } else {
+ followDownBranchAddress = addr;
+ followingChar = (char)(0xFF & mDict[pos-1]);
+ if (firstAddress) {
+ firstAddress = false;
+ haveToSearchAll = true;
+ }
+ }
+ }
+ pos += 4;
+ } else { // freq only (2 byte)
+ pos += 2;
+ }
+
+ // skipping bigram
+ int bigramExist = (mDict[pos] & FLAG_BIGRAM_READ);
+ if (bigramExist > 0) {
+ int nextBigramExist = 1;
+ while (nextBigramExist > 0) {
+ pos += 3;
+ nextBigramExist = (mDict[pos++] & FLAG_BIGRAM_CONTINUED);
+ }
+ } else {
+ pos++;
+ }
+ }
+ }
+ depth++;
+ if (followDownBranchAddress == 0) {
+ LOGI("ERROR!!! Cannot find bigram!!");
+ break;
+ }
+ }
+ if (checkFirstCharacter(word)) {
+ addWordBigram(word, depth, frequency);
+ }
+}
+
bool
-Dictionary::isValidWord(unsigned short *word, int length)
+Dictionary::checkFirstCharacter(unsigned short *word)
{
- return isValidWordRec(0, word, 0, length);
+ // Checks whether this word starts with same character or neighboring characters of
+ // what user typed.
+
+ int *inputCodes = mInputCodes;
+ int maxAlt = mMaxAlternatives;
+ while (maxAlt > 0) {
+ if ((unsigned int) *inputCodes == (unsigned int) *word) {
+ return true;
+ }
+ inputCodes++;
+ maxAlt--;
+ }
+ return false;
}
bool
+Dictionary::isValidWord(unsigned short *word, int length)
+{
+ if (checkIfDictVersionIsLatest()) {
+ return (isValidWordRec(DICTIONARY_HEADER_SIZE, word, 0, length) != NOT_VALID_WORD);
+ } else {
+ return (isValidWordRec(0, word, 0, length) != NOT_VALID_WORD);
+ }
+}
+
+int
Dictionary::isValidWordRec(int pos, unsigned short *word, int offset, int length) {
+ // returns address of bigram data of that word
+ // return -99 if not found
+
int count = getCount(&pos);
unsigned short currentChar = (unsigned short) word[offset];
for (int j = 0; j < count; j++) {
@@ -294,12 +572,13 @@ Dictionary::isValidWordRec(int pos, unsigned short *word, int offset, int length
if (c == currentChar) {
if (offset == length - 1) {
if (terminal) {
- return true;
+ return (pos+1);
}
} else {
if (childPos != 0) {
- if (isValidWordRec(childPos, word, offset + 1, length)) {
- return true;
+ int t = isValidWordRec(childPos, word, offset + 1, length);
+ if (t > 0) {
+ return t;
}
}
}
@@ -310,7 +589,7 @@ Dictionary::isValidWordRec(int pos, unsigned short *word, int offset, int length
// There could be two instances of each alphabet - upper and lower case. So continue
// looking ...
}
- return false;
+ return NOT_VALID_WORD;
}
diff --git a/native/src/dictionary.h b/native/src/dictionary.h
index 3749f3d88..d13496e01 100644
--- a/native/src/dictionary.h
+++ b/native/src/dictionary.h
@@ -28,12 +28,20 @@ namespace latinime {
// if the word has other endings.
#define FLAG_TERMINAL_MASK 0x80
+#define FLAG_BIGRAM_READ 0x80
+#define FLAG_BIGRAM_CHILDEXIST 0x40
+#define FLAG_BIGRAM_CONTINUED 0x80
+#define FLAG_BIGRAM_FREQ 0x7F
+
class Dictionary {
public:
Dictionary(void *dict, int typedLetterMultipler, int fullWordMultiplier);
int getSuggestions(int *codes, int codesSize, unsigned short *outWords, int *frequencies,
int maxWordLength, int maxWords, int maxAlternatives, int skipPos,
int *nextLetters, int nextLettersSize);
+ int getBigrams(unsigned short *word, int length, int *codes, int codesSize,
+ unsigned short *outWords, int *frequencies, int maxWordLength, int maxBigrams,
+ int maxAlternatives);
bool isValidWord(unsigned short *word, int length);
void setAsset(void *asset) { mAsset = asset; }
void *getAsset() { return mAsset; }
@@ -41,28 +49,41 @@ public:
private:
+ void getVersionNumber();
+ bool checkIfDictVersionIsLatest();
int getAddress(int *pos);
+ int getBigramAddress(int *pos, bool advance);
+ int getFreq(int *pos);
+ int getBigramFreq(int *pos);
+ void searchForTerminalNode(int address, int frequency);
+
+ bool getFirstBitOfByte(int *pos) { return (mDict[*pos] & 0x80) > 0; }
+ bool getSecondBitOfByte(int *pos) { return (mDict[*pos] & 0x40) > 0; }
bool getTerminal(int *pos) { return (mDict[*pos] & FLAG_TERMINAL_MASK) > 0; }
- int getFreq(int *pos) { return mDict[(*pos)++] & 0xFF; }
int getCount(int *pos) { return mDict[(*pos)++] & 0xFF; }
unsigned short getChar(int *pos);
int wideStrLen(unsigned short *str);
bool sameAsTyped(unsigned short *word, int length);
+ bool checkFirstCharacter(unsigned short *word);
bool addWord(unsigned short *word, int length, int frequency);
+ bool addWordBigram(unsigned short *word, int length, int frequency);
unsigned short toLowerCase(unsigned short c);
void getWordsRec(int pos, int depth, int maxDepth, bool completion, int frequency,
int inputIndex, int diffs);
- bool isValidWordRec(int pos, unsigned short *word, int offset, int length);
+ int isValidWordRec(int pos, unsigned short *word, int offset, int length);
void registerNextLetter(unsigned short c);
unsigned char *mDict;
void *mAsset;
int *mFrequencies;
+ int *mBigramFreq;
int mMaxWords;
+ int mMaxBigrams;
int mMaxWordLength;
unsigned short *mOutputChars;
+ unsigned short *mBigramChars;
int *mInputCodes;
int mInputLength;
int mMaxAlternatives;
@@ -74,6 +95,8 @@ private:
int mTypedLetterMultiplier;
int *mNextLettersFrequencies;
int mNextLettersSize;
+ int mVersion;
+ int mBigram;
};
// ----------------------------------------------------------------------------
diff --git a/tests/Android.mk b/tests/Android.mk
new file mode 100644
index 000000000..fba7a8d74
--- /dev/null
+++ b/tests/Android.mk
@@ -0,0 +1,17 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# We only want this apk build for tests.
+LOCAL_MODULE_TAGS := tests
+LOCAL_CERTIFICATE := shared
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+# Include all test java files.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := LatinIMETests
+
+LOCAL_INSTRUMENTATION_FOR := LatinIME
+
+include $(BUILD_PACKAGE)
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
new file mode 100644
index 000000000..66cecee8b
--- /dev/null
+++ b/tests/AndroidManifest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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"
+ package="com.android.inputmethod.latin.tests">
+
+ <uses-permission android:name="android.permission.READ_CONTACTS" />
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ <!-- meta-data android:name="com.android.contacts.iconset" android:resource="@xml/iconset" /-->
+ <uses-permission android:name="android.permission.READ_CONTACTS" />
+
+ </application>
+
+ <instrumentation android:name="android.test.InstrumentationTestRunner"
+ android:targetPackage="com.google.android.inputmethod.latin"
+ android:label="LatinIME tests">
+ </instrumentation>
+</manifest>
diff --git a/tests/data/bigramlist.xml b/tests/data/bigramlist.xml
new file mode 100644
index 000000000..dd3f2916e
--- /dev/null
+++ b/tests/data/bigramlist.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2010, 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.
+*/
+-->
+
+<bigrams>
+ <bi w1="I'm" count="1">
+ <w w2="about" p="100" />
+ </bi>
+ <bi w1="about" count="3">
+ <w w2="part" p="117" />
+ <w w2="business" p="100" />
+ <w w2="being" p="10" />
+ </bi>
+ <bi w1="business" count="1">
+ <w w2="people" p="100" />
+ </bi>
+ <bi w1="from" count="1">
+ <w w2="same" p="117" />
+ </bi>
+</bigrams>
diff --git a/tests/data/wordlist.xml b/tests/data/wordlist.xml
new file mode 100644
index 000000000..b870eb2a3
--- /dev/null
+++ b/tests/data/wordlist.xml
@@ -0,0 +1,244 @@
+<wordlist>
+ <w f="255">the</w>
+ <w f="246">and</w>
+ <w f="245">of</w>
+ <w f="242">to</w>
+ <w f="231">in</w>
+ <w f="230">that</w>
+ <w f="229">for</w>
+ <w f="224">with</w>
+ <w f="224">on</w>
+ <w f="224">it</w>
+ <w f="223">this</w>
+ <w f="222">you</w>
+ <w f="219">is</w>
+ <w f="219">was</w>
+ <w f="219">by</w>
+ <w f="219">or</w>
+ <w f="218">from</w>
+ <w f="217">but</w>
+ <w f="216">be</w>
+ <w f="216">Sunday</w>
+ <w f="215">are</w>
+ <w f="215">he</w>
+ <w f="214">so</w>
+ <w f="214">not</w>
+ <w f="213">have</w>
+ <w f="213">as</w>
+ <w f="211">all</w>
+ <w f="211">his</w>
+ <w f="210">my</w>
+ <w f="210">if</w>
+ <w f="210">which</w>
+ <w f="210">they</w>
+ <w f="209">at</w>
+ <w f="207">it's</w>
+ <w f="207">an</w>
+ <w f="207">your</w>
+ <w f="206">will</w>
+ <w f="206">about</w>
+ <w f="206">I'm</w>
+ <w f="205">there</w>
+ <w f="205">had</w>
+ <w f="205">has</w>
+ <w f="204">when</w>
+ <w f="203">no</w>
+ <w f="203">were</w>
+ <w f="203">what</w>
+ <w f="203">more</w>
+ <w f="203">out</w>
+ <w f="203">just</w>
+ <w f="202">their</w>
+ <w f="202">up</w>
+ <w f="202">would</w>
+ <w f="202">here</w>
+ <w f="202">can</w>
+ <w f="201">who</w>
+ <w f="200">her</w>
+ <w f="200">me</w>
+ <w f="200">now</w>
+ <w f="200">our</w>
+ <w f="200">do</w>
+ <w f="200">some</w>
+ <w f="199">been</w>
+ <w f="199">two</w>
+ <w f="199">like</w>
+ <w f="199">them</w>
+ <w f="199">new</w>
+ <w f="198">time</w>
+ <w f="198">we</w>
+ <w f="198">she</w>
+ <w f="197">one</w>
+ <w f="197">over</w>
+ <w f="197">may</w>
+ <w f="197">any</w>
+ <w f="197">him</w>
+ <w f="197">calling</w>
+ <w f="196">other</w>
+ <w f="196">how</w>
+ <w f="196">see</w>
+ <w f="195">because</w>
+ <w f="195">then</w>
+ <w f="195">right</w>
+ <w f="195">into</w>
+ <w f="195">well</w>
+ <w f="195">very</w>
+ <w f="195">said</w>
+ <w f="195">people</w>
+ <w f="194">these</w>
+ <w f="194">than</w>
+ <w f="193">only</w>
+ <w f="193">back</w>
+ <w f="193">first</w>
+ <w f="193">dot</w>
+ <w f="193">after</w>
+ <w f="193">where</w>
+ <w f="192">please</w>
+ <w f="192">could</w>
+ <w f="192">its</w>
+ <w f="192">before</w>
+ <w f="192">us</w>
+ <w f="192">again</w>
+ <w f="192">home</w>
+ <w f="191">also</w>
+ <w f="191">that's</w>
+ <w f="191">think</w>
+ <w f="191">three</w>
+ <w f="191">good</w>
+ <w f="191">get</w>
+ <w f="190">know</w>
+ <w f="190">thank</w>
+ <w f="190">should</w>
+ <w f="190">going</w>
+ <w f="190">down</w>
+ <w f="189">last</w>
+ <w f="189">today</w>
+ <w f="189">those</w>
+ <w f="189">go</w>
+ <w f="189">through</w>
+ <w f="189">such</w>
+ <w f="189">don't</w>
+ <w f="189">did</w>
+ <w f="188">most</w>
+ <w f="188">day</w>
+ <w f="188">man</w>
+ <w f="188">number</w>
+ <w f="188">work</w>
+ <w f="187">too</w>
+ <w f="187">show</w>
+ <w f="187">made</w>
+ <w f="187">even</w>
+ <w f="187">being</w>
+ <w f="187">make</w>
+ <w f="187">give</w>
+ <w f="186">off</w>
+ <w f="186">com</w>
+ <w f="186">much</w>
+ <w f="186">great</w>
+ <w f="186">take</w>
+ <w f="186">call</w>
+ <w f="186">way</w>
+ <w f="186">four</w>
+ <w f="186">say</w>
+ <w f="185">information</w>
+ <w f="185">under</w>
+ <w f="185">page</w>
+ <w f="185">many</w>
+ <w f="185">little</w>
+ <w f="185">thanks</w>
+ <w f="185">okay</w>
+ <w f="185">five</w>
+ <w f="185">we're</w>
+ <w f="185">between</w>
+ <w f="184">use</w>
+ <w f="184">come</w>
+ <w f="184">years</w>
+ <w f="184">office</w>
+ <w f="184">house</w>
+ <w f="184">search</w>
+ <w f="184">free</w>
+ <w f="183">next</w>
+ <w f="183">without</w>
+ <w f="183">still</w>
+ <w f="183">around</w>
+ <w f="183">I've</w>
+ <w f="183">business</w>
+ <w f="183">part</w>
+ <w f="183">every</w>
+ <w f="183">bye</w>
+ <w f="183">upon</w>
+ <w f="183">you're</w>
+ <w f="183">state</w>
+ <w f="183">life</w>
+ <w f="183">year</w>
+ <w f="182">thing</w>
+ <w f="182">since</w>
+ <w f="182">things</w>
+ <w f="182">something</w>
+ <w f="182">long</w>
+ <w f="182">got</w>
+ <w f="182">while</w>
+ <w f="182">I'll</w>
+ <w f="182">help</w>
+ <w f="182">service</w>
+ <w f="182">really</w>
+ <w f="182">must</w>
+ <w f="182">does</w>
+ <w f="182">name</w>
+ <w f="181">both</w>
+ <w f="181">six</w>
+ <w f="181">want</w>
+ <w f="181">same</w>
+ <w f="181">each</w>
+ <w f="181">yet</w>
+ <w f="181">let</w>
+ <w f="181">view</w>
+ <w f="181">place</w>
+ <w f="181">another</w>
+ <w f="181">company</w>
+ <w f="181">talk</w>
+ <w f="181">might</w>
+ <w f="181">am</w>
+ <w f="181">though</w>
+ <w f="181">find</w>
+ <w f="180">details</w>
+ <w f="180">look</w>
+ <w f="180">world</w>
+ <w f="180">old</w>
+ <w f="180">called</w>
+ <w f="180">case</w>
+ <w f="180">system</w>
+ <w f="180">news</w>
+ <w f="179">used</w>
+ <w f="179">contact</w>
+ <w f="179">never</w>
+ <w f="179">seven</w>
+ <w f="179">city</w>
+ <w f="179">until</w>
+ <w f="179">during</w>
+ <w f="179">set</w>
+ <w f="179">why</w>
+ <w f="179">point</w>
+ <w f="179">twenty</w>
+ <w f="179">high</w>
+ <w f="179">love</w>
+ <w f="179">services</w>
+ <w f="170">niño</w>
+ <w f="170">María</w>
+ <w f="70">car</w>
+ <w f="0">hmmm</w>
+ <w f="0">hon</w>
+ <w f="0">tty</w>
+ <w f="0">ttyl</w>
+ <w f="0">txt</w>
+ <w f="0">ur</w>
+ <w f="0">wah</w>
+ <w f="0">whatcha</w>
+ <w f="0">woah</w>
+ <w f="0">ya</w>
+ <w f="0">yea</w>
+ <w f="0">yeh</w>
+ <w f="0">yessir</w>
+ <w f="0">yikes</w>
+ <w f="0">yrs</w>
+</wordlist>
diff --git a/tests/res/raw/test.dict b/tests/res/raw/test.dict
new file mode 100644
index 000000000..6a5d6d794
--- /dev/null
+++ b/tests/res/raw/test.dict
Binary files differ
diff --git a/tests/res/raw/testtext.txt b/tests/res/raw/testtext.txt
new file mode 100644
index 000000000..eca20c05f
--- /dev/null
+++ b/tests/res/raw/testtext.txt
@@ -0,0 +1,24 @@
+This text is used as test text for measuring performance of dictionary prediction. Any text can be put into this file to test the performance (total keystroke savings).
+When you think about “information,” what probably comes to mind are streams of words and numbers. Google’s pretty good at organizing these types of information, but consider all the things you can’t express with words: what does it look like in the middle of a sandstorm? What are some great examples of Art Nouveau architecture? Should I consider wedding cupcakes instead of a traditional cake?
+This is why we built Google Images in 2001. We realized that for many searches, the best answer wasn’t text—it was an image or a set of images. The service has grown quite a bit since then. In 2001, we indexed around 250 million images. By 2005, we had indexed over 1 billion. And today, we have an index of over 10 billion images.
+It’s not just about quantity, though. Over the past decade we’ve been baking deep computer science into Google Images to make it even faster and easier for you to find precisely the right images. We not only find images for pretty much anything you type in; we can also instantly pull out images of clip art, line drawings, faces and even colors.
+There’s even more sophisticated computer vision technology powering our “Similar images” tool. For example, did you know there are nine subspecies of leopards, each with a distinct pattern of spots? Google Images can recognize the difference, returning just leopards of a particular subspecies. It can tell you the name of the subspecies in a particular image—even if that image isn’t labeled—because other similar leopard images on the web are labeled with that subspecies’s name.
+And our “Similar colors” refinement doesn’t just return images based on the overall color of an image. If it did, lots of images would simply be classified as “white.” If you’re looking for [tulips] and you refine results to “white,” you really want images in which the tulips themselves are white—not the surrounding image. It takes some heavy-duty algorithmic wizardry and processing power for a search engine to understand what the items of interest are in all the images out there.
+Those are just a few of the technologies we’ve built to make Google Images more useful. Meanwhile, the quantity and variety of images on the web has ballooned since 2001, and images have become one of the most popular types of content people search for. So over the next few days we’re rolling out an update to Google Images to match the scope and beauty of this fast-growing visual web, and to bring to the surface some of the powerful technology behind Images.
+Here’s what’s new in this refreshed design of Google Images:
+Dense tiled layout designed to make it easy to look at lots of images at once. We want to get the app out of the way so you can find what you’re really looking for.
+Instant scrolling between pages, without letting you get lost in the images. You can now get up to 1,000 images, all in one scrolling page. And we’ll show small, unobtrusive page numbers so you don’t lose track of where you are.
+Larger thumbnail previews on the results page, designed for modern browsers and high-res screens.
+A hover pane that appears when you mouse over a given thumbnail image, giving you a larger preview, more info about the image and other image-specific features such as “Similar images.”
+Once you click on an image, you’re taken to a new landing page that displays a large image in context, with the website it’s hosted on visible right behind it. Click anywhere outside the image, and you’re right in the original page where you can learn more about the source and context.
+Optimized keyboard navigation for faster scrolling through many pages, taking advantage of standard web keyboard shortcuts such as Page Up / Page Down. It’s all about getting you to the info you need quickly, so you can get on with actually building that treehouse or buying those flowers.
+Apple's not really ready to say it's sorry about the iPhone 4 antenna design, but it is willing to give all you darn squeaky wheels free cases for your trouble. Since Apple can't build its own Bumpers fast enough, it will give you a few options and let you decide, then send it your way for free as long as you purchased the phone before September 30th. Not good enough for you? Well, if you already bought a bumper from Apple you'll get a refund, and you can also return your phone for a full refund within 30 days as long as it's unharmed.
+This solution comes at the end of 22 days of Apple engineers "working their butts off," according to Steve, with "physics" ultimately being pinned as the main culprit. Apple claims you can replicate the left-handed "death grip" bar-dropping problem on the BlackBerry Bold 9700, HTC Droid Eris, and Samsung Omnia II, and that "phones aren't perfect." Steve also claims that only 0.55% of people who bought the iPhone 4 have called into AppleCare to complain about the antenna, and the phone has a 1.7% return rate at AT&T, compared to 6% with the 3GS, though he would cop to a slight increase in dropped calls over the iPhone 3GS. For this Steve has what he confesses to be a pet theory: that 3GS users were using the case they had from the 3G, and therefore weren't met with the horrible reality of a naked, call dropping handset. Hence the free case solution, which will probably satisfy some, infuriate others, and never even blip onto the radar of many of the massive horde of consumers that's devoured this product in unprecedented numbers.
+Update: Our own Richard Lai just waltzed down to the Regent Street Apple Store in London with his iPhone Bumper receipt in hand. A few minutes later he left with cold, hard cash, and kept the Bumper to boot. Seems as if the refund effort is a go, at least over in the UK.
+Update 2: We've heard from several tipsters saying Apple no longer does Bumper refunds at its stores; customers will now have to make an online claim instead. Looks like we got super lucky.
+If you have ever received an instant message, text message, or any text-based chat message that seemed to be written in a foreign language, this Webopedia Quick Reference will help you decipher the text chat lingo by providing the definitions to more than 1,300 chat, text message, and Twitter abbreviations.
+With the popularity and rise in real-time text-based communications, such as Facebook, Twitter, instant messaging, e-mail, Internet and online gaming services, chat rooms, discussion boards and mobile phone text messaging (SMS), came the emergence of a new language tailored to the immediacy and compactness of these new communication media.
+While it does seem incredible that there are so many chat abbreviations, remember that different chat abbreviations are used by different groups of people when communicating online. Some of the following chat abbreviations may be familiar to you, while others may be foreign because they are used by a group of people with different online interests and hobbies than your own. For example, people playing online games are likely to use chat abbreviations that are different than those used by someone running a financial blog updating their Twitter status.
+Twitter is a free microblog, or social messaging tool that lets people stay connected through brief text message updates up to 140 characters in length. Twitter is based on you answering the question "What are you doing?" You then post thoughts, observations and goings-on during the day in answer to that question. Your update is posted on your Twitter profile page through SMS text messaging, the Twitter Web site, instant messaging, RSS, e-mail or through other social applications and sites, such as Facebook.
+As with any new social medium, there is an entire vocabulary that users of the Twitter service adopt. Many of the new lingo Twitter-based terms and phrases are used to describe the collection of people who use the service, while other terms are used in reference to describe specific functions and features of the service itself. Also, there are a number of "chat terms," which are basically shorthand abbreviations that users often include in their tweets. Lastly, our guide also provides descriptions to a number of Twitter tools and applications that you can use to enhance your Twitter experience.
+Here are definitions to more than 100 Twitter-related abbreviations, words, phrases, and tools that are associated with the Twitter microblogging service. If you know of a Twitter slang term or application name that is not included in our Twitter Dictionary, please let us know.
diff --git a/tests/src/com/android/inputmethod/latin/ImeLoggerTests.java b/tests/src/com/android/inputmethod/latin/ImeLoggerTests.java
new file mode 100644
index 000000000..234559bb7
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/ImeLoggerTests.java
@@ -0,0 +1,59 @@
+package com.android.inputmethod.latin;
+
+import android.test.ServiceTestCase;
+
+public class ImeLoggerTests extends ServiceTestCase<LatinIME> {
+
+ private static final String WORD_SEPARATORS
+ = ".\u0009\u0020,;:!?\n()[]*&@{}<>;_+=|\\u0022";
+
+ public ImeLoggerTests() {
+ super(LatinIME.class);
+ }
+ static LatinImeLogger sLogger;
+ @Override
+ protected void setUp() {
+ try {
+ super.setUp();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ setupService();
+ // startService(null); // can't be started because VoiceInput can't be found.
+ final LatinIME context = getService();
+ context.mWordSeparators = WORD_SEPARATORS;
+ LatinImeLogger.init(context);
+ sLogger = LatinImeLogger.sLatinImeLogger;
+ }
+ /*********************** Tests *********************/
+ public void testRingBuffer() {
+ for (int i = 0; i < sLogger.mRingCharBuffer.BUFSIZE * 2; ++i) {
+ LatinImeLogger.logOnDelete();
+ }
+ assertEquals("", sLogger.mRingCharBuffer.getLastString());
+ LatinImeLogger.logOnInputChar('t');
+ LatinImeLogger.logOnInputChar('g');
+ LatinImeLogger.logOnInputChar('i');
+ LatinImeLogger.logOnInputChar('s');
+ LatinImeLogger.logOnInputChar(' ');
+ LatinImeLogger.logOnAutoSuggestion("tgis", "this");
+ LatinImeLogger.logOnInputChar(' ');
+ LatinImeLogger.logOnDelete();
+ assertEquals("", sLogger.mRingCharBuffer.getLastString());
+ LatinImeLogger.logOnDelete();
+ assertEquals("tgis", sLogger.mRingCharBuffer.getLastString());
+ assertEquals("tgis", LatinImeLogger.sLastAutoSuggestBefore);
+ LatinImeLogger.logOnAutoSuggestionCanceled();
+ assertEquals("", LatinImeLogger.sLastAutoSuggestBefore);
+ LatinImeLogger.logOnDelete();
+ assertEquals("tgi", sLogger.mRingCharBuffer.getLastString());
+ for (int i = 0; i < sLogger.mRingCharBuffer.BUFSIZE * 2; ++i) {
+ LatinImeLogger.logOnDelete();
+ }
+ assertEquals("", sLogger.mRingCharBuffer.getLastString());
+ for (int i = 0; i < sLogger.mRingCharBuffer.BUFSIZE * 2; ++i) {
+ LatinImeLogger.logOnInputChar('a');
+ }
+ assertEquals(sLogger.mRingCharBuffer.BUFSIZE, sLogger.mRingCharBuffer.length);
+ }
+}
diff --git a/tests/src/com/android/inputmethod/latin/tests/SuggestHelper.java b/tests/src/com/android/inputmethod/latin/tests/SuggestHelper.java
new file mode 100644
index 000000000..107f04c7c
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/tests/SuggestHelper.java
@@ -0,0 +1,193 @@
+/*
+ * Copyright (C) 2010 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.tests;
+
+import android.content.Context;
+import android.test.AndroidTestCase;
+import android.text.TextUtils;
+import android.util.Log;
+import com.android.inputmethod.latin.Suggest;
+import com.android.inputmethod.latin.WordComposer;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.channels.Channels;
+import java.util.List;
+
+public class SuggestHelper {
+ private Suggest mSuggest;
+ private final String TAG;
+
+ public SuggestHelper(String tag, Context context, int[] resId) {
+ TAG = tag;
+ InputStream[] res = null;
+ try {
+ // merging separated dictionary into one if dictionary is separated
+ int total = 0;
+ res = new InputStream[resId.length];
+ for (int i = 0; i < resId.length; i++) {
+ res[i] = context.getResources().openRawResource(resId[i]);
+ total += res[i].available();
+ }
+
+ ByteBuffer byteBuffer =
+ ByteBuffer.allocateDirect(total).order(ByteOrder.nativeOrder());
+ int got = 0;
+ for (int i = 0; i < resId.length; i++) {
+ got += Channels.newChannel(res[i]).read(byteBuffer);
+ }
+ if (got != total) {
+ Log.w(TAG, "Read " + got + " bytes, expected " + total);
+ } else {
+ mSuggest = new Suggest(context, byteBuffer);
+ Log.i(TAG, "Created mSuggest " + total + " bytes");
+ }
+ } catch (IOException e) {
+ Log.w(TAG, "No available memory for binary dictionary");
+ } finally {
+ try {
+ for (int i = 0;i < res.length; i++) {
+ res[i].close();
+ }
+ } catch (IOException e) {
+ Log.w(TAG, "Failed to close input stream");
+ }
+ }
+ mSuggest.setAutoTextEnabled(false);
+ mSuggest.setCorrectionMode(Suggest.CORRECTION_FULL_BIGRAM);
+ }
+
+ private WordComposer createWordComposer(CharSequence s) {
+ WordComposer word = new WordComposer();
+ for (int i = 0; i < s.length(); i++) {
+ final char c = s.charAt(i);
+ int[] codes;
+ // If it's not a lowercase letter, don't find adjacent letters
+ if (c < 'a' || c > 'z') {
+ codes = new int[] { c };
+ } else {
+ codes = adjacents[c - 'a'];
+ }
+ word.add(c, codes);
+ }
+ return word;
+ }
+
+ private void showList(String title, List<CharSequence> suggestions) {
+ Log.i(TAG, title);
+ for (int i = 0; i < suggestions.size(); i++) {
+ Log.i(title, suggestions.get(i) + ", ");
+ }
+ }
+
+ private boolean isDefaultSuggestion(List<CharSequence> suggestions, CharSequence word) {
+ // Check if either the word is what you typed or the first alternative
+ return suggestions.size() > 0 &&
+ (/*TextUtils.equals(suggestions.get(0), word) || */
+ (suggestions.size() > 1 && TextUtils.equals(suggestions.get(1), word)));
+ }
+
+ boolean isDefaultSuggestion(CharSequence typed, CharSequence expected) {
+ WordComposer word = createWordComposer(typed);
+ List<CharSequence> suggestions = mSuggest.getSuggestions(null, word, false, null);
+ return isDefaultSuggestion(suggestions, expected);
+ }
+
+ boolean isDefaultCorrection(CharSequence typed, CharSequence expected) {
+ WordComposer word = createWordComposer(typed);
+ List<CharSequence> suggestions = mSuggest.getSuggestions(null, word, false, null);
+ return isDefaultSuggestion(suggestions, expected) && mSuggest.hasMinimalCorrection();
+ }
+
+ boolean isASuggestion(CharSequence typed, CharSequence expected) {
+ WordComposer word = createWordComposer(typed);
+ List<CharSequence> suggestions = mSuggest.getSuggestions(null, word, false, null);
+ for (int i = 1; i < suggestions.size(); i++) {
+ if (TextUtils.equals(suggestions.get(i), expected)) return true;
+ }
+ return false;
+ }
+
+ private void getBigramSuggestions(CharSequence previous, CharSequence typed) {
+ if(!TextUtils.isEmpty(previous) && (typed.length() > 1)) {
+ WordComposer firstChar = createWordComposer(typed.charAt(0) + "");
+ mSuggest.getSuggestions(null, firstChar, false, previous);
+ }
+ }
+
+ boolean isDefaultNextSuggestion(CharSequence previous, CharSequence typed,
+ CharSequence expected) {
+ WordComposer word = createWordComposer(typed);
+ getBigramSuggestions(previous, typed);
+ List<CharSequence> suggestions = mSuggest.getSuggestions(null, word, false, previous);
+ return isDefaultSuggestion(suggestions, expected);
+ }
+
+ boolean isDefaultNextCorrection(CharSequence previous, CharSequence typed,
+ CharSequence expected) {
+ WordComposer word = createWordComposer(typed);
+ getBigramSuggestions(previous, typed);
+ List<CharSequence> suggestions = mSuggest.getSuggestions(null, word, false, previous);
+ return isDefaultSuggestion(suggestions, expected) && mSuggest.hasMinimalCorrection();
+ }
+
+ boolean isASuggestion(CharSequence previous, CharSequence typed,
+ CharSequence expected) {
+ WordComposer word = createWordComposer(typed);
+ getBigramSuggestions(previous, typed);
+ List<CharSequence> suggestions = mSuggest.getSuggestions(null, word, false, previous);
+ for (int i = 1; i < suggestions.size(); i++) {
+ if (TextUtils.equals(suggestions.get(i), expected)) return true;
+ }
+ return false;
+ }
+
+ boolean isValid(CharSequence typed) {
+ return mSuggest.isValidWord(typed);
+ }
+
+ final int[][] adjacents = {
+ {'a','s','w','q',-1},
+ {'b','h','v','n','g','j',-1},
+ {'c','v','f','x','g',},
+ {'d','f','r','e','s','x',-1},
+ {'e','w','r','s','d',-1},
+ {'f','g','d','c','t','r',-1},
+ {'g','h','f','y','t','v',-1},
+ {'h','j','u','g','b','y',-1},
+ {'i','o','u','k',-1},
+ {'j','k','i','h','u','n',-1},
+ {'k','l','o','j','i','m',-1},
+ {'l','k','o','p',-1},
+ {'m','k','n','l',-1},
+ {'n','m','j','k','b',-1},
+ {'o','p','i','l',-1},
+ {'p','o',-1},
+ {'q','w',-1},
+ {'r','t','e','f',-1},
+ {'s','d','e','w','a','z',-1},
+ {'t','y','r',-1},
+ {'u','y','i','h','j',-1},
+ {'v','b','g','c','h',-1},
+ {'w','e','q',-1},
+ {'x','c','d','z','f',-1},
+ {'y','u','t','h','g',-1},
+ {'z','s','x','a','d',-1},
+ };
+}
diff --git a/tests/src/com/android/inputmethod/latin/tests/SuggestPerformanceTests.java b/tests/src/com/android/inputmethod/latin/tests/SuggestPerformanceTests.java
new file mode 100644
index 000000000..473c440f9
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/tests/SuggestPerformanceTests.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2010 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.tests;
+
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import java.io.InputStreamReader;
+import java.io.InputStream;
+import java.io.BufferedReader;
+import java.util.StringTokenizer;
+import java.util.regex.Pattern;
+
+public class SuggestPerformanceTests extends AndroidTestCase {
+ private static final String TAG = "SuggestPerformanceTests";
+
+ private String mTestText;
+ private SuggestHelper sh;
+
+ @Override
+ protected void setUp() {
+ // TODO Figure out a way to directly using the dictionary rather than copying it over
+
+ // For testing with real dictionary, TEMPORARILY COPY main dictionary into test directory.
+ // DO NOT SUBMIT real dictionary under test directory.
+ //int[] resId = new int[] { R.raw.main0, R.raw.main1, R.raw.main2 };
+
+ int[] resId = new int[] { R.raw.test };
+
+ sh = new SuggestHelper(TAG, getTestContext(), resId);
+ loadString();
+ }
+
+ private void loadString() {
+ try {
+ InputStream is = getTestContext().getResources().openRawResource(R.raw.testtext);
+ BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+ StringBuilder sb = new StringBuilder();
+ String line = reader.readLine();
+ while (line != null) {
+ sb.append(line + " ");
+ line = reader.readLine();
+ }
+ mTestText = sb.toString();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ /************************** Helper functions ************************/
+ private int lookForSuggestion(String prevWord, String currentWord) {
+ for (int i = 1; i < currentWord.length(); i++) {
+ if (i == 1) {
+ if (sh.isDefaultNextSuggestion(prevWord, currentWord.substring(0, i),
+ currentWord)) {
+ return i;
+ }
+ } else {
+ if (sh.isDefaultNextCorrection(prevWord, currentWord.substring(0, i),
+ currentWord)) {
+ return i;
+ }
+ }
+ }
+ return currentWord.length();
+ }
+
+ private double runText(boolean withBigrams) {
+ StringTokenizer st = new StringTokenizer(mTestText);
+ String prevWord = null;
+ int typeCount = 0;
+ int characterCount = 0; // without space
+ int wordCount = 0;
+ while (st.hasMoreTokens()) {
+ String currentWord = st.nextToken();
+ boolean endCheck = false;
+ if (currentWord.matches("[\\w]*[\\.|?|!|*|@|&|/|:|;]")) {
+ currentWord = currentWord.substring(0, currentWord.length() - 1);
+ endCheck = true;
+ }
+ if (withBigrams && prevWord != null) {
+ typeCount += lookForSuggestion(prevWord, currentWord);
+ } else {
+ typeCount += lookForSuggestion(null, currentWord);
+ }
+ characterCount += currentWord.length();
+ if (!endCheck) prevWord = currentWord;
+ wordCount++;
+ }
+
+ double result = (double) (characterCount - typeCount) / characterCount * 100;
+ if (withBigrams) {
+ Log.i(TAG, "with bigrams -> " + result + " % saved!");
+ } else {
+ Log.i(TAG, "without bigrams -> " + result + " % saved!");
+ }
+ Log.i(TAG, "\ttotal number of words: " + wordCount);
+ Log.i(TAG, "\ttotal number of characters: " + mTestText.length());
+ Log.i(TAG, "\ttotal number of characters without space: " + characterCount);
+ Log.i(TAG, "\ttotal number of characters typed: " + typeCount);
+ return result;
+ }
+
+
+ /************************** Performance Tests ************************/
+ /**
+ * Compare the Suggest with and without bigram
+ * Check the log for detail
+ */
+ public void testSuggestPerformance() {
+ assertTrue(runText(false) < runText(true));
+ }
+}
diff --git a/tests/src/com/android/inputmethod/latin/tests/SuggestTests.java b/tests/src/com/android/inputmethod/latin/tests/SuggestTests.java
new file mode 100644
index 000000000..a42422b91
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/tests/SuggestTests.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2010 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.tests;
+
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+public class SuggestTests extends AndroidTestCase {
+ private static final String TAG = "SuggestTests";
+
+ private SuggestHelper sh;
+
+ @Override
+ protected void setUp() {
+ int[] resId = new int[] { R.raw.test };
+ sh = new SuggestHelper(TAG, getTestContext(), resId);
+ }
+
+ /************************** Tests ************************/
+
+ /**
+ * Tests for simple completions of one character.
+ */
+ public void testCompletion1char() {
+ assertTrue(sh.isDefaultSuggestion("peopl", "people"));
+ assertTrue(sh.isDefaultSuggestion("abou", "about"));
+ assertTrue(sh.isDefaultSuggestion("thei", "their"));
+ }
+
+ /**
+ * Tests for simple completions of two characters.
+ */
+ public void testCompletion2char() {
+ assertTrue(sh.isDefaultSuggestion("peop", "people"));
+ assertTrue(sh.isDefaultSuggestion("calli", "calling"));
+ assertTrue(sh.isDefaultSuggestion("busine", "business"));
+ }
+
+ /**
+ * Tests for proximity errors.
+ */
+ public void testProximityPositive() {
+ assertTrue(sh.isDefaultSuggestion("peiple", "people"));
+ assertTrue(sh.isDefaultSuggestion("peoole", "people"));
+ assertTrue(sh.isDefaultSuggestion("pwpple", "people"));
+ }
+
+ /**
+ * Tests for proximity errors - negative, when the error key is not near.
+ */
+ public void testProximityNegative() {
+ assertFalse(sh.isDefaultSuggestion("arout", "about"));
+ assertFalse(sh.isDefaultSuggestion("ire", "are"));
+ }
+
+ /**
+ * Tests for checking if apostrophes are added automatically.
+ */
+ public void testApostropheInsertion() {
+ assertTrue(sh.isDefaultSuggestion("im", "I'm"));
+ assertTrue(sh.isDefaultSuggestion("dont", "don't"));
+ }
+
+ /**
+ * Test to make sure apostrophed word is not suggested for an apostrophed word.
+ */
+ public void testApostrophe() {
+ assertFalse(sh.isDefaultSuggestion("don't", "don't"));
+ }
+
+ /**
+ * Tests for suggestion of capitalized version of a word.
+ */
+ public void testCapitalization() {
+ assertTrue(sh.isDefaultSuggestion("i'm", "I'm"));
+ assertTrue(sh.isDefaultSuggestion("sunday", "Sunday"));
+ assertTrue(sh.isDefaultSuggestion("sundat", "Sunday"));
+ }
+
+ /**
+ * Tests to see if more than one completion is provided for certain prefixes.
+ */
+ public void testMultipleCompletions() {
+ assertTrue(sh.isASuggestion("com", "come"));
+ assertTrue(sh.isASuggestion("com", "company"));
+ assertTrue(sh.isASuggestion("th", "the"));
+ assertTrue(sh.isASuggestion("th", "that"));
+ assertTrue(sh.isASuggestion("th", "this"));
+ assertTrue(sh.isASuggestion("th", "they"));
+ }
+
+ /**
+ * Does the suggestion engine recognize zero frequency words as valid words.
+ */
+ public void testZeroFrequencyAccepted() {
+ assertTrue(sh.isValid("yikes"));
+ assertFalse(sh.isValid("yike"));
+ }
+
+ /**
+ * Tests to make sure that zero frequency words are not suggested as completions.
+ */
+ public void testZeroFrequencySuggestionsNegative() {
+ assertFalse(sh.isASuggestion("yike", "yikes"));
+ assertFalse(sh.isASuggestion("what", "whatcha"));
+ }
+
+ /**
+ * Tests to ensure that words with large edit distances are not suggested, in some cases
+ * and not considered corrections, in some cases.
+ */
+ public void testTooLargeEditDistance() {
+ assertFalse(sh.isASuggestion("sniyr", "about"));
+ assertFalse(sh.isDefaultCorrection("rjw", "the"));
+ }
+
+ /**
+ * Make sure sh.isValid is case-sensitive.
+ */
+ public void testValidityCaseSensitivity() {
+ assertTrue(sh.isValid("Sunday"));
+ assertFalse(sh.isValid("sunday"));
+ }
+
+ /**
+ * Are accented forms of words suggested as corrections?
+ */
+ public void testAccents() {
+ // ni<LATIN SMALL LETTER N WITH TILDE>o
+ assertTrue(sh.isDefaultCorrection("nino", "ni\u00F1o"));
+ // ni<LATIN SMALL LETTER N WITH TILDE>o
+ assertTrue(sh.isDefaultCorrection("nimo", "ni\u00F1o"));
+ // Mar<LATIN SMALL LETTER I WITH ACUTE>a
+ assertTrue(sh.isDefaultCorrection("maria", "Mar\u00EDa"));
+ }
+
+ /**
+ * Make sure bigrams are showing when first character is typed
+ * and don't show any when there aren't any
+ */
+ public void testBigramsAtFirstChar() {
+ assertTrue(sh.isDefaultNextSuggestion("about", "p", "part"));
+ assertTrue(sh.isDefaultNextSuggestion("I'm", "a", "about"));
+ assertTrue(sh.isDefaultNextSuggestion("about", "b", "business"));
+ assertTrue(sh.isASuggestion("about", "b", "being"));
+ assertFalse(sh.isDefaultNextSuggestion("about", "p", "business"));
+ }
+
+ /**
+ * Make sure bigrams score affects the original score
+ */
+ public void testBigramsScoreEffect() {
+ assertTrue(sh.isDefaultCorrection("pa", "page"));
+ assertTrue(sh.isDefaultNextCorrection("about", "pa", "part"));
+ assertTrue(sh.isDefaultCorrection("sa", "said"));
+ assertTrue(sh.isDefaultNextCorrection("from", "sa", "same"));
+ }
+}