diff options
Diffstat (limited to 'java')
204 files changed, 7574 insertions, 586 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 Binary files differdeleted file mode 100755 index ca7637552..000000000 --- a/java/res/drawable-en-hdpi/sym_keyboard_feedback_delete.png +++ /dev/null diff --git a/java/res/drawable-hdpi/btn_close_normal.png b/java/res/drawable-hdpi/btn_close_normal.png Binary files differnew file mode 100644 index 000000000..38b49f1a3 --- /dev/null +++ b/java/res/drawable-hdpi/btn_close_normal.png diff --git a/java/res/drawable-hdpi/btn_close_pressed.png b/java/res/drawable-hdpi/btn_close_pressed.png Binary files differnew file mode 100644 index 000000000..aa9ea49f0 --- /dev/null +++ b/java/res/drawable-hdpi/btn_close_pressed.png diff --git a/java/res/drawable-hdpi/btn_close_selected.png b/java/res/drawable-hdpi/btn_close_selected.png Binary files differnew file mode 100644 index 000000000..870c670f7 --- /dev/null +++ b/java/res/drawable-hdpi/btn_close_selected.png 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 Binary files differnew file mode 100644 index 000000000..b67732cd4 --- /dev/null +++ b/java/res/drawable-hdpi/btn_keyboard_key_normal_off_stone.9.png 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 Binary files differnew file mode 100644 index 000000000..534f1cdfd --- /dev/null +++ b/java/res/drawable-hdpi/btn_keyboard_key_normal_on_stone.9.png 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 Binary files differnew file mode 100644 index 000000000..fba10b888 --- /dev/null +++ b/java/res/drawable-hdpi/btn_keyboard_key_normal_stone.9.png diff --git a/java/res/drawable-hdpi/btn_keyboard_normal_metal.9.png b/java/res/drawable-hdpi/btn_keyboard_normal_metal.9.png Binary files differnew file mode 100644 index 000000000..b29d6d174 --- /dev/null +++ b/java/res/drawable-hdpi/btn_keyboard_normal_metal.9.png diff --git a/java/res/drawable-hdpi/btn_keyboard_toggle_off.png b/java/res/drawable-hdpi/btn_keyboard_toggle_off.png Binary files differnew file mode 100644 index 000000000..bfe78402f --- /dev/null +++ b/java/res/drawable-hdpi/btn_keyboard_toggle_off.png diff --git a/java/res/drawable-hdpi/btn_keyboard_toggle_on.png b/java/res/drawable-hdpi/btn_keyboard_toggle_on.png Binary files differnew file mode 100644 index 000000000..0a1221e97 --- /dev/null +++ b/java/res/drawable-hdpi/btn_keyboard_toggle_on.png diff --git a/java/res/drawable-hdpi/btn_led_off.9.png b/java/res/drawable-hdpi/btn_led_off.9.png Binary files differnew file mode 100644 index 000000000..a60f96539 --- /dev/null +++ b/java/res/drawable-hdpi/btn_led_off.9.png diff --git a/java/res/drawable-hdpi/btn_led_on.9.png b/java/res/drawable-hdpi/btn_led_on.9.png Binary files differnew file mode 100644 index 000000000..c90260967 --- /dev/null +++ b/java/res/drawable-hdpi/btn_led_on.9.png 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 Binary files differnew file mode 100644 index 000000000..ab6c036c3 --- /dev/null +++ b/java/res/drawable-hdpi/dialog_top_dark_bottom_medium.9.png diff --git a/java/res/drawable-hdpi/dialog_top_dark_bottom_medium.png b/java/res/drawable-hdpi/dialog_top_dark_bottom_medium.png Binary files differdeleted file mode 100755 index 7c79a4f90..000000000 --- a/java/res/drawable-hdpi/dialog_top_dark_bottom_medium.png +++ /dev/null diff --git a/java/res/drawable-hdpi/keyboard_key_feedback_background.9.png b/java/res/drawable-hdpi/keyboard_key_feedback_background.9.png Binary files differnew file mode 100644 index 000000000..6ba42db82 --- /dev/null +++ b/java/res/drawable-hdpi/keyboard_key_feedback_background.9.png 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 Binary files differnew file mode 100644 index 000000000..4d0b60109 --- /dev/null +++ b/java/res/drawable-hdpi/keyboard_key_feedback_more_background.9.png diff --git a/java/res/drawable-hdpi/keyboard_popup_panel_background.9.png b/java/res/drawable-hdpi/keyboard_popup_panel_background.9.png Binary files differnew file mode 100644 index 000000000..8e2461b3f --- /dev/null +++ b/java/res/drawable-hdpi/keyboard_popup_panel_background.9.png 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 Binary files differnew file mode 100644 index 000000000..fd7366e20 --- /dev/null +++ b/java/res/drawable-hdpi/keyboard_popup_panel_trans_background.9.png diff --git a/java/res/drawable-hdpi/sym_bkeyboard_123_mic.png b/java/res/drawable-hdpi/sym_bkeyboard_123_mic.png Binary files differnew file mode 100644 index 000000000..3e4eff698 --- /dev/null +++ b/java/res/drawable-hdpi/sym_bkeyboard_123_mic.png diff --git a/java/res/drawable-hdpi/sym_bkeyboard_delete.png b/java/res/drawable-hdpi/sym_bkeyboard_delete.png Binary files differnew file mode 100644 index 000000000..1d24cc85c --- /dev/null +++ b/java/res/drawable-hdpi/sym_bkeyboard_delete.png diff --git a/java/res/drawable-hdpi/sym_bkeyboard_done.png b/java/res/drawable-hdpi/sym_bkeyboard_done.png Binary files differnew file mode 100644 index 000000000..b77803d21 --- /dev/null +++ b/java/res/drawable-hdpi/sym_bkeyboard_done.png diff --git a/java/res/drawable-hdpi/sym_bkeyboard_globe.png b/java/res/drawable-hdpi/sym_bkeyboard_globe.png Binary files differnew file mode 100644 index 000000000..f5dbe0cd1 --- /dev/null +++ b/java/res/drawable-hdpi/sym_bkeyboard_globe.png diff --git a/java/res/drawable-hdpi/sym_bkeyboard_mic.png b/java/res/drawable-hdpi/sym_bkeyboard_mic.png Binary files differnew file mode 100644 index 000000000..512f46080 --- /dev/null +++ b/java/res/drawable-hdpi/sym_bkeyboard_mic.png diff --git a/java/res/drawable-hdpi/sym_bkeyboard_num0.png b/java/res/drawable-hdpi/sym_bkeyboard_num0.png Binary files differnew file mode 100644 index 000000000..678a790de --- /dev/null +++ b/java/res/drawable-hdpi/sym_bkeyboard_num0.png diff --git a/java/res/drawable-hdpi/sym_bkeyboard_num1.png b/java/res/drawable-hdpi/sym_bkeyboard_num1.png Binary files differnew file mode 100644 index 000000000..4e68e35b3 --- /dev/null +++ b/java/res/drawable-hdpi/sym_bkeyboard_num1.png diff --git a/java/res/drawable-hdpi/sym_bkeyboard_num2.png b/java/res/drawable-hdpi/sym_bkeyboard_num2.png Binary files differnew file mode 100644 index 000000000..546663fda --- /dev/null +++ b/java/res/drawable-hdpi/sym_bkeyboard_num2.png diff --git a/java/res/drawable-hdpi/sym_bkeyboard_num3.png b/java/res/drawable-hdpi/sym_bkeyboard_num3.png Binary files differnew file mode 100644 index 000000000..57f9a8d8e --- /dev/null +++ b/java/res/drawable-hdpi/sym_bkeyboard_num3.png diff --git a/java/res/drawable-hdpi/sym_bkeyboard_num4.png b/java/res/drawable-hdpi/sym_bkeyboard_num4.png Binary files differnew file mode 100644 index 000000000..de504388f --- /dev/null +++ b/java/res/drawable-hdpi/sym_bkeyboard_num4.png diff --git a/java/res/drawable-hdpi/sym_bkeyboard_num5.png b/java/res/drawable-hdpi/sym_bkeyboard_num5.png Binary files differnew file mode 100644 index 000000000..1d2e1ef89 --- /dev/null +++ b/java/res/drawable-hdpi/sym_bkeyboard_num5.png diff --git a/java/res/drawable-hdpi/sym_bkeyboard_num6.png b/java/res/drawable-hdpi/sym_bkeyboard_num6.png Binary files differnew file mode 100644 index 000000000..39788b727 --- /dev/null +++ b/java/res/drawable-hdpi/sym_bkeyboard_num6.png diff --git a/java/res/drawable-hdpi/sym_bkeyboard_num7.png b/java/res/drawable-hdpi/sym_bkeyboard_num7.png Binary files differnew file mode 100644 index 000000000..fff6f27bf --- /dev/null +++ b/java/res/drawable-hdpi/sym_bkeyboard_num7.png diff --git a/java/res/drawable-hdpi/sym_bkeyboard_num8.png b/java/res/drawable-hdpi/sym_bkeyboard_num8.png Binary files differnew file mode 100644 index 000000000..8cc1a955e --- /dev/null +++ b/java/res/drawable-hdpi/sym_bkeyboard_num8.png diff --git a/java/res/drawable-hdpi/sym_bkeyboard_num9.png b/java/res/drawable-hdpi/sym_bkeyboard_num9.png Binary files differnew file mode 100644 index 000000000..021742509 --- /dev/null +++ b/java/res/drawable-hdpi/sym_bkeyboard_num9.png diff --git a/java/res/drawable-hdpi/sym_bkeyboard_numalt.png b/java/res/drawable-hdpi/sym_bkeyboard_numalt.png Binary files differnew file mode 100644 index 000000000..200714f66 --- /dev/null +++ b/java/res/drawable-hdpi/sym_bkeyboard_numalt.png diff --git a/java/res/drawable-hdpi/sym_bkeyboard_numpound.png b/java/res/drawable-hdpi/sym_bkeyboard_numpound.png Binary files differnew file mode 100644 index 000000000..0a46122b2 --- /dev/null +++ b/java/res/drawable-hdpi/sym_bkeyboard_numpound.png diff --git a/java/res/drawable-hdpi/sym_bkeyboard_numstar.png b/java/res/drawable-hdpi/sym_bkeyboard_numstar.png Binary files differnew file mode 100644 index 000000000..ca22bd535 --- /dev/null +++ b/java/res/drawable-hdpi/sym_bkeyboard_numstar.png diff --git a/java/res/drawable-hdpi/sym_bkeyboard_return.png b/java/res/drawable-hdpi/sym_bkeyboard_return.png Binary files differnew file mode 100644 index 000000000..426e1599e --- /dev/null +++ b/java/res/drawable-hdpi/sym_bkeyboard_return.png diff --git a/java/res/drawable-hdpi/sym_bkeyboard_search.png b/java/res/drawable-hdpi/sym_bkeyboard_search.png Binary files differnew file mode 100644 index 000000000..1b6f884fa --- /dev/null +++ b/java/res/drawable-hdpi/sym_bkeyboard_search.png diff --git a/java/res/drawable-hdpi/sym_bkeyboard_shift.png b/java/res/drawable-hdpi/sym_bkeyboard_shift.png Binary files differnew file mode 100644 index 000000000..5a22dd309 --- /dev/null +++ b/java/res/drawable-hdpi/sym_bkeyboard_shift.png diff --git a/java/res/drawable-hdpi/sym_bkeyboard_shift_locked.png b/java/res/drawable-hdpi/sym_bkeyboard_shift_locked.png Binary files differnew file mode 100644 index 000000000..566449126 --- /dev/null +++ b/java/res/drawable-hdpi/sym_bkeyboard_shift_locked.png diff --git a/java/res/drawable-hdpi/sym_bkeyboard_space.png b/java/res/drawable-hdpi/sym_bkeyboard_space.png Binary files differnew file mode 100644 index 000000000..cd0ebe2f4 --- /dev/null +++ b/java/res/drawable-hdpi/sym_bkeyboard_space.png diff --git a/java/res/drawable-hdpi/sym_bkeyboard_tab.png b/java/res/drawable-hdpi/sym_bkeyboard_tab.png Binary files differnew file mode 100644 index 000000000..3466e1271 --- /dev/null +++ b/java/res/drawable-hdpi/sym_bkeyboard_tab.png diff --git a/java/res/drawable-hdpi/sym_bkeyboard_tabprev.png b/java/res/drawable-hdpi/sym_bkeyboard_tabprev.png Binary files differnew file mode 100644 index 000000000..eb4f0eb68 --- /dev/null +++ b/java/res/drawable-hdpi/sym_bkeyboard_tabprev.png diff --git a/java/res/drawable-hdpi/voice_swipe_hint.png b/java/res/drawable-hdpi/voice_swipe_hint.png Binary files differnew file mode 100644 index 000000000..130f83a9c --- /dev/null +++ b/java/res/drawable-hdpi/voice_swipe_hint.png 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 Binary files differnew file mode 100644 index 000000000..67a204f85 --- /dev/null +++ b/java/res/drawable-land-hdpi/btn_keyboard_key_normal_off_stone.9.png 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 Binary files differnew file mode 100644 index 000000000..63cbe60a3 --- /dev/null +++ b/java/res/drawable-land-hdpi/btn_keyboard_key_normal_on_stone.9.png 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 Binary files differnew file mode 100644 index 000000000..0dd33b429 --- /dev/null +++ b/java/res/drawable-land-hdpi/btn_keyboard_key_normal_stone.9.png 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 Binary files differdeleted file mode 100644 index bda9b8394..000000000 --- a/java/res/drawable-land-mdpi/btn_keyboard_key_normal_off.9.png +++ /dev/null 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 Binary files differnew file mode 100644 index 000000000..67a204f85 --- /dev/null +++ b/java/res/drawable-land-mdpi/btn_keyboard_key_normal_off_stone.9.png 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 Binary files differdeleted file mode 100644 index 0c16ed509..000000000 --- a/java/res/drawable-land-mdpi/btn_keyboard_key_normal_on.9.png +++ /dev/null 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 Binary files differnew file mode 100644 index 000000000..63cbe60a3 --- /dev/null +++ b/java/res/drawable-land-mdpi/btn_keyboard_key_normal_on_stone.9.png 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 Binary files differnew file mode 100644 index 000000000..0dd33b429 --- /dev/null +++ b/java/res/drawable-land-mdpi/btn_keyboard_key_normal_stone.9.png 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 Binary files differdeleted file mode 100644 index bdcf06e1b..000000000 --- a/java/res/drawable-land-mdpi/btn_keyboard_key_pressed_off.9.png +++ /dev/null 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 Binary files differdeleted file mode 100644 index 79621a9e6..000000000 --- a/java/res/drawable-land-mdpi/btn_keyboard_key_pressed_on.9.png +++ /dev/null diff --git a/java/res/drawable-mdpi/btn_close_normal.png b/java/res/drawable-mdpi/btn_close_normal.png Binary files differnew file mode 100644 index 000000000..4c6e79dc8 --- /dev/null +++ b/java/res/drawable-mdpi/btn_close_normal.png diff --git a/java/res/drawable-mdpi/btn_close_pressed.png b/java/res/drawable-mdpi/btn_close_pressed.png Binary files differnew file mode 100644 index 000000000..fc983afdc --- /dev/null +++ b/java/res/drawable-mdpi/btn_close_pressed.png diff --git a/java/res/drawable-mdpi/btn_close_selected.png b/java/res/drawable-mdpi/btn_close_selected.png Binary files differnew file mode 100644 index 000000000..f2bf91a2d --- /dev/null +++ b/java/res/drawable-mdpi/btn_close_selected.png 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 Binary files differnew file mode 100644 index 000000000..b67732cd4 --- /dev/null +++ b/java/res/drawable-mdpi/btn_keyboard_key_normal_off_stone.9.png 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 Binary files differnew file mode 100644 index 000000000..534f1cdfd --- /dev/null +++ b/java/res/drawable-mdpi/btn_keyboard_key_normal_on_stone.9.png 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 Binary files differnew file mode 100644 index 000000000..fba10b888 --- /dev/null +++ b/java/res/drawable-mdpi/btn_keyboard_key_normal_stone.9.png diff --git a/java/res/drawable-mdpi/btn_keyboard_normal_metal.9.png b/java/res/drawable-mdpi/btn_keyboard_normal_metal.9.png Binary files differnew file mode 100644 index 000000000..f4fe0a8a0 --- /dev/null +++ b/java/res/drawable-mdpi/btn_keyboard_normal_metal.9.png diff --git a/java/res/drawable-mdpi/btn_keyboard_toggle_off.png b/java/res/drawable-mdpi/btn_keyboard_toggle_off.png Binary files differnew file mode 100644 index 000000000..21399a4f3 --- /dev/null +++ b/java/res/drawable-mdpi/btn_keyboard_toggle_off.png diff --git a/java/res/drawable-mdpi/btn_keyboard_toggle_on.png b/java/res/drawable-mdpi/btn_keyboard_toggle_on.png Binary files differnew file mode 100644 index 000000000..22d5683e2 --- /dev/null +++ b/java/res/drawable-mdpi/btn_keyboard_toggle_on.png diff --git a/java/res/drawable-mdpi/btn_led_off.9.png b/java/res/drawable-mdpi/btn_led_off.9.png Binary files differnew file mode 100644 index 000000000..68ce7a67a --- /dev/null +++ b/java/res/drawable-mdpi/btn_led_off.9.png diff --git a/java/res/drawable-mdpi/btn_led_on.9.png b/java/res/drawable-mdpi/btn_led_on.9.png Binary files differnew file mode 100644 index 000000000..fe77abb08 --- /dev/null +++ b/java/res/drawable-mdpi/btn_led_on.9.png diff --git a/java/res/drawable/cancel.png b/java/res/drawable-mdpi/cancel.png Binary files differindex 081532bec..081532bec 100644 --- a/java/res/drawable/cancel.png +++ b/java/res/drawable-mdpi/cancel.png diff --git a/java/res/drawable/caution.png b/java/res/drawable-mdpi/caution.png Binary files differindex eaef53425..eaef53425 100644 --- a/java/res/drawable/caution.png +++ b/java/res/drawable-mdpi/caution.png 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 Binary files differindex 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 diff --git a/java/res/drawable/ic_dialog_alert_large.png b/java/res/drawable-mdpi/ic_dialog_alert_large.png Binary files differindex 2d4a164a7..2d4a164a7 100644 --- a/java/res/drawable/ic_dialog_alert_large.png +++ b/java/res/drawable-mdpi/ic_dialog_alert_large.png diff --git a/java/res/drawable/ic_dialog_voice_input.png b/java/res/drawable-mdpi/ic_dialog_voice_input.png Binary files differindex d28914132..d28914132 100644 --- a/java/res/drawable/ic_dialog_voice_input.png +++ b/java/res/drawable-mdpi/ic_dialog_voice_input.png diff --git a/java/res/drawable/ic_dialog_wave_0_0.png b/java/res/drawable-mdpi/ic_dialog_wave_0_0.png Binary files differindex 9c3c28f37..9c3c28f37 100644 --- a/java/res/drawable/ic_dialog_wave_0_0.png +++ b/java/res/drawable-mdpi/ic_dialog_wave_0_0.png diff --git a/java/res/drawable/ic_dialog_wave_1_3.png b/java/res/drawable-mdpi/ic_dialog_wave_1_3.png Binary files differindex d33bd0d9b..d33bd0d9b 100644 --- a/java/res/drawable/ic_dialog_wave_1_3.png +++ b/java/res/drawable-mdpi/ic_dialog_wave_1_3.png diff --git a/java/res/drawable/ic_dialog_wave_2_3.png b/java/res/drawable-mdpi/ic_dialog_wave_2_3.png Binary files differindex 5094a6e6c..5094a6e6c 100644 --- a/java/res/drawable/ic_dialog_wave_2_3.png +++ b/java/res/drawable-mdpi/ic_dialog_wave_2_3.png diff --git a/java/res/drawable/ic_dialog_wave_3_3.png b/java/res/drawable-mdpi/ic_dialog_wave_3_3.png Binary files differindex 69917564d..69917564d 100644 --- a/java/res/drawable/ic_dialog_wave_3_3.png +++ b/java/res/drawable-mdpi/ic_dialog_wave_3_3.png diff --git a/java/res/drawable/ic_dialog_wave_4_3.png b/java/res/drawable-mdpi/ic_dialog_wave_4_3.png Binary files differindex af5a84c31..af5a84c31 100644 --- a/java/res/drawable/ic_dialog_wave_4_3.png +++ b/java/res/drawable-mdpi/ic_dialog_wave_4_3.png diff --git a/java/res/drawable-mdpi/keyboard_key_feedback_background.9.png b/java/res/drawable-mdpi/keyboard_key_feedback_background.9.png Binary files differnew file mode 100644 index 000000000..2a80f096d --- /dev/null +++ b/java/res/drawable-mdpi/keyboard_key_feedback_background.9.png 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 Binary files differnew file mode 100755 index 000000000..29aa285bd --- /dev/null +++ b/java/res/drawable-mdpi/keyboard_key_feedback_more_background.9.png diff --git a/java/res/drawable-mdpi/keyboard_popup_panel_background.9.png b/java/res/drawable-mdpi/keyboard_popup_panel_background.9.png Binary files differnew file mode 100644 index 000000000..36d75df6f --- /dev/null +++ b/java/res/drawable-mdpi/keyboard_popup_panel_background.9.png 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 Binary files differnew file mode 100644 index 000000000..4ba2a4908 --- /dev/null +++ b/java/res/drawable-mdpi/keyboard_popup_panel_trans_background.9.png diff --git a/java/res/drawable/mic_slash.png b/java/res/drawable-mdpi/mic_slash.png Binary files differindex 0b0fb5803..0b0fb5803 100644 --- a/java/res/drawable/mic_slash.png +++ b/java/res/drawable-mdpi/mic_slash.png diff --git a/java/res/drawable/ok_cancel.png b/java/res/drawable-mdpi/ok_cancel.png Binary files differindex 0601d3231..0601d3231 100644 --- a/java/res/drawable/ok_cancel.png +++ b/java/res/drawable-mdpi/ok_cancel.png diff --git a/java/res/drawable/speak_now_level0.png b/java/res/drawable-mdpi/speak_now_level0.png Binary files differindex abc845466..abc845466 100644 --- a/java/res/drawable/speak_now_level0.png +++ b/java/res/drawable-mdpi/speak_now_level0.png diff --git a/java/res/drawable/speak_now_level1.png b/java/res/drawable-mdpi/speak_now_level1.png Binary files differindex 67cb235bf..67cb235bf 100644 --- a/java/res/drawable/speak_now_level1.png +++ b/java/res/drawable-mdpi/speak_now_level1.png diff --git a/java/res/drawable/speak_now_level2.png b/java/res/drawable-mdpi/speak_now_level2.png Binary files differindex 1e07f26c6..1e07f26c6 100644 --- a/java/res/drawable/speak_now_level2.png +++ b/java/res/drawable-mdpi/speak_now_level2.png diff --git a/java/res/drawable/speak_now_level3.png b/java/res/drawable-mdpi/speak_now_level3.png Binary files differindex 31991daee..31991daee 100644 --- a/java/res/drawable/speak_now_level3.png +++ b/java/res/drawable-mdpi/speak_now_level3.png diff --git a/java/res/drawable/speak_now_level4.png b/java/res/drawable-mdpi/speak_now_level4.png Binary files differindex 7363ca892..7363ca892 100644 --- a/java/res/drawable/speak_now_level4.png +++ b/java/res/drawable-mdpi/speak_now_level4.png diff --git a/java/res/drawable/speak_now_level5.png b/java/res/drawable-mdpi/speak_now_level5.png Binary files differindex 9034908f4..9034908f4 100644 --- a/java/res/drawable/speak_now_level5.png +++ b/java/res/drawable-mdpi/speak_now_level5.png diff --git a/java/res/drawable/speak_now_level6.png b/java/res/drawable-mdpi/speak_now_level6.png Binary files differindex 3eaa9bdad..3eaa9bdad 100644 --- a/java/res/drawable/speak_now_level6.png +++ b/java/res/drawable-mdpi/speak_now_level6.png diff --git a/java/res/drawable-mdpi/sym_bkeyboard_123_mic.png b/java/res/drawable-mdpi/sym_bkeyboard_123_mic.png Binary files differnew file mode 100644 index 000000000..0749b5fc6 --- /dev/null +++ b/java/res/drawable-mdpi/sym_bkeyboard_123_mic.png diff --git a/java/res/drawable-mdpi/sym_bkeyboard_delete.png b/java/res/drawable-mdpi/sym_bkeyboard_delete.png Binary files differnew file mode 100644 index 000000000..1a5ff439e --- /dev/null +++ b/java/res/drawable-mdpi/sym_bkeyboard_delete.png diff --git a/java/res/drawable-mdpi/sym_bkeyboard_done.png b/java/res/drawable-mdpi/sym_bkeyboard_done.png Binary files differnew file mode 100644 index 000000000..05ce7c643 --- /dev/null +++ b/java/res/drawable-mdpi/sym_bkeyboard_done.png diff --git a/java/res/drawable-mdpi/sym_bkeyboard_globe.png b/java/res/drawable-mdpi/sym_bkeyboard_globe.png Binary files differnew file mode 100644 index 000000000..c6595cf62 --- /dev/null +++ b/java/res/drawable-mdpi/sym_bkeyboard_globe.png diff --git a/java/res/drawable-mdpi/sym_bkeyboard_mic.png b/java/res/drawable-mdpi/sym_bkeyboard_mic.png Binary files differnew file mode 100644 index 000000000..a6cb1cc01 --- /dev/null +++ b/java/res/drawable-mdpi/sym_bkeyboard_mic.png diff --git a/java/res/drawable-mdpi/sym_bkeyboard_num0.png b/java/res/drawable-mdpi/sym_bkeyboard_num0.png Binary files differnew file mode 100644 index 000000000..7188f9ca5 --- /dev/null +++ b/java/res/drawable-mdpi/sym_bkeyboard_num0.png diff --git a/java/res/drawable-mdpi/sym_bkeyboard_num1.png b/java/res/drawable-mdpi/sym_bkeyboard_num1.png Binary files differnew file mode 100644 index 000000000..2a31bd458 --- /dev/null +++ b/java/res/drawable-mdpi/sym_bkeyboard_num1.png diff --git a/java/res/drawable-mdpi/sym_bkeyboard_num2.png b/java/res/drawable-mdpi/sym_bkeyboard_num2.png Binary files differnew file mode 100644 index 000000000..c1e9cc9b1 --- /dev/null +++ b/java/res/drawable-mdpi/sym_bkeyboard_num2.png diff --git a/java/res/drawable-mdpi/sym_bkeyboard_num3.png b/java/res/drawable-mdpi/sym_bkeyboard_num3.png Binary files differnew file mode 100644 index 000000000..e9987668c --- /dev/null +++ b/java/res/drawable-mdpi/sym_bkeyboard_num3.png diff --git a/java/res/drawable-mdpi/sym_bkeyboard_num4.png b/java/res/drawable-mdpi/sym_bkeyboard_num4.png Binary files differnew file mode 100644 index 000000000..7f0f3cccc --- /dev/null +++ b/java/res/drawable-mdpi/sym_bkeyboard_num4.png diff --git a/java/res/drawable-mdpi/sym_bkeyboard_num5.png b/java/res/drawable-mdpi/sym_bkeyboard_num5.png Binary files differnew file mode 100644 index 000000000..5f748b416 --- /dev/null +++ b/java/res/drawable-mdpi/sym_bkeyboard_num5.png diff --git a/java/res/drawable-mdpi/sym_bkeyboard_num6.png b/java/res/drawable-mdpi/sym_bkeyboard_num6.png Binary files differnew file mode 100644 index 000000000..78aae74a0 --- /dev/null +++ b/java/res/drawable-mdpi/sym_bkeyboard_num6.png diff --git a/java/res/drawable-mdpi/sym_bkeyboard_num7.png b/java/res/drawable-mdpi/sym_bkeyboard_num7.png Binary files differnew file mode 100644 index 000000000..5bb874c47 --- /dev/null +++ b/java/res/drawable-mdpi/sym_bkeyboard_num7.png diff --git a/java/res/drawable-mdpi/sym_bkeyboard_num8.png b/java/res/drawable-mdpi/sym_bkeyboard_num8.png Binary files differnew file mode 100644 index 000000000..6b58fdc8a --- /dev/null +++ b/java/res/drawable-mdpi/sym_bkeyboard_num8.png diff --git a/java/res/drawable-mdpi/sym_bkeyboard_num9.png b/java/res/drawable-mdpi/sym_bkeyboard_num9.png Binary files differnew file mode 100644 index 000000000..f348c92af --- /dev/null +++ b/java/res/drawable-mdpi/sym_bkeyboard_num9.png diff --git a/java/res/drawable-mdpi/sym_bkeyboard_numalt.png b/java/res/drawable-mdpi/sym_bkeyboard_numalt.png Binary files differnew file mode 100644 index 000000000..4fa410b62 --- /dev/null +++ b/java/res/drawable-mdpi/sym_bkeyboard_numalt.png diff --git a/java/res/drawable-mdpi/sym_bkeyboard_numpound.png b/java/res/drawable-mdpi/sym_bkeyboard_numpound.png Binary files differnew file mode 100644 index 000000000..9126eed0d --- /dev/null +++ b/java/res/drawable-mdpi/sym_bkeyboard_numpound.png diff --git a/java/res/drawable-mdpi/sym_bkeyboard_numstar.png b/java/res/drawable-mdpi/sym_bkeyboard_numstar.png Binary files differnew file mode 100644 index 000000000..9b9f1b986 --- /dev/null +++ b/java/res/drawable-mdpi/sym_bkeyboard_numstar.png diff --git a/java/res/drawable-mdpi/sym_bkeyboard_return.png b/java/res/drawable-mdpi/sym_bkeyboard_return.png Binary files differnew file mode 100644 index 000000000..e76225d0f --- /dev/null +++ b/java/res/drawable-mdpi/sym_bkeyboard_return.png diff --git a/java/res/drawable-mdpi/sym_bkeyboard_search.png b/java/res/drawable-mdpi/sym_bkeyboard_search.png Binary files differnew file mode 100644 index 000000000..1f180155d --- /dev/null +++ b/java/res/drawable-mdpi/sym_bkeyboard_search.png diff --git a/java/res/drawable-mdpi/sym_bkeyboard_shift.png b/java/res/drawable-mdpi/sym_bkeyboard_shift.png Binary files differnew file mode 100644 index 000000000..c981188dd --- /dev/null +++ b/java/res/drawable-mdpi/sym_bkeyboard_shift.png diff --git a/java/res/drawable-mdpi/sym_bkeyboard_shift_locked.png b/java/res/drawable-mdpi/sym_bkeyboard_shift_locked.png Binary files differnew file mode 100644 index 000000000..b8cebd060 --- /dev/null +++ b/java/res/drawable-mdpi/sym_bkeyboard_shift_locked.png diff --git a/java/res/drawable-mdpi/sym_bkeyboard_space.png b/java/res/drawable-mdpi/sym_bkeyboard_space.png Binary files differnew file mode 100644 index 000000000..4da7ee86e --- /dev/null +++ b/java/res/drawable-mdpi/sym_bkeyboard_space.png diff --git a/java/res/drawable-mdpi/sym_bkeyboard_tab.png b/java/res/drawable-mdpi/sym_bkeyboard_tab.png Binary files differnew file mode 100644 index 000000000..2cb991cbf --- /dev/null +++ b/java/res/drawable-mdpi/sym_bkeyboard_tab.png diff --git a/java/res/drawable-mdpi/sym_bkeyboard_tabprev.png b/java/res/drawable-mdpi/sym_bkeyboard_tabprev.png Binary files differnew file mode 100644 index 000000000..5298291d5 --- /dev/null +++ b/java/res/drawable-mdpi/sym_bkeyboard_tabprev.png diff --git a/java/res/drawable/voice_ime_background.9.png b/java/res/drawable-mdpi/voice_ime_background.9.png Binary files differindex 67802492a..67802492a 100644 --- a/java/res/drawable/voice_ime_background.9.png +++ b/java/res/drawable-mdpi/voice_ime_background.9.png diff --git a/java/res/drawable/voice_swipe_hint.png b/java/res/drawable-mdpi/voice_swipe_hint.png Binary files differindex bb8873251..bb8873251 100644 --- a/java/res/drawable/voice_swipe_hint.png +++ b/java/res/drawable-mdpi/voice_swipe_hint.png diff --git a/java/res/drawable/working.png b/java/res/drawable-mdpi/working.png Binary files differindex 6246a6d1c..6246a6d1c 100644 --- a/java/res/drawable/working.png +++ b/java/res/drawable-mdpi/working.png 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()[]*&@{}/<>_+=|\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="&"/> + <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="[{<" + /> + <Key android:codes="41" android:keyLabel=")" android:keyEdgeFlags="right" + android:popupKeyboard="@xml/kbd_popup_template" + android:popupCharacters="]}>" + /> + </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=""" + 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="<" + android:popupKeyboard="@xml/kbd_popup_template" + android:popupCharacters="≤«‹" + /> + <Key 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: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 {} |