Don't let the native code target be included twice when unbundling.

Move java code to a different directory so that the unbundled
version doesn't try to compile the native code again.

Change-Id: I05cf9e643824ddc448821f69805ccb0240c5b986
diff --git a/java/Android.mk b/java/Android.mk
new file mode 100755
index 0000000..4bb8986
--- /dev/null
+++ b/java/Android.mk
@@ -0,0 +1,22 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := LatinIME
+
+LOCAL_CERTIFICATE := shared
+
+LOCAL_JNI_SHARED_LIBRARIES := libjni_latinime
+
+LOCAL_STATIC_JAVA_LIBRARIES := android-common
+
+#LOCAL_AAPT_FLAGS := -0 .dict
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_PROGUARD_FLAGS := -include $(LOCAL_PATH)/proguard.flags
+
+include $(BUILD_PACKAGE)
diff --git a/java/AndroidManifest.xml b/java/AndroidManifest.xml
new file mode 100755
index 0000000..23dbf75
--- /dev/null
+++ b/java/AndroidManifest.xml
@@ -0,0 +1,40 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.inputmethod.latin"
+        android:sharedUserId="android.uid.shared">
+
+    <original-package android:name="com.android.inputmethod.latin" />
+
+    <uses-permission android:name="android.permission.VIBRATE"/>
+    <uses-permission android:name="android.permission.READ_USER_DICTIONARY" />
+    <uses-permission android:name="android.permission.WRITE_USER_DICTIONARY" />
+    <uses-permission android:name="android.permission.RECORD_AUDIO" />
+
+    <application android:label="@string/english_ime_name"
+            android:backupAgent="LatinIMEBackupAgent"
+            android:killAfterRestore="false">
+
+        <service android:name="LatinIME"
+                android:label="@string/english_ime_name"
+                android:permission="android.permission.BIND_INPUT_METHOD">
+            <intent-filter>
+                <action android:name="android.view.InputMethod" />
+            </intent-filter>
+            <meta-data android:name="android.view.im" android:resource="@xml/method" />
+        </service>
+
+        <activity android:name="LatinIMESettings" android:label="@string/english_ime_settings">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+            </intent-filter>
+        </activity>
+
+        <activity android:name="InputLanguageSelection"
+                android:label="@string/language_selection_title">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <action android:name="com.android.inputmethod.latin.INPUT_LANGUAGE_SELECTION"/>
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/java/proguard.flags b/java/proguard.flags
new file mode 100644
index 0000000..0a5d2dd
--- /dev/null
+++ b/java/proguard.flags
@@ -0,0 +1,3 @@
+-keep class com.android.inputmethod.latin.BinaryDictionary {
+  int mDictLength;
+}
diff --git a/java/res/drawable-en-hdpi/sym_keyboard_delete.png b/java/res/drawable-en-hdpi/sym_keyboard_delete.png
new file mode 100755
index 0000000..569369e
--- /dev/null
+++ b/java/res/drawable-en-hdpi/sym_keyboard_delete.png
Binary files differ
diff --git a/java/res/drawable-en-hdpi/sym_keyboard_feedback_delete.png b/java/res/drawable-en-hdpi/sym_keyboard_feedback_delete.png
new file mode 100755
index 0000000..ca76375
--- /dev/null
+++ b/java/res/drawable-en-hdpi/sym_keyboard_feedback_delete.png
Binary files differ
diff --git a/java/res/drawable-en-mdpi/sym_keyboard_delete.png b/java/res/drawable-en-mdpi/sym_keyboard_delete.png
new file mode 100644
index 0000000..f1f7c58
--- /dev/null
+++ b/java/res/drawable-en-mdpi/sym_keyboard_delete.png
Binary files differ
diff --git a/java/res/drawable-en-mdpi/sym_keyboard_feedback_delete.png b/java/res/drawable-en-mdpi/sym_keyboard_feedback_delete.png
new file mode 100644
index 0000000..3c90839
--- /dev/null
+++ b/java/res/drawable-en-mdpi/sym_keyboard_feedback_delete.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_fulltrans_normal.9.png b/java/res/drawable-hdpi/btn_keyboard_key_fulltrans_normal.9.png
new file mode 100644
index 0000000..b6c234c
--- /dev/null
+++ b/java/res/drawable-hdpi/btn_keyboard_key_fulltrans_normal.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_fulltrans_pressed.9.png b/java/res/drawable-hdpi/btn_keyboard_key_fulltrans_pressed.9.png
new file mode 100644
index 0000000..73a8cd1
--- /dev/null
+++ b/java/res/drawable-hdpi/btn_keyboard_key_fulltrans_pressed.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_normal.9.png b/java/res/drawable-hdpi/btn_keyboard_key_normal.9.png
new file mode 100644
index 0000000..42c7c14
--- /dev/null
+++ b/java/res/drawable-hdpi/btn_keyboard_key_normal.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_normal_off.9.png b/java/res/drawable-hdpi/btn_keyboard_key_normal_off.9.png
new file mode 100644
index 0000000..01e2506
--- /dev/null
+++ b/java/res/drawable-hdpi/btn_keyboard_key_normal_off.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_normal_on.9.png b/java/res/drawable-hdpi/btn_keyboard_key_normal_on.9.png
new file mode 100644
index 0000000..83c6eb3
--- /dev/null
+++ b/java/res/drawable-hdpi/btn_keyboard_key_normal_on.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_pressed.9.png b/java/res/drawable-hdpi/btn_keyboard_key_pressed.9.png
new file mode 100644
index 0000000..e047eaf
--- /dev/null
+++ b/java/res/drawable-hdpi/btn_keyboard_key_pressed.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_pressed_off.9.png b/java/res/drawable-hdpi/btn_keyboard_key_pressed_off.9.png
new file mode 100644
index 0000000..218a2d2
--- /dev/null
+++ b/java/res/drawable-hdpi/btn_keyboard_key_pressed_off.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_keyboard_key_pressed_on.9.png b/java/res/drawable-hdpi/btn_keyboard_key_pressed_on.9.png
new file mode 100644
index 0000000..afe4951
--- /dev/null
+++ b/java/res/drawable-hdpi/btn_keyboard_key_pressed_on.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/cancel.png b/java/res/drawable-hdpi/cancel.png
new file mode 100755
index 0000000..6585bde
--- /dev/null
+++ b/java/res/drawable-hdpi/cancel.png
Binary files differ
diff --git a/java/res/drawable-hdpi/candidate_feedback_background.9.png b/java/res/drawable-hdpi/candidate_feedback_background.9.png
new file mode 100755
index 0000000..203c4e6
--- /dev/null
+++ b/java/res/drawable-hdpi/candidate_feedback_background.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/caution.png b/java/res/drawable-hdpi/caution.png
new file mode 100755
index 0000000..5cb6c54
--- /dev/null
+++ b/java/res/drawable-hdpi/caution.png
Binary files differ
diff --git a/java/res/drawable-hdpi/dialog_bubble_step02.9.png b/java/res/drawable-hdpi/dialog_bubble_step02.9.png
new file mode 100755
index 0000000..b338364
--- /dev/null
+++ b/java/res/drawable-hdpi/dialog_bubble_step02.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/dialog_bubble_step07.9.png b/java/res/drawable-hdpi/dialog_bubble_step07.9.png
new file mode 100755
index 0000000..94b9154
--- /dev/null
+++ b/java/res/drawable-hdpi/dialog_bubble_step07.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/dialog_top_dark_bottom_medium.png b/java/res/drawable-hdpi/dialog_top_dark_bottom_medium.png
new file mode 100755
index 0000000..7c79a4f
--- /dev/null
+++ b/java/res/drawable-hdpi/dialog_top_dark_bottom_medium.png
Binary files differ
diff --git a/java/res/drawable-hdpi/highlight_pressed.png b/java/res/drawable-hdpi/highlight_pressed.png
new file mode 100644
index 0000000..ae04901
--- /dev/null
+++ b/java/res/drawable-hdpi/highlight_pressed.png
Binary files differ
diff --git a/java/res/drawable-hdpi/ic_dialog_alert_large.png b/java/res/drawable-hdpi/ic_dialog_alert_large.png
new file mode 100755
index 0000000..7e2646d
--- /dev/null
+++ b/java/res/drawable-hdpi/ic_dialog_alert_large.png
Binary files differ
diff --git a/java/res/drawable-hdpi/ic_dialog_keyboard.png b/java/res/drawable-hdpi/ic_dialog_keyboard.png
new file mode 100755
index 0000000..c772956
--- /dev/null
+++ b/java/res/drawable-hdpi/ic_dialog_keyboard.png
Binary files differ
diff --git a/java/res/drawable-hdpi/ic_dialog_voice_input.png b/java/res/drawable-hdpi/ic_dialog_voice_input.png
new file mode 100755
index 0000000..11a3cfe
--- /dev/null
+++ b/java/res/drawable-hdpi/ic_dialog_voice_input.png
Binary files differ
diff --git a/java/res/drawable-hdpi/ic_dialog_wave_0_0.png b/java/res/drawable-hdpi/ic_dialog_wave_0_0.png
new file mode 100755
index 0000000..33023c3
--- /dev/null
+++ b/java/res/drawable-hdpi/ic_dialog_wave_0_0.png
Binary files differ
diff --git a/java/res/drawable-hdpi/ic_dialog_wave_1_3.png b/java/res/drawable-hdpi/ic_dialog_wave_1_3.png
new file mode 100755
index 0000000..662686c
--- /dev/null
+++ b/java/res/drawable-hdpi/ic_dialog_wave_1_3.png
Binary files differ
diff --git a/java/res/drawable-hdpi/ic_dialog_wave_2_3.png b/java/res/drawable-hdpi/ic_dialog_wave_2_3.png
new file mode 100755
index 0000000..e23ada5
--- /dev/null
+++ b/java/res/drawable-hdpi/ic_dialog_wave_2_3.png
Binary files differ
diff --git a/java/res/drawable-hdpi/ic_dialog_wave_3_3.png b/java/res/drawable-hdpi/ic_dialog_wave_3_3.png
new file mode 100755
index 0000000..5fe5492
--- /dev/null
+++ b/java/res/drawable-hdpi/ic_dialog_wave_3_3.png
Binary files differ
diff --git a/java/res/drawable-hdpi/ic_dialog_wave_4_3.png b/java/res/drawable-hdpi/ic_dialog_wave_4_3.png
new file mode 100755
index 0000000..81b803f
--- /dev/null
+++ b/java/res/drawable-hdpi/ic_dialog_wave_4_3.png
Binary files differ
diff --git a/java/res/drawable-hdpi/ic_mic_dialog.png b/java/res/drawable-hdpi/ic_mic_dialog.png
new file mode 100644
index 0000000..349dc4b
--- /dev/null
+++ b/java/res/drawable-hdpi/ic_mic_dialog.png
Binary files differ
diff --git a/java/res/drawable-hdpi/ic_suggest_strip_microphone.png b/java/res/drawable-hdpi/ic_suggest_strip_microphone.png
new file mode 100644
index 0000000..c00b4aa
--- /dev/null
+++ b/java/res/drawable-hdpi/ic_suggest_strip_microphone.png
Binary files differ
diff --git a/java/res/drawable-hdpi/ic_suggest_strip_microphone_swipe.png b/java/res/drawable-hdpi/ic_suggest_strip_microphone_swipe.png
new file mode 100644
index 0000000..256dc3d
--- /dev/null
+++ b/java/res/drawable-hdpi/ic_suggest_strip_microphone_swipe.png
Binary files differ
diff --git a/java/res/drawable-hdpi/ic_suggest_strip_scroll_left_arrow.png b/java/res/drawable-hdpi/ic_suggest_strip_scroll_left_arrow.png
new file mode 100755
index 0000000..e375f26
--- /dev/null
+++ b/java/res/drawable-hdpi/ic_suggest_strip_scroll_left_arrow.png
Binary files differ
diff --git a/java/res/drawable-hdpi/ic_suggest_strip_scroll_right_arrow.png b/java/res/drawable-hdpi/ic_suggest_strip_scroll_right_arrow.png
new file mode 100755
index 0000000..d38ae75
--- /dev/null
+++ b/java/res/drawable-hdpi/ic_suggest_strip_scroll_right_arrow.png
Binary files differ
diff --git a/java/res/drawable-hdpi/keyboard_background.9.png b/java/res/drawable-hdpi/keyboard_background.9.png
new file mode 100755
index 0000000..edffac5
--- /dev/null
+++ b/java/res/drawable-hdpi/keyboard_background.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/keyboard_suggest_strip.9.png b/java/res/drawable-hdpi/keyboard_suggest_strip.9.png
new file mode 100755
index 0000000..0cbb3ed
--- /dev/null
+++ b/java/res/drawable-hdpi/keyboard_suggest_strip.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/keyboard_suggest_strip_divider.png b/java/res/drawable-hdpi/keyboard_suggest_strip_divider.png
new file mode 100644
index 0000000..a598038
--- /dev/null
+++ b/java/res/drawable-hdpi/keyboard_suggest_strip_divider.png
Binary files differ
diff --git a/java/res/drawable-hdpi/list_selector_background_pressed.9.png b/java/res/drawable-hdpi/list_selector_background_pressed.9.png
new file mode 100644
index 0000000..ba79cf7
--- /dev/null
+++ b/java/res/drawable-hdpi/list_selector_background_pressed.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/mic_slash.png b/java/res/drawable-hdpi/mic_slash.png
new file mode 100644
index 0000000..a7b734c
--- /dev/null
+++ b/java/res/drawable-hdpi/mic_slash.png
Binary files differ
diff --git a/java/res/drawable-hdpi/ok_cancel.png b/java/res/drawable-hdpi/ok_cancel.png
new file mode 100755
index 0000000..6a99528
--- /dev/null
+++ b/java/res/drawable-hdpi/ok_cancel.png
Binary files differ
diff --git a/java/res/drawable-hdpi/speak_now_level0.png b/java/res/drawable-hdpi/speak_now_level0.png
new file mode 100755
index 0000000..a681da6
--- /dev/null
+++ b/java/res/drawable-hdpi/speak_now_level0.png
Binary files differ
diff --git a/java/res/drawable-hdpi/speak_now_level1.png b/java/res/drawable-hdpi/speak_now_level1.png
new file mode 100755
index 0000000..0dbec69
--- /dev/null
+++ b/java/res/drawable-hdpi/speak_now_level1.png
Binary files differ
diff --git a/java/res/drawable-hdpi/speak_now_level2.png b/java/res/drawable-hdpi/speak_now_level2.png
new file mode 100755
index 0000000..45cbff2
--- /dev/null
+++ b/java/res/drawable-hdpi/speak_now_level2.png
Binary files differ
diff --git a/java/res/drawable-hdpi/speak_now_level3.png b/java/res/drawable-hdpi/speak_now_level3.png
new file mode 100755
index 0000000..abda8f6
--- /dev/null
+++ b/java/res/drawable-hdpi/speak_now_level3.png
Binary files differ
diff --git a/java/res/drawable-hdpi/speak_now_level4.png b/java/res/drawable-hdpi/speak_now_level4.png
new file mode 100755
index 0000000..1835635
--- /dev/null
+++ b/java/res/drawable-hdpi/speak_now_level4.png
Binary files differ
diff --git a/java/res/drawable-hdpi/speak_now_level5.png b/java/res/drawable-hdpi/speak_now_level5.png
new file mode 100755
index 0000000..7d4fd5f
--- /dev/null
+++ b/java/res/drawable-hdpi/speak_now_level5.png
Binary files differ
diff --git a/java/res/drawable-hdpi/speak_now_level6.png b/java/res/drawable-hdpi/speak_now_level6.png
new file mode 100755
index 0000000..e06990f
--- /dev/null
+++ b/java/res/drawable-hdpi/speak_now_level6.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_123_mic.png b/java/res/drawable-hdpi/sym_keyboard_123_mic.png
new file mode 100644
index 0000000..6266980
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_123_mic.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_delete.png b/java/res/drawable-hdpi/sym_keyboard_delete.png
new file mode 100755
index 0000000..59d78be
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_delete.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_done.png b/java/res/drawable-hdpi/sym_keyboard_done.png
new file mode 100755
index 0000000..471c502
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_done.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_feedback_123_mic.png b/java/res/drawable-hdpi/sym_keyboard_feedback_123_mic.png
new file mode 100644
index 0000000..f57e581
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_feedback_123_mic.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_feedback_delete.png b/java/res/drawable-hdpi/sym_keyboard_feedback_delete.png
new file mode 100755
index 0000000..ca76375
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_feedback_delete.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_feedback_done.png b/java/res/drawable-hdpi/sym_keyboard_feedback_done.png
new file mode 100755
index 0000000..7015e26
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_feedback_done.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_feedback_language_arrows_left.png b/java/res/drawable-hdpi/sym_keyboard_feedback_language_arrows_left.png
new file mode 100644
index 0000000..c7638bf
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_feedback_language_arrows_left.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_feedback_language_arrows_right.png b/java/res/drawable-hdpi/sym_keyboard_feedback_language_arrows_right.png
new file mode 100644
index 0000000..7acb07c
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_feedback_language_arrows_right.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_feedback_mic.png b/java/res/drawable-hdpi/sym_keyboard_feedback_mic.png
new file mode 100644
index 0000000..cb86a55
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_feedback_mic.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_feedback_numalt.png b/java/res/drawable-hdpi/sym_keyboard_feedback_numalt.png
new file mode 100755
index 0000000..728c6f7
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_feedback_numalt.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_feedback_return.png b/java/res/drawable-hdpi/sym_keyboard_feedback_return.png
new file mode 100755
index 0000000..ae57299
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_feedback_return.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_feedback_search.png b/java/res/drawable-hdpi/sym_keyboard_feedback_search.png
new file mode 100755
index 0000000..d931b39
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_feedback_search.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_feedback_shift.png b/java/res/drawable-hdpi/sym_keyboard_feedback_shift.png
new file mode 100755
index 0000000..4db31c8
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_feedback_shift.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_feedback_shift_locked.png b/java/res/drawable-hdpi/sym_keyboard_feedback_shift_locked.png
new file mode 100755
index 0000000..3fd5659
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_feedback_shift_locked.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_feedback_space.png b/java/res/drawable-hdpi/sym_keyboard_feedback_space.png
new file mode 100755
index 0000000..98266ee
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_feedback_space.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_feedback_tab.png b/java/res/drawable-hdpi/sym_keyboard_feedback_tab.png
new file mode 100644
index 0000000..7754752
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_feedback_tab.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_feedback_tabprev.png b/java/res/drawable-hdpi/sym_keyboard_feedback_tabprev.png
new file mode 100644
index 0000000..36f6b53
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_feedback_tabprev.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_globe.png b/java/res/drawable-hdpi/sym_keyboard_globe.png
new file mode 100644
index 0000000..fa74764
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_globe.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_language_arrows_left.png b/java/res/drawable-hdpi/sym_keyboard_language_arrows_left.png
new file mode 100644
index 0000000..65ccfda
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_language_arrows_left.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_language_arrows_right.png b/java/res/drawable-hdpi/sym_keyboard_language_arrows_right.png
new file mode 100644
index 0000000..0d01bc2
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_language_arrows_right.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_mic.png b/java/res/drawable-hdpi/sym_keyboard_mic.png
new file mode 100644
index 0000000..0a0a68a
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_mic.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_num0.png b/java/res/drawable-hdpi/sym_keyboard_num0.png
new file mode 100755
index 0000000..10ac70b
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_num0.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_num1.png b/java/res/drawable-hdpi/sym_keyboard_num1.png
new file mode 100755
index 0000000..0fc03ef
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_num1.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_num2.png b/java/res/drawable-hdpi/sym_keyboard_num2.png
new file mode 100755
index 0000000..283560b
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_num2.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_num3.png b/java/res/drawable-hdpi/sym_keyboard_num3.png
new file mode 100755
index 0000000..9a3b329
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_num3.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_num4.png b/java/res/drawable-hdpi/sym_keyboard_num4.png
new file mode 100755
index 0000000..f13ff1a
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_num4.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_num5.png b/java/res/drawable-hdpi/sym_keyboard_num5.png
new file mode 100755
index 0000000..c251329
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_num5.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_num6.png b/java/res/drawable-hdpi/sym_keyboard_num6.png
new file mode 100755
index 0000000..4acba4c
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_num6.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_num7.png b/java/res/drawable-hdpi/sym_keyboard_num7.png
new file mode 100755
index 0000000..14931c1
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_num7.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_num8.png b/java/res/drawable-hdpi/sym_keyboard_num8.png
new file mode 100755
index 0000000..d4973fd
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_num8.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_num9.png b/java/res/drawable-hdpi/sym_keyboard_num9.png
new file mode 100755
index 0000000..49cec66
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_num9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_numalt.png b/java/res/drawable-hdpi/sym_keyboard_numalt.png
new file mode 100755
index 0000000..3cc5311
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_numalt.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_numpound.png b/java/res/drawable-hdpi/sym_keyboard_numpound.png
new file mode 100755
index 0000000..d091339
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_numpound.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_numstar.png b/java/res/drawable-hdpi/sym_keyboard_numstar.png
new file mode 100755
index 0000000..e838e16
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_numstar.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_return.png b/java/res/drawable-hdpi/sym_keyboard_return.png
new file mode 100755
index 0000000..58505c5
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_return.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_search.png b/java/res/drawable-hdpi/sym_keyboard_search.png
new file mode 100755
index 0000000..e72cde3
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_search.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_shift.png b/java/res/drawable-hdpi/sym_keyboard_shift.png
new file mode 100755
index 0000000..8149081
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_shift.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_shift_locked.png b/java/res/drawable-hdpi/sym_keyboard_shift_locked.png
new file mode 100755
index 0000000..31ca277
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_shift_locked.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_space.png b/java/res/drawable-hdpi/sym_keyboard_space.png
new file mode 100755
index 0000000..3e98b30
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_space.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_tab.png b/java/res/drawable-hdpi/sym_keyboard_tab.png
new file mode 100644
index 0000000..1d4d92b
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_tab.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_tabprev.png b/java/res/drawable-hdpi/sym_keyboard_tabprev.png
new file mode 100644
index 0000000..51bff1b
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_tabprev.png
Binary files differ
diff --git a/java/res/drawable-hdpi/voice_ime_background.9.png b/java/res/drawable-hdpi/voice_ime_background.9.png
new file mode 100755
index 0000000..73fb090
--- /dev/null
+++ b/java/res/drawable-hdpi/voice_ime_background.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/working.png b/java/res/drawable-hdpi/working.png
new file mode 100755
index 0000000..8b51ed1
--- /dev/null
+++ b/java/res/drawable-hdpi/working.png
Binary files differ
diff --git a/java/res/drawable-land-hdpi/btn_keyboard_key_normal.9.png b/java/res/drawable-land-hdpi/btn_keyboard_key_normal.9.png
new file mode 100755
index 0000000..603bf0e
--- /dev/null
+++ b/java/res/drawable-land-hdpi/btn_keyboard_key_normal.9.png
Binary files differ
diff --git a/java/res/drawable-land-hdpi/btn_keyboard_key_normal_off.9.png b/java/res/drawable-land-hdpi/btn_keyboard_key_normal_off.9.png
new file mode 100755
index 0000000..6ddd516
--- /dev/null
+++ b/java/res/drawable-land-hdpi/btn_keyboard_key_normal_off.9.png
Binary files differ
diff --git a/java/res/drawable-land-hdpi/btn_keyboard_key_normal_on.9.png b/java/res/drawable-land-hdpi/btn_keyboard_key_normal_on.9.png
new file mode 100755
index 0000000..65fdeb3
--- /dev/null
+++ b/java/res/drawable-land-hdpi/btn_keyboard_key_normal_on.9.png
Binary files differ
diff --git a/java/res/drawable-land-hdpi/btn_keyboard_key_pressed.9.png b/java/res/drawable-land-hdpi/btn_keyboard_key_pressed.9.png
new file mode 100755
index 0000000..7ec915f
--- /dev/null
+++ b/java/res/drawable-land-hdpi/btn_keyboard_key_pressed.9.png
Binary files differ
diff --git a/java/res/drawable-land-hdpi/btn_keyboard_key_pressed_off.9.png b/java/res/drawable-land-hdpi/btn_keyboard_key_pressed_off.9.png
new file mode 100755
index 0000000..4392717
--- /dev/null
+++ b/java/res/drawable-land-hdpi/btn_keyboard_key_pressed_off.9.png
Binary files differ
diff --git a/java/res/drawable-land-hdpi/btn_keyboard_key_pressed_on.9.png b/java/res/drawable-land-hdpi/btn_keyboard_key_pressed_on.9.png
new file mode 100755
index 0000000..c2cc320
--- /dev/null
+++ b/java/res/drawable-land-hdpi/btn_keyboard_key_pressed_on.9.png
Binary files differ
diff --git a/java/res/drawable-land-hdpi/keyboard_suggest_strip_divider.png b/java/res/drawable-land-hdpi/keyboard_suggest_strip_divider.png
new file mode 100755
index 0000000..1a03c52
--- /dev/null
+++ b/java/res/drawable-land-hdpi/keyboard_suggest_strip_divider.png
Binary files differ
diff --git a/java/res/drawable-land-mdpi/btn_keyboard_key_normal.9.png b/java/res/drawable-land-mdpi/btn_keyboard_key_normal.9.png
new file mode 100644
index 0000000..ea2506c
--- /dev/null
+++ b/java/res/drawable-land-mdpi/btn_keyboard_key_normal.9.png
Binary files differ
diff --git a/java/res/drawable-land-mdpi/btn_keyboard_key_normal_off.9.png b/java/res/drawable-land-mdpi/btn_keyboard_key_normal_off.9.png
new file mode 100644
index 0000000..bda9b83
--- /dev/null
+++ b/java/res/drawable-land-mdpi/btn_keyboard_key_normal_off.9.png
Binary files differ
diff --git a/java/res/drawable-land-mdpi/btn_keyboard_key_normal_on.9.png b/java/res/drawable-land-mdpi/btn_keyboard_key_normal_on.9.png
new file mode 100644
index 0000000..0c16ed5
--- /dev/null
+++ b/java/res/drawable-land-mdpi/btn_keyboard_key_normal_on.9.png
Binary files differ
diff --git a/java/res/drawable-land-mdpi/btn_keyboard_key_pressed.9.png b/java/res/drawable-land-mdpi/btn_keyboard_key_pressed.9.png
new file mode 100755
index 0000000..6b5c718
--- /dev/null
+++ b/java/res/drawable-land-mdpi/btn_keyboard_key_pressed.9.png
Binary files differ
diff --git a/java/res/drawable-land-mdpi/btn_keyboard_key_pressed_off.9.png b/java/res/drawable-land-mdpi/btn_keyboard_key_pressed_off.9.png
new file mode 100644
index 0000000..bdcf06e
--- /dev/null
+++ b/java/res/drawable-land-mdpi/btn_keyboard_key_pressed_off.9.png
Binary files differ
diff --git a/java/res/drawable-land-mdpi/btn_keyboard_key_pressed_on.9.png b/java/res/drawable-land-mdpi/btn_keyboard_key_pressed_on.9.png
new file mode 100644
index 0000000..79621a9
--- /dev/null
+++ b/java/res/drawable-land-mdpi/btn_keyboard_key_pressed_on.9.png
Binary files differ
diff --git a/java/res/drawable-land-mdpi/keyboard_suggest_strip_divider.png b/java/res/drawable-land-mdpi/keyboard_suggest_strip_divider.png
new file mode 100644
index 0000000..e54c5b0
--- /dev/null
+++ b/java/res/drawable-land-mdpi/keyboard_suggest_strip_divider.png
Binary files differ
diff --git a/java/res/drawable-land/btn_keyboard_key.xml b/java/res/drawable-land/btn_keyboard_key.xml
new file mode 100644
index 0000000..45578e5
--- /dev/null
+++ b/java/res/drawable-land/btn_keyboard_key.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<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_pressed_off" />
+    <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_normal_off" />
+
+    <!-- Normal keys -->
+
+    <item android:state_pressed="true"
+          android:drawable="@drawable/btn_keyboard_key_pressed" />
+    <item
+          android:drawable="@drawable/btn_keyboard_key_normal" />
+          
+</selector>
diff --git a/java/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal.9.png b/java/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal.9.png
new file mode 100644
index 0000000..20f3d50
--- /dev/null
+++ b/java/res/drawable-mdpi/btn_keyboard_key_fulltrans_normal.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed.9.png b/java/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed.9.png
new file mode 100644
index 0000000..1ed3065
--- /dev/null
+++ b/java/res/drawable-mdpi/btn_keyboard_key_fulltrans_pressed.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_keyboard_key_normal.9.png b/java/res/drawable-mdpi/btn_keyboard_key_normal.9.png
new file mode 100644
index 0000000..7ba18dd
--- /dev/null
+++ b/java/res/drawable-mdpi/btn_keyboard_key_normal.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_keyboard_key_normal_off.9.png b/java/res/drawable-mdpi/btn_keyboard_key_normal_off.9.png
new file mode 100644
index 0000000..bda9b83
--- /dev/null
+++ b/java/res/drawable-mdpi/btn_keyboard_key_normal_off.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_keyboard_key_normal_on.9.png b/java/res/drawable-mdpi/btn_keyboard_key_normal_on.9.png
new file mode 100644
index 0000000..0c16ed5
--- /dev/null
+++ b/java/res/drawable-mdpi/btn_keyboard_key_normal_on.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_keyboard_key_pressed.9.png b/java/res/drawable-mdpi/btn_keyboard_key_pressed.9.png
new file mode 100644
index 0000000..39b9314
--- /dev/null
+++ b/java/res/drawable-mdpi/btn_keyboard_key_pressed.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_keyboard_key_pressed_off.9.png b/java/res/drawable-mdpi/btn_keyboard_key_pressed_off.9.png
new file mode 100644
index 0000000..bdcf06e
--- /dev/null
+++ b/java/res/drawable-mdpi/btn_keyboard_key_pressed_off.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_keyboard_key_pressed_on.9.png b/java/res/drawable-mdpi/btn_keyboard_key_pressed_on.9.png
new file mode 100644
index 0000000..79621a9
--- /dev/null
+++ b/java/res/drawable-mdpi/btn_keyboard_key_pressed_on.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/candidate_feedback_background.9.png b/java/res/drawable-mdpi/candidate_feedback_background.9.png
new file mode 100644
index 0000000..2a80f09
--- /dev/null
+++ b/java/res/drawable-mdpi/candidate_feedback_background.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/dialog_bubble_step02.9.png b/java/res/drawable-mdpi/dialog_bubble_step02.9.png
new file mode 100755
index 0000000..d77f85f
--- /dev/null
+++ b/java/res/drawable-mdpi/dialog_bubble_step02.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/dialog_bubble_step07.9.png b/java/res/drawable-mdpi/dialog_bubble_step07.9.png
new file mode 100755
index 0000000..80f4a0e
--- /dev/null
+++ b/java/res/drawable-mdpi/dialog_bubble_step07.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/highlight_pressed.png b/java/res/drawable-mdpi/highlight_pressed.png
new file mode 100644
index 0000000..d27f106
--- /dev/null
+++ b/java/res/drawable-mdpi/highlight_pressed.png
Binary files differ
diff --git a/java/res/drawable-mdpi/ic_dialog_keyboard.png b/java/res/drawable-mdpi/ic_dialog_keyboard.png
new file mode 100644
index 0000000..9a5aada
--- /dev/null
+++ b/java/res/drawable-mdpi/ic_dialog_keyboard.png
Binary files differ
diff --git a/java/res/drawable-mdpi/ic_mic_dialog.png b/java/res/drawable-mdpi/ic_mic_dialog.png
new file mode 100644
index 0000000..77613ca
--- /dev/null
+++ b/java/res/drawable-mdpi/ic_mic_dialog.png
Binary files differ
diff --git a/java/res/drawable-mdpi/ic_suggest_strip_microphone.png b/java/res/drawable-mdpi/ic_suggest_strip_microphone.png
new file mode 100644
index 0000000..18f314a
--- /dev/null
+++ b/java/res/drawable-mdpi/ic_suggest_strip_microphone.png
Binary files differ
diff --git a/java/res/drawable-mdpi/ic_suggest_strip_microphone_swipe.png b/java/res/drawable-mdpi/ic_suggest_strip_microphone_swipe.png
new file mode 100644
index 0000000..ff629b6
--- /dev/null
+++ b/java/res/drawable-mdpi/ic_suggest_strip_microphone_swipe.png
Binary files differ
diff --git a/java/res/drawable-mdpi/ic_suggest_strip_scroll_left_arrow.png b/java/res/drawable-mdpi/ic_suggest_strip_scroll_left_arrow.png
new file mode 100644
index 0000000..a9adef2
--- /dev/null
+++ b/java/res/drawable-mdpi/ic_suggest_strip_scroll_left_arrow.png
Binary files differ
diff --git a/java/res/drawable-mdpi/ic_suggest_strip_scroll_right_arrow.png b/java/res/drawable-mdpi/ic_suggest_strip_scroll_right_arrow.png
new file mode 100644
index 0000000..639a287
--- /dev/null
+++ b/java/res/drawable-mdpi/ic_suggest_strip_scroll_right_arrow.png
Binary files differ
diff --git a/java/res/drawable-mdpi/keyboard_background.9.png b/java/res/drawable-mdpi/keyboard_background.9.png
new file mode 100644
index 0000000..2bd4b62
--- /dev/null
+++ b/java/res/drawable-mdpi/keyboard_background.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/keyboard_suggest_strip.9.png b/java/res/drawable-mdpi/keyboard_suggest_strip.9.png
new file mode 100644
index 0000000..71bf5e8
--- /dev/null
+++ b/java/res/drawable-mdpi/keyboard_suggest_strip.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/keyboard_suggest_strip_divider.png b/java/res/drawable-mdpi/keyboard_suggest_strip_divider.png
new file mode 100644
index 0000000..c9413d7
--- /dev/null
+++ b/java/res/drawable-mdpi/keyboard_suggest_strip_divider.png
Binary files differ
diff --git a/java/res/drawable-mdpi/list_selector_background_pressed.9.png b/java/res/drawable-mdpi/list_selector_background_pressed.9.png
new file mode 100644
index 0000000..02b4e9a
--- /dev/null
+++ b/java/res/drawable-mdpi/list_selector_background_pressed.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_123_mic.png b/java/res/drawable-mdpi/sym_keyboard_123_mic.png
new file mode 100644
index 0000000..35afe08
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_123_mic.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_delete.png b/java/res/drawable-mdpi/sym_keyboard_delete.png
new file mode 100644
index 0000000..43a033e
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_delete.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_done.png b/java/res/drawable-mdpi/sym_keyboard_done.png
new file mode 100755
index 0000000..c0d6d13
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_done.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_feedback_123_mic.png b/java/res/drawable-mdpi/sym_keyboard_feedback_123_mic.png
new file mode 100644
index 0000000..bc11cf3
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_feedback_123_mic.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_feedback_delete.png b/java/res/drawable-mdpi/sym_keyboard_feedback_delete.png
new file mode 100644
index 0000000..1edb10b
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_feedback_delete.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_feedback_done.png b/java/res/drawable-mdpi/sym_keyboard_feedback_done.png
new file mode 100755
index 0000000..0d7ebd4
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_feedback_done.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_feedback_language_arrows_left.png b/java/res/drawable-mdpi/sym_keyboard_feedback_language_arrows_left.png
new file mode 100644
index 0000000..e3f80fa
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_feedback_language_arrows_left.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_feedback_language_arrows_right.png b/java/res/drawable-mdpi/sym_keyboard_feedback_language_arrows_right.png
new file mode 100644
index 0000000..3cb0d99
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_feedback_language_arrows_right.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_feedback_mic.png b/java/res/drawable-mdpi/sym_keyboard_feedback_mic.png
new file mode 100644
index 0000000..247d5b3
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_feedback_mic.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_feedback_numalt.png b/java/res/drawable-mdpi/sym_keyboard_feedback_numalt.png
new file mode 100644
index 0000000..aac7376
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_feedback_numalt.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_feedback_return.png b/java/res/drawable-mdpi/sym_keyboard_feedback_return.png
new file mode 100644
index 0000000..03d9c9b
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_feedback_return.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_feedback_search.png b/java/res/drawable-mdpi/sym_keyboard_feedback_search.png
new file mode 100755
index 0000000..f4af341
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_feedback_search.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_feedback_shift.png b/java/res/drawable-mdpi/sym_keyboard_feedback_shift.png
new file mode 100644
index 0000000..97f4661
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_feedback_shift.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_feedback_shift_locked.png b/java/res/drawable-mdpi/sym_keyboard_feedback_shift_locked.png
new file mode 100755
index 0000000..7194b30
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_feedback_shift_locked.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_feedback_space.png b/java/res/drawable-mdpi/sym_keyboard_feedback_space.png
new file mode 100644
index 0000000..739db68
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_feedback_space.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_feedback_tab.png b/java/res/drawable-mdpi/sym_keyboard_feedback_tab.png
new file mode 100644
index 0000000..593fa67
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_feedback_tab.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_feedback_tabprev.png b/java/res/drawable-mdpi/sym_keyboard_feedback_tabprev.png
new file mode 100644
index 0000000..d4b3e7d
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_feedback_tabprev.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_globe.png b/java/res/drawable-mdpi/sym_keyboard_globe.png
new file mode 100644
index 0000000..f30c1b6
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_globe.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_language_arrows_left.png b/java/res/drawable-mdpi/sym_keyboard_language_arrows_left.png
new file mode 100644
index 0000000..91eda5f
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_language_arrows_left.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_language_arrows_right.png b/java/res/drawable-mdpi/sym_keyboard_language_arrows_right.png
new file mode 100644
index 0000000..62a5beb
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_language_arrows_right.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_mic.png b/java/res/drawable-mdpi/sym_keyboard_mic.png
new file mode 100644
index 0000000..a758095
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_mic.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_num0.png b/java/res/drawable-mdpi/sym_keyboard_num0.png
new file mode 100644
index 0000000..e7007c8
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_num0.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_num1.png b/java/res/drawable-mdpi/sym_keyboard_num1.png
new file mode 100644
index 0000000..aaac11b
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_num1.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_num2.png b/java/res/drawable-mdpi/sym_keyboard_num2.png
new file mode 100644
index 0000000..4372eb8
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_num2.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_num3.png b/java/res/drawable-mdpi/sym_keyboard_num3.png
new file mode 100644
index 0000000..6f54c85
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_num3.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_num4.png b/java/res/drawable-mdpi/sym_keyboard_num4.png
new file mode 100644
index 0000000..3e50bb9
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_num4.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_num5.png b/java/res/drawable-mdpi/sym_keyboard_num5.png
new file mode 100644
index 0000000..c39ef44
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_num5.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_num6.png b/java/res/drawable-mdpi/sym_keyboard_num6.png
new file mode 100644
index 0000000..ea88ceb
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_num6.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_num7.png b/java/res/drawable-mdpi/sym_keyboard_num7.png
new file mode 100644
index 0000000..ce800ba
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_num7.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_num8.png b/java/res/drawable-mdpi/sym_keyboard_num8.png
new file mode 100644
index 0000000..1a8ff94
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_num8.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_num9.png b/java/res/drawable-mdpi/sym_keyboard_num9.png
new file mode 100644
index 0000000..8b344c0
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_num9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_numalt.png b/java/res/drawable-mdpi/sym_keyboard_numalt.png
new file mode 100644
index 0000000..32a2cf3
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_numalt.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_numpound.png b/java/res/drawable-mdpi/sym_keyboard_numpound.png
new file mode 100644
index 0000000..b2419d9
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_numpound.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_numstar.png b/java/res/drawable-mdpi/sym_keyboard_numstar.png
new file mode 100644
index 0000000..cb66f96
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_numstar.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_return.png b/java/res/drawable-mdpi/sym_keyboard_return.png
new file mode 100644
index 0000000..17f2574
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_return.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_search.png b/java/res/drawable-mdpi/sym_keyboard_search.png
new file mode 100755
index 0000000..127755d
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_search.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_shift.png b/java/res/drawable-mdpi/sym_keyboard_shift.png
new file mode 100644
index 0000000..0566e5a
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_shift.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_shift_locked.png b/java/res/drawable-mdpi/sym_keyboard_shift_locked.png
new file mode 100755
index 0000000..ccaf05d
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_shift_locked.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_space.png b/java/res/drawable-mdpi/sym_keyboard_space.png
new file mode 100644
index 0000000..4e6273b
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_space.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_tab.png b/java/res/drawable-mdpi/sym_keyboard_tab.png
new file mode 100644
index 0000000..cd9daff
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_tab.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_tabprev.png b/java/res/drawable-mdpi/sym_keyboard_tabprev.png
new file mode 100644
index 0000000..eb90872
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_tabprev.png
Binary files differ
diff --git a/java/res/drawable/btn_keyboard_key.xml b/java/res/drawable/btn_keyboard_key.xml
new file mode 100644
index 0000000..45578e5
--- /dev/null
+++ b/java/res/drawable/btn_keyboard_key.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<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_pressed_off" />
+    <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_normal_off" />
+
+    <!-- Normal keys -->
+
+    <item android:state_pressed="true"
+          android:drawable="@drawable/btn_keyboard_key_pressed" />
+    <item
+          android:drawable="@drawable/btn_keyboard_key_normal" />
+          
+</selector>
diff --git a/java/res/drawable/btn_keyboard_key_fulltrans.xml b/java/res/drawable/btn_keyboard_key_fulltrans.xml
new file mode 100644
index 0000000..bad2a93
--- /dev/null
+++ b/java/res/drawable/btn_keyboard_key_fulltrans.xml
@@ -0,0 +1,26 @@
+<?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">
+
+    <!-- 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/cancel.png b/java/res/drawable/cancel.png
new file mode 100644
index 0000000..081532b
--- /dev/null
+++ b/java/res/drawable/cancel.png
Binary files differ
diff --git a/java/res/drawable/caution.png b/java/res/drawable/caution.png
new file mode 100644
index 0000000..eaef534
--- /dev/null
+++ b/java/res/drawable/caution.png
Binary files differ
diff --git a/java/res/drawable/dialog_top_dark_bottom_medium.9.png b/java/res/drawable/dialog_top_dark_bottom_medium.9.png
new file mode 100644
index 0000000..cf7ecaf
--- /dev/null
+++ b/java/res/drawable/dialog_top_dark_bottom_medium.9.png
Binary files differ
diff --git a/java/res/drawable/ic_dialog_alert_large.png b/java/res/drawable/ic_dialog_alert_large.png
new file mode 100644
index 0000000..2d4a164
--- /dev/null
+++ b/java/res/drawable/ic_dialog_alert_large.png
Binary files differ
diff --git a/java/res/drawable/ic_dialog_voice_input.png b/java/res/drawable/ic_dialog_voice_input.png
new file mode 100644
index 0000000..d289141
--- /dev/null
+++ b/java/res/drawable/ic_dialog_voice_input.png
Binary files differ
diff --git a/java/res/drawable/ic_dialog_wave_0_0.png b/java/res/drawable/ic_dialog_wave_0_0.png
new file mode 100644
index 0000000..9c3c28f
--- /dev/null
+++ b/java/res/drawable/ic_dialog_wave_0_0.png
Binary files differ
diff --git a/java/res/drawable/ic_dialog_wave_1_3.png b/java/res/drawable/ic_dialog_wave_1_3.png
new file mode 100644
index 0000000..d33bd0d
--- /dev/null
+++ b/java/res/drawable/ic_dialog_wave_1_3.png
Binary files differ
diff --git a/java/res/drawable/ic_dialog_wave_2_3.png b/java/res/drawable/ic_dialog_wave_2_3.png
new file mode 100644
index 0000000..5094a6e
--- /dev/null
+++ b/java/res/drawable/ic_dialog_wave_2_3.png
Binary files differ
diff --git a/java/res/drawable/ic_dialog_wave_3_3.png b/java/res/drawable/ic_dialog_wave_3_3.png
new file mode 100644
index 0000000..6991756
--- /dev/null
+++ b/java/res/drawable/ic_dialog_wave_3_3.png
Binary files differ
diff --git a/java/res/drawable/ic_dialog_wave_4_3.png b/java/res/drawable/ic_dialog_wave_4_3.png
new file mode 100644
index 0000000..af5a84c
--- /dev/null
+++ b/java/res/drawable/ic_dialog_wave_4_3.png
Binary files differ
diff --git a/java/res/drawable/ic_suggest_scroll_background.xml b/java/res/drawable/ic_suggest_scroll_background.xml
new file mode 100644
index 0000000..9d246e4
--- /dev/null
+++ b/java/res/drawable/ic_suggest_scroll_background.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:state_pressed="false"
+        android:drawable="@android:color/transparent" />
+
+    <item android:state_pressed="true"
+        android:drawable="@drawable/highlight_pressed" />
+
+</selector>
\ No newline at end of file
diff --git a/java/res/drawable/mic_slash.png b/java/res/drawable/mic_slash.png
new file mode 100644
index 0000000..0b0fb58
--- /dev/null
+++ b/java/res/drawable/mic_slash.png
Binary files differ
diff --git a/java/res/drawable/ok_cancel.png b/java/res/drawable/ok_cancel.png
new file mode 100644
index 0000000..0601d32
--- /dev/null
+++ b/java/res/drawable/ok_cancel.png
Binary files differ
diff --git a/java/res/drawable/speak_now_level0.png b/java/res/drawable/speak_now_level0.png
new file mode 100644
index 0000000..abc8454
--- /dev/null
+++ b/java/res/drawable/speak_now_level0.png
Binary files differ
diff --git a/java/res/drawable/speak_now_level1.png b/java/res/drawable/speak_now_level1.png
new file mode 100644
index 0000000..67cb235
--- /dev/null
+++ b/java/res/drawable/speak_now_level1.png
Binary files differ
diff --git a/java/res/drawable/speak_now_level2.png b/java/res/drawable/speak_now_level2.png
new file mode 100644
index 0000000..1e07f26
--- /dev/null
+++ b/java/res/drawable/speak_now_level2.png
Binary files differ
diff --git a/java/res/drawable/speak_now_level3.png b/java/res/drawable/speak_now_level3.png
new file mode 100644
index 0000000..31991da
--- /dev/null
+++ b/java/res/drawable/speak_now_level3.png
Binary files differ
diff --git a/java/res/drawable/speak_now_level4.png b/java/res/drawable/speak_now_level4.png
new file mode 100644
index 0000000..7363ca8
--- /dev/null
+++ b/java/res/drawable/speak_now_level4.png
Binary files differ
diff --git a/java/res/drawable/speak_now_level5.png b/java/res/drawable/speak_now_level5.png
new file mode 100644
index 0000000..9034908
--- /dev/null
+++ b/java/res/drawable/speak_now_level5.png
Binary files differ
diff --git a/java/res/drawable/speak_now_level6.png b/java/res/drawable/speak_now_level6.png
new file mode 100644
index 0000000..3eaa9bd
--- /dev/null
+++ b/java/res/drawable/speak_now_level6.png
Binary files differ
diff --git a/java/res/drawable/voice_ime_background.9.png b/java/res/drawable/voice_ime_background.9.png
new file mode 100644
index 0000000..6780249
--- /dev/null
+++ b/java/res/drawable/voice_ime_background.9.png
Binary files differ
diff --git a/java/res/drawable/voice_swipe_hint.png b/java/res/drawable/voice_swipe_hint.png
new file mode 100644
index 0000000..bb88732
--- /dev/null
+++ b/java/res/drawable/voice_swipe_hint.png
Binary files differ
diff --git a/java/res/drawable/working.png b/java/res/drawable/working.png
new file mode 100644
index 0000000..6246a6d
--- /dev/null
+++ b/java/res/drawable/working.png
Binary files differ
diff --git a/java/res/layout/bubble_text.xml b/java/res/layout/bubble_text.xml
new file mode 100644
index 0000000..c3957b6
--- /dev/null
+++ b/java/res/layout/bubble_text.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* 
+**
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content" 
+    android:layout_height="wrap_content"
+    android:textSize="14sp"
+    android:textColor="?android:attr/textColorPrimary"
+    android:minWidth="32dip"
+    android:gravity="center"
+    android:shadowRadius="2.75"
+    android:shadowColor="#BB000000"
+    />
diff --git a/java/res/layout/candidate_preview.xml b/java/res/layout/candidate_preview.xml
new file mode 100755
index 0000000..fe2002d
--- /dev/null
+++ b/java/res/layout/candidate_preview.xml
@@ -0,0 +1,29 @@
+<?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.
+*/
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content" 
+    android:layout_height="wrap_content"
+    android:textSize="18sp"
+    android:textColor="?android:attr/textColorPrimaryInverse"
+    android:minWidth="32dip"
+    android:gravity="center"
+    android:background="@drawable/candidate_feedback_background"
+    />
diff --git a/java/res/layout/candidates.xml b/java/res/layout/candidates.xml
new file mode 100755
index 0000000..068c17e
--- /dev/null
+++ b/java/res/layout/candidates.xml
@@ -0,0 +1,79 @@
+<?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.
+*/
+-->
+
+<com.android.inputmethod.latin.CandidateViewContainer
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="@dimen/candidate_strip_height"
+        android:background="@drawable/keyboard_suggest_strip"
+        >
+
+    <LinearLayout
+            android:id="@+id/candidate_left_parent"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:orientation="horizontal">
+        <ImageButton
+            android:id="@+id/candidate_left"
+            android:background="@drawable/ic_suggest_scroll_background"
+            android:src="@drawable/ic_suggest_strip_scroll_left_arrow" 
+            android:layout_width="36dp"
+            android:layout_height="match_parent"
+            android:clickable="true"
+            />
+        
+        <ImageView
+            android:src="@drawable/keyboard_suggest_strip_divider"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            />
+    </LinearLayout>
+    
+    <com.android.inputmethod.latin.CandidateView
+        android:id="@+id/candidates"
+        android:layout_width="wrap_content"
+        android:layout_height="@dimen/candidate_strip_height"
+        android:layout_weight="1"
+        />
+        
+    <LinearLayout
+            android:id="@+id/candidate_right_parent"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:clickable="true"
+            android:orientation="horizontal">
+        <ImageView
+            android:src="@drawable/keyboard_suggest_strip_divider"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            />
+        
+        <ImageButton
+            android:id="@+id/candidate_right"
+            android:background="@drawable/ic_suggest_scroll_background"
+            android:src="@drawable/ic_suggest_strip_scroll_right_arrow" 
+            android:layout_width="36dp"
+            android:layout_height="match_parent"
+            android:clickable="true"
+            />
+    </LinearLayout>        
+    
+</com.android.inputmethod.latin.CandidateViewContainer>
diff --git a/java/res/layout/input.xml b/java/res/layout/input.xml
new file mode 100755
index 0000000..1d7c6f7
--- /dev/null
+++ b/java/res/layout/input.xml
@@ -0,0 +1,29 @@
+<?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.
+*/
+-->
+
+<com.android.inputmethod.latin.LatinKeyboardView
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@android:id/keyboardView"
+        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"
+        />
diff --git a/java/res/layout/input_trans.xml b/java/res/layout/input_trans.xml
new file mode 100755
index 0000000..a988bcc
--- /dev/null
+++ b/java/res/layout/input_trans.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.
+*/
+-->
+
+<com.android.inputmethod.latin.LatinKeyboardView
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@android:id/keyboardView"
+        android:layout_alignParentBottom="true"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="#A0000000"
+        android:keyBackground="@drawable/btn_keyboard_key_fulltrans"
+        />
diff --git a/java/res/layout/recognition_status.xml b/java/res/layout/recognition_status.xml
new file mode 100644
index 0000000..1ec06ba
--- /dev/null
+++ b/java/res/layout/recognition_status.xml
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* 
+**
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+--> 
+
+
+<LinearLayout 
+      xmlns:android="http://schemas.android.com/apk/res/android"
+      android:orientation="vertical"
+      android:layout_height="wrap_content"
+      android:layout_width="wrap_content"
+      android:background="@android:color/black"
+      android:paddingBottom="0dip"
+      android:paddingLeft="0dip"
+      android:paddingRight="0dip"
+>
+
+    <LinearLayout 
+       xmlns:android="http://schemas.android.com/apk/res/android"
+       android:id="@+id/main_image"
+       android:orientation="vertical"
+       android:background="@drawable/voice_ime_background"
+                 android:scaleType="fitXY"
+                 android:layout_width="match_parent"
+                 android:layout_height="180dip"
+                 android:paddingBottom="2dip"
+                 android:paddingTop="2dip"
+    >
+
+    <TextView android:id="@+id/text"
+        android:text="@string/voice_initializing"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:layout_marginTop="15dip"
+        android:textSize="28sp"
+        android:textColor="#ffffff"
+        android:layout_gravity="center_horizontal"
+    />
+
+    <ImageView android:id="@+id/image"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:layout_marginTop="20dip"
+        android:layout_gravity="center_horizontal"
+        android:src="@drawable/mic_slash"
+    />
+
+    <ProgressBar android:id="@+id/progress"
+        android:layout_height="60dip"
+        android:layout_width="60dip"
+        android:layout_marginTop="20dip"
+        android:layout_gravity="center_horizontal"
+        android:visibility="gone"
+        android:indeterminate="true"
+        android:indeterminateOnly="false"
+    />
+
+
+
+    </LinearLayout>
+
+    <LinearLayout android:id="@+id/button"
+        android:orientation="vertical"
+        android:background="@drawable/ok_cancel"
+        android:scaleType="fitXY"
+        android:layout_width="match_parent"
+        android:layout_height="42dip"
+        android:paddingLeft="1dip"
+        android:paddingRight="1dip"
+    >
+
+    <TextView android:id="@+id/button_text"
+        android:text="@string/cancel"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:layout_marginTop="7dip"
+        android:textSize="19sp"
+        android:textColor="#ffffff"
+        android:layout_gravity="center_horizontal"
+    />
+    </LinearLayout>
+
+</LinearLayout>
+
diff --git a/java/res/layout/voice_punctuation_hint.xml b/java/res/layout/voice_punctuation_hint.xml
new file mode 100644
index 0000000..629a7f2
--- /dev/null
+++ b/java/res/layout/voice_punctuation_hint.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* 
+**
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+--> 
+
+<LinearLayout 
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="@drawable/keyboard_suggest_strip">
+
+    <!-- TODO: Use dark mic icon. -->
+    <ImageView android:id="@+id/image"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:paddingLeft="8dip"
+            android:paddingRight="8dip"
+            android:layout_gravity="center_horizontal"
+            android:src="@drawable/ic_suggest_strip_microphone"
+    />
+
+    <TextView android:id="@+id/text"
+            android:text="@string/voice_punctuation_hint"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:paddingTop="2dip"
+            android:paddingRight="3dip"
+            android:textSize="13sp"
+            android:textColor="#888888"
+            android:layout_gravity="center_horizontal"
+    />
+
+</LinearLayout>
diff --git a/java/res/layout/voice_swipe_hint.xml b/java/res/layout/voice_swipe_hint.xml
new file mode 100644
index 0000000..4e8859a
--- /dev/null
+++ b/java/res/layout/voice_swipe_hint.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* 
+**
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+--> 
+
+<LinearLayout 
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:background="@drawable/keyboard_suggest_strip"
+        android:gravity="center_horizontal"
+        android:paddingTop="2dip">
+
+    <TextView android:id="@+id/text"
+            android:text="@string/voice_swipe_hint"
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:paddingTop="10dip"
+            android:paddingRight="6dip"
+            android:textSize="13sp"
+            android:textColor="#888888"
+            android:layout_gravity="center_horizontal"
+    />
+
+    <ImageView
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:layout_gravity="center_horizontal"
+            android:src="@drawable/ic_suggest_strip_microphone"
+    />
+    
+    <ImageView
+            android:layout_height="wrap_content"
+            android:layout_width="wrap_content"
+            android:layout_gravity="center_horizontal"
+            android:src="@drawable/ic_suggest_strip_microphone_swipe"
+    />
+    
+
+</LinearLayout>
diff --git a/java/res/raw/main.dict b/java/res/raw/main.dict
new file mode 100755
index 0000000..a456cf3
--- /dev/null
+++ b/java/res/raw/main.dict
Binary files differ
diff --git a/java/res/raw/type3.ogg b/java/res/raw/type3.ogg
new file mode 100755
index 0000000..20e6708
--- /dev/null
+++ b/java/res/raw/type3.ogg
Binary files differ
diff --git a/java/res/values-cs/bools.xml b/java/res/values-cs/bools.xml
new file mode 100644
index 0000000..897f4b3
--- /dev/null
+++ b/java/res/values-cs/bools.xml
@@ -0,0 +1,22 @@
+<?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>
+	<bool name="im_is_default">true</bool>
+</resources>
diff --git a/java/res/values-cs/donottranslate.xml b/java/res/values-cs/donottranslate.xml
new file mode 100644
index 0000000..9c2a476
--- /dev/null
+++ b/java/res/values-cs/donottranslate.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Accented characters related to "d" -->
+    <string name="alternates_for_d">ď</string>
+    <!-- Accented characters related to "r" -->
+    <string name="alternates_for_r">ř</string>
+    <!-- Accented characters related to "t" -->
+    <string name="alternates_for_t">ť</string>
+    <!-- Accented characters related to "z" -->
+    <string name="alternates_for_z">ž</string>
+</resources>
diff --git a/java/res/values-cs/strings.xml b/java/res/values-cs/strings.xml
new file mode 100644
index 0000000..02361e1
--- /dev/null
+++ b/java/res/values-cs/strings.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="7252517407088836577">"Klávesnice Android"</string>
+    <string name="english_ime_settings" msgid="6661589557206947774">"Nastavení klávesnice Android"</string>
+    <string name="vibrate_on_keypress" msgid="5258079494276955460">"Při stisku klávesy vibrovat"</string>
+    <string name="sound_on_keypress" msgid="6093592297198243644">"Zvuk při stisku klávesy"</string>
+    <string name="hit_correction" msgid="4855351009261318389">"Opravovat překlepy"</string>
+    <string name="hit_correction_summary" msgid="8761701873008070796">"Povolit opravu chyb vstupu"</string>
+    <string name="hit_correction_land" msgid="2567691684825205448">"Chyby vstupu v zobrazení na šířku"</string>
+    <string name="hit_correction_land_summary" msgid="4076803842198368328">"Povolit opravu chyb vstupu"</string>
+    <string name="auto_correction" msgid="7911639788808958255">"Návrhy slov"</string>
+    <string name="auto_correction_summary" msgid="6881047311475758267">"Automaticky opravit předchozí slovo"</string>
+    <string name="prediction" msgid="466220283138359837">"Návrhy slov"</string>
+    <string name="prediction_category" msgid="7027100625580696660">"Nastavení návrhů slov"</string>
+    <string name="prediction_summary" msgid="459788228830873110">"Povolit automatické dokončování při psaní"</string>
+    <string name="auto_complete_dialog_title" msgid="2172048590607201920">"Automatické dokončování"</string>
+    <string name="prediction_landscape" msgid="4874601565593216183">"Zvětšit textové pole"</string>
+    <string name="prediction_landscape_summary" msgid="6736551095997839472">"Skrýt návrhy slov v zobrazení na šířku"</string>
+    <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>
+    <!-- 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 běžné překlepy"</string>
+    <string name="show_suggestions" msgid="507074425254289133">"Zobrazovat návrhy"</string>
+    <string name="show_suggestions_summary" msgid="1989672863935759654">"Při psaní zobrazovat návrhy slov"</string>
+    <string name="auto_complete" msgid="1103196318775486023">"Automatické dokončování"</string>
+    <string name="auto_complete_summary" msgid="6113149638718274624">"Stisknutí mezerníku nebo interpunkce automaticky vloží zvýrazněné slovo"</string>
+  <string-array name="prediction_modes">
+    <item msgid="4870266572388153286">"Není"</item>
+    <item msgid="1669461741568287396">"Základní"</item>
+    <item msgid="4894328801530136615">"Pokročilé"</item>
+  </string-array>
+    <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Uloženo"</string>
+    <string name="alternates_for_a" msgid="2566516493365324765">"áàâãäåæ"</string>
+    <string name="alternates_for_e" msgid="3900510936875547555">"éěèêë"</string>
+    <string name="alternates_for_i" msgid="7097915268629342242">"íìîï"</string>
+    <string name="alternates_for_o" msgid="6151402748321267776">"óòôõöœø"</string>
+    <string name="alternates_for_u" msgid="5899096818189442934">"ůúùûü"</string>
+    <string name="alternates_for_s" msgid="348762530927662188">"š§ß"</string>
+    <string name="alternates_for_n" msgid="6257322556221886400">"ňñ"</string>
+    <string name="alternates_for_c" msgid="151699780720639892">"čç"</string>
+    <string name="alternates_for_y" msgid="1722776806607271199">"ýÿ"</string>
+    <string name="tip_long_press" msgid="6101270866284343344">"Podržením klávesy zobrazíte diakritiku (ž, á atd.)"</string>
+    <string name="tip_dismiss" msgid="7585579046862204381">"Stisknutím klávesy Zpět ↶ můžete klávesnici kdykoli zavřít"</string>
+    <string name="tip_access_symbols" msgid="6344098517525531652">"Přístup k číslům a symbolům"</string>
+    <string name="tip_add_to_dictionary" msgid="1487293888469227817">"Stisknutím a podržením slova zcela vlevo toto slovo přidáte do slovníku"</string>
+    <string name="touch_to_continue" msgid="7869803257948414531">"Chcete-li pokračovat, dotkněte se této rady »"</string>
+    <string name="touch_to_finish" msgid="7990196086480585789">"Chcete-li tuto radu zavřít a začít psát, dotkněte se zde."</string>
+    <string name="tip_to_open_keyboard" msgid="6821200275486950452"><b>"Klávesnice se zobrazí, kdykoli se dotknete textového pole."</b></string>
+    <string name="tip_to_view_accents" msgid="5433158573693308501"><b>"Stisknutím a podržením klávesy zobrazíte diakritiku"\n"(ó, ø, ö, ô atd.)"</b></string>
+    <string name="tip_to_open_symbols" msgid="7345139325622444880"><b>"Stisknutím této klávesy přejdete do režimu číslic a symbolů"</b></string>
+    <string name="tip_to_close_symbols" msgid="5227724217206927185"><b>"Opětovným stisknutím této klávesy se vrátíte zpět do režimu písmen"</b></string>
+    <string name="tip_to_launch_settings" msgid="8402961128983196128"><b>"Chcete-li změnit nastavení klávesnice, jako například automatické dokončování, stiskněte tuto klávesu a podržte ji."</b></string>
+    <string name="tip_to_start_typing" msgid="7213843601369174313"><b>"Zkuste to."</b></string>
+    <string name="label_go_key" msgid="1635148082137219148">"Přejít"</string>
+    <string name="label_next_key" msgid="362972844525672568">"Další"</string>
+    <string name="label_done_key" msgid="2441578748772529288">"Hotovo"</string>
+    <string name="label_send_key" msgid="2815056534433717444">"Odeslat"</string>
+    <string name="label_symbol_key" msgid="6175820506864489453">"?123"</string>
+    <string name="label_phone_key" msgid="4275497665515080551">"123"</string>
+    <string name="label_alpha_key" msgid="8864943487292437456">"ABC"</string>
+    <string name="label_alt_key" msgid="2846315350346694811">"Alt"</string>
+    <string name="voice_warning_title" msgid="4419354150908395008">"Hlasový vstup"</string>
+    <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"Pro váš jazyk aktuálně není hlasový vstup podporován, ale funguje v angličtině."</string>
+    <string name="voice_warning_may_not_understand" msgid="4611518823070986445">"Hlasový vstup je experimentální funkce, která využívá síťové rozpoznávání řeči společnosti Google."</string>
+    <string name="voice_warning_how_to_turn_off" msgid="5652369578498701761">"Chcete-li vypnout hlasový vstup, přejděte do nastavení klávesnice."</string>
+    <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>
+    <!-- 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>
+    <string name="voice_audio_error" msgid="5072707727016414454">"Problém se zvukem"</string>
+    <string name="voice_server_error" msgid="7807129913977261644">"Chyba serveru"</string>
+    <string name="voice_speech_timeout" msgid="8461817525075498795">"Nebyla detekována žádná řeč."</string>
+    <string name="voice_no_match" msgid="4285117547030179174">"Nebyly nalezeny žádné shody"</string>
+    <string name="voice_not_installed" msgid="5552450909753842415">"Hlasové vyhledávání není nainstalováno"</string>
+    <string name="voice_swipe_hint" msgid="6943546180310682021"><b>"Nápověda:"</b>" Chcete-li aktivovat hlasový vstup, přejeďte prstem přes klávesnici."</string>
+    <string name="voice_punctuation_hint" msgid="1611389463237317754"><b>"Nápověda:"</b>" Příště zkuste vyslovit interpunkci, například „tečka“, „čárka“ nebo „otazník“."</string>
+    <string name="cancel" msgid="6830980399865683324">"Zrušit"</string>
+    <string name="ok" msgid="7898366843681727667">"OK"</string>
+    <string name="voice_input" msgid="2466640768843347841">"Hlasový vstup"</string>
+  <string-array name="voice_input_modes">
+    <item msgid="1349082139076086774">"Na hlavní klávesnici"</item>
+    <item msgid="8529385602829095903">"Na klávesnici se symboly"</item>
+    <item msgid="7283103513488381103">"Vypnout"</item>
+  </string-array>
+  <string-array name="voice_input_modes_summary">
+    <item msgid="554248625705084903">"Mikrofon na hlavní klávesnici"</item>
+    <item msgid="6907837061058876770">"Mikrofon na klávesnici se symboly"</item>
+    <item msgid="3664304608587798036">"Hlasový vstup je deaktivován"</item>
+  </string-array>
+    <string name="auto_submit" msgid="9151008027068358518">"Po hlasovém vstupu automaticky odeslat"</string>
+    <string name="auto_submit_summary" msgid="4961875269610384226">"Při vyhledávání nebo přechodu na další pole automaticky stisknout Enter."</string>
+    <string name="open_the_keyboard" msgid="2215920976029260466"><font size="17"><b>"Otevřete klávesnici"\n</b></font><font size="3">\n</font>"Dotkněte se jakéhokoli textového pole."</string>
+    <string name="close_the_keyboard" msgid="6251022259044940103"><font size="17"><b>"Zavřete klávesnici"\n</b></font><font size="3">\n</font>"Stiskněte klávesu Zpět."</string>
+    <string name="touch_and_hold" msgid="6154166367273010534"><font size="17"><b>"Přidržením klávesy zobrazte možnosti"\n</b></font><font size="3">\n</font>"Použijte interpunkční znaménka a diakritiku."</string>
+    <string name="keyboard_settings" msgid="4585753477617374032"><font size="17"><b>"Nastavení klávesnice"\n</b></font><font size="3">\n</font>"Dotkněte se klávesy "<b>"?123"</b>" a přidržte ji."</string>
+    <string name="popular_domain_0" msgid="3745279225122472969">".com"</string>
+    <string name="popular_domain_1" msgid="1370572248164278467">".cz"</string>
+    <string name="popular_domain_2" msgid="3036812463748402878">".org"</string>
+    <string name="popular_domain_3" msgid="8718639560809452028">".net"</string>
+    <string name="popular_domain_4" msgid="35359437471311470">".eu"</string>
+    <string name="inputMethod" msgid="7854532062009028116">"Metoda zadávání dat"</string>
+    <string name="language_selection_title" msgid="1651299598555326750">"Vstupní jazyky"</string>
+    <string name="language_selection_summary" msgid="187110938289512256">"Jazyk můžete změnit posunutím prstu po mezerníku."</string>
+    <string name="hint_add_to_dictionary" msgid="8058519710062071085">"← Uložte slovo opětovným klepnutím"</string>
+</resources>
diff --git a/java/res/values-da/strings.xml b/java/res/values-da/strings.xml
new file mode 100644
index 0000000..15f6b1c
--- /dev/null
+++ b/java/res/values-da/strings.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="7252517407088836577">"Android-tastatur"</string>
+    <string name="english_ime_settings" msgid="6661589557206947774">"Indstillinger for Android-tastatur"</string>
+    <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibration ved tastetryk"</string>
+    <string name="sound_on_keypress" msgid="6093592297198243644">"Lyd ved tastetryk"</string>
+    <string name="hit_correction" msgid="4855351009261318389">"Ret stavefejl"</string>
+    <string name="hit_correction_summary" msgid="8761701873008070796">"Aktiver fejlretning af input"</string>
+    <string name="hit_correction_land" msgid="2567691684825205448">"Inputfejl i landskab"</string>
+    <string name="hit_correction_land_summary" msgid="4076803842198368328">"Aktiver fejlretning af input"</string>
+    <string name="auto_correction" msgid="7911639788808958255">"Ordforslag"</string>
+    <string name="auto_correction_summary" msgid="6881047311475758267">"Ret automatisk det forrige ord"</string>
+    <string name="prediction" msgid="466220283138359837">"Ordforslag"</string>
+    <string name="prediction_category" msgid="7027100625580696660">"Indstillinger for ordforslag"</string>
+    <string name="prediction_summary" msgid="459788228830873110">"Aktiver automatisk udfyldelse, når du indtaster"</string>
+    <string name="auto_complete_dialog_title" msgid="2172048590607201920">"Automatisk udfyldelse"</string>
+    <string name="prediction_landscape" msgid="4874601565593216183">"Forøg tekstfeltets størrelse"</string>
+    <string name="prediction_landscape_summary" msgid="6736551095997839472">"Skjul ordforslag i landskabsvisning"</string>
+    <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>
+    <!-- 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>
+    <string name="show_suggestions_summary" msgid="1989672863935759654">"Vis ordforslag under indtastning"</string>
+    <string name="auto_complete" msgid="1103196318775486023">"Udfyld automatisk"</string>
+    <string name="auto_complete_summary" msgid="6113149638718274624">"Mellemrumstast og tegnsætning indsætter automatisk fremhævet ord"</string>
+  <string-array name="prediction_modes">
+    <item msgid="4870266572388153286">"Ingen"</item>
+    <item msgid="1669461741568287396">"Grundlæggende"</item>
+    <item msgid="4894328801530136615">"Avanceret"</item>
+  </string-array>
+    <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: Gemt"</string>
+    <string name="alternates_for_a" msgid="2566516493365324765">"àáâãäåæ"</string>
+    <string name="alternates_for_e" msgid="3900510936875547555">"èéêë"</string>
+    <string name="alternates_for_i" msgid="7097915268629342242">"ìíîï"</string>
+    <string name="alternates_for_o" msgid="6151402748321267776">"òóôõöœø"</string>
+    <string name="alternates_for_u" msgid="5899096818189442934">"ùúûü"</string>
+    <string name="alternates_for_s" msgid="348762530927662188">"§ß"</string>
+    <string name="alternates_for_n" msgid="6257322556221886400">"ñ"</string>
+    <string name="alternates_for_c" msgid="151699780720639892">"ç"</string>
+    <string name="alternates_for_y" msgid="1722776806607271199">"ýÿ"</string>
+    <string name="tip_long_press" msgid="6101270866284343344">"Hold en tast nede for at se accenter (ø, ö osv.)"</string>
+    <string name="tip_dismiss" msgid="7585579046862204381">"Tryk på tilbagetasten ↶ for når som helst at lukke for tastaturet"</string>
+    <string name="tip_access_symbols" msgid="6344098517525531652">"Få adgang til tal og symboler"</string>
+    <string name="tip_add_to_dictionary" msgid="1487293888469227817">"Tryk og hold på ordet længst til venstre for at føje det til ordbogen"</string>
+    <string name="touch_to_continue" msgid="7869803257948414531">"Berør dette tip for at fortsætte »"</string>
+    <string name="touch_to_finish" msgid="7990196086480585789">"Berør her for at lukke dette tip og begynde at indtaste!"</string>
+    <string name="tip_to_open_keyboard" msgid="6821200275486950452"><b>"Tastaturet åbnes, når du berører et tekstfelt"</b></string>
+    <string name="tip_to_view_accents" msgid="5433158573693308501"><b>"Berør og hold en tast nede for at vise accenter"\n"(ø, ö, ô, ó osv.)"</b></string>
+    <string name="tip_to_open_symbols" msgid="7345139325622444880"><b>"Skift til tal og symboler ved at røre denne tast"</b></string>
+    <string name="tip_to_close_symbols" msgid="5227724217206927185"><b>"Gå tilbage til bogstaverne ved at berøre denne tast igen"</b></string>
+    <string name="tip_to_launch_settings" msgid="8402961128983196128"><b>"Berør og hold denne tast nede for at ændre indstillingerne for tastaturet, som f.eks. automatisk udfyldelse"</b></string>
+    <string name="tip_to_start_typing" msgid="7213843601369174313"><b>"Prøv det!"</b></string>
+    <string name="label_go_key" msgid="1635148082137219148">"Gå"</string>
+    <string name="label_next_key" msgid="362972844525672568">"Næste"</string>
+    <string name="label_done_key" msgid="2441578748772529288">"Udfør"</string>
+    <string name="label_send_key" msgid="2815056534433717444">"Send"</string>
+    <string name="label_symbol_key" msgid="6175820506864489453">"?123"</string>
+    <string name="label_phone_key" msgid="4275497665515080551">"123"</string>
+    <string name="label_alpha_key" msgid="8864943487292437456">"ABC"</string>
+    <string name="label_alt_key" msgid="2846315350346694811">"ALT"</string>
+    <string name="voice_warning_title" msgid="4419354150908395008">"Stemmeinput"</string>
+    <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"Stemmeinput understøttes i øjeblikket ikke for dit sprog, men fungerer på engelsk."</string>
+    <string name="voice_warning_may_not_understand" msgid="4611518823070986445">"Stemme-input er en funktion på forsøgsbasis, som bruger Googles netværksstemmegenkendelse."</string>
+    <string name="voice_warning_how_to_turn_off" msgid="5652369578498701761">"Slå stemmeinput fra i indstillingerne for tastaturet."</string>
+    <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>
+    <!-- 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>
+    <string name="voice_audio_error" msgid="5072707727016414454">"Lydproblem"</string>
+    <string name="voice_server_error" msgid="7807129913977261644">"Serverfejl"</string>
+    <string name="voice_speech_timeout" msgid="8461817525075498795">"Der høres ingen tale"</string>
+    <string name="voice_no_match" msgid="4285117547030179174">"Der blev ikke fundet nogen matches"</string>
+    <string name="voice_not_installed" msgid="5552450909753842415">"Stemmesøgning er ikke installeret"</string>
+    <string name="voice_swipe_hint" msgid="6943546180310682021"><b>"Tip:"</b>" Glid hen over tastaturet for at tale"</string>
+    <string name="voice_punctuation_hint" msgid="1611389463237317754"><b>"Tip:"</b>" Næste gang kan du forsøge at sige tegnsætning, f.eks. \"punktum\", \"komma\" eller \"spørgsmålstegn\"."</string>
+    <string name="cancel" msgid="6830980399865683324">"Annuller"</string>
+    <string name="ok" msgid="7898366843681727667">"OK"</string>
+    <string name="voice_input" msgid="2466640768843347841">"Stemmeinput"</string>
+  <string-array name="voice_input_modes">
+    <item msgid="1349082139076086774">"På hovedtastatur"</item>
+    <item msgid="8529385602829095903">"På symboltastatur"</item>
+    <item msgid="7283103513488381103">"Fra"</item>
+  </string-array>
+  <string-array name="voice_input_modes_summary">
+    <item msgid="554248625705084903">"Mikrofon på hovedtastatur"</item>
+    <item msgid="6907837061058876770">"Mikrofon på symboltastatur"</item>
+    <item msgid="3664304608587798036">"Stemmeinput er deaktiveret"</item>
+  </string-array>
+    <string name="auto_submit" msgid="9151008027068358518">"Send automatisk efter stemme"</string>
+    <string name="auto_submit_summary" msgid="4961875269610384226">"Tryk automatisk på enter, når du søger eller går til det næste felt."</string>
+    <string name="open_the_keyboard" msgid="2215920976029260466"><font size="17"><b>"Åbn tastaturet"\n</b></font><font size="3">\n</font>"Tryk på et hvilket som helst tekstfelt."</string>
+    <string name="close_the_keyboard" msgid="6251022259044940103"><font size="17"><b>"Luk tastaturet"\n</b></font><font size="3">\n</font>"Tryk på Tilbagetasten."</string>
+    <string name="touch_and_hold" msgid="6154166367273010534"><font size="17"><b>"Tryk på og hold en tast nede for valgmuligheder"\n</b></font><font size="3">\n</font>"Få adgang til tegnsætning og accenter."</string>
+    <string name="keyboard_settings" msgid="4585753477617374032"><font size="17"><b>"Indstillinger for tastatur"\n</b></font><font size="3">\n</font>"Tryk på og hold tasten "<b>"?123"</b>" nede."</string>
+    <string name="popular_domain_0" msgid="3745279225122472969">".com"</string>
+    <string name="popular_domain_1" msgid="1370572248164278467">".net"</string>
+    <string name="popular_domain_2" msgid="3036812463748402878">".org"</string>
+    <string name="popular_domain_3" msgid="8718639560809452028">".gov"</string>
+    <string name="popular_domain_4" msgid="35359437471311470">".edu"</string>
+    <string name="inputMethod" msgid="7854532062009028116">"Inputmetode"</string>
+    <string name="language_selection_title" msgid="1651299598555326750">"Inputsprog"</string>
+    <string name="language_selection_summary" msgid="187110938289512256">"Træk fingeren på mellemrumstasten for at skifte sprog"</string>
+    <string name="hint_add_to_dictionary" msgid="8058519710062071085">"← Tast igen for at gemme"</string>
+</resources>
diff --git a/java/res/values-de/bools.xml b/java/res/values-de/bools.xml
new file mode 100644
index 0000000..897f4b3
--- /dev/null
+++ b/java/res/values-de/bools.xml
@@ -0,0 +1,22 @@
+<?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>
+	<bool name="im_is_default">true</bool>
+</resources>
diff --git a/java/res/values-de/strings.xml b/java/res/values-de/strings.xml
new file mode 100644
index 0000000..e203fb3
--- /dev/null
+++ b/java/res/values-de/strings.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="7252517407088836577">"Android-Tastatur"</string>
+    <string name="english_ime_settings" msgid="6661589557206947774">"Android-Tastatureinstellungen"</string>
+    <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrieren bei Tastendruck"</string>
+    <string name="sound_on_keypress" msgid="6093592297198243644">"Sound bei Tastendruck"</string>
+    <string name="hit_correction" msgid="4855351009261318389">"Eingabefehler korrigieren"</string>
+    <string name="hit_correction_summary" msgid="8761701873008070796">"Korrektur von Eingabefehlern aktivieren"</string>
+    <string name="hit_correction_land" msgid="2567691684825205448">"Eingabefehler im Querformat"</string>
+    <string name="hit_correction_land_summary" msgid="4076803842198368328">"Korrektur von Eingabefehlern aktivieren"</string>
+    <string name="auto_correction" msgid="7911639788808958255">"Wortvorschläge"</string>
+    <string name="auto_correction_summary" msgid="6881047311475758267">"Vorheriges Wort automatisch korrigieren"</string>
+    <string name="prediction" msgid="466220283138359837">"Wortvorschläge"</string>
+    <string name="prediction_category" msgid="7027100625580696660">"Einstellungen für Wortvorschläge"</string>
+    <string name="prediction_summary" msgid="459788228830873110">"Automatische Vervollständigung während der Eingabe aktivieren"</string>
+    <string name="auto_complete_dialog_title" msgid="2172048590607201920">"Autom. vervollständigen"</string>
+    <string name="prediction_landscape" msgid="4874601565593216183">"Textfeld vergrößern"</string>
+    <string name="prediction_landscape_summary" msgid="6736551095997839472">"Wortvorschläge in Querformat ausblenden"</string>
+    <string name="auto_cap" msgid="1719746674854628252">"Autom. Groß-/Kleinschreibung"</string>
+    <string name="auto_cap_summary" msgid="3260681697600786825">"Sätze mit Großbuchstaben beginnen"</string>
+    <string name="auto_punctuate" msgid="7276672334264521751">"Autom. Zeichensetzung"</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>
+    <string name="show_suggestions_summary" msgid="1989672863935759654">"Vorgeschlagene Wörter während des Tippens anzeigen"</string>
+    <string name="auto_complete" msgid="1103196318775486023">"Autom. vervollständigen"</string>
+    <string name="auto_complete_summary" msgid="6113149638718274624">"Leertaste und Interpunktion fügen autom. ein markiertes Wort ein"</string>
+  <string-array name="prediction_modes">
+    <item msgid="4870266572388153286">"Keine"</item>
+    <item msgid="1669461741568287396">"Standard"</item>
+    <item msgid="4894328801530136615">"Erweitert"</item>
+  </string-array>
+    <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Gespeichert"</string>
+    <string name="alternates_for_a" msgid="2566516493365324765">"ä"</string>
+    <string name="alternates_for_e" msgid="3900510936875547555">"èéêë"</string>
+    <string name="alternates_for_i" msgid="7097915268629342242">"ìíîï"</string>
+    <string name="alternates_for_o" msgid="6151402748321267776">"ö"</string>
+    <string name="alternates_for_u" msgid="5899096818189442934">"ùúûü"</string>
+    <string name="alternates_for_s" msgid="348762530927662188">"§ß"</string>
+    <string name="alternates_for_n" msgid="6257322556221886400">"ñ"</string>
+    <string name="alternates_for_c" msgid="151699780720639892">"ç"</string>
+    <string name="alternates_for_y" msgid="1722776806607271199">"ýÿ"</string>
+    <string name="tip_long_press" msgid="6101270866284343344">"Zur Anzeige von Umlauten (ä, ö usw.) Taste gedrückt halten"</string>
+    <string name="tip_dismiss" msgid="7585579046862204381">"Zum Schließen der Tastatur ↶ drücken"</string>
+    <string name="tip_access_symbols" msgid="6344098517525531652">"Auf Zahlen und Symbole zugreifen"</string>
+    <string name="tip_add_to_dictionary" msgid="1487293888469227817">"Lange auf das Wort ganz links außen drücken, um es zum Wörterbuch hinzuzufügen"</string>
+    <string name="touch_to_continue" msgid="7869803257948414531">"Diesen Hinweis berühren, um fortzufahren »"</string>
+    <string name="touch_to_finish" msgid="7990196086480585789">"Hier berühren, um diesen Hinweis zu schließen und mit dem Tippen zu beginnen!"</string>
+    <string name="tip_to_open_keyboard" msgid="6821200275486950452"><b>"Die Tastatur wird immer dann geöffnet, wenn Sie ein Textfeld berühren."</b></string>
+    <string name="tip_to_view_accents" msgid="5433158573693308501"><b>"Halten Sie eine Taste gedrückt, um Sonderzeichen anzuzeigen"\n"(ø, ö, ô, ó usw.)."</b></string>
+    <string name="tip_to_open_symbols" msgid="7345139325622444880"><b>"Wechseln Sie zu Nummern und Symbolen, indem Sie diese Taste berühren."</b></string>
+    <string name="tip_to_close_symbols" msgid="5227724217206927185"><b>"Gehen Sie zurück zu Buchstaben, indem Sie diese Taste erneut drücken."</b></string>
+    <string name="tip_to_launch_settings" msgid="8402961128983196128"><b>"Halten Sie diese Taste gedrückt, um die Tastatureinstellungen, wie beispielsweise die automatische Vervollständigung, zu ändern."</b></string>
+    <string name="tip_to_start_typing" msgid="7213843601369174313"><b>"Probieren Sie es!"</b></string>
+    <string name="label_go_key" msgid="1635148082137219148">"Los"</string>
+    <string name="label_next_key" msgid="362972844525672568">"Weiter"</string>
+    <string name="label_done_key" msgid="2441578748772529288">"Fertig"</string>
+    <string name="label_send_key" msgid="2815056534433717444">"Senden"</string>
+    <string name="label_symbol_key" msgid="6175820506864489453">"?123"</string>
+    <string name="label_phone_key" msgid="4275497665515080551">"123"</string>
+    <string name="label_alpha_key" msgid="8864943487292437456">"ABC"</string>
+    <string name="label_alt_key" msgid="2846315350346694811">"ALT"</string>
+    <string name="voice_warning_title" msgid="4419354150908395008">"Spracheingabe"</string>
+    <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"Spracheingaben werden derzeit nicht für Ihre Sprache unterstützt, funktionieren jedoch in Englisch."</string>
+    <string name="voice_warning_may_not_understand" msgid="4611518823070986445">"Die Spracheingabe ist eine Funktion im Versuchsstadium, die die vernetzte Spracherkennung von Google verwendet."</string>
+    <string name="voice_warning_how_to_turn_off" msgid="5652369578498701761">"Wenn Sie die Spracheingabe deaktivieren möchten, rufen Sie die Tastatureinstellungen auf."</string>
+    <string name="voice_hint_dialog_message" msgid="6892342981545727994">"Um die Spracheingabe zu verwenden, drücken Sie den Mikrofonknopf 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>
+    <!-- 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>
+    <string name="voice_audio_error" msgid="5072707727016414454">"Audio-Problem"</string>
+    <string name="voice_server_error" msgid="7807129913977261644">"Serverfehler"</string>
+    <string name="voice_speech_timeout" msgid="8461817525075498795">"Keine Sprache zu hören"</string>
+    <string name="voice_no_match" msgid="4285117547030179174">"Keine Übereinstimmungen gefunden"</string>
+    <string name="voice_not_installed" msgid="5552450909753842415">"Sprach-Suche nicht installiert"</string>
+    <string name="voice_swipe_hint" msgid="6943546180310682021"><b>"Hinweis:"</b>" Ziehen Sie zum Sprechen den Finger über die Tastatur."</string>
+    <string name="voice_punctuation_hint" msgid="1611389463237317754"><b>"Hinweis:"</b>" Versuchen Sie beim nächsten Mal, Satzzeichen wie \"Punkt\", \"Komma\" oder \"Fragezeichen\" per Sprachbefehl einzugeben."</string>
+    <string name="cancel" msgid="6830980399865683324">"Abbrechen"</string>
+    <string name="ok" msgid="7898366843681727667">"OK"</string>
+    <string name="voice_input" msgid="2466640768843347841">"Spracheingabe"</string>
+  <string-array name="voice_input_modes">
+    <item msgid="1349082139076086774">"Auf Haupttastatur"</item>
+    <item msgid="8529385602829095903">"Auf Symboltastatur"</item>
+    <item msgid="7283103513488381103">"Aus"</item>
+  </string-array>
+  <string-array name="voice_input_modes_summary">
+    <item msgid="554248625705084903">"Mikrofon auf Haupttastatur"</item>
+    <item msgid="6907837061058876770">"Mikrofon auf Symboltastatur"</item>
+    <item msgid="3664304608587798036">"Spracheingabe ist deaktiviert"</item>
+  </string-array>
+    <string name="auto_submit" msgid="9151008027068358518">"Nach Sprachaufnahme automatisch senden"</string>
+    <string name="auto_submit_summary" msgid="4961875269610384226">"Drücken Sie auf die Eingabetaste, wenn Sie einen Suchvorgang durchführen oder zum nächsten Feld wechseln."</string>
+    <string name="open_the_keyboard" msgid="2215920976029260466"><font size="17"><b>"Tastatur öffnen"\n</b></font><font size="3">\n</font>"Berühren Sie ein beliebiges Textfeld."</string>
+    <string name="close_the_keyboard" msgid="6251022259044940103"><font size="17"><b>"Tastatur schließen"\n</b></font><font size="3">\n</font>"Drücken Sie die Taste \"Zurück\"."</string>
+    <string name="touch_and_hold" msgid="6154166367273010534"><font size="17"><b>"Eine Taste für Optionen berühren und gedrückt halten"\n</b></font><font size="3">\n</font>"Greifen Sie auf Satzzeichen und Akzente zu."</string>
+    <string name="keyboard_settings" msgid="4585753477617374032"><font size="17"><b>"Tastatureinstellungen"\n</b></font><font size="3">\n</font>"Berühren und halten Sie die Taste "<b>"?123"</b>" gedrückt."</string>
+    <string name="popular_domain_0" msgid="3745279225122472969">".com"</string>
+    <string name="popular_domain_1" msgid="1370572248164278467">".net"</string>
+    <string name="popular_domain_2" msgid="3036812463748402878">".org"</string>
+    <string name="popular_domain_3" msgid="8718639560809452028">".gov"</string>
+    <string name="popular_domain_4" msgid="35359437471311470">".edu"</string>
+    <string name="inputMethod" msgid="7854532062009028116">"Eingabemethode"</string>
+    <string name="language_selection_title" msgid="1651299598555326750">"Eingabesprachen"</string>
+    <string name="language_selection_summary" msgid="187110938289512256">"Finger über die Leertaste bewegen, um die Sprache zu ändern"</string>
+    <string name="hint_add_to_dictionary" msgid="8058519710062071085">"← Zum Speichern erneut tippen"</string>
+</resources>
diff --git a/java/res/values-el/strings.xml b/java/res/values-el/strings.xml
new file mode 100644
index 0000000..85c18df
--- /dev/null
+++ b/java/res/values-el/strings.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="7252517407088836577">"Πληκτρολόγιο Android"</string>
+    <string name="english_ime_settings" msgid="6661589557206947774">"Ρυθμίσεις πληκτρολογίου Android"</string>
+    <string name="vibrate_on_keypress" msgid="5258079494276955460">"Δόνηση κατά το πάτημα πλήκτρων"</string>
+    <string name="sound_on_keypress" msgid="6093592297198243644">"Ήχος κατά το πάτημα πλήκτρων"</string>
+    <string name="hit_correction" msgid="4855351009261318389">"Διόρθωση σφαλμάτων πληκτρολόγησης"</string>
+    <string name="hit_correction_summary" msgid="8761701873008070796">"Ενεργοποίηση διόρθωσης σφαλμάτων εισόδου"</string>
+    <string name="hit_correction_land" msgid="2567691684825205448">"Σφάλματα οριζόντιας εισαγωγής"</string>
+    <string name="hit_correction_land_summary" msgid="4076803842198368328">"Ενεργοποίηση διόρθωσης σφαλμάτων εισόδου"</string>
+    <string name="auto_correction" msgid="7911639788808958255">"Υποδείξεις λέξεων"</string>
+    <string name="auto_correction_summary" msgid="6881047311475758267">"Αυτόματη διόρθωση της προηγούμενης λέξης"</string>
+    <string name="prediction" msgid="466220283138359837">"Υποδείξεις λέξεων"</string>
+    <string name="prediction_category" msgid="7027100625580696660">"Ρυθμίσεις υποδείξεων λέξεων"</string>
+    <string name="prediction_summary" msgid="459788228830873110">"Ενεργοποίηση αυτόματης συμπλήρωσης κατά την πληκτρολόγηση"</string>
+    <string name="auto_complete_dialog_title" msgid="2172048590607201920">"Αυτόματη συμπλήρωση"</string>
+    <string name="prediction_landscape" msgid="4874601565593216183">"Αυξήστε το μέγεθος του πεδίου κειμένου"</string>
+    <string name="prediction_landscape_summary" msgid="6736551095997839472">"Απόκρυψη υποδείξεων λέξεων στην οριζόντια προβολή"</string>
+    <string name="auto_cap" msgid="1719746674854628252">"Αυτόματη χρήση κεφαλαίων"</string>
+    <string name="auto_cap_summary" msgid="3260681697600786825">"Κεφαλαίο το πρώτο γράμμα της πρότασης"</string>
+    <string name="auto_punctuate" msgid="7276672334264521751">"Αυτόματος τονισμός"</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>
+    <string name="show_suggestions_summary" msgid="1989672863935759654">"Προβολή προτεινόμενων λέξεων κατά την πληκτρολόγηση"</string>
+    <string name="auto_complete" msgid="1103196318775486023">"Αυτόματη συμπλήρωση"</string>
+    <string name="auto_complete_summary" msgid="6113149638718274624">"Τα πλήκ.διαστήμ.και τονισμού εισάγ.αυτόμ.την επιλ.λέξη"</string>
+  <string-array name="prediction_modes">
+    <item msgid="4870266572388153286">"Καμία"</item>
+    <item msgid="1669461741568287396">"Βασική"</item>
+    <item msgid="4894328801530136615">"Σύνθετη"</item>
+  </string-array>
+    <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Αποθηκεύτηκε"</string>
+    <string name="alternates_for_a" msgid="2566516493365324765">"àáâãäåæ"</string>
+    <string name="alternates_for_e" msgid="3900510936875547555">"èéêë"</string>
+    <string name="alternates_for_i" msgid="7097915268629342242">"ìíîï"</string>
+    <string name="alternates_for_o" msgid="6151402748321267776">"òóôõöœø"</string>
+    <string name="alternates_for_u" msgid="5899096818189442934">"ùúûü"</string>
+    <string name="alternates_for_s" msgid="348762530927662188">"§ß"</string>
+    <string name="alternates_for_n" msgid="6257322556221886400">"ñ"</string>
+    <string name="alternates_for_c" msgid="151699780720639892">"ç"</string>
+    <string name="alternates_for_y" msgid="1722776806607271199">"ýÿ"</string>
+    <string name="tip_long_press" msgid="6101270866284343344">"Κρατήστε πατημένο ένα πλήκτρο για να δείτε τους τονισμένους χαρακτήρες (ø, ö, κ.τ.λ.)"</string>
+    <string name="tip_dismiss" msgid="7585579046862204381">"Πατήστε το πλήκτρο Πίσω ↶ για να κλείσετε το πληκτρολόγιο ανά πάσα στιγμή"</string>
+    <string name="tip_access_symbols" msgid="6344098517525531652">"Πρόσβαση σε αριθμούς και σύμβολα"</string>
+    <string name="tip_add_to_dictionary" msgid="1487293888469227817">"Κρατήστε πατημένη τη λέξη στην άκρη αριστερά, για να την προσθέσετε στο λεξικό"</string>
+    <string name="touch_to_continue" msgid="7869803257948414531">"Αγγίξτε αυτή τη συμβουλή για να συνεχίσετε »"</string>
+    <string name="touch_to_finish" msgid="7990196086480585789">"Αγγίξτε εδώ για να κλείσετε τη συμβουλή και να ξεκινήσετε την πληκτρολόγηση!"</string>
+    <string name="tip_to_open_keyboard" msgid="6821200275486950452"><b>"Το πληκτρολόγιο ανοίγει κάθε φορά που αγγίζετε ένα πεδίο κειμένου"</b></string>
+    <string name="tip_to_view_accents" msgid="5433158573693308501"><b>"Αγγίξτε και κρατήστε κάποιο πλήκτρο για να προβάλετε τους τονισμένους χαρακτήρες"\n"(ø, ö, ô, ó κ.τ.λ.)"</b></string>
+    <string name="tip_to_open_symbols" msgid="7345139325622444880"><b>"Αλλαγή σε αριθμούς και σύμβολα με το πάτημα αυτού του πλήκτρου"</b></string>
+    <string name="tip_to_close_symbols" msgid="5227724217206927185"><b>"Επιστρέψτε στα γράμματα αγγίζοντας ξανά αυτό το πλήκτρο"</b></string>
+    <string name="tip_to_launch_settings" msgid="8402961128983196128"><b>"Αγγίξτε &amp; κρατήστε πατημένο αυτό το πλήκτρο για να αλλάξετε τις ρυθμίσεις πληκτρολογίου, όπως η αυτόματη συμπλήρωση"</b></string>
+    <string name="tip_to_start_typing" msgid="7213843601369174313"><b>"Δοκιμάστε το!"</b></string>
+    <string name="label_go_key" msgid="1635148082137219148">"Μετάβαση"</string>
+    <string name="label_next_key" msgid="362972844525672568">"Επόμενο"</string>
+    <string name="label_done_key" msgid="2441578748772529288">"Τέλος"</string>
+    <string name="label_send_key" msgid="2815056534433717444">"Αποστολή"</string>
+    <string name="label_symbol_key" msgid="6175820506864489453">"?123"</string>
+    <string name="label_phone_key" msgid="4275497665515080551">"123"</string>
+    <string name="label_alpha_key" msgid="8864943487292437456">"ΑΒΓ"</string>
+    <string name="label_alt_key" msgid="2846315350346694811">"ALT"</string>
+    <string name="voice_warning_title" msgid="4419354150908395008">"Φωνητική είσοδος"</string>
+    <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"Η φωνητική είσοδος δεν υποστηρίζεται αυτή τη στιγμή για τη γλώσσα σας, ωστόσο λειτουργεί στα Αγγλικά."</string>
+    <string name="voice_warning_may_not_understand" msgid="4611518823070986445">"Οι φωνητικές εντολές είναι μια πειραματική λειτουργία, η οποία χρησιμοποιεί τη δικτυακή αναγνώριση ομιλίας της Google."</string>
+    <string name="voice_warning_how_to_turn_off" msgid="5652369578498701761">"Για να απενεργοποιήσετε τη φωνητική είσοδο, μεταβείτε στις ρυθμίσεις πληκτρολογίου."</string>
+    <string name="voice_hint_dialog_message" msgid="6892342981545727994">"Για να χρησιμοποιήσετε τις φωνητικές εντολές, πιέστε το κουμπί μικροφώνου ή σύρετε το δάχτυλό σας κατά μήκος του πληκτρολογίου της οθόνης."</string>
+    <string name="voice_listening" msgid="467518160751321844">"Μιλήστε τώρα"</string>
+    <string name="voice_working" msgid="6666937792815731889">"Σε λειτουργία"</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>
+    <string name="voice_audio_error" msgid="5072707727016414454">"Πρόβλημα ήχου"</string>
+    <string name="voice_server_error" msgid="7807129913977261644">"Σφάλμα διακομιστή"</string>
+    <string name="voice_speech_timeout" msgid="8461817525075498795">"Δεν ακούγεται ομιλία"</string>
+    <string name="voice_no_match" msgid="4285117547030179174">"Δεν βρέθηκε καμία αντιστοίχιση"</string>
+    <string name="voice_not_installed" msgid="5552450909753842415">"Η Αναζήτηση με φωνή δεν εγκαταστάθηκε"</string>
+    <string name="voice_swipe_hint" msgid="6943546180310682021"><b>"Υπόδειξη:"</b>" Σύρετε κατά μήκος του πληκτρολογίου για να μιλήσετε"</string>
+    <string name="voice_punctuation_hint" msgid="1611389463237317754"><b>"Υπόδειξη:"</b>" Την επόμενη φορά, προσπαθήστε να προφέρετε σημεία στίξης, όπως \"τελεία\", \"κόμμα\" ή \"ερωτηματικό\"."</string>
+    <string name="cancel" msgid="6830980399865683324">"Ακύρωση"</string>
+    <string name="ok" msgid="7898366843681727667">"ΟΚ"</string>
+    <string name="voice_input" msgid="2466640768843347841">"Φωνητική είσοδος"</string>
+  <string-array name="voice_input_modes">
+    <item msgid="1349082139076086774">"Στο κύριο πληκτρολόγιο"</item>
+    <item msgid="8529385602829095903">"Πληκτρολόγιο συμβόλων ενεργοποίησης"</item>
+    <item msgid="7283103513488381103">"Απενεργοποίηση"</item>
+  </string-array>
+  <string-array name="voice_input_modes_summary">
+    <item msgid="554248625705084903">"Μικρόφωνο στο κύριο πληκτρολόγιο"</item>
+    <item msgid="6907837061058876770">"Μικρόφωνο στο πληκτρολόγιο συμβόλων"</item>
+    <item msgid="3664304608587798036">"Η φωνητική είσοδος είναι απενεργοποιημένη"</item>
+  </string-array>
+    <string name="auto_submit" msgid="9151008027068358518">"Αυτόματη υποβολή μετά από ήχο"</string>
+    <string name="auto_submit_summary" msgid="4961875269610384226">"Πατήστε enter αυτόματα κατά την αναζήτηση ή τη μετάβαση στο επόμενο πεδίο."</string>
+    <string name="open_the_keyboard" msgid="2215920976029260466"><font size="17"><b>"Ανοίξτε το πληκτρολόγιο"\n</b></font><font size="3">\n</font>"Αγγίξτε οποιοδήποτε πεδίο κειμένου."</string>
+    <string name="close_the_keyboard" msgid="6251022259044940103"><font size="17"><b>"Κλείστε το πληκτρολόγιο"\n</b></font><font size="3">\n</font>"Πατήστε το πλήκτρο Πίσω."</string>
+    <string name="touch_and_hold" msgid="6154166367273010534"><font size="17"><b>"Αγγίξτε και κρατήστε ένα πλήκτρο για ορισμό επιλογών"\n</b></font><font size="3">\n</font>"Πρόσβαση στα σημεία στίξης και τονισμού."</string>
+    <string name="keyboard_settings" msgid="4585753477617374032"><font size="17"><b>"Ρυθμίσεις πληκτρολογίου"\n</b></font><font size="3">\n</font>"Αγγίξτε και κρατήστε το πλήκτρο "<b>"?123"</b>"."</string>
+    <string name="popular_domain_0" msgid="3745279225122472969">".com"</string>
+    <string name="popular_domain_1" msgid="1370572248164278467">".net"</string>
+    <string name="popular_domain_2" msgid="3036812463748402878">".org"</string>
+    <string name="popular_domain_3" msgid="8718639560809452028">".gov"</string>
+    <string name="popular_domain_4" msgid="35359437471311470">".edu"</string>
+    <string name="inputMethod" msgid="7854532062009028116">"Μέθοδος εισόδου"</string>
+    <string name="language_selection_title" msgid="1651299598555326750">"Γλώσσες εισόδου"</string>
+    <string name="language_selection_summary" msgid="187110938289512256">"Σύρετε το δάχτυλο στο πλήκτρο διαστήματος για να αλλάξετε γλώσσα"</string>
+    <string name="hint_add_to_dictionary" msgid="8058519710062071085">"← Πατήστε ξανά για αποθήκευση"</string>
+</resources>
diff --git a/java/res/values-en/bools.xml b/java/res/values-en/bools.xml
new file mode 100644
index 0000000..897f4b3
--- /dev/null
+++ b/java/res/values-en/bools.xml
@@ -0,0 +1,22 @@
+<?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>
+	<bool name="im_is_default">true</bool>
+</resources>
diff --git a/java/res/values-es-rUS/strings.xml b/java/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000..541e153
--- /dev/null
+++ b/java/res/values-es-rUS/strings.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="7252517407088836577">"Teclado de Android"</string>
+    <string name="english_ime_settings" msgid="6661589557206947774">"Configuración de teclado de Android"</string>
+    <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrar al pulsar teclas"</string>
+    <string name="sound_on_keypress" msgid="6093592297198243644">"Sonar al pulsar las teclas"</string>
+    <string name="hit_correction" msgid="4855351009261318389">"Corregir errores de escritura"</string>
+    <string name="hit_correction_summary" msgid="8761701873008070796">"Habilitar corrección de error de entrada"</string>
+    <string name="hit_correction_land" msgid="2567691684825205448">"Errores de entrada apaisada"</string>
+    <string name="hit_correction_land_summary" msgid="4076803842198368328">"Habilitar corrección de error de entrada"</string>
+    <string name="auto_correction" msgid="7911639788808958255">"Sugerencias de palabras"</string>
+    <string name="auto_correction_summary" msgid="6881047311475758267">"Corregir automáticamente la palabra anterior"</string>
+    <string name="prediction" msgid="466220283138359837">"Sugerencias de palabras"</string>
+    <string name="prediction_category" msgid="7027100625580696660">"Configuración de sugerencia de palabra"</string>
+    <string name="prediction_summary" msgid="459788228830873110">"Habilitar finalización automática al escribir"</string>
+    <string name="auto_complete_dialog_title" msgid="2172048590607201920">"Finalización automática"</string>
+    <string name="prediction_landscape" msgid="4874601565593216183">"Aumentar el tamaño del campo de texto"</string>
+    <string name="prediction_landscape_summary" msgid="6736551095997839472">"Ocultar sugerencias de palabras en vista apaisada"</string>
+    <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>
+    <!-- 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>
+    <string name="show_suggestions_summary" msgid="1989672863935759654">"Mostrar palabras sugeridas mientras escribe"</string>
+    <string name="auto_complete" msgid="1103196318775486023">"Completar automát."</string>
+    <string name="auto_complete_summary" msgid="6113149638718274624">"La barra espaciadora o la puntuación insertan automáticamente la palabra resaltada."</string>
+  <string-array name="prediction_modes">
+    <item msgid="4870266572388153286">"Ninguno"</item>
+    <item msgid="1669461741568287396">"Básico"</item>
+    <item msgid="4894328801530136615">"Avanzado"</item>
+  </string-array>
+    <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: guardada"</string>
+    <string name="alternates_for_a" msgid="2566516493365324765">"àáâãäåæ"</string>
+    <string name="alternates_for_e" msgid="3900510936875547555">"èéêë"</string>
+    <string name="alternates_for_i" msgid="7097915268629342242">"ìíîï"</string>
+    <string name="alternates_for_o" msgid="6151402748321267776">"òóôõöœø"</string>
+    <string name="alternates_for_u" msgid="5899096818189442934">"ùúûü"</string>
+    <string name="alternates_for_s" msgid="348762530927662188">"§ß"</string>
+    <string name="alternates_for_n" msgid="6257322556221886400">"ñ"</string>
+    <string name="alternates_for_c" msgid="151699780720639892">"ç"</string>
+    <string name="alternates_for_y" msgid="1722776806607271199">"ýÿ"</string>
+    <string name="tip_long_press" msgid="6101270866284343344">"Mantén una tecla presionada para ver los acentos (ø, ö, etc.)"</string>
+    <string name="tip_dismiss" msgid="7585579046862204381">"Pulsa la tecla hacia atrás ↶ para cerrar el teclado en cualquier momento"</string>
+    <string name="tip_access_symbols" msgid="6344098517525531652">"Acceder a números y símbolos"</string>
+    <string name="tip_add_to_dictionary" msgid="1487293888469227817">"Presiona y mantén presionada la palabra de la izquierda para agregarla al diccionario"</string>
+    <string name="touch_to_continue" msgid="7869803257948414531">"Toca esta sugerencia para continuar »"</string>
+    <string name="touch_to_finish" msgid="7990196086480585789">"Toca aquí para cerrar esta sugerencia y comenzar a escribir."</string>
+    <string name="tip_to_open_keyboard" msgid="6821200275486950452"><b>"El teclado se abre cada vez que tocas un campo de texto."</b></string>
+    <string name="tip_to_view_accents" msgid="5433158573693308501"><b>"Toca y mantén presionada una tecla para ver los acentos"\n"(ø, ö, ô, ó, y así sucesivamente)."</b></string>
+    <string name="tip_to_open_symbols" msgid="7345139325622444880"><b>"Cambia de números a símbolos tocando esta tecla."</b></string>
+    <string name="tip_to_close_symbols" msgid="5227724217206927185"><b>"Vuelve a letras tocando esta tecla nuevamente."</b></string>
+    <string name="tip_to_launch_settings" msgid="8402961128983196128"><b>"Toca &amp;amp y mantén presionada esta tecla para cambiar la configuración del teclado, como completar automáticamente."</b></string>
+    <string name="tip_to_start_typing" msgid="7213843601369174313"><b>"¡Pruébalo!"</b></string>
+    <string name="label_go_key" msgid="1635148082137219148">"Ir"</string>
+    <string name="label_next_key" msgid="362972844525672568">"Siguiente"</string>
+    <string name="label_done_key" msgid="2441578748772529288">"Hecho"</string>
+    <string name="label_send_key" msgid="2815056534433717444">"Enviar"</string>
+    <string name="label_symbol_key" msgid="6175820506864489453">"?123"</string>
+    <string name="label_phone_key" msgid="4275497665515080551">"123"</string>
+    <string name="label_alpha_key" msgid="8864943487292437456">"ABC"</string>
+    <string name="label_alt_key" msgid="2846315350346694811">"ALT"</string>
+    <string name="voice_warning_title" msgid="4419354150908395008">"Entrada por voz"</string>
+    <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"La entrada por voz no está admitida en tu idioma, pero sí funciona en inglés."</string>
+    <string name="voice_warning_may_not_understand" msgid="4611518823070986445">"La entrada por voz es una característica experimental que utiliza la red de reconocimiento de voz de Google."</string>
+    <string name="voice_warning_how_to_turn_off" msgid="5652369578498701761">"Para desactivar la entrada por voz, ve a configuración del teclado."</string>
+    <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>
+    <!-- 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>
+    <string name="voice_audio_error" msgid="5072707727016414454">"Problema de audio"</string>
+    <string name="voice_server_error" msgid="7807129913977261644">"Error del servidor"</string>
+    <string name="voice_speech_timeout" msgid="8461817525075498795">"No se oyó la voz"</string>
+    <string name="voice_no_match" msgid="4285117547030179174">"No se encontraron coincidencias"</string>
+    <string name="voice_not_installed" msgid="5552450909753842415">"Búsqueda por voz no instalada"</string>
+    <string name="voice_swipe_hint" msgid="6943546180310682021"><b>"Sugerencia:"</b>" Deslizar en el teclado para hablar"</string>
+    <string name="voice_punctuation_hint" msgid="1611389463237317754"><b>"Sugerencia:"</b>" La próxima vez intenta decir la puntuación como \"punto\", \"coma\" o \"signo de pregunta\"."</string>
+    <string name="cancel" msgid="6830980399865683324">"Cancelar"</string>
+    <string name="ok" msgid="7898366843681727667">"Aceptar"</string>
+    <string name="voice_input" msgid="2466640768843347841">"Entrada por voz"</string>
+  <string-array name="voice_input_modes">
+    <item msgid="1349082139076086774">"En el teclado principal"</item>
+    <item msgid="8529385602829095903">"En el teclado de símbolos"</item>
+    <item msgid="7283103513488381103">"Apagado"</item>
+  </string-array>
+  <string-array name="voice_input_modes_summary">
+    <item msgid="554248625705084903">"Micrófono en el teclado principal"</item>
+    <item msgid="6907837061058876770">"Micrófono en el teclado de símbolos"</item>
+    <item msgid="3664304608587798036">"La entrada por voz está inhabilitada."</item>
+  </string-array>
+    <string name="auto_submit" msgid="9151008027068358518">"Enviar automáticamente después del audio"</string>
+    <string name="auto_submit_summary" msgid="4961875269610384226">"Presionar automáticamente Ingresar al buscar o ir al campo siguiente."</string>
+    <string name="open_the_keyboard" msgid="2215920976029260466"><font size="17"><b>"Abrir el teclado"\n</b></font><font size="3">\n</font>"Tocar cualquier campo de texto."</string>
+    <string name="close_the_keyboard" msgid="6251022259044940103"><font size="17"><b>"Cerrar el teclado"\n</b></font><font size="3">\n</font>"Presionar la tecla Atrás."</string>
+    <string name="touch_and_hold" msgid="6154166367273010534"><font size="17"><b>"Tocar &amp; y mantener presionada una tecla para las opciones"\n</b></font><font size="3">\n</font>"Acceder a puntuación y acentos."</string>
+    <string name="keyboard_settings" msgid="4585753477617374032"><font size="17"><b>"Configuración del teclado"\n</b></font><font size="3">\n</font>"Tocar &amp; y mantener presionada la tecla "<b>"?123"</b>"."</string>
+    <string name="popular_domain_0" msgid="3745279225122472969">".com"</string>
+    <string name="popular_domain_1" msgid="1370572248164278467">".net"</string>
+    <string name="popular_domain_2" msgid="3036812463748402878">".org"</string>
+    <string name="popular_domain_3" msgid="8718639560809452028">".gov"</string>
+    <string name="popular_domain_4" msgid="35359437471311470">".edu"</string>
+    <string name="inputMethod" msgid="7854532062009028116">"Método de entrada"</string>
+    <string name="language_selection_title" msgid="1651299598555326750">"Idiomas de entrada"</string>
+    <string name="language_selection_summary" msgid="187110938289512256">"Deslizarse manualmente por la barra espaciadora para cambiar el idioma"</string>
+    <string name="hint_add_to_dictionary" msgid="8058519710062071085">"← Presionar nuevamente para guardar"</string>
+</resources>
diff --git a/java/res/values-es/bools.xml b/java/res/values-es/bools.xml
new file mode 100644
index 0000000..897f4b3
--- /dev/null
+++ b/java/res/values-es/bools.xml
@@ -0,0 +1,22 @@
+<?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>
+	<bool name="im_is_default">true</bool>
+</resources>
diff --git a/java/res/values-es/strings.xml b/java/res/values-es/strings.xml
new file mode 100644
index 0000000..fbc597e
--- /dev/null
+++ b/java/res/values-es/strings.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="7252517407088836577">"Teclado de Android"</string>
+    <string name="english_ime_settings" msgid="6661589557206947774">"Configuración del teclado de Android"</string>
+    <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrar al pulsar tecla"</string>
+    <string name="sound_on_keypress" msgid="6093592297198243644">"Sonido al pulsar tecla"</string>
+    <string name="hit_correction" msgid="4855351009261318389">"Corregir errores de escritura"</string>
+    <string name="hit_correction_summary" msgid="8761701873008070796">"Habilitar la introducción de corrección de errores"</string>
+    <string name="hit_correction_land" msgid="2567691684825205448">"Errores de introducción de datos en vista horizontal"</string>
+    <string name="hit_correction_land_summary" msgid="4076803842198368328">"Habilitar la introducción de corrección de errores"</string>
+    <string name="auto_correction" msgid="7911639788808958255">"Sugerencias de palabras"</string>
+    <string name="auto_correction_summary" msgid="6881047311475758267">"Corregir automáticamente la palabra anterior"</string>
+    <string name="prediction" msgid="466220283138359837">"Sugerencias de palabras"</string>
+    <string name="prediction_category" msgid="7027100625580696660">"Configuración de sugerencia de palabras"</string>
+    <string name="prediction_summary" msgid="459788228830873110">"Habilitar Autocompletar al escribir"</string>
+    <string name="auto_complete_dialog_title" msgid="2172048590607201920">"Autocompletar"</string>
+    <string name="prediction_landscape" msgid="4874601565593216183">"Aumentar el tamaño del campo de texto"</string>
+    <string name="prediction_landscape_summary" msgid="6736551095997839472">"Ocultar sugerencias de palabras en la vista horizontal"</string>
+    <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>
+    <!-- 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>
+    <string name="show_suggestions_summary" msgid="1989672863935759654">"Muestra las palabras sugeridas mientras se escribe."</string>
+    <string name="auto_complete" msgid="1103196318775486023">"Autocompletar"</string>
+    <string name="auto_complete_summary" msgid="6113149638718274624">"La barra espaciadora y los signos de puntuación insertan automáticamente la palabra resaltada."</string>
+  <string-array name="prediction_modes">
+    <item msgid="4870266572388153286">"Ninguno"</item>
+    <item msgid="1669461741568287396">"Básico"</item>
+    <item msgid="4894328801530136615">"Avanzado"</item>
+  </string-array>
+    <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Guardada"</string>
+    <string name="alternates_for_a" msgid="2566516493365324765">"á"</string>
+    <string name="alternates_for_e" msgid="3900510936875547555">"é"</string>
+    <string name="alternates_for_i" msgid="7097915268629342242">"ìíîï"</string>
+    <string name="alternates_for_o" msgid="6151402748321267776">"ó"</string>
+    <string name="alternates_for_u" msgid="5899096818189442934">"ùúûü"</string>
+    <string name="alternates_for_s" msgid="348762530927662188">"§ß"</string>
+    <string name="alternates_for_n" msgid="6257322556221886400">"ñ"</string>
+    <string name="alternates_for_c" msgid="151699780720639892">"ç"</string>
+    <string name="alternates_for_y" msgid="1722776806607271199">"ýÿ"</string>
+    <string name="tip_long_press" msgid="6101270866284343344">"Mantén pulsada una tecla para ver los caracteres acentuados (ø, ö, etc.)."</string>
+    <string name="tip_dismiss" msgid="7585579046862204381">"Pulsa la tecla \"Atrás\" ↶ para cerrar el teclado en cualquier momento."</string>
+    <string name="tip_access_symbols" msgid="6344098517525531652">"Acceso a números y símbolos"</string>
+    <string name="tip_add_to_dictionary" msgid="1487293888469227817">"Mantén pulsada la palabra situada más a la izquierda para añadirla al diccionario."</string>
+    <string name="touch_to_continue" msgid="7869803257948414531">"Toca esta sugerencia para continuar »"</string>
+    <string name="touch_to_finish" msgid="7990196086480585789">"Toca aquí para cerrar la sugerencia y comenzar a escribir."</string>
+    <string name="tip_to_open_keyboard" msgid="6821200275486950452"><b>"El teclado se abre cada vez que tocas un campo de texto"</b>"."</string>
+    <string name="tip_to_view_accents" msgid="5433158573693308501"><b>"Mantén pulsada una tecla para ver los caracteres acentuados "\n"(ø, ö, ô, ó, etc.)"</b></string>
+    <string name="tip_to_open_symbols" msgid="7345139325622444880"><b>"Toca esta tecla para cambiar al teclado de números y símbolos"</b>"."</string>
+    <string name="tip_to_close_symbols" msgid="5227724217206927185"><b>"Toca esta tecla de nuevo para volver a las letras"</b></string>
+    <string name="tip_to_launch_settings" msgid="8402961128983196128"><b>"Mantén pulsada esta tecla para cambiar la configuración del teclado como, por ejemplo, la opción de autocompletar"</b>"."</string>
+    <string name="tip_to_start_typing" msgid="7213843601369174313"><b>"¡Pruébalo!"</b></string>
+    <string name="label_go_key" msgid="1635148082137219148">"Ir"</string>
+    <string name="label_next_key" msgid="362972844525672568">"Sig."</string>
+    <string name="label_done_key" msgid="2441578748772529288">"Hecho"</string>
+    <string name="label_send_key" msgid="2815056534433717444">"Enviar"</string>
+    <string name="label_symbol_key" msgid="6175820506864489453">"?123"</string>
+    <string name="label_phone_key" msgid="4275497665515080551">"123"</string>
+    <string name="label_alpha_key" msgid="8864943487292437456">"ABC"</string>
+    <string name="label_alt_key" msgid="2846315350346694811">"ALT"</string>
+    <string name="voice_warning_title" msgid="4419354150908395008">"Introducción de voz"</string>
+    <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"Actualmente la introducción de voz no está disponible en tu idioma, pero se puede utilizar en inglés."</string>
+    <string name="voice_warning_may_not_understand" msgid="4611518823070986445">"La introducción de voz es una función en fase experimental que utiliza la tecnología de reconocimiento de voz en red de Google."</string>
+    <string name="voice_warning_how_to_turn_off" msgid="5652369578498701761">"Para desactivar la función de introducción de voz, accede a la configuración del teclado."</string>
+    <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>
+    <!-- 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>
+    <string name="voice_audio_error" msgid="5072707727016414454">"Problema de audio"</string>
+    <string name="voice_server_error" msgid="7807129913977261644">"Error del servidor"</string>
+    <string name="voice_speech_timeout" msgid="8461817525075498795">"Ninguna conversación escuchada"</string>
+    <string name="voice_no_match" msgid="4285117547030179174">"No se ha encontrado ninguna coincidencia."</string>
+    <string name="voice_not_installed" msgid="5552450909753842415">"La búsqueda por voz no está instalada."</string>
+    <string name="voice_swipe_hint" msgid="6943546180310682021"><b>"Sugerencia:"</b>" muévete por el teclado para hablar."</string>
+    <string name="voice_punctuation_hint" msgid="1611389463237317754"><b>"Sugerencia:"</b>" la próxima vez, prueba a indicar signos de puntuación como, por ejemplo, \"punto\", \"coma\" o \"signo de interrogación\"."</string>
+    <string name="cancel" msgid="6830980399865683324">"Cancelar"</string>
+    <string name="ok" msgid="7898366843681727667">"Aceptar"</string>
+    <string name="voice_input" msgid="2466640768843347841">"Introducción de voz"</string>
+  <string-array name="voice_input_modes">
+    <item msgid="1349082139076086774">"En teclado principal"</item>
+    <item msgid="8529385602829095903">"En teclado de símbolos"</item>
+    <item msgid="7283103513488381103">"Desactivado"</item>
+  </string-array>
+  <string-array name="voice_input_modes_summary">
+    <item msgid="554248625705084903">"Micrófono en teclado principal"</item>
+    <item msgid="6907837061058876770">"Micrófono en teclado de símbolos"</item>
+    <item msgid="3664304608587798036">"La función de introducción de voz no está habilitada."</item>
+  </string-array>
+    <string name="auto_submit" msgid="9151008027068358518">"Enviar automáticamente después de la introducción de voz"</string>
+    <string name="auto_submit_summary" msgid="4961875269610384226">"Pulsar Intro automáticamente al buscar o al pasar al siguiente campo"</string>
+    <string name="open_the_keyboard" msgid="2215920976029260466"><font size="17"><b>"Abrir el teclado"\n</b></font><font size="3">\n</font>"Pulsa cualquier campo de texto."</string>
+    <string name="close_the_keyboard" msgid="6251022259044940103"><font size="17"><b>"Cerrar el teclado"\n</b></font><font size="3">\n</font>"Pulsa la tecla \"Atrás\"."</string>
+    <string name="touch_and_hold" msgid="6154166367273010534"><font size="17"><b>"Mantén pulsada una tecla para acceder a las opciones."\n</b></font><font size="3">\n</font>"Accede a los signos de puntuación y a los acentos."</string>
+    <string name="keyboard_settings" msgid="4585753477617374032"><font size="17"><b>"Configuración del teclado"\n</b></font><font size="3">\n</font>"Mantén pulsada la tecla "<b>"?123"</b>"."</string>
+    <string name="popular_domain_0" msgid="3745279225122472969">".com"</string>
+    <string name="popular_domain_1" msgid="1370572248164278467">".net"</string>
+    <string name="popular_domain_2" msgid="3036812463748402878">".org"</string>
+    <string name="popular_domain_3" msgid="8718639560809452028">".gov"</string>
+    <string name="popular_domain_4" msgid="35359437471311470">".edu"</string>
+    <string name="inputMethod" msgid="7854532062009028116">"Método de introducción de texto"</string>
+    <string name="language_selection_title" msgid="1651299598555326750">"Idiomas de entrada"</string>
+    <string name="language_selection_summary" msgid="187110938289512256">"Deslizar el dedo por la barra espaciadora para cambiar el idioma"</string>
+    <string name="hint_add_to_dictionary" msgid="8058519710062071085">"← Volver a tocar para guardar"</string>
+</resources>
diff --git a/java/res/values-fr-rCA/strings.xml b/java/res/values-fr-rCA/strings.xml
new file mode 100644
index 0000000..b56463e
--- /dev/null
+++ b/java/res/values-fr-rCA/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="7252517407088836577">"Clavier Android"</string>
+</resources>
diff --git a/java/res/values-fr/bools.xml b/java/res/values-fr/bools.xml
new file mode 100644
index 0000000..897f4b3
--- /dev/null
+++ b/java/res/values-fr/bools.xml
@@ -0,0 +1,22 @@
+<?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>
+	<bool name="im_is_default">true</bool>
+</resources>
diff --git a/java/res/values-fr/donottranslate.xml b/java/res/values-fr/donottranslate.xml
new file mode 100644
index 0000000..b79df7b
--- /dev/null
+++ b/java/res/values-fr/donottranslate.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Symbols that are commonly considered word separators in this language -->
+    <string name="word_separators">.\u0009\u0020,;:!?\'\n()[]*&amp;@{}/&lt;&gt;_+=|\u0022</string>
+    <!-- Symbols that are sentence separators, for purposes of making it hug the last sentence. -->
+    <string name="sentence_separators">.,</string>
+</resources>
diff --git a/java/res/values-fr/strings.xml b/java/res/values-fr/strings.xml
new file mode 100644
index 0000000..0859414
--- /dev/null
+++ b/java/res/values-fr/strings.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="7252517407088836577">"Clavier Android"</string>
+    <string name="english_ime_settings" msgid="6661589557206947774">"Paramètres du clavier Android"</string>
+    <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrer à chaque touche"</string>
+    <string name="sound_on_keypress" msgid="6093592297198243644">"Son à chaque touche"</string>
+    <string name="hit_correction" msgid="4855351009261318389">"Corriger les fautes de frappe"</string>
+    <string name="hit_correction_summary" msgid="8761701873008070796">"Activer la correction des erreurs de saisie"</string>
+    <string name="hit_correction_land" msgid="2567691684825205448">"Erreurs de saisie en mode paysage"</string>
+    <string name="hit_correction_land_summary" msgid="4076803842198368328">"Activer la correction des erreurs de saisie"</string>
+    <string name="auto_correction" msgid="7911639788808958255">"Saisie prédictive"</string>
+    <string name="auto_correction_summary" msgid="6881047311475758267">"Corriger automatiquement le mot précédent"</string>
+    <string name="prediction" msgid="466220283138359837">"Saisie prédictive"</string>
+    <string name="prediction_category" msgid="7027100625580696660">"Paramètres de la saisie prédictive"</string>
+    <string name="prediction_summary" msgid="459788228830873110">"Activer la saisie semi-automatique"</string>
+    <string name="auto_complete_dialog_title" msgid="2172048590607201920">"Saisie semi-automatique"</string>
+    <string name="prediction_landscape" msgid="4874601565593216183">"Agrandir le champ de texte"</string>
+    <string name="prediction_landscape_summary" msgid="6736551095997839472">"Masquer la saisie prédictive en mode paysage"</string>
+    <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>
+    <!-- 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>
+    <string name="show_suggestions_summary" msgid="1989672863935759654">"Afficher les suggestions de terme lors de la saisie"</string>
+    <string name="auto_complete" msgid="1103196318775486023">"Saisie semi-automatique"</string>
+    <string name="auto_complete_summary" msgid="6113149638718274624">"Insérer auto. le terme surligné avec barre espace/ponctuation"</string>
+  <string-array name="prediction_modes">
+    <item msgid="4870266572388153286">"Aucun"</item>
+    <item msgid="1669461741568287396">"Simple"</item>
+    <item msgid="4894328801530136615">"Avancé"</item>
+  </string-array>
+    <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : enregistré"</string>
+    <string name="alternates_for_a" msgid="2566516493365324765">"àáâãäåæ"</string>
+    <string name="alternates_for_e" msgid="3900510936875547555">"èéêë"</string>
+    <string name="alternates_for_i" msgid="7097915268629342242">"ìíîï"</string>
+    <string name="alternates_for_o" msgid="6151402748321267776">"òóôõöœø"</string>
+    <string name="alternates_for_u" msgid="5899096818189442934">"ùúûü"</string>
+    <string name="alternates_for_s" msgid="348762530927662188">"§ß"</string>
+    <string name="alternates_for_n" msgid="6257322556221886400">"ñ"</string>
+    <string name="alternates_for_c" msgid="151699780720639892">"ç"</string>
+    <string name="alternates_for_y" msgid="1722776806607271199">"ýÿ"</string>
+    <string name="tip_long_press" msgid="6101270866284343344">"Maintenir une touche enfoncée pour afficher les accents (à, é, etc.)"</string>
+    <string name="tip_dismiss" msgid="7585579046862204381">"Appuyez sur la touche Retour ↶ pour fermer le clavier à tout moment."</string>
+    <string name="tip_access_symbols" msgid="6344098517525531652">"Accéder aux chiffres et symboles"</string>
+    <string name="tip_add_to_dictionary" msgid="1487293888469227817">"Appuyer et maintenir le doigt sur le mot le plus à gauche pour l\'ajouter au dictionnaire"</string>
+    <string name="touch_to_continue" msgid="7869803257948414531">"Touchez ce conseil pour continuer »"</string>
+    <string name="touch_to_finish" msgid="7990196086480585789">"Touchez ici pour fermer ce conseil et commencer à saisir votre texte."</string>
+    <string name="tip_to_open_keyboard" msgid="6821200275486950452"><b>"Le clavier s\'affiche à chaque fois que vous touchez une zone de texte."</b></string>
+    <string name="tip_to_view_accents" msgid="5433158573693308501"><b>"Maintenez une touche enfoncée pour afficher les accents"\n"(ø, ö, ô, ó, etc.)"</b></string>
+    <string name="tip_to_open_symbols" msgid="7345139325622444880"><b>"Appuyez sur cette touche pour basculer vers les chiffres et les symboles."</b></string>
+    <string name="tip_to_close_symbols" msgid="5227724217206927185"><b>"Retourner au clavier en appuyant de nouveau sur cette touche"</b></string>
+    <string name="tip_to_launch_settings" msgid="8402961128983196128"><b>"Maintenez cette touche enfoncée afin de modifier les paramètres du clavier, tels que la saisie semi-automatique."</b></string>
+    <string name="tip_to_start_typing" msgid="7213843601369174313"><b>"Essayez !"</b></string>
+    <string name="label_go_key" msgid="1635148082137219148">"OK"</string>
+    <string name="label_next_key" msgid="362972844525672568">"Suivant"</string>
+    <string name="label_done_key" msgid="2441578748772529288">"OK"</string>
+    <string name="label_send_key" msgid="2815056534433717444">"Envoyer"</string>
+    <string name="label_symbol_key" msgid="6175820506864489453">"?123"</string>
+    <string name="label_phone_key" msgid="4275497665515080551">"123"</string>
+    <string name="label_alpha_key" msgid="8864943487292437456">"ABC"</string>
+    <string name="label_alt_key" msgid="2846315350346694811">"ALT"</string>
+    <string name="voice_warning_title" msgid="4419354150908395008">"Saisie vocale"</string>
+    <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"La saisie vocale n\'est pas encore prise en charge pour votre langue, mais elle fonctionne en anglais."</string>
+    <string name="voice_warning_may_not_understand" msgid="4611518823070986445">"La saisie vocale est une fonctionnalité expérimentale qui fait appel à la reconnaissance vocale en réseau de Google."</string>
+    <string name="voice_warning_how_to_turn_off" msgid="5652369578498701761">"Pour désactiver la saisie vocale, accédez aux paramètres du clavier."</string>
+    <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>
+    <!-- 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>
+    <string name="voice_audio_error" msgid="5072707727016414454">"Problème audio"</string>
+    <string name="voice_server_error" msgid="7807129913977261644">"Erreur serveur"</string>
+    <string name="voice_speech_timeout" msgid="8461817525075498795">"Aucune requête vocale détectée"</string>
+    <string name="voice_no_match" msgid="4285117547030179174">"Aucune correspondance n\'a été trouvée."</string>
+    <string name="voice_not_installed" msgid="5552450909753842415">"Recherche vocale non installée"</string>
+    <string name="voice_swipe_hint" msgid="6943546180310682021"><b>"Astuce :"</b>" Faites glisser votre doigt sur le clavier pour parler."</string>
+    <string name="voice_punctuation_hint" msgid="1611389463237317754"><b>"Astuce :"</b>" La prochaine fois, essayez de prononcer la ponctuation, en énonçant des termes tels que \"point\", \"virgule\" ou \"point d\'interrogation\"."</string>
+    <string name="cancel" msgid="6830980399865683324">"Annuler"</string>
+    <string name="ok" msgid="7898366843681727667">"OK"</string>
+    <string name="voice_input" msgid="2466640768843347841">"Saisie vocale"</string>
+  <string-array name="voice_input_modes">
+    <item msgid="1349082139076086774">"Sur le clavier principal"</item>
+    <item msgid="8529385602829095903">"Sur le clavier des symboles"</item>
+    <item msgid="7283103513488381103">"Désactivée"</item>
+  </string-array>
+  <string-array name="voice_input_modes_summary">
+    <item msgid="554248625705084903">"Micro sur le clavier principal"</item>
+    <item msgid="6907837061058876770">"Micro sur le clavier des symboles"</item>
+    <item msgid="3664304608587798036">"Saisie vocale désactivée"</item>
+  </string-array>
+    <string name="auto_submit" msgid="9151008027068358518">"Envoi automatique après la saisie vocale"</string>
+    <string name="auto_submit_summary" msgid="4961875269610384226">"Appuyez automatiquement sur Entrée pour effectuer une recherche ou accéder au champ suivant."</string>
+    <string name="open_the_keyboard" msgid="2215920976029260466"><font size="17"><b>"Ouvrir le clavier"\n</b></font><font size="3">\n</font>"Appuyez sur un champ de texte."</string>
+    <string name="close_the_keyboard" msgid="6251022259044940103"><font size="17"><b>"Fermer le clavier"\n</b></font><font size="3">\n</font>"Appuyez sur la touche Retour."</string>
+    <string name="touch_and_hold" msgid="6154166367273010534"><font size="17"><b>"Appuyer sur une touche de manière prolongée pour accéder aux options"\n</b></font><font size="3">\n</font>"Accédez aux signes de ponctuation et aux accents."</string>
+    <string name="keyboard_settings" msgid="4585753477617374032"><font size="17"><b>"Paramètres du clavier"\n</b></font><font size="3">\n</font>"Appuyez sur la touche "<b>"?123"</b>" de manière prolongée."</string>
+    <string name="popular_domain_0" msgid="3745279225122472969">".com"</string>
+    <string name="popular_domain_1" msgid="1370572248164278467">".net"</string>
+    <string name="popular_domain_2" msgid="3036812463748402878">".org"</string>
+    <string name="popular_domain_3" msgid="8718639560809452028">".gouv"</string>
+    <string name="popular_domain_4" msgid="35359437471311470">".edu"</string>
+    <string name="inputMethod" msgid="7854532062009028116">"Mode de saisie"</string>
+    <string name="language_selection_title" msgid="1651299598555326750">"Langues de saisie"</string>
+    <string name="language_selection_summary" msgid="187110938289512256">"Faites glisser votre doigt sur la barre d\'espacement pour changer la langue."</string>
+    <string name="hint_add_to_dictionary" msgid="8058519710062071085">"← Appuyer de nouveau pour enregistrer"</string>
+</resources>
diff --git a/java/res/values-it/bools.xml b/java/res/values-it/bools.xml
new file mode 100644
index 0000000..897f4b3
--- /dev/null
+++ b/java/res/values-it/bools.xml
@@ -0,0 +1,22 @@
+<?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>
+	<bool name="im_is_default">true</bool>
+</resources>
diff --git a/java/res/values-it/donottranslate.xml b/java/res/values-it/donottranslate.xml
new file mode 100644
index 0000000..3e3f3ef
--- /dev/null
+++ b/java/res/values-it/donottranslate.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Symbols that are commonly considered word separators in this language -->
+    <string name="word_separators">.\u0009\u0020,;:!?\'\n()[]*&amp;@{}/&lt;&gt;_+=|\u0022</string>
+</resources>
diff --git a/java/res/values-it/strings.xml b/java/res/values-it/strings.xml
new file mode 100644
index 0000000..1135dd0
--- /dev/null
+++ b/java/res/values-it/strings.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="7252517407088836577">"Tastiera Android"</string>
+    <string name="english_ime_settings" msgid="6661589557206947774">"Impostazioni tastiera Android"</string>
+    <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibra se premi un tasto"</string>
+    <string name="sound_on_keypress" msgid="6093592297198243644">"Suona se premi un tasto"</string>
+    <string name="hit_correction" msgid="4855351009261318389">"Correggi errori di digitazione"</string>
+    <string name="hit_correction_summary" msgid="8761701873008070796">"Attiva la correzione degli errori di inserimento"</string>
+    <string name="hit_correction_land" msgid="2567691684825205448">"Errori di inserimento in visualizzazione orizzontale"</string>
+    <string name="hit_correction_land_summary" msgid="4076803842198368328">"Attiva la correzione degli errori di inserimento"</string>
+    <string name="auto_correction" msgid="7911639788808958255">"Suggerimenti parola"</string>
+    <string name="auto_correction_summary" msgid="6881047311475758267">"Correggi automaticamente la parola precedente"</string>
+    <string name="prediction" msgid="466220283138359837">"Suggerimenti parola"</string>
+    <string name="prediction_category" msgid="7027100625580696660">"Impostazioni suggerimento parole"</string>
+    <string name="prediction_summary" msgid="459788228830873110">"Attiva il completamento automatico durante la digitazione"</string>
+    <string name="auto_complete_dialog_title" msgid="2172048590607201920">"Completamento automatico"</string>
+    <string name="prediction_landscape" msgid="4874601565593216183">"Aumenta dimensioni campo di testo"</string>
+    <string name="prediction_landscape_summary" msgid="6736551095997839472">"Nascondi i suggerimenti delle parole in visualizzazione orizzontale"</string>
+    <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>
+    <!-- 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>
+    <string name="show_suggestions_summary" msgid="1989672863935759654">"Visualizza le parole suggerite durante la digitazione"</string>
+    <string name="auto_complete" msgid="1103196318775486023">"Completamento autom."</string>
+    <string name="auto_complete_summary" msgid="6113149638718274624">"Barra spaziatrice e punteggiatura inseriscono la parola evidenziata"</string>
+  <string-array name="prediction_modes">
+    <item msgid="4870266572388153286">"Nessuna"</item>
+    <item msgid="1669461741568287396">"Base"</item>
+    <item msgid="4894328801530136615">"Avanzate"</item>
+  </string-array>
+    <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : parola salvata"</string>
+    <string name="alternates_for_a" msgid="2566516493365324765">"àá"</string>
+    <string name="alternates_for_e" msgid="3900510936875547555">"èé"</string>
+    <string name="alternates_for_i" msgid="7097915268629342242">"ìíîï"</string>
+    <string name="alternates_for_o" msgid="6151402748321267776">"òó"</string>
+    <string name="alternates_for_u" msgid="5899096818189442934">"ùúûü"</string>
+    <string name="alternates_for_s" msgid="348762530927662188">"§"</string>
+    <string name="alternates_for_n" msgid="6257322556221886400">"ñ"</string>
+    <string name="alternates_for_c" msgid="151699780720639892">"ç"</string>
+    <string name="alternates_for_y" msgid="1722776806607271199">"ýÿ"</string>
+    <string name="tip_long_press" msgid="6101270866284343344">"Tieni premuto un tasto per vedere le lettere con segni diacritici (ø, ö etc.)"</string>
+    <string name="tip_dismiss" msgid="7585579046862204381">"Premi il tasto Indietro ↶ per chiudere la tastiera"</string>
+    <string name="tip_access_symbols" msgid="6344098517525531652">"Accedi a numeri e simboli"</string>
+    <string name="tip_add_to_dictionary" msgid="1487293888469227817">"Tieni premuto sulla parola all\'estrema sinistra per aggiungerla al dizionario"</string>
+    <string name="touch_to_continue" msgid="7869803257948414531">"Tocca questo suggerimento per continuare »"</string>
+    <string name="touch_to_finish" msgid="7990196086480585789">"Tocca qui per chiudere questo suggerimento e iniziare a digitare."</string>
+    <string name="tip_to_open_keyboard" msgid="6821200275486950452"><b>"La tastiera si apre ogni volta che tocchi un campo di testo"</b></string>
+    <string name="tip_to_view_accents" msgid="5433158573693308501"><b>"Tocca e tieni premuto un tasto per visualizzare le lettere con segni diacritici"\n"(ø, ö, ô, ó e così via)"</b></string>
+    <string name="tip_to_open_symbols" msgid="7345139325622444880"><b>"Passa a numeri e simboli toccando questo tasto"</b></string>
+    <string name="tip_to_close_symbols" msgid="5227724217206927185"><b>"Torna all\'alfabeto toccando di nuovo questo tasto"</b></string>
+    <string name="tip_to_launch_settings" msgid="8402961128983196128"><b>"Tocca e tieni premuto questo tasto per modificare le impostazioni della tastiera, come il completamento automatico"</b></string>
+    <string name="tip_to_start_typing" msgid="7213843601369174313"><b>"Prova."</b></string>
+    <string name="label_go_key" msgid="1635148082137219148">"Vai"</string>
+    <string name="label_next_key" msgid="362972844525672568">"Avanti"</string>
+    <string name="label_done_key" msgid="2441578748772529288">"Fine"</string>
+    <string name="label_send_key" msgid="2815056534433717444">"Invia"</string>
+    <string name="label_symbol_key" msgid="6175820506864489453">"?123"</string>
+    <string name="label_phone_key" msgid="4275497665515080551">"123"</string>
+    <string name="label_alpha_key" msgid="8864943487292437456">"ABC"</string>
+    <string name="label_alt_key" msgid="2846315350346694811">"ALT"</string>
+    <string name="voice_warning_title" msgid="4419354150908395008">"Comandi vocali"</string>
+    <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"I comandi vocali non sono attualmente supportati per la tua lingua ma funzionano in inglese."</string>
+    <string name="voice_warning_may_not_understand" msgid="4611518823070986445">"I comandi vocali sono una funzione sperimentale che utilizza il riconoscimento vocale in rete di Google."</string>
+    <string name="voice_warning_how_to_turn_off" msgid="5652369578498701761">"Per disattivare i comandi vocali, vai alle impostazioni della tastiera."</string>
+    <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>
+    <!-- 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>
+    <string name="voice_audio_error" msgid="5072707727016414454">"Problema audio"</string>
+    <string name="voice_server_error" msgid="7807129913977261644">"Errore del server"</string>
+    <string name="voice_speech_timeout" msgid="8461817525075498795">"Nessuna frase vocale rilevata"</string>
+    <string name="voice_no_match" msgid="4285117547030179174">"Nessuna corrispondenza trovata"</string>
+    <string name="voice_not_installed" msgid="5552450909753842415">"Ricerca vocale non installata"</string>
+    <string name="voice_swipe_hint" msgid="6943546180310682021"><b>"Suggerimento."</b>" Fai scorrere il dito sulla tastiera per parlare"</string>
+    <string name="voice_punctuation_hint" msgid="1611389463237317754"><b>"Suggerimento."</b>" La prossima volta, prova a pronunciare termini relativi alla punteggiatura come \"punto\", \"virgola\" o \"punto di domanda\"."</string>
+    <string name="cancel" msgid="6830980399865683324">"Annulla"</string>
+    <string name="ok" msgid="7898366843681727667">"OK"</string>
+    <string name="voice_input" msgid="2466640768843347841">"Comandi vocali"</string>
+  <string-array name="voice_input_modes">
+    <item msgid="1349082139076086774">"Su tastiera principale"</item>
+    <item msgid="8529385602829095903">"Su tastiera simboli"</item>
+    <item msgid="7283103513488381103">"Non attivi"</item>
+  </string-array>
+  <string-array name="voice_input_modes_summary">
+    <item msgid="554248625705084903">"Microfono su tastiera principale"</item>
+    <item msgid="6907837061058876770">"Microfono su tastiera simboli"</item>
+    <item msgid="3664304608587798036">"Comandi vocali disabilitati"</item>
+  </string-array>
+    <string name="auto_submit" msgid="9151008027068358518">"Invia automaticamente dopo comando vocale"</string>
+    <string name="auto_submit_summary" msgid="4961875269610384226">"Premi automaticamente \"Invio\" durante una ricerca o un passaggio al campo successivo."</string>
+    <string name="open_the_keyboard" msgid="2215920976029260466"><font size="17"><b>"Apertura tastiera"\n</b></font><font size="3">\n</font>"Tocca qualsiasi campo di testo."</string>
+    <string name="close_the_keyboard" msgid="6251022259044940103"><font size="17"><b>"Chiusura tastiera"\n</b></font><font size="3">\n</font>"Premi il tasto Indietro."</string>
+    <string name="touch_and_hold" msgid="6154166367273010534"><font size="17"><b>"Tocca e tieni premuto un tasto per le opzioni"\n</b></font><font size="3">\n</font>"Accesso a punteggiatura e accenti."</string>
+    <string name="keyboard_settings" msgid="4585753477617374032"><font size="17"><b>"Impostazioni tastiera"\n</b></font><font size="3">\n</font>"Tocca e tieni premuto il tasto "<b>"?123"</b>"."</string>
+    <string name="popular_domain_0" msgid="3745279225122472969">".com"</string>
+    <string name="popular_domain_1" msgid="1370572248164278467">".net"</string>
+    <string name="popular_domain_2" msgid="3036812463748402878">".org"</string>
+    <string name="popular_domain_3" msgid="8718639560809452028">".gov"</string>
+    <string name="popular_domain_4" msgid="35359437471311470">".edu"</string>
+    <string name="inputMethod" msgid="7854532062009028116">"Metodo inserimento"</string>
+    <string name="language_selection_title" msgid="1651299598555326750">"Lingue comandi"</string>
+    <string name="language_selection_summary" msgid="187110938289512256">"Scorri il dito sulla barra spaziatrice per cambiare la lingua"</string>
+    <string name="hint_add_to_dictionary" msgid="8058519710062071085">"← Tocca di nuovo per salvare"</string>
+</resources>
diff --git a/java/res/values-ja/strings.xml b/java/res/values-ja/strings.xml
new file mode 100644
index 0000000..9097bd9
--- /dev/null
+++ b/java/res/values-ja/strings.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="7252517407088836577">"Androidキーボード"</string>
+    <string name="english_ime_settings" msgid="6661589557206947774">"Androidキーボードの設定"</string>
+    <string name="vibrate_on_keypress" msgid="5258079494276955460">"キー操作バイブ"</string>
+    <string name="sound_on_keypress" msgid="6093592297198243644">"キー操作音"</string>
+    <string name="hit_correction" msgid="4855351009261318389">"入力ミス補正"</string>
+    <string name="hit_correction_summary" msgid="8761701873008070796">"入力間違いを自動修正する"</string>
+    <string name="hit_correction_land" msgid="2567691684825205448">"横表示での入力修正"</string>
+    <string name="hit_correction_land_summary" msgid="4076803842198368328">"入力間違いを自動修正する"</string>
+    <string name="auto_correction" msgid="7911639788808958255">"入力候補表示"</string>
+    <string name="auto_correction_summary" msgid="6881047311475758267">"前の単語を自動修正する"</string>
+    <string name="prediction" msgid="466220283138359837">"入力候補表示"</string>
+    <string name="prediction_category" msgid="7027100625580696660">"入力候補の設定"</string>
+    <string name="prediction_summary" msgid="459788228830873110">"オートコンプリートを使用する"</string>
+    <string name="auto_complete_dialog_title" msgid="2172048590607201920">"オートコンプリート"</string>
+    <string name="prediction_landscape" msgid="4874601565593216183">"入力作業スペースを広げる"</string>
+    <string name="prediction_landscape_summary" msgid="6736551095997839472">"横表示では入力候補を表示しない"</string>
+    <string name="auto_cap" msgid="1719746674854628252">"自動大文字変換"</string>
+    <string name="auto_cap_summary" msgid="3260681697600786825">"英字入力で文頭文字を大文字にする"</string>
+    <string name="auto_punctuate" msgid="7276672334264521751">"句読点を自動入力"</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>
+    <string name="show_suggestions_summary" msgid="1989672863935759654">"入力時に入力候補を表示する"</string>
+    <string name="auto_complete" msgid="1103196318775486023">"オートコンプリート"</string>
+    <string name="auto_complete_summary" msgid="6113149638718274624">"反転表示されている変換候補をスペースまたは句読点キーで挿入する"</string>
+  <string-array name="prediction_modes">
+    <item msgid="4870266572388153286">"なし"</item>
+    <item msgid="1669461741568287396">"基本"</item>
+    <item msgid="4894328801530136615">"高度"</item>
+  </string-array>
+    <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>:保存しました"</string>
+    <string name="alternates_for_a" msgid="2566516493365324765">"àáâãäåæ"</string>
+    <string name="alternates_for_e" msgid="3900510936875547555">"èéêë"</string>
+    <string name="alternates_for_i" msgid="7097915268629342242">"ìíîï"</string>
+    <string name="alternates_for_o" msgid="6151402748321267776">"òóôõöœø"</string>
+    <string name="alternates_for_u" msgid="5899096818189442934">"ùúûü"</string>
+    <string name="alternates_for_s" msgid="348762530927662188">"§ß"</string>
+    <string name="alternates_for_n" msgid="6257322556221886400">"ñ"</string>
+    <string name="alternates_for_c" msgid="151699780720639892">"ç"</string>
+    <string name="alternates_for_y" msgid="1722776806607271199">"ýÿ"</string>
+    <string name="tip_long_press" msgid="6101270866284343344">"キー長押しでアクセント文字を表示(ø、öなど)"</string>
+    <string name="tip_dismiss" msgid="7585579046862204381">"戻るキーでキーボードを閉じます"</string>
+    <string name="tip_access_symbols" msgid="6344098517525531652">"数字と記号"</string>
+    <string name="tip_add_to_dictionary" msgid="1487293888469227817">"一番左の単語を長押しすると辞書に追加されます"</string>
+    <string name="touch_to_continue" msgid="7869803257948414531">"続けるにはここをタッチ"</string>
+    <string name="touch_to_finish" msgid="7990196086480585789">"タッチしてこのヒントを終了し、入力を開始してください。"</string>
+    <string name="tip_to_open_keyboard" msgid="6821200275486950452"><b>"テキストフィールドを選択するとキーボードが表示されます"</b></string>
+    <string name="tip_to_view_accents" msgid="5433158573693308501"><b>"キーを長押しするとアクセント付き文字"\n"(ø、ö、ô、óなど)が表示されます"</b></string>
+    <string name="tip_to_open_symbols" msgid="7345139325622444880"><b>"このキーを押すと、数字/記号入力に切り替わります"</b></string>
+    <string name="tip_to_close_symbols" msgid="5227724217206927185"><b>"このキーを押すと、文字入力に再度切り替わります"</b></string>
+    <string name="tip_to_launch_settings" msgid="8402961128983196128"><b>"オートコンプリートなどのキーボードの設定を変更するには、このキーを長押しします"</b></string>
+    <string name="tip_to_start_typing" msgid="7213843601369174313"><b>"試してみてください。"</b></string>
+    <string name="label_go_key" msgid="1635148082137219148">"実行"</string>
+    <string name="label_next_key" msgid="362972844525672568">"次へ"</string>
+    <string name="label_done_key" msgid="2441578748772529288">"完了"</string>
+    <string name="label_send_key" msgid="2815056534433717444">"送信"</string>
+    <string name="label_symbol_key" msgid="6175820506864489453">"?123"</string>
+    <string name="label_phone_key" msgid="4275497665515080551">"123"</string>
+    <string name="label_alpha_key" msgid="8864943487292437456">"ABC"</string>
+    <string name="label_alt_key" msgid="2846315350346694811">"ALT"</string>
+    <string name="voice_warning_title" msgid="4419354150908395008">"音声入力"</string>
+    <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"音声入力は現在英語には対応していますが、日本語には対応していません。"</string>
+    <string name="voice_warning_may_not_understand" msgid="4611518823070986445">"音声入力はGoogleのネットワーク音声認識技術を利用した試験段階の機能です。"</string>
+    <string name="voice_warning_how_to_turn_off" msgid="5652369578498701761">"音声入力をOFFにするには、キーボードの設定を開きます。"</string>
+    <string name="voice_hint_dialog_message" msgid="6892342981545727994">"音声入力するには、マイクボタンを押すか画面キーボードをスワイプしてください。"</string>
+    <string name="voice_listening" msgid="467518160751321844">"お話しください"</string>
+    <string name="voice_working" msgid="6666937792815731889">"処理中"</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>
+    <string name="voice_audio_error" msgid="5072707727016414454">"オーディオエラー"</string>
+    <string name="voice_server_error" msgid="7807129913977261644">"サーバーエラー"</string>
+    <string name="voice_speech_timeout" msgid="8461817525075498795">"音声が聞き取れません"</string>
+    <string name="voice_no_match" msgid="4285117547030179174">"該当なし"</string>
+    <string name="voice_not_installed" msgid="5552450909753842415">"Voice Searchはインストールされていません"</string>
+    <string name="voice_swipe_hint" msgid="6943546180310682021"><b>"ヒント:"</b>" 音声入力するにはキーボードをスワイプします"</string>
+    <string name="voice_punctuation_hint" msgid="1611389463237317754"><b>"ヒント:"</b>" 次回は句読点として「period」、「comma」、「question mark」などの音声入力を試してみてください。"</string>
+    <string name="cancel" msgid="6830980399865683324">"キャンセル"</string>
+    <string name="ok" msgid="7898366843681727667">"OK"</string>
+    <string name="voice_input" msgid="2466640768843347841">"音声入力"</string>
+  <string-array name="voice_input_modes">
+    <item msgid="1349082139076086774">"メインキーボード上"</item>
+    <item msgid="8529385602829095903">"記号キーボード上"</item>
+    <item msgid="7283103513488381103">"OFF"</item>
+  </string-array>
+  <string-array name="voice_input_modes_summary">
+    <item msgid="554248625705084903">"メインキーボードのマイク"</item>
+    <item msgid="6907837061058876770">"記号キーボードのマイク"</item>
+    <item msgid="3664304608587798036">"音声入力は無効です"</item>
+  </string-array>
+    <string name="auto_submit" msgid="9151008027068358518">"入力後に自動送信する"</string>
+    <string name="auto_submit_summary" msgid="4961875269610384226">"検索または次のフィールドに進む際、Enterキーが自動的に押されます。"</string>
+    <string name="open_the_keyboard" msgid="2215920976029260466"><font size="17"><b>"キーボードを開く"\n</b></font><font size="3">\n</font>"テキストフィールドをタップします。"</string>
+    <string name="close_the_keyboard" msgid="6251022259044940103"><font size="17"><b>"キーボードを閉じる"\n</b></font><font size="3">\n</font>"[戻る]キーを押します。"</string>
+    <string name="touch_and_hold" msgid="6154166367273010534"><font size="17"><b>"キーを長押しして選択する"\n</b></font><font size="3">\n</font>"句読点キーとアクセント文字を表示します。"</string>
+    <string name="keyboard_settings" msgid="4585753477617374032"><font size="17"><b>"キーボードの設定"\n</b></font><font size="3">\n</font>"["<b>"?123"</b>"]キーを長押しします。"</string>
+    <string name="popular_domain_0" msgid="3745279225122472969">".com"</string>
+    <string name="popular_domain_1" msgid="1370572248164278467">".net"</string>
+    <string name="popular_domain_2" msgid="3036812463748402878">".org"</string>
+    <string name="popular_domain_3" msgid="8718639560809452028">".gov"</string>
+    <string name="popular_domain_4" msgid="35359437471311470">".edu"</string>
+    <string name="inputMethod" msgid="7854532062009028116">"入力方法"</string>
+    <string name="language_selection_title" msgid="1651299598555326750">"入力言語"</string>
+    <string name="language_selection_summary" msgid="187110938289512256">"スペースバーで指をスライドさせて言語を変更する"</string>
+    <string name="hint_add_to_dictionary" msgid="8058519710062071085">"←保存するにはもう一度タップ"</string>
+</resources>
diff --git a/java/res/values-ko/strings.xml b/java/res/values-ko/strings.xml
new file mode 100644
index 0000000..15000e6
--- /dev/null
+++ b/java/res/values-ko/strings.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="7252517407088836577">"Android 키보드"</string>
+    <string name="english_ime_settings" msgid="6661589557206947774">"Android 키보드 설정"</string>
+    <string name="vibrate_on_keypress" msgid="5258079494276955460">"키를 누를 때 진동 발생"</string>
+    <string name="sound_on_keypress" msgid="6093592297198243644">"버튼을 누를 때 소리 발생"</string>
+    <string name="hit_correction" msgid="4855351009261318389">"입력 오류 수정"</string>
+    <string name="hit_correction_summary" msgid="8761701873008070796">"입력 오류 수정 사용"</string>
+    <string name="hit_correction_land" msgid="2567691684825205448">"가로 입력 오류"</string>
+    <string name="hit_correction_land_summary" msgid="4076803842198368328">"입력 오류 수정 사용"</string>
+    <string name="auto_correction" msgid="7911639788808958255">"단어 추천"</string>
+    <string name="auto_correction_summary" msgid="6881047311475758267">"이전 단어를 자동으로 수정"</string>
+    <string name="prediction" msgid="466220283138359837">"단어 추천"</string>
+    <string name="prediction_category" msgid="7027100625580696660">"단어 추천 설정"</string>
+    <string name="prediction_summary" msgid="459788228830873110">"입력할 때 자동 완성 사용"</string>
+    <string name="auto_complete_dialog_title" msgid="2172048590607201920">"자동 완성"</string>
+    <string name="prediction_landscape" msgid="4874601565593216183">"입력란 크기 늘리기"</string>
+    <string name="prediction_landscape_summary" msgid="6736551095997839472">"가로 보기에서 단어 추천 숨기기"</string>
+    <string name="auto_cap" msgid="1719746674854628252">"자동 대문자화"</string>
+    <string name="auto_cap_summary" msgid="3260681697600786825">"문장의 첫 글자를 대문자로 표시"</string>
+    <string name="auto_punctuate" msgid="7276672334264521751">"자동 구두점 입력"</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>
+    <string name="show_suggestions_summary" msgid="1989672863935759654">"글자를 입력하는 동안 추천 단어를 표시"</string>
+    <string name="auto_complete" msgid="1103196318775486023">"자동 완성"</string>
+    <string name="auto_complete_summary" msgid="6113149638718274624">"스페이스바와 문장부호 키로 강조 표시된 단어를 자동 삽입"</string>
+  <string-array name="prediction_modes">
+    <item msgid="4870266572388153286">"없음"</item>
+    <item msgid="1669461741568287396">"기본"</item>
+    <item msgid="4894328801530136615">"고급"</item>
+  </string-array>
+    <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : 저장됨"</string>
+    <string name="alternates_for_a" msgid="2566516493365324765">"àáâãäåæ"</string>
+    <string name="alternates_for_e" msgid="3900510936875547555">"èéêë"</string>
+    <string name="alternates_for_i" msgid="7097915268629342242">"ìíîï"</string>
+    <string name="alternates_for_o" msgid="6151402748321267776">"òóôõöœø"</string>
+    <string name="alternates_for_u" msgid="5899096818189442934">"ùúûü"</string>
+    <string name="alternates_for_s" msgid="348762530927662188">"§ß"</string>
+    <string name="alternates_for_n" msgid="6257322556221886400">"ñ"</string>
+    <string name="alternates_for_c" msgid="151699780720639892">"ç"</string>
+    <string name="alternates_for_y" msgid="1722776806607271199">"ýÿ"</string>
+    <string name="tip_long_press" msgid="6101270866284343344">"키를 길게 누르면 악센트(ø, ö 등)가 표시됩니다."</string>
+    <string name="tip_dismiss" msgid="7585579046862204381">"키보드를 닫으려면 언제든지 뒤로 키(↶)를 누르세요."</string>
+    <string name="tip_access_symbols" msgid="6344098517525531652">"숫자 및 기호 액세스"</string>
+    <string name="tip_add_to_dictionary" msgid="1487293888469227817">"맨 왼쪽에 있는 단어를 길게 누르면 사전에 추가됩니다."</string>
+    <string name="touch_to_continue" msgid="7869803257948414531">"계속하려면 힌트를 터치하세요. »"</string>
+    <string name="touch_to_finish" msgid="7990196086480585789">"힌트를 닫고 입력을 시작하려면 여기를 터치하세요."</string>
+    <string name="tip_to_open_keyboard" msgid="6821200275486950452"><b>"입력란을 터치하면 키보드가 열립니다."</b></string>
+    <string name="tip_to_view_accents" msgid="5433158573693308501"><b>"키를 길게 누르면 악센트"\n"(ø, ö, ô, ó 등)를 볼 수 있습니다."</b></string>
+    <string name="tip_to_open_symbols" msgid="7345139325622444880"><b>"이 키를 터치하면 숫자 및 기호 자판으로 변경됩니다."</b></string>
+    <string name="tip_to_close_symbols" msgid="5227724217206927185"><b>"이 키를 다시 터치하면 키보드가 표시됩니다."</b></string>
+    <string name="tip_to_launch_settings" msgid="8402961128983196128"><b>"자동 완성과 같은 키보드 설정을 변경하려면 이 키를 길게 누르세요."</b></string>
+    <string name="tip_to_start_typing" msgid="7213843601369174313"><b>"이제 직접 사용해 보세요!"</b></string>
+    <string name="label_go_key" msgid="1635148082137219148">"이동"</string>
+    <string name="label_next_key" msgid="362972844525672568">"다음"</string>
+    <string name="label_done_key" msgid="2441578748772529288">"완료"</string>
+    <string name="label_send_key" msgid="2815056534433717444">"전송"</string>
+    <string name="label_symbol_key" msgid="6175820506864489453">"?123"</string>
+    <string name="label_phone_key" msgid="4275497665515080551">"123"</string>
+    <string name="label_alpha_key" msgid="8864943487292437456">"ABC"</string>
+    <string name="label_alt_key" msgid="2846315350346694811">"ALT"</string>
+    <string name="voice_warning_title" msgid="4419354150908395008">"음성 입력"</string>
+    <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"음성 입력은 현재 자국어로 지원되지 않으며 영어로 작동됩니다."</string>
+    <string name="voice_warning_may_not_understand" msgid="4611518823070986445">"음성 입력은 Google의 네트워크화된 음성 인식을 사용하는 실험적 기능입니다."</string>
+    <string name="voice_warning_how_to_turn_off" msgid="5652369578498701761">"음성 입력을 사용하지 않으려면 키보드 설정으로 이동하세요."</string>
+    <string name="voice_hint_dialog_message" msgid="6892342981545727994">"음성 입력을 사용하려면 마이크 버튼을 누르거나 터치 키보드 위로 손가락을 미끄러지듯 움직이세요."</string>
+    <string name="voice_listening" msgid="467518160751321844">"지금 시작하세요."</string>
+    <string name="voice_working" msgid="6666937792815731889">"인식 중"</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>
+    <string name="voice_audio_error" msgid="5072707727016414454">"오디오 문제"</string>
+    <string name="voice_server_error" msgid="7807129913977261644">"서버 오류"</string>
+    <string name="voice_speech_timeout" msgid="8461817525075498795">"음성이 인식되지 않았습니다."</string>
+    <string name="voice_no_match" msgid="4285117547030179174">"일치하는 항목 없음"</string>
+    <string name="voice_not_installed" msgid="5552450909753842415">"음성 검색이 설치되지 않았습니다."</string>
+    <string name="voice_swipe_hint" msgid="6943546180310682021"><b>"도움말:"</b>" 키보드를 스와이프하고 말하세요."</string>
+    <string name="voice_punctuation_hint" msgid="1611389463237317754"><b>"도움말:"</b>" 다음 번에는 \'마침표\', \'쉼표\', \'물음표\'와 같은 구두점을 말해 보세요."</string>
+    <string name="cancel" msgid="6830980399865683324">"취소"</string>
+    <string name="ok" msgid="7898366843681727667">"확인"</string>
+    <string name="voice_input" msgid="2466640768843347841">"음성 입력"</string>
+  <string-array name="voice_input_modes">
+    <item msgid="1349082139076086774">"기본 키보드"</item>
+    <item msgid="8529385602829095903">"기호 키보드"</item>
+    <item msgid="7283103513488381103">"사용 안함"</item>
+  </string-array>
+  <string-array name="voice_input_modes_summary">
+    <item msgid="554248625705084903">"기본 키보드의 마이크"</item>
+    <item msgid="6907837061058876770">"기호 키보드의 마이크"</item>
+    <item msgid="3664304608587798036">"음성 입력이 사용 중지됨"</item>
+  </string-array>
+    <string name="auto_submit" msgid="9151008027068358518">"음성을 입력한 다음 자동 제출"</string>
+    <string name="auto_submit_summary" msgid="4961875269610384226">"검색하거나 다음 입력란으로 이동할 때 자동으로 Enter 키를 누릅니다."</string>
+    <string name="open_the_keyboard" msgid="2215920976029260466"><font size="17"><b>"키보드 열기"\n</b></font><font size="3">\n</font>"아무 텍스트 입력란이나 터치하세요."</string>
+    <string name="close_the_keyboard" msgid="6251022259044940103"><font size="17"><b>"키보드 닫기"\n</b></font><font size="3">\n</font>"\'뒤로\' 키를 누르세요."</string>
+    <string name="touch_and_hold" msgid="6154166367273010534"><font size="17"><b>"키를 길게 터치하여 옵션 보기"\n</b></font><font size="3">\n</font>"문장 부호 및 악센트 기호에 액세스하세요."</string>
+    <string name="keyboard_settings" msgid="4585753477617374032"><font size="17"><b>"키보드 설정"\n</b></font><font size="3">\n</font><b>"?123"</b>" 키를 길게 터치하세요."</string>
+    <string name="popular_domain_0" msgid="3745279225122472969">".com"</string>
+    <string name="popular_domain_1" msgid="1370572248164278467">".net"</string>
+    <string name="popular_domain_2" msgid="3036812463748402878">".org"</string>
+    <string name="popular_domain_3" msgid="8718639560809452028">".gov"</string>
+    <string name="popular_domain_4" msgid="35359437471311470">".edu"</string>
+    <string name="inputMethod" msgid="7854532062009028116">"입력 방법"</string>
+    <string name="language_selection_title" msgid="1651299598555326750">"입력 언어"</string>
+    <string name="language_selection_summary" msgid="187110938289512256">"손가락을 스페이스바에서 미끄러지듯 움직여 언어 변경"</string>
+    <string name="hint_add_to_dictionary" msgid="8058519710062071085">"← 저장하려면 다시 누르세요."</string>
+</resources>
diff --git a/java/res/values-land/dimens.xml b/java/res/values-land/dimens.xml
new file mode 100644
index 0000000..4395155
--- /dev/null
+++ b/java/res/values-land/dimens.xml
@@ -0,0 +1,25 @@
+<?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>
+    <dimen name="key_height">47dip</dimen>
+    <dimen name="candidate_strip_height">38dip</dimen>
+    <dimen name="spacebar_vertical_correction">2dip</dimen>
+</resources>
\ No newline at end of file
diff --git a/java/res/values-nb/strings.xml b/java/res/values-nb/strings.xml
new file mode 100644
index 0000000..a7ddc75
--- /dev/null
+++ b/java/res/values-nb/strings.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="7252517407088836577">"Skjermtastatur"</string>
+    <string name="english_ime_settings" msgid="6661589557206947774">"Innstillinger for skjermtastatur"</string>
+    <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrer ved tastetrykk"</string>
+    <string name="sound_on_keypress" msgid="6093592297198243644">"Lyd ved tastetrykk"</string>
+    <string name="hit_correction" msgid="4855351009261318389">"Rett opp skrivefeil"</string>
+    <string name="hit_correction_summary" msgid="8761701873008070796">"Slå på retting av skrivefeil"</string>
+    <string name="hit_correction_land" msgid="2567691684825205448">"Rett opp skrivefeil i breddeformat"</string>
+    <string name="hit_correction_land_summary" msgid="4076803842198368328">"Slå på retting av skrivefeil"</string>
+    <string name="auto_correction" msgid="7911639788808958255">"Autokorrektur"</string>
+    <string name="auto_correction_summary" msgid="6881047311475758267">"Autokorriger forrige ord"</string>
+    <string name="prediction" msgid="466220283138359837">"Ordforslag"</string>
+    <string name="prediction_category" msgid="7027100625580696660">"Innstillinger for ordforslag"</string>
+    <string name="prediction_summary" msgid="459788228830873110">"Skru på autofullføring under skriving"</string>
+    <string name="auto_complete_dialog_title" msgid="2172048590607201920">"Autofullfør"</string>
+    <string name="prediction_landscape" msgid="4874601565593216183">"Større tekstfelt"</string>
+    <string name="prediction_landscape_summary" msgid="6736551095997839472">"Skjul ordforslag i breddeformat"</string>
+    <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>
+    <!-- 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>
+    <string name="show_suggestions_summary" msgid="1989672863935759654">"Vis foreslåtte ord under skriving"</string>
+    <string name="auto_complete" msgid="1103196318775486023">"Autokomplettering"</string>
+    <string name="auto_complete_summary" msgid="6113149638718274624">"Mellomrom og punktum setter automatisk inn valgt ord"</string>
+  <string-array name="prediction_modes">
+    <item msgid="4870266572388153286">"Ingen"</item>
+    <item msgid="1669461741568287396">"Grunnleggende"</item>
+    <item msgid="4894328801530136615">"Avansert"</item>
+  </string-array>
+    <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: Lagret"</string>
+    <string name="alternates_for_a" msgid="2566516493365324765">"åæáàâãä"</string>
+    <string name="alternates_for_e" msgid="3900510936875547555">"éèêë"</string>
+    <string name="alternates_for_i" msgid="7097915268629342242">"íìîï"</string>
+    <string name="alternates_for_o" msgid="6151402748321267776">"ôóòöõœø"</string>
+    <string name="alternates_for_u" msgid="5899096818189442934">"üùúû"</string>
+    <string name="alternates_for_s" msgid="348762530927662188">"ß§"</string>
+    <string name="alternates_for_n" msgid="6257322556221886400">"ñ"</string>
+    <string name="alternates_for_c" msgid="151699780720639892">"ç"</string>
+    <string name="alternates_for_y" msgid="1722776806607271199">"ýÿ"</string>
+    <string name="tip_long_press" msgid="6101270866284343344">"Hold en tast nede for å se aksenterte tegn (ø, ö, osv.)"</string>
+    <string name="tip_dismiss" msgid="7585579046862204381">"Trykk tilbakeknappen, ↶, for å lukke tastaturet"</string>
+    <string name="tip_access_symbols" msgid="6344098517525531652">"Få tilgang til tall og symboler"</string>
+    <string name="tip_add_to_dictionary" msgid="1487293888469227817">"Trykk lenge på ordet lengst til venstre for å legge det til i ordlisten"</string>
+    <string name="touch_to_continue" msgid="7869803257948414531">"Trykk på dette hintet for å forsette »"</string>
+    <string name="touch_to_finish" msgid="7990196086480585789">"Trykk her for å lukke dette hintet og begynne å skrive!"</string>
+    <string name="tip_to_open_keyboard" msgid="6821200275486950452"><b>"Tastaturet åpnes når du tar på et tekstfelt"</b></string>
+    <string name="tip_to_view_accents" msgid="5433158573693308501"><b>"Trykk på og hold nede en tast for å se aksenter"\n"(ø, ö, ô, ó, osv.)"</b></string>
+    <string name="tip_to_open_symbols" msgid="7345139325622444880"><b>"Bytt til tall og symboler ved å trykke på denne tasten"</b></string>
+    <string name="tip_to_close_symbols" msgid="5227724217206927185"><b>"Gå tilbake til bokstaver igjen ved å trykke på denne tasten"</b></string>
+    <string name="tip_to_launch_settings" msgid="8402961128983196128"><b>"Trykk på og hold nede denne tasten for å endre tastaturinnstillinger, som autofullføring"</b></string>
+    <string name="tip_to_start_typing" msgid="7213843601369174313"><b>"Prøv det!"</b></string>
+    <string name="label_go_key" msgid="1635148082137219148">"Gå"</string>
+    <string name="label_next_key" msgid="362972844525672568">"Neste"</string>
+    <string name="label_done_key" msgid="2441578748772529288">"Utfør"</string>
+    <string name="label_send_key" msgid="2815056534433717444">"Send"</string>
+    <string name="label_symbol_key" msgid="6175820506864489453">"?123"</string>
+    <string name="label_phone_key" msgid="4275497665515080551">"123"</string>
+    <string name="label_alpha_key" msgid="8864943487292437456">"ABC"</string>
+    <string name="label_alt_key" msgid="2846315350346694811">"ALT"</string>
+    <string name="voice_warning_title" msgid="4419354150908395008">"Stemmedata"</string>
+    <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"Stemmedata håndteres foreløpig ikke på ditt språk, men fungerer på engelsk."</string>
+    <string name="voice_warning_may_not_understand" msgid="4611518823070986445">"Talekommandoer er en eksperimentell funksjon som bruker Googles nettverksbaserte talegjenkjenning."</string>
+    <string name="voice_warning_how_to_turn_off" msgid="5652369578498701761">"Gå til innstillinger for tastatur for å slå av stemmedata."</string>
+    <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>
+    <!-- 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>
+    <string name="voice_audio_error" msgid="5072707727016414454">"Lydproblem"</string>
+    <string name="voice_server_error" msgid="7807129913977261644">"Tjenerfeil"</string>
+    <string name="voice_speech_timeout" msgid="8461817525075498795">"Ingen tale høres"</string>
+    <string name="voice_no_match" msgid="4285117547030179174">"Ingen treff"</string>
+    <string name="voice_not_installed" msgid="5552450909753842415">"Talesøk ikke installert"</string>
+    <string name="voice_swipe_hint" msgid="6943546180310682021">"Hint:"<b>" Sveip over tastaturet for å snakke"</b></string>
+    <string name="voice_punctuation_hint" msgid="1611389463237317754"><b>"Hint:"</b>" Neste gang kan du prøve å tale inn tegnsettingen ved for eksempel å si «punktum», «komma» eller «spørsmålstegn»."</string>
+    <string name="cancel" msgid="6830980399865683324">"Avbryt"</string>
+    <string name="ok" msgid="7898366843681727667">"OK"</string>
+    <string name="voice_input" msgid="2466640768843347841">"Talekommando"</string>
+  <string-array name="voice_input_modes">
+    <item msgid="1349082139076086774">"På hovedtastatur"</item>
+    <item msgid="8529385602829095903">"På talltastatur"</item>
+    <item msgid="7283103513488381103">"Av"</item>
+  </string-array>
+  <string-array name="voice_input_modes_summary">
+    <item msgid="554248625705084903">"Mikronfon på hovedtastatur"</item>
+    <item msgid="6907837061058876770">"Mikronfon på talltastatur"</item>
+    <item msgid="3664304608587798036">"Talekommando er deaktivert"</item>
+  </string-array>
+    <string name="auto_submit" msgid="9151008027068358518">"Send inn automatisk etter tale"</string>
+    <string name="auto_submit_summary" msgid="4961875269610384226">"Trykk Enter automatisk ved søk eller flytting til neste felt."</string>
+    <string name="open_the_keyboard" msgid="2215920976029260466"><font size="17"><b>"Åpne tastaturet"\n</b></font><font size="3">\n</font>"Trykk på et tekstfelt."</string>
+    <string name="close_the_keyboard" msgid="6251022259044940103"><font size="17"><b>"Lukke tastaturet"\n</b></font><font size="3">\n</font>"Trykk på tilbaketasten."</string>
+    <string name="touch_and_hold" msgid="6154166367273010534"><font size="17"><b>"Trykk og hold nede en tast for flere valg"\n</b></font><font size="3">\n</font>"Få tilgang til skilletegn og aksenter."</string>
+    <string name="keyboard_settings" msgid="4585753477617374032"><font size="17"><b>"Innstillinger for tastatur"\n</b></font><font size="3">\n</font>"Trykk på &amp; hold "<b>"?123"</b>"-tasten."</string>
+    <string name="popular_domain_0" msgid="3745279225122472969">".no"</string>
+    <string name="popular_domain_1" msgid="1370572248164278467">".com"</string>
+    <string name="popular_domain_2" msgid="3036812463748402878">".net"</string>
+    <string name="popular_domain_3" msgid="8718639560809452028">".org"</string>
+    <string name="popular_domain_4" msgid="35359437471311470">".info"</string>
+    <string name="inputMethod" msgid="7854532062009028116">"Inndatametode"</string>
+    <string name="language_selection_title" msgid="1651299598555326750">"Inndataspråk"</string>
+    <string name="language_selection_summary" msgid="187110938289512256">"Dra fingeren på mellomromstasten for å endre språk"</string>
+    <string name="hint_add_to_dictionary" msgid="8058519710062071085">"← Trykk på nytt for å lagre"</string>
+</resources>
diff --git a/java/res/values-nl/bools.xml b/java/res/values-nl/bools.xml
new file mode 100644
index 0000000..897f4b3
--- /dev/null
+++ b/java/res/values-nl/bools.xml
@@ -0,0 +1,22 @@
+<?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>
+	<bool name="im_is_default">true</bool>
+</resources>
diff --git a/java/res/values-nl/strings.xml b/java/res/values-nl/strings.xml
new file mode 100644
index 0000000..9a69c12
--- /dev/null
+++ b/java/res/values-nl/strings.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="7252517407088836577">"Android-toetsenbord"</string>
+    <string name="english_ime_settings" msgid="6661589557206947774">"Instellingen voor Android-toetsenbord"</string>
+    <string name="vibrate_on_keypress" msgid="5258079494276955460">"Trillen bij druk op een toets"</string>
+    <string name="sound_on_keypress" msgid="6093592297198243644">"Geluid bij druk op een toets"</string>
+    <string name="hit_correction" msgid="4855351009261318389">"Typefouten corrigeren"</string>
+    <string name="hit_correction_summary" msgid="8761701873008070796">"Foutcorrectie tijdens invoer inschakelen"</string>
+    <string name="hit_correction_land" msgid="2567691684825205448">"Invoerfouten in liggende weergave"</string>
+    <string name="hit_correction_land_summary" msgid="4076803842198368328">"Foutcorrectie tijdens invoer inschakelen"</string>
+    <string name="auto_correction" msgid="7911639788808958255">"Woordsuggesties"</string>
+    <string name="auto_correction_summary" msgid="6881047311475758267">"Het vorige woord automatisch corrigeren"</string>
+    <string name="prediction" msgid="466220283138359837">"Woordsuggesties"</string>
+    <string name="prediction_category" msgid="7027100625580696660">"Instellingen voor woordsuggesties"</string>
+    <string name="prediction_summary" msgid="459788228830873110">"Automatisch voltooien tijdens typen inschakelen"</string>
+    <string name="auto_complete_dialog_title" msgid="2172048590607201920">"Automatisch voltooien"</string>
+    <string name="prediction_landscape" msgid="4874601565593216183">"Tekstveld vergroten"</string>
+    <string name="prediction_landscape_summary" msgid="6736551095997839472">"Woordsuggesties verbergen in liggende weergave"</string>
+    <string name="auto_cap" msgid="1719746674854628252">"Automatisch 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>
+    <!-- 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>
+    <string name="show_suggestions_summary" msgid="1989672863935759654">"Voorgestelde woorden weergeven tijdens typen"</string>
+    <string name="auto_complete" msgid="1103196318775486023">"Auto-aanvullen"</string>
+    <string name="auto_complete_summary" msgid="6113149638718274624">"Gemarkeerd woord automatisch invoegen met spatiebalk en interpunctie"</string>
+  <string-array name="prediction_modes">
+    <item msgid="4870266572388153286">"Geen"</item>
+    <item msgid="1669461741568287396">"Basis"</item>
+    <item msgid="4894328801530136615">"Geavanceerd"</item>
+  </string-array>
+    <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Opgeslagen"</string>
+    <string name="alternates_for_a" msgid="2566516493365324765">"àáâãäåæ"</string>
+    <string name="alternates_for_e" msgid="3900510936875547555">"èéêë"</string>
+    <string name="alternates_for_i" msgid="7097915268629342242">"ìíîï"</string>
+    <string name="alternates_for_o" msgid="6151402748321267776">"òóôõöœø"</string>
+    <string name="alternates_for_u" msgid="5899096818189442934">"ùúûü"</string>
+    <string name="alternates_for_s" msgid="348762530927662188">"§ß"</string>
+    <string name="alternates_for_n" msgid="6257322556221886400">"ñ"</string>
+    <string name="alternates_for_c" msgid="151699780720639892">"ç"</string>
+    <string name="alternates_for_y" msgid="1722776806607271199">"ýÿ"</string>
+    <string name="tip_long_press" msgid="6101270866284343344">"Houd een toets ingedrukt om diakritische tekens weer te geven (ø, ö, enzovoort)"</string>
+    <string name="tip_dismiss" msgid="7585579046862204381">"Druk op elk gewenst moment op de toets Terug ↶ om het toetsenbord te sluiten"</string>
+    <string name="tip_access_symbols" msgid="6344098517525531652">"Toegang tot cijfers en symbolen"</string>
+    <string name="tip_add_to_dictionary" msgid="1487293888469227817">"Blijf drukken op het meest linkse woord om het toe te voegen aan het woordenboek"</string>
+    <string name="touch_to_continue" msgid="7869803257948414531">"Raak deze tip aan om door te gaan »"</string>
+    <string name="touch_to_finish" msgid="7990196086480585789">"Raak dit punt aan om deze tip te sluiten en te beginnen met typen."</string>
+    <string name="tip_to_open_keyboard" msgid="6821200275486950452"><b>"Het toetsenbord wordt geopend wanneer u een tekstveld aanraakt"</b></string>
+    <string name="tip_to_view_accents" msgid="5433158573693308501"><b>"Blijf een toets aanraken om diakritische tekens weer te geven"\n"(ø, ö, ô, ó, enzovoort)"</b></string>
+    <string name="tip_to_open_symbols" msgid="7345139325622444880"><b>"Schakel over naar cijfers en symbolen door deze toets aan te raken"</b></string>
+    <string name="tip_to_close_symbols" msgid="5227724217206927185"><b>"Ga terug naar letters door deze toets nogmaals aan te raken"</b></string>
+    <string name="tip_to_launch_settings" msgid="8402961128983196128"><b>"Blijf deze toets aanraken om toetsenbordinstellingen te wijzigen, zoals auto-aanvullen"</b></string>
+    <string name="tip_to_start_typing" msgid="7213843601369174313"><b>"Probeer het zelf!"</b></string>
+    <string name="label_go_key" msgid="1635148082137219148">"Beginnen"</string>
+    <string name="label_next_key" msgid="362972844525672568">"Volgende"</string>
+    <string name="label_done_key" msgid="2441578748772529288">"Gereed"</string>
+    <string name="label_send_key" msgid="2815056534433717444">"Verzenden"</string>
+    <string name="label_symbol_key" msgid="6175820506864489453">"?123"</string>
+    <string name="label_phone_key" msgid="4275497665515080551">"123"</string>
+    <string name="label_alpha_key" msgid="8864943487292437456">"ABC"</string>
+    <string name="label_alt_key" msgid="2846315350346694811">"Alt"</string>
+    <string name="voice_warning_title" msgid="4419354150908395008">"Spraakinvoer"</string>
+    <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"Spraakinvoer wordt momenteel niet ondersteund in uw taal, maar is wel beschikbaar in het Engels."</string>
+    <string name="voice_warning_may_not_understand" msgid="4611518823070986445">"Spraakinvoer is een experimentele functie met de spraakherkenning van het Google-netwerk."</string>
+    <string name="voice_warning_how_to_turn_off" msgid="5652369578498701761">"Als u spraakinvoer wilt uitschakelen, gaat u naar de toetsenbordinstellingen."</string>
+    <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>
+    <!-- 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>
+    <string name="voice_audio_error" msgid="5072707727016414454">"Audioprobleem"</string>
+    <string name="voice_server_error" msgid="7807129913977261644">"Serverfout"</string>
+    <string name="voice_speech_timeout" msgid="8461817525075498795">"Geen spraak te horen"</string>
+    <string name="voice_no_match" msgid="4285117547030179174">"Geen resultaten gevonden"</string>
+    <string name="voice_not_installed" msgid="5552450909753842415">"Voice Search is niet geïnstalleerd"</string>
+    <string name="voice_swipe_hint" msgid="6943546180310682021"><b>"Hint:"</b>" schuif over het toetsenbord om te spreken"</string>
+    <string name="voice_punctuation_hint" msgid="1611389463237317754"><b>"Hint:"</b>" spreek de volgende keer interpunctie uit, zoals \'period\' (punt), \'comma\' (komma) of \'question mark\' (vraagteken)."</string>
+    <string name="cancel" msgid="6830980399865683324">"Annuleren"</string>
+    <string name="ok" msgid="7898366843681727667">"OK"</string>
+    <string name="voice_input" msgid="2466640768843347841">"Spraakinvoer"</string>
+  <string-array name="voice_input_modes">
+    <item msgid="1349082139076086774">"Op hoofdtoetsenbord"</item>
+    <item msgid="8529385602829095903">"Op toetsenbord voor symbolen"</item>
+    <item msgid="7283103513488381103">"Uit"</item>
+  </string-array>
+  <string-array name="voice_input_modes_summary">
+    <item msgid="554248625705084903">"Microfoon op hoofdtoetsenbord"</item>
+    <item msgid="6907837061058876770">"Microfoon op toetsenbord voor symbolen"</item>
+    <item msgid="3664304608587798036">"Spraakinvoer is uitgeschakeld"</item>
+  </string-array>
+    <string name="auto_submit" msgid="9151008027068358518">"Automatisch verzenden na spraak"</string>
+    <string name="auto_submit_summary" msgid="4961875269610384226">"Drukt automatisch op Enter tijdens het zoeken of wanneer u naar het volgende veld wilt gaan."</string>
+    <string name="open_the_keyboard" msgid="2215920976029260466"><font size="17"><b>"Het toetsenbord openen"\n</b></font><font size="3">\n</font>"Raak een tekstveld aan."</string>
+    <string name="close_the_keyboard" msgid="6251022259044940103"><font size="17"><b>"Het toetsenbord sluiten"\n</b></font><font size="3">\n</font>"Druk op de terugtoets."</string>
+    <string name="touch_and_hold" msgid="6154166367273010534"><font size="17"><b>"Een toets blijven aanraken voor opties"\n</b></font><font size="3">\n</font>"Toegang tot interpunctie en diakritische tekens."</string>
+    <string name="keyboard_settings" msgid="4585753477617374032"><font size="17"><b>"Toetsenbordinstellingen"\n</b></font><font size="3">\n</font>"Blijf de toets "<b>"?123"</b>" aanraken."</string>
+    <string name="popular_domain_0" msgid="3745279225122472969">".com"</string>
+    <string name="popular_domain_1" msgid="1370572248164278467">".net"</string>
+    <string name="popular_domain_2" msgid="3036812463748402878">".org"</string>
+    <string name="popular_domain_3" msgid="8718639560809452028">".gov"</string>
+    <string name="popular_domain_4" msgid="35359437471311470">".edu"</string>
+    <string name="inputMethod" msgid="7854532062009028116">"Invoermethode"</string>
+    <string name="language_selection_title" msgid="1651299598555326750">"Invoertalen"</string>
+    <string name="language_selection_summary" msgid="187110938289512256">"Schuif uw vinger over de spatiebalk om de taal te wijzigen"</string>
+    <string name="hint_add_to_dictionary" msgid="8058519710062071085">"← Tik nogmaals om op te slaan"</string>
+</resources>
diff --git a/java/res/values-pl/bools.xml b/java/res/values-pl/bools.xml
new file mode 100644
index 0000000..897f4b3
--- /dev/null
+++ b/java/res/values-pl/bools.xml
@@ -0,0 +1,22 @@
+<?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>
+	<bool name="im_is_default">true</bool>
+</resources>
diff --git a/java/res/values-pl/donottranslate.xml b/java/res/values-pl/donottranslate.xml
new file mode 100644
index 0000000..196e73c
--- /dev/null
+++ b/java/res/values-pl/donottranslate.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Accented characters related to "z" -->
+    <string name="alternates_for_z">źż</string>
+    <!-- Accented characters related to "l" -->
+    <string name="alternates_for_l">ł</string>    
+</resources>
diff --git a/java/res/values-pl/strings.xml b/java/res/values-pl/strings.xml
new file mode 100644
index 0000000..a411cc2
--- /dev/null
+++ b/java/res/values-pl/strings.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="7252517407088836577">"Klawiatura Android"</string>
+    <string name="english_ime_settings" msgid="6661589557206947774">"Ustawienia klawiatury Android"</string>
+    <string name="vibrate_on_keypress" msgid="5258079494276955460">"Wibracja przy naciśnięciu"</string>
+    <string name="sound_on_keypress" msgid="6093592297198243644">"Dźwięk przy naciśnięciu"</string>
+    <string name="hit_correction" msgid="4855351009261318389">"Popraw błędy pisowni"</string>
+    <string name="hit_correction_summary" msgid="8761701873008070796">"Włącz poprawianie błędów wprowadzania"</string>
+    <string name="hit_correction_land" msgid="2567691684825205448">"Błędy wprowadzania w orientacji poziomej"</string>
+    <string name="hit_correction_land_summary" msgid="4076803842198368328">"Włącz poprawianie błędów wprowadzania"</string>
+    <string name="auto_correction" msgid="7911639788808958255">"Propozycje słów"</string>
+    <string name="auto_correction_summary" msgid="6881047311475758267">"Automatycznie poprawiaj poprzednie słowo"</string>
+    <string name="prediction" msgid="466220283138359837">"Propozycje słów"</string>
+    <string name="prediction_category" msgid="7027100625580696660">"Ustawienia propozycji słów"</string>
+    <string name="prediction_summary" msgid="459788228830873110">"Włącz autouzupełnianie podczas wpisywania"</string>
+    <string name="auto_complete_dialog_title" msgid="2172048590607201920">"Autouzupełnianie"</string>
+    <string name="prediction_landscape" msgid="4874601565593216183">"Zwiększ rozmiar pola tekstowego"</string>
+    <string name="prediction_landscape_summary" msgid="6736551095997839472">"Wyłącz propozycje słów w orientacji poziomej"</string>
+    <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>
+    <!-- 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>
+    <string name="show_suggestions_summary" msgid="1989672863935759654">"Wyświetl sugerowane słowa podczas wpisywania"</string>
+    <string name="auto_complete" msgid="1103196318775486023">"Autouzupełnianie"</string>
+    <string name="auto_complete_summary" msgid="6113149638718274624">"Spacja i znaki przestankowe wstawiają wyróżnione słowo"</string>
+  <string-array name="prediction_modes">
+    <item msgid="4870266572388153286">"Brak"</item>
+    <item msgid="1669461741568287396">"Podstawowy"</item>
+    <item msgid="4894328801530136615">"Zaawansowany"</item>
+  </string-array>
+    <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Zapisano"</string>
+    <string name="alternates_for_a" msgid="2566516493365324765">"ą"</string>
+    <string name="alternates_for_e" msgid="3900510936875547555">"ę"</string>
+    <string name="alternates_for_i" msgid="7097915268629342242">"ìíîï"</string>
+    <string name="alternates_for_o" msgid="6151402748321267776">"ó"</string>
+    <string name="alternates_for_u" msgid="5899096818189442934">"ùúûü"</string>
+    <string name="alternates_for_s" msgid="348762530927662188">"ś"</string>
+    <string name="alternates_for_n" msgid="6257322556221886400">"ń"</string>
+    <string name="alternates_for_c" msgid="151699780720639892">"ć"</string>
+    <string name="alternates_for_y" msgid="1722776806607271199">"ýÿ"</string>
+    <string name="tip_long_press" msgid="6101270866284343344">"Przytrzymaj klawisz, aby wyświetlić znaki akcentowane (ą, ó itp.)"</string>
+    <string name="tip_dismiss" msgid="7585579046862204381">"Naciśnij klawisz cofania ↶, aby zamknąć klawiaturę w dowolnym momencie"</string>
+    <string name="tip_access_symbols" msgid="6344098517525531652">"Przejdź do cyfr i symboli"</string>
+    <string name="tip_add_to_dictionary" msgid="1487293888469227817">"Naciśnij i przytrzymaj słowo po lewej stronie w celu dodania go do słownika"</string>
+    <string name="touch_to_continue" msgid="7869803257948414531">"Dotknij tej podpowiedzi, aby kontynuować »"</string>
+    <string name="touch_to_finish" msgid="7990196086480585789">"Dotknij tutaj, aby zamknąć tę podpowiedź i zacząć pisać!"</string>
+    <string name="tip_to_open_keyboard" msgid="6821200275486950452"><b>"Klawiatura jest otwierana po każdym dotknięciu pola tekstowego."</b></string>
+    <string name="tip_to_view_accents" msgid="5433158573693308501"><b>"Dotknij i przytrzymaj klawisz, aby wyświetlić znaki akcentowane"\n"(ą, ę, ł, ó itd.)."</b></string>
+    <string name="tip_to_open_symbols" msgid="7345139325622444880"><b>"Przełącz na cyfry i symbole, dotykając tego klawisza."</b></string>
+    <string name="tip_to_close_symbols" msgid="5227724217206927185"><b>"Wróć do trybu liter, dotykając ponownie tego klawisza."</b></string>
+    <string name="tip_to_launch_settings" msgid="8402961128983196128"><b>"Dotknij i przytrzymaj ten klawisz, aby zmienić ustawienia klawiatury, takie jak autouzupełnianie."</b></string>
+    <string name="tip_to_start_typing" msgid="7213843601369174313"><b>"Wypróbuj!"</b></string>
+    <string name="label_go_key" msgid="1635148082137219148">"Przejdź"</string>
+    <string name="label_next_key" msgid="362972844525672568">"Dalej"</string>
+    <string name="label_done_key" msgid="2441578748772529288">"Gotowe"</string>
+    <string name="label_send_key" msgid="2815056534433717444">"Wyślij"</string>
+    <string name="label_symbol_key" msgid="6175820506864489453">"?123"</string>
+    <string name="label_phone_key" msgid="4275497665515080551">"123"</string>
+    <string name="label_alpha_key" msgid="8864943487292437456">"ABC"</string>
+    <string name="label_alt_key" msgid="2846315350346694811">"ALT"</string>
+    <string name="voice_warning_title" msgid="4419354150908395008">"Wprowadzanie głosowe"</string>
+    <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"Wprowadzanie głosowe obecnie nie jest obsługiwane w Twoim języku, ale działa w języku angielskim."</string>
+    <string name="voice_warning_may_not_understand" msgid="4611518823070986445">"Wprowadzanie głosowe to funkcja eksperymentalna wykorzystująca funkcję firmy Google umożliwiającą rozpoznawanie mowy przy użyciu sieci."</string>
+    <string name="voice_warning_how_to_turn_off" msgid="5652369578498701761">"Aby wyłączyć wprowadzanie głosowe, przejdź do ustawień klawiatury."</string>
+    <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">"Działa"</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>
+    <string name="voice_audio_error" msgid="5072707727016414454">"Problem z dźwiękiem"</string>
+    <string name="voice_server_error" msgid="7807129913977261644">"Błąd serwera"</string>
+    <string name="voice_speech_timeout" msgid="8461817525075498795">"Nie wykryto mowy"</string>
+    <string name="voice_no_match" msgid="4285117547030179174">"Nie znaleziono żadnych wyników"</string>
+    <string name="voice_not_installed" msgid="5552450909753842415">"Wyszukiwanie głosowe nie jest zainstalowane"</string>
+    <string name="voice_swipe_hint" msgid="6943546180310682021"><b>"Wskazówka:"</b>" przesuń palcem po klawiaturze, aby mówić."</string>
+    <string name="voice_punctuation_hint" msgid="1611389463237317754"><b>"Wskazówka:"</b>" następnym razem spróbuj wypowiadać nazwy znaków interpunkcyjnych: „kropka”, „przecinek” lub „pytajnik”."</string>
+    <string name="cancel" msgid="6830980399865683324">"Anuluj"</string>
+    <string name="ok" msgid="7898366843681727667">"OK"</string>
+    <string name="voice_input" msgid="2466640768843347841">"Wprowadzanie głosowe"</string>
+  <string-array name="voice_input_modes">
+    <item msgid="1349082139076086774">"Na klawiaturze głównej"</item>
+    <item msgid="8529385602829095903">"Na klawiaturze z symbolami"</item>
+    <item msgid="7283103513488381103">"Wyłączone"</item>
+  </string-array>
+  <string-array name="voice_input_modes_summary">
+    <item msgid="554248625705084903">"Mikrofon na klawiaturze głównej"</item>
+    <item msgid="6907837061058876770">"Mikrofon na klawiaturze z symbolami"</item>
+    <item msgid="3664304608587798036">"Wprowadzanie głosowe jest wyłączone"</item>
+  </string-array>
+    <string name="auto_submit" msgid="9151008027068358518">"Automatyczne przesyłanie uruchamiane głosem"</string>
+    <string name="auto_submit_summary" msgid="4961875269610384226">"Podczas wyszukiwania lub przechodzenia do następnego pola automatycznie naciśnij klawisz Enter."</string>
+    <string name="open_the_keyboard" msgid="2215920976029260466"><font size="17"><b>"Otwórz klawiaturę"\n</b></font><font size="3">\n</font>"Dotknij dowolnego pola tekstowego."</string>
+    <string name="close_the_keyboard" msgid="6251022259044940103"><font size="17"><b>"Zamknij klawiaturę"\n</b></font><font size="3">\n</font>"Naciśnij klawisz Wróć."</string>
+    <string name="touch_and_hold" msgid="6154166367273010534"><font size="17"><b>"Dotknij klawisza i przytrzymaj go, aby wyświetlić opcje"\n</b></font><font size="3">\n</font>"Dostęp do znaków przestankowych i akcentowanych."</string>
+    <string name="keyboard_settings" msgid="4585753477617374032"><font size="17"><b>"Ustawienia klawiatury"\n</b></font><font size="3">\n</font>"Dotknij klawisza "<b>"?123"</b>" i przytrzymaj go."</string>
+    <string name="popular_domain_0" msgid="3745279225122472969">".com"</string>
+    <string name="popular_domain_1" msgid="1370572248164278467">".net"</string>
+    <string name="popular_domain_2" msgid="3036812463748402878">".org"</string>
+    <string name="popular_domain_3" msgid="8718639560809452028">".gov"</string>
+    <string name="popular_domain_4" msgid="35359437471311470">".edu"</string>
+    <string name="inputMethod" msgid="7854532062009028116">"Metoda wprowadzania"</string>
+    <string name="language_selection_title" msgid="1651299598555326750">"Języki wprowadzania"</string>
+    <string name="language_selection_summary" msgid="187110938289512256">"Przesuń palcem po spacji, aby zmienić język"</string>
+    <string name="hint_add_to_dictionary" msgid="8058519710062071085">"← Dotknij ponownie, aby zapisać"</string>
+</resources>
diff --git a/java/res/values-pt-rPT/strings.xml b/java/res/values-pt-rPT/strings.xml
new file mode 100644
index 0000000..399975d8
--- /dev/null
+++ b/java/res/values-pt-rPT/strings.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="7252517407088836577">"Teclado do Android"</string>
+    <string name="english_ime_settings" msgid="6661589557206947774">"Definições de teclado do Android"</string>
+    <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrar ao primir as teclas"</string>
+    <string name="sound_on_keypress" msgid="6093592297198243644">"Som ao premir as teclas"</string>
+    <string name="hit_correction" msgid="4855351009261318389">"Corrigir erros de escrita"</string>
+    <string name="hit_correction_summary" msgid="8761701873008070796">"Activar a correcção de erros de entrada"</string>
+    <string name="hit_correction_land" msgid="2567691684825205448">"Erros de entrada na horizontal"</string>
+    <string name="hit_correction_land_summary" msgid="4076803842198368328">"Activar a correcção de erros de entrada"</string>
+    <string name="auto_correction" msgid="7911639788808958255">"Sugestões de palavras"</string>
+    <string name="auto_correction_summary" msgid="6881047311475758267">"Corrigir automaticamente a palavra anterior"</string>
+    <string name="prediction" msgid="466220283138359837">"Sugestões de palavras"</string>
+    <string name="prediction_category" msgid="7027100625580696660">"Definições de sugestão de palavras"</string>
+    <string name="prediction_summary" msgid="459788228830873110">"Activar a conclusão automática durante a escrita"</string>
+    <string name="auto_complete_dialog_title" msgid="2172048590607201920">"Conclusão automática"</string>
+    <string name="prediction_landscape" msgid="4874601565593216183">"Aumentar o tamanho do campo de texto"</string>
+    <string name="prediction_landscape_summary" msgid="6736551095997839472">"Ocultar sugestões de palavras na vista horizontal"</string>
+    <string name="auto_cap" msgid="1719746674854628252">"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>
+    <!-- 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>
+    <string name="show_suggestions_summary" msgid="1989672863935759654">"Apresentar palavras sugeridas durante a escrita"</string>
+    <string name="auto_complete" msgid="1103196318775486023">"Conclusão automática"</string>
+    <string name="auto_complete_summary" msgid="6113149638718274624">"A barra de espaços e a pontuação inserem automaticamente uma palavra realçada"</string>
+  <string-array name="prediction_modes">
+    <item msgid="4870266572388153286">"Nenhum"</item>
+    <item msgid="1669461741568287396">"Básico"</item>
+    <item msgid="4894328801530136615">"Avançados"</item>
+  </string-array>
+    <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: Guardada"</string>
+    <string name="alternates_for_a" msgid="2566516493365324765">"àáâãäåæ"</string>
+    <string name="alternates_for_e" msgid="3900510936875547555">"èéêë"</string>
+    <string name="alternates_for_i" msgid="7097915268629342242">"ìíîï"</string>
+    <string name="alternates_for_o" msgid="6151402748321267776">"òóôõöœø"</string>
+    <string name="alternates_for_u" msgid="5899096818189442934">"ùúûü"</string>
+    <string name="alternates_for_s" msgid="348762530927662188">"§ß"</string>
+    <string name="alternates_for_n" msgid="6257322556221886400">"ñ"</string>
+    <string name="alternates_for_c" msgid="151699780720639892">"ç"</string>
+    <string name="alternates_for_y" msgid="1722776806607271199">"ýÿ"</string>
+    <string name="tip_long_press" msgid="6101270866284343344">"Mantenha uma tecla premida para ver os acentos (ø, ö, etc.)"</string>
+    <string name="tip_dismiss" msgid="7585579046862204381">"Prima a tecla de retrocesso ↶ para fechar o teclado a qualquer momento"</string>
+    <string name="tip_access_symbols" msgid="6344098517525531652">"Aceder a números e símbolos"</string>
+    <string name="tip_add_to_dictionary" msgid="1487293888469227817">"Prima e mantenha premida a palavra mais à esquerda para a adicionar ao dicionário"</string>
+    <string name="touch_to_continue" msgid="7869803257948414531">"Toque nesta sugestão para continuar »"</string>
+    <string name="touch_to_finish" msgid="7990196086480585789">"Toque aqui para fechar esta sugestão e começar a escrever!"</string>
+    <string name="tip_to_open_keyboard" msgid="6821200275486950452"><b>"O teclado abre quando tocar num campo de texto"</b></string>
+    <string name="tip_to_view_accents" msgid="5433158573693308501"><b>"Toque e mantenha premida uma tecla para ver os acentos"\n"(ø, ö, ô, ó, etc.)"</b></string>
+    <string name="tip_to_open_symbols" msgid="7345139325622444880"><b>"Mude para números e símbolos tocando nesta tecla"</b></string>
+    <string name="tip_to_close_symbols" msgid="5227724217206927185"><b>"Regresse às letras tocando novamente nesta tecla"</b></string>
+    <string name="tip_to_launch_settings" msgid="8402961128983196128"><b>"Toque e mantenha premida esta tecla para alterar definições do teclado, como a conclusão automática."</b></string>
+    <string name="tip_to_start_typing" msgid="7213843601369174313"><b>"Experimente!"</b></string>
+    <string name="label_go_key" msgid="1635148082137219148">"Ir"</string>
+    <string name="label_next_key" msgid="362972844525672568">"Seguinte"</string>
+    <string name="label_done_key" msgid="2441578748772529288">"Feito"</string>
+    <string name="label_send_key" msgid="2815056534433717444">"Enviar"</string>
+    <string name="label_symbol_key" msgid="6175820506864489453">"?123"</string>
+    <string name="label_phone_key" msgid="4275497665515080551">"123"</string>
+    <string name="label_alpha_key" msgid="8864943487292437456">"ABC"</string>
+    <string name="label_alt_key" msgid="2846315350346694811">"ALT"</string>
+    <string name="voice_warning_title" msgid="4419354150908395008">"Entrada de voz"</string>
+    <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"Actualmente, a entrada de voz não é suportada para o seu idioma, mas funciona em inglês."</string>
+    <string name="voice_warning_may_not_understand" msgid="4611518823070986445">"A entrada de voz é uma funcionalidade experimental que utiliza o reconhecimento de voz em rede da Google."</string>
+    <string name="voice_warning_how_to_turn_off" msgid="5652369578498701761">"Para desactivar a entrada de voz, aceda às definições do teclado."</string>
+    <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>
+    <!-- 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>
+    <string name="voice_audio_error" msgid="5072707727016414454">"Problema de áudio"</string>
+    <string name="voice_server_error" msgid="7807129913977261644">"Erro no servidor"</string>
+    <string name="voice_speech_timeout" msgid="8461817525075498795">"Nenhuma voz ouvida"</string>
+    <string name="voice_no_match" msgid="4285117547030179174">"Não foram encontradas correspondências"</string>
+    <string name="voice_not_installed" msgid="5552450909753842415">"Pesquisa de voz não instalada"</string>
+    <string name="voice_swipe_hint" msgid="6943546180310682021"><b>"Sugestão:"</b>" Deslize no teclado para falar"</string>
+    <string name="voice_punctuation_hint" msgid="1611389463237317754"><b>"Sugestão:"</b>" Da próxima vez, experimente dizer a pontuação como \"ponto final\", \"vírgula\" ou \"ponto de interrogação\"."</string>
+    <string name="cancel" msgid="6830980399865683324">"Cancelar"</string>
+    <string name="ok" msgid="7898366843681727667">"OK"</string>
+    <string name="voice_input" msgid="2466640768843347841">"Entrada de voz"</string>
+  <string-array name="voice_input_modes">
+    <item msgid="1349082139076086774">"No teclado principal"</item>
+    <item msgid="8529385602829095903">"No teclado de símbolos"</item>
+    <item msgid="7283103513488381103">"Desactivada"</item>
+  </string-array>
+  <string-array name="voice_input_modes_summary">
+    <item msgid="554248625705084903">"Microfone no teclado principal"</item>
+    <item msgid="6907837061058876770">"Microfone no teclado de símbolos"</item>
+    <item msgid="3664304608587798036">"A entrada de voz está desactivada"</item>
+  </string-array>
+    <string name="auto_submit" msgid="9151008027068358518">"Enviar automaticamente depois da voz"</string>
+    <string name="auto_submit_summary" msgid="4961875269610384226">"Premir automaticamente ENTER ao pesquisar ou avançar para o campo seguinte."</string>
+    <string name="open_the_keyboard" msgid="2215920976029260466"><font size="17"><b>"Abra o teclado"\n</b></font><font size="3">\n</font>"Toque em qualquer campo de texto."</string>
+    <string name="close_the_keyboard" msgid="6251022259044940103"><font size="17"><b>"Feche o teclado"\n</b></font><font size="3">\n</font>"Prima a tecla \"Anterior\"."</string>
+    <string name="touch_and_hold" msgid="6154166367273010534"><font size="17"><b>"Mantenha premida uma tecla para as opções"\n</b></font><font size="3">\n</font>"Aceder a pontuação e acentos."</string>
+    <string name="keyboard_settings" msgid="4585753477617374032"><font size="17"><b>"Definições do teclado"\n</b></font><font size="3">\n</font>"Mantenha premida a tecla "<b>"?123"</b>"."</string>
+    <string name="popular_domain_0" msgid="3745279225122472969">".com"</string>
+    <string name="popular_domain_1" msgid="1370572248164278467">".net"</string>
+    <string name="popular_domain_2" msgid="3036812463748402878">".org"</string>
+    <string name="popular_domain_3" msgid="8718639560809452028">".gov"</string>
+    <string name="popular_domain_4" msgid="35359437471311470">".edu"</string>
+    <string name="inputMethod" msgid="7854532062009028116">"Método de entrada"</string>
+    <string name="language_selection_title" msgid="1651299598555326750">"Idiomas de entrada"</string>
+    <string name="language_selection_summary" msgid="187110938289512256">"Deslize o dedo pela barra de espaço para alterar o idioma"</string>
+    <string name="hint_add_to_dictionary" msgid="8058519710062071085">"← Toque novamente para guardar"</string>
+</resources>
diff --git a/java/res/values-pt/strings.xml b/java/res/values-pt/strings.xml
new file mode 100644
index 0000000..18ffdf0
--- /dev/null
+++ b/java/res/values-pt/strings.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="7252517407088836577">"Teclado Android"</string>
+    <string name="english_ime_settings" msgid="6661589557206947774">"Configurações de teclado Android"</string>
+    <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrar ao tocar a tecla"</string>
+    <string name="sound_on_keypress" msgid="6093592297198243644">"Som ao tocar a tecla"</string>
+    <string name="hit_correction" msgid="4855351009261318389">"Corrigir erros de digitação"</string>
+    <string name="hit_correction_summary" msgid="8761701873008070796">"Ativar a correção de erro de entrada"</string>
+    <string name="hit_correction_land" msgid="2567691684825205448">"Erros de entrada de paisagem"</string>
+    <string name="hit_correction_land_summary" msgid="4076803842198368328">"Ativar a correção de erro de entrada"</string>
+    <string name="auto_correction" msgid="7911639788808958255">"Sugestões de palavra"</string>
+    <string name="auto_correction_summary" msgid="6881047311475758267">"Corrigir automaticamente a palavra anterior"</string>
+    <string name="prediction" msgid="466220283138359837">"Sugestões de palavra"</string>
+    <string name="prediction_category" msgid="7027100625580696660">"Configurações de sugestão de palavra"</string>
+    <string name="prediction_summary" msgid="459788228830873110">"Ativar a conclusão automática durante a digitação"</string>
+    <string name="auto_complete_dialog_title" msgid="2172048590607201920">"Conclusão automática"</string>
+    <string name="prediction_landscape" msgid="4874601565593216183">"Aumentar o tamanho do arquivo de texto"</string>
+    <string name="prediction_landscape_summary" msgid="6736551095997839472">"Ocultar sugestões de palavra na visualização da paisagem"</string>
+    <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>
+    <!-- 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>
+    <string name="show_suggestions_summary" msgid="1989672863935759654">"Exibir sugestões de palavras durante a digitação"</string>
+    <string name="auto_complete" msgid="1103196318775486023">"Conclusão automática"</string>
+    <string name="auto_complete_summary" msgid="6113149638718274624">"Barra de espaço e pontuação inserem a palavra destacada"</string>
+  <string-array name="prediction_modes">
+    <item msgid="4870266572388153286">"Nenhum"</item>
+    <item msgid="1669461741568287396">"Básico"</item>
+    <item msgid="4894328801530136615">"Avançado"</item>
+  </string-array>
+    <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Salvo"</string>
+    <string name="alternates_for_a" msgid="2566516493365324765">"àáâãäåæ"</string>
+    <string name="alternates_for_e" msgid="3900510936875547555">"èéêë"</string>
+    <string name="alternates_for_i" msgid="7097915268629342242">"ìíîï"</string>
+    <string name="alternates_for_o" msgid="6151402748321267776">"òóôõöœø"</string>
+    <string name="alternates_for_u" msgid="5899096818189442934">"ùúûü"</string>
+    <string name="alternates_for_s" msgid="348762530927662188">"§ß"</string>
+    <string name="alternates_for_n" msgid="6257322556221886400">"ñ"</string>
+    <string name="alternates_for_c" msgid="151699780720639892">"ç"</string>
+    <string name="alternates_for_y" msgid="1722776806607271199">"ýÿ"</string>
+    <string name="tip_long_press" msgid="6101270866284343344">"Segure uma tecla pressionada para ver os acentos (ø, ö, etc.)"</string>
+    <string name="tip_dismiss" msgid="7585579046862204381">"Apertar a tecla voltar ↶ para fechar o teclado, em qualquer ponto"</string>
+    <string name="tip_access_symbols" msgid="6344098517525531652">"Acessar números e símbolos"</string>
+    <string name="tip_add_to_dictionary" msgid="1487293888469227817">"Pressione e segure a palavra mais à esquerda para adicioná-la ao dicionário"</string>
+    <string name="touch_to_continue" msgid="7869803257948414531">"Toque nesta dica para continuar »"</string>
+    <string name="touch_to_finish" msgid="7990196086480585789">"Toque aqui para fechar esta dica e começar a digitar!"</string>
+    <string name="tip_to_open_keyboard" msgid="6821200275486950452"><b>"O teclado abre toda vez que você tocar em um arquivo de texto"</b></string>
+    <string name="tip_to_view_accents" msgid="5433158573693308501"><b>"Tocar e segurar a tecla para visualizar acentos"\n"(ø, ö, ô, ó e assim por diante)"</b></string>
+    <string name="tip_to_open_symbols" msgid="7345139325622444880"><b>"Alternar para números e símbolos tocando nessa tecla"</b></string>
+    <string name="tip_to_close_symbols" msgid="5227724217206927185"><b>"Voltar às letras tocando novamente nessa tecla"</b></string>
+    <string name="tip_to_launch_settings" msgid="8402961128983196128"><b>"Tocar e segurar esta tecla para alterar as configurações do teclado, como a conclusão automática"</b></string>
+    <string name="tip_to_start_typing" msgid="7213843601369174313"><b>"Experimente!"</b></string>
+    <string name="label_go_key" msgid="1635148082137219148">"Ir"</string>
+    <string name="label_next_key" msgid="362972844525672568">"Avançar"</string>
+    <string name="label_done_key" msgid="2441578748772529288">"Feito"</string>
+    <string name="label_send_key" msgid="2815056534433717444">"Enviar"</string>
+    <string name="label_symbol_key" msgid="6175820506864489453">"?123"</string>
+    <string name="label_phone_key" msgid="4275497665515080551">"123"</string>
+    <string name="label_alpha_key" msgid="8864943487292437456">"ABC"</string>
+    <string name="label_alt_key" msgid="2846315350346694811">"ALT"</string>
+    <string name="voice_warning_title" msgid="4419354150908395008">"Entrada de voz"</string>
+    <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"A entrada de voz não é suportada no momento para o seu idioma, mas funciona em inglês."</string>
+    <string name="voice_warning_may_not_understand" msgid="4611518823070986445">"A entrada de voz é um recurso experimental que usa o reconhecimento de fala de rede do Google."</string>
+    <string name="voice_warning_how_to_turn_off" msgid="5652369578498701761">"Para desativar a entrada de voz, vá para as configurações do teclado."</string>
+    <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>
+    <!-- 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>
+    <string name="voice_audio_error" msgid="5072707727016414454">"Problema com o áudio"</string>
+    <string name="voice_server_error" msgid="7807129913977261644">"Erro do servidor"</string>
+    <string name="voice_speech_timeout" msgid="8461817525075498795">"Nenhuma fala ouvida"</string>
+    <string name="voice_no_match" msgid="4285117547030179174">"Não há resultados compatíveis"</string>
+    <string name="voice_not_installed" msgid="5552450909753842415">"A pesquisa por voz não está instalada"</string>
+    <string name="voice_swipe_hint" msgid="6943546180310682021"><b>"Dica:"</b>" Deslize sobre o teclado para falar"</string>
+    <string name="voice_punctuation_hint" msgid="1611389463237317754"><b>"Dica:"</b>" Da próxima vez, tente falar o nome da pontuação como \"ponto\", \"vírgula\" ou \"ponto de interrogação\"."</string>
+    <string name="cancel" msgid="6830980399865683324">"Cancelar"</string>
+    <string name="ok" msgid="7898366843681727667">"OK"</string>
+    <string name="voice_input" msgid="2466640768843347841">"Entrada de voz"</string>
+  <string-array name="voice_input_modes">
+    <item msgid="1349082139076086774">"No teclado principal"</item>
+    <item msgid="8529385602829095903">"No teclado de símbolos"</item>
+    <item msgid="7283103513488381103">"Desativado"</item>
+  </string-array>
+  <string-array name="voice_input_modes_summary">
+    <item msgid="554248625705084903">"Microfone no teclado principal"</item>
+    <item msgid="6907837061058876770">"Microfone no teclado de símbolos"</item>
+    <item msgid="3664304608587798036">"Entrada de voz desativada"</item>
+  </string-array>
+    <string name="auto_submit" msgid="9151008027068358518">"Enviar automaticamente depois de falar"</string>
+    <string name="auto_submit_summary" msgid="4961875269610384226">"Pressione Enter automaticamente ao pesquisar ou ir para o próximo campo."</string>
+    <string name="open_the_keyboard" msgid="2215920976029260466"><font size="17"><b>"Abra o teclado"\n</b></font><font size="3">\n</font>"Toque em qualquer campo de texto."</string>
+    <string name="close_the_keyboard" msgid="6251022259044940103"><font size="17"><b>"Feche o teclado"\n</b></font><font size="3">\n</font>"Pressione a tecla Voltar."</string>
+    <string name="touch_and_hold" msgid="6154166367273010534"><font size="17"><b>"Toque e mantenha pressionada uma tecla para ver as opções"\n</b></font><font size="3">\n</font>"Acesse a pontuação e os acentos."</string>
+    <string name="keyboard_settings" msgid="4585753477617374032"><font size="17"><b>"Configurações de teclado"\n</b></font><font size="3">\n</font>"Toque e mantenha pressionada a tecla "<b>"?123"</b>"."</string>
+    <string name="popular_domain_0" msgid="3745279225122472969">".com"</string>
+    <string name="popular_domain_1" msgid="1370572248164278467">".net"</string>
+    <string name="popular_domain_2" msgid="3036812463748402878">".org"</string>
+    <string name="popular_domain_3" msgid="8718639560809452028">".gov"</string>
+    <string name="popular_domain_4" msgid="35359437471311470">".edu"</string>
+    <string name="inputMethod" msgid="7854532062009028116">"Método de entrada"</string>
+    <string name="language_selection_title" msgid="1651299598555326750">"Idiomas de entrada"</string>
+    <string name="language_selection_summary" msgid="187110938289512256">"Deslize o dedo na barra de espaços para alterar o idioma"</string>
+    <string name="hint_add_to_dictionary" msgid="8058519710062071085">"← Toque novamente para salvar"</string>
+</resources>
diff --git a/java/res/values-ru/strings.xml b/java/res/values-ru/strings.xml
new file mode 100644
index 0000000..d4ebc6d
--- /dev/null
+++ b/java/res/values-ru/strings.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="7252517407088836577">"Клавиатура Android"</string>
+    <string name="english_ime_settings" msgid="6661589557206947774">"Настройки клавиатуры Android"</string>
+    <string name="vibrate_on_keypress" msgid="5258079494276955460">"Виброотклик клавиш"</string>
+    <string name="sound_on_keypress" msgid="6093592297198243644">"Звук клавиш"</string>
+    <string name="hit_correction" msgid="4855351009261318389">"Исправлять опечатки"</string>
+    <string name="hit_correction_summary" msgid="8761701873008070796">"Включить исправление ошибок при вводе"</string>
+    <string name="hit_correction_land" msgid="2567691684825205448">"Ошибки при вводе в горизонтальной ориентации"</string>
+    <string name="hit_correction_land_summary" msgid="4076803842198368328">"Включить исправление ошибок при вводе"</string>
+    <string name="auto_correction" msgid="7911639788808958255">"Предложение слов"</string>
+    <string name="auto_correction_summary" msgid="6881047311475758267">"Автоматически исправлять предыдущее слово"</string>
+    <string name="prediction" msgid="466220283138359837">"Предложение слов"</string>
+    <string name="prediction_category" msgid="7027100625580696660">"Настройки подсказок"</string>
+    <string name="prediction_summary" msgid="459788228830873110">"Включить автоматическое завершение слов при вводе"</string>
+    <string name="auto_complete_dialog_title" msgid="2172048590607201920">"Автоматическое завершение"</string>
+    <string name="prediction_landscape" msgid="4874601565593216183">"Увеличить размер текстового поля"</string>
+    <string name="prediction_landscape_summary" msgid="6736551095997839472">"Скрывать предложение слов в горизонтальной ориентации"</string>
+    <string name="auto_cap" msgid="1719746674854628252">"Автоподст. заглавных"</string>
+    <string name="auto_cap_summary" msgid="3260681697600786825">"Делать заглавной первую букву предложения"</string>
+    <string name="auto_punctuate" msgid="7276672334264521751">"Автоматическая пунктуация"</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>
+    <string name="show_suggestions_summary" msgid="1989672863935759654">"Предлагать варианты слов во время ввода"</string>
+    <string name="auto_complete" msgid="1103196318775486023">"Автозавершение"</string>
+    <string name="auto_complete_summary" msgid="6113149638718274624">"При нажатии пробела или знака препинания вставлять предложенное слово"</string>
+  <string-array name="prediction_modes">
+    <item msgid="4870266572388153286">"Нет"</item>
+    <item msgid="1669461741568287396">"Основной"</item>
+    <item msgid="4894328801530136615">"Расширенный"</item>
+  </string-array>
+    <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : сохранено"</string>
+    <string name="alternates_for_a" msgid="2566516493365324765">"àáâãäåæ"</string>
+    <string name="alternates_for_e" msgid="3900510936875547555">"èéêë"</string>
+    <string name="alternates_for_i" msgid="7097915268629342242">"ìíîï"</string>
+    <string name="alternates_for_o" msgid="6151402748321267776">"òóôõöœø"</string>
+    <string name="alternates_for_u" msgid="5899096818189442934">"ùúûü"</string>
+    <string name="alternates_for_s" msgid="348762530927662188">"§ß"</string>
+    <string name="alternates_for_n" msgid="6257322556221886400">"ñ"</string>
+    <string name="alternates_for_c" msgid="151699780720639892">"ç"</string>
+    <string name="alternates_for_y" msgid="1722776806607271199">"ýÿ"</string>
+    <string name="tip_long_press" msgid="6101270866284343344">"Удерживайте клавишу, чтобы увидеть варианты с диакритическими знаками (ø, ö и т.д.)"</string>
+    <string name="tip_dismiss" msgid="7585579046862204381">"Нажмите клавишу \"Назад\" ↶, чтобы закрыть клавиатуру в любой момент"</string>
+    <string name="tip_access_symbols" msgid="6344098517525531652">"Открыть цифры и символы"</string>
+    <string name="tip_add_to_dictionary" msgid="1487293888469227817">"Нажмите и удерживайте слово слева, чтобы добавить его в словарь"</string>
+    <string name="touch_to_continue" msgid="7869803257948414531">"Чтобы продолжить, нажмите на эту подсказку »"</string>
+    <string name="touch_to_finish" msgid="7990196086480585789">"Нажмите здесь, чтобы закрыть подсказку и начать вводить текст."</string>
+    <string name="tip_to_open_keyboard" msgid="6821200275486950452"><b>"Клавиатура появляется автоматически при касании текстового поля"</b></string>
+    <string name="tip_to_view_accents" msgid="5433158573693308501"><b>"Нажмите и удерживайте клавишу, чтобы появились спецсимволы "\n"(ø, ö, ô, ó и т. п.)"</b></string>
+    <string name="tip_to_open_symbols" msgid="7345139325622444880"><b>"Чтобы переключаться между вводом цифр и символов, используйте эту клавишу"</b></string>
+    <string name="tip_to_close_symbols" msgid="5227724217206927185"><b>"Чтобы вернуться к буквенной клавиатуре, снова нажмите на эту клавишу"</b></string>
+    <string name="tip_to_launch_settings" msgid="8402961128983196128"><b>"Чтобы сменить настройки клавиатуры (такие как автозавершение), нажмите и удерживайте эту клавишу"</b></string>
+    <string name="tip_to_start_typing" msgid="7213843601369174313"><b>"Попробуйте!"</b></string>
+    <string name="label_go_key" msgid="1635148082137219148">"Перейти"</string>
+    <string name="label_next_key" msgid="362972844525672568">"Далее"</string>
+    <string name="label_done_key" msgid="2441578748772529288">"Готово"</string>
+    <string name="label_send_key" msgid="2815056534433717444">"Отправить"</string>
+    <string name="label_symbol_key" msgid="6175820506864489453">"?123"</string>
+    <string name="label_phone_key" msgid="4275497665515080551">"123"</string>
+    <string name="label_alpha_key" msgid="8864943487292437456">"АБВ"</string>
+    <string name="label_alt_key" msgid="2846315350346694811">"ALT"</string>
+    <string name="voice_warning_title" msgid="4419354150908395008">"Голосовой ввод"</string>
+    <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"В настоящее время функция голосового ввода не поддерживает ваш язык, но вы можете пользоваться ей на английском."</string>
+    <string name="voice_warning_may_not_understand" msgid="4611518823070986445">"Голосовой ввод – экспериментальная функция на основе технологии сетевого распознавания речи от Google."</string>
+    <string name="voice_warning_how_to_turn_off" msgid="5652369578498701761">"Функция голосового ввода отключается в настройках клавиатуры."</string>
+    <string name="voice_hint_dialog_message" msgid="6892342981545727994">"Чтобы использовать голосовой ввод, нажмите кнопку микрофона или проведите пальцем по экранной клавиатуре."</string>
+    <string name="voice_listening" msgid="467518160751321844">"Говорите"</string>
+    <string name="voice_working" msgid="6666937792815731889">"Выполняется обработка"</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>
+    <string name="voice_audio_error" msgid="5072707727016414454">"Неполадка со звуком"</string>
+    <string name="voice_server_error" msgid="7807129913977261644">"Ошибка сервера"</string>
+    <string name="voice_speech_timeout" msgid="8461817525075498795">"Речи не слышно"</string>
+    <string name="voice_no_match" msgid="4285117547030179174">"Соответствий не найдено"</string>
+    <string name="voice_not_installed" msgid="5552450909753842415">"Голосовой поиск не установлен"</string>
+    <string name="voice_swipe_hint" msgid="6943546180310682021"><b>"Совет"</b>". Проведите пальцем по клавиатуре для голосового ввода."</string>
+    <string name="voice_punctuation_hint" msgid="1611389463237317754"><b>"Совет"</b>". В следующий раз проговаривайте знаки препинания, например \"точка\", \"запятая\", \"вопросительный знак\"."</string>
+    <string name="cancel" msgid="6830980399865683324">"Отмена"</string>
+    <string name="ok" msgid="7898366843681727667">"ОК"</string>
+    <string name="voice_input" msgid="2466640768843347841">"Голосовой ввод"</string>
+  <string-array name="voice_input_modes">
+    <item msgid="1349082139076086774">"На основной клавиатуре"</item>
+    <item msgid="8529385602829095903">"На клавиатуре символов"</item>
+    <item msgid="7283103513488381103">"Выкл."</item>
+  </string-array>
+  <string-array name="voice_input_modes_summary">
+    <item msgid="554248625705084903">"Микрофон на основной клавиатуре"</item>
+    <item msgid="6907837061058876770">"Микрофон на клавиатуре символов"</item>
+    <item msgid="3664304608587798036">"Голосовой ввод отключен"</item>
+  </string-array>
+    <string name="auto_submit" msgid="9151008027068358518">"Автоматически отправлять по окончании голосового ввода"</string>
+    <string name="auto_submit_summary" msgid="4961875269610384226">"Автоматически нажимать \"Ввод\" при поиске или переходе к следующему полю."</string>
+    <string name="open_the_keyboard" msgid="2215920976029260466"><font size="17"><b>"Откройте клавиатуру"\n</b></font><font size="3">\n</font>"Нажмите на любое текстовое поле."</string>
+    <string name="close_the_keyboard" msgid="6251022259044940103"><font size="17"><b>"Закрытие клавиатуры"\n</b></font><font size="3">\n</font>"Нажмите клавишу \"Назад\"."</string>
+    <string name="touch_and_hold" msgid="6154166367273010534"><font size="17"><b>"Нажмите и удерживайте клавишу для вызова параметров"\n</b></font><font size="3">\n</font>"Доступ к пунктуационным и диакритическим знакам."</string>
+    <string name="keyboard_settings" msgid="4585753477617374032"><font size="17"><b>"Настройки клавиатуры"\n</b></font><font size="3">\n</font>"Нажмите и удерживайте клавишу "<b>"?123"</b>"."</string>
+    <string name="popular_domain_0" msgid="3745279225122472969">".com"</string>
+    <string name="popular_domain_1" msgid="1370572248164278467">".net"</string>
+    <string name="popular_domain_2" msgid="3036812463748402878">".org"</string>
+    <string name="popular_domain_3" msgid="8718639560809452028">".gov"</string>
+    <string name="popular_domain_4" msgid="35359437471311470">".edu"</string>
+    <string name="inputMethod" msgid="7854532062009028116">"Способ ввода"</string>
+    <string name="language_selection_title" msgid="1651299598555326750">"Языки ввода"</string>
+    <string name="language_selection_summary" msgid="187110938289512256">"Для изменения языка проведите пальцем по пробелу"</string>
+    <string name="hint_add_to_dictionary" msgid="8058519710062071085">"← Нажмите повторно, чтобы сохранить"</string>
+</resources>
diff --git a/java/res/values-sv/strings.xml b/java/res/values-sv/strings.xml
new file mode 100644
index 0000000..2d07a07
--- /dev/null
+++ b/java/res/values-sv/strings.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="7252517407088836577">"Androids tangentbord"</string>
+    <string name="english_ime_settings" msgid="6661589557206947774">"Inställningar för Androids tangentbord"</string>
+    <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrera vid tangenttryck"</string>
+    <string name="sound_on_keypress" msgid="6093592297198243644">"Knappljud"</string>
+    <string name="hit_correction" msgid="4855351009261318389">"Rätta skrivfel"</string>
+    <string name="hit_correction_summary" msgid="8761701873008070796">"Aktivera rättning av felaktiga inmatningar"</string>
+    <string name="hit_correction_land" msgid="2567691684825205448">"Inmatningsfel i liggande vy"</string>
+    <string name="hit_correction_land_summary" msgid="4076803842198368328">"Aktivera rättning av felaktiga inmatningar"</string>
+    <string name="auto_correction" msgid="7911639788808958255">"Ordförslag"</string>
+    <string name="auto_correction_summary" msgid="6881047311475758267">"Rätta automatiskt föregående ord"</string>
+    <string name="prediction" msgid="466220283138359837">"Ordförslag"</string>
+    <string name="prediction_category" msgid="7027100625580696660">"Inställningar för ordförslag"</string>
+    <string name="prediction_summary" msgid="459788228830873110">"Aktivera Komplettera automatiskt när du skriver"</string>
+    <string name="auto_complete_dialog_title" msgid="2172048590607201920">"Komplettera automatiskt"</string>
+    <string name="prediction_landscape" msgid="4874601565593216183">"Gör textfältet större"</string>
+    <string name="prediction_landscape_summary" msgid="6736551095997839472">"Dölj ordförslag i liggande vy"</string>
+    <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>
+    <!-- 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>
+    <string name="show_suggestions_summary" msgid="1989672863935759654">"Visar ordförslag när du skriver"</string>
+    <string name="auto_complete" msgid="1103196318775486023">"Komplettera automatiskt"</string>
+    <string name="auto_complete_summary" msgid="6113149638718274624">"Blanksteg och punkt infogar automatiskt markerat ord"</string>
+  <string-array name="prediction_modes">
+    <item msgid="4870266572388153286">"Inget"</item>
+    <item msgid="1669461741568287396">"Grundinställningar"</item>
+    <item msgid="4894328801530136615">"Avancerade"</item>
+  </string-array>
+    <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>: sparat"</string>
+    <string name="alternates_for_a" msgid="2566516493365324765">"àáâãäåæ"</string>
+    <string name="alternates_for_e" msgid="3900510936875547555">"èéêë"</string>
+    <string name="alternates_for_i" msgid="7097915268629342242">"ìíîï"</string>
+    <string name="alternates_for_o" msgid="6151402748321267776">"òóôõöœø"</string>
+    <string name="alternates_for_u" msgid="5899096818189442934">"ùúûü"</string>
+    <string name="alternates_for_s" msgid="348762530927662188">"§ß"</string>
+    <string name="alternates_for_n" msgid="6257322556221886400">"ñ"</string>
+    <string name="alternates_for_c" msgid="151699780720639892">"ç"</string>
+    <string name="alternates_for_y" msgid="1722776806607271199">"ýÿ"</string>
+    <string name="tip_long_press" msgid="6101270866284343344">"Håll nere en tangent om du vill visa accenter (ø, ö, etc.)"</string>
+    <string name="tip_dismiss" msgid="7585579046862204381">"Tryck på Tillbaka ↶ om du vill stänga tangentbordet"</string>
+    <string name="tip_access_symbols" msgid="6344098517525531652">"För siffror och symboler"</string>
+    <string name="tip_add_to_dictionary" msgid="1487293888469227817">"Tryck och håll ned ordet längst till vänster om du vill lägga till det i ordlistan"</string>
+    <string name="touch_to_continue" msgid="7869803257948414531">"Tryck på tipset för att fortsätta »"</string>
+    <string name="touch_to_finish" msgid="7990196086480585789">"Tryck här om du vill stänga tipset och börja skriva!"</string>
+    <string name="tip_to_open_keyboard" msgid="6821200275486950452"><b>"Tangentbordet öppnas när du trycker på ett textfält"</b></string>
+    <string name="tip_to_view_accents" msgid="5433158573693308501"><b>"Tryck och håll ned en tangent om du vill visa accenter"\n"(ø, ö, ô, ó och så vidare)"</b></string>
+    <string name="tip_to_open_symbols" msgid="7345139325622444880"><b>"Växla till siffror och symboler med den här tangenten"</b></string>
+    <string name="tip_to_close_symbols" msgid="5227724217206927185"><b>"Återgå till bokstäver genom att trycka på tangenten en gång till"</b></string>
+    <string name="tip_to_launch_settings" msgid="8402961128983196128"><b>"Tryck och håll ned tangenten om du vill ändra inställningarna för tangentbordet, till exempel Komplettera automatiskt"</b></string>
+    <string name="tip_to_start_typing" msgid="7213843601369174313"><b>"Testa!"</b></string>
+    <string name="label_go_key" msgid="1635148082137219148">"Kör"</string>
+    <string name="label_next_key" msgid="362972844525672568">"Nästa"</string>
+    <string name="label_done_key" msgid="2441578748772529288">"Färdig"</string>
+    <string name="label_send_key" msgid="2815056534433717444">"Skicka"</string>
+    <string name="label_symbol_key" msgid="6175820506864489453">"?123"</string>
+    <string name="label_phone_key" msgid="4275497665515080551">"123"</string>
+    <string name="label_alpha_key" msgid="8864943487292437456">"ABC"</string>
+    <string name="label_alt_key" msgid="2846315350346694811">"ALT"</string>
+    <string name="voice_warning_title" msgid="4419354150908395008">"Röstindata"</string>
+    <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"Röstindata stöds inte på ditt språk än, men tjänsten fungerar på engelska."</string>
+    <string name="voice_warning_may_not_understand" msgid="4611518823070986445">"Röstinmatning är en funktion på experimentstadiet som använder Googles nätverks taligenkänning."</string>
+    <string name="voice_warning_how_to_turn_off" msgid="5652369578498701761">"Om du vill stänga av röstindata öppnar du inställningarna för tangentbordet."</string>
+    <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>
+    <!-- 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>
+    <string name="voice_audio_error" msgid="5072707727016414454">"Ljudproblem"</string>
+    <string name="voice_server_error" msgid="7807129913977261644">"Serverfel"</string>
+    <string name="voice_speech_timeout" msgid="8461817525075498795">"Hörde inget tal"</string>
+    <string name="voice_no_match" msgid="4285117547030179174">"Inga träffar hittades"</string>
+    <string name="voice_not_installed" msgid="5552450909753842415">"Voice Search är inte installerat"</string>
+    <string name="voice_swipe_hint" msgid="6943546180310682021"><b>"Tips!"</b>" Dra över tangentbordet om du vill tala"</string>
+    <string name="voice_punctuation_hint" msgid="1611389463237317754"><b>"Tips!"</b>" Nästa gång testar du att säga skiljetecknen, som \"punkt\", \"komma\" eller \"frågetecken\"."</string>
+    <string name="cancel" msgid="6830980399865683324">"Avbryt"</string>
+    <string name="ok" msgid="7898366843681727667">"OK"</string>
+    <string name="voice_input" msgid="2466640768843347841">"Röstindata"</string>
+  <string-array name="voice_input_modes">
+    <item msgid="1349082139076086774">"På huvudtangentbordet"</item>
+    <item msgid="8529385602829095903">"På symboltangentbordet"</item>
+    <item msgid="7283103513488381103">"Av"</item>
+  </string-array>
+  <string-array name="voice_input_modes_summary">
+    <item msgid="554248625705084903">"Mikrofon på huvudtangentbordet"</item>
+    <item msgid="6907837061058876770">"Mikrofon på symboltangentbordet"</item>
+    <item msgid="3664304608587798036">"Röstindata är inaktiverat"</item>
+  </string-array>
+    <string name="auto_submit" msgid="9151008027068358518">"Skicka automatiskt efter röst"</string>
+    <string name="auto_submit_summary" msgid="4961875269610384226">"Tryck automatiskt på retur vid sökning eller när du fortsätter till nästa fält."</string>
+    <string name="open_the_keyboard" msgid="2215920976029260466"><font size="17"><b>"Öppna tangentbordet"\n</b></font><font size="3">\n</font>"Tryck på ett textfält."</string>
+    <string name="close_the_keyboard" msgid="6251022259044940103"><font size="17"><b>"Stäng tangentbordet"\n</b></font><font size="3">\n</font>"Tryck på Tillbaka."</string>
+    <string name="touch_and_hold" msgid="6154166367273010534"><font size="17"><b>"Tryck länge på en tangent om du vill se alternativ"\n</b></font><font size="3">\n</font>"Använda skiljetecken och accenter."</string>
+    <string name="keyboard_settings" msgid="4585753477617374032"><font size="17"><b>"Tangentbordsinställningar"\n</b></font><font size="3">\n</font>"Tryck länge på tangenten "<b>"?123"</b>"."</string>
+    <string name="popular_domain_0" msgid="3745279225122472969">".com"</string>
+    <string name="popular_domain_1" msgid="1370572248164278467">".net"</string>
+    <string name="popular_domain_2" msgid="3036812463748402878">".org"</string>
+    <string name="popular_domain_3" msgid="8718639560809452028">".gov"</string>
+    <string name="popular_domain_4" msgid="35359437471311470">".edu"</string>
+    <string name="inputMethod" msgid="7854532062009028116">"Indatametod"</string>
+    <string name="language_selection_title" msgid="1651299598555326750">"Inmatningsspråk"</string>
+    <string name="language_selection_summary" msgid="187110938289512256">"Dra med fingret på blanksteg om du vill ändra språk"</string>
+    <string name="hint_add_to_dictionary" msgid="8058519710062071085">"← Peka igen för att spara"</string>
+</resources>
diff --git a/java/res/values-tr/donottranslate.xml b/java/res/values-tr/donottranslate.xml
new file mode 100644
index 0000000..2154c16
--- /dev/null
+++ b/java/res/values-tr/donottranslate.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Accented characters related to "g" -->
+    <string name="alternates_for_g">ğ</string>
+</resources>
\ No newline at end of file
diff --git a/java/res/values-tr/strings.xml b/java/res/values-tr/strings.xml
new file mode 100644
index 0000000..e150a5e
--- /dev/null
+++ b/java/res/values-tr/strings.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="7252517407088836577">"Android klavyesi"</string>
+    <string name="english_ime_settings" msgid="6661589557206947774">"Android klavye ayarları"</string>
+    <string name="vibrate_on_keypress" msgid="5258079494276955460">"Tuşa basıldığında titret"</string>
+    <string name="sound_on_keypress" msgid="6093592297198243644">"Tuşa basıldığında ses çıkar"</string>
+    <string name="hit_correction" msgid="4855351009261318389">"Yazım hatalarını düzelt"</string>
+    <string name="hit_correction_summary" msgid="8761701873008070796">"Giriş hatası düzeltmeyi etkinleştir"</string>
+    <string name="hit_correction_land" msgid="2567691684825205448">"Yatay giriş hataları"</string>
+    <string name="hit_correction_land_summary" msgid="4076803842198368328">"Giriş hatası düzeltmeyi etkinleştir"</string>
+    <string name="auto_correction" msgid="7911639788808958255">"Kelime önerileri"</string>
+    <string name="auto_correction_summary" msgid="6881047311475758267">"Önceki kelimeyi otomatik olarak düzelt"</string>
+    <string name="prediction" msgid="466220283138359837">"Kelime önerileri"</string>
+    <string name="prediction_category" msgid="7027100625580696660">"Kelime önerme ayarları"</string>
+    <string name="prediction_summary" msgid="459788228830873110">"Yazarken otomatik tamamlamayı etkinleştir"</string>
+    <string name="auto_complete_dialog_title" msgid="2172048590607201920">"Otomatik tamamlama"</string>
+    <string name="prediction_landscape" msgid="4874601565593216183">"Metin alanı boyutunu artır"</string>
+    <string name="prediction_landscape_summary" msgid="6736551095997839472">"Yatay görünümde kelime önerilerini gizle"</string>
+    <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>
+    <!-- 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>
+    <string name="show_suggestions_summary" msgid="1989672863935759654">"Yazarken önerilen kelimeleri görüntüle"</string>
+    <string name="auto_complete" msgid="1103196318775486023">"Otomatik tamamla"</string>
+    <string name="auto_complete_summary" msgid="6113149638718274624">"Boşluk tuşu ve noktalama vurgulanan kelimeyi otomatik ekler"</string>
+  <string-array name="prediction_modes">
+    <item msgid="4870266572388153286">"Yok"</item>
+    <item msgid="1669461741568287396">"Temel"</item>
+    <item msgid="4894328801530136615">"Gelişmiş"</item>
+  </string-array>
+    <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g> : Kaydedildi"</string>
+    <string name="alternates_for_a" msgid="2566516493365324765">"àáâãäåæ"</string>
+    <string name="alternates_for_e" msgid="3900510936875547555">"èéêë"</string>
+    <string name="alternates_for_i" msgid="7097915268629342242">"iìíîï"</string>
+    <string name="alternates_for_o" msgid="6151402748321267776">"öòóôõœø"</string>
+    <string name="alternates_for_u" msgid="5899096818189442934">"üùúû"</string>
+    <string name="alternates_for_s" msgid="348762530927662188">"ş§ß"</string>
+    <string name="alternates_for_n" msgid="6257322556221886400">"ñ"</string>
+    <string name="alternates_for_c" msgid="151699780720639892">"ç"</string>
+    <string name="alternates_for_y" msgid="1722776806607271199">"ýÿ"</string>
+    <string name="tip_long_press" msgid="6101270866284343344">"Vurguları görmek için bir tuşu basılı tutun (ø, ö, v.b.)"</string>
+    <string name="tip_dismiss" msgid="7585579046862204381">"Klavyeyi herhangi bir anda kapatmak için geri tuşuna ↶ basın"</string>
+    <string name="tip_access_symbols" msgid="6344098517525531652">"Sayılara ve simgelere erişin"</string>
+    <string name="tip_add_to_dictionary" msgid="1487293888469227817">"Sözlüğe eklemek için en soldaki kelimeye basın ve basılı tutun"</string>
+    <string name="touch_to_continue" msgid="7869803257948414531">"Devam etmek için bu ipucuna dokunun »"</string>
+    <string name="touch_to_finish" msgid="7990196086480585789">"Bu ipucunu kapatmak için buraya dokunun ve yazmaya başlayın!"</string>
+    <string name="tip_to_open_keyboard" msgid="6821200275486950452"><b>"Bir metin alanına dokunduğunuzda klavye açılır"</b></string>
+    <string name="tip_to_view_accents" msgid="5433158573693308501"><b>"Vurguları görüntülemek için bir tuşa basın ve basılı tutun"\n"(ø, ö, ô, ó v.b.)"</b></string>
+    <string name="tip_to_open_symbols" msgid="7345139325622444880"><b>"Bu tuşa dokunarak sayılar ve simgeler arasında geçiş yap"</b></string>
+    <string name="tip_to_close_symbols" msgid="5227724217206927185"><b>"Bu tuşa tekrar dokunarak harflere geri dönün"</b></string>
+    <string name="tip_to_launch_settings" msgid="8402961128983196128"><b>"Otomatik tamamlama gibi klavye ayarlarını değiştirmek için bu tuşa basın ve basılı tutun"</b></string>
+    <string name="tip_to_start_typing" msgid="7213843601369174313"><b>"Deneyin!"</b></string>
+    <string name="label_go_key" msgid="1635148082137219148">"Git"</string>
+    <string name="label_next_key" msgid="362972844525672568">"İleri"</string>
+    <string name="label_done_key" msgid="2441578748772529288">"Bitti"</string>
+    <string name="label_send_key" msgid="2815056534433717444">"Gönder"</string>
+    <string name="label_symbol_key" msgid="6175820506864489453">"?123"</string>
+    <string name="label_phone_key" msgid="4275497665515080551">"123"</string>
+    <string name="label_alpha_key" msgid="8864943487292437456">"ABC"</string>
+    <string name="label_alt_key" msgid="2846315350346694811">"ALT"</string>
+    <string name="voice_warning_title" msgid="4419354150908395008">"Ses girişi"</string>
+    <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"Ses girişi, şu anda sizin diliniz için desteklenmiyor ama İngilizce dilinde kullanılabilir."</string>
+    <string name="voice_warning_may_not_understand" msgid="4611518823070986445">"Ses girişi, Google\'ın ağ bağlantılı ses tanıma işlevini kullanan deneysel bir özelliktir."</string>
+    <string name="voice_warning_how_to_turn_off" msgid="5652369578498701761">"Ses girişini kapatmak için klavye ayarlarına gidin."</string>
+    <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>
+    <!-- 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>
+    <string name="voice_audio_error" msgid="5072707727016414454">"Ses sorunu"</string>
+    <string name="voice_server_error" msgid="7807129913977261644">"Sunucu hatası"</string>
+    <string name="voice_speech_timeout" msgid="8461817525075498795">"Konuşma duyulmadı"</string>
+    <string name="voice_no_match" msgid="4285117547030179174">"Eşleşme bulunamadı"</string>
+    <string name="voice_not_installed" msgid="5552450909753842415">"Sesle arama yüklenmedi"</string>
+    <string name="voice_swipe_hint" msgid="6943546180310682021"><b>"İpucu:"</b>" Konuşmak için parmağınızı klavye üzerinde kaydırın"</string>
+    <string name="voice_punctuation_hint" msgid="1611389463237317754"><b>"İpucu:"</b>" Sonraki sefer, \"nokta\", \"virgül\" veya \"soru işareti\" gibi noktalama işaretlerini telaffuz etmeyi deneyin."</string>
+    <string name="cancel" msgid="6830980399865683324">"İptal"</string>
+    <string name="ok" msgid="7898366843681727667">"Tamam"</string>
+    <string name="voice_input" msgid="2466640768843347841">"Ses girişi"</string>
+  <string-array name="voice_input_modes">
+    <item msgid="1349082139076086774">"Ana klavyede"</item>
+    <item msgid="8529385602829095903">"Simge klavyesinde"</item>
+    <item msgid="7283103513488381103">"Kapalı"</item>
+  </string-array>
+  <string-array name="voice_input_modes_summary">
+    <item msgid="554248625705084903">"Ana klavyedeki mikrofon"</item>
+    <item msgid="6907837061058876770">"Simge klavyesindeki mikrofon"</item>
+    <item msgid="3664304608587798036">"Sesle giriş devre dışı bırakıldı"</item>
+  </string-array>
+    <string name="auto_submit" msgid="9151008027068358518">"Sesten sonra otomatik gönder"</string>
+    <string name="auto_submit_summary" msgid="4961875269610384226">"Arama yaparken veya bir sonraki alana giderken enter tuşuna otomatik olarak basın."</string>
+    <string name="open_the_keyboard" msgid="2215920976029260466"><font size="17"><b>"Klavyeyi açın"\n</b></font><font size="3">\n</font>"Herhangi bir metin alanına dokunun."</string>
+    <string name="close_the_keyboard" msgid="6251022259044940103"><font size="17"><b>"Klavyeyi kapatın"\n</b></font><font size="3">\n</font>"Geri tuşuna basın."</string>
+    <string name="touch_and_hold" msgid="6154166367273010534"><font size="17"><b>"Seçenekler için bir tuşa dokunun ve basılı tutun"\n</b></font><font size="3">\n</font>"Noktalama ve vurgulama işaretlerine erişin."</string>
+    <string name="keyboard_settings" msgid="4585753477617374032"><font size="17"><b>"Klavye ayarları"\n</b></font><font size="3">\n</font><b>"?123"</b>" tuşuna dokunun ve basılı tutun."</string>
+    <string name="popular_domain_0" msgid="3745279225122472969">".com"</string>
+    <string name="popular_domain_1" msgid="1370572248164278467">".net"</string>
+    <string name="popular_domain_2" msgid="3036812463748402878">".org"</string>
+    <string name="popular_domain_3" msgid="8718639560809452028">".gov"</string>
+    <string name="popular_domain_4" msgid="35359437471311470">".edu"</string>
+    <string name="inputMethod" msgid="7854532062009028116">"Giriş yöntemi"</string>
+    <string name="language_selection_title" msgid="1651299598555326750">"Giriş dilleri"</string>
+    <string name="language_selection_summary" msgid="187110938289512256">"Dili değiştirmek için parmağınızı boşluk çubuğu üzerinde kaydırın"</string>
+    <string name="hint_add_to_dictionary" msgid="8058519710062071085">"← Kaydetmek için tekrar dokunun"</string>
+</resources>
diff --git a/java/res/values-zh-rCN/strings.xml b/java/res/values-zh-rCN/strings.xml
new file mode 100644
index 0000000..f372991
--- /dev/null
+++ b/java/res/values-zh-rCN/strings.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="7252517407088836577">"Android 键盘"</string>
+    <string name="english_ime_settings" msgid="6661589557206947774">"Android 键盘设置"</string>
+    <string name="vibrate_on_keypress" msgid="5258079494276955460">"按键时振动"</string>
+    <string name="sound_on_keypress" msgid="6093592297198243644">"按键时播放音效"</string>
+    <string name="hit_correction" msgid="4855351009261318389">"纠正输入错误"</string>
+    <string name="hit_correction_summary" msgid="8761701873008070796">"启用输入错误纠正"</string>
+    <string name="hit_correction_land" msgid="2567691684825205448">"横向输入错误"</string>
+    <string name="hit_correction_land_summary" msgid="4076803842198368328">"启用输入错误纠正"</string>
+    <string name="auto_correction" msgid="7911639788808958255">"字词建议"</string>
+    <string name="auto_correction_summary" msgid="6881047311475758267">"自动纠正之前的字词"</string>
+    <string name="prediction" msgid="466220283138359837">"字词建议"</string>
+    <string name="prediction_category" msgid="7027100625580696660">"字词建议设置"</string>
+    <string name="prediction_summary" msgid="459788228830873110">"输入时启用自动填写功能"</string>
+    <string name="auto_complete_dialog_title" msgid="2172048590607201920">"自动完成"</string>
+    <string name="prediction_landscape" msgid="4874601565593216183">"增加文本域的大小"</string>
+    <string name="prediction_landscape_summary" msgid="6736551095997839472">"在横向视图中隐藏字词建议"</string>
+    <string name="auto_cap" msgid="1719746674854628252">"自动大写"</string>
+    <string name="auto_cap_summary" msgid="3260681697600786825">"句首字母大写"</string>
+    <string name="auto_punctuate" msgid="7276672334264521751">"自动加标点"</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>
+    <string name="show_suggestions_summary" msgid="1989672863935759654">"输入时启用联想提示"</string>
+    <string name="auto_complete" msgid="1103196318775486023">"自动填写"</string>
+    <string name="auto_complete_summary" msgid="6113149638718274624">"按空格键和标点符号时自动插入突出显示的字词"</string>
+  <string-array name="prediction_modes">
+    <item msgid="4870266572388153286">"无"</item>
+    <item msgid="1669461741568287396">"基本模式"</item>
+    <item msgid="4894328801530136615">"高级模式"</item>
+  </string-array>
+    <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>:已保存"</string>
+    <string name="alternates_for_a" msgid="2566516493365324765">"àáâãäåæ"</string>
+    <string name="alternates_for_e" msgid="3900510936875547555">"èéêë"</string>
+    <string name="alternates_for_i" msgid="7097915268629342242">"ìíîï"</string>
+    <string name="alternates_for_o" msgid="6151402748321267776">"òóôõöœø"</string>
+    <string name="alternates_for_u" msgid="5899096818189442934">"ùúûü"</string>
+    <string name="alternates_for_s" msgid="348762530927662188">"§ß"</string>
+    <string name="alternates_for_n" msgid="6257322556221886400">"ñ"</string>
+    <string name="alternates_for_c" msgid="151699780720639892">"ç"</string>
+    <string name="alternates_for_y" msgid="1722776806607271199">"ýÿ"</string>
+    <string name="tip_long_press" msgid="6101270866284343344">"按住某个键可看到重音符号(例如 ø、ö 等)"</string>
+    <string name="tip_dismiss" msgid="7585579046862204381">"随时可以通过按后退键 ↶ 关闭键盘"</string>
+    <string name="tip_access_symbols" msgid="6344098517525531652">"访问数字和符号"</string>
+    <string name="tip_add_to_dictionary" msgid="1487293888469227817">"长按最左侧的字可将其添加到词典中"</string>
+    <string name="touch_to_continue" msgid="7869803257948414531">"轻触此提示继续 »"</string>
+    <string name="touch_to_finish" msgid="7990196086480585789">"轻触此处可关闭该提示,然后便可开始输入内容!"</string>
+    <string name="tip_to_open_keyboard" msgid="6821200275486950452"><b>"轻触文本字段即可打开键盘"</b></string>
+    <string name="tip_to_view_accents" msgid="5433158573693308501"><b>"按住某个键可看到重音符号"\n"(例如 ø、ö、ô、ó 等)"</b></string>
+    <string name="tip_to_open_symbols" msgid="7345139325622444880"><b>"轻触该键可切换到数字和符号输入模式"</b></string>
+    <string name="tip_to_close_symbols" msgid="5227724217206927185"><b>"再次轻触该键可返回字母输入模式"</b></string>
+    <string name="tip_to_launch_settings" msgid="8402961128983196128"><b>"长按该键可更改键盘设置,例如自动填写"</b></string>
+    <string name="tip_to_start_typing" msgid="7213843601369174313"><b>"试试吧!"</b></string>
+    <string name="label_go_key" msgid="1635148082137219148">"开始"</string>
+    <string name="label_next_key" msgid="362972844525672568">"下一步"</string>
+    <string name="label_done_key" msgid="2441578748772529288">"完成"</string>
+    <string name="label_send_key" msgid="2815056534433717444">"发送"</string>
+    <string name="label_symbol_key" msgid="6175820506864489453">"?123"</string>
+    <string name="label_phone_key" msgid="4275497665515080551">"123"</string>
+    <string name="label_alpha_key" msgid="8864943487292437456">"ABC"</string>
+    <string name="label_alt_key" msgid="2846315350346694811">"ALT"</string>
+    <string name="voice_warning_title" msgid="4419354150908395008">"语音输入"</string>
+    <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"语音输入功能当前还不支持您的语言,您只能输入英语语音。"</string>
+    <string name="voice_warning_may_not_understand" msgid="4611518823070986445">"语音输入是一项试验性的功能,它采用了 Google 的网络语音识别功能。"</string>
+    <string name="voice_warning_how_to_turn_off" msgid="5652369578498701761">"要关闭语音输入功能,请转至键盘设置。"</string>
+    <string name="voice_hint_dialog_message" msgid="6892342981545727994">"要使用语音输入,请按麦克风按钮或者在屏幕键盘上滑动手指。"</string>
+    <string name="voice_listening" msgid="467518160751321844">"请开始说话"</string>
+    <string name="voice_working" msgid="6666937792815731889">"正在处理"</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>
+    <string name="voice_audio_error" msgid="5072707727016414454">"音频问题"</string>
+    <string name="voice_server_error" msgid="7807129913977261644">"服务器出错"</string>
+    <string name="voice_speech_timeout" msgid="8461817525075498795">"未听到语音"</string>
+    <string name="voice_no_match" msgid="4285117547030179174">"未找到匹配项"</string>
+    <string name="voice_not_installed" msgid="5552450909753842415">"未安装语音搜索"</string>
+    <string name="voice_swipe_hint" msgid="6943546180310682021"><b>"提示:"</b>"在键盘上滑动手指可激活语音功能"</string>
+    <string name="voice_punctuation_hint" msgid="1611389463237317754"><b>"提示:"</b>"稍后,请尝试使用语音输入标点符号,如“句号”、“逗号”或“问号”。"</string>
+    <string name="cancel" msgid="6830980399865683324">"取消"</string>
+    <string name="ok" msgid="7898366843681727667">"确定"</string>
+    <string name="voice_input" msgid="2466640768843347841">"语音输入"</string>
+  <string-array name="voice_input_modes">
+    <item msgid="1349082139076086774">"主键盘上"</item>
+    <item msgid="8529385602829095903">"符号键盘上"</item>
+    <item msgid="7283103513488381103">"关"</item>
+  </string-array>
+  <string-array name="voice_input_modes_summary">
+    <item msgid="554248625705084903">"主键盘上的麦克风"</item>
+    <item msgid="6907837061058876770">"符号键盘上的麦克风"</item>
+    <item msgid="3664304608587798036">"已停用语音输入"</item>
+  </string-array>
+    <string name="auto_submit" msgid="9151008027068358518">"语音结束后自动提交"</string>
+    <string name="auto_submit_summary" msgid="4961875269610384226">"搜索或转到下一字段时自动按 Enter。"</string>
+    <string name="open_the_keyboard" msgid="2215920976029260466"><font size="17"><b>"打开键盘"\n</b></font><font size="3">\n</font>"轻触任意文本字段。"</string>
+    <string name="close_the_keyboard" msgid="6251022259044940103"><font size="17"><b>"关闭键盘"\n</b></font><font size="3">\n</font>"按“返回”键。"</string>
+    <string name="touch_and_hold" msgid="6154166367273010534"><font size="17"><b>"按住某个键可开启其他字符选项"\n</b></font><font size="3">\n</font>"访问标点和重音符号。"</string>
+    <string name="keyboard_settings" msgid="4585753477617374032"><font size="17"><b>"键盘设置"\n</b></font><font size="3">\n</font>"长按 "<b>"?123"</b>" 键。"</string>
+    <string name="popular_domain_0" msgid="3745279225122472969">".com"</string>
+    <string name="popular_domain_1" msgid="1370572248164278467">".net"</string>
+    <string name="popular_domain_2" msgid="3036812463748402878">".org"</string>
+    <string name="popular_domain_3" msgid="8718639560809452028">".gov"</string>
+    <string name="popular_domain_4" msgid="35359437471311470">".edu"</string>
+    <string name="inputMethod" msgid="7854532062009028116">"输入法"</string>
+    <string name="language_selection_title" msgid="1651299598555326750">"输入语言"</string>
+    <string name="language_selection_summary" msgid="187110938289512256">"在空格键上滑动手指可更改语言"</string>
+    <string name="hint_add_to_dictionary" msgid="8058519710062071085">"← 再次点按即可保存"</string>
+</resources>
diff --git a/java/res/values-zh-rTW/strings.xml b/java/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000..f6b4e31
--- /dev/null
+++ b/java/res/values-zh-rTW/strings.xml
@@ -0,0 +1,129 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<resources xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <string name="english_ime_name" msgid="7252517407088836577">"Android 鍵盤"</string>
+    <string name="english_ime_settings" msgid="6661589557206947774">"Android 鍵盤設定"</string>
+    <string name="vibrate_on_keypress" msgid="5258079494276955460">"按鍵時震動"</string>
+    <string name="sound_on_keypress" msgid="6093592297198243644">"按鍵時播放音效"</string>
+    <string name="hit_correction" msgid="4855351009261318389">"修正輸入錯誤"</string>
+    <string name="hit_correction_summary" msgid="8761701873008070796">"啟用輸入錯誤修正功能"</string>
+    <string name="hit_correction_land" msgid="2567691684825205448">"橫向輸入錯誤"</string>
+    <string name="hit_correction_land_summary" msgid="4076803842198368328">"啟用輸入錯誤修正功能"</string>
+    <string name="auto_correction" msgid="7911639788808958255">"字詞建議"</string>
+    <string name="auto_correction_summary" msgid="6881047311475758267">"自動修正前一個字詞"</string>
+    <string name="prediction" msgid="466220283138359837">"字詞建議"</string>
+    <string name="prediction_category" msgid="7027100625580696660">"字詞建議設定"</string>
+    <string name="prediction_summary" msgid="459788228830873110">"輸入時啟用自動完成"</string>
+    <string name="auto_complete_dialog_title" msgid="2172048590607201920">"自動完成"</string>
+    <string name="prediction_landscape" msgid="4874601565593216183">"放大文字欄位大小"</string>
+    <string name="prediction_landscape_summary" msgid="6736551095997839472">"在橫向檢視模式中隱藏字詞建議"</string>
+    <string name="auto_cap" msgid="1719746674854628252">"自動大寫"</string>
+    <string name="auto_cap_summary" msgid="3260681697600786825">"句首字母大寫"</string>
+    <string name="auto_punctuate" msgid="7276672334264521751">"自動標點"</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>
+    <string name="show_suggestions_summary" msgid="1989672863935759654">"打字時顯示建議字詞"</string>
+    <string name="auto_complete" msgid="1103196318775486023">"自動完成"</string>
+    <string name="auto_complete_summary" msgid="6113149638718274624">"在反白顯示的字詞處自動插入空白鍵和標點符號鍵盤"</string>
+  <string-array name="prediction_modes">
+    <item msgid="4870266572388153286">"無"</item>
+    <item msgid="1669461741568287396">"基本模式"</item>
+    <item msgid="4894328801530136615">"進階模式"</item>
+  </string-array>
+    <string name="added_word" msgid="8993883354622484372">"<xliff:g id="WORD">%s</xliff:g>:已儲存"</string>
+    <string name="alternates_for_a" msgid="2566516493365324765">"àáâãäåæ"</string>
+    <string name="alternates_for_e" msgid="3900510936875547555">"èéêë"</string>
+    <string name="alternates_for_i" msgid="7097915268629342242">"ìíîï"</string>
+    <string name="alternates_for_o" msgid="6151402748321267776">"òóôõöœø"</string>
+    <string name="alternates_for_u" msgid="5899096818189442934">"ùúûü"</string>
+    <string name="alternates_for_s" msgid="348762530927662188">"§ß"</string>
+    <string name="alternates_for_n" msgid="6257322556221886400">"ñ"</string>
+    <string name="alternates_for_c" msgid="151699780720639892">"ç"</string>
+    <string name="alternates_for_y" msgid="1722776806607271199">"ýÿ"</string>
+    <string name="tip_long_press" msgid="6101270866284343344">"按住按鍵可查看重音符號 (ø、ö 等)"</string>
+    <string name="tip_dismiss" msgid="7585579046862204381">"隨時可以透過按後退鍵 ↶ 關閉鍵盤"</string>
+    <string name="tip_access_symbols" msgid="6344098517525531652">"使用數字和符號"</string>
+    <string name="tip_add_to_dictionary" msgid="1487293888469227817">"按住最左邊的字詞,將其新增到字典中"</string>
+    <string name="touch_to_continue" msgid="7869803257948414531">"輕觸此提示繼續 »"</string>
+    <string name="touch_to_finish" msgid="7990196086480585789">"輕觸此處以關閉提示,並開始打字!"</string>
+    <string name="tip_to_open_keyboard" msgid="6821200275486950452"><b>"輕觸文字欄位時即會開啟鍵盤"</b></string>
+    <string name="tip_to_view_accents" msgid="5433158573693308501"><b>"輕觸並按住某個鍵即可查看聲調"\n"(ø、ö、ô、ó 等)"</b></string>
+    <string name="tip_to_open_symbols" msgid="7345139325622444880"><b>"輕觸此鍵即可切換到數字和符號鍵盤"</b></string>
+    <string name="tip_to_close_symbols" msgid="5227724217206927185"><b>"再次輕觸此鍵即可返回到字母鍵盤"</b></string>
+    <string name="tip_to_launch_settings" msgid="8402961128983196128"><b>"輕觸並按住此鍵即可變更鍵盤設定,例如自動完成"</b></string>
+    <string name="tip_to_start_typing" msgid="7213843601369174313"><b>"試試看!"</b></string>
+    <string name="label_go_key" msgid="1635148082137219148">"開始"</string>
+    <string name="label_next_key" msgid="362972844525672568">"下一步"</string>
+    <string name="label_done_key" msgid="2441578748772529288">"完成"</string>
+    <string name="label_send_key" msgid="2815056534433717444">"傳送"</string>
+    <string name="label_symbol_key" msgid="6175820506864489453">"?123"</string>
+    <string name="label_phone_key" msgid="4275497665515080551">"123"</string>
+    <string name="label_alpha_key" msgid="8864943487292437456">"ABC"</string>
+    <string name="label_alt_key" msgid="2846315350346694811">"ALT"</string>
+    <string name="voice_warning_title" msgid="4419354150908395008">"語音輸入"</string>
+    <string name="voice_warning_locale_not_supported" msgid="637923019716442333">"語音輸入目前不支援您的語言,但是可以辨識英文。"</string>
+    <string name="voice_warning_may_not_understand" msgid="4611518823070986445">"語音輸入這項實驗功能運用了 Google 的網路語音辨識系統。"</string>
+    <string name="voice_warning_how_to_turn_off" msgid="5652369578498701761">"請前往鍵盤設定來關閉語音輸入。"</string>
+    <string name="voice_hint_dialog_message" msgid="6892342981545727994">"如要使用語音輸入,按下 [麥克風] 按鈕,或將手指滑過螢幕小鍵盤即可。"</string>
+    <string name="voice_listening" msgid="467518160751321844">"請說話"</string>
+    <string name="voice_working" msgid="6666937792815731889">"辨識中"</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>
+    <string name="voice_audio_error" msgid="5072707727016414454">"音訊問題"</string>
+    <string name="voice_server_error" msgid="7807129913977261644">"伺服器錯誤"</string>
+    <string name="voice_speech_timeout" msgid="8461817525075498795">"沒有聽到任何聲音"</string>
+    <string name="voice_no_match" msgid="4285117547030179174">"找不到相符的項目"</string>
+    <string name="voice_not_installed" msgid="5552450909753842415">"未安裝語音搜尋"</string>
+    <string name="voice_swipe_hint" msgid="6943546180310682021"><b>"提示:"</b>"滑過鍵盤即可說話"</string>
+    <string name="voice_punctuation_hint" msgid="1611389463237317754"><b>"提示:"</b>"下次可嘗試說出標點符號,例如「句號」、「逗號」或「問號」。"</string>
+    <string name="cancel" msgid="6830980399865683324">"取消"</string>
+    <string name="ok" msgid="7898366843681727667">"確定"</string>
+    <string name="voice_input" msgid="2466640768843347841">"語音輸入"</string>
+  <string-array name="voice_input_modes">
+    <item msgid="1349082139076086774">"於主鍵盤"</item>
+    <item msgid="8529385602829095903">"於符號鍵盤"</item>
+    <item msgid="7283103513488381103">"關閉"</item>
+  </string-array>
+  <string-array name="voice_input_modes_summary">
+    <item msgid="554248625705084903">"主鍵盤上的麥克風"</item>
+    <item msgid="6907837061058876770">"符號鍵盤上的麥克風"</item>
+    <item msgid="3664304608587798036">"已停用語音輸入"</item>
+  </string-array>
+    <string name="auto_submit" msgid="9151008027068358518">"說話後自動提交"</string>
+    <string name="auto_submit_summary" msgid="4961875269610384226">"搜尋或前往下一個欄位時自動按下輸入。"</string>
+    <string name="open_the_keyboard" msgid="2215920976029260466"><font size="17"><b>"開啟鍵盤"\n</b></font><font size="3">\n</font>"輕觸任何文字欄位。"</string>
+    <string name="close_the_keyboard" msgid="6251022259044940103"><font size="17"><b>"關閉鍵盤"\n</b></font><font size="3">\n</font>"按下 Back 鍵。"</string>
+    <string name="touch_and_hold" msgid="6154166367273010534"><font size="17"><b>\n"輕觸並按住按鍵開啟選項"</b></font><font size="3">\n</font>"輸入標點與輕重音。"</string>
+    <string name="keyboard_settings" msgid="4585753477617374032"><font size="17"><b>"鍵盤設定"\n</b></font><font size="3">\n</font>"輕觸並按住 "<b>"?123"</b>" 鍵。"</string>
+    <string name="popular_domain_0" msgid="3745279225122472969">".com"</string>
+    <string name="popular_domain_1" msgid="1370572248164278467">".net"</string>
+    <string name="popular_domain_2" msgid="3036812463748402878">".org"</string>
+    <string name="popular_domain_3" msgid="8718639560809452028">".gov"</string>
+    <string name="popular_domain_4" msgid="35359437471311470">".edu"</string>
+    <string name="inputMethod" msgid="7854532062009028116">"輸入方式"</string>
+    <string name="language_selection_title" msgid="1651299598555326750">"輸入語言"</string>
+    <string name="language_selection_summary" msgid="187110938289512256">"以手指在空白鍵上滑動可變更語言"</string>
+    <string name="hint_add_to_dictionary" msgid="8058519710062071085">"← 再次輕按可儲存"</string>
+    <!-- no translation found for has_dictionary (6071847973466625007) -->
+    <skip />
+</resources>
diff --git a/java/res/values/bools.xml b/java/res/values/bools.xml
new file mode 100644
index 0000000..ebe2f04
--- /dev/null
+++ b/java/res/values/bools.xml
@@ -0,0 +1,28 @@
+<?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>
+    <!-- Whether or not auto-correction should be enabled by default -->
+    <bool name="enable_autocorrect">true</bool>
+    <!-- Whether this input method should be used as the default for a locale. Override it
+         for latin languages. -->
+    <bool name="im_is_default">false</bool>
+    <!-- Whether or not voice input is enabled by default. -->
+    <bool name="voice_input_default">true</bool>
+</resources>
diff --git a/java/res/values/colors.xml b/java/res/values/colors.xml
new file mode 100644
index 0000000..c90d9f6
--- /dev/null
+++ b/java/res/values/colors.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* 
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     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>
+    <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
diff --git a/java/res/values/dimens.xml b/java/res/values/dimens.xml
new file mode 100644
index 0000000..5b2095c
--- /dev/null
+++ b/java/res/values/dimens.xml
@@ -0,0 +1,26 @@
+<?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>
+    <dimen name="key_height">54dip</dimen>
+    <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
diff --git a/java/res/values/donottranslate.xml b/java/res/values/donottranslate.xml
new file mode 100644
index 0000000..d501735
--- /dev/null
+++ b/java/res/values/donottranslate.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- Symbols that are commonly considered word separators in this language -->
+    <string name="word_separators">.\u0009\u0020,;:!?\n()[]*&amp;@{}/&lt;&gt;_+=|\u0022</string>
+    <!-- Symbols that are sentence separators, for purposes of making it hug the last sentence. -->
+    <string name="sentence_separators">.,!?</string>
+    <!-- Symbols that are suggested between words -->
+    <string name="suggested_punctuations">!?,@_</string>
+    <!-- Accented characters related to "d" -->
+    <string name="alternates_for_d"></string>
+    <!-- Accented characters related to "r" -->
+    <string name="alternates_for_r"></string>
+    <!-- Accented characters related to "t" -->
+    <string name="alternates_for_t"></string>
+    <!-- Accented characters related to "z" -->
+    <string name="alternates_for_z"></string>
+    <!-- Accented characters related to "l" -->
+    <string name="alternates_for_l"></string>
+    <!-- Accented characters related to "g" -->
+    <string name="alternates_for_g"></string>
+</resources>
diff --git a/java/res/values/durations.xml b/java/res/values/durations.xml
new file mode 100644
index 0000000..92af68e
--- /dev/null
+++ b/java/res/values/durations.xml
@@ -0,0 +1,25 @@
+<?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>
+    <!-- Vibration duration in milliseconds, for key presses in the IME. This can be hardware
+        dependent and may require overriding with a device specific overlay. -->
+    <integer name="vibrate_duration_ms">40</integer>
+</resources>
diff --git a/java/res/values/keycodes.xml b/java/res/values/keycodes.xml
new file mode 100644
index 0000000..8156c0e
--- /dev/null
+++ b/java/res/values/keycodes.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* 
+**
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License"); 
+** you may not use this file except in compliance with the License. 
+** You may obtain a copy of the License at 
+**
+**     http://www.apache.org/licenses/LICENSE-2.0 
+**
+** Unless required by applicable law or agreed to in writing, software 
+** distributed under the License is distributed on an "AS IS" BASIS, 
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+** See the License for the specific language governing permissions and 
+** limitations under the License.
+*/
+-->
+
+<resources>
+    <!-- Keycode for F1 (function) key. This one switches between language switch & comma/.com -->
+    <integer name="key_f1">-103</integer>
+</resources>
diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml
new file mode 100644
index 0000000..35dd3e0
--- /dev/null
+++ b/java/res/values/strings.xml
@@ -0,0 +1,325 @@
+<?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">Android keyboard</string>
+    <!-- Title for Latin keyboard settings activity / dialog -->
+    <string name="english_ime_settings">Android keyboard settings</string>
+
+    <!-- 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>
+    
+    <!-- Description for hit_correction  -->
+    <string name="hit_correction_summary">Enable input error correction</string>
+    
+    <!-- Option to enable using nearby keys when correcting/predicting in landscape-->
+    <string name="hit_correction_land">Landscape input errors</string>
+    
+    <!-- Description for hit_correction in landscape -->
+    <string name="hit_correction_land_summary">Enable input error correction</string>
+    
+    <!-- Option to automatically correct word on hitting space -->
+    <string name="auto_correction">Word suggestions</string> 
+    
+    <!-- Description for auto_correction -->
+    <string name="auto_correction_summary">Automatically correct the previous word</string>
+	
+    <!-- Option to enable text prediction -->
+    <string name="prediction">Word suggestions</string>
+    <!-- Category title for text prediction -->
+    <string name="prediction_category">Word suggestion settings</string>
+    <!-- Description for text prediction -->
+    <string name="prediction_summary">Enable auto completion while typing</string>
+	
+    <!-- Dialog title for auto complete choices -->
+    <string name="auto_complete_dialog_title">Auto completion</string>
+    
+    <!-- Option to enable text prediction in landscape -->
+    <string name="prediction_landscape">Increase text field size</string> 
+    <!-- Description for text prediction -->
+    <string name="prediction_landscape_summary">Hide word suggestions in landscape view</string>
+	
+    <!-- Option to enable auto capitalization of sentences -->
+    <string name="auto_cap">Auto-capitalization</string> 
+    <!-- Description for auto cap -->
+    <string name="auto_cap_summary">Capitalize the start of a sentence</string>
+    <!-- Option to enable auto punctuate -->
+    <string name="auto_punctuate">Auto-punctuate</string> 
+    <!-- Description for auto punctuate -->
+    <string name="auto_punctuate_summary"></string>
+    
+    <!-- Option to enable quick fixes -->
+    <string name="quick_fixes">Quick fixes</string>
+    <!-- Description for quick fixes -->
+    <string name="quick_fixes_summary">Corrects commonly typed mistakes</string>
+    
+    <!-- Option to enable showing suggestions -->
+    <string name="show_suggestions">Show suggestions</string>
+    <!-- Description for show suggestions -->
+    <string name="show_suggestions_summary">Display suggested words while typing</string>
+    
+    <!-- Option to enable auto completion -->
+    <string name="auto_complete">Auto-complete</string>
+    <!-- Description for auto completion -->
+    <string name="auto_complete_summary">Spacebar and punctuation automatically insert highlighted word</string>
+    
+    <!-- Array of prediction modes -->
+    <string-array name="prediction_modes">
+        <item>None</item>
+        <item>Basic</item>
+        <item>Advanced</item>
+    </string-array>
+    
+    <!-- Don't translate -->
+    <string name="prediction_none" translatable="false">0</string>
+    <!-- Don't translate -->
+    <string name="prediction_basic" translatable="false">1</string>
+    <!-- Don't translate -->
+    <string name="prediction_full"  translatable="false">2</string>
+
+    <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>
+    <!-- Accented forms of "a" -->
+    <string name="alternates_for_a">àáâãäåæ</string>
+    <!-- Accented forms of "e" -->
+    <string name="alternates_for_e">èéêë</string>
+    <!-- Accented forms of "i" -->
+    <string name="alternates_for_i">ìíîï</string>
+    <!-- Accented forms of "o" -->
+    <string name="alternates_for_o">òóôõöœø</string>
+    <!-- Accented forms of "u" -->
+    <string name="alternates_for_u">ùúûü</string>
+    <!-- Letters associated with "s" -->
+    <string name="alternates_for_s">§ß</string>
+    <!-- Accented forms of "n" -->
+    <string name="alternates_for_n">ñ</string>
+    <!-- Accented forms of "c" -->
+    <string name="alternates_for_c">ç</string>
+    <!-- Accented forms of "y" -->
+    <string name="alternates_for_y">ýÿ</string>
+    
+    <!-- Tip to long press on keys -->
+    <string name="tip_long_press">Hold a key down to see accents (ø, ö, etc.)</string>
+    <!-- Tip to dismiss keyboard -->
+    <string name="tip_dismiss">Press the back key \u21B6 to close the keyboard at any point</string>
+    <!-- Tip to press ?123 to access numbers and symbols -->
+    <string name="tip_access_symbols">Access numbers and symbols</string>    
+    <!-- Tip to long press on typed word to add to dictionary -->
+    <string name="tip_add_to_dictionary">Press and hold the left-most word to add it to the dictionary
+        </string>
+        
+    <!-- Instruction to touch the bubble to continue -->
+    <string name="touch_to_continue">Touch this hint to continue »</string>
+    
+    <!-- Instruction to touch the bubble to start typing -->
+    <string name="touch_to_finish">Touch here to close this hint and start typing!</string>
+    
+    <!-- Tutorial tip 1 - The keyboard opens any time you touch a text field -->
+    <string name="tip_to_open_keyboard"><b>The keyboard opens any time you touch a text field</b></string>
+    
+    <!-- Tutorial tip 2 - Touch and hold a key to view accents (examples) -->
+    <string name="tip_to_view_accents"><b>Touch &amp; hold a key to view accents\n(ø, ö, ô, ó, and so on)</b>
+    </string>
+    
+    <!-- Tutorial tip 3 - How to switch to number/symbol keyboard -->
+    <string name="tip_to_open_symbols"><b>Switch to numbers and symbols by touching this key</b></string>
+    
+    <!-- Tutorial tip 4 - How to switch back to alphabet keyboard -->
+    <string name="tip_to_close_symbols"><b>Go back to letters by touching this key again</b></string>
+    
+    <!-- Tutorial tip 5 - How to launch keyboard settings -->
+    <string name="tip_to_launch_settings"><b>Touch &amp; hold this key to change keyboard settings, like auto complete</b></string>
+    
+    <!-- Tutorial tip 6 - Done with the tutorial -->
+    <string name="tip_to_start_typing"><b>Try it!</b></string>
+    
+    
+    <!-- Label for soft enter key when it performs GO action.  Must be short to fit on key! -->
+    <string name="label_go_key">Go</string>
+    <!-- Label for soft enter key when it performs NEXT action.  Must be short to fit on key! -->
+    <string name="label_next_key">Next</string>
+    <!-- Label for soft enter key when it performs DONE action.  Must be short to fit on key! -->
+    <string name="label_done_key">Done</string>
+    <!-- Label for soft enter key when it performs SEND action.  Must be short to fit on key! -->
+    <string name="label_send_key">Send</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">ABC</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">Voice input</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">Voice input is not currently supported for your language, but does work in English.</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">Voice input is an experimental feature using Google\'s networked speech recognition.</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">To turn off voice input, go to keyboard settings.</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">To use voice input, press the microphone button or slide your finger across the on-screen keyboard.</string>
+    
+    <!-- Short message to tell the user the system is ready for them to speak. -->
+    <string name="voice_listening">Speak now</string>
+
+    <!-- Short message shown after the user finishes speaking. -->
+    <string name="voice_working">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">Error. Please try again.</string>
+
+    <!-- Short message shown for a network error. -->
+    <string name="voice_network_error">Couldn\'t connect</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">Error, too much speech.</string>
+
+    <!-- Short message shown for an audio error. -->
+    <string name="voice_audio_error">Audio problem</string>
+
+    <!-- Short message shown for an error with the voice server. -->
+    <string name="voice_server_error">Server error</string>
+
+    <!-- Short message shown when no speech is heard. -->
+    <string name="voice_speech_timeout">No speech heard</string>
+
+    <!-- Short message shown when the server couldn't parse any speech. -->
+    <string name="voice_no_match">No matches found</string>
+
+    <!-- Short message shown when the user initiates voice and voice
+	search is not installed. -->
+    <string name="voice_not_installed">Voice search not installed</string>
+
+    <!-- Short hint shown in candidate view to explain voice input. -->
+    <string name="voice_swipe_hint"><b>Hint:</b> Swipe across keyboard to speak</string>
+
+    <!-- Short hint shown in candidate view to explain that user can speak punctuation. -->
+    <string name="voice_punctuation_hint"><b>Hint:</b> Next time, try speaking punctuation like \"period\", \"comma\", or \"question mark\".</string>
+
+    <!-- Label on button to stop recognition. Must be short to fit on button. -->
+    <string name="cancel">Cancel</string>
+
+    <!-- Label on button when an error occurs -->
+    <string name="ok">OK</string>
+
+    <!-- Preferences item for enabling speech input -->
+    <string name="voice_input">Voice input</string>
+
+    <!-- Array of Voice Input modes -->
+    <string-array name="voice_input_modes">
+        <item>On main keyboard</item>
+        <item>On symbols keyboard</item>
+        <item>Off</item>
+    </string-array>
+
+    <!-- Don't translate -->
+    <string name="voice_mode_main" translatable="false">0</string>
+    <!-- Don't translate -->
+    <string name="voice_mode_symbols" translatable="false">1</string>
+    <!-- Don't translate -->
+    <string name="voice_mode_off"  translatable="false">2</string>
+
+    <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>Mic on main keyboard</item>
+        <item>Mic on symbols keyboard</item>
+        <item>Voice input is disabled</item>
+    </string-array>
+
+    <!-- Press the "enter" key after the user speaks. Option on settings.-->
+    <string name="auto_submit">Auto submit after voice</string>
+
+    <!-- Press the "enter" key after the user speaks. Summary of option in settings.-->
+    <string name="auto_submit_summary">Automatically press enter when searching or going to the next field.</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>Open the keyboard\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>Close the keyboard\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>Touch \u0026 hold a key for options\n</b></font><font size="3">\n</font>Access punctuation and accents.</string>
+
+    <!-- appears above image showing how to access keyboard settings -->
+    <string name="keyboard_settings"><font size="17"><b>Keyboard settings\n</b></font><font size="3">\n</font>Touch \u0026 hold the <b>\?123\</b> key.</string>
+
+    <!-- popular web domains for the locale - most popular, displayed on the keyboard -->
+    <string name="popular_domain_0">".com"</string>
+    <!-- popular web domains for the locale - item 1, displayed in the popup -->
+    <string name="popular_domain_1">".net"</string>
+    <!-- popular web domains for the locale - item 2, displayed in the popup -->
+    <string name="popular_domain_2">".org"</string>
+    <!-- popular web domains for the locale - item 3, displayed in the popup -->
+    <string name="popular_domain_3">".gov"</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">Input method</string>
+    
+    <!-- Title for input language selection screen -->
+    <string name="language_selection_title">Input languages</string>
+    <!-- Title summary for input language selection screen -->
+    <string name="language_selection_summary">Slide finger on spacebar to change language</string>
+    
+    <!-- Add to dictionary hint -->
+    <string name="hint_add_to_dictionary">\u2190 Tap again to save</string>
+    
+    <!-- Inform the user that a particular language has an available dictionary -->
+    <string name="has_dictionary">Dictionary available</string>
+</resources>
diff --git a/java/res/xml-de/kbd_qwerty.xml b/java/res/xml-de/kbd_qwerty.xml
new file mode 100755
index 0000000..4e57c60
--- /dev/null
+++ b/java/res/xml-de/kbd_qwerty.xml
@@ -0,0 +1,189 @@
+<?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"/>
+        <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_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="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_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: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="\@"/>
+        <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-fr/kbd_qwerty.xml b/java/res/xml-fr/kbd_qwerty.xml
new file mode 100644
index 0000000..1b20c04
--- /dev/null
+++ b/java/res/xml-fr/kbd_qwerty.xml
@@ -0,0 +1,193 @@
+<?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="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_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="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_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_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="\@"/>
+        <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-ru/kbd_qwerty.xml b/java/res/xml-ru/kbd_qwerty.xml
new file mode 100755
index 0000000..45d355b
--- /dev/null
+++ b/java/res/xml-ru/kbd_qwerty.xml
@@ -0,0 +1,174 @@
+<?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="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="е"
+                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: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="э"
+                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: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/azerty.xml b/java/res/xml/azerty.xml
new file mode 100644
index 0000000..614aa49
--- /dev/null
+++ b/java/res/xml/azerty.xml
@@ -0,0 +1,114 @@
+<?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="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_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="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: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:rowEdgeFlags="bottom">
+        <Key android:codes="-3" android:keyIcon="@drawable/sym_keyboard_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_keyboard_space" 
+                android:iconPreview="@drawable/sym_keyboard_feedback_space"
+                android:keyWidth="30%p" android:isRepeatable="true"/>
+        <Key android:codes="46,44" android:keyLabel=". ," 
+                android:popupKeyboard="@xml/popup_punctuation" android:keyWidth="15%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>
+</Keyboard>
+    
\ No newline at end of file
diff --git a/java/res/xml/kbd_alpha.xml b/java/res/xml/kbd_alpha.xml
new file mode 100644
index 0000000..4e8af33
--- /dev/null
+++ b/java/res/xml/kbd_alpha.xml
@@ -0,0 +1,106 @@
+<?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: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_keyboard_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_keyboard_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_keyboard_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_keyboard_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_keyboard_return" 
+                android:iconPreview="@drawable/sym_keyboard_feedback_return"
+                android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+    </Row>
+</Keyboard>
\ No newline at end of file
diff --git a/java/res/xml/kbd_extension.xml b/java/res/xml/kbd_extension.xml
new file mode 100755
index 0000000..c64f666
--- /dev/null
+++ b/java/res/xml/kbd_extension.xml
@@ -0,0 +1,61 @@
+<?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 android:rowEdgeFlags="top">
+        <Key android:keyLabel="!" android:keyEdgeFlags="left"/>
+        <Key android:keyLabel="\@"/>
+        <Key android:keyLabel="\#"/>        
+        <Key android:keyLabel="&amp;"/>
+        <Key android:keyLabel="-"/>
+        <Key android:keyLabel="\'"/>
+        <Key android:keyLabel=":"/>
+        <Key android:keyLabel="&quot;"/>
+        <Key android:keyLabel="/"/>
+        <Key android:keyLabel="\?" android:keyEdgeFlags="right"
+        />
+    </Row>
+
+    <Row android:rowEdgeFlags="bottom">
+        <Key android:codes="49" android:keyLabel="1" android:keyEdgeFlags="left"
+        />
+        <Key android:codes="50" android:keyLabel="2"
+        />
+        <Key android:codes="51" android:keyLabel="3"
+        />
+        <Key android:codes="52" android:keyLabel="4"
+        />
+        <Key android:codes="53" android:keyLabel="5"
+        />
+        <Key android:codes="54" android:keyLabel="6"/>
+        <Key android:codes="55" android:keyLabel="7"
+        />
+        <Key android:codes="56" android:keyLabel="8"/>
+        <Key android:codes="57" android:keyLabel="9"/>
+        <Key android:codes="48" android:keyLabel="0" 
+                android:keyEdgeFlags="right"/>
+    </Row>    
+</Keyboard>
diff --git a/java/res/xml/kbd_phone.xml b/java/res/xml/kbd_phone.xml
new file mode 100755
index 0000000..d2bcdac
--- /dev/null
+++ b/java/res/xml/kbd_phone.xml
@@ -0,0 +1,67 @@
+<?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="26.67%p"
+    android:horizontalGap="0px"
+    android:verticalGap="0px"
+    android:keyHeight="@dimen/key_height"
+    >
+
+    <Row>
+        <Key android:codes="49" android:keyIcon="@drawable/sym_keyboard_num1" android:keyEdgeFlags="left"/>
+        <Key android:codes="50" android:keyIcon="@drawable/sym_keyboard_num2"/>
+        <Key android:codes="51" android:keyIcon="@drawable/sym_keyboard_num3"/>
+        <Key android:keyLabel="-" android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row>
+        <Key android:codes="52" android:keyIcon="@drawable/sym_keyboard_num4" android:keyEdgeFlags="left"/>
+        <Key android:codes="53" android:keyIcon="@drawable/sym_keyboard_num5"/>
+        <Key android:codes="54" android:keyIcon="@drawable/sym_keyboard_num6"/>
+        <Key android:keyLabel="." android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+    </Row>
+    
+    <Row>
+        <Key android:codes="55" android:keyIcon="@drawable/sym_keyboard_num7" android:keyEdgeFlags="left"/>
+        <Key android:codes="56" android:keyIcon="@drawable/sym_keyboard_num8"/>
+        <Key android:codes="57" android:keyIcon="@drawable/sym_keyboard_num9"/>
+        <Key android:codes="-5" android:keyIcon="@drawable/sym_keyboard_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_keyboard_numalt"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="_"
+                android:iconPreview="@drawable/sym_keyboard_feedback_numalt"/>
+
+        <Key android:codes="48" android:keyIcon="@drawable/sym_keyboard_num0"/>
+
+        <Key android:codes="32" android:keyIcon="@drawable/sym_keyboard_space" 
+                android:iconPreview="@drawable/sym_keyboard_feedback_space"
+                android:isRepeatable="true"/>
+        <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return"
+                android:keyWidth="20%p"
+                android:keyEdgeFlags="right"/>
+    </Row>
+    
+</Keyboard>
\ No newline at end of file
diff --git a/java/res/xml/kbd_phone_symbols.xml b/java/res/xml/kbd_phone_symbols.xml
new file mode 100755
index 0000000..9604664
--- /dev/null
+++ b/java/res/xml/kbd_phone_symbols.xml
@@ -0,0 +1,70 @@
+<?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="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_keyboard_numstar"
+                android:keyEdgeFlags="left"/>
+        <!-- Wait is a semicolon. -->
+        <Key android:codes="59" android:keyLabel="Wait"/>
+        <Key android:codes="35" android:keyIcon="@drawable/sym_keyboard_numpound"/>
+        <Key android:codes="-5" android:keyIcon="@drawable/sym_keyboard_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_keyboard_space" 
+                android:iconPreview="@drawable/sym_keyboard_feedback_space"
+                android:isRepeatable="true"/>
+        <Key android:codes="10" android:keyIcon="@drawable/sym_keyboard_return"
+                android:keyWidth="20%p"
+                android:keyEdgeFlags="right"/>
+    </Row>
+    
+</Keyboard>
\ No newline at end of file
diff --git a/java/res/xml/kbd_popup_template.xml b/java/res/xml/kbd_popup_template.xml
new file mode 100644
index 0000000..aca4693
--- /dev/null
+++ b/java/res/xml/kbd_popup_template.xml
@@ -0,0 +1,27 @@
+<?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"
+    >
+</Keyboard>
diff --git a/java/res/xml/kbd_qwerty.xml b/java/res/xml/kbd_qwerty.xml
new file mode 100755
index 0000000..1a9ea5c
--- /dev/null
+++ b/java/res/xml/kbd_qwerty.xml
@@ -0,0 +1,208 @@
+<?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_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="@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_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="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="\@"/>
+        <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_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/kbd_symbols.xml b/java/res/xml/kbd_symbols.xml
new file mode 100755
index 0000000..ab5792f
--- /dev/null
+++ b/java/res/xml/kbd_symbols.xml
@@ -0,0 +1,141 @@
+<?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="49" android:keyLabel="1" android:keyEdgeFlags="left"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="¹½⅓¼⅛"
+        />
+        <Key android:codes="50" android:keyLabel="2"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="²⅔"
+        />
+        <Key android:codes="51" android:keyLabel="3"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="³¾⅜"
+        />
+        <Key android:codes="52" android:keyLabel="4"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="⁴"
+        />
+        <Key android:codes="53" android:keyLabel="5"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="⅝"
+        />
+        <Key android:codes="54" android:keyLabel="6"/>
+        <Key android:codes="55" android:keyLabel="7"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="⅞"
+        />
+        <Key android:codes="56" android:keyLabel="8"/>
+        <Key android:codes="57" android:keyLabel="9"/>
+        <Key android:codes="48" android:keyLabel="0" 
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="ⁿ∅"        
+                android:keyEdgeFlags="right"/>
+    </Row>
+    
+    <Row>
+        <Key android:codes="64" android:keyLabel="\@" android:keyEdgeFlags="left"/>
+        <Key android:codes="35" android:keyLabel="\#"/>
+        <Key android:codes="36" android:keyLabel="$"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="¢£€¥₣₤₱"
+        />        
+        <Key android:codes="37" android:keyLabel="%"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="‰"
+        />
+        <Key android:codes="38" android:keyLabel="&amp;"/>
+        <Key android:codes="42" android:keyLabel="*"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="†‡★"
+        />
+        <Key android:codes="45" android:keyLabel="-"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="_–—"
+        />
+        <Key android:keyLabel="+"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="±"
+        />
+        <Key android:codes="40" android:keyLabel="("
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="[{&lt;"
+        />
+        <Key android:codes="41" android:keyLabel=")" android:keyEdgeFlags="right"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="]}&gt;"
+        />
+    </Row>
+    
+    <Row>
+        <Key android:codes="-1" android:keyLabel="@string/label_alt_key"
+                android:keyWidth="15%p" android:isModifier="true"
+                android:isSticky="true" android:keyEdgeFlags="left"/>
+        <Key android:codes="33" android:keyLabel="!"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="¡"
+        />
+        <Key android:codes="34" android:keyLabel="&quot;"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="“”«»˝"
+        />
+        <Key android:codes="39" android:keyLabel="\'"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="‘’"
+        />
+        <Key android:codes="58" android:keyLabel=":"/>
+        <Key android:codes="59" android:keyLabel=";"/>
+        <Key android:codes="47" android:keyLabel="/" />
+        <Key android:codes="63" android:keyLabel="\?"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="¿"
+        />
+        <Key android:codes="-5" android:keyIcon="@drawable/sym_keyboard_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_keyboard_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_keyboard_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
new file mode 100755
index 0000000..09b5c3f
--- /dev/null
+++ b/java/res/xml/kbd_symbols_shift.xml
@@ -0,0 +1,104 @@
+<?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: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="π"/>
+        <Key android:keyLabel="÷"/>
+        <Key android:keyLabel="×"/>
+        <Key android:keyLabel="{"/>
+        <Key android:keyLabel="}" android:keyEdgeFlags="right"/>
+    </Row>
+    
+    <Row>
+        <Key android:codes="9" android:keyLabel="\u21E5" android:keyEdgeFlags="left"/>
+        <Key android:keyLabel="£"/>
+        <Key android:keyLabel="¢"/>
+        <Key android:keyLabel="€"/>
+        <Key android:keyLabel="°"/>
+        <Key android:keyLabel="^"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="↑↓←→"
+        />
+        <Key android:keyLabel="_"/>
+        <Key android:keyLabel="="
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="≠≈∞"
+        />
+        <Key android:keyLabel="["/>
+        <Key android:keyLabel="]" android:keyEdgeFlags="right"/>
+    </Row>
+    
+    <Row>
+        <Key android:codes="-1" android:keyLabel="@string/label_alt_key"
+                android:keyWidth="15%p" android:isModifier="true"
+                android:isSticky="true" android:keyEdgeFlags="left"/>
+        <Key android:keyLabel="™"/>
+        <Key android:keyLabel="®"/>
+        <Key android:keyLabel="©"/>
+        <Key android:keyLabel="¶"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="§"
+        />
+        <Key android:keyLabel="\\"/>
+        <Key android:keyLabel="&lt;" 
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="≤«‹"
+        />
+        <Key android:keyLabel="&gt;"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="≥»›"
+        />
+        <Key android:codes="-5" android:keyIcon="@drawable/sym_keyboard_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_keyboard_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_keyboard_return"
+                android:keyWidth="20%p" android:keyEdgeFlags="right"
+                android:iconPreview="@drawable/sym_keyboard_feedback_return"
+                />
+    </Row>
+</Keyboard>
diff --git a/java/res/xml/language_prefs.xml b/java/res/xml/language_prefs.xml
new file mode 100644
index 0000000..b7a4c07
--- /dev/null
+++ b/java/res/xml/language_prefs.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+        android:title="@string/language_selection_title">
+</PreferenceScreen>
diff --git a/java/res/xml/method.xml b/java/res/xml/method.xml
new file mode 100644
index 0000000..195691b
--- /dev/null
+++ b/java/res/xml/method.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/**
+ * Copyright (c) 2008, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); 
+ * you may not use this file except in compliance with the License. 
+ * You may obtain a copy of the License at 
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0 
+ *
+ * Unless required by applicable law or agreed to in writing, software 
+ * distributed under the License is distributed on an "AS IS" BASIS, 
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
+ * See the License for the specific language governing permissions and 
+ * limitations under the License.
+ */
+-->
+
+<!-- The attributes in this XML file provide configuration information -->
+<!-- for the Input Method Manager. -->
+
+<input-method xmlns:android="http://schemas.android.com/apk/res/android"
+        android:settingsActivity="com.android.inputmethod.latin.LatinIMESettings"
+        android:isDefault="true"
+/>
diff --git a/java/res/xml/popup_domains.xml b/java/res/xml/popup_domains.xml
new file mode 100644
index 0000000..b733fe2
--- /dev/null
+++ b/java/res/xml/popup_domains.xml
@@ -0,0 +1,38 @@
+<?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="15%p"
+    android:horizontalGap="0px"
+    android:verticalGap="0px"
+    android:keyHeight="@dimen/key_height"
+    >
+
+    <Row android:rowEdgeFlags="top|bottom">
+        <Key android:keyLabel="@string/popular_domain_1"
+            android:keyOutputText="@string/popular_domain_1" android:keyEdgeFlags="left" />
+        <Key android:keyLabel="@string/popular_domain_2"
+            android:keyOutputText="@string/popular_domain_2"/>
+        <Key android:keyLabel="@string/popular_domain_3"
+            android:keyOutputText="@string/popular_domain_3"/>
+        <Key android:keyLabel="@string/popular_domain_4"
+            android:keyOutputText="@string/popular_domain_4" android:keyEdgeFlags="right" />
+    </Row>
+</Keyboard>
diff --git a/java/res/xml/popup_punctuation.xml b/java/res/xml/popup_punctuation.xml
new file mode 100644
index 0000000..a29fb52
--- /dev/null
+++ b/java/res/xml/popup_punctuation.xml
@@ -0,0 +1,46 @@
+<?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 android:rowEdgeFlags="top">
+        <Key android:keyLabel=":" android:keyEdgeFlags="left" />
+        <Key android:keyLabel="/" />
+        <Key android:keyLabel="&amp;" />
+        <Key android:keyLabel="(" />
+        <Key android:keyLabel=")" />
+        <Key android:keyLabel="-" />
+        <Key android:keyLabel="+" android:keyEdgeFlags="right" />
+    </Row>
+    <Row android:rowEdgeFlags="bottom">
+        <Key android:keyLabel=";" android:keyEdgeFlags="left" />
+        <Key android:keyLabel="," />
+        <Key android:keyLabel="\@" />
+        <Key android:keyLabel="\'" />
+        <Key android:keyLabel="&quot;" />
+        <Key android:keyLabel="\?" />
+        <Key android:keyLabel="!" android:keyEdgeFlags="right" />
+    </Row>
+</Keyboard>
diff --git a/java/res/xml/popup_smileys.xml b/java/res/xml/popup_smileys.xml
new file mode 100644
index 0000000..5663fef
--- /dev/null
+++ b/java/res/xml/popup_smileys.xml
@@ -0,0 +1,49 @@
+<?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="15%p"
+    android:horizontalGap="0px"
+    android:verticalGap="0px"
+    android:keyHeight="@dimen/key_height"
+    >
+
+    <Row android:rowEdgeFlags="top">
+        <Key android:keyLabel=":-)" android:keyOutputText=":-) " android:keyEdgeFlags="left" />
+        <Key android:keyLabel=":-(" android:keyOutputText=":-( "/>
+        <Key android:keyLabel=";-)" android:keyOutputText=";-) "/>
+        <Key android:keyLabel=":-P" android:keyOutputText=":-P "/>
+        <Key android:keyLabel="=-O" android:keyOutputText="=-O " android:keyEdgeFlags="right" />
+    </Row>
+    <Row>
+        <Key android:keyLabel=":-*" android:keyOutputText=":-* " android:keyEdgeFlags="left" />
+        <Key android:keyLabel=":O" android:keyOutputText=":O "/>
+        <Key android:keyLabel="B-)" android:keyOutputText="B-) "/>
+        <Key android:keyLabel=":-$" android:keyOutputText=":-$ "/>
+        <Key android:keyLabel=":-!" android:keyOutputText=":-! " android:keyEdgeFlags="right" />
+    </Row>
+    <Row android:rowEdgeFlags="bottom">
+        <Key android:keyLabel=":-[" android:keyOutputText=":-[ " android:keyEdgeFlags="left" />
+        <Key android:keyLabel="O:-)" android:keyOutputText="O:-) "/>
+        <Key android:keyLabel=":-\\" android:keyOutputText=":-\\ "/>
+        <Key android:keyLabel=":'(" android:keyOutputText=":'( "/>
+        <Key android:keyLabel=":-D" android:keyOutputText=":-D " android:keyEdgeFlags="right" />
+    </Row>
+</Keyboard>
diff --git a/java/res/xml/prefs.xml b/java/res/xml/prefs.xml
new file mode 100644
index 0000000..535b63f
--- /dev/null
+++ b/java/res/xml/prefs.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+  
+          http://www.apache.org/licenses/LICENSE-2.0
+  
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
+        android:title="@string/english_ime_settings"
+        android:key="english_ime_settings">
+
+    <CheckBoxPreference
+            android:key="vibrate_on"
+            android:title="@string/vibrate_on_keypress"
+            android:persistent="true"
+            />
+
+    <CheckBoxPreference
+            android:key="sound_on"
+            android:title="@string/sound_on_keypress"
+            android:persistent="true" 
+            />
+
+    <CheckBoxPreference
+            android:key="auto_cap"
+            android:title="@string/auto_cap"
+            android:persistent="true"
+            android:defaultValue="true"
+            />
+
+    <ListPreference
+            android:key="voice_mode"
+            android:title="@string/voice_input"
+            android:persistent="true"
+            android:entryValues="@array/voice_input_modes_values"
+            android:entries="@array/voice_input_modes"
+            android:defaultValue="@string/voice_mode_main"
+            />
+
+    <PreferenceScreen
+            android:title="@string/language_selection_title"
+            android:summary="@string/language_selection_summary">
+        <intent
+                android:action="com.android.inputmethod.latin.INPUT_LANGUAGE_SELECTION"/>
+    </PreferenceScreen>
+
+    <PreferenceCategory
+            android:title="@string/prediction_category"
+            android:key="prediction_settings">
+
+        <CheckBoxPreference
+            android:key="quick_fixes"
+            android:title="@string/quick_fixes"
+            android:summary="@string/quick_fixes_summary"
+            android:persistent="true"
+            android:defaultValue="true"
+            />
+
+        <CheckBoxPreference
+            android:key="show_suggestions"
+            android:title="@string/show_suggestions"
+            android:summary="@string/show_suggestions_summary"
+            android:persistent="true"
+            android:defaultValue="true"
+            />
+
+        <CheckBoxPreference
+            android:key="auto_complete"
+            android:title="@string/auto_complete"
+            android:summary="@string/auto_complete_summary"
+            android:persistent="true" 
+            android:defaultValue="@bool/enable_autocorrect"
+            android:dependency="show_suggestions"
+            />
+            
+    </PreferenceCategory>            
+</PreferenceScreen>
diff --git a/java/src/com/android/inputmethod/latin/AutoDictionary.java b/java/src/com/android/inputmethod/latin/AutoDictionary.java
new file mode 100644
index 0000000..3d76dc3
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/AutoDictionary.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2010 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.latin;
+
+import java.util.HashMap;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.database.sqlite.SQLiteQueryBuilder;
+import android.provider.BaseColumns;
+import android.provider.UserDictionary.Words;
+import android.util.Log;
+
+/**
+ * Stores new words temporarily until they are promoted to the user dictionary
+ * for longevity. Words in the auto dictionary are used to determine if it's ok
+ * to accept a word that's not in the main or user dictionary. Using a new word
+ * repeatedly will promote it to the user dictionary.
+ */
+public class AutoDictionary extends ExpandableDictionary {
+    // Weight added to a user picking a new word from the suggestion strip
+    static final int FREQUENCY_FOR_PICKED = 3;
+    // Weight added to a user typing a new word that doesn't get corrected (or is reverted)
+    static final int FREQUENCY_FOR_TYPED = 1;
+    // A word that is frequently typed and gets promoted to the user dictionary, uses this
+    // frequency.
+    static final int FREQUENCY_FOR_AUTO_ADD = 250;
+    // If the user touches a typed word 2 times or more, it will become valid.
+    private static final int VALIDITY_THRESHOLD = 2 * FREQUENCY_FOR_PICKED;
+    // If the user touches a typed word 4 times or more, it will be added to the user dict.
+    private static final int PROMOTION_THRESHOLD = 4 * FREQUENCY_FOR_PICKED;
+
+    private LatinIME mIme;
+    // Locale for which this auto dictionary is storing words
+    private String mLocale;
+
+    private static final String DATABASE_NAME = "auto_dict.db";
+    private static final int DATABASE_VERSION = 1;
+
+    // These are the columns in the dictionary
+    // TODO: Consume less space by using a unique id for locale instead of the whole
+    // 2-5 character string.
+    private static final String COLUMN_ID = BaseColumns._ID;
+    private static final String COLUMN_WORD = "word";
+    private static final String COLUMN_FREQUENCY = "freq";
+    private static final String COLUMN_LOCALE = "locale";
+
+    /** Sort by descending order of frequency. */
+    public static final String DEFAULT_SORT_ORDER = COLUMN_FREQUENCY + " DESC";
+
+    /** Name of the words table in the auto_dict.db */
+    private static final String AUTODICT_TABLE_NAME = "words";
+
+    private static HashMap<String, String> sDictProjectionMap;
+
+    static {
+        sDictProjectionMap = new HashMap<String, String>();
+        sDictProjectionMap.put(COLUMN_ID, COLUMN_ID);
+        sDictProjectionMap.put(COLUMN_WORD, COLUMN_WORD);
+        sDictProjectionMap.put(COLUMN_FREQUENCY, COLUMN_FREQUENCY);
+        sDictProjectionMap.put(COLUMN_LOCALE, COLUMN_LOCALE);
+    }
+
+    private DatabaseHelper mOpenHelper;
+
+    public AutoDictionary(Context context, LatinIME ime, String locale) {
+        super(context);
+        mIme = ime;
+        mLocale = locale;
+        mOpenHelper = new DatabaseHelper(getContext());
+        if (mLocale != null && mLocale.length() > 1) {
+            loadDictionary();
+        }
+    }
+
+    @Override
+    public boolean isValidWord(CharSequence word) {
+        final int frequency = getWordFrequency(word);
+        return frequency >= VALIDITY_THRESHOLD;
+    }
+
+    public void close() {
+        mOpenHelper.close();
+    }
+
+    private void loadDictionary() {
+        // Load the words that correspond to the current input locale
+        Cursor cursor = query(COLUMN_LOCALE + "=?", new String[] { mLocale });
+        if (cursor.moveToFirst()) {
+            int wordIndex = cursor.getColumnIndex(COLUMN_WORD);
+            int frequencyIndex = cursor.getColumnIndex(COLUMN_FREQUENCY);
+            while (!cursor.isAfterLast()) {
+                String word = cursor.getString(wordIndex);
+                int frequency = cursor.getInt(frequencyIndex);
+                // Safeguard against adding really long words. Stack may overflow due
+                // to recursive lookup
+                if (word.length() < getMaxWordLength()) {
+                    super.addWord(word, frequency);
+                }
+                cursor.moveToNext();
+            }
+        }
+        cursor.close();
+    }
+
+    @Override
+    public void addWord(String word, int addFrequency) {
+        final int length = word.length();
+        // Don't add very short or very long words.
+        if (length < 2 || length > getMaxWordLength()) return;
+        if (mIme.getCurrentWord().isAutoCapitalized()) {
+            // Remove caps before adding
+            word = Character.toLowerCase(word.charAt(0)) + word.substring(1);
+        }
+        int freq = getWordFrequency(word);
+        freq = freq < 0 ? addFrequency : freq + addFrequency;
+        super.addWord(word, freq);
+        if (freq >= PROMOTION_THRESHOLD) {
+            mIme.promoteToUserDictionary(word, FREQUENCY_FOR_AUTO_ADD);
+            // Delete the word (for input locale) from the auto dictionary db, as it
+            // is now in the user dictionary provider.
+            delete(COLUMN_WORD + "=? AND " + COLUMN_LOCALE + "=?",
+                    new String[] { word, mLocale });
+        } else {
+            update(word, freq, mLocale);
+        }
+    }
+
+    /**
+     * This class helps open, create, and upgrade the database file.
+     */
+    private static class DatabaseHelper extends SQLiteOpenHelper {
+
+        DatabaseHelper(Context context) {
+            super(context, DATABASE_NAME, null, DATABASE_VERSION);
+        }
+
+        @Override
+        public void onCreate(SQLiteDatabase db) {
+            db.execSQL("CREATE TABLE " + AUTODICT_TABLE_NAME + " ("
+                    + COLUMN_ID + " INTEGER PRIMARY KEY,"
+                    + COLUMN_WORD + " TEXT,"
+                    + COLUMN_FREQUENCY + " INTEGER,"
+                    + COLUMN_LOCALE + " TEXT"
+                    + ");");
+        }
+
+        @Override
+        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+            Log.w("AutoDictionary", "Upgrading database from version " + oldVersion + " to "
+                    + newVersion + ", which will destroy all old data");
+            db.execSQL("DROP TABLE IF EXISTS " + AUTODICT_TABLE_NAME);
+            onCreate(db);
+        }
+    }
+
+    private Cursor query(String selection, String[] selectionArgs) {
+        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+        qb.setTables(AUTODICT_TABLE_NAME);
+        qb.setProjectionMap(sDictProjectionMap);
+
+        // Get the database and run the query
+        SQLiteDatabase db = mOpenHelper.getReadableDatabase();
+        Cursor c = qb.query(db, null, selection, selectionArgs, null, null,
+                DEFAULT_SORT_ORDER);
+        return c;
+    }
+
+    private boolean insert(ContentValues values) {
+        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+        long rowId = db.insert(AUTODICT_TABLE_NAME, Words.WORD, values);
+        if (rowId > 0) {
+            return true;
+        }
+        return false;
+    }
+
+    private int delete(String where, String[] whereArgs) {
+        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+        int count = db.delete(AUTODICT_TABLE_NAME, where, whereArgs);
+        return count;
+    }
+
+    private int update(String word, int frequency, String locale) {
+        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
+        long count = db.delete(AUTODICT_TABLE_NAME, COLUMN_WORD + "=? AND " + COLUMN_LOCALE + "=?",
+                new String[] { word, locale });
+        count = db.insert(AUTODICT_TABLE_NAME, null,
+                getContentValues(word, frequency, locale));
+        return (int) count;
+    }
+
+    private ContentValues getContentValues(String word, int frequency, String locale) {
+        ContentValues values = new ContentValues(4);
+        values.put(COLUMN_WORD, word);
+        values.put(COLUMN_FREQUENCY, frequency);
+        values.put(COLUMN_LOCALE, locale);
+        return values;
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
new file mode 100644
index 0000000..43f4c4c
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2008-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.latin;
+
+import java.util.Arrays;
+
+import android.content.Context;
+import android.content.res.AssetManager;
+import android.util.Log;
+
+/**
+ * Implements a static, compacted, binary dictionary of standard words.
+ */
+public class BinaryDictionary extends Dictionary {
+
+    public static final int MAX_WORD_LENGTH = 48;
+    private static final int MAX_ALTERNATIVES = 16;
+    private static final int MAX_WORDS = 16;
+
+    private static final int TYPED_LETTER_MULTIPLIER = 2;
+    private static final boolean ENABLE_MISSED_CHARACTERS = true;
+
+    private int mNativeDict;
+    private int mDictLength; // This value is set from native code, don't change the name!!!!
+    private int[] mInputCodes = new int[MAX_WORD_LENGTH * MAX_ALTERNATIVES];
+    private char[] mOutputChars = new char[MAX_WORD_LENGTH * MAX_WORDS];
+    private int[] mFrequencies = new int[MAX_WORDS];
+
+    static {
+        try {
+            System.loadLibrary("jni_latinime");
+        } catch (UnsatisfiedLinkError ule) {
+            Log.e("BinaryDictionary", "Could not load native library jni_latinime");
+        }
+    }
+
+    /**
+     * Create a dictionary from a raw resource file
+     * @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) {
+            loadDictionary(context, resId);
+        }
+    }
+
+    private native int openNative(AssetManager am, String resourcePath, 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);
+    }
+
+    @Override
+    public void getWords(final WordComposer codes, final WordCallback callback,
+            int[] nextLettersFrequencies) {
+        final int codesSize = codes.size();
+        // Wont deal with really long words.
+        if (codesSize > MAX_WORD_LENGTH - 1) return;
+        
+        Arrays.fill(mInputCodes, -1);
+        for (int i = 0; i < codesSize; i++) {
+            int[] alternatives = codes.getCodesAt(i);
+            System.arraycopy(alternatives, 0, mInputCodes, i * MAX_ALTERNATIVES,
+                    Math.min(alternatives.length, MAX_ALTERNATIVES));
+        }
+        Arrays.fill(mOutputChars, (char) 0);
+        Arrays.fill(mFrequencies, 0);
+
+        int count = getSuggestionsNative(mNativeDict, mInputCodes, codesSize,
+                mOutputChars, mFrequencies,
+                MAX_WORD_LENGTH, MAX_WORDS, MAX_ALTERNATIVES, -1,
+                nextLettersFrequencies,
+                nextLettersFrequencies != null ? nextLettersFrequencies.length : 0);
+
+        // If there aren't sufficient suggestions, search for words by allowing wild cards at
+        // the different character positions. This feature is not ready for prime-time as we need
+        // to figure out the best ranking for such words compared to proximity corrections and
+        // completions.
+        if (ENABLE_MISSED_CHARACTERS && count < 5) {
+            for (int skip = 0; skip < codesSize; skip++) {
+                int tempCount = getSuggestionsNative(mNativeDict, mInputCodes, codesSize,
+                        mOutputChars, mFrequencies,
+                        MAX_WORD_LENGTH, MAX_WORDS, MAX_ALTERNATIVES, skip,
+                        null, 0);
+                count = Math.max(count, tempCount);
+                if (tempCount > 0) break;
+            }
+        }
+
+        for (int j = 0; j < count; j++) {
+            if (mFrequencies[j] < 1) break;
+            int start = j * MAX_WORD_LENGTH;
+            int len = 0;
+            while (mOutputChars[start + len] != 0) {
+                len++;
+            }
+            if (len > 0) {
+                callback.addWord(mOutputChars, start, len, mFrequencies[j]);
+            }
+        }
+    }
+
+    @Override
+    public boolean isValidWord(CharSequence word) {
+        if (word == null) return false;
+        char[] chars = word.toString().toCharArray();
+        return isValidWordNative(mNativeDict, chars, chars.length);
+    }
+
+    public int getSize() {
+        return mDictLength; // This value is initialized on the call to openNative()
+    }
+
+    @Override
+    public synchronized void close() {
+        if (mNativeDict != 0) {
+            closeNative(mNativeDict);
+            mNativeDict = 0;
+        }
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        close();
+        super.finalize();
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/CandidateView.java b/java/src/com/android/inputmethod/latin/CandidateView.java
new file mode 100755
index 0000000..0c0373b
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/CandidateView.java
@@ -0,0 +1,533 @@
+/*
+ * Copyright (C) 2008-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.latin;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.Typeface;
+import android.graphics.Paint.Align;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.view.GestureDetector;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.PopupWindow;
+import android.widget.TextView;
+
+public class CandidateView extends View {
+
+    private static final int OUT_OF_BOUNDS = -1;
+    private static final List<CharSequence> EMPTY_LIST = new ArrayList<CharSequence>();
+
+    private LatinIME mService;
+    private List<CharSequence> mSuggestions = EMPTY_LIST;
+    private boolean mShowingCompletions;
+    private CharSequence mSelectedString;
+    private int mSelectedIndex;
+    private int mTouchX = OUT_OF_BOUNDS;
+    private Drawable mSelectionHighlight;
+    private boolean mTypedWordValid;
+    
+    private boolean mHaveMinimalSuggestion;
+    
+    private Rect mBgPadding;
+
+    private TextView mPreviewText;
+    private PopupWindow mPreviewPopup;
+    private int mCurrentWordIndex;
+    private Drawable mDivider;
+    
+    private static final int MAX_SUGGESTIONS = 32;
+    private static final int SCROLL_PIXELS = 20;
+    
+    private static final int MSG_REMOVE_PREVIEW = 1;
+    private static final int MSG_REMOVE_THROUGH_PREVIEW = 2;
+    
+    private int[] mWordWidth = new int[MAX_SUGGESTIONS];
+    private int[] mWordX = new int[MAX_SUGGESTIONS];
+    private int mPopupPreviewX;
+    private int mPopupPreviewY;
+
+    private static final int X_GAP = 10;
+    
+    private int mColorNormal;
+    private int mColorRecommended;
+    private int mColorOther;
+    private Paint mPaint;
+    private int mDescent;
+    private boolean mScrolled;
+    private boolean mShowingAddToDictionary;
+    private CharSequence mWordToAddToDictionary;
+    private CharSequence mAddToDictionaryHint;
+
+    private int mTargetScrollX;
+
+    private int mMinTouchableWidth;
+
+    private int mTotalWidth;
+    
+    private GestureDetector mGestureDetector;
+
+    Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_REMOVE_PREVIEW:
+                    mPreviewText.setVisibility(GONE);
+                    break;
+                case MSG_REMOVE_THROUGH_PREVIEW:
+                    mPreviewText.setVisibility(GONE);
+                    if (mTouchX != OUT_OF_BOUNDS) {
+                        removeHighlight();
+                    }
+                    break;
+            }
+            
+        }
+    };
+
+    /**
+     * Construct a CandidateView for showing suggested words for completion.
+     * @param context
+     * @param attrs
+     */
+    public CandidateView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mSelectionHighlight = context.getResources().getDrawable(
+                R.drawable.list_selector_background_pressed);
+
+        LayoutInflater inflate =
+            (LayoutInflater) context
+                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        Resources res = context.getResources();
+        mPreviewPopup = new PopupWindow(context);
+        mPreviewText = (TextView) inflate.inflate(R.layout.candidate_preview, null);
+        mPreviewPopup.setWindowLayoutMode(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
+        mPreviewPopup.setContentView(mPreviewText);
+        mPreviewPopup.setBackgroundDrawable(null);
+        mColorNormal = res.getColor(R.color.candidate_normal);
+        mColorRecommended = res.getColor(R.color.candidate_recommended);
+        mColorOther = res.getColor(R.color.candidate_other);
+        mDivider = res.getDrawable(R.drawable.keyboard_suggest_strip_divider);
+        mAddToDictionaryHint = res.getString(R.string.hint_add_to_dictionary);
+
+        mPaint = new Paint();
+        mPaint.setColor(mColorNormal);
+        mPaint.setAntiAlias(true);
+        mPaint.setTextSize(mPreviewText.getTextSize());
+        mPaint.setStrokeWidth(0);
+        mPaint.setTextAlign(Align.CENTER);
+        mDescent = (int) mPaint.descent();
+        // 80 pixels for a 160dpi device would mean half an inch
+        mMinTouchableWidth = (int) (getResources().getDisplayMetrics().density * 50);
+        
+        mGestureDetector = new GestureDetector(new GestureDetector.SimpleOnGestureListener() {
+            @Override
+            public void onLongPress(MotionEvent me) {
+                if (mSuggestions.size() > 0) {
+                    if (me.getX() + getScrollX() < mWordWidth[0] && getScrollX() < 10) {
+                        longPressFirstWord();
+                    }
+                }
+            }
+            
+            @Override
+            public boolean onScroll(MotionEvent e1, MotionEvent e2,
+                    float distanceX, float distanceY) {
+                final int width = getWidth();
+                mScrolled = true;
+                int scrollX = getScrollX();
+                scrollX += (int) distanceX;
+                if (scrollX < 0) {
+                    scrollX = 0;
+                }
+                if (distanceX > 0 && scrollX + width > mTotalWidth) {                    
+                    scrollX -= (int) distanceX;
+                }
+                mTargetScrollX = scrollX;
+                scrollTo(scrollX, getScrollY());
+                hidePreview();
+                invalidate();
+                return true;
+            }
+        });
+        setHorizontalFadingEdgeEnabled(true);
+        setWillNotDraw(false);
+        setHorizontalScrollBarEnabled(false);
+        setVerticalScrollBarEnabled(false);
+        scrollTo(0, getScrollY());
+    }
+    
+    /**
+     * A connection back to the service to communicate with the text field
+     * @param listener
+     */
+    public void setService(LatinIME listener) {
+        mService = listener;
+    }
+    
+    @Override
+    public int computeHorizontalScrollRange() {
+        return mTotalWidth;
+    }
+
+    /**
+     * If the canvas is null, then only touch calculations are performed to pick the target
+     * candidate.
+     */
+    @Override
+    protected void onDraw(Canvas canvas) {
+        if (canvas != null) {
+            super.onDraw(canvas);
+        }
+        mTotalWidth = 0;
+        if (mSuggestions == null) return;
+        
+        final int height = getHeight();
+        if (mBgPadding == null) {
+            mBgPadding = new Rect(0, 0, 0, 0);
+            if (getBackground() != null) {
+                getBackground().getPadding(mBgPadding);
+            }
+            mDivider.setBounds(0, 0, mDivider.getIntrinsicWidth(),
+                    mDivider.getIntrinsicHeight());
+        }
+        int x = 0;
+        final int count = mSuggestions.size(); 
+        final int width = getWidth();
+        final Rect bgPadding = mBgPadding;
+        final Paint paint = mPaint;
+        final int touchX = mTouchX;
+        final int scrollX = getScrollX();
+        final boolean scrolled = mScrolled;
+        final boolean typedWordValid = mTypedWordValid;
+        final int y = (int) (height + mPaint.getTextSize() - mDescent) / 2;
+
+        for (int i = 0; i < count; i++) {
+            CharSequence suggestion = mSuggestions.get(i);
+            if (suggestion == null) continue;
+            paint.setColor(mColorNormal);
+            if (mHaveMinimalSuggestion 
+                    && ((i == 1 && !typedWordValid) || (i == 0 && typedWordValid))) {
+                paint.setTypeface(Typeface.DEFAULT_BOLD);
+                paint.setColor(mColorRecommended);
+            } else if (i != 0) {
+                paint.setColor(mColorOther);
+            }
+            final int wordWidth;
+            if (mWordWidth[i] != 0) {
+                wordWidth = mWordWidth[i];
+            } else {
+                float textWidth =  paint.measureText(suggestion, 0, suggestion.length());
+                wordWidth = Math.max(mMinTouchableWidth, (int) textWidth + X_GAP * 2);
+                mWordWidth[i] = wordWidth;
+            }
+
+            mWordX[i] = x;
+
+            if (touchX + scrollX >= x && touchX + scrollX < x + wordWidth && !scrolled &&
+                    touchX != OUT_OF_BOUNDS) {
+                if (canvas != null && !mShowingAddToDictionary) {
+                    canvas.translate(x, 0);
+                    mSelectionHighlight.setBounds(0, bgPadding.top, wordWidth, height);
+                    mSelectionHighlight.draw(canvas);
+                    canvas.translate(-x, 0);
+                    showPreview(i, null);
+                }
+                mSelectedString = suggestion;
+                mSelectedIndex = i;
+            }
+
+            if (canvas != null) {
+                canvas.drawText(suggestion, 0, suggestion.length(), x + wordWidth / 2, y, paint);
+                paint.setColor(mColorOther);
+                canvas.translate(x + wordWidth, 0);
+                // Draw a divider unless it's after the hint
+                if (!(mShowingAddToDictionary && i == 1)) {
+                    mDivider.draw(canvas);
+                }
+                canvas.translate(-x - wordWidth, 0);
+            }
+            paint.setTypeface(Typeface.DEFAULT);
+            x += wordWidth;
+        }
+        mTotalWidth = x;
+        if (mTargetScrollX != scrollX) {
+            scrollToTarget();
+        }
+    }
+    
+    private void scrollToTarget() {
+        int scrollX = getScrollX();
+        if (mTargetScrollX > scrollX) {
+            scrollX += SCROLL_PIXELS;
+            if (scrollX >= mTargetScrollX) {
+                scrollX = mTargetScrollX;
+                scrollTo(scrollX, getScrollY());
+                requestLayout();
+            } else {
+                scrollTo(scrollX, getScrollY());
+            }
+        } else {
+            scrollX -= SCROLL_PIXELS;
+            if (scrollX <= mTargetScrollX) {
+                scrollX = mTargetScrollX;
+                scrollTo(scrollX, getScrollY());
+                requestLayout();
+            } else {
+                scrollTo(scrollX, getScrollY());
+            }
+        }
+        invalidate();
+    }
+    
+    public void setSuggestions(List<CharSequence> suggestions, boolean completions,
+            boolean typedWordValid, boolean haveMinimalSuggestion) {
+        clear();
+        if (suggestions != null) {
+            mSuggestions = new ArrayList<CharSequence>(suggestions);
+        }
+        mShowingCompletions = completions;
+        mTypedWordValid = typedWordValid;
+        scrollTo(0, getScrollY());
+        mTargetScrollX = 0;
+        mHaveMinimalSuggestion = haveMinimalSuggestion;
+        // Compute the total width
+        onDraw(null);
+        invalidate();
+        requestLayout();
+    }
+
+    public void showAddToDictionaryHint(CharSequence word) {
+        mWordToAddToDictionary = word;
+        ArrayList<CharSequence> suggestions = new ArrayList<CharSequence>();
+        suggestions.add(word);
+        suggestions.add(mAddToDictionaryHint);
+        setSuggestions(suggestions, false, false, false);
+        mShowingAddToDictionary = true;
+    }
+
+    public void scrollPrev() {
+        int i = 0;
+        final int count = mSuggestions.size();
+        int firstItem = 0; // Actually just before the first item, if at the boundary
+        while (i < count) {
+            if (mWordX[i] < getScrollX() 
+                    && mWordX[i] + mWordWidth[i] >= getScrollX() - 1) {
+                firstItem = i;
+                break;
+            }
+            i++;
+        }
+        int leftEdge = mWordX[firstItem] + mWordWidth[firstItem] - getWidth();
+        if (leftEdge < 0) leftEdge = 0;
+        updateScrollPosition(leftEdge);
+    }
+    
+    public void scrollNext() {
+        int i = 0;
+        int scrollX = getScrollX();
+        int targetX = scrollX;
+        final int count = mSuggestions.size();
+        int rightEdge = scrollX + getWidth();
+        while (i < count) {
+            if (mWordX[i] <= rightEdge &&
+                    mWordX[i] + mWordWidth[i] >= rightEdge) {
+                targetX = Math.min(mWordX[i], mTotalWidth - getWidth());
+                break;
+            }
+            i++;
+        }
+        updateScrollPosition(targetX);
+    }
+
+    private void updateScrollPosition(int targetX) {
+        if (targetX != getScrollX()) {
+            // TODO: Animate
+            mTargetScrollX = targetX;
+            requestLayout();
+            invalidate();
+            mScrolled = true;
+        }
+    }
+    
+    public void clear() {
+        mSuggestions = EMPTY_LIST;
+        mTouchX = OUT_OF_BOUNDS;
+        mSelectedString = null;
+        mSelectedIndex = -1;
+        mShowingAddToDictionary = false;
+        invalidate();
+        Arrays.fill(mWordWidth, 0);
+        Arrays.fill(mWordX, 0);
+        if (mPreviewPopup.isShowing()) {
+            mPreviewPopup.dismiss();
+        }
+    }
+    
+    @Override
+    public boolean onTouchEvent(MotionEvent me) {
+
+        if (mGestureDetector.onTouchEvent(me)) {
+            return true;
+        }
+
+        int action = me.getAction();
+        int x = (int) me.getX();
+        int y = (int) me.getY();
+        mTouchX = x;
+
+        switch (action) {
+        case MotionEvent.ACTION_DOWN:
+            mScrolled = false;
+            invalidate();
+            break;
+        case MotionEvent.ACTION_MOVE:
+            if (y <= 0) {
+                // Fling up!?
+                if (mSelectedString != null) {
+                    if (!mShowingCompletions) {
+                        TextEntryState.acceptedSuggestion(mSuggestions.get(0),
+                                mSelectedString);
+                    }
+                    mService.pickSuggestionManually(mSelectedIndex, mSelectedString);
+                    mSelectedString = null;
+                    mSelectedIndex = -1;
+                }
+            }
+            invalidate();
+            break;
+        case MotionEvent.ACTION_UP:
+            if (!mScrolled) {
+                if (mSelectedString != null) {
+                    if (mShowingAddToDictionary) {
+                        longPressFirstWord();
+                        clear();
+                    } else {
+                        if (!mShowingCompletions) {
+                            TextEntryState.acceptedSuggestion(mSuggestions.get(0),
+                                    mSelectedString);
+                        }
+                        mService.pickSuggestionManually(mSelectedIndex, mSelectedString);
+                    }
+                }
+            }
+            mSelectedString = null;
+            mSelectedIndex = -1;
+            removeHighlight();
+            hidePreview();
+            requestLayout();
+            break;
+        }
+        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;
+        if (mPreviewPopup.isShowing()) {
+            mHandler.sendMessageDelayed(mHandler
+                    .obtainMessage(MSG_REMOVE_PREVIEW), 60);
+        }
+    }
+    
+    private void showPreview(int wordIndex, String altText) {
+        int oldWordIndex = mCurrentWordIndex;
+        mCurrentWordIndex = wordIndex;
+        // If index changed or changing text
+        if (oldWordIndex != mCurrentWordIndex || altText != null) {
+            if (wordIndex == OUT_OF_BOUNDS) {
+                hidePreview();
+            } else {
+                CharSequence word = altText != null? altText : mSuggestions.get(wordIndex);
+                mPreviewText.setText(word);
+                mPreviewText.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 
+                        MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
+                int wordWidth = (int) (mPaint.measureText(word, 0, word.length()) + X_GAP * 2);
+                final int popupWidth = wordWidth
+                        + mPreviewText.getPaddingLeft() + mPreviewText.getPaddingRight();
+                final int popupHeight = mPreviewText.getMeasuredHeight();
+                //mPreviewText.setVisibility(INVISIBLE);
+                mPopupPreviewX = mWordX[wordIndex] - mPreviewText.getPaddingLeft() - getScrollX()
+                        + (mWordWidth[wordIndex] - wordWidth) / 2;
+                mPopupPreviewY = - popupHeight;
+                mHandler.removeMessages(MSG_REMOVE_PREVIEW);
+                int [] offsetInWindow = new int[2];
+                getLocationInWindow(offsetInWindow);
+                if (mPreviewPopup.isShowing()) {
+                    mPreviewPopup.update(mPopupPreviewX, mPopupPreviewY + offsetInWindow[1], 
+                            popupWidth, popupHeight);
+                } else {
+                    mPreviewPopup.setWidth(popupWidth);
+                    mPreviewPopup.setHeight(popupHeight);
+                    mPreviewPopup.showAtLocation(this, Gravity.NO_GRAVITY, mPopupPreviewX, 
+                            mPopupPreviewY + offsetInWindow[1]);
+                }
+                mPreviewText.setVisibility(VISIBLE);
+            }
+        }
+    }
+    
+    private void removeHighlight() {
+        mTouchX = OUT_OF_BOUNDS;
+        invalidate();
+    }
+    
+    private void longPressFirstWord() {
+        CharSequence word = mSuggestions.get(0);
+        if (word.length() < 2) return;
+        if (mService.addWordToDictionary(word.toString())) {
+            showPreview(0, getContext().getResources().getString(R.string.added_word, word));
+        }
+    }
+    
+    @Override
+    public void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        hidePreview();
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/CandidateViewContainer.java b/java/src/com/android/inputmethod/latin/CandidateViewContainer.java
new file mode 100644
index 0000000..e13f273
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/CandidateViewContainer.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2008-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.latin;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnTouchListener;
+import android.widget.LinearLayout;
+
+public class CandidateViewContainer extends LinearLayout implements OnTouchListener {
+
+    private View mButtonLeft;
+    private View mButtonRight;
+    private View mButtonLeftLayout;
+    private View mButtonRightLayout;
+    private CandidateView mCandidates;
+    
+    public CandidateViewContainer(Context screen, AttributeSet attrs) {
+        super(screen, attrs);
+    }
+
+    public void initViews() {
+        if (mCandidates == null) {
+            mButtonLeftLayout = findViewById(R.id.candidate_left_parent);
+            mButtonLeft = findViewById(R.id.candidate_left);
+            if (mButtonLeft != null) {
+                mButtonLeft.setOnTouchListener(this);
+            }
+            mButtonRightLayout = findViewById(R.id.candidate_right_parent);
+            mButtonRight = findViewById(R.id.candidate_right);
+            if (mButtonRight != null) {
+                mButtonRight.setOnTouchListener(this);
+            }
+            mCandidates = (CandidateView) findViewById(R.id.candidates);
+        }
+    }
+
+    @Override
+    public void requestLayout() {
+        if (mCandidates != null) {
+            int availableWidth = mCandidates.getWidth();
+            int neededWidth = mCandidates.computeHorizontalScrollRange();
+            int x = mCandidates.getScrollX();
+            boolean leftVisible = x > 0;
+            boolean rightVisible = x + availableWidth < neededWidth;
+            if (mButtonLeftLayout != null) {
+                mButtonLeftLayout.setVisibility(leftVisible ? VISIBLE : GONE);
+            }
+            if (mButtonRightLayout != null) {
+                mButtonRightLayout.setVisibility(rightVisible ? VISIBLE : GONE);
+            }
+        }
+        super.requestLayout();
+    }
+
+    public boolean onTouch(View v, MotionEvent event) {
+        if (event.getAction() == MotionEvent.ACTION_DOWN) {
+            if (v == mButtonRight) {
+                mCandidates.scrollNext();
+            } else if (v == mButtonLeft) {
+                mCandidates.scrollPrev();
+            }
+        }
+        return false;
+    }
+    
+}
diff --git a/java/src/com/android/inputmethod/latin/ContactsDictionary.java b/java/src/com/android/inputmethod/latin/ContactsDictionary.java
new file mode 100644
index 0000000..f53ebf3
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/ContactsDictionary.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.os.AsyncTask;
+import android.os.SystemClock;
+import android.provider.ContactsContract.Contacts;
+
+public class ContactsDictionary extends ExpandableDictionary {
+
+    private static final String[] PROJECTION = {
+        Contacts._ID,
+        Contacts.DISPLAY_NAME,
+    };
+
+    private static final int INDEX_NAME = 1;
+
+    private ContentObserver mObserver;
+
+    private boolean mRequiresReload;
+
+    private long mLastLoadedContacts;
+
+    private boolean mUpdatingContacts;
+
+    // Use this lock before touching mUpdatingContacts & mRequiresDownload
+    private Object mUpdatingLock = new Object();
+
+    public ContactsDictionary(Context context) {
+        super(context);
+        // 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) {
+                synchronized (mUpdatingLock) {
+                    mRequiresReload = true;
+                }
+            }
+        });
+
+        synchronized (mUpdatingLock) {
+            loadDictionaryAsyncLocked();
+        }
+    }
+
+    public synchronized void close() {
+        if (mObserver != null) {
+            getContext().getContentResolver().unregisterContentObserver(mObserver);
+            mObserver = null;
+        }
+    }
+
+    private synchronized void loadDictionaryAsyncLocked() {
+        long now = SystemClock.uptimeMillis();
+        if (mLastLoadedContacts == 0
+                || now - mLastLoadedContacts > 30 * 60 * 1000 /* 30 minutes */) {
+            if (!mUpdatingContacts) {
+                mUpdatingContacts = true;
+                mRequiresReload = false;
+                new LoadContactsTask().execute();
+            }
+        }
+    }
+
+    @Override
+    public synchronized void getWords(final WordComposer codes, final WordCallback callback,
+            int[] nextLettersFrequencies) {
+        synchronized (mUpdatingLock) {
+            // If we need to update, start off a background task
+            if (mRequiresReload) loadDictionaryAsyncLocked();
+            // Currently updating contacts, don't return any results.
+            if (mUpdatingContacts) return;
+        }
+        super.getWords(codes, callback, nextLettersFrequencies);
+    }
+
+    @Override
+    public synchronized boolean isValidWord(CharSequence word) {
+        synchronized (mUpdatingLock) {
+            // If we need to update, start off a background task
+            if (mRequiresReload) loadDictionaryAsyncLocked();
+            if (mUpdatingContacts) return false;
+        }
+
+        return super.isValidWord(word);
+    }
+
+    private void addWords(Cursor cursor) {
+        clearDictionary();
+
+        final int maxWordLength = getMaxWordLength();
+        if (cursor.moveToFirst()) {
+            while (!cursor.isAfterLast()) {
+                String name = cursor.getString(INDEX_NAME);
+
+                if (name != null) {
+                    int len = name.length();
+
+                    // TODO: Better tokenization for non-Latin writing systems
+                    for (int i = 0; i < len; i++) {
+                        if (Character.isLetter(name.charAt(i))) {
+                            int j;
+                            for (j = i + 1; j < len; j++) {
+                                char c = name.charAt(j);
+
+                                if (!(c == '-' || c == '\'' ||
+                                      Character.isLetter(c))) {
+                                    break;
+                                }
+                            }
+
+                            String word = name.substring(i, j);
+                            i = j - 1;
+
+                            // Safeguard against adding really long words. Stack
+                            // may overflow due to recursion
+                            // Also don't add single letter words, possibly confuses
+                            // capitalization of i.
+                            final int wordLen = word.length();
+                            if (wordLen < maxWordLength && wordLen > 1) {
+                                super.addWord(word, 128);
+                            }
+                        }
+                    }
+                }
+
+                cursor.moveToNext();
+            }
+        }
+        cursor.close();
+    }
+    
+    private class LoadContactsTask extends AsyncTask<Void, Void, Void> {
+        @Override
+        protected Void doInBackground(Void... v) {
+            Cursor cursor = getContext().getContentResolver()
+                    .query(Contacts.CONTENT_URI, PROJECTION, null, null, null);
+            if (cursor != null) {
+                addWords(cursor);
+            }
+            mLastLoadedContacts = SystemClock.uptimeMillis();
+            return null;
+        }
+
+        @Override
+        protected void onPostExecute(Void result) {
+            // TODO Auto-generated method stub
+            synchronized (mUpdatingLock) {
+                mUpdatingContacts = false;
+            }
+            super.onPostExecute(result);
+        }
+        
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/Dictionary.java b/java/src/com/android/inputmethod/latin/Dictionary.java
new file mode 100644
index 0000000..b656d04
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/Dictionary.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2008-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.latin;
+
+/**
+ * Abstract base class for a dictionary that can do a fuzzy search for words based on a set of key
+ * strokes.
+ */
+abstract public class Dictionary {
+    
+    /**
+     * Whether or not to replicate the typed word in the suggested list, even if it's valid.
+     */
+    protected static final boolean INCLUDE_TYPED_WORD_IF_VALID = false;
+    
+    /**
+     * 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;
+    
+    /**
+     * Interface to be implemented by classes requesting words to be fetched from the dictionary.
+     * @see #getWords(WordComposer, WordCallback)
+     */
+    public interface WordCallback {
+        /**
+         * Adds a word to a list of suggestions. The word is expected to be ordered based on
+         * the provided frequency. 
+         * @param word the character array containing the word
+         * @param wordOffset starting offset of the word in the character array
+         * @param wordLength length of valid characters in the character array
+         * @param frequency the frequency of occurence. This is normalized between 1 and 255, but
+         * can exceed those limits
+         * @return true if the word was added, false if no more words are required
+         */
+        boolean addWord(char[] word, int wordOffset, int wordLength, int frequency);
+    }
+
+    /**
+     * Searches for words in the dictionary that match the characters in the composer. Matched 
+     * words are added through the callback object.
+     * @param composer the key sequence to match
+     * @param callback the callback object to send matched words to as possible candidates
+     * @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.
+     * @see WordCallback#addWord(char[], int, int)
+     */
+    abstract public void getWords(final WordComposer composer, final WordCallback callback,
+            int[] nextLettersFrequencies);
+
+    /**
+     * 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
+     */
+    abstract public boolean isValidWord(CharSequence word);
+    
+    /**
+     * Compares the contents of the character array with the typed word and returns true if they
+     * are the same.
+     * @param word the array of characters that make up the word
+     * @param length the number of valid characters in the character array
+     * @param typedWord the word to compare with
+     * @return true if they are the same, false otherwise.
+     */
+    protected boolean same(final char[] word, final int length, final CharSequence typedWord) {
+        if (typedWord.length() != length) {
+            return false;
+        }
+        for (int i = 0; i < length; i++) {
+            if (word[i] != typedWord.charAt(i)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Override to clean up any resources.
+     */
+    public void close() {
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
new file mode 100644
index 0000000..0065937
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
@@ -0,0 +1,465 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin;
+
+import android.content.Context;
+
+/**
+ * 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 {
+    private Context mContext;
+    private char[] mWordBuilder = new char[MAX_WORD_LENGTH];
+    private int mMaxDepth;
+    private int mInputLength;
+    private int[] mNextLettersFrequencies;
+
+    public static final int MAX_WORD_LENGTH = 32;
+    private static final char QUOTE = '\'';
+
+    static class Node {
+        char code;
+        int frequency;
+        boolean terminal;
+        NodeArray children;
+    }
+
+    static class NodeArray {
+        Node[] data;
+        int length = 0;
+        private static final int INCREMENT = 2;
+
+        NodeArray() {
+            data = new Node[INCREMENT];
+        }
+
+        void add(Node n) {
+            if (length + 1 > data.length) {
+                Node[] tempData = new Node[length + INCREMENT];
+                if (length > 0) {
+                    System.arraycopy(data, 0, tempData, 0, length);
+                }
+                data = tempData;
+            }
+            data[length++] = n;
+        }
+    }
+
+    private NodeArray mRoots;
+
+    private int[][] mCodes;
+
+    ExpandableDictionary(Context context) {
+        mContext = context;
+        clearDictionary();
+        mCodes = new int[MAX_WORD_LENGTH][];
+    }
+
+    Context getContext() {
+        return mContext;
+    }
+    
+    int getMaxWordLength() {
+        return MAX_WORD_LENGTH;
+    }
+
+    public void addWord(String word, int frequency) {
+        addWordRec(mRoots, word, 0, frequency);
+    }
+
+    private void addWordRec(NodeArray children, final String word,
+            final int depth, final int frequency) {
+        
+        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;
+            children.add(childNode);
+        }
+        if (wordLength == depth + 1) {
+            // Terminate this word
+            childNode.terminal = true;
+            childNode.frequency = Math.max(frequency, childNode.frequency);
+            if (childNode.frequency > 255) childNode.frequency = 255;
+            return;
+        }
+        if (childNode.children == null) {
+            childNode.children = new NodeArray();
+        }
+        addWordRec(childNode.children, word, depth + 1, frequency);
+    }
+
+    @Override
+    public void getWords(final WordComposer codes, final WordCallback callback,
+            int[] nextLettersFrequencies) {
+        mInputLength = codes.size();
+        mNextLettersFrequencies = nextLettersFrequencies;
+        if (mCodes.length < mInputLength) mCodes = new int[mInputLength][];
+        // Cache the codes so that we don't have to lookup an array list
+        for (int i = 0; i < mInputLength; i++) {
+            mCodes[i] = codes.getCodesAt(i);
+        }
+        mMaxDepth = mInputLength * 3;
+        getWordsRec(mRoots, codes, mWordBuilder, 0, false, 1, 0, -1, callback);
+        for (int i = 0; i < mInputLength; i++) {
+            getWordsRec(mRoots, codes, mWordBuilder, 0, false, 1, 0, i, callback);
+        }
+    }
+
+    @Override
+    public synchronized boolean isValidWord(CharSequence word) {
+        final int freq = getWordFrequencyRec(mRoots, word, 0, word.length());
+        return freq > -1;
+    }
+
+    /**
+     * 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;
+    }
+
+    /**
+     * Recursively traverse the tree for words that match the input. Input consists of
+     * a list of arrays. Each item in the list is one input character position. An input
+     * character is actually an array of multiple possible candidates. This function is not
+     * optimized for speed, assuming that the user dictionary will only be a few hundred words in
+     * size.
+     * @param roots node whose children have to be search for matches
+     * @param codes the input character codes
+     * @param word the word being composed as a possible match
+     * @param depth the depth of traversal - the length of the word being composed thus far
+     * @param completion whether the traversal is now in completion mode - meaning that we've
+     * exhausted the input and we're looking for all possible suffixes.
+     * @param snr current weight of the word being formed
+     * @param inputIndex position in the input characters. This can be off from the depth in 
+     * case we skip over some punctuations such as apostrophe in the traversal. That is, if you type
+     * "wouldve", it could be matching "would've", so the depth will be one more than the
+     * inputIndex
+     * @param callback the callback class for adding a word
+     */
+    protected void getWordsRec(NodeArray roots, final WordComposer codes, final char[] word, 
+            final int depth, boolean completion, int snr, int inputIndex, int skipPos,
+            WordCallback callback) {
+        final int count = roots.length;
+        final int codeSize = mInputLength;
+        // Optimization: Prune out words that are too long compared to how much was typed.
+        if (depth > mMaxDepth) {
+            return;
+        }
+        int[] currentChars = null;
+        if (codeSize <= inputIndex) {
+            completion = true;
+        } else {
+            currentChars = mCodes[inputIndex];
+        }
+
+        for (int i = 0; i < count; i++) {
+            final Node node = roots.data[i];
+            final char c = node.code;
+            final char lowerC = toLowerCase(c);
+            final boolean terminal = node.terminal;
+            final NodeArray children = node.children;
+            final int freq = node.frequency;
+            if (completion) {
+                word[depth] = c;
+                if (terminal) {
+                    if (!callback.addWord(word, 0, depth + 1, freq * snr)) {
+                        return;
+                    }
+                    // Add to frequency of next letters for predictive correction
+                    if (mNextLettersFrequencies != null && depth >= inputIndex && skipPos < 0
+                            && mNextLettersFrequencies.length > word[inputIndex]) {
+                        mNextLettersFrequencies[word[inputIndex]]++;
+                    }
+                }
+                if (children != null) {
+                    getWordsRec(children, codes, word, depth + 1, completion, snr, inputIndex,
+                            skipPos, callback);
+                }
+            } else if ((c == QUOTE && currentChars[0] != QUOTE) || depth == skipPos) {
+                // Skip the ' and continue deeper
+                word[depth] = c;
+                if (children != null) {
+                    getWordsRec(children, codes, word, depth + 1, completion, snr, inputIndex, 
+                            skipPos, callback);
+                }
+            } else {
+                // Don't use alternatives if we're looking for missing characters
+                final int alternativesSize = skipPos >= 0? 1 : currentChars.length;
+                for (int j = 0; j < alternativesSize; j++) {
+                    final int addedAttenuation = (j > 0 ? 1 : 2);
+                    final int currentChar = currentChars[j];
+                    if (currentChar == -1) {
+                        break;
+                    }
+                    if (currentChar == lowerC || currentChar == c) {
+                        word[depth] = c;
+
+                        if (codeSize == inputIndex + 1) {
+                            if (terminal) {
+                                if (INCLUDE_TYPED_WORD_IF_VALID 
+                                        || !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);
+                                }
+                            }
+                            if (children != null) {
+                                getWordsRec(children, codes, word, depth + 1,
+                                        true, snr * addedAttenuation, inputIndex + 1,
+                                        skipPos, callback);
+                            }
+                        } else if (children != null) {
+                            getWordsRec(children, codes, word, depth + 1, 
+                                    false, snr * addedAttenuation, inputIndex + 1,
+                                    skipPos, callback);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    protected void clearDictionary() {
+        mRoots = new NodeArray();
+    }
+
+    static char toLowerCase(char c) {
+        if (c < BASE_CHARS.length) {
+            c = BASE_CHARS[c];
+        }
+        if (c >= 'A' && c <= 'Z') {
+            c = (char) (c | 32);
+        } else if (c > 127) {
+            c = Character.toLowerCase(c);
+        }
+        return c;
+    }
+
+    /**
+     * Table mapping most combined Latin, Greek, and Cyrillic characters
+     * to their base characters.  If c is in range, BASE_CHARS[c] == c
+     * if c is not a combined character, or the base character if it
+     * is combined.
+     */
+    static final char BASE_CHARS[] = {
+        0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 
+        0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 
+        0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 
+        0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, 
+        0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 
+        0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, 
+        0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 
+        0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 
+        0x0040, 0x0041, 0x0042, 0x0043, 0x0044, 0x0045, 0x0046, 0x0047, 
+        0x0048, 0x0049, 0x004a, 0x004b, 0x004c, 0x004d, 0x004e, 0x004f, 
+        0x0050, 0x0051, 0x0052, 0x0053, 0x0054, 0x0055, 0x0056, 0x0057, 
+        0x0058, 0x0059, 0x005a, 0x005b, 0x005c, 0x005d, 0x005e, 0x005f, 
+        0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 
+        0x0068, 0x0069, 0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 
+        0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 
+        0x0078, 0x0079, 0x007a, 0x007b, 0x007c, 0x007d, 0x007e, 0x007f, 
+        0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 
+        0x0088, 0x0089, 0x008a, 0x008b, 0x008c, 0x008d, 0x008e, 0x008f, 
+        0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 
+        0x0098, 0x0099, 0x009a, 0x009b, 0x009c, 0x009d, 0x009e, 0x009f, 
+        0x0020, 0x00a1, 0x00a2, 0x00a3, 0x00a4, 0x00a5, 0x00a6, 0x00a7, 
+        0x0020, 0x00a9, 0x0061, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x0020, 
+        0x00b0, 0x00b1, 0x0032, 0x0033, 0x0020, 0x03bc, 0x00b6, 0x00b7, 
+        0x0020, 0x0031, 0x006f, 0x00bb, 0x0031, 0x0031, 0x0033, 0x00bf, 
+        0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x0041, 0x00c6, 0x0043, 
+        0x0045, 0x0045, 0x0045, 0x0045, 0x0049, 0x0049, 0x0049, 0x0049, 
+        0x00d0, 0x004e, 0x004f, 0x004f, 0x004f, 0x004f, 0x004f, 0x00d7, 
+        0x004f, 0x0055, 0x0055, 0x0055, 0x0055, 0x0059, 0x00de, 0x0073, // Manually changed d8 to 4f
+                                                                        // Manually changed df to 73
+        0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x0061, 0x00e6, 0x0063, 
+        0x0065, 0x0065, 0x0065, 0x0065, 0x0069, 0x0069, 0x0069, 0x0069, 
+        0x00f0, 0x006e, 0x006f, 0x006f, 0x006f, 0x006f, 0x006f, 0x00f7, 
+        0x006f, 0x0075, 0x0075, 0x0075, 0x0075, 0x0079, 0x00fe, 0x0079, // Manually changed f8 to 6f
+        0x0041, 0x0061, 0x0041, 0x0061, 0x0041, 0x0061, 0x0043, 0x0063, 
+        0x0043, 0x0063, 0x0043, 0x0063, 0x0043, 0x0063, 0x0044, 0x0064, 
+        0x0110, 0x0111, 0x0045, 0x0065, 0x0045, 0x0065, 0x0045, 0x0065, 
+        0x0045, 0x0065, 0x0045, 0x0065, 0x0047, 0x0067, 0x0047, 0x0067, 
+        0x0047, 0x0067, 0x0047, 0x0067, 0x0048, 0x0068, 0x0126, 0x0127, 
+        0x0049, 0x0069, 0x0049, 0x0069, 0x0049, 0x0069, 0x0049, 0x0069, 
+        0x0049, 0x0131, 0x0049, 0x0069, 0x004a, 0x006a, 0x004b, 0x006b, 
+        0x0138, 0x004c, 0x006c, 0x004c, 0x006c, 0x004c, 0x006c, 0x004c, 
+        0x006c, 0x0141, 0x0142, 0x004e, 0x006e, 0x004e, 0x006e, 0x004e, 
+        0x006e, 0x02bc, 0x014a, 0x014b, 0x004f, 0x006f, 0x004f, 0x006f, 
+        0x004f, 0x006f, 0x0152, 0x0153, 0x0052, 0x0072, 0x0052, 0x0072, 
+        0x0052, 0x0072, 0x0053, 0x0073, 0x0053, 0x0073, 0x0053, 0x0073, 
+        0x0053, 0x0073, 0x0054, 0x0074, 0x0054, 0x0074, 0x0166, 0x0167, 
+        0x0055, 0x0075, 0x0055, 0x0075, 0x0055, 0x0075, 0x0055, 0x0075, 
+        0x0055, 0x0075, 0x0055, 0x0075, 0x0057, 0x0077, 0x0059, 0x0079, 
+        0x0059, 0x005a, 0x007a, 0x005a, 0x007a, 0x005a, 0x007a, 0x0073, 
+        0x0180, 0x0181, 0x0182, 0x0183, 0x0184, 0x0185, 0x0186, 0x0187, 
+        0x0188, 0x0189, 0x018a, 0x018b, 0x018c, 0x018d, 0x018e, 0x018f, 
+        0x0190, 0x0191, 0x0192, 0x0193, 0x0194, 0x0195, 0x0196, 0x0197, 
+        0x0198, 0x0199, 0x019a, 0x019b, 0x019c, 0x019d, 0x019e, 0x019f, 
+        0x004f, 0x006f, 0x01a2, 0x01a3, 0x01a4, 0x01a5, 0x01a6, 0x01a7, 
+        0x01a8, 0x01a9, 0x01aa, 0x01ab, 0x01ac, 0x01ad, 0x01ae, 0x0055, 
+        0x0075, 0x01b1, 0x01b2, 0x01b3, 0x01b4, 0x01b5, 0x01b6, 0x01b7, 
+        0x01b8, 0x01b9, 0x01ba, 0x01bb, 0x01bc, 0x01bd, 0x01be, 0x01bf, 
+        0x01c0, 0x01c1, 0x01c2, 0x01c3, 0x0044, 0x0044, 0x0064, 0x004c, 
+        0x004c, 0x006c, 0x004e, 0x004e, 0x006e, 0x0041, 0x0061, 0x0049, 
+        0x0069, 0x004f, 0x006f, 0x0055, 0x0075, 0x00dc, 0x00fc, 0x00dc, 
+        0x00fc, 0x00dc, 0x00fc, 0x00dc, 0x00fc, 0x01dd, 0x00c4, 0x00e4, 
+        0x0226, 0x0227, 0x00c6, 0x00e6, 0x01e4, 0x01e5, 0x0047, 0x0067, 
+        0x004b, 0x006b, 0x004f, 0x006f, 0x01ea, 0x01eb, 0x01b7, 0x0292, 
+        0x006a, 0x0044, 0x0044, 0x0064, 0x0047, 0x0067, 0x01f6, 0x01f7, 
+        0x004e, 0x006e, 0x00c5, 0x00e5, 0x00c6, 0x00e6, 0x00d8, 0x00f8, 
+        0x0041, 0x0061, 0x0041, 0x0061, 0x0045, 0x0065, 0x0045, 0x0065, 
+        0x0049, 0x0069, 0x0049, 0x0069, 0x004f, 0x006f, 0x004f, 0x006f, 
+        0x0052, 0x0072, 0x0052, 0x0072, 0x0055, 0x0075, 0x0055, 0x0075, 
+        0x0053, 0x0073, 0x0054, 0x0074, 0x021c, 0x021d, 0x0048, 0x0068, 
+        0x0220, 0x0221, 0x0222, 0x0223, 0x0224, 0x0225, 0x0041, 0x0061, 
+        0x0045, 0x0065, 0x00d6, 0x00f6, 0x00d5, 0x00f5, 0x004f, 0x006f, 
+        0x022e, 0x022f, 0x0059, 0x0079, 0x0234, 0x0235, 0x0236, 0x0237, 
+        0x0238, 0x0239, 0x023a, 0x023b, 0x023c, 0x023d, 0x023e, 0x023f, 
+        0x0240, 0x0241, 0x0242, 0x0243, 0x0244, 0x0245, 0x0246, 0x0247, 
+        0x0248, 0x0249, 0x024a, 0x024b, 0x024c, 0x024d, 0x024e, 0x024f, 
+        0x0250, 0x0251, 0x0252, 0x0253, 0x0254, 0x0255, 0x0256, 0x0257, 
+        0x0258, 0x0259, 0x025a, 0x025b, 0x025c, 0x025d, 0x025e, 0x025f, 
+        0x0260, 0x0261, 0x0262, 0x0263, 0x0264, 0x0265, 0x0266, 0x0267, 
+        0x0268, 0x0269, 0x026a, 0x026b, 0x026c, 0x026d, 0x026e, 0x026f, 
+        0x0270, 0x0271, 0x0272, 0x0273, 0x0274, 0x0275, 0x0276, 0x0277, 
+        0x0278, 0x0279, 0x027a, 0x027b, 0x027c, 0x027d, 0x027e, 0x027f, 
+        0x0280, 0x0281, 0x0282, 0x0283, 0x0284, 0x0285, 0x0286, 0x0287, 
+        0x0288, 0x0289, 0x028a, 0x028b, 0x028c, 0x028d, 0x028e, 0x028f, 
+        0x0290, 0x0291, 0x0292, 0x0293, 0x0294, 0x0295, 0x0296, 0x0297, 
+        0x0298, 0x0299, 0x029a, 0x029b, 0x029c, 0x029d, 0x029e, 0x029f, 
+        0x02a0, 0x02a1, 0x02a2, 0x02a3, 0x02a4, 0x02a5, 0x02a6, 0x02a7, 
+        0x02a8, 0x02a9, 0x02aa, 0x02ab, 0x02ac, 0x02ad, 0x02ae, 0x02af, 
+        0x0068, 0x0266, 0x006a, 0x0072, 0x0279, 0x027b, 0x0281, 0x0077, 
+        0x0079, 0x02b9, 0x02ba, 0x02bb, 0x02bc, 0x02bd, 0x02be, 0x02bf, 
+        0x02c0, 0x02c1, 0x02c2, 0x02c3, 0x02c4, 0x02c5, 0x02c6, 0x02c7, 
+        0x02c8, 0x02c9, 0x02ca, 0x02cb, 0x02cc, 0x02cd, 0x02ce, 0x02cf, 
+        0x02d0, 0x02d1, 0x02d2, 0x02d3, 0x02d4, 0x02d5, 0x02d6, 0x02d7, 
+        0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x0020, 0x02de, 0x02df, 
+        0x0263, 0x006c, 0x0073, 0x0078, 0x0295, 0x02e5, 0x02e6, 0x02e7, 
+        0x02e8, 0x02e9, 0x02ea, 0x02eb, 0x02ec, 0x02ed, 0x02ee, 0x02ef, 
+        0x02f0, 0x02f1, 0x02f2, 0x02f3, 0x02f4, 0x02f5, 0x02f6, 0x02f7, 
+        0x02f8, 0x02f9, 0x02fa, 0x02fb, 0x02fc, 0x02fd, 0x02fe, 0x02ff, 
+        0x0300, 0x0301, 0x0302, 0x0303, 0x0304, 0x0305, 0x0306, 0x0307, 
+        0x0308, 0x0309, 0x030a, 0x030b, 0x030c, 0x030d, 0x030e, 0x030f, 
+        0x0310, 0x0311, 0x0312, 0x0313, 0x0314, 0x0315, 0x0316, 0x0317, 
+        0x0318, 0x0319, 0x031a, 0x031b, 0x031c, 0x031d, 0x031e, 0x031f, 
+        0x0320, 0x0321, 0x0322, 0x0323, 0x0324, 0x0325, 0x0326, 0x0327, 
+        0x0328, 0x0329, 0x032a, 0x032b, 0x032c, 0x032d, 0x032e, 0x032f, 
+        0x0330, 0x0331, 0x0332, 0x0333, 0x0334, 0x0335, 0x0336, 0x0337, 
+        0x0338, 0x0339, 0x033a, 0x033b, 0x033c, 0x033d, 0x033e, 0x033f, 
+        0x0300, 0x0301, 0x0342, 0x0313, 0x0308, 0x0345, 0x0346, 0x0347, 
+        0x0348, 0x0349, 0x034a, 0x034b, 0x034c, 0x034d, 0x034e, 0x034f, 
+        0x0350, 0x0351, 0x0352, 0x0353, 0x0354, 0x0355, 0x0356, 0x0357, 
+        0x0358, 0x0359, 0x035a, 0x035b, 0x035c, 0x035d, 0x035e, 0x035f, 
+        0x0360, 0x0361, 0x0362, 0x0363, 0x0364, 0x0365, 0x0366, 0x0367, 
+        0x0368, 0x0369, 0x036a, 0x036b, 0x036c, 0x036d, 0x036e, 0x036f, 
+        0x0370, 0x0371, 0x0372, 0x0373, 0x02b9, 0x0375, 0x0376, 0x0377, 
+        0x0378, 0x0379, 0x0020, 0x037b, 0x037c, 0x037d, 0x003b, 0x037f, 
+        0x0380, 0x0381, 0x0382, 0x0383, 0x0020, 0x00a8, 0x0391, 0x00b7, 
+        0x0395, 0x0397, 0x0399, 0x038b, 0x039f, 0x038d, 0x03a5, 0x03a9, 
+        0x03ca, 0x0391, 0x0392, 0x0393, 0x0394, 0x0395, 0x0396, 0x0397, 
+        0x0398, 0x0399, 0x039a, 0x039b, 0x039c, 0x039d, 0x039e, 0x039f, 
+        0x03a0, 0x03a1, 0x03a2, 0x03a3, 0x03a4, 0x03a5, 0x03a6, 0x03a7, 
+        0x03a8, 0x03a9, 0x0399, 0x03a5, 0x03b1, 0x03b5, 0x03b7, 0x03b9, 
+        0x03cb, 0x03b1, 0x03b2, 0x03b3, 0x03b4, 0x03b5, 0x03b6, 0x03b7, 
+        0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf, 
+        0x03c0, 0x03c1, 0x03c2, 0x03c3, 0x03c4, 0x03c5, 0x03c6, 0x03c7, 
+        0x03c8, 0x03c9, 0x03b9, 0x03c5, 0x03bf, 0x03c5, 0x03c9, 0x03cf, 
+        0x03b2, 0x03b8, 0x03a5, 0x03d2, 0x03d2, 0x03c6, 0x03c0, 0x03d7, 
+        0x03d8, 0x03d9, 0x03da, 0x03db, 0x03dc, 0x03dd, 0x03de, 0x03df, 
+        0x03e0, 0x03e1, 0x03e2, 0x03e3, 0x03e4, 0x03e5, 0x03e6, 0x03e7, 
+        0x03e8, 0x03e9, 0x03ea, 0x03eb, 0x03ec, 0x03ed, 0x03ee, 0x03ef, 
+        0x03ba, 0x03c1, 0x03c2, 0x03f3, 0x0398, 0x03b5, 0x03f6, 0x03f7, 
+        0x03f8, 0x03a3, 0x03fa, 0x03fb, 0x03fc, 0x03fd, 0x03fe, 0x03ff, 
+        0x0415, 0x0415, 0x0402, 0x0413, 0x0404, 0x0405, 0x0406, 0x0406, 
+        0x0408, 0x0409, 0x040a, 0x040b, 0x041a, 0x0418, 0x0423, 0x040f, 
+        0x0410, 0x0411, 0x0412, 0x0413, 0x0414, 0x0415, 0x0416, 0x0417, 
+        0x0418, 0x0418, 0x041a, 0x041b, 0x041c, 0x041d, 0x041e, 0x041f, 
+        0x0420, 0x0421, 0x0422, 0x0423, 0x0424, 0x0425, 0x0426, 0x0427, 
+        0x0428, 0x0429, 0x042a, 0x042b, 0x042c, 0x042d, 0x042e, 0x042f, 
+        0x0430, 0x0431, 0x0432, 0x0433, 0x0434, 0x0435, 0x0436, 0x0437, 
+        0x0438, 0x0438, 0x043a, 0x043b, 0x043c, 0x043d, 0x043e, 0x043f, 
+        0x0440, 0x0441, 0x0442, 0x0443, 0x0444, 0x0445, 0x0446, 0x0447, 
+        0x0448, 0x0449, 0x044a, 0x044b, 0x044c, 0x044d, 0x044e, 0x044f, 
+        0x0435, 0x0435, 0x0452, 0x0433, 0x0454, 0x0455, 0x0456, 0x0456, 
+        0x0458, 0x0459, 0x045a, 0x045b, 0x043a, 0x0438, 0x0443, 0x045f, 
+        0x0460, 0x0461, 0x0462, 0x0463, 0x0464, 0x0465, 0x0466, 0x0467, 
+        0x0468, 0x0469, 0x046a, 0x046b, 0x046c, 0x046d, 0x046e, 0x046f, 
+        0x0470, 0x0471, 0x0472, 0x0473, 0x0474, 0x0475, 0x0474, 0x0475, 
+        0x0478, 0x0479, 0x047a, 0x047b, 0x047c, 0x047d, 0x047e, 0x047f, 
+        0x0480, 0x0481, 0x0482, 0x0483, 0x0484, 0x0485, 0x0486, 0x0487, 
+        0x0488, 0x0489, 0x048a, 0x048b, 0x048c, 0x048d, 0x048e, 0x048f, 
+        0x0490, 0x0491, 0x0492, 0x0493, 0x0494, 0x0495, 0x0496, 0x0497, 
+        0x0498, 0x0499, 0x049a, 0x049b, 0x049c, 0x049d, 0x049e, 0x049f, 
+        0x04a0, 0x04a1, 0x04a2, 0x04a3, 0x04a4, 0x04a5, 0x04a6, 0x04a7, 
+        0x04a8, 0x04a9, 0x04aa, 0x04ab, 0x04ac, 0x04ad, 0x04ae, 0x04af, 
+        0x04b0, 0x04b1, 0x04b2, 0x04b3, 0x04b4, 0x04b5, 0x04b6, 0x04b7, 
+        0x04b8, 0x04b9, 0x04ba, 0x04bb, 0x04bc, 0x04bd, 0x04be, 0x04bf, 
+        0x04c0, 0x0416, 0x0436, 0x04c3, 0x04c4, 0x04c5, 0x04c6, 0x04c7, 
+        0x04c8, 0x04c9, 0x04ca, 0x04cb, 0x04cc, 0x04cd, 0x04ce, 0x04cf, 
+        0x0410, 0x0430, 0x0410, 0x0430, 0x04d4, 0x04d5, 0x0415, 0x0435, 
+        0x04d8, 0x04d9, 0x04d8, 0x04d9, 0x0416, 0x0436, 0x0417, 0x0437, 
+        0x04e0, 0x04e1, 0x0418, 0x0438, 0x0418, 0x0438, 0x041e, 0x043e, 
+        0x04e8, 0x04e9, 0x04e8, 0x04e9, 0x042d, 0x044d, 0x0423, 0x0443, 
+        0x0423, 0x0443, 0x0423, 0x0443, 0x0427, 0x0447, 0x04f6, 0x04f7, 
+        0x042b, 0x044b, 0x04fa, 0x04fb, 0x04fc, 0x04fd, 0x04fe, 0x04ff, 
+    };
+
+    // generated with:
+    // cat UnicodeData.txt | perl -e 'while (<>) { @foo = split(/;/); $foo[5] =~ s/<.*> //; $base[hex($foo[0])] = hex($foo[5]);} for ($i = 0; $i < 0x500; $i += 8) { for ($j = $i; $j < $i + 8; $j++) { printf("0x%04x, ", $base[$j] ? $base[$j] : $j)}; print "\n"; }'
+
+}
diff --git a/java/src/com/android/inputmethod/latin/Hints.java b/java/src/com/android/inputmethod/latin/Hints.java
new file mode 100644
index 0000000..689c8d8
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/Hints.java
@@ -0,0 +1,188 @@
+/*
+ * 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.latin;
+
+import com.android.inputmethod.voice.SettingsUtil;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.preference.PreferenceManager;
+import android.view.inputmethod.InputConnection;
+
+import java.util.Calendar;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Logic to determine when to display hints on usage to the user.
+ */
+public class Hints {
+    public interface Display {
+        public void showHint(int viewResource);
+    }
+
+    private static final String TAG = "Hints";
+    private static final String PREF_VOICE_HINT_NUM_UNIQUE_DAYS_SHOWN =
+            "voice_hint_num_unique_days_shown";
+    private static final String PREF_VOICE_HINT_LAST_TIME_SHOWN =
+            "voice_hint_last_time_shown";
+    private static final String PREF_VOICE_INPUT_LAST_TIME_USED =
+            "voice_input_last_time_used";
+    private static final String PREF_VOICE_PUNCTUATION_HINT_VIEW_COUNT =
+            "voice_punctuation_hint_view_count";
+    private static final int DEFAULT_SWIPE_HINT_MAX_DAYS_TO_SHOW = 7;
+    private static final int DEFAULT_PUNCTUATION_HINT_MAX_DISPLAYS = 7;
+
+    private Context mContext;
+    private Display mDisplay;
+    private boolean mVoiceResultContainedPunctuation;
+    private int mSwipeHintMaxDaysToShow;
+    private int mPunctuationHintMaxDisplays;
+
+    // Only show punctuation hint if voice result did not contain punctuation.
+    static final Map<CharSequence, String> SPEAKABLE_PUNCTUATION
+            = new HashMap<CharSequence, String>();
+    static {
+        SPEAKABLE_PUNCTUATION.put(",", "comma");
+        SPEAKABLE_PUNCTUATION.put(".", "period");
+        SPEAKABLE_PUNCTUATION.put("?", "question mark");
+    }
+
+    public Hints(Context context, Display display) {
+        mContext = context;
+        mDisplay = display;
+
+        ContentResolver cr = mContext.getContentResolver();
+        mSwipeHintMaxDaysToShow = SettingsUtil.getSettingsInt(
+                cr,
+                SettingsUtil.LATIN_IME_VOICE_INPUT_SWIPE_HINT_MAX_DAYS,
+                DEFAULT_SWIPE_HINT_MAX_DAYS_TO_SHOW);
+        mPunctuationHintMaxDisplays = SettingsUtil.getSettingsInt(
+                cr,
+                SettingsUtil.LATIN_IME_VOICE_INPUT_PUNCTUATION_HINT_MAX_DISPLAYS,
+                DEFAULT_PUNCTUATION_HINT_MAX_DISPLAYS);
+    }
+
+    public boolean showSwipeHintIfNecessary(boolean fieldRecommended) {
+        if (fieldRecommended && shouldShowSwipeHint()) {
+            showHint(R.layout.voice_swipe_hint);
+            return true;
+        }
+
+        return false;
+    }
+
+    public boolean showPunctuationHintIfNecessary(InputConnection ic) {
+        if (!mVoiceResultContainedPunctuation
+                && ic != null
+                && getAndIncrementPref(PREF_VOICE_PUNCTUATION_HINT_VIEW_COUNT)
+                        < mPunctuationHintMaxDisplays) {
+            CharSequence charBeforeCursor = ic.getTextBeforeCursor(1, 0);
+            if (SPEAKABLE_PUNCTUATION.containsKey(charBeforeCursor)) {
+                showHint(R.layout.voice_punctuation_hint);
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    public void registerVoiceResult(String text) {
+        // Update the current time as the last time voice input was used.
+        SharedPreferences.Editor editor =
+                PreferenceManager.getDefaultSharedPreferences(mContext).edit();
+        editor.putLong(PREF_VOICE_INPUT_LAST_TIME_USED, System.currentTimeMillis());
+        editor.commit();
+
+        mVoiceResultContainedPunctuation = false;
+        for (CharSequence s : SPEAKABLE_PUNCTUATION.keySet()) {
+            if (text.indexOf(s.toString()) >= 0) {
+                mVoiceResultContainedPunctuation = true;
+                break;
+            }
+        }
+    }
+
+    private boolean shouldShowSwipeHint() {
+        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
+
+        int numUniqueDaysShown = sp.getInt(PREF_VOICE_HINT_NUM_UNIQUE_DAYS_SHOWN, 0);
+
+        // If we've already shown the hint for enough days, we'll return false.
+        if (numUniqueDaysShown < mSwipeHintMaxDaysToShow) {
+
+            long lastTimeVoiceWasUsed = sp.getLong(PREF_VOICE_INPUT_LAST_TIME_USED, 0);
+
+            // If the user has used voice today, we'll return false. (We don't show the hint on
+            // any day that the user has already used voice.)
+            if (!isFromToday(lastTimeVoiceWasUsed)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Determines whether the provided time is from some time today (i.e., this day, month,
+     * and year).
+     */
+    private boolean isFromToday(long timeInMillis) {
+        if (timeInMillis == 0) return false;
+
+        Calendar today = Calendar.getInstance();
+        today.setTimeInMillis(System.currentTimeMillis());
+
+        Calendar timestamp = Calendar.getInstance();
+        timestamp.setTimeInMillis(timeInMillis);
+
+        return (today.get(Calendar.YEAR) == timestamp.get(Calendar.YEAR) &&
+                today.get(Calendar.DAY_OF_MONTH) == timestamp.get(Calendar.DAY_OF_MONTH) &&
+                today.get(Calendar.MONTH) == timestamp.get(Calendar.MONTH));
+    }
+
+    private void showHint(int hintViewResource) {
+        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
+
+        int numUniqueDaysShown = sp.getInt(PREF_VOICE_HINT_NUM_UNIQUE_DAYS_SHOWN, 0);
+        long lastTimeHintWasShown = sp.getLong(PREF_VOICE_HINT_LAST_TIME_SHOWN, 0);
+
+        // If this is the first time the hint is being shown today, increase the saved values
+        // to represent that. We don't need to increase the last time the hint was shown unless
+        // it is a different day from the current value.
+        if (!isFromToday(lastTimeHintWasShown)) {
+            SharedPreferences.Editor editor = sp.edit();
+            editor.putInt(PREF_VOICE_HINT_NUM_UNIQUE_DAYS_SHOWN, numUniqueDaysShown + 1);
+            editor.putLong(PREF_VOICE_HINT_LAST_TIME_SHOWN, System.currentTimeMillis());
+            editor.commit();
+        }
+
+        if (mDisplay != null) {
+            mDisplay.showHint(hintViewResource);
+        }
+    }
+
+    private int getAndIncrementPref(String pref) {
+        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext);
+        int value = sp.getInt(pref, 0);
+        SharedPreferences.Editor editor = sp.edit();
+        editor.putInt(pref, value + 1);
+        editor.commit();
+        return value;
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/InputLanguageSelection.java b/java/src/com/android/inputmethod/latin/InputLanguageSelection.java
new file mode 100644
index 0000000..b1ddb21
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/InputLanguageSelection.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2008-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.latin;
+
+import java.text.Collator;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Locale;
+
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.os.Bundle;
+import android.preference.CheckBoxPreference;
+import android.preference.PreferenceActivity;
+import android.preference.PreferenceGroup;
+import android.preference.PreferenceManager;
+import android.text.TextUtils;
+
+public class InputLanguageSelection extends PreferenceActivity {
+
+    private String mSelectedLanguages;
+    private ArrayList<Loc> mAvailableLanguages = new ArrayList<Loc>();
+    private static final String[] BLACKLIST_LANGUAGES = {
+        "ko", "ja", "zh"
+    };
+
+    private static class Loc implements Comparable {
+        static Collator sCollator = Collator.getInstance();
+
+        String label;
+        Locale locale;
+
+        public Loc(String label, Locale locale) {
+            this.label = label;
+            this.locale = locale;
+        }
+
+        @Override
+        public String toString() {
+            return this.label;
+        }
+
+        public int compareTo(Object o) {
+            return sCollator.compare(this.label, ((Loc) o).label);
+        }
+    }
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        addPreferencesFromResource(R.xml.language_prefs);
+        // Get the settings preferences
+        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
+        mSelectedLanguages = sp.getString(LatinIME.PREF_SELECTED_LANGUAGES, "");
+        String[] languageList = mSelectedLanguages.split(",");
+        mAvailableLanguages = getUniqueLocales();
+        PreferenceGroup parent = getPreferenceScreen();
+        for (int i = 0; i < mAvailableLanguages.size(); i++) {
+            CheckBoxPreference pref = new CheckBoxPreference(this);
+            Locale locale = mAvailableLanguages.get(i).locale;
+            pref.setTitle(LanguageSwitcher.toTitleCase(locale.getDisplayName(locale)));
+            boolean checked = isLocaleIn(locale, languageList);
+            pref.setChecked(checked);
+            parent.addPreference(pref);
+        }
+    }
+
+    private boolean isLocaleIn(Locale locale, String[] list) {
+        String lang = get5Code(locale);
+        for (int i = 0; i < list.length; i++) {
+            if (lang.equalsIgnoreCase(list[i])) return true;
+        }
+        // If it matches the current locale
+        Locale displayLocale = getResources().getConfiguration().locale;
+        if (lang.equalsIgnoreCase(get5Code(displayLocale))) {
+            return true;
+        }
+        return false;
+    }
+
+    private String get5Code(Locale locale) {
+        String country = locale.getCountry();
+        return locale.getLanguage()
+                + (TextUtils.isEmpty(country) ? "" : "_" + country);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        // Save the selected languages
+        String checkedLanguages = "";
+        PreferenceGroup parent = getPreferenceScreen();
+        int count = parent.getPreferenceCount();
+        for (int i = 0; i < count; i++) {
+            CheckBoxPreference pref = (CheckBoxPreference) parent.getPreference(i);
+            if (pref.isChecked()) {
+                Locale locale = mAvailableLanguages.get(i).locale;
+                checkedLanguages += get5Code(locale) + ",";
+            }
+        }
+        if (checkedLanguages.length() < 1) checkedLanguages = null; // Save null
+        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
+        Editor editor = sp.edit();
+        editor.putString(LatinIME.PREF_SELECTED_LANGUAGES, checkedLanguages);
+        editor.commit();
+    }
+
+    ArrayList<Loc> getUniqueLocales() {
+        String[] locales = getAssets().getLocales();
+        Arrays.sort(locales);
+        ArrayList<Loc> uniqueLocales = new ArrayList<Loc>();
+
+        final int origSize = locales.length;
+        Loc[] preprocess = new Loc[origSize];
+        int finalSize = 0;
+        for (int i = 0 ; i < origSize; i++ ) {
+            String s = locales[i];
+            int len = s.length();
+            if (len == 5) {
+                String language = s.substring(0, 2);
+                String country = s.substring(3, 5);
+                Locale l = new Locale(language, country);
+
+                // Exclude languages that are not relevant to LatinIME
+                if (arrayContains(BLACKLIST_LANGUAGES, language)) continue;
+
+                if (finalSize == 0) {
+                    preprocess[finalSize++] =
+                            new Loc(LanguageSwitcher.toTitleCase(l.getDisplayName(l)), l);
+                } else {
+                    // check previous entry:
+                    //  same lang and a country -> upgrade to full name and
+                    //    insert ours with full name
+                    //  diff lang -> insert ours with lang-only name
+                    if (preprocess[finalSize-1].locale.getLanguage().equals(
+                            language)) {
+                        preprocess[finalSize-1].label = LanguageSwitcher.toTitleCase(
+                                preprocess[finalSize-1].locale.getDisplayName());
+                        preprocess[finalSize++] =
+                                new Loc(LanguageSwitcher.toTitleCase(l.getDisplayName()), l);
+                    } else {
+                        String displayName;
+                        if (s.equals("zz_ZZ")) {
+                        } else {
+                            displayName = LanguageSwitcher.toTitleCase(l.getDisplayName(l));
+                            preprocess[finalSize++] = new Loc(displayName, l);
+                        }
+                    }
+                }
+            }
+        }
+        for (int i = 0; i < finalSize ; i++) {
+            uniqueLocales.add(preprocess[i]);
+        }
+        return uniqueLocales;
+    }
+
+    private boolean arrayContains(String[] array, String value) {
+        for (int i = 0; i < array.length; i++) {
+            if (array[i].equalsIgnoreCase(value)) return true;
+        }
+        return false;
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/KeyboardSwitcher.java b/java/src/com/android/inputmethod/latin/KeyboardSwitcher.java
new file mode 100644
index 0000000..438680c
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/KeyboardSwitcher.java
@@ -0,0 +1,351 @@
+/*
+ * Copyright (C) 2008 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.latin;
+
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.inputmethodservice.InputMethodService;
+
+public class KeyboardSwitcher {
+
+    public static final int MODE_TEXT = 1;
+    public static final int MODE_SYMBOLS = 2;
+    public static final int MODE_PHONE = 3;
+    public static final int MODE_URL = 4;
+    public static final int MODE_EMAIL = 5;
+    public static final int MODE_IM = 6;
+    public static final int MODE_WEB = 7;
+    
+    public static final int MODE_TEXT_QWERTY = 0;
+    public static final int MODE_TEXT_ALPHA = 1;
+    public static final int MODE_TEXT_COUNT = 2;
+    
+    public static final int KEYBOARDMODE_NORMAL = R.id.mode_normal;
+    public static final int KEYBOARDMODE_URL = R.id.mode_url;
+    public static final int KEYBOARDMODE_EMAIL = R.id.mode_email;
+    public static final int KEYBOARDMODE_IM = R.id.mode_im;
+    public static final int KEYBOARDMODE_WEB = R.id.mode_webentry;
+
+    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;
+
+    LatinKeyboardView mInputView;
+    private static final int[] ALPHABET_MODES = {
+        KEYBOARDMODE_NORMAL,
+        KEYBOARDMODE_URL,
+        KEYBOARDMODE_EMAIL,
+        KEYBOARDMODE_IM,
+        KEYBOARDMODE_WEB};
+
+    //LatinIME mContext;
+    Context mContext;
+    InputMethodService mInputMethodService;
+    
+    private KeyboardId mSymbolsId;
+    private KeyboardId mSymbolsShiftedId;
+
+    private KeyboardId mCurrentId;
+    private Map<KeyboardId, LatinKeyboard> mKeyboards;
+
+    private int mMode; /** One of the MODE_XXX values */
+    private int mImeOptions;
+    private int mTextMode = MODE_TEXT_QWERTY;
+    private boolean mIsSymbols;
+    private boolean mHasVoice;
+    private boolean mVoiceOnPrimary;
+    private boolean mPreferSymbols;
+    private int mSymbolsModeState = SYMBOLS_MODE_STATE_NONE;
+
+    private int mLastDisplayWidth;
+    private LanguageSwitcher mLanguageSwitcher;
+    private Locale mInputLocale;
+    private boolean mEnableMultipleLanguages;
+
+    KeyboardSwitcher(Context context, InputMethodService ims) {
+        mContext = context;
+        mKeyboards = new HashMap<KeyboardId, LatinKeyboard>();
+        mSymbolsId = new KeyboardId(R.xml.kbd_symbols, false);
+        mSymbolsShiftedId = new KeyboardId(R.xml.kbd_symbols_shift, false);
+        mInputMethodService = ims;
+    }
+
+    /**
+     * Sets the input locale, when there are multiple locales for input.
+     * If no locale switching is required, then the locale should be set to null.
+     * @param locale the current input locale, or null for default locale with no locale 
+     * button.
+     */
+    void setLanguageSwitcher(LanguageSwitcher languageSwitcher) {
+        mLanguageSwitcher = languageSwitcher;
+        mInputLocale = mLanguageSwitcher.getInputLocale();
+        mEnableMultipleLanguages = mLanguageSwitcher.getLocaleCount() > 1;
+    }
+
+    void setInputView(LatinKeyboardView inputView) {
+        mInputView = inputView;
+    }
+    
+    void makeKeyboards(boolean forceCreate) {
+        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 
+        // create the keyboard layouts again at the correct orientation
+        int displayWidth = mInputMethodService.getMaxWidth();
+        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);
+    }
+
+    /**
+     * Represents the parameters necessary to construct a new LatinKeyboard,
+     * which also serve as a unique identifier for each keyboard type.
+     */
+    private static class KeyboardId {
+        public int mXml;
+        public int mKeyboardMode; /** A KEYBOARDMODE_XXX value */
+        public boolean mEnableShiftLock;
+        public boolean mHasVoice;
+
+        public KeyboardId(int xml, int mode, boolean enableShiftLock, boolean hasVoice) {
+            this.mXml = xml;
+            this.mKeyboardMode = mode;
+            this.mEnableShiftLock = enableShiftLock;
+            this.mHasVoice = hasVoice;
+        }
+
+        public KeyboardId(int xml, boolean hasVoice) {
+            this(xml, 0, false, hasVoice);
+        }
+
+        public boolean equals(Object other) {
+            return other instanceof KeyboardId && equals((KeyboardId) other);
+        }
+
+        public boolean equals(KeyboardId other) {
+          return other.mXml == this.mXml
+              && other.mKeyboardMode == this.mKeyboardMode
+              && other.mEnableShiftLock == this.mEnableShiftLock;
+        }
+
+        public int hashCode() {
+            return (mXml + 1) * (mKeyboardMode + 1) * (mEnableShiftLock ? 2 : 1)
+                    * (mHasVoice ? 4 : 8);
+        }
+    }
+
+    void setVoiceMode(boolean enableVoice, boolean voiceOnPrimary) {
+        if (enableVoice != mHasVoice || voiceOnPrimary != mVoiceOnPrimary) {
+            mKeyboards.clear();
+        }
+        mHasVoice = enableVoice;
+        mVoiceOnPrimary = voiceOnPrimary;
+        setKeyboardMode(mMode, mImeOptions, mHasVoice,
+                mIsSymbols);
+    }
+
+    boolean hasVoiceButton(boolean isSymbols) {
+        return mHasVoice && (isSymbols != mVoiceOnPrimary);
+    }
+
+    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);
+    }
+
+    void setKeyboardMode(int mode, int imeOptions, boolean enableVoice, boolean isSymbols) {
+        if (mInputView == null) return;
+        mMode = mode;
+        mImeOptions = imeOptions;
+        if (enableVoice != mHasVoice) {
+            setVoiceMode(mHasVoice, mVoiceOnPrimary);
+        }
+        mIsSymbols = isSymbols;
+
+        mInputView.setPreviewEnabled(true);
+        KeyboardId id = getKeyboardId(mode, imeOptions, isSymbols);
+
+        LatinKeyboard keyboard = getKeyboard(id);
+
+        if (mode == MODE_PHONE) {
+            mInputView.setPhoneKeyboard(keyboard);
+            mInputView.setPreviewEnabled(false);
+        }
+
+        mCurrentId = id;
+        mInputView.setKeyboard(keyboard);
+        keyboard.setShifted(false);
+        keyboard.setShiftLocked(keyboard.isShiftLocked());
+        keyboard.setImeOptions(mContext.getResources(), mMode, imeOptions);
+    }
+
+    private LatinKeyboard getKeyboard(KeyboardId id) {
+        if (!mKeyboards.containsKey(id)) {
+            Resources orig = mContext.getResources();
+            Configuration conf = orig.getConfiguration();
+            Locale saveLocale = conf.locale;
+            conf.locale = mInputLocale;
+            orig.updateConfiguration(conf, null);
+            LatinKeyboard keyboard = new LatinKeyboard(
+                mContext, id.mXml, id.mKeyboardMode);
+            keyboard.setVoiceMode(hasVoiceButton(id.mXml == R.xml.kbd_symbols), mHasVoice);
+            keyboard.setLanguageSwitcher(mLanguageSwitcher);
+            if (id.mKeyboardMode == KEYBOARDMODE_NORMAL
+                    || id.mKeyboardMode == KEYBOARDMODE_URL
+                    || id.mKeyboardMode == KEYBOARDMODE_IM
+                    || id.mKeyboardMode == KEYBOARDMODE_EMAIL
+                    || id.mKeyboardMode == KEYBOARDMODE_WEB
+                    ) {
+                keyboard.setExtension(R.xml.kbd_extension);
+            }
+
+            if (id.mEnableShiftLock) {
+                keyboard.enableShiftLock();
+            }
+            mKeyboards.put(id, keyboard);
+
+            conf.locale = saveLocale;
+            orig.updateConfiguration(conf, null);
+        }
+        return mKeyboards.get(id);
+    }
+
+    private KeyboardId getKeyboardId(int mode, int imeOptions, boolean isSymbols) {
+        boolean hasVoice = hasVoiceButton(isSymbols);
+        if (isSymbols) {
+            return (mode == MODE_PHONE)
+                ? new KeyboardId(R.xml.kbd_phone_symbols, hasVoice)
+                : new KeyboardId(R.xml.kbd_symbols, hasVoice);
+        }
+        switch (mode) {
+            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);
+                }
+                break;
+            case MODE_SYMBOLS:
+                return new KeyboardId(R.xml.kbd_symbols, hasVoice);
+            case MODE_PHONE:
+                return new KeyboardId(R.xml.kbd_phone, hasVoice);
+            case MODE_URL:
+                return new KeyboardId(R.xml.kbd_qwerty, KEYBOARDMODE_URL, true, hasVoice);
+            case MODE_EMAIL:
+                return new KeyboardId(R.xml.kbd_qwerty, KEYBOARDMODE_EMAIL, true, hasVoice);
+            case MODE_IM:
+                return new KeyboardId(R.xml.kbd_qwerty, KEYBOARDMODE_IM, true, hasVoice);
+            case MODE_WEB:
+                return new KeyboardId(R.xml.kbd_qwerty, KEYBOARDMODE_WEB, true, hasVoice);
+        }
+        return null;
+    }
+
+    int getKeyboardMode() {
+        return mMode;
+    }
+    
+    boolean isTextMode() {
+        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;
+    }
+
+    boolean isAlphabetMode() {
+        int currentMode = mCurrentId.mKeyboardMode;
+        for (Integer mode : ALPHABET_MODES) {
+            if (currentMode == mode) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    void toggleShift() {
+        if (mCurrentId.equals(mSymbolsId)) {
+            LatinKeyboard symbolsKeyboard = getKeyboard(mSymbolsId);
+            LatinKeyboard symbolsShiftedKeyboard = getKeyboard(mSymbolsShiftedId);
+            symbolsKeyboard.setShifted(true);
+            mCurrentId = mSymbolsShiftedId;
+            mInputView.setKeyboard(symbolsShiftedKeyboard);
+            symbolsShiftedKeyboard.setShifted(true);
+            symbolsShiftedKeyboard.setImeOptions(mContext.getResources(), mMode, mImeOptions);
+        } else if (mCurrentId.equals(mSymbolsShiftedId)) {
+            LatinKeyboard symbolsKeyboard = getKeyboard(mSymbolsId);
+            LatinKeyboard symbolsShiftedKeyboard = getKeyboard(mSymbolsShiftedId);
+            symbolsShiftedKeyboard.setShifted(false);
+            mCurrentId = mSymbolsId;
+            mInputView.setKeyboard(getKeyboard(mSymbolsId));
+            symbolsKeyboard.setShifted(false);
+            symbolsKeyboard.setImeOptions(mContext.getResources(), mMode, mImeOptions);
+        }
+    }
+
+    void toggleSymbols() {
+        setKeyboardMode(mMode, mImeOptions, mHasVoice, !mIsSymbols);
+        if (mIsSymbols && !mPreferSymbols) {
+            mSymbolsModeState = SYMBOLS_MODE_STATE_BEGIN;
+        } else {
+            mSymbolsModeState = SYMBOLS_MODE_STATE_NONE;
+        }
+    }
+
+    /**
+     * Updates state machine to figure out when to automatically switch back to alpha mode.
+     * Returns true if the keyboard needs to switch back 
+     */
+    boolean onKey(int key) {
+        // Switch back to alpha mode if user types one or more non-space/enter characters
+        // followed by a space/enter
+        switch (mSymbolsModeState) {
+            case SYMBOLS_MODE_STATE_BEGIN:
+                if (key != LatinIME.KEYCODE_SPACE && key != LatinIME.KEYCODE_ENTER && key > 0) {
+                    mSymbolsModeState = SYMBOLS_MODE_STATE_SYMBOL;
+                }
+                break;
+            case SYMBOLS_MODE_STATE_SYMBOL:
+                if (key == LatinIME.KEYCODE_ENTER || key == LatinIME.KEYCODE_SPACE) return true;
+                break;
+        }
+        return false;
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/LanguageSwitcher.java b/java/src/com/android/inputmethod/latin/LanguageSwitcher.java
new file mode 100644
index 0000000..1204512
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/LanguageSwitcher.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2010 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.latin;
+
+import java.util.Locale;
+
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.preference.PreferenceManager;
+import android.text.TextUtils;
+
+/**
+ * Keeps track of list of selected input languages and the current
+ * input language that the user has selected.
+ */
+public class LanguageSwitcher {
+
+    private Locale[] mLocales;
+    private LatinIME mIme;
+    private String[] mSelectedLanguageArray;
+    private String   mSelectedLanguages;
+    private int      mCurrentIndex = 0;
+    private String   mDefaultInputLanguage;
+    private Locale   mDefaultInputLocale;
+
+    public LanguageSwitcher(LatinIME ime) {
+        mIme = ime;
+        mLocales = new Locale[0];
+    }
+
+    public Locale[]  getLocales() {
+        return mLocales;
+    }
+
+    public int getLocaleCount() {
+        return mLocales.length;
+    }
+
+    /**
+     * Loads the currently selected input languages from shared preferences.
+     * @param sp
+     * @return whether there was any change
+     */
+    public boolean loadLocales(SharedPreferences sp) {
+        String selectedLanguages = sp.getString(LatinIME.PREF_SELECTED_LANGUAGES, null);
+        String currentLanguage   = sp.getString(LatinIME.PREF_INPUT_LANGUAGE, null);
+        if (selectedLanguages == null || selectedLanguages.length() < 1) {
+            loadDefaults();
+            if (mLocales.length == 0) {
+                return false;
+            }
+            mLocales = new Locale[0];
+            return true;
+        }
+        if (selectedLanguages.equals(mSelectedLanguages)) {
+            return false;
+        }
+        mSelectedLanguageArray = selectedLanguages.split(",");
+        mSelectedLanguages = selectedLanguages; // Cache it for comparison later
+        constructLocales();
+        mCurrentIndex = 0;
+        if (currentLanguage != null) {
+            // Find the index
+            mCurrentIndex = 0;
+            for (int i = 0; i < mLocales.length; i++) {
+                if (mSelectedLanguageArray[i].equals(currentLanguage)) {
+                    mCurrentIndex = i;
+                    break;
+                }
+            }
+            // If we didn't find the index, use the first one
+        }
+        return true;
+    }
+
+    private void loadDefaults() {
+        mDefaultInputLocale = mIme.getResources().getConfiguration().locale;
+        String country = mDefaultInputLocale.getCountry();
+        mDefaultInputLanguage = mDefaultInputLocale.getLanguage() +
+                (TextUtils.isEmpty(country) ? "" : "_" + country);
+    }
+
+    private void constructLocales() {
+        mLocales = new Locale[mSelectedLanguageArray.length];
+        for (int i = 0; i < mLocales.length; i++) {
+            mLocales[i] = new Locale(mSelectedLanguageArray[i]);
+        }
+    }
+
+    /**
+     * Returns the currently selected input language code, or the display language code if
+     * no specific locale was selected for input.
+     */
+    public String getInputLanguage() {
+        if (getLocaleCount() == 0) return mDefaultInputLanguage;
+
+        return mSelectedLanguageArray[mCurrentIndex];
+    }
+    
+    /**
+     * Returns the list of enabled language codes.
+     */
+    public String[] getEnabledLanguages() {
+        return mSelectedLanguageArray;
+    }
+
+    /**
+     * Returns the currently selected input locale, or the display locale if no specific
+     * locale was selected for input.
+     * @return
+     */
+    public Locale getInputLocale() {
+        if (getLocaleCount() == 0) return mDefaultInputLocale;
+
+        return mLocales[mCurrentIndex];
+    }
+
+    /**
+     * Returns the next input locale in the list. Wraps around to the beginning of the
+     * list if we're at the end of the list.
+     * @return
+     */
+    public Locale getNextInputLocale() {
+        if (getLocaleCount() == 0) return mDefaultInputLocale;
+
+        return mLocales[(mCurrentIndex + 1) % mLocales.length];
+    }
+
+    /**
+     * Returns the previous input locale in the list. Wraps around to the end of the
+     * list if we're at the beginning of the list.
+     * @return
+     */
+    public Locale getPrevInputLocale() {
+        if (getLocaleCount() == 0) return mDefaultInputLocale;
+
+        return mLocales[(mCurrentIndex - 1 + mLocales.length) % mLocales.length];
+    }
+
+    public void reset() {
+        mCurrentIndex = 0;
+    }
+
+    public void next() {
+        mCurrentIndex++;
+        if (mCurrentIndex >= mLocales.length) mCurrentIndex = 0; // Wrap around
+    }
+
+    public void prev() {
+        mCurrentIndex--;
+        if (mCurrentIndex < 0) mCurrentIndex = mLocales.length - 1; // Wrap around
+    }
+
+    public void persist() {
+        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mIme);
+        Editor editor = sp.edit();
+        editor.putString(LatinIME.PREF_INPUT_LANGUAGE, getInputLanguage());
+        editor.commit();
+    }
+
+    static String toTitleCase(String s) {
+        if (s.length() == 0) {
+            return s;
+        }
+
+        return Character.toUpperCase(s.charAt(0)) + s.substring(1);
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
new file mode 100644
index 0000000..43972d9
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -0,0 +1,1949 @@
+/*
+ * Copyright (C) 2008-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.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 android.app.AlertDialog;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.inputmethodservice.InputMethodService;
+import android.inputmethodservice.Keyboard;
+import android.inputmethodservice.KeyboardView;
+import android.media.AudioManager;
+import android.os.Debug;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
+import android.preference.PreferenceManager;
+import android.speech.RecognitionManager;
+import android.text.AutoText;
+import android.text.ClipboardManager;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.PrintWriterPrinter;
+import android.util.Printer;
+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.Window;
+import android.view.WindowManager;
+import android.view.inputmethod.CompletionInfo;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.ExtractedText;
+import android.view.inputmethod.ExtractedTextRequest;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputMethodManager;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * Input method implementation for Qwerty'ish keyboard.
+ */
+public class LatinIME extends InputMethodService
+        implements KeyboardView.OnKeyboardActionListener,
+        VoiceInput.UiListener,
+        SharedPreferences.OnSharedPreferenceChangeListener {
+    private static final String TAG = "LatinIME";
+    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 String PREF_VIBRATE_ON = "vibrate_on";
+    private static final String PREF_SOUND_ON = "sound_on";
+    private static final String PREF_AUTO_CAP = "auto_cap";
+    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_VOICE_MODE = "voice_mode";
+
+    // Whether or not the user has used voice input before (and thus, whether to show the
+    // first-run warning dialog or not).
+    private static final String PREF_HAS_USED_VOICE_INPUT = "has_used_voice_input";
+
+    // Whether or not the user has used voice input from an unsupported locale UI before.
+    // For example, the user has a Chinese UI but activates voice input.
+    private static final String PREF_HAS_USED_VOICE_INPUT_UNSUPPORTED_LOCALE =
+            "has_used_voice_input_unsupported_locale";
+
+    // A list of locales which are supported by default for voice input, unless we get a
+    // different list from Gservices.
+    public static final String DEFAULT_VOICE_INPUT_SUPPORTED_LOCALES =
+            "en " +
+            "en_US " +
+            "en_GB " +
+            "en_AU " +
+            "en_CA " +
+            "en_IE " +
+            "en_IN " +
+            "en_NZ " +
+            "en_SG " +
+            "en_ZA ";
+
+    // The private IME option used to indicate that no microphone should be shown for a
+    // given text field. For instance this is specified by the search dialog when the
+    // dialog is already showing a voice search button.
+    private static final String IME_OPTION_NO_MICROPHONE = "nm";
+
+    public static final String PREF_SELECTED_LANGUAGES = "selected_languages";
+    public static final String PREF_INPUT_LANGUAGE = "input_language";
+
+    private static final int MSG_UPDATE_SUGGESTIONS = 0;
+    private static final int MSG_START_TUTORIAL = 1;
+    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;
+
+    // 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.
+    private static final long MIN_MILLIS_AFTER_TYPING_BEFORE_SWIPE = 1000;
+
+    // How many continuous deletes at which to start deleting at a higher speed.
+    private static final int DELETE_ACCELERATE_AT = 20;
+    // Key events coming any faster than this are long-presses.
+    private static final int QUICK_PRESS = 200;
+
+    static final int KEYCODE_ENTER = '\n';
+    static final int KEYCODE_SPACE = ' ';
+    static final int KEYCODE_PERIOD = '.';
+
+    // Contextual menu positions
+    private static final int POS_SETTINGS = 0;
+    private static final int POS_METHOD = 1;
+
+    private LatinKeyboardView mInputView;
+    private CandidateViewContainer mCandidateViewContainer;
+    private CandidateView mCandidateView;
+    private Suggest mSuggest;
+    private CompletionInfo[] mCompletions;
+
+    private AlertDialog mOptionsDialog;
+    private AlertDialog mVoiceWarningDialog;
+
+    KeyboardSwitcher mKeyboardSwitcher;
+
+    private UserDictionary mUserDictionary;
+    private ContactsDictionary mContactsDictionary;
+    private ExpandableDictionary mAutoDictionary;
+
+    private Hints mHints;
+
+    Resources mResources;
+
+    private String mLocale;
+    private LanguageSwitcher mLanguageSwitcher;
+
+    private StringBuilder mComposing = new StringBuilder();
+    private WordComposer mWord = new WordComposer();
+    private int mCommittedLength;
+    private boolean mPredicting;
+    private boolean mRecognizing;
+    private boolean mAfterVoiceInput;
+    private boolean mImmediatelyAfterVoiceInput;
+    private boolean mShowingVoiceSuggestions;
+    private boolean mImmediatelyAfterVoiceSuggestions;
+    private boolean mVoiceInputHighlighted;
+    private boolean mEnableVoiceButton;
+    private CharSequence mBestWord;
+    private boolean mPredictionOn;
+    private boolean mCompletionOn;
+    private boolean mHasDictionary;
+    private boolean mAutoSpace;
+    private boolean mJustAddedAutoSpace;
+    private boolean mAutoCorrectEnabled;
+    private boolean mAutoCorrectOn;
+    private boolean mCapsLock;
+    private boolean mPasswordText;
+    private boolean mEmailText;
+    private boolean mVibrateOn;
+    private boolean mSoundOn;
+    private boolean mAutoCap;
+    private boolean mQuickFixes;
+    private boolean mHasUsedVoiceInput;
+    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;
+
+    // Indicates whether the suggestion strip is to be on in landscape
+    private boolean mJustAccepted;
+    private CharSequence mJustRevertedSeparator;
+    private int mDeleteCount;
+    private long mLastKeyTime;
+
+    private Tutorial mTutorial;
+
+    private AudioManager mAudioManager;
+    // Align sound effect volume on music volume
+    private final float FX_VOLUME = -1.0f;
+    private boolean mSilentMode;
+
+    private String mWordSeparators;
+    private String mSentenceSeparators;
+    private VoiceInput mVoiceInput;
+    private VoiceResults mVoiceResults = new VoiceResults();
+    private long mSwipeTriggerTimeMillis;
+    private boolean mConfigurationChanging;
+
+    // For each word, a list of potential replacements, usually from voice.
+    private Map<String, List<CharSequence>> mWordToSuggestions =
+            new HashMap<String, List<CharSequence>>();
+
+    private class VoiceResults {
+        List<String> candidates;
+        Map<String, List<CharSequence>> alternatives;
+    }
+    private boolean mRefreshKeyboardRequired;
+
+    Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_UPDATE_SUGGESTIONS:
+                    updateSuggestions();
+                    break;
+                case MSG_START_TUTORIAL:
+                    if (mTutorial == null) {
+                        if (mInputView.isShown()) {
+                            mTutorial = new Tutorial(LatinIME.this, mInputView);
+                            mTutorial.start();
+                        } else {
+                            // Try again soon if the view is not yet showing
+                            sendMessageDelayed(obtainMessage(MSG_START_TUTORIAL), 100);
+                        }
+                    }
+                    break;
+                case MSG_UPDATE_SHIFT_STATE:
+                    updateShiftKeyState(getCurrentInputEditorInfo());
+                    break;
+                case MSG_VOICE_RESULTS:
+                    handleVoiceResults();
+                    break;
+                case MSG_START_LISTENING_AFTER_SWIPE:
+                    if (mLastKeyTime < mSwipeTriggerTimeMillis) {
+                        startListening(true);
+                    }
+            }
+        }
+    };
+
+    @Override public void onCreate() {
+        super.onCreate();
+        //setStatusIcon(R.drawable.ime_qwerty);
+        mResources = getResources();
+        final Configuration conf = mResources.getConfiguration();
+        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
+        mLanguageSwitcher = new LanguageSwitcher(this);
+        mLanguageSwitcher.loadLocales(prefs);
+        mKeyboardSwitcher = new KeyboardSwitcher(this, this);
+        mKeyboardSwitcher.setLanguageSwitcher(mLanguageSwitcher);
+        boolean enableMultipleLanguages = mLanguageSwitcher.getLocaleCount() > 0;
+        String inputLanguage = mLanguageSwitcher.getInputLanguage();
+        if (inputLanguage == null) {
+            inputLanguage = conf.locale.toString();
+        }
+        initSuggest(inputLanguage);
+        mOrientation = conf.orientation;
+        initSuggestPuncList();
+
+        // register to receive ringer mode changes for silent mode
+        IntentFilter filter = new IntentFilter(AudioManager.RINGER_MODE_CHANGED_ACTION);
+        registerReceiver(mReceiver, filter);
+        if (VOICE_INSTALLED) {
+            mVoiceInput = new VoiceInput(this, this);
+            mHints = new Hints(this, new Hints.Display() {
+                public void showHint(int viewResource) {
+                    LayoutInflater inflater = (LayoutInflater) getSystemService(
+                            Context.LAYOUT_INFLATER_SERVICE);
+                    View view = inflater.inflate(viewResource, null);
+                    setCandidatesView(view);
+                    setCandidatesViewShown(true);
+                    mIsShowingHint = true;
+                }
+              });
+        }
+        prefs.registerOnSharedPreferenceChangeListener(this);
+    }
+
+    private void initSuggest(String locale) {
+        mLocale = locale;
+
+        Resources orig = getResources();
+        Configuration conf = orig.getConfiguration();
+        Locale saveLocale = conf.locale;
+        conf.locale = new Locale(locale);
+        orig.updateConfiguration(conf, orig.getDisplayMetrics());
+        if (mSuggest != null) {
+            mSuggest.close();
+        }
+        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
+        mQuickFixes = sp.getBoolean(PREF_QUICK_FIXES, true);
+        mSuggest = new Suggest(this, R.raw.main);
+        updateAutoTextEnabled(saveLocale);
+        if (mUserDictionary != null) mUserDictionary.close();
+        mUserDictionary = new UserDictionary(this, mLocale);
+        if (mContactsDictionary == null) {
+            mContactsDictionary = new ContactsDictionary(this);
+        }
+        if (mAutoDictionary != null) {
+            mAutoDictionary.close();
+        }
+        mAutoDictionary = new AutoDictionary(this, this, mLocale);
+        mSuggest.setUserDictionary(mUserDictionary);
+        mSuggest.setContactsDictionary(mContactsDictionary);
+        mSuggest.setAutoDictionary(mAutoDictionary);
+        updateCorrectionMode();
+        mWordSeparators = mResources.getString(R.string.word_separators);
+        mSentenceSeparators = mResources.getString(R.string.sentence_separators);
+
+        conf.locale = saveLocale;
+        orig.updateConfiguration(conf, orig.getDisplayMetrics());
+    }
+
+    @Override
+    public void onDestroy() {
+        mUserDictionary.close();
+        mContactsDictionary.close();
+        unregisterReceiver(mReceiver);
+        if (VOICE_INSTALLED) {
+            mVoiceInput.destroy();
+        }
+        super.onDestroy();
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration conf) {
+        // If the system locale changes and is different from the saved
+        // locale (mLocale), then reload the input locale list from the 
+        // latin ime settings (shared prefs) and reset the input locale
+        // to the first one.
+        if (!TextUtils.equals(conf.locale.toString(), mLocale)) {
+            if (mLanguageSwitcher != null) {
+                mLanguageSwitcher.loadLocales(
+                        PreferenceManager.getDefaultSharedPreferences(this));
+                toggleLanguage(true, true);
+            } else {
+                reloadKeyboards();
+            }
+        }
+        // If orientation changed while predicting, commit the change
+        if (conf.orientation != mOrientation) {
+            InputConnection ic = getCurrentInputConnection();
+            commitTyped(ic);
+            if (ic != null) ic.finishComposingText(); // For voice input
+            mOrientation = conf.orientation;
+            reloadKeyboards();
+        }
+        mConfigurationChanging = true;
+        super.onConfigurationChanged(conf);
+        if (mRecognizing) {
+            switchToRecognitionStatusView();
+        }
+        mConfigurationChanging = false;
+    }
+
+    @Override
+    public View onCreateInputView() {
+        mInputView = (LatinKeyboardView) getLayoutInflater().inflate(
+                R.layout.input, null);
+        mKeyboardSwitcher.setInputView(mInputView);
+        mKeyboardSwitcher.makeKeyboards(true);
+        mInputView.setOnKeyboardActionListener(this);
+        mKeyboardSwitcher.setKeyboardMode(
+                KeyboardSwitcher.MODE_TEXT, 0,
+                shouldShowVoiceButton(makeFieldContext(), getCurrentInputEditorInfo()));
+        return mInputView;
+    }
+
+    @Override
+    public View onCreateCandidatesView() {
+        mKeyboardSwitcher.makeKeyboards(true);
+        mCandidateViewContainer = (CandidateViewContainer) getLayoutInflater().inflate(
+                R.layout.candidates, null);
+        mCandidateViewContainer.initViews();
+        mCandidateView = (CandidateView) mCandidateViewContainer.findViewById(R.id.candidates);
+        mCandidateView.setService(this);
+        setCandidatesViewShown(true);
+        return mCandidateViewContainer;
+    }
+
+    @Override
+    public void onStartInputView(EditorInfo attribute, boolean restarting) {
+        // In landscape mode, this method gets called without the input view being created.
+        if (mInputView == null) {
+            return;
+        }
+
+        if (mRefreshKeyboardRequired) {
+            mRefreshKeyboardRequired = false;
+            toggleLanguage(true, true);
+        }
+
+        mKeyboardSwitcher.makeKeyboards(false);
+
+        TextEntryState.newSession(this);
+
+        // Most such things we decide below in the switch statement, but we need to know
+        // now whether this is a password text field, because we need to know now (before
+        // the switch statement) whether we want to enable the voice button.
+        mPasswordText = false;
+        int variation = attribute.inputType & EditorInfo.TYPE_MASK_VARIATION;
+        if (variation == EditorInfo.TYPE_TEXT_VARIATION_PASSWORD ||
+                variation == EditorInfo.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD) {
+            mPasswordText = true;
+        }
+
+        mEnableVoiceButton = shouldShowVoiceButton(makeFieldContext(), attribute);
+        final boolean enableVoiceButton = mEnableVoiceButton && mEnableVoice;
+
+        mAfterVoiceInput = false;
+        mImmediatelyAfterVoiceInput = false;
+        mShowingVoiceSuggestions = false;
+        mImmediatelyAfterVoiceSuggestions = false;
+        mVoiceInputHighlighted = false;
+        mWordToSuggestions.clear();
+        mInputTypeNoAutoCorrect = false;
+        mPredictionOn = false;
+        mCompletionOn = false;
+        mCompletions = null;
+        mCapsLock = false;
+        mEmailText = false;
+        switch (attribute.inputType & EditorInfo.TYPE_MASK_CLASS) {
+            case EditorInfo.TYPE_CLASS_NUMBER:
+            case EditorInfo.TYPE_CLASS_DATETIME:
+                mKeyboardSwitcher.setKeyboardMode(KeyboardSwitcher.MODE_SYMBOLS,
+                        attribute.imeOptions, enableVoiceButton);
+                break;
+            case EditorInfo.TYPE_CLASS_PHONE:
+                mKeyboardSwitcher.setKeyboardMode(KeyboardSwitcher.MODE_PHONE,
+                        attribute.imeOptions, enableVoiceButton);
+                break;
+            case EditorInfo.TYPE_CLASS_TEXT:
+                mKeyboardSwitcher.setKeyboardMode(KeyboardSwitcher.MODE_TEXT,
+                        attribute.imeOptions, enableVoiceButton);
+                //startPrediction();
+                mPredictionOn = true;
+                // Make sure that passwords are not displayed in candidate view
+                if (variation == EditorInfo.TYPE_TEXT_VARIATION_PASSWORD ||
+                        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;
+                } else {
+                    mAutoSpace = true;
+                }
+                if (variation == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS) {
+                    mPredictionOn = false;
+                    mKeyboardSwitcher.setKeyboardMode(KeyboardSwitcher.MODE_EMAIL,
+                            attribute.imeOptions, enableVoiceButton);
+                } else if (variation == EditorInfo.TYPE_TEXT_VARIATION_URI) {
+                    mPredictionOn = false;
+                    mKeyboardSwitcher.setKeyboardMode(KeyboardSwitcher.MODE_URL,
+                            attribute.imeOptions, enableVoiceButton);
+                } else if (variation == EditorInfo.TYPE_TEXT_VARIATION_SHORT_MESSAGE) {
+                    mKeyboardSwitcher.setKeyboardMode(KeyboardSwitcher.MODE_IM,
+                            attribute.imeOptions, enableVoiceButton);
+                } else if (variation == EditorInfo.TYPE_TEXT_VARIATION_FILTER) {
+                    mPredictionOn = false;
+                } else if (variation == EditorInfo.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT) {
+                    mKeyboardSwitcher.setKeyboardMode(KeyboardSwitcher.MODE_WEB,
+                            attribute.imeOptions, enableVoiceButton);
+                    // If it's a browser edit field and auto correct is not ON explicitly, then
+                    // disable auto correction, but keep suggestions on.
+                    if ((attribute.inputType & EditorInfo.TYPE_TEXT_FLAG_AUTO_CORRECT) == 0) {
+                        mInputTypeNoAutoCorrect = true;
+                    }
+                }
+
+                // If NO_SUGGESTIONS is set, don't do prediction.
+                if ((attribute.inputType & EditorInfo.TYPE_TEXT_FLAG_NO_SUGGESTIONS) != 0) {
+                    mPredictionOn = false;
+                    mInputTypeNoAutoCorrect = true;
+                }
+                // If it's not multiline and the autoCorrect flag is not set, then don't correct
+                if ((attribute.inputType & EditorInfo.TYPE_TEXT_FLAG_AUTO_CORRECT) == 0 &&
+                        (attribute.inputType & EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE) == 0) {
+                    mInputTypeNoAutoCorrect = true;
+                }
+                if ((attribute.inputType&EditorInfo.TYPE_TEXT_FLAG_AUTO_COMPLETE) != 0) {
+                    mPredictionOn = false;
+                    mCompletionOn = true && isFullscreenMode();
+                }
+                updateShiftKeyState(attribute);
+                break;
+            default:
+                mKeyboardSwitcher.setKeyboardMode(KeyboardSwitcher.MODE_TEXT,
+                        attribute.imeOptions, enableVoiceButton);
+                updateShiftKeyState(attribute);
+        }
+        mInputView.closing();
+        mComposing.setLength(0);
+        mPredicting = false;
+        mDeleteCount = 0;
+        mJustAddedAutoSpace = false;
+        loadSettings();
+        updateShiftKeyState(attribute);
+
+        setCandidatesViewShown(false);
+        setSuggestions(null, false, false, false);
+
+        // If the dictionary is not big enough, don't auto correct
+        mHasDictionary = mSuggest.hasMainDictionary();
+
+        updateCorrectionMode();
+
+        mInputView.setProximityCorrectionEnabled(true);
+        mPredictionOn = mPredictionOn && (mCorrectionMode > 0 || mShowSuggestions);
+        checkTutorial(attribute.privateImeOptions);
+        if (TRACE) Debug.startMethodTracing("/data/trace/latinime");
+    }
+
+    @Override
+    public void onFinishInput() {
+        super.onFinishInput();
+
+        if (VOICE_INSTALLED && !mConfigurationChanging) {
+            if (mAfterVoiceInput) {
+                mVoiceInput.logInputEnded();
+            }
+            mVoiceInput.flushLogs();
+            mVoiceInput.cancel();
+        }
+        if (mInputView != null) {
+            mInputView.closing();
+        }
+    }
+
+    @Override
+    public void onUpdateExtractedText(int token, ExtractedText text) {
+        super.onUpdateExtractedText(token, text);
+        InputConnection ic = getCurrentInputConnection();
+        if (!mImmediatelyAfterVoiceInput && mAfterVoiceInput && ic != null) {
+            mVoiceInput.logTextModified();
+
+            if (mHints.showPunctuationHintIfNecessary(ic)) {
+                mVoiceInput.logPunctuationHintDisplayed();
+            }
+        }
+        mImmediatelyAfterVoiceInput = false;
+    }
+
+    @Override
+    public void onUpdateSelection(int oldSelStart, int oldSelEnd,
+            int newSelStart, int newSelEnd,
+            int candidatesStart, int candidatesEnd) {
+        super.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd,
+                candidatesStart, candidatesEnd);
+
+        if (DEBUG) {
+            Log.i(TAG, "onUpdateSelection: oss=" + oldSelStart
+                    + ", ose=" + oldSelEnd
+                    + ", nss=" + newSelStart
+                    + ", nse=" + newSelEnd
+                    + ", cs=" + candidatesStart
+                    + ", ce=" + candidatesEnd);
+        }
+
+        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))) {
+            mComposing.setLength(0);
+            mPredicting = false;
+            updateSuggestions();
+            TextEntryState.reset();
+            InputConnection ic = getCurrentInputConnection();
+            if (ic != null) {
+                ic.finishComposingText();
+            }
+            mVoiceInputHighlighted = false;
+        } else if (!mPredicting && !mJustAccepted) {
+            switch (TextEntryState.getState()) {
+                case TextEntryState.STATE_ACCEPTED_DEFAULT:
+                    TextEntryState.reset();
+                    // fall through
+                case TextEntryState.STATE_SPACE_AFTER_PICKED:
+                    mJustAddedAutoSpace = false;  // The user moved the cursor.
+                    break;
+            }
+        }
+        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());
+
+                    setSuggestions(suggestions, false, true, true);
+                    setCandidatesViewShown(true);
+                }
+            }
+        }
+    }
+
+    @Override
+    public void hideWindow() {
+        if (TRACE) Debug.stopMethodTracing();
+        if (mOptionsDialog != null && mOptionsDialog.isShowing()) {
+            mOptionsDialog.dismiss();
+            mOptionsDialog = null;
+        }
+        if (!mConfigurationChanging) {
+            if (mAfterVoiceInput) mVoiceInput.logInputEnded();
+            if (mVoiceWarningDialog != null && mVoiceWarningDialog.isShowing()) {
+                mVoiceInput.logKeyboardWarningDialogDismissed();
+                mVoiceWarningDialog.dismiss();
+                mVoiceWarningDialog = null;
+            }
+            if (VOICE_INSTALLED & mRecognizing) {
+                mVoiceInput.cancel();
+            }
+        }
+        super.hideWindow();
+        TextEntryState.endSession();
+    }
+
+    @Override
+    public void onDisplayCompletions(CompletionInfo[] completions) {
+        if (false) {
+            Log.i("foo", "Received completions:");
+            for (int i=0; i<(completions != null ? completions.length : 0); i++) {
+                Log.i("foo", "  #" + i + ": " + completions[i]);
+            }
+        }
+        if (mCompletionOn) {
+            mCompletions = completions;
+            if (completions == null) {
+                setSuggestions(null, false, false, false);
+                return;
+            }
+
+            List<CharSequence> stringList = new ArrayList<CharSequence>();
+            for (int i=0; i<(completions != null ? completions.length : 0); i++) {
+                CompletionInfo ci = completions[i];
+                if (ci != null) stringList.add(ci.getText());
+            }
+            //CharSequence typedWord = mWord.getTypedWord();
+            setSuggestions(stringList, true, true, true);
+            mBestWord = null;
+            setCandidatesViewShown(isCandidateStripVisible() || mCompletionOn);
+        }
+    }
+
+    @Override
+    public void setCandidatesViewShown(boolean shown) {
+        // TODO: Remove this if we support candidates with hard keyboard
+        if (onEvaluateInputViewShown()) {
+            super.setCandidatesViewShown(shown);
+        }
+    }
+
+    @Override
+    public void onComputeInsets(InputMethodService.Insets outInsets) {
+        super.onComputeInsets(outInsets);
+        if (!isFullscreenMode()) {
+            outInsets.contentTopInsets = outInsets.visibleTopInsets;
+        }
+    }
+
+    @Override
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_BACK:
+                if (event.getRepeatCount() == 0 && mInputView != null) {
+                    if (mInputView.handleBack()) {
+                        return true;
+                    } else if (mTutorial != null) {
+                        mTutorial.close();
+                        mTutorial = null;
+                    }
+                }
+                break;
+            case KeyEvent.KEYCODE_DPAD_DOWN:
+            case KeyEvent.KEYCODE_DPAD_UP:
+            case KeyEvent.KEYCODE_DPAD_LEFT:
+            case KeyEvent.KEYCODE_DPAD_RIGHT:
+                // If tutorial is visible, don't allow dpad to work
+                if (mTutorial != null) {
+                    return true;
+                }
+                break;
+        }
+        return super.onKeyDown(keyCode, event);
+    }
+
+    @Override
+    public boolean onKeyUp(int keyCode, KeyEvent event) {
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_DPAD_DOWN:
+            case KeyEvent.KEYCODE_DPAD_UP:
+            case KeyEvent.KEYCODE_DPAD_LEFT:
+            case KeyEvent.KEYCODE_DPAD_RIGHT:
+                // If tutorial is visible, don't allow dpad to work
+                if (mTutorial != null) {
+                    return true;
+                }
+                // Enable shift key and DPAD to do selections
+                if (mInputView != null && mInputView.isShown() && mInputView.isShifted()) {
+                    event = new KeyEvent(event.getDownTime(), event.getEventTime(),
+                            event.getAction(), event.getKeyCode(), event.getRepeatCount(),
+                            event.getDeviceId(), event.getScanCode(),
+                            KeyEvent.META_SHIFT_LEFT_ON | KeyEvent.META_SHIFT_ON);
+                    InputConnection ic = getCurrentInputConnection();
+                    if (ic != null) ic.sendKeyEvent(event);
+                    return true;
+                }
+                break;
+        }
+        return super.onKeyUp(keyCode, event);
+    }
+
+    private void revertVoiceInput() {
+        InputConnection ic = getCurrentInputConnection();
+        if (ic != null) ic.commitText("", 1);
+        updateSuggestions();
+        mVoiceInputHighlighted = false;
+    }
+
+    private void commitVoiceInput() {
+        InputConnection ic = getCurrentInputConnection();
+        if (ic != null) ic.finishComposingText();
+        updateSuggestions();
+        mVoiceInputHighlighted = false;
+    }
+
+    private void reloadKeyboards() {
+        if (mKeyboardSwitcher == null) {
+            mKeyboardSwitcher = new KeyboardSwitcher(this, this);
+        }
+        mKeyboardSwitcher.setLanguageSwitcher(mLanguageSwitcher);
+        if (mInputView != null) {
+            mKeyboardSwitcher.setVoiceMode(mEnableVoice && mEnableVoiceButton, mVoiceOnPrimary);
+        }
+        mKeyboardSwitcher.makeKeyboards(true);
+    }
+
+    private void commitTyped(InputConnection inputConnection) {
+        if (mPredicting) {
+            mPredicting = false;
+            if (mComposing.length() > 0) {
+                if (inputConnection != null) {
+                    inputConnection.commitText(mComposing, 1);
+                }
+                mCommittedLength = mComposing.length();
+                TextEntryState.acceptedTyped(mComposing);
+                checkAddToDictionary(mComposing, AutoDictionary.FREQUENCY_FOR_TYPED);
+            }
+            updateSuggestions();
+        }
+    }
+
+    private void postUpdateShiftKeyState() {
+        mHandler.removeMessages(MSG_UPDATE_SHIFT_STATE);
+        mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_UPDATE_SHIFT_STATE), 300);
+    }
+
+    public void updateShiftKeyState(EditorInfo attr) {
+        InputConnection ic = getCurrentInputConnection();
+        if (attr != null && mInputView != null && mKeyboardSwitcher.isAlphabetMode()
+                && ic != null) {
+            mInputView.setShifted(mCapsLock || getCursorCapsMode(ic, attr) != 0);
+        }
+    }
+
+    private int getCursorCapsMode(InputConnection ic, EditorInfo attr) {
+        int caps = 0;
+        EditorInfo ei = getCurrentInputEditorInfo();
+        if (mAutoCap && ei != null && ei.inputType != EditorInfo.TYPE_NULL) {
+            caps = ic.getCursorCapsMode(attr.inputType);
+        }
+        return caps;
+    }
+
+    private void swapPunctuationAndSpace() {
+        final InputConnection ic = getCurrentInputConnection();
+        if (ic == null) return;
+        CharSequence lastTwo = ic.getTextBeforeCursor(2, 0);
+        if (lastTwo != null && lastTwo.length() == 2
+                && lastTwo.charAt(0) == KEYCODE_SPACE && isSentenceSeparator(lastTwo.charAt(1))) {
+            ic.beginBatchEdit();
+            ic.deleteSurroundingText(2, 0);
+            ic.commitText(lastTwo.charAt(1) + " ", 1);
+            ic.endBatchEdit();
+            updateShiftKeyState(getCurrentInputEditorInfo());
+            mJustAddedAutoSpace = true;
+        }
+    }
+
+    private void reswapPeriodAndSpace() {
+        final InputConnection ic = getCurrentInputConnection();
+        if (ic == null) return;
+        CharSequence lastThree = ic.getTextBeforeCursor(3, 0);
+        if (lastThree != null && lastThree.length() == 3
+                && lastThree.charAt(0) == KEYCODE_PERIOD
+                && lastThree.charAt(1) == KEYCODE_SPACE
+                && lastThree.charAt(2) == KEYCODE_PERIOD) {
+            ic.beginBatchEdit();
+            ic.deleteSurroundingText(3, 0);
+            ic.commitText(" ..", 1);
+            ic.endBatchEdit();
+            updateShiftKeyState(getCurrentInputEditorInfo());
+        }
+    }
+
+    private void doubleSpace() {
+        //if (!mAutoPunctuate) return;
+        if (mCorrectionMode == Suggest.CORRECTION_NONE) return;
+        final InputConnection ic = getCurrentInputConnection();
+        if (ic == null) return;
+        CharSequence lastThree = ic.getTextBeforeCursor(3, 0);
+        if (lastThree != null && lastThree.length() == 3
+                && Character.isLetterOrDigit(lastThree.charAt(0))
+                && lastThree.charAt(1) == KEYCODE_SPACE && lastThree.charAt(2) == KEYCODE_SPACE) {
+            ic.beginBatchEdit();
+            ic.deleteSurroundingText(2, 0);
+            ic.commitText(". ", 1);
+            ic.endBatchEdit();
+            updateShiftKeyState(getCurrentInputEditorInfo());
+            mJustAddedAutoSpace = true;
+        }
+    }
+
+    private void maybeRemovePreviousPeriod(CharSequence text) {
+        final InputConnection ic = getCurrentInputConnection();
+        if (ic == null) return;
+
+        // When the text's first character is '.', remove the previous period
+        // if there is one.
+        CharSequence lastOne = ic.getTextBeforeCursor(1, 0);
+        if (lastOne != null && lastOne.length() == 1
+                && lastOne.charAt(0) == KEYCODE_PERIOD
+                && text.charAt(0) == KEYCODE_PERIOD) {
+            ic.deleteSurroundingText(1, 0);
+        }
+    }
+
+    private void removeTrailingSpace() {
+        final InputConnection ic = getCurrentInputConnection();
+        if (ic == null) return;
+
+        CharSequence lastOne = ic.getTextBeforeCursor(1, 0);
+        if (lastOne != null && lastOne.length() == 1
+                && lastOne.charAt(0) == KEYCODE_SPACE) {
+            ic.deleteSurroundingText(1, 0);
+        }
+    }
+
+    public boolean addWordToDictionary(String word) {
+        mUserDictionary.addWord(word, 128);
+        return true;
+    }
+
+    private boolean isAlphabet(int code) {
+        if (Character.isLetter(code)) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    // Implementation of KeyboardViewListener
+
+    public void onKey(int primaryCode, int[] keyCodes) {
+        long when = SystemClock.uptimeMillis();
+        if (primaryCode != Keyboard.KEYCODE_DELETE ||
+                when > mLastKeyTime + QUICK_PRESS) {
+            mDeleteCount = 0;
+        }
+        mLastKeyTime = when;
+        switch (primaryCode) {
+            case Keyboard.KEYCODE_DELETE:
+                handleBackspace();
+                mDeleteCount++;
+                break;
+            case Keyboard.KEYCODE_SHIFT:
+                handleShift();
+                break;
+            case Keyboard.KEYCODE_CANCEL:
+                if (mOptionsDialog == null || !mOptionsDialog.isShowing()) {
+                    handleClose();
+                }
+                break;
+            case LatinKeyboardView.KEYCODE_OPTIONS:
+                showOptionsMenu();
+                break;
+            case LatinKeyboardView.KEYCODE_NEXT_LANGUAGE:
+                toggleLanguage(false, true);
+                break;
+            case LatinKeyboardView.KEYCODE_PREV_LANGUAGE:
+                toggleLanguage(false, false);
+                break;
+            case LatinKeyboardView.KEYCODE_SHIFT_LONGPRESS:
+                if (mCapsLock) {
+                    handleShift();
+                } else {
+                    toggleCapsLock();
+                }
+                break;
+            case Keyboard.KEYCODE_MODE_CHANGE:
+                changeKeyboardMode();
+                break;
+            case LatinKeyboardView.KEYCODE_VOICE:
+                if (VOICE_INSTALLED) {
+                    startListening(false /* was a button press, was not a swipe */);
+                }
+                break;
+            case 9 /*Tab*/:
+                sendDownUpKeyEvents(KeyEvent.KEYCODE_TAB);
+                break;
+            default:
+                if (primaryCode != KEYCODE_ENTER) {
+                    mJustAddedAutoSpace = false;
+                }
+                if (isWordSeparator(primaryCode)) {
+                    handleSeparator(primaryCode);
+                } else {
+                    handleCharacter(primaryCode, keyCodes);
+                }
+                // Cancel the just reverted state
+                mJustRevertedSeparator = null;
+        }
+        if (mKeyboardSwitcher.onKey(primaryCode)) {
+            changeKeyboardMode();
+        }
+    }
+
+    public void onText(CharSequence text) {
+        if (VOICE_INSTALLED && mVoiceInputHighlighted) {
+            commitVoiceInput();
+        }
+        InputConnection ic = getCurrentInputConnection();
+        if (ic == null) return;
+        ic.beginBatchEdit();
+        if (mPredicting) {
+            commitTyped(ic);
+        }
+        maybeRemovePreviousPeriod(text);
+        ic.commitText(text, 1);
+        ic.endBatchEdit();
+        updateShiftKeyState(getCurrentInputEditorInfo());
+        mJustRevertedSeparator = null;
+        mJustAddedAutoSpace = false;
+    }
+
+    private void handleBackspace() {
+        if (VOICE_INSTALLED && mVoiceInputHighlighted) {
+            revertVoiceInput();
+            return;
+        }
+        boolean deleteChar = false;
+        InputConnection ic = getCurrentInputConnection();
+        if (ic == null) return;
+        if (mPredicting) {
+            final int length = mComposing.length();
+            if (length > 0) {
+                mComposing.delete(length - 1, length);
+                mWord.deleteLast();
+                ic.setComposingText(mComposing, 1);
+                if (mComposing.length() == 0) {
+                    mPredicting = false;
+                }
+                postUpdateSuggestions();
+            } else {
+                ic.deleteSurroundingText(1, 0);
+            }
+        } else {
+            deleteChar = true;
+        }
+        postUpdateShiftKeyState();
+        TextEntryState.backspace();
+        if (TextEntryState.getState() == TextEntryState.STATE_UNDO_COMMIT) {
+            revertLastWord(deleteChar);
+            return;
+        } else if (deleteChar) {
+            sendDownUpKeyEvents(KeyEvent.KEYCODE_DEL);
+            if (mDeleteCount > DELETE_ACCELERATE_AT) {
+                sendDownUpKeyEvents(KeyEvent.KEYCODE_DEL);
+            }
+        }
+        mJustRevertedSeparator = null;
+    }
+
+    private void handleShift() {
+        mHandler.removeMessages(MSG_UPDATE_SHIFT_STATE);
+        if (mKeyboardSwitcher.isAlphabetMode()) {
+            // Alphabet keyboard
+            checkToggleCapsLock();
+            mInputView.setShifted(mCapsLock || !mInputView.isShifted());
+        } else {
+            mKeyboardSwitcher.toggleShift();
+        }
+    }
+
+    private void handleCharacter(int primaryCode, int[] keyCodes) {
+        if (VOICE_INSTALLED && mVoiceInputHighlighted) {
+            commitVoiceInput();
+        }
+        if (isAlphabet(primaryCode) && isPredictionOn() && !isCursorTouchingWord()) {
+            if (!mPredicting) {
+                mPredicting = true;
+                mComposing.setLength(0);
+                mWord.reset();
+            }
+        }
+        if (mInputView.isShifted()) {
+            // TODO: This doesn't work with ß, 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);
+        }
+        if (mPredicting) {
+            if (mInputView.isShifted() && mComposing.length() == 0) {
+                mWord.setCapitalized(true);
+            }
+            mComposing.append((char) primaryCode);
+            mWord.add(primaryCode, keyCodes);
+            InputConnection ic = getCurrentInputConnection();
+            if (ic != null) {
+                // If it's the first letter, make note of auto-caps state
+                if (mWord.size() == 1) {
+                    mWord.setAutoCapitalized(
+                            getCursorCapsMode(ic, getCurrentInputEditorInfo()) != 0);
+                }
+                ic.setComposingText(mComposing, 1);
+            }
+            postUpdateSuggestions();
+        } else {
+            sendKeyChar((char)primaryCode);
+        }
+        updateShiftKeyState(getCurrentInputEditorInfo());
+        measureCps();
+        TextEntryState.typedCharacter((char) primaryCode, isWordSeparator(primaryCode));
+    }
+
+    private void handleSeparator(int primaryCode) {
+        if (VOICE_INSTALLED && mVoiceInputHighlighted) {
+            commitVoiceInput();
+        }
+        boolean pickedDefault = false;
+        // Handle separator
+        InputConnection ic = getCurrentInputConnection();
+        if (ic != null) {
+            ic.beginBatchEdit();
+        }
+        if (mPredicting) {
+            // In certain languages where single quote is a separator, it's better
+            // not to auto correct, but accept the typed word. For instance,
+            // in Italian dov' should not be expanded to dove' because the elision
+            // requires the last vowel to be removed.
+            if (mAutoCorrectOn && primaryCode != '\'' &&
+                    (mJustRevertedSeparator == null
+                            || mJustRevertedSeparator.length() == 0
+                            || mJustRevertedSeparator.charAt(0) != primaryCode)) {
+                pickDefaultSuggestion();
+                pickedDefault = true;
+                // Picked the suggestion by the space key.  We consider this
+                // as "added an auto space".
+                if (primaryCode == KEYCODE_SPACE) {
+                    mJustAddedAutoSpace = true;
+                }
+            } else {
+                commitTyped(ic);
+            }
+        }
+        if (mJustAddedAutoSpace && primaryCode == KEYCODE_ENTER) {
+            removeTrailingSpace();
+            mJustAddedAutoSpace = false;
+        }
+        sendKeyChar((char)primaryCode);
+
+        // Handle the case of ". ." -> " .." with auto-space if necessary
+        // before changing the TextEntryState.
+        if (TextEntryState.getState() == TextEntryState.STATE_PUNCTUATION_AFTER_ACCEPTED
+                && primaryCode == KEYCODE_PERIOD) {
+            reswapPeriodAndSpace();
+        }
+
+        TextEntryState.typedCharacter((char) primaryCode, true);
+        if (TextEntryState.getState() == TextEntryState.STATE_PUNCTUATION_AFTER_ACCEPTED
+                && 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);
+        }
+        updateShiftKeyState(getCurrentInputEditorInfo());
+        if (ic != null) {
+            ic.endBatchEdit();
+        }
+    }
+
+    private void handleClose() {
+        commitTyped(getCurrentInputConnection());
+        if (VOICE_INSTALLED & mRecognizing) {
+            mVoiceInput.cancel();
+        }
+        requestHideSelf(0);
+        mInputView.closing();
+        TextEntryState.endSession();
+    }
+
+    private void checkToggleCapsLock() {
+        if (mInputView.getKeyboard().isShifted()) {
+            toggleCapsLock();
+        }
+    }
+
+    private void toggleCapsLock() {
+        mCapsLock = !mCapsLock;
+        if (mKeyboardSwitcher.isAlphabetMode()) {
+            ((LatinKeyboard) mInputView.getKeyboard()).setShiftLocked(mCapsLock);
+        }
+    }
+
+    private void postUpdateSuggestions() {
+        mHandler.removeMessages(MSG_UPDATE_SUGGESTIONS);
+        mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_UPDATE_SUGGESTIONS), 100);
+    }
+
+    private boolean isPredictionOn() {
+        boolean predictionOn = mPredictionOn;
+        return predictionOn;
+    }
+
+    private boolean isCandidateStripVisible() {
+        return isPredictionOn() && mShowSuggestions;
+    }
+
+    public void onCancelVoice() {
+        if (mRecognizing) {
+            switchToKeyboardView();
+        }
+    }
+
+    private void switchToKeyboardView() {
+      mHandler.post(new Runnable() {
+          public void run() {
+              mRecognizing = false;
+              if (mInputView != null) {
+                setInputView(mInputView);
+              }
+              updateInputViewShown();
+          }});
+    }
+
+    private void switchToRecognitionStatusView() {
+        final boolean configChanged = mConfigurationChanging;
+        mHandler.post(new Runnable() {
+            public void run() {
+                mRecognizing = true;
+                View v = mVoiceInput.getView();
+                ViewParent p = v.getParent();
+                if (p != null && p instanceof ViewGroup) {
+                    ((ViewGroup)v.getParent()).removeView(v);
+                }
+                setInputView(v);
+                updateInputViewShown();
+                if (configChanged) {
+                    mVoiceInput.onConfigurationChanged();
+                }
+        }});
+    }
+
+    private void startListening(boolean swipe) {
+        if (!mHasUsedVoiceInput ||
+                (!mLocaleSupportedForVoiceInput && !mHasUsedVoiceInputUnsupportedLocale)) {
+            // Calls reallyStartListening if user clicks OK, does nothing if user clicks Cancel.
+            showVoiceWarningDialog(swipe);
+        } else {
+            reallyStartListening(swipe);
+        }
+    }
+
+    private void reallyStartListening(boolean swipe) {
+        if (!mHasUsedVoiceInput) {
+            // The user has started a voice input, so remember that in the
+            // future (so we don't show the warning dialog after the first run).
+            SharedPreferences.Editor editor =
+                    PreferenceManager.getDefaultSharedPreferences(this).edit();
+            editor.putBoolean(PREF_HAS_USED_VOICE_INPUT, true);
+            editor.commit();
+            mHasUsedVoiceInput = true;
+        }
+
+        if (!mLocaleSupportedForVoiceInput && !mHasUsedVoiceInputUnsupportedLocale) {
+            // The user has started a voice input from an unsupported locale, so remember that
+            // in the future (so we don't show the warning dialog the next time they do this).
+            SharedPreferences.Editor editor =
+                    PreferenceManager.getDefaultSharedPreferences(this).edit();
+            editor.putBoolean(PREF_HAS_USED_VOICE_INPUT_UNSUPPORTED_LOCALE, true);
+            editor.commit();
+            mHasUsedVoiceInputUnsupportedLocale = true;
+        }
+
+        // Clear N-best suggestions
+        setSuggestions(null, false, false, true);
+
+        FieldContext context = new FieldContext(
+            getCurrentInputConnection(),
+            getCurrentInputEditorInfo(),
+            mLanguageSwitcher.getInputLanguage(),
+            mLanguageSwitcher.getEnabledLanguages());
+        mVoiceInput.startListening(context, swipe);
+        switchToRecognitionStatusView();
+    }
+
+    private void showVoiceWarningDialog(final boolean swipe) {
+        AlertDialog.Builder builder = new AlertDialog.Builder(this);
+        builder.setCancelable(true);
+        builder.setIcon(R.drawable.ic_mic_dialog);
+        builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+            public void onClick(DialogInterface dialog, int whichButton) {
+                mVoiceInput.logKeyboardWarningDialogOk();
+                reallyStartListening(swipe);
+            }
+        });
+        builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
+            public void onClick(DialogInterface dialog, int whichButton) {
+                mVoiceInput.logKeyboardWarningDialogCancel();
+            }
+        });
+
+        if (mLocaleSupportedForVoiceInput) {
+            String message = getString(R.string.voice_warning_may_not_understand) + "\n\n" +
+                    getString(R.string.voice_warning_how_to_turn_off);
+            builder.setMessage(message);
+        } else {
+            String message = getString(R.string.voice_warning_locale_not_supported) + "\n\n" +
+                    getString(R.string.voice_warning_may_not_understand) + "\n\n" +
+                    getString(R.string.voice_warning_how_to_turn_off);
+            builder.setMessage(message);
+        }
+
+        builder.setTitle(R.string.voice_warning_title);
+        mVoiceWarningDialog = builder.create();
+
+        Window window = mVoiceWarningDialog.getWindow();
+        WindowManager.LayoutParams lp = window.getAttributes();
+        lp.token = mInputView.getWindowToken();
+        lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
+        window.setAttributes(lp);
+        window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
+        mVoiceInput.logKeyboardWarningDialogShown();
+        mVoiceWarningDialog.show();
+    }
+
+    public void onVoiceResults(List<String> candidates,
+            Map<String, List<CharSequence>> alternatives) {
+        if (!mRecognizing) {
+            return;
+        }
+        mVoiceResults.candidates = candidates;
+        mVoiceResults.alternatives = alternatives;
+        mHandler.sendMessage(mHandler.obtainMessage(MSG_VOICE_RESULTS));
+    }
+
+    private void handleVoiceResults() {
+        mAfterVoiceInput = true;
+        mImmediatelyAfterVoiceInput = true;
+
+        InputConnection ic = getCurrentInputConnection();
+        if (!isFullscreenMode()) {
+            // Start listening for updates to the text from typing, etc.
+            if (ic != null) {
+                ExtractedTextRequest req = new ExtractedTextRequest();
+                ic.getExtractedText(req, InputConnection.GET_EXTRACTED_TEXT_MONITOR);
+            }
+        }
+
+        vibrate();
+        switchToKeyboardView();
+
+        final List<CharSequence> nBest = new ArrayList<CharSequence>();
+        boolean capitalizeFirstWord = preferCapitalization()
+                || (mKeyboardSwitcher.isAlphabetMode() && mInputView.isShifted());
+        for (String c : mVoiceResults.candidates) {
+            if (capitalizeFirstWord) {
+                c = Character.toUpperCase(c.charAt(0)) + c.substring(1, c.length());
+            }
+            nBest.add(c);
+        }
+
+        if (nBest.size() == 0) {
+            return;
+        }
+
+        String bestResult = nBest.get(0).toString();
+
+        mVoiceInput.logVoiceInputDelivered();
+
+        mHints.registerVoiceResult(bestResult);
+
+        if (ic != null) ic.beginBatchEdit(); // To avoid extra updates on committing older text
+
+        commitTyped(ic);
+        EditingUtil.appendText(ic, bestResult);
+
+        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);
+
+    }
+
+    private void setSuggestions(
+            List<CharSequence> suggestions,
+            boolean completions,
+
+            boolean typedWordValid,
+            boolean haveMinimalSuggestion) {
+
+        if (mIsShowingHint) {
+             setCandidatesView(mCandidateViewContainer);
+             mIsShowingHint = false;
+        }
+
+        if (mCandidateView != null) {
+            mCandidateView.setSuggestions(
+                    suggestions, completions, typedWordValid, haveMinimalSuggestion);
+        }
+    }
+
+    private void updateSuggestions() {
+        mSuggestionShouldReplaceCurrentWord = false;
+
+        ((LatinKeyboard) mInputView.getKeyboard()).setPreferredLetters(null);
+
+        // Check if we have a suggestion engine attached.
+        if ((mSuggest == null || !isPredictionOn()) && !mVoiceInputHighlighted) {
+            return;
+        }
+
+        if (!mPredicting) {
+            setNextSuggestions();
+            return;
+        }
+
+        List<CharSequence> stringList = mSuggest.getSuggestions(mInputView, mWord, false);
+        int[] nextLettersFrequencies = mSuggest.getNextLettersFrequencies();
+
+        ((LatinKeyboard) mInputView.getKeyboard()).setPreferredLetters(nextLettersFrequencies);
+
+        boolean correctionAvailable = mSuggest.hasMinimalCorrection();
+        //|| mCorrectionMode == mSuggest.CORRECTION_FULL;
+        CharSequence typedWord = mWord.getTypedWord();
+        // If we're in basic correct
+        boolean typedWordValid = mSuggest.isValidWord(typedWord) ||
+                (preferCapitalization() && mSuggest.isValidWord(typedWord.toString().toLowerCase()));
+        if (mCorrectionMode == Suggest.CORRECTION_FULL) {
+            correctionAvailable |= typedWordValid;
+        }
+        // Don't auto-correct words with multiple capital letter
+        correctionAvailable &= !mWord.isMostlyCaps();
+
+        setSuggestions(stringList, false, typedWordValid, correctionAvailable);
+        if (stringList.size() > 0) {
+            if (correctionAvailable && !typedWordValid && stringList.size() > 1) {
+                mBestWord = stringList.get(1);
+            } else {
+                mBestWord = typedWord;
+            }
+        } else {
+            mBestWord = null;
+        }
+        setCandidatesViewShown(isCandidateStripVisible() || mCompletionOn);
+    }
+
+    private void pickDefaultSuggestion() {
+        // Complete any pending candidate query first
+        if (mHandler.hasMessages(MSG_UPDATE_SUGGESTIONS)) {
+            mHandler.removeMessages(MSG_UPDATE_SUGGESTIONS);
+            updateSuggestions();
+        }
+        if (mBestWord != null) {
+            TextEntryState.acceptedDefault(mWord.getTypedWord(), mBestWord);
+            mJustAccepted = true;
+            pickSuggestion(mBestWord);
+            // Add the word to the auto dictionary if it's not a known word
+            checkAddToDictionary(mBestWord, AutoDictionary.FREQUENCY_FOR_TYPED);
+        }
+    }
+
+    public void pickSuggestionManually(int index, CharSequence suggestion) {
+        if (mAfterVoiceInput && mShowingVoiceSuggestions) mVoiceInput.logNBestChoose(index);
+
+        InputConnection ic = getCurrentInputConnection();
+        if (ic != null) {
+            ic.beginBatchEdit();
+        }
+        if (mCompletionOn && mCompletions != null && index >= 0
+                && index < mCompletions.length) {
+            CompletionInfo ci = mCompletions[index];
+            if (ic != null) {
+                ic.commitCompletion(ci);
+            }
+            mCommittedLength = suggestion.length();
+            if (mCandidateView != null) {
+                mCandidateView.clear();
+            }
+            updateShiftKeyState(getCurrentInputEditorInfo());
+            if (ic != null) {
+                ic.endBatchEdit();
+            }
+            return;
+        }
+
+        // If this is a punctuation, apply it through the normal key press
+        if (suggestion.length() == 1 && isWordSeparator(suggestion.charAt(0))) {
+            onKey(suggestion.charAt(0), null);
+            if (ic != null) {
+                ic.endBatchEdit();
+            }
+            return;
+        }
+        mJustAccepted = true;
+        pickSuggestion(suggestion);
+        // Add the word to the auto dictionary if it's not a known word
+        checkAddToDictionary(suggestion, AutoDictionary.FREQUENCY_FOR_PICKED);
+        TextEntryState.acceptedSuggestion(mComposing.toString(), suggestion);
+        // Follow it with a space
+        if (mAutoSpace) {
+            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)) {
+            mCandidateView.showAddToDictionaryHint(suggestion);
+        }
+        if (ic != null) {
+            ic.endBatchEdit();
+        }
+    }
+
+    private void pickSuggestion(CharSequence suggestion) {
+        if (mCapsLock) {
+            suggestion = suggestion.toString().toUpperCase();
+        } else if (preferCapitalization()
+                || (mKeyboardSwitcher.isAlphabetMode() && mInputView.isShifted())) {
+            suggestion = suggestion.toString().toUpperCase().charAt(0)
+                    + suggestion.subSequence(1, suggestion.length()).toString();
+        }
+        InputConnection ic = getCurrentInputConnection();
+        if (ic != null) {
+            if (mSuggestionShouldReplaceCurrentWord) {
+                EditingUtil.deleteWordAtCursor(ic, getWordSeparators());
+            }
+            if (!VoiceInput.DELETE_SYMBOL.equals(suggestion)) {
+                ic.commitText(suggestion, 1);
+            }
+        }
+        mPredicting = false;
+        mCommittedLength = suggestion.length();
+        ((LatinKeyboard) mInputView.getKeyboard()).setPreferredLetters(null);
+        setNextSuggestions();
+        updateShiftKeyState(getCurrentInputEditorInfo());
+    }
+
+    private void setNextSuggestions() {
+        setSuggestions(mSuggestPuncList, false, false, false);
+    }
+
+    private void checkAddToDictionary(CharSequence suggestion, int frequencyDelta) {
+        if (mAutoDictionary.isValidWord(suggestion)
+                || !mSuggest.isValidWord(suggestion.toString().toLowerCase())) {
+            mAutoDictionary.addWord(suggestion.toString(), frequencyDelta);
+        }
+    }
+
+    private boolean isCursorTouchingWord() {
+        InputConnection ic = getCurrentInputConnection();
+        if (ic == null) return false;
+        CharSequence toLeft = ic.getTextBeforeCursor(1, 0);
+        CharSequence toRight = ic.getTextAfterCursor(1, 0);
+        if (!TextUtils.isEmpty(toLeft)
+                && !isWordSeparator(toLeft.charAt(0))) {
+            return true;
+        }
+        if (!TextUtils.isEmpty(toRight)
+                && !isWordSeparator(toRight.charAt(0))) {
+            return true;
+        }
+        return false;
+    }
+
+    public void revertLastWord(boolean deleteChar) {
+        final int length = mComposing.length();
+        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;
+            CharSequence toTheLeft = ic.getTextBeforeCursor(mCommittedLength, 0);
+            if (toTheLeft != null && toTheLeft.length() > 0
+                    && isWordSeparator(toTheLeft.charAt(0))) {
+                toDelete--;
+            }
+            ic.deleteSurroundingText(toDelete, 0);
+            ic.setComposingText(mComposing, 1);
+            TextEntryState.backspace();
+            ic.endBatchEdit();
+            postUpdateSuggestions();
+        } else {
+            sendDownUpKeyEvents(KeyEvent.KEYCODE_DEL);
+            mJustRevertedSeparator = null;
+        }
+    }
+
+    protected String getWordSeparators() {
+        return mWordSeparators;
+    }
+
+    public boolean isWordSeparator(int code) {
+        String separators = getWordSeparators();
+        return separators.contains(String.valueOf((char)code));
+    }
+
+    public boolean isSentenceSeparator(int code) {
+        return mSentenceSeparators.contains(String.valueOf((char)code));
+    }
+
+    private void sendSpace() {
+        sendKeyChar((char)KEYCODE_SPACE);
+        updateShiftKeyState(getCurrentInputEditorInfo());
+        //onKey(KEY_SPACE[0], KEY_SPACE);
+    }
+
+    public boolean preferCapitalization() {
+        return mWord.isCapitalized();
+    }
+
+    public void swipeRight() {
+        if (userHasNotTypedRecently() && VOICE_INSTALLED && mEnableVoice &&
+                fieldCanDoVoice(makeFieldContext())) {
+            startListening(true /* was a swipe */);
+        }
+
+        if (LatinKeyboardView.DEBUG_AUTO_PLAY) {
+            ClipboardManager cm = ((ClipboardManager)getSystemService(CLIPBOARD_SERVICE));
+            CharSequence text = cm.getText();
+            if (!TextUtils.isEmpty(text)) {
+                mInputView.startPlaying(text.toString());
+            }
+        }
+    }
+
+    private void toggleLanguage(boolean reset, boolean next) {
+        if (reset) {
+            mLanguageSwitcher.reset();
+        } else {
+            if (next) {
+                mLanguageSwitcher.next();
+            } else {
+                mLanguageSwitcher.prev();
+            }
+        }
+        int currentKeyboardMode = mKeyboardSwitcher.getKeyboardMode();
+        reloadKeyboards();
+        mKeyboardSwitcher.makeKeyboards(true);
+        mKeyboardSwitcher.setKeyboardMode(currentKeyboardMode, 0,
+                mEnableVoiceButton && mEnableVoice);
+        initSuggest(mLanguageSwitcher.getInputLanguage());
+        mLanguageSwitcher.persist();
+        updateShiftKeyState(getCurrentInputEditorInfo());
+    }
+
+    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
+            String key) {
+        if (PREF_SELECTED_LANGUAGES.equals(key)) {
+            mLanguageSwitcher.loadLocales(sharedPreferences);
+            mRefreshKeyboardRequired = true;
+        }
+    }
+
+    public void swipeLeft() {
+    }
+
+    public void swipeDown() {
+        handleClose();
+    }
+
+    public void swipeUp() {
+        //launchSettings();
+    }
+
+    public void onPress(int primaryCode) {
+        vibrate();
+        playKeyClick(primaryCode);
+    }
+
+    public void onRelease(int primaryCode) {
+        // Reset any drag flags in the keyboard
+        ((LatinKeyboard) mInputView.getKeyboard()).keyReleased();
+        //vibrate();
+    }
+
+    private FieldContext makeFieldContext() {
+        return new FieldContext(
+                getCurrentInputConnection(),
+                getCurrentInputEditorInfo(),
+                mLanguageSwitcher.getInputLanguage(),
+                mLanguageSwitcher.getEnabledLanguages());
+    }
+
+    private boolean fieldCanDoVoice(FieldContext fieldContext) {
+        return !mPasswordText
+                && mVoiceInput != null
+                && !mVoiceInput.isBlacklistedField(fieldContext);
+    }
+
+    private boolean shouldShowVoiceButton(FieldContext fieldContext, EditorInfo attribute) {
+        return ENABLE_VOICE_BUTTON && fieldCanDoVoice(fieldContext)
+                && !(attribute != null && attribute.privateImeOptions != null
+                        && attribute.privateImeOptions.equals(IME_OPTION_NO_MICROPHONE))
+                && RecognitionManager.isRecognitionAvailable(this);
+    }
+
+    // receive ringer mode changes to detect silent mode
+    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            updateRingerMode();
+        }
+    };
+
+    // update flags for silent mode
+    private void updateRingerMode() {
+        if (mAudioManager == null) {
+            mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+        }
+        if (mAudioManager != null) {
+            mSilentMode = (mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL);
+        }
+    }
+
+    private boolean userHasNotTypedRecently() {
+        return (SystemClock.uptimeMillis() - mLastKeyTime)
+            > MIN_MILLIS_AFTER_TYPING_BEFORE_SWIPE;
+    }
+
+    private void playKeyClick(int primaryCode) {
+        // if mAudioManager is null, we don't have the ringer state yet
+        // mAudioManager will be set by updateRingerMode
+        if (mAudioManager == null) {
+            if (mInputView != null) {
+                updateRingerMode();
+            }
+        }
+        if (mSoundOn && !mSilentMode) {
+            // FIXME: Volume and enable should come from UI settings
+            // FIXME: These should be triggered after auto-repeat logic
+            int sound = AudioManager.FX_KEYPRESS_STANDARD;
+            switch (primaryCode) {
+                case Keyboard.KEYCODE_DELETE:
+                    sound = AudioManager.FX_KEYPRESS_DELETE;
+                    break;
+                case KEYCODE_ENTER:
+                    sound = AudioManager.FX_KEYPRESS_RETURN;
+                    break;
+                case KEYCODE_SPACE:
+                    sound = AudioManager.FX_KEYPRESS_SPACEBAR;
+                    break;
+            }
+            mAudioManager.playSoundEffect(sound, FX_VOLUME);
+        }
+    }
+
+    private void vibrate() {
+        if (!mVibrateOn) {
+            return;
+        }
+        if (mInputView != null) {
+            mInputView.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP,
+                    HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
+        }
+    }
+
+    private void checkTutorial(String privateImeOptions) {
+        if (privateImeOptions == null) return;
+        if (privateImeOptions.equals("com.android.setupwizard:ShowTutorial")) {
+            if (mTutorial == null) startTutorial();
+        } else if (privateImeOptions.equals("com.android.setupwizard:HideTutorial")) {
+            if (mTutorial != null) {
+                if (mTutorial.close()) {
+                    mTutorial = null;
+                }
+            }
+        }
+    }
+
+    private void startTutorial() {
+        mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_START_TUTORIAL), 500);
+    }
+
+    void tutorialDone() {
+        mTutorial = null;
+    }
+
+    void promoteToUserDictionary(String word, int frequency) {
+        if (mUserDictionary.isValidWord(word)) return;
+        mUserDictionary.addWord(word, frequency);
+    }
+
+    WordComposer getCurrentWord() {
+        return mWord;
+    }
+
+    private void updateCorrectionMode() {
+        mHasDictionary = mSuggest != null ? mSuggest.hasMainDictionary() : false;
+        mAutoCorrectOn = (mAutoCorrectEnabled || mQuickFixes)
+                && !mInputTypeNoAutoCorrect && mHasDictionary;
+        mCorrectionMode = (mAutoCorrectOn && mAutoCorrectEnabled)
+                ? Suggest.CORRECTION_FULL
+                : (mAutoCorrectOn ? Suggest.CORRECTION_BASIC : Suggest.CORRECTION_NONE);
+        if (mSuggest != null) {
+            mSuggest.setCorrectionMode(mCorrectionMode);
+        }
+    }
+
+    private void updateAutoTextEnabled(Locale systemLocale) {
+        if (mSuggest == null) return;
+        boolean different = !systemLocale.getLanguage().equalsIgnoreCase(mLocale.substring(0, 2));
+        mSuggest.setAutoTextEnabled(!different && mQuickFixes);
+    }
+
+    protected void launchSettings() {
+        launchSettings(LatinIMESettings.class);
+    }
+
+    protected void launchSettings(Class settingsClass) {
+        handleClose();
+        Intent intent = new Intent();
+        intent.setClass(LatinIME.this, settingsClass);
+        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        startActivity(intent);
+    }
+
+    private void loadSettings() {
+        // Get the settings preferences
+        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
+        mVibrateOn = sp.getBoolean(PREF_VIBRATE_ON, false);
+        mSoundOn = sp.getBoolean(PREF_SOUND_ON, false);
+        mAutoCap = sp.getBoolean(PREF_AUTO_CAP, true);
+        mQuickFixes = sp.getBoolean(PREF_QUICK_FIXES, true);
+        mHasUsedVoiceInput = sp.getBoolean(PREF_HAS_USED_VOICE_INPUT, false);
+        mHasUsedVoiceInputUnsupportedLocale =
+                sp.getBoolean(PREF_HAS_USED_VOICE_INPUT_UNSUPPORTED_LOCALE, false);
+
+        // Get the current list of supported locales and check the current locale against that
+        // list. We cache this value so as not to check it every time the user starts a voice
+        // input. Because this method is called by onStartInputView, this should mean that as
+        // long as the locale doesn't change while the user is keeping the IME open, the
+        // value should never be stale.
+        String supportedLocalesString = SettingsUtil.getSettingsString(
+                getContentResolver(),
+                SettingsUtil.LATIN_IME_VOICE_INPUT_SUPPORTED_LOCALES,
+                DEFAULT_VOICE_INPUT_SUPPORTED_LOCALES);
+        ArrayList<String> voiceInputSupportedLocales =
+                newArrayList(supportedLocalesString.split("\\s+"));
+
+        mLocaleSupportedForVoiceInput = voiceInputSupportedLocales.contains(mLocale);
+
+        mShowSuggestions = sp.getBoolean(PREF_SHOW_SUGGESTIONS, true);
+
+        if (VOICE_INSTALLED) {
+            final String voiceMode = sp.getString(PREF_VOICE_MODE,
+                    getString(R.string.voice_mode_main));
+            boolean enableVoice = !voiceMode.equals(getString(R.string.voice_mode_off))
+                    && mEnableVoiceButton;
+            boolean voiceOnPrimary = voiceMode.equals(getString(R.string.voice_mode_main));
+            if (mKeyboardSwitcher != null &&
+                    (enableVoice != mEnableVoice || voiceOnPrimary != mVoiceOnPrimary)) {
+                mKeyboardSwitcher.setVoiceMode(enableVoice, voiceOnPrimary);
+            }
+            mEnableVoice = enableVoice;
+            mVoiceOnPrimary = voiceOnPrimary;
+        }
+        mAutoCorrectEnabled = sp.getBoolean(PREF_AUTO_COMPLETE,
+                mResources.getBoolean(R.bool.enable_autocorrect)) & mShowSuggestions;
+        updateCorrectionMode();
+        updateAutoTextEnabled(mResources.getConfiguration().locale);
+        mLanguageSwitcher.loadLocales(sp);
+    }
+
+    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));
+            }
+        }
+    }
+
+    private void showOptionsMenu() {
+        AlertDialog.Builder builder = new AlertDialog.Builder(this);
+        builder.setCancelable(true);
+        builder.setIcon(R.drawable.ic_dialog_keyboard);
+        builder.setNegativeButton(android.R.string.cancel, null);
+        CharSequence itemSettings = getString(R.string.english_ime_settings);
+        CharSequence itemInputMethod = getString(R.string.inputMethod);
+        builder.setItems(new CharSequence[] {
+                itemSettings, itemInputMethod},
+                new DialogInterface.OnClickListener() {
+
+            public void onClick(DialogInterface di, int position) {
+                di.dismiss();
+                switch (position) {
+                    case POS_SETTINGS:
+                        launchSettings();
+                        break;
+                    case POS_METHOD:
+                        ((InputMethodManager) getSystemService(INPUT_METHOD_SERVICE))
+                            .showInputMethodPicker();
+                        break;
+                }
+            }
+        });
+        builder.setTitle(mResources.getString(R.string.english_ime_name));
+        mOptionsDialog = builder.create();
+        Window window = mOptionsDialog.getWindow();
+        WindowManager.LayoutParams lp = window.getAttributes();
+        lp.token = mInputView.getWindowToken();
+        lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
+        window.setAttributes(lp);
+        window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
+        mOptionsDialog.show();
+    }
+
+    private void changeKeyboardMode() {
+        mKeyboardSwitcher.toggleSymbols();
+        if (mCapsLock && mKeyboardSwitcher.isAlphabetMode()) {
+            ((LatinKeyboard) mInputView.getKeyboard()).setShiftLocked(mCapsLock);
+        }
+
+        updateShiftKeyState(getCurrentInputEditorInfo());
+    }
+
+    public static <E> ArrayList<E> newArrayList(E... elements) {
+        int capacity = (elements.length * 110) / 100 + 5;
+        ArrayList<E> list = new ArrayList<E>(capacity);
+        Collections.addAll(list, elements);
+        return list;
+    }
+
+    @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+        super.dump(fd, fout, args);
+
+        final Printer p = new PrintWriterPrinter(fout);
+        p.println("LatinIME state :");
+        p.println("  Keyboard mode = " + mKeyboardSwitcher.getKeyboardMode());
+        p.println("  mCapsLock=" + mCapsLock);
+        p.println("  mComposing=" + mComposing.toString());
+        p.println("  mPredictionOn=" + mPredictionOn);
+        p.println("  mCorrectionMode=" + mCorrectionMode);
+        p.println("  mPredicting=" + mPredicting);
+        p.println("  mAutoCorrectOn=" + mAutoCorrectOn);
+        p.println("  mAutoSpace=" + mAutoSpace);
+        p.println("  mCompletionOn=" + mCompletionOn);
+        p.println("  TextEntryState.state=" + TextEntryState.getState());
+        p.println("  mSoundOn=" + mSoundOn);
+        p.println("  mVibrateOn=" + mVibrateOn);
+    }
+
+    // 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;
+        mLastCpsTime = now;
+        mCpsIndex = (mCpsIndex + 1) % CPS_BUFFER_SIZE;
+        long total = 0;
+        for (int i = 0; i < CPS_BUFFER_SIZE; i++) total += mCpsIntervals[i];
+        System.out.println("CPS = " + ((CPS_BUFFER_SIZE * 1000f) / total));
+    }
+
+}
diff --git a/java/src/com/android/inputmethod/latin/LatinIMEBackupAgent.java b/java/src/com/android/inputmethod/latin/LatinIMEBackupAgent.java
new file mode 100644
index 0000000..62da86c
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/LatinIMEBackupAgent.java
@@ -0,0 +1,31 @@
+/*
+ * 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.latin;
+
+import android.app.backup.BackupHelperAgent;
+import android.app.backup.SharedPreferencesBackupHelper;
+
+/**
+ * Backs up the Latin IME shared preferences.
+ */
+public class LatinIMEBackupAgent extends BackupHelperAgent {
+
+    public void onCreate() {
+        addHelper("shared_pref", new SharedPreferencesBackupHelper(this,
+                getPackageName() + "_preferences"));
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/LatinIMESettings.java b/java/src/com/android/inputmethod/latin/LatinIMESettings.java
new file mode 100644
index 0000000..f76ec99
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/LatinIMESettings.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2008-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.latin;
+
+import java.util.ArrayList;
+import java.util.Locale;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.backup.BackupManager;
+import android.content.DialogInterface;
+import android.content.SharedPreferences;
+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.RecognitionManager;
+import android.text.AutoText;
+import android.util.Log;
+
+import com.android.inputmethod.voice.SettingsUtil;
+import com.android.inputmethod.voice.VoiceInputLogger;
+
+public class LatinIMESettings extends PreferenceActivity
+        implements SharedPreferences.OnSharedPreferenceChangeListener,
+        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 TAG = "LatinIMESettings";
+
+    // Dialog ids
+    private static final int VOICE_INPUT_CONFIRM_DIALOG = 0;
+
+    private CheckBoxPreference mQuickFixes;
+    private CheckBoxPreference mShowSuggestions;
+    private ListPreference mVoicePreference;
+    private boolean mVoiceOn;
+
+    private VoiceInputLogger mLogger;
+
+    private boolean mOkClicked = false;
+    private String mVoiceModeOff;
+
+    @Override
+    protected void onCreate(Bundle icicle) {
+        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);
+
+        mVoiceModeOff = getString(R.string.voice_mode_off);
+        mVoiceOn = !(prefs.getString(VOICE_SETTINGS_KEY, mVoiceModeOff).equals(mVoiceModeOff));
+        mLogger = VoiceInputLogger.getLogger(this);
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        int autoTextSize = AutoText.getSize(getListView());
+        if (autoTextSize < 1) {
+            ((PreferenceGroup) findPreference(PREDICTION_SETTINGS_KEY))
+                    .removePreference(mQuickFixes);
+        }
+        if (!LatinIME.VOICE_INSTALLED
+                || !RecognitionManager.isRecognitionAvailable(this)) {
+            getPreferenceScreen().removePreference(mVoicePreference);
+        } else {
+            updateVoiceModeSummary();
+        }
+    }
+
+    @Override
+    protected void onDestroy() {
+        getPreferenceManager().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(
+                this);
+        super.onDestroy();
+    }
+
+    public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
+        (new BackupManager(this)).dataChanged();
+        // If turning on voice input, show dialog
+        if (key.equals(VOICE_SETTINGS_KEY) && !mVoiceOn) {
+            if (! prefs.getString(VOICE_SETTINGS_KEY, mVoiceModeOff)
+                    .equals(mVoiceModeOff)) {
+                showVoiceConfirmation();
+            }
+        }
+        mVoiceOn = !(prefs.getString(VOICE_SETTINGS_KEY, mVoiceModeOff).equals(mVoiceModeOff));
+        updateVoiceModeSummary();
+    }
+
+    private void showVoiceConfirmation() {
+        mOkClicked = false;
+        showDialog(VOICE_INPUT_CONFIRM_DIALOG);
+    }
+
+    private void updateVoiceModeSummary() {
+        mVoicePreference.setSummary(
+                getResources().getStringArray(R.array.voice_input_modes_summary)
+                [mVoicePreference.findIndexOfValue(mVoicePreference.getValue())]);
+    }
+
+    @Override
+    protected Dialog onCreateDialog(int id) {
+        switch (id) {
+            case VOICE_INPUT_CONFIRM_DIALOG:
+                DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
+                    public void onClick(DialogInterface dialog, int whichButton) {
+                        if (whichButton == DialogInterface.BUTTON_NEGATIVE) {
+                            mVoicePreference.setValue(mVoiceModeOff);
+                            mLogger.settingsWarningDialogCancel();
+                        } else if (whichButton == DialogInterface.BUTTON_POSITIVE) {
+                            mOkClicked = true;
+                            mLogger.settingsWarningDialogOk();
+                        }
+                        updateVoicePreference();
+                    }
+                };
+                AlertDialog.Builder builder = new AlertDialog.Builder(this)
+                        .setTitle(R.string.voice_warning_title)
+                        .setPositiveButton(android.R.string.ok, listener)
+                        .setNegativeButton(android.R.string.cancel, listener);
+
+                // Get the current list of supported locales and check the current locale against
+                // that list, to decide whether to put a warning that voice input will not work in
+                // the current language as part of the pop-up confirmation dialog.
+                String supportedLocalesString = SettingsUtil.getSettingsString(
+                        getContentResolver(),
+                        SettingsUtil.LATIN_IME_VOICE_INPUT_SUPPORTED_LOCALES,
+                        LatinIME.DEFAULT_VOICE_INPUT_SUPPORTED_LOCALES);
+                ArrayList<String> voiceInputSupportedLocales =
+                        LatinIME.newArrayList(supportedLocalesString.split("\\s+"));
+                boolean localeSupported = voiceInputSupportedLocales.contains(
+                        Locale.getDefault().toString());
+
+                if (localeSupported) {
+                    String message = getString(R.string.voice_warning_may_not_understand) + "\n\n" +
+                            getString(R.string.voice_hint_dialog_message);
+                    builder.setMessage(message);
+                } else {
+                    String message = getString(R.string.voice_warning_locale_not_supported) +
+                            "\n\n" + getString(R.string.voice_warning_may_not_understand) + "\n\n" +
+                            getString(R.string.voice_hint_dialog_message);
+                    builder.setMessage(message);
+                }
+
+                AlertDialog dialog = builder.create();
+                dialog.setOnDismissListener(this);
+                mLogger.settingsWarningDialogShown();
+                return dialog;
+            default:
+                Log.e(TAG, "unknown dialog " + id);
+                return null;
+        }
+    }
+
+    public void onDismiss(DialogInterface dialog) {
+        mLogger.settingsWarningDialogDismissed();
+        if (!mOkClicked) {
+            // This assumes that onPreferenceClick gets called first, and this if the user
+            // agreed after the warning, we set the mOkClicked value to true.
+            mVoicePreference.setValue(mVoiceModeOff);
+        }
+    }
+
+    private void updateVoicePreference() {
+        boolean isChecked = !mVoicePreference.getValue().equals(mVoiceModeOff);
+        if (isChecked) {
+            mLogger.voiceInputSettingEnabled();
+        } else {
+            mLogger.voiceInputSettingDisabled();
+        }
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/LatinKeyboard.java b/java/src/com/android/inputmethod/latin/LatinKeyboard.java
new file mode 100644
index 0000000..58e1dc7
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/LatinKeyboard.java
@@ -0,0 +1,762 @@
+/*
+ * Copyright (C) 2008-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.latin;
+
+import java.util.List;
+import java.util.Locale;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
+import android.graphics.Rect;
+import android.graphics.Paint.Align;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.inputmethodservice.Keyboard;
+import android.text.TextPaint;
+import android.util.Log;
+import android.view.ViewConfiguration;
+import android.view.inputmethod.EditorInfo;
+
+public class LatinKeyboard extends Keyboard {
+
+    private static final boolean DEBUG_PREFERRED_LETTER = false;
+    private static final String TAG = "LatinKeyboard";
+
+    private Drawable mShiftLockIcon;
+    private Drawable mShiftLockPreviewIcon;
+    private Drawable mOldShiftIcon;
+    private Drawable mOldShiftPreviewIcon;
+    private Drawable mSpaceIcon;
+    private Drawable mSpacePreviewIcon;
+    private Drawable mMicIcon;
+    private Drawable mMicPreviewIcon;
+    private Drawable m123MicIcon;
+    private Drawable m123MicPreviewIcon;
+    private Drawable mButtonArrowLeftIcon;
+    private Drawable mButtonArrowRightIcon;
+    private Key mShiftKey;
+    private Key mEnterKey;
+    private Key mF1Key;
+    private Key mSpaceKey;
+    private Key m123Key;
+    private int mSpaceKeyIndex = -1;
+    private int mSpaceDragStartX;
+    private int mSpaceDragLastDiff;
+    /* package */ Locale mLocale;
+    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
+    private boolean mVoiceEnabled;
+    private boolean mIsAlphaKeyboard;
+    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 static final int SHIFT_OFF = 0;
+    private static final int SHIFT_ON = 1;
+    private static final int SHIFT_LOCKED = 2;
+    
+    private int mShiftState = SHIFT_OFF;
+
+    private static final float SPACEBAR_DRAG_THRESHOLD = 0.8f;
+    private static final float OVERLAP_PERCENTAGE_LOW_PROB = 0.70f;
+    private static final float OVERLAP_PERCENTAGE_HIGH_PROB = 0.85f;
+
+    static int sSpacebarVerticalCorrection;
+
+    public LatinKeyboard(Context context, int xmlLayoutResId) {
+        this(context, xmlLayoutResId, 0);
+    }
+
+    public LatinKeyboard(Context context, int xmlLayoutResId, int mode) {
+        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);
+        mShiftLockPreviewIcon.setBounds(0, 0, 
+                mShiftLockPreviewIcon.getIntrinsicWidth(),
+                mShiftLockPreviewIcon.getIntrinsicHeight());
+        mSpaceIcon = res.getDrawable(R.drawable.sym_keyboard_space);
+        mSpacePreviewIcon = res.getDrawable(R.drawable.sym_keyboard_feedback_space);
+        mMicIcon = res.getDrawable(R.drawable.sym_keyboard_mic);
+        mMicPreviewIcon = res.getDrawable(R.drawable.sym_keyboard_feedback_mic);
+        setDefaultBounds(mMicPreviewIcon);
+        mButtonArrowLeftIcon = res.getDrawable(R.drawable.sym_keyboard_language_arrows_left);
+        mButtonArrowRightIcon = res.getDrawable(R.drawable.sym_keyboard_language_arrows_right);
+        m123MicIcon = res.getDrawable(R.drawable.sym_keyboard_123_mic);
+        m123MicPreviewIcon = res.getDrawable(R.drawable.sym_keyboard_feedback_123_mic);
+        setDefaultBounds(m123MicPreviewIcon);
+        sSpacebarVerticalCorrection = res.getDimensionPixelOffset(
+                R.dimen.spacebar_vertical_correction);
+        mIsAlphaKeyboard = xmlLayoutResId == R.xml.kbd_qwerty;
+        mSpaceKeyIndex = indexOf((int) ' ');
+    }
+
+    public LatinKeyboard(Context context, int layoutTemplateResId, 
+            CharSequence characters, int columns, int horizontalPadding) {
+        super(context, layoutTemplateResId, characters, columns, horizontalPadding);
+    }
+
+    @Override
+    protected Key createKeyFromXml(Resources res, Row parent, int x, int y, 
+            XmlResourceParser parser) {
+        Key key = new LatinKey(res, parent, x, y, parser);
+        switch (key.codes[0]) {
+        case 10:
+            mEnterKey = key;
+            break;
+        case LatinKeyboardView.KEYCODE_F1:
+            mF1Key = key;
+            break;
+        case 32:
+            mSpaceKey = key;
+            break;
+        case KEYCODE_MODE_CHANGE:
+            m123Key = key;
+            m123Label = key.label;
+            break;
+        }
+        return key;
+    }
+
+    void setImeOptions(Resources res, int mode, int options) {
+        if (mEnterKey != null) {
+            // Reset some of the rarely used attributes.
+            mEnterKey.popupCharacters = null;
+            mEnterKey.popupResId = 0;
+            mEnterKey.text = null;
+            switch (options&(EditorInfo.IME_MASK_ACTION|EditorInfo.IME_FLAG_NO_ENTER_ACTION)) {
+                case EditorInfo.IME_ACTION_GO:
+                    mEnterKey.iconPreview = null;
+                    mEnterKey.icon = null;
+                    mEnterKey.label = res.getText(R.string.label_go_key);
+                    break;
+                case EditorInfo.IME_ACTION_NEXT:
+                    mEnterKey.iconPreview = null;
+                    mEnterKey.icon = null;
+                    mEnterKey.label = res.getText(R.string.label_next_key);
+                    break;
+                case EditorInfo.IME_ACTION_DONE:
+                    mEnterKey.iconPreview = null;
+                    mEnterKey.icon = null;
+                    mEnterKey.label = res.getText(R.string.label_done_key);
+                    break;
+                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.label = null;
+                    break;
+                case EditorInfo.IME_ACTION_SEND:
+                    mEnterKey.iconPreview = null;
+                    mEnterKey.icon = null;
+                    mEnterKey.label = res.getText(R.string.label_send_key);
+                    break;
+                default:
+                    if (mode == KeyboardSwitcher.MODE_IM) {
+                        mEnterKey.icon = null;
+                        mEnterKey.iconPreview = null;
+                        mEnterKey.label = ":-)";
+                        mEnterKey.text = ":-) ";
+                        mEnterKey.popupResId = R.xml.popup_smileys;
+                    } else {
+                        mEnterKey.iconPreview = res.getDrawable(
+                                R.drawable.sym_keyboard_feedback_return);
+                        mEnterKey.icon = res.getDrawable(
+                                R.drawable.sym_keyboard_return);
+                        mEnterKey.label = null;
+                    }
+                    break;
+            }
+            // Set the initial size of the preview icon
+            if (mEnterKey.iconPreview != null) {
+                mEnterKey.iconPreview.setBounds(0, 0, 
+                        mEnterKey.iconPreview.getIntrinsicWidth(),
+                        mEnterKey.iconPreview.getIntrinsicHeight());
+            }
+        }
+    }
+    
+    void enableShiftLock() {
+        int index = getShiftKeyIndex();
+        if (index >= 0) {
+            mShiftKey = getKeys().get(index);
+            if (mShiftKey instanceof LatinKey) {
+                ((LatinKey)mShiftKey).enableShiftLock();
+            }
+            mOldShiftIcon = mShiftKey.icon;
+            mOldShiftPreviewIcon = mShiftKey.iconPreview;
+        }
+    }
+
+    void setShiftLocked(boolean shiftLocked) {
+        if (mShiftKey != null) {
+            if (shiftLocked) {
+                mShiftKey.on = true;
+                mShiftKey.icon = mShiftLockIcon;
+                mShiftState = SHIFT_LOCKED;
+            } else {
+                mShiftKey.on = false;
+                mShiftKey.icon = mShiftLockIcon;
+                mShiftState = SHIFT_ON;
+            }
+        }
+    }
+
+    boolean isShiftLocked() {
+        return mShiftState == SHIFT_LOCKED;
+    }
+    
+    @Override
+    public boolean setShifted(boolean shiftState) {
+        boolean shiftChanged = false;
+        if (mShiftKey != null) {
+            if (shiftState == false) {
+                shiftChanged = mShiftState != SHIFT_OFF;
+                mShiftState = SHIFT_OFF;
+                mShiftKey.on = false;
+                mShiftKey.icon = mOldShiftIcon;
+            } else {
+                if (mShiftState == SHIFT_OFF) {
+                    shiftChanged = mShiftState == SHIFT_OFF;
+                    mShiftState = SHIFT_ON;
+                    mShiftKey.icon = mShiftLockIcon;
+                }
+            }
+        } else {
+            return super.setShifted(shiftState);
+        }
+        return shiftChanged;
+    }
+
+    @Override
+    public boolean isShifted() {
+        if (mShiftKey != null) {
+            return mShiftState != SHIFT_OFF;
+        } else {
+            return super.isShifted();
+        }
+    }
+
+    public void setExtension(int resId) {
+        mExtensionResId = resId;
+    }
+
+    public int getExtension() {
+        return mExtensionResId;
+    }
+
+    private void setDefaultBounds(Drawable drawable) {
+        drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
+    }
+
+    public void setVoiceMode(boolean hasVoiceButton, boolean hasVoice) {
+        mHasVoiceButton = hasVoiceButton;
+        mVoiceEnabled = hasVoice;
+        updateF1Key();
+    }
+
+    private void updateF1Key() {
+        if (mF1Key == null) return;
+        if (m123Key != null && mIsAlphaKeyboard) {
+            if (mVoiceEnabled && !mHasVoiceButton) {
+                m123Key.icon = m123MicIcon;
+                m123Key.iconPreview = m123MicPreviewIcon;
+                m123Key.label = null;
+            } else {
+                m123Key.icon = null;
+                m123Key.iconPreview = null;
+                m123Key.label = m123Label;
+            }
+        }
+
+        if (mHasVoiceButton && mVoiceEnabled) {
+            mF1Key.codes = new int[] { LatinKeyboardView.KEYCODE_VOICE };
+            mF1Key.label = null;
+            mF1Key.icon = mMicIcon;
+            mF1Key.iconPreview = mMicPreviewIcon;
+        } else {
+            mF1Key.label = ",";
+            mF1Key.codes = new int[] { ',' };
+            mF1Key.icon = null;
+            mF1Key.iconPreview = null;
+        }
+    }
+
+    private void updateSpaceBarForLocale() {
+        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);
+            mSpaceKey.icon = new BitmapDrawable(mRes, buffer);
+            mSpaceKey.repeatable = mLanguageSwitcher.getLocaleCount() < 2;
+        } else {
+            mSpaceKey.icon = 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);
+        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);
+        // Put arrows on either side of the text
+        if (mLanguageSwitcher.getLocaleCount() > 1) {
+            Rect bounds = new Rect();
+            paint.getTextBounds(language, 0, language.length(), bounds);
+            drawButtonArrow(mButtonArrowLeftIcon, canvas,
+                    (mSpaceKey.width - bounds.right) / 2
+                    - mButtonArrowLeftIcon.getIntrinsicWidth(),
+                    (int) paint.getTextSize());
+            drawButtonArrow(mButtonArrowRightIcon, canvas,
+                    (mSpaceKey.width + bounds.right) / 2, (int) paint.getTextSize());
+        }
+        // Draw the spacebar icon at the bottom
+        int x = (width - mSpaceIcon.getIntrinsicWidth()) / 2;
+        int y = height - mSpaceIcon.getIntrinsicHeight();
+        mSpaceIcon.setBounds(x, y, 
+                x + mSpaceIcon.getIntrinsicWidth(), y + mSpaceIcon.getIntrinsicHeight());
+        mSpaceIcon.draw(canvas);
+    }
+
+    private void drawButtonArrow(Drawable arrow, Canvas canvas, int x, int bottomY) {
+        arrow.setBounds(x, bottomY - arrow.getIntrinsicHeight(), x + arrow.getIntrinsicWidth(),
+                bottomY);
+        arrow.draw(canvas);
+    }
+
+    private String getInputLanguage(int widthAvail, Paint paint) {
+        return chooseDisplayName(mLanguageSwitcher.getInputLocale(), widthAvail, paint);
+    }
+
+    private String getNextInputLanguage(int widthAvail, Paint paint) {
+        return chooseDisplayName(mLanguageSwitcher.getNextInputLocale(), widthAvail, paint);
+    }
+
+    private String getPrevInputLanguage(int widthAvail, Paint paint) {
+        return chooseDisplayName(mLanguageSwitcher.getPrevInputLocale(), widthAvail, paint);
+    }
+
+    private String chooseDisplayName(Locale locale, int widthAvail, Paint paint) {
+        if (widthAvail < (int) (.35 * getMinWidth())) {
+            return LanguageSwitcher.toTitleCase(locale.getLanguage().substring(0, 2));
+        } else {
+            return LanguageSwitcher.toTitleCase(locale.getDisplayLanguage(locale));
+        }
+    }
+
+    private void updateLocaleDrag(int diff) {
+        if (mSlidingLocaleIcon == null) {
+            mSlidingLocaleIcon = new SlidingLocaleDrawable(mSpacePreviewIcon, mSpaceKey.width,
+                    mSpacePreviewIcon.getIntrinsicHeight());
+            mSlidingLocaleIcon.setBounds(0, 0, mSpaceKey.width,
+                    mSpacePreviewIcon.getIntrinsicHeight());
+            mSpaceKey.iconPreview = mSlidingLocaleIcon;
+        }
+        mSlidingLocaleIcon.setDiff(diff);
+        if (Math.abs(diff) == Integer.MAX_VALUE) {
+            mSpaceKey.iconPreview = mSpacePreviewIcon;
+        } else {
+            mSpaceKey.iconPreview = mSlidingLocaleIcon;
+        }
+        mSpaceKey.iconPreview.invalidateSelf();
+    }
+
+    public int getLanguageChangeDirection() {
+        if (mSpaceKey == null || mLanguageSwitcher.getLocaleCount() < 2
+                || Math.abs(mSpaceDragLastDiff) < mSpaceKey.width * SPACEBAR_DRAG_THRESHOLD ) {
+            return 0; // No change
+        }
+        return mSpaceDragLastDiff > 0 ? 1 : -1;
+    }
+
+    public void setLanguageSwitcher(LanguageSwitcher switcher) {
+        mLanguageSwitcher = switcher;
+        Locale locale = mLanguageSwitcher.getLocaleCount() > 0
+                ? mLanguageSwitcher.getInputLocale()
+                : null;
+        if (mLocale != null && mLocale.equals(locale)) return;
+        mLocale = locale;
+        updateSpaceBarForLocale();
+    }
+
+    boolean isCurrentlyInSpace() {
+        return mCurrentlyInSpace;
+    }
+
+    void setPreferredLetters(int[] frequencies) {
+        mPrefLetterFrequencies = frequencies;
+        mPrefLetter = 0;
+    }
+
+    void keyReleased() {
+        mCurrentlyInSpace = false;
+        mSpaceDragLastDiff = 0;
+        mPrefLetter = 0;
+        mPrefLetterX = 0;
+        mPrefLetterY = 0;
+        mPrefDistance = Integer.MAX_VALUE;
+        if (mSpaceKey != null) {
+            updateLocaleDrag(Integer.MAX_VALUE);
+        }
+    }
+
+    /**
+     * Does the magic of locking the touch gesture into the spacebar when
+     * switching input languages.
+     */
+    boolean isInside(LatinKey key, int x, int y) {
+        final int code = key.codes[0];
+        if (code == KEYCODE_SHIFT ||
+                code == KEYCODE_DELETE) {
+            y -= key.height / 10;
+            if (code == KEYCODE_SHIFT) x += key.width / 6;
+            if (code == KEYCODE_DELETE) x -= key.width / 6;
+        } else if (code == LatinIME.KEYCODE_SPACE) {
+            y += LatinKeyboard.sSpacebarVerticalCorrection;
+            if (mLanguageSwitcher.getLocaleCount() > 1) {
+                if (mCurrentlyInSpace) {
+                    int diff = x - mSpaceDragStartX;
+                    if (Math.abs(diff - mSpaceDragLastDiff) > 0) {
+                        updateLocaleDrag(diff);
+                    }
+                    mSpaceDragLastDiff = diff;
+                    return true;
+                } else {
+                    boolean insideSpace = key.isInsideSuper(x, y);
+                    if (insideSpace) {
+                        mCurrentlyInSpace = true;
+                        mSpaceDragStartX = x;
+                        updateLocaleDrag(0);
+                    }
+                    return insideSpace;
+                }
+            }
+        } else if (mPrefLetterFrequencies != null) {
+            // New coordinate? Reset
+            if (mPrefLetterX != x || mPrefLetterY != y) {
+                mPrefLetter = 0;
+                mPrefDistance = Integer.MAX_VALUE;
+            }
+            // 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 !!!!!!");
+                }
+                return mPrefLetter == code;
+            } else {
+                final boolean inside = key.isInsideSuper(x, y);
+                int[] nearby = getNearestKeys(x, y);
+                List<Key> nearbyKeys = getKeys();
+                if (inside) {
+                    // If it's a preferred letter
+                    if (inPrefList(code, pref)) {
+                        // Check if its frequency is much lower than a nearby key
+                        mPrefLetter = code;
+                        mPrefLetterX = x;
+                        mPrefLetterY = y;
+                        for (int i = 0; i < nearby.length; i++) {
+                            Key k = nearbyKeys.get(nearby[i]);
+                            if (k != key && inPrefList(k.codes[0], pref)) {
+                                final int dist = distanceFrom(k, x, y);
+                                if (dist < (int) (k.width * OVERLAP_PERCENTAGE_LOW_PROB) &&
+                                        (pref[k.codes[0]] > pref[mPrefLetter] * 3))  {
+                                    mPrefLetter = k.codes[0];
+                                    mPrefDistance = dist;
+                                    if (DEBUG_PREFERRED_LETTER) {
+                                        Log.d(TAG, "CORRECTED ALTHOUGH PREFERRED !!!!!!");
+                                    }
+                                    break;
+                                }
+                            }
+                        }
+
+                        return mPrefLetter == code;
+                    }
+                }
+
+                // Get the surrounding keys and intersect with the preferred list
+                // For all in the intersection
+                //   if distance from touch point is within a reasonable distance
+                //       make this the pref letter
+                // If no pref letter
+                //   return inside;
+                // else return thiskey == prefletter;
+
+                for (int i = 0; i < nearby.length; i++) {
+                    Key k = nearbyKeys.get(nearby[i]);
+                    if (inPrefList(k.codes[0], pref)) {
+                        final int dist = distanceFrom(k, x, y);
+                        if (dist < (int) (k.width * OVERLAP_PERCENTAGE_HIGH_PROB)
+                                && dist < mPrefDistance)  {
+                            mPrefLetter = k.codes[0];
+                            mPrefLetterX = x;
+                            mPrefLetterY = y;
+                            mPrefDistance = dist;
+                        }
+                    }
+                }
+                // Didn't find any
+                if (mPrefLetter == 0) {
+                    return inside;
+                } else {
+                    return mPrefLetter == code;
+                }
+            }
+        }
+
+        // Lock into the spacebar
+        if (mCurrentlyInSpace) return false;
+
+        return key.isInsideSuper(x, y);
+    }
+
+    private boolean inPrefList(int code, int[] pref) {
+        if (code < pref.length && code >= 0) return pref[code] > 0;
+        return false;
+    }
+
+    private int distanceFrom(Key k, int x, int y) {
+        if (y > k.y && y < k.y + k.height) {
+            return Math.abs(k.x + k.width / 2 - x);
+        } else {
+            return Integer.MAX_VALUE;
+        }
+    }
+
+    @Override
+    public int[] getNearestKeys(int x, int y) {
+        if (mCurrentlyInSpace) {
+            return new int[] { mSpaceKeyIndex };
+        } else {
+            return super.getNearestKeys(x, y);
+        }
+    }
+
+    private int indexOf(int code) {
+        List<Key> keys = getKeys();
+        int count = keys.size();
+        for (int i = 0; i < count; i++) {
+            if (keys.get(i).codes[0] == code) return i;
+        }
+        return -1;
+    }
+
+    private int getTextSizeFromTheme(int style, int defValue) {
+        TypedArray array = mContext.getTheme().obtainStyledAttributes(
+                style, new int[] { android.R.attr.textSize });
+        int textSize = array.getDimensionPixelSize(array.getResourceId(0, 0), defValue);
+        return textSize;
+    }
+
+    class LatinKey extends Keyboard.Key {
+        
+        private boolean mShiftLockEnabled;
+        
+        public LatinKey(Resources res, Keyboard.Row parent, int x, int y, 
+                XmlResourceParser parser) {
+            super(res, parent, x, y, parser);
+            if (popupCharacters != null && popupCharacters.length() == 0) {
+                // If there is a keyboard with no keys specified in popupCharacters
+                popupResId = 0;
+            }
+        }
+        
+        void enableShiftLock() {
+            mShiftLockEnabled = true;
+        }
+
+        @Override
+        public void onReleased(boolean inside) {
+            if (!mShiftLockEnabled) {
+                super.onReleased(inside);
+            } else {
+                pressed = !pressed;
+            }
+        }
+
+        /**
+         * Overriding this method so that we can reduce the target area for certain keys.
+         */
+        @Override
+        public boolean isInside(int x, int y) {
+            boolean result = LatinKeyboard.this.isInside(this, x, y);
+            return result;
+        }
+
+        boolean isInsideSuper(int x, int y) {
+            return super.isInside(x, y);
+        }
+    }
+
+    /**
+     * Animation to be displayed on the spacebar preview popup when switching 
+     * languages by swiping the spacebar. It draws the current, previous and
+     * next languages and moves them by the delta of touch movement on the spacebar.
+     */
+    class SlidingLocaleDrawable extends Drawable {
+
+        private int mWidth;
+        private int mHeight;
+        private Drawable mBackground;
+        private int mDiff;
+        private TextPaint mTextPaint;
+        private int mMiddleX;
+        private int mAscent;
+        private Drawable mLeftDrawable;
+        private Drawable mRightDrawable;
+        private boolean mHitThreshold;
+        private int     mThreshold;
+        private String mCurrentLanguage;
+        private String mNextLanguage;
+        private String mPrevLanguage;
+
+        public SlidingLocaleDrawable(Drawable background, int width, int height) {
+            mBackground = background;
+            mBackground.setBounds(0, 0,
+                    mBackground.getIntrinsicWidth(), mBackground.getIntrinsicHeight());
+            mWidth = width;
+            mHeight = height;
+            mTextPaint = new TextPaint();
+            int textSize = getTextSizeFromTheme(android.R.style.TextAppearance_Medium, 18);
+            mTextPaint.setTextSize(textSize);
+            mTextPaint.setColor(0);
+            mTextPaint.setTextAlign(Align.CENTER);
+            mTextPaint.setAlpha(255);
+            mTextPaint.setAntiAlias(true);
+            mAscent = (int) mTextPaint.ascent();
+            mMiddleX = (mWidth - mBackground.getIntrinsicWidth()) / 2;
+            mLeftDrawable =
+                    mRes.getDrawable(R.drawable.sym_keyboard_feedback_language_arrows_left);
+            mRightDrawable =
+                    mRes.getDrawable(R.drawable.sym_keyboard_feedback_language_arrows_right);
+            mLeftDrawable.setBounds(0, 0,
+                    mLeftDrawable.getIntrinsicWidth(), mLeftDrawable.getIntrinsicHeight());
+            mRightDrawable.setBounds(mWidth - mRightDrawable.getIntrinsicWidth(), 0,
+                    mWidth, mRightDrawable.getIntrinsicHeight());
+            mThreshold = ViewConfiguration.get(mContext).getScaledTouchSlop();
+        }
+
+        void setDiff(int diff) {
+            if (diff == Integer.MAX_VALUE) {
+                mHitThreshold = false;
+                mCurrentLanguage = null;
+                return;
+            }
+            mDiff = diff;
+            if (mDiff > mWidth) mDiff = mWidth;
+            if (mDiff < -mWidth) mDiff = -mWidth;
+            if (Math.abs(mDiff) > mThreshold) mHitThreshold = true;
+            invalidateSelf();
+        }
+
+        @Override
+        public void draw(Canvas canvas) {
+            canvas.save();
+            if (mHitThreshold) {
+                mTextPaint.setColor(0xFF000000);
+                canvas.clipRect(0, 0, mWidth, mHeight);
+                if (mCurrentLanguage == null) {
+                    mCurrentLanguage = getInputLanguage(mWidth, mTextPaint);
+                    mNextLanguage = getNextInputLanguage(mWidth, mTextPaint);
+                    mPrevLanguage = getPrevInputLanguage(mWidth, mTextPaint);
+                }
+                canvas.drawText(mCurrentLanguage,
+                        mWidth / 2 + mDiff, -mAscent + 4, mTextPaint);
+                canvas.drawText(mNextLanguage,
+                        mDiff - mWidth / 2, -mAscent + 4, mTextPaint);
+                canvas.drawText(mPrevLanguage,
+                        mDiff + mWidth + mWidth / 2, -mAscent + 4, mTextPaint);
+                mLeftDrawable.draw(canvas);
+                mRightDrawable.draw(canvas);
+            }
+            if (mBackground != null) {
+                canvas.translate(mMiddleX, 0);
+                mBackground.draw(canvas);
+            }
+            canvas.restore();
+        }
+
+        @Override
+        public int getOpacity() {
+            return PixelFormat.TRANSLUCENT;
+        }
+
+        @Override
+        public void setAlpha(int alpha) {
+            // Ignore
+        }
+
+        @Override
+        public void setColorFilter(ColorFilter cf) {
+            // Ignore
+        }
+
+        @Override
+        public int getIntrinsicWidth() {
+            return mWidth;
+        }
+
+        @Override
+        public int getIntrinsicHeight() {
+            return mHeight;
+        }
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/LatinKeyboardView.java b/java/src/com/android/inputmethod/latin/LatinKeyboardView.java
new file mode 100644
index 0000000..2686a93
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/LatinKeyboardView.java
@@ -0,0 +1,332 @@
+/*
+ * Copyright (C) 2008-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.latin;
+
+import java.util.List;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.inputmethodservice.Keyboard;
+import android.inputmethodservice.KeyboardView;
+import android.inputmethodservice.Keyboard.Key;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.widget.PopupWindow;
+
+public class LatinKeyboardView extends KeyboardView {
+
+    static final int KEYCODE_OPTIONS = -100;
+    static final int KEYCODE_SHIFT_LONGPRESS = -101;
+    static final int KEYCODE_VOICE = -102;
+    static final int KEYCODE_F1 = -103;
+    static final int KEYCODE_NEXT_LANGUAGE = -104;
+    static final int KEYCODE_PREV_LANGUAGE = -105;
+
+    private Keyboard mPhoneKeyboard;
+
+    private boolean mExtensionVisible;
+    private LatinKeyboardView mExtension;
+    private PopupWindow mExtensionPopup;
+    private boolean mFirstEvent;
+
+    public LatinKeyboardView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public LatinKeyboardView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    public void setPhoneKeyboard(Keyboard phoneKeyboard) {
+        mPhoneKeyboard = phoneKeyboard;
+    }
+
+    @Override
+    protected boolean onLongPress(Key key) {
+        if (key.codes[0] == Keyboard.KEYCODE_MODE_CHANGE) {
+            getOnKeyboardActionListener().onKey(KEYCODE_OPTIONS, null);
+            return true;
+        } else if (key.codes[0] == Keyboard.KEYCODE_SHIFT) {
+            getOnKeyboardActionListener().onKey(KEYCODE_SHIFT_LONGPRESS, null);
+            invalidateAllKeys();
+            return true;
+        } else if (key.codes[0] == '0' && getKeyboard() == mPhoneKeyboard) {
+            // Long pressing on 0 in phone number keypad gives you a '+'.
+            getOnKeyboardActionListener().onKey('+', null);
+            return true;
+        } else {
+            return super.onLongPress(key);
+        }
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent me) {
+        LatinKeyboard keyboard = (LatinKeyboard) getKeyboard();
+        if (DEBUG_LINE) {
+            mLastX = (int) me.getX();
+            mLastY = (int) me.getY();
+            invalidate();
+        }
+        // Reset any bounding box controls in the keyboard
+        if (me.getAction() == MotionEvent.ACTION_DOWN) {
+            keyboard.keyReleased();
+        }
+
+        if (me.getAction() == MotionEvent.ACTION_UP) {
+            int languageDirection = keyboard.getLanguageChangeDirection();
+            if (languageDirection != 0) {
+                getOnKeyboardActionListener().onKey(
+                        languageDirection == 1 ? KEYCODE_NEXT_LANGUAGE : KEYCODE_PREV_LANGUAGE,
+                        null);
+                me.setAction(MotionEvent.ACTION_CANCEL);
+                keyboard.keyReleased();
+                return super.onTouchEvent(me);
+            }
+        }
+
+        // If we don't have an extension keyboard, don't go any further.
+        if (keyboard.getExtension() == 0) {
+            return super.onTouchEvent(me);
+        }
+        if (me.getY() < 0) {
+            if (mExtensionVisible) {
+                int action = me.getAction();
+                if (mFirstEvent) action = MotionEvent.ACTION_DOWN;
+                mFirstEvent = false;
+                MotionEvent translated = MotionEvent.obtain(me.getEventTime(), me.getEventTime(),
+                        action,
+                        me.getX(), me.getY() + mExtension.getHeight(), me.getMetaState());
+                boolean result = mExtension.onTouchEvent(translated);
+                translated.recycle();
+                if (me.getAction() == MotionEvent.ACTION_UP
+                        || me.getAction() == MotionEvent.ACTION_CANCEL) {
+                    closeExtension();
+                }
+                return result;
+            } else {
+                if (openExtension()) {
+                    MotionEvent cancel = MotionEvent.obtain(me.getDownTime(), me.getEventTime(),
+                            MotionEvent.ACTION_CANCEL, me.getX() - 100, me.getY() - 100, 0);
+                    super.onTouchEvent(cancel);
+                    cancel.recycle();
+                    if (mExtension.getHeight() > 0) {
+                        MotionEvent translated = MotionEvent.obtain(me.getEventTime(),
+                                me.getEventTime(),
+                                MotionEvent.ACTION_DOWN,
+                                me.getX(), me.getY() + mExtension.getHeight(),
+                                me.getMetaState());
+                        mExtension.onTouchEvent(translated);
+                        translated.recycle();
+                    } else {
+                        mFirstEvent = true;
+                    }
+                }
+                return true;
+            }
+        } else if (mExtensionVisible) {
+            closeExtension();
+            // Send a down event into the main keyboard first
+            MotionEvent down = MotionEvent.obtain(me.getEventTime(), me.getEventTime(),
+                    MotionEvent.ACTION_DOWN,
+                    me.getX(), me.getY(), me.getMetaState());
+            super.onTouchEvent(down);
+            down.recycle();
+            // Send the actual event
+            return super.onTouchEvent(me);
+        } else {
+            return super.onTouchEvent(me);
+        }
+    }
+
+    private boolean openExtension() {
+        if (((LatinKeyboard) getKeyboard()).getExtension() == 0) return false;
+        makePopupWindow();
+        mExtensionVisible = true;
+        return true;
+    }
+
+    private void makePopupWindow() {
+        if (mExtensionPopup == null) {
+            int[] windowLocation = new int[2];
+            mExtensionPopup = new PopupWindow(getContext());
+            mExtensionPopup.setBackgroundDrawable(null);
+            LayoutInflater li = (LayoutInflater) getContext().getSystemService(
+                    Context.LAYOUT_INFLATER_SERVICE);
+            mExtension = (LatinKeyboardView) li.inflate(R.layout.input_trans, null);
+            mExtension.setOnKeyboardActionListener((LatinIME) getContext());
+            mExtension.setPopupParent(this);
+            mExtension.setPopupOffset(0, -windowLocation[1]);
+            Keyboard keyboard;
+            mExtension.setKeyboard(keyboard = new LatinKeyboard(getContext(),
+                    ((LatinKeyboard) getKeyboard()).getExtension()));
+            mExtensionPopup.setContentView(mExtension);
+            mExtensionPopup.setWidth(getWidth());
+            mExtensionPopup.setHeight(keyboard.getHeight());
+            getLocationInWindow(windowLocation);
+            // TODO: Fix the "- 30". 
+            mExtension.setPopupOffset(0, -windowLocation[1] - 30);
+            mExtensionPopup.showAtLocation(this, 0, 0, -keyboard.getHeight()
+                    + windowLocation[1]);
+        } else {
+            mExtension.setVisibility(VISIBLE);
+        }
+    }
+
+    @Override
+    public void closing() {
+        super.closing();
+        if (mExtensionPopup != null && mExtensionPopup.isShowing()) {
+            mExtensionPopup.dismiss();
+            mExtensionPopup = null;
+        }
+    }
+
+    private void closeExtension() {
+        mExtension.setVisibility(INVISIBLE);
+        mExtension.closing();
+        mExtensionVisible = false;
+    }
+
+    /****************************  INSTRUMENTATION  *******************************/
+
+    static final boolean DEBUG_AUTO_PLAY = false;
+    static final boolean DEBUG_LINE = false;
+    private static final int MSG_TOUCH_DOWN = 1;
+    private static final int MSG_TOUCH_UP = 2;
+    
+    Handler mHandler2;
+    
+    private String mStringToPlay;
+    private int mStringIndex;
+    private boolean mDownDelivered;
+    private Key[] mAsciiKeys = new Key[256];
+    private boolean mPlaying;
+    private int mLastX;
+    private int mLastY;
+    private Paint mPaint;
+
+    @Override
+    public void setKeyboard(Keyboard k) {
+        super.setKeyboard(k);
+        if (DEBUG_AUTO_PLAY) {
+            findKeys();
+            if (mHandler2 == null) {
+                mHandler2 = new Handler() {
+                    @Override
+                    public void handleMessage(Message msg) {
+                        removeMessages(MSG_TOUCH_DOWN);
+                        removeMessages(MSG_TOUCH_UP);
+                        if (mPlaying == false) return;
+                        
+                        switch (msg.what) {
+                            case MSG_TOUCH_DOWN:
+                                if (mStringIndex >= mStringToPlay.length()) {
+                                    mPlaying = false;
+                                    return;
+                                }
+                                char c = mStringToPlay.charAt(mStringIndex);
+                                while (c > 255 || mAsciiKeys[(int) c] == null) {
+                                    mStringIndex++;
+                                    if (mStringIndex >= mStringToPlay.length()) {
+                                        mPlaying = false;
+                                        return;
+                                    }
+                                    c = mStringToPlay.charAt(mStringIndex);
+                                }
+                                int x = mAsciiKeys[c].x + 10;
+                                int y = mAsciiKeys[c].y + 26;
+                                MotionEvent me = MotionEvent.obtain(SystemClock.uptimeMillis(), 
+                                        SystemClock.uptimeMillis(), 
+                                        MotionEvent.ACTION_DOWN, x, y, 0);
+                                LatinKeyboardView.this.dispatchTouchEvent(me);
+                                me.recycle();
+                                sendEmptyMessageDelayed(MSG_TOUCH_UP, 500); // Deliver up in 500ms if nothing else
+                                // happens
+                                mDownDelivered = true;
+                                break;
+                            case MSG_TOUCH_UP:
+                                char cUp = mStringToPlay.charAt(mStringIndex);
+                                int x2 = mAsciiKeys[cUp].x + 10;
+                                int y2 = mAsciiKeys[cUp].y + 26;
+                                mStringIndex++;
+                                
+                                MotionEvent me2 = MotionEvent.obtain(SystemClock.uptimeMillis(), 
+                                        SystemClock.uptimeMillis(), 
+                                        MotionEvent.ACTION_UP, x2, y2, 0);
+                                LatinKeyboardView.this.dispatchTouchEvent(me2);
+                                me2.recycle();
+                                sendEmptyMessageDelayed(MSG_TOUCH_DOWN, 500); // Deliver up in 500ms if nothing else
+                                // happens
+                                mDownDelivered = false;
+                                break;
+                        }
+                    }
+                };
+
+            }
+        }
+    }
+
+    private void findKeys() {
+        List<Key> keys = getKeyboard().getKeys();
+        // Get the keys on this keyboard
+        for (int i = 0; i < keys.size(); i++) {
+            int code = keys.get(i).codes[0];
+            if (code >= 0 && code <= 255) { 
+                mAsciiKeys[code] = keys.get(i);
+            }
+        }
+    }
+    
+    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);
+    }
+
+    @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);
+            }
+        }
+        if (DEBUG_LINE) {
+            if (mPaint == null) {
+                mPaint = new Paint();
+                mPaint.setColor(0x80FFFFFF);
+                mPaint.setAntiAlias(false);
+            }
+            c.drawLine(mLastX, 0, mLastX, getHeight(), mPaint);
+            c.drawLine(0, mLastY, getWidth(), mLastY, mPaint);
+        }
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
new file mode 100755
index 0000000..712b9cf
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -0,0 +1,378 @@
+/*
+ * Copyright (C) 2008-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.latin;
+
+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.
+ * @hide pending API Council Approval
+ */
+public class Suggest implements Dictionary.WordCallback {
+
+    public static final int CORRECTION_NONE = 0;
+    public static final int CORRECTION_BASIC = 1;
+    public static final int CORRECTION_FULL = 2;
+
+    private static final int LARGE_DICTIONARY_THRESHOLD = 200 * 1000;
+
+    private BinaryDictionary mMainDict;
+
+    private Dictionary mUserDictionary;
+
+    private Dictionary mAutoDictionary;
+
+    private Dictionary mContactsDictionary;
+
+    private int mPrefMaxSuggestions = 12;
+
+    private boolean mAutoTextEnabled;
+
+    private int[] mPriorities = new int[mPrefMaxSuggestions];
+    // 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.
+    // 1280 is the size of the BASE_CHARS array in ExpandableDictionary, which is a basic set of
+    // latin characters.
+    private int[] mNextLettersFrequencies = new int[1280];
+    private ArrayList<CharSequence> mSuggestions = new ArrayList<CharSequence>();
+    private ArrayList<CharSequence> mStringPool = new ArrayList<CharSequence>();
+    private boolean mHaveCorrection;
+    private CharSequence mOriginalWord;
+    private String mLowerOriginalWord;
+    private boolean mCapitalize;
+
+    private int mCorrectionMode = CORRECTION_BASIC;
+
+
+    public Suggest(Context context, int dictionaryResId) {
+        mMainDict = new BinaryDictionary(context, dictionaryResId);
+        for (int i = 0; i < mPrefMaxSuggestions; i++) {
+            StringBuilder sb = new StringBuilder(32);
+            mStringPool.add(sb);
+        }
+    }
+
+    public void setAutoTextEnabled(boolean enabled) {
+        mAutoTextEnabled = enabled;
+    }
+
+    public int getCorrectionMode() {
+        return mCorrectionMode;
+    }
+
+    public void setCorrectionMode(int mode) {
+        mCorrectionMode = mode;
+    }
+
+    public boolean hasMainDictionary() {
+        return mMainDict.getSize() > LARGE_DICTIONARY_THRESHOLD;
+    }
+
+    /**
+     * Sets an optional user dictionary resource to be loaded. The user dictionary is consulted
+     * before the main dictionary, if set.
+     */
+    public void setUserDictionary(Dictionary userDictionary) {
+        mUserDictionary = userDictionary;
+    }
+
+    /**
+     * Sets an optional contacts dictionary resource to be loaded.
+     */
+    public void setContactsDictionary(Dictionary userDictionary) {
+        mContactsDictionary = userDictionary;
+    }
+    
+    public void setAutoDictionary(Dictionary autoDictionary) {
+        mAutoDictionary = autoDictionary;
+    }
+
+    /**
+     * Number of suggestions to generate from the input key sequence. This has
+     * to be a number between 1 and 100 (inclusive).
+     * @param maxSuggestions
+     * @throws IllegalArgumentException if the number is out of range
+     */
+    public void setMaxSuggestions(int maxSuggestions) {
+        if (maxSuggestions < 1 || maxSuggestions > 100) {
+            throw new IllegalArgumentException("maxSuggestions must be between 1 and 100");
+        }
+        mPrefMaxSuggestions = maxSuggestions;
+        mPriorities = new int[mPrefMaxSuggestions];
+        collectGarbage();
+        while (mStringPool.size() < mPrefMaxSuggestions) {
+            StringBuilder sb = new StringBuilder(32);
+            mStringPool.add(sb);
+        }
+    }
+
+    private boolean haveSufficientCommonality(String original, CharSequence suggestion) {
+        final int originalLength = original.length();
+        final int suggestionLength = suggestion.length();
+        final int minLength = Math.min(originalLength, suggestionLength);
+        if (minLength <= 2) return true;
+        int matching = 0;
+        int lessMatching = 0; // Count matches if we skip one character
+        int i;
+        for (i = 0; i < minLength; i++) {
+            final char origChar = ExpandableDictionary.toLowerCase(original.charAt(i));
+            if (origChar == ExpandableDictionary.toLowerCase(suggestion.charAt(i))) {
+                matching++;
+                lessMatching++;
+            } else if (i + 1 < suggestionLength
+                    && origChar == ExpandableDictionary.toLowerCase(suggestion.charAt(i + 1))) {
+                lessMatching++;
+            }
+        }
+        matching = Math.max(matching, lessMatching);
+
+        if (minLength <= 4) {
+            return matching >= 2;
+        } else {
+            return matching > minLength / 2;
+        }
+    }
+
+    /**
+     * 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. 
+     * @return list of suggestions.
+     */
+    public List<CharSequence> getSuggestions(View view, WordComposer wordComposer, 
+            boolean includeTypedWordIfValid) {
+        mHaveCorrection = false;
+        mCapitalize = wordComposer.isCapitalized();
+        collectGarbage();
+        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();
+        } else {
+            mLowerOriginalWord = "";
+        }
+        // Search the dictionary only if there are at least 2 characters
+        if (wordComposer.size() > 1) {
+            if (mUserDictionary != null || mContactsDictionary != null) {
+                if (mUserDictionary != null) {
+                    mUserDictionary.getWords(wordComposer, this, mNextLettersFrequencies);
+                }
+                if (mContactsDictionary != null) {
+                    mContactsDictionary.getWords(wordComposer, this, mNextLettersFrequencies);
+                }
+
+                if (mSuggestions.size() > 0 && isValidWord(mOriginalWord)
+                        && mCorrectionMode == CORRECTION_FULL) {
+                    mHaveCorrection = true;
+                }
+            }
+            mMainDict.getWords(wordComposer, this, mNextLettersFrequencies);
+            if (mCorrectionMode == CORRECTION_FULL && 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 (!haveSufficientCommonality(mLowerOriginalWord, mSuggestions.get(1))) {
+                mHaveCorrection = false;
+            }
+        }
+
+        if (mAutoTextEnabled) {
+            int i = 0;
+            int max = 6;
+            // Don't autotext the suggestions from the dictionaries
+            if (mCorrectionMode == CORRECTION_BASIC) max = 1;
+            while (i < mSuggestions.size() && i < max) {
+                String suggestedWord = mSuggestions.get(i).toString().toLowerCase();
+                CharSequence autoText =
+                        AutoText.get(suggestedWord, 0, suggestedWord.length(), view);
+                // Is there an AutoText correction?
+                boolean canAdd = autoText != null;
+                // Is that correction already the current prediction (or original word)?
+                canAdd &= !TextUtils.equals(autoText, mSuggestions.get(i));
+                // Is that correction already the next predicted word?
+                if (canAdd && i + 1 < mSuggestions.size() && mCorrectionMode != CORRECTION_BASIC) {
+                    canAdd &= !TextUtils.equals(autoText, mSuggestions.get(i + 1));
+                }
+                if (canAdd) {
+                    mHaveCorrection = true;
+                    mSuggestions.add(i + 1, autoText);
+                    i++;
+                }
+                i++;
+            }
+        }
+
+        removeDupes();
+        return mSuggestions;
+    }
+
+    public int[] getNextLettersFrequencies() {
+        return mNextLettersFrequencies;
+    }
+
+    private void removeDupes() {
+        final ArrayList<CharSequence> suggestions = mSuggestions;
+        if (suggestions.size() < 2) return;
+        int i = 1;
+        // Don't cache suggestions.size(), since we may be removing items
+        while (i < suggestions.size()) {
+            final CharSequence cur = suggestions.get(i);
+            // Compare each candidate with each previous candidate
+            for (int j = 0; j < i; j++) {
+                CharSequence previous = suggestions.get(j);
+                if (TextUtils.equals(cur, previous)) {
+                    removeFromSuggestions(i);
+                    i--;
+                    break;
+                }
+            }
+            i++;
+        }
+    }
+
+    private void removeFromSuggestions(int index) {
+        CharSequence garbage = mSuggestions.remove(index);
+        if (garbage != null && garbage instanceof StringBuilder) {
+            mStringPool.add(garbage);
+        }
+    }
+
+    public boolean hasMinimalCorrection() {
+        return mHaveCorrection;
+    }
+
+    private boolean compareCaseInsensitive(final String mLowerOriginalWord, 
+            final char[] word, final int offset, final int length) {
+        final int originalLength = mLowerOriginalWord.length();
+        if (originalLength == length && Character.isUpperCase(word[offset])) {
+            for (int i = 0; i < originalLength; i++) {
+                if (mLowerOriginalWord.charAt(i) != Character.toLowerCase(word[offset+i])) {
+                    return false;
+                }
+            }
+            return true;
+        }
+        return false;
+    }
+
+    public boolean addWord(final char[] word, final int offset, final int length, final int freq) {
+        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 {
+            // 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())) {
+                    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);
+        sb.setLength(0);
+        if (mCapitalize) {
+            sb.append(Character.toUpperCase(word[offset]));
+            if (length > 1) {
+                sb.append(word, offset + 1, length - 1);
+            }
+        } else {
+            sb.append(word, offset, length);
+        }
+        mSuggestions.add(pos, sb);
+        if (mSuggestions.size() > prefMaxSuggestions) {
+            CharSequence garbage = mSuggestions.remove(prefMaxSuggestions);
+            if (garbage instanceof StringBuilder) {
+                mStringPool.add(garbage);
+            }
+        }
+        return true;
+    }
+
+    public boolean isValidWord(final CharSequence word) {
+        if (word == null || word.length() == 0) {
+            return false;
+        }
+        return (mCorrectionMode == CORRECTION_FULL && mMainDict.isValidWord(word)) 
+                || (mCorrectionMode > CORRECTION_NONE && 
+                    ((mUserDictionary != null && mUserDictionary.isValidWord(word)))
+                     || (mAutoDictionary != null && mAutoDictionary.isValidWord(word))
+                     || (mContactsDictionary != null && mContactsDictionary.isValidWord(word)));
+    }
+    
+    private void collectGarbage() {
+        int poolSize = mStringPool.size();
+        int garbageSize = mSuggestions.size();
+        while (poolSize < mPrefMaxSuggestions && garbageSize > 0) {
+            CharSequence garbage = mSuggestions.get(garbageSize - 1);
+            if (garbage != null && garbage instanceof StringBuilder) {
+                mStringPool.add(garbage);
+                poolSize++;
+            }
+            garbageSize--;
+        }
+        if (poolSize == mPrefMaxSuggestions + 1) {
+            Log.w("Suggest", "String pool got too big: " + poolSize);
+        }
+        mSuggestions.clear();
+    }
+
+    public void close() {
+        if (mMainDict != null) {
+            mMainDict.close();
+        }
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/TextEntryState.java b/java/src/com/android/inputmethod/latin/TextEntryState.java
new file mode 100644
index 0000000..c5e8ad9
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/TextEntryState.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2008-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.latin;
+
+import android.content.Context;
+import android.text.format.DateFormat;
+import android.util.Log;
+
+import android.inputmethodservice.Keyboard.Key;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Calendar;
+
+public class TextEntryState {
+    
+    private static boolean LOGGING = false;
+    
+    private static int sBackspaceCount = 0;
+    
+    private static int sAutoSuggestCount = 0;
+    
+    private static int sAutoSuggestUndoneCount = 0;
+    
+    private static int sManualSuggestCount = 0;
+    
+    private static int sWordNotInDictionaryCount = 0;
+    
+    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;
+    public static final int STATE_ACCEPTED_DEFAULT = 3;
+    public static final int STATE_PICKED_SUGGESTION = 4;
+    public static final int STATE_PUNCTUATION_AFTER_WORD = 5;
+    public static final int STATE_PUNCTUATION_AFTER_ACCEPTED = 6;
+    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;
+    
+    private static int sState = STATE_UNKNOWN;
+    
+    private static FileOutputStream sKeyLocationFile;
+    private static FileOutputStream sUserActionFile;
+    
+    public static void newSession(Context context) {
+        sSessionCount++;
+        sAutoSuggestCount = 0;
+        sBackspaceCount = 0;
+        sAutoSuggestUndoneCount = 0;
+        sManualSuggestCount = 0;
+        sWordNotInDictionaryCount = 0;
+        sTypedChars = 0;
+        sActualChars = 0;
+        sState = STATE_START;
+        
+        if (LOGGING) {
+            try {
+                sKeyLocationFile = context.openFileOutput("key.txt", Context.MODE_APPEND);
+                sUserActionFile = context.openFileOutput("action.txt", Context.MODE_APPEND);
+            } catch (IOException ioe) {
+                Log.e("TextEntryState", "Couldn't open file for output: " + ioe);
+            }
+        }
+    }
+    
+    public static void endSession() {
+        if (sKeyLocationFile == null) {
+            return;
+        }
+        try {
+            sKeyLocationFile.close();
+            // Write to log file            
+            // Write timestamp, settings,
+            String out = DateFormat.format("MM:dd hh:mm:ss", Calendar.getInstance().getTime())
+                    .toString()
+                    + " BS: " + sBackspaceCount
+                    + " auto: " + sAutoSuggestCount
+                    + " manual: " + sManualSuggestCount
+                    + " typed: " + sWordNotInDictionaryCount
+                    + " undone: " + sAutoSuggestUndoneCount
+                    + " saved: " + ((float) (sActualChars - sTypedChars) / sActualChars)
+                    + "\n";
+            sUserActionFile.write(out.getBytes());
+            sUserActionFile.close();
+            sKeyLocationFile = null;
+            sUserActionFile = null;
+        } catch (IOException ioe) {
+            
+        }
+    }
+    
+    public static void acceptedDefault(CharSequence typedWord, CharSequence actualWord) {
+        if (typedWord == null) return;
+        if (!typedWord.equals(actualWord)) {
+            sAutoSuggestCount++;
+        }
+        sTypedChars += typedWord.length();
+        sActualChars += actualWord.length();
+        sState = STATE_ACCEPTED_DEFAULT;
+    }
+    
+    public static void acceptedTyped(CharSequence typedWord) {
+        sWordNotInDictionaryCount++;
+        sState = STATE_PICKED_SUGGESTION;
+    }
+
+    public static void acceptedSuggestion(CharSequence typedWord, CharSequence actualWord) {
+        sManualSuggestCount++;
+        if (typedWord.equals(actualWord)) {
+            acceptedTyped(typedWord);
+        }
+        sState = STATE_PICKED_SUGGESTION;
+    }
+    
+    public static void typedCharacter(char c, boolean isSeparator) {
+        boolean isSpace = c == ' ';
+        switch (sState) {
+            case STATE_IN_WORD:
+                if (isSpace || isSeparator) {
+                    sState = STATE_START;
+                } else {
+                    // State hasn't changed.
+                }
+                break;
+            case STATE_ACCEPTED_DEFAULT:
+            case STATE_SPACE_AFTER_PICKED:
+                if (isSpace) {
+                    sState = STATE_SPACE_AFTER_ACCEPTED;
+                } else if (isSeparator) {
+                    sState = STATE_PUNCTUATION_AFTER_ACCEPTED;
+                } else {
+                    sState = STATE_IN_WORD;
+                }
+                break;
+            case STATE_PICKED_SUGGESTION:
+                if (isSpace) {
+                    sState = STATE_SPACE_AFTER_PICKED;
+                } else if (isSeparator) {
+                    // Swap 
+                    sState = STATE_PUNCTUATION_AFTER_ACCEPTED;
+                } else {
+                    sState = STATE_IN_WORD;
+                }
+                break;
+            case STATE_START:
+            case STATE_UNKNOWN:
+            case STATE_SPACE_AFTER_ACCEPTED:
+            case STATE_PUNCTUATION_AFTER_ACCEPTED:
+            case STATE_PUNCTUATION_AFTER_WORD:
+                if (!isSpace && !isSeparator) {
+                    sState = STATE_IN_WORD;
+                } else {
+                    sState = STATE_START;
+                }
+                break;
+            case STATE_UNDO_COMMIT:
+                if (isSpace || isSeparator) {
+                    sState = STATE_ACCEPTED_DEFAULT;
+                } else {
+                    sState = STATE_IN_WORD;
+                }
+        }
+    }
+    
+    public static void backspace() {
+        if (sState == STATE_ACCEPTED_DEFAULT) {
+            sState = STATE_UNDO_COMMIT;
+            sAutoSuggestUndoneCount++;
+        } else if (sState == STATE_UNDO_COMMIT) {
+            sState = STATE_IN_WORD;
+        }
+        sBackspaceCount++;
+    }
+    
+    public static void reset() {
+        sState = STATE_START;
+    }
+    
+    public static int getState() {
+        return sState;
+    }
+    
+    public static void keyPressedAt(Key key, int x, int y) {
+        if (LOGGING && sKeyLocationFile != null && key.codes[0] >= 32) {
+            String out = 
+                    "KEY: " + (char) key.codes[0] 
+                    + " X: " + x 
+                    + " Y: " + y
+                    + " MX: " + (key.x + key.width / 2)
+                    + " MY: " + (key.y + key.height / 2) 
+                    + "\n";
+            try {
+                sKeyLocationFile.write(out.getBytes());
+            } catch (IOException ioe) {
+                // TODO: May run out of space
+            }
+        }
+    }
+}
+
diff --git a/java/src/com/android/inputmethod/latin/Tutorial.java b/java/src/com/android/inputmethod/latin/Tutorial.java
new file mode 100644
index 0000000..03d4858
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/Tutorial.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2008-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.latin;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Message;
+import android.text.Layout;
+import android.text.SpannableStringBuilder;
+import android.text.StaticLayout;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnTouchListener;
+import android.widget.PopupWindow;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Tutorial implements OnTouchListener {
+    
+    private List<Bubble> mBubbles = new ArrayList<Bubble>();
+    private long mStartTime;
+    private static final long MINIMUM_TIME = 6000;
+    private static final long MAXIMUM_TIME = 20000;
+    private View mInputView;
+    private LatinIME mIme;
+    private int[] mLocation = new int[2];
+    private int mBubblePointerOffset;
+    
+    private static final int MSG_SHOW_BUBBLE = 0;
+    
+    private int mBubbleIndex;
+    
+    Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_SHOW_BUBBLE:
+                    Bubble bubba = (Bubble) msg.obj;
+                    bubba.show(mLocation[0], mLocation[1]);
+                    break;
+            }
+        }
+    };
+
+    class Bubble {
+        Drawable bubbleBackground;
+        int x;
+        int y;
+        int width;
+        int gravity;
+        CharSequence text;
+        boolean dismissOnTouch;
+        boolean dismissOnClose;
+        PopupWindow window;
+        TextView textView;
+        View inputView;
+        
+        Bubble(Context context, View inputView,
+                int backgroundResource, int bx, int by, int textResource1, int textResource2) {
+            bubbleBackground = context.getResources().getDrawable(backgroundResource);
+            x = bx;
+            y = by;
+            width = (int) (inputView.getWidth() * 0.9);
+            this.gravity = Gravity.TOP | Gravity.LEFT;
+            text = new SpannableStringBuilder()
+                .append(context.getResources().getText(textResource1))
+                .append("\n") 
+                .append(context.getResources().getText(textResource2));
+            this.dismissOnTouch = true;
+            this.dismissOnClose = false;
+            this.inputView = inputView;
+            window = new PopupWindow(context);
+            window.setBackgroundDrawable(null);
+            LayoutInflater inflate =
+                (LayoutInflater) context
+                        .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+            textView = (TextView) inflate.inflate(R.layout.bubble_text, null);
+            textView.setBackgroundDrawable(bubbleBackground);
+            textView.setText(text);
+            //textView.setText(textResource1);
+            window.setContentView(textView);
+            window.setFocusable(false);
+            window.setTouchable(true);
+            window.setOutsideTouchable(false);
+        }
+
+        private int chooseSize(PopupWindow pop, View parentView, CharSequence text, TextView tv) {
+            int wid = tv.getPaddingLeft() + tv.getPaddingRight();
+            int ht = tv.getPaddingTop() + tv.getPaddingBottom();
+
+            /*
+             * Figure out how big the text would be if we laid it out to the
+             * full width of this view minus the border.
+             */
+            int cap = width - wid;
+
+            Layout l = new StaticLayout(text, tv.getPaint(), cap,
+                                        Layout.Alignment.ALIGN_NORMAL, 1, 0, true);
+            float max = 0;
+            for (int i = 0; i < l.getLineCount(); i++) {
+                max = Math.max(max, l.getLineWidth(i));
+            }
+
+            /*
+             * Now set the popup size to be big enough for the text plus the border.
+             */
+            pop.setWidth(width);
+            pop.setHeight(ht + l.getHeight());
+            return l.getHeight();
+        }
+
+        void show(int offx, int offy) {
+            int textHeight = chooseSize(window, inputView, text, textView);
+            offy -= textView.getPaddingTop() + textHeight;
+            if (inputView.getVisibility() == View.VISIBLE 
+                    && inputView.getWindowVisibility() == View.VISIBLE) {
+                try {
+                    if ((gravity & Gravity.BOTTOM) == Gravity.BOTTOM) offy -= window.getHeight();
+                    if ((gravity & Gravity.RIGHT) == Gravity.RIGHT) offx -= window.getWidth();
+                    textView.setOnTouchListener(new View.OnTouchListener() {
+                        public boolean onTouch(View view, MotionEvent me) {
+                            Tutorial.this.next();
+                            return true;
+                        }
+                    });
+                    window.showAtLocation(inputView, Gravity.NO_GRAVITY, x + offx, y + offy);
+                } catch (Exception e) {
+                    // Input view is not valid
+                }
+            }
+        }
+        
+        void hide() {
+            if (window.isShowing()) {
+                textView.setOnTouchListener(null);
+                window.dismiss();
+            }
+        }
+        
+        boolean isShowing() {
+            return window.isShowing();
+        }
+    }
+    
+    public Tutorial(LatinIME ime, LatinKeyboardView inputView) {
+        Context context = inputView.getContext();
+        mIme = ime;
+        int inputWidth = inputView.getWidth();
+        final int x = inputWidth / 20; // Half of 1/10th
+        mBubblePointerOffset = inputView.getContext().getResources()
+            .getDimensionPixelOffset(R.dimen.bubble_pointer_offset);
+        Bubble bWelcome = new Bubble(context, inputView, 
+                R.drawable.dialog_bubble_step02, x, 0, 
+                R.string.tip_to_open_keyboard, R.string.touch_to_continue);
+        mBubbles.add(bWelcome);
+        Bubble bAccents = new Bubble(context, inputView, 
+                R.drawable.dialog_bubble_step02, x, 0, 
+                R.string.tip_to_view_accents, R.string.touch_to_continue);
+        mBubbles.add(bAccents);
+        Bubble b123 = new Bubble(context, inputView, 
+                R.drawable.dialog_bubble_step07, x, 0, 
+                R.string.tip_to_open_symbols, R.string.touch_to_continue);
+        mBubbles.add(b123);
+        Bubble bABC = new Bubble(context, inputView, 
+                R.drawable.dialog_bubble_step07, x, 0, 
+                R.string.tip_to_close_symbols, R.string.touch_to_continue);
+        mBubbles.add(bABC);
+        Bubble bSettings = new Bubble(context, inputView, 
+                R.drawable.dialog_bubble_step07, x, 0, 
+                R.string.tip_to_launch_settings, R.string.touch_to_continue);
+        mBubbles.add(bSettings);
+        Bubble bDone = new Bubble(context, inputView, 
+                R.drawable.dialog_bubble_step02, x, 0, 
+                R.string.tip_to_start_typing, R.string.touch_to_finish);
+        mBubbles.add(bDone);
+        mInputView = inputView;
+    }
+    
+    void start() {
+        mInputView.getLocationInWindow(mLocation);
+        mBubbleIndex = -1;
+        mInputView.setOnTouchListener(this);
+        next();
+    }
+
+    boolean next() {
+        if (mBubbleIndex >= 0) {
+            // If the bubble is not yet showing, don't move to the next.
+            if (!mBubbles.get(mBubbleIndex).isShowing()) {
+                return true;
+            }
+            // Hide all previous bubbles as well, as they may have had a delayed show
+            for (int i = 0; i <= mBubbleIndex; i++) {
+                mBubbles.get(i).hide();
+            }
+        }
+        mBubbleIndex++;
+        if (mBubbleIndex >= mBubbles.size()) {
+            mInputView.setOnTouchListener(null);
+            mIme.sendDownUpKeyEvents(-1); // Inform the setupwizard that tutorial is in last bubble
+            mIme.tutorialDone();
+            return false;
+        }
+        if (mBubbleIndex == 3 || mBubbleIndex == 4) {
+            mIme.mKeyboardSwitcher.toggleSymbols();
+        }
+        mHandler.sendMessageDelayed(
+                mHandler.obtainMessage(MSG_SHOW_BUBBLE, mBubbles.get(mBubbleIndex)), 500);
+        return true;
+    }
+    
+    void hide() {
+        for (int i = 0; i < mBubbles.size(); i++) {
+            mBubbles.get(i).hide();
+        }
+        mInputView.setOnTouchListener(null);
+    }
+
+    boolean close() {
+        mHandler.removeMessages(MSG_SHOW_BUBBLE);
+        hide();
+        return true;
+    }
+
+    public boolean onTouch(View v, MotionEvent event) {
+        if (event.getAction() == MotionEvent.ACTION_DOWN) {
+            next();
+        }
+        return true;
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/UserDictionary.java b/java/src/com/android/inputmethod/latin/UserDictionary.java
new file mode 100644
index 0000000..4b98eac
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/UserDictionary.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.UserDictionary.Words;
+
+public class UserDictionary extends ExpandableDictionary {
+    
+    private static final String[] PROJECTION = {
+        Words._ID,
+        Words.WORD,
+        Words.FREQUENCY
+    };
+    
+    private static final int INDEX_WORD = 1;
+    private static final int INDEX_FREQUENCY = 2;
+    
+    private ContentObserver mObserver;
+    
+    private boolean mRequiresReload;
+    private String mLocale;
+
+    public UserDictionary(Context context, String locale) {
+        super(context);
+        mLocale = locale;
+        // Perform a managed query. The Activity will handle closing and requerying the cursor
+        // when needed.
+        ContentResolver cres = context.getContentResolver();
+        
+        cres.registerContentObserver(Words.CONTENT_URI, true, mObserver = new ContentObserver(null) {
+            @Override
+            public void onChange(boolean self) {
+                mRequiresReload = true;
+            }
+        });
+
+        loadDictionary();
+    }
+    
+    public synchronized void close() {
+        if (mObserver != null) {
+            getContext().getContentResolver().unregisterContentObserver(mObserver);
+            mObserver = null;
+        }
+    }
+    
+    private synchronized void loadDictionary() {
+        Cursor cursor = getContext().getContentResolver()
+                .query(Words.CONTENT_URI, PROJECTION, "(locale IS NULL) or (locale=?)", 
+                        new String[] { mLocale }, null);
+        addWords(cursor);
+        mRequiresReload = false;
+    }
+
+    /**
+     * Adds a word to the dictionary and makes it persistent.
+     * @param word the word to add. If the word is capitalized, then the dictionary will
+     * recognize it as a capitalized word when searched.
+     * @param frequency the frequency of occurrence of the word. A frequency of 255 is considered
+     * the highest.
+     * @TODO use a higher or float range for frequency
+     */
+    @Override
+    public synchronized void addWord(String word, int frequency) {
+        if (mRequiresReload) loadDictionary();
+        // Safeguard against adding long words. Can cause stack overflow.
+        if (word.length() >= getMaxWordLength()) return;
+
+        super.addWord(word, frequency);
+
+        // Update the user dictionary provider
+        ContentValues values = new ContentValues(5);
+        values.put(Words.WORD, word);
+        values.put(Words.FREQUENCY, frequency);
+        values.put(Words.LOCALE, mLocale);
+        values.put(Words.APP_ID, 0);
+
+        getContext().getContentResolver().insert(Words.CONTENT_URI, values);
+        // In case the above does a synchronous callback of the change observer
+        mRequiresReload = false;
+    }
+
+    @Override
+    public synchronized void getWords(final WordComposer codes, final WordCallback callback,
+            int[] nextLettersFrequencies) {
+        if (mRequiresReload) loadDictionary();
+        super.getWords(codes, callback, nextLettersFrequencies);
+    }
+
+    @Override
+    public synchronized boolean isValidWord(CharSequence word) {
+        if (mRequiresReload) loadDictionary();
+        return super.isValidWord(word);
+    }
+
+    private void addWords(Cursor cursor) {
+        clearDictionary();
+
+        final int maxWordLength = getMaxWordLength();
+        if (cursor.moveToFirst()) {
+            while (!cursor.isAfterLast()) {
+                String word = cursor.getString(INDEX_WORD);
+                int frequency = cursor.getInt(INDEX_FREQUENCY);
+                // Safeguard against adding really long words. Stack may overflow due
+                // to recursion
+                if (word.length() < maxWordLength) {
+                    super.addWord(word, frequency);
+                }
+                cursor.moveToNext();
+            }
+        }
+        cursor.close();
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
new file mode 100644
index 0000000..e97cb24
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2008-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.latin;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A place to store the currently composing word with information such as adjacent key codes as well
+ */
+public class WordComposer {
+    /**
+     * The list of unicode values for each keystroke (including surrounding keys)
+     */
+    private ArrayList<int[]> mCodes;
+    
+    /**
+     * The word chosen from the candidate list, until it is committed.
+     */
+    private String mPreferredWord;
+    
+    private StringBuilder mTypedWord;
+
+    private int mCapsCount;
+
+    private boolean mAutoCapitalized;
+    
+    /**
+     * Whether the user chose to capitalize the word.
+     */
+    private boolean mIsCapitalized;
+
+    WordComposer() {
+        mCodes = new ArrayList<int[]>(12);
+        mTypedWord = new StringBuilder(20);
+    }
+
+    /**
+     * Clear out the keys registered so far.
+     */
+    public void reset() {
+        mCodes.clear();
+        mIsCapitalized = false;
+        mPreferredWord = null;
+        mTypedWord.setLength(0);
+        mCapsCount = 0;
+    }
+
+    /**
+     * Number of keystrokes in the composing word.
+     * @return the number of keystrokes
+     */
+    public int size() {
+        return mCodes.size();
+    }
+
+    /**
+     * Returns the codes at a particular position in the word.
+     * @param index the position in the word
+     * @return the unicode for the pressed and surrounding keys
+     */
+    public int[] getCodesAt(int index) {
+        return mCodes.get(index);
+    }
+
+    /**
+     * Add a new keystroke, with codes[0] containing the pressed key's unicode and the rest of
+     * the array containing unicode for adjacent keys, sorted by reducing probability/proximity.
+     * @param codes the array of unicode values
+     */
+    public void add(int primaryCode, int[] codes) {
+        mTypedWord.append((char) primaryCode);
+        mCodes.add(codes);
+        if (Character.isUpperCase((char) primaryCode)) mCapsCount++;
+    }
+
+    /**
+     * Delete the last keystroke as a result of hitting backspace.
+     */
+    public void deleteLast() {
+        mCodes.remove(mCodes.size() - 1);
+        final int lastPos = mTypedWord.length() - 1;
+        char last = mTypedWord.charAt(lastPos);
+        mTypedWord.deleteCharAt(lastPos);
+        if (Character.isUpperCase(last)) mCapsCount--;
+    }
+
+    /**
+     * Returns the word as it was typed, without any correction applied.
+     * @return the word that was typed so far
+     */
+    public CharSequence getTypedWord() {
+        int wordSize = mCodes.size();
+        if (wordSize == 0) {
+            return null;
+        }
+//        StringBuffer sb = new StringBuffer(wordSize);
+//        for (int i = 0; i < wordSize; i++) {
+//            char c = (char) mCodes.get(i)[0];
+//            if (i == 0 && mIsCapitalized) {
+//                c = Character.toUpperCase(c);
+//            }
+//            sb.append(c);
+//        }
+//        return sb;
+        return mTypedWord;
+    }
+
+    public void setCapitalized(boolean capitalized) {
+        mIsCapitalized = capitalized;
+    }
+    
+    /**
+     * Whether or not the user typed a capital letter as the first letter in the word
+     * @return capitalization preference
+     */
+    public boolean isCapitalized() {
+        return mIsCapitalized;
+    }
+    
+    /**
+     * Stores the user's selected word, before it is actually committed to the text field.
+     * @param preferred
+     */
+    public void setPreferredWord(String preferred) {
+        mPreferredWord = preferred;
+    }
+    
+    /**
+     * Return the word chosen by the user, or the typed word if no other word was chosen.
+     * @return the preferred word
+     */
+    public CharSequence getPreferredWord() {
+        return mPreferredWord != null ? mPreferredWord : getTypedWord();
+    }
+
+    /**
+     * Returns true if more than one character is upper case, otherwise returns false.
+     */
+    public boolean isMostlyCaps() {
+        return mCapsCount > 1;
+    }
+
+    /** 
+     * Saves the reason why the word is capitalized - whether it was automatic or
+     * due to the user hitting shift in the middle of a sentence.
+     * @param auto whether it was an automatic capitalization due to start of sentence
+     */
+    public void setAutoCapitalized(boolean auto) {
+        mAutoCapitalized = auto;
+    }
+
+    /**
+     * Returns whether the word was automatically capitalized.
+     * @return whether the word was automatically capitalized
+     */
+    public boolean isAutoCapitalized() {
+        return mAutoCapitalized;
+    }
+}
diff --git a/java/src/com/android/inputmethod/voice/EditingUtil.java b/java/src/com/android/inputmethod/voice/EditingUtil.java
new file mode 100644
index 0000000..6316d8c
--- /dev/null
+++ b/java/src/com/android/inputmethod/voice/EditingUtil.java
@@ -0,0 +1,162 @@
+/*
+ * 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.view.inputmethod.ExtractedText;
+import android.view.inputmethod.ExtractedTextRequest;
+import android.view.inputmethod.InputConnection;
+
+/**
+ * Utility methods to deal with editing text through an InputConnection.
+ */
+public class EditingUtil {
+    private EditingUtil() {};
+
+    /**
+     * Append newText to the text field represented by connection.
+     * The new text becomes selected.
+     */
+    public static void appendText(InputConnection connection, String newText) {
+        if (connection == null) {
+            return;
+        }
+
+        // Commit the composing text
+        connection.finishComposingText();
+
+        // Add a space if the field already has text.
+        CharSequence charBeforeCursor = connection.getTextBeforeCursor(1, 0);
+        if (charBeforeCursor != null
+                && !charBeforeCursor.equals(" ")
+                && (charBeforeCursor.length() > 0)) {
+            newText = " " + newText;
+        }
+
+        connection.setComposingText(newText, 1);
+    }
+
+    private static int getCursorPosition(InputConnection connection) {
+        ExtractedText extracted = connection.getExtractedText(
+            new ExtractedTextRequest(), 0);
+        if (extracted == null) {
+          return -1;
+        }
+        return extracted.startOffset + extracted.selectionStart;
+    }
+
+    private static int getSelectionEnd(InputConnection connection) {
+        ExtractedText extracted = connection.getExtractedText(
+            new ExtractedTextRequest(), 0);
+        if (extracted == null) {
+          return -1;
+        }
+        return extracted.startOffset + extracted.selectionEnd;
+    }
+
+    /**
+     * @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 = getWordRangeAtCursor(connection, separators);
+        return (range == null) ? null : range.word;
+    }
+
+    /**
+     * Removes the word surrounding the cursor. Parameters are identical to
+     * getWordAtCursor.
+     */
+    public static void deleteWordAtCursor(
+        InputConnection connection, String separators) {
+
+        Range range = getWordRangeAtCursor(connection, separators);
+        if (range == null) return;
+
+        connection.finishComposingText();
+        // Move cursor to beginning of word, to avoid crash when cursor is outside
+        // of valid range after deleting text.
+        int newCursor = getCursorPosition(connection) - range.charsBefore;
+        connection.setSelection(newCursor, newCursor);
+        connection.deleteSurroundingText(0, range.charsBefore + range.charsAfter);
+    }
+
+    /**
+     * Represents a range of text, relative to the current cursor position.
+     */
+    private static class Range {
+        /** Characters before selection start */
+        int charsBefore;
+
+        /**
+         * Characters after selection start, including one trailing word
+         * separator.
+         */
+        int charsAfter;
+
+        /** The actual characters that make up a word */
+        String word;
+
+        public Range(int charsBefore, int charsAfter, String word) {
+            if (charsBefore < 0 || charsAfter < 0) {
+                throw new IndexOutOfBoundsException();
+            }
+            this.charsBefore = charsBefore;
+            this.charsAfter = charsAfter;
+            this.word = word;
+        }
+    }
+
+    private static Range getWordRangeAtCursor(
+        InputConnection connection, String sep) {
+        if (connection == null || sep == null) {
+            return null;
+        }
+        CharSequence before = connection.getTextBeforeCursor(1000, 0);
+        CharSequence after = connection.getTextAfterCursor(1000, 0);
+        if (before == null || after == null) {
+            return null;
+        }
+
+        // Find first word separator before the cursor
+        int start = before.length();
+        while (--start > 0 && !isWhitespace(before.charAt(start - 1), sep));
+
+        // 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);
+        }
+
+        return null;
+    }
+
+    private static boolean isWhitespace(int code, String whitespace) {
+        return whitespace.contains(String.valueOf((char) code));
+    }
+}
diff --git a/java/src/com/android/inputmethod/voice/FieldContext.java b/java/src/com/android/inputmethod/voice/FieldContext.java
new file mode 100644
index 0000000..5fbacfb
--- /dev/null
+++ b/java/src/com/android/inputmethod/voice/FieldContext.java
@@ -0,0 +1,102 @@
+/*
+ * 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.os.Bundle;
+import android.util.Log;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.ExtractedText;
+import android.view.inputmethod.ExtractedTextRequest;
+import android.view.inputmethod.InputConnection;
+
+/**
+ * Represents information about a given text field, which can be passed
+ * to the speech recognizer as context information.
+ */
+public class FieldContext {
+    private static final boolean DBG = false;
+    
+    static final String LABEL = "label";
+    static final String HINT = "hint";
+    static final String PACKAGE_NAME = "packageName";
+    static final String FIELD_ID = "fieldId";
+    static final String FIELD_NAME = "fieldName";
+    static final String SINGLE_LINE = "singleLine";
+    static final String INPUT_TYPE = "inputType";
+    static final String IME_OPTIONS = "imeOptions";
+    static final String SELECTED_LANGUAGE = "selectedLanguage";
+    static final String ENABLED_LANGUAGES = "enabledLanguages";
+
+    Bundle mFieldInfo;
+
+    public FieldContext(InputConnection conn, EditorInfo info,
+            String selectedLanguage, String[] enabledLanguages) {
+        mFieldInfo = new Bundle();
+        addEditorInfoToBundle(info, mFieldInfo);
+        addInputConnectionToBundle(conn, mFieldInfo);
+        addLanguageInfoToBundle(selectedLanguage, enabledLanguages, mFieldInfo);
+        if (DBG) Log.i("FieldContext", "Bundle = " + mFieldInfo.toString());
+    }
+
+    private static String safeToString(Object o) {
+        if (o == null) {
+            return "";
+        }
+        return o.toString();
+    }
+
+    private static void addEditorInfoToBundle(EditorInfo info, Bundle bundle) {
+        if (info == null) {
+            return;
+        }
+
+        bundle.putString(LABEL, safeToString(info.label));
+        bundle.putString(HINT, safeToString(info.hintText));
+        bundle.putString(PACKAGE_NAME, safeToString(info.packageName));
+        bundle.putInt(FIELD_ID, info.fieldId);
+        bundle.putString(FIELD_NAME, safeToString(info.fieldName));
+        bundle.putInt(INPUT_TYPE, info.inputType);
+        bundle.putInt(IME_OPTIONS, info.imeOptions);
+    }
+
+    private static void addInputConnectionToBundle(
+        InputConnection conn, Bundle bundle) {
+        if (conn == null) {
+            return;
+        }
+
+        ExtractedText et = conn.getExtractedText(new ExtractedTextRequest(), 0);
+        if (et == null) {
+            return;
+        }
+        bundle.putBoolean(SINGLE_LINE, (et.flags & et.FLAG_SINGLE_LINE) > 0);
+    }
+    
+    private static void addLanguageInfoToBundle(
+            String selectedLanguage, String[] enabledLanguages, Bundle bundle) {
+        bundle.putString(SELECTED_LANGUAGE, selectedLanguage);
+        bundle.putStringArray(ENABLED_LANGUAGES, enabledLanguages);
+    }
+
+    public Bundle getBundle() {
+        return mFieldInfo;
+    }
+
+    public String toString() {
+        return mFieldInfo.toString();
+    }
+}
diff --git a/java/src/com/android/inputmethod/voice/LatinIMEWithVoice.java b/java/src/com/android/inputmethod/voice/LatinIMEWithVoice.java
new file mode 100644
index 0000000..ccbf5b6
--- /dev/null
+++ b/java/src/com/android/inputmethod/voice/LatinIMEWithVoice.java
@@ -0,0 +1,28 @@
+/*
+ * 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
new file mode 100644
index 0000000..13a58e1
--- /dev/null
+++ b/java/src/com/android/inputmethod/voice/LatinIMEWithVoiceSettings.java
@@ -0,0 +1,21 @@
+/*
+ * 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/RecognitionView.java b/java/src/com/android/inputmethod/voice/RecognitionView.java
new file mode 100644
index 0000000..1e99c3c
--- /dev/null
+++ b/java/src/com/android/inputmethod/voice/RecognitionView.java
@@ -0,0 +1,324 @@
+/*
+ * 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 java.io.ByteArrayOutputStream;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.ShortBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.CornerPathEffect;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PathEffect;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.util.TypedValue;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup.MarginLayoutParams;
+import android.widget.ImageView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import com.android.inputmethod.latin.R;
+
+/**
+ * The user interface for the "Speak now" and "working" states.
+ * Displays a recognition dialog (with waveform, voice meter, etc.),
+ * plays beeps, shows errors, etc.
+ */
+public class RecognitionView {
+    private static final String TAG = "RecognitionView";
+
+    private Handler mUiHandler;  // Reference to UI thread
+    private View mView;
+    private Context mContext;
+
+    private ImageView mImage;
+    private TextView mText;
+    private View mButton;
+    private TextView mButtonText;
+    private View mProgress;
+
+    private Drawable mInitializing;
+    private Drawable mError;
+    private List<Drawable> mSpeakNow;
+
+    private float mVolume = 0.0f;
+    private int mLevel = 0;
+
+    private enum State {LISTENING, WORKING, READY}
+    private State mState = State.READY;
+
+    private float mMinMicrophoneLevel;
+    private float mMaxMicrophoneLevel;
+
+    /** Updates the microphone icon to show user their volume.*/
+    private Runnable mUpdateVolumeRunnable = new Runnable() {
+        public void run() {
+            if (mState != State.LISTENING) {
+                return;
+            }
+
+            final float min = mMinMicrophoneLevel;
+            final float max = mMaxMicrophoneLevel;
+            final int maxLevel = mSpeakNow.size() - 1;
+
+            int index = (int) ((mVolume - min) / (max - min) * maxLevel);
+            final int level = Math.min(Math.max(0, index), maxLevel);
+
+            if (level != mLevel) {
+                mImage.setImageDrawable(mSpeakNow.get(level));
+                mLevel = level;
+            }
+            mUiHandler.postDelayed(mUpdateVolumeRunnable, 50);
+        }
+      };
+
+    public RecognitionView(Context context, OnClickListener clickListener) {
+        mUiHandler = new Handler();
+
+        LayoutInflater inflater = (LayoutInflater) context.getSystemService(
+            Context.LAYOUT_INFLATER_SERVICE);
+        mView = inflater.inflate(R.layout.recognition_status, null);
+        ContentResolver cr = context.getContentResolver();
+        mMinMicrophoneLevel = SettingsUtil.getSettingsFloat(
+                cr, SettingsUtil.LATIN_IME_MIN_MICROPHONE_LEVEL, 15.f);
+        mMaxMicrophoneLevel = SettingsUtil.getSettingsFloat(
+                cr, SettingsUtil.LATIN_IME_MAX_MICROPHONE_LEVEL, 30.f);
+
+        // Pre-load volume level images
+        Resources r = context.getResources();
+
+        mSpeakNow = new ArrayList<Drawable>();
+        mSpeakNow.add(r.getDrawable(R.drawable.speak_now_level0));
+        mSpeakNow.add(r.getDrawable(R.drawable.speak_now_level1));
+        mSpeakNow.add(r.getDrawable(R.drawable.speak_now_level2));
+        mSpeakNow.add(r.getDrawable(R.drawable.speak_now_level3));
+        mSpeakNow.add(r.getDrawable(R.drawable.speak_now_level4));
+        mSpeakNow.add(r.getDrawable(R.drawable.speak_now_level5));
+        mSpeakNow.add(r.getDrawable(R.drawable.speak_now_level6));
+
+        mInitializing = r.getDrawable(R.drawable.mic_slash);
+        mError = r.getDrawable(R.drawable.caution);
+
+        mImage = (ImageView) mView.findViewById(R.id.image);
+        mButton = mView.findViewById(R.id.button);
+        mButton.setOnClickListener(clickListener);
+        mText = (TextView) mView.findViewById(R.id.text);
+        mButtonText = (TextView) mView.findViewById(R.id.button_text);
+        mProgress = mView.findViewById(R.id.progress);
+
+        mContext = context;
+    }
+
+    public View getView() {
+        return mView;
+    }
+
+    public void restoreState() {
+        mUiHandler.post(new Runnable() {
+            public void run() {
+                // Restart the spinner
+                if (mState == State.WORKING) {
+                    ((ProgressBar)mProgress).setIndeterminate(false);
+                    ((ProgressBar)mProgress).setIndeterminate(true);
+                }
+            }
+        });
+    }
+
+    public void showInitializing() {
+        mUiHandler.post(new Runnable() {
+            public void run() {
+                prepareDialog(false, mContext.getText(R.string.voice_initializing), mInitializing,
+                        mContext.getText(R.string.cancel)); 
+            }
+          });
+    }
+
+    public void showListening() {
+        mUiHandler.post(new Runnable() {
+            public void run() {
+                mState = State.LISTENING;
+                prepareDialog(false, mContext.getText(R.string.voice_listening), mSpeakNow.get(0),
+                        mContext.getText(R.string.cancel));
+            }
+          });
+        mUiHandler.postDelayed(mUpdateVolumeRunnable, 50);
+    }
+
+    public void updateVoiceMeter(final float rmsdB) {
+        mVolume = rmsdB;
+    }
+
+    public void showError(final String message) {
+        mUiHandler.post(new Runnable() {
+            public void run() {
+                mState = State.READY;
+                prepareDialog(false, message, mError, mContext.getText(R.string.ok));
+            }
+          });
+    }
+
+    public void showWorking(
+        final ByteArrayOutputStream waveBuffer,
+        final int speechStartPosition,
+        final int speechEndPosition) {
+
+        mUiHandler.post(new Runnable() {
+            public void run() {
+                mState = State.WORKING;
+                prepareDialog(true, mContext.getText(R.string.voice_working), null, mContext
+                        .getText(R.string.cancel));
+                final ShortBuffer buf = ByteBuffer.wrap(waveBuffer.toByteArray()).order(
+                        ByteOrder.nativeOrder()).asShortBuffer();
+                buf.position(0);
+                waveBuffer.reset();
+                showWave(buf, speechStartPosition / 2, speechEndPosition / 2);
+            }
+          });
+    }
+    
+    private void prepareDialog(boolean spinVisible, CharSequence text, Drawable image,
+            CharSequence btnTxt) {
+        if (spinVisible) {
+            mProgress.setVisibility(View.VISIBLE);
+            mImage.setVisibility(View.GONE);
+        } else {
+            mProgress.setVisibility(View.GONE);
+            mImage.setImageDrawable(image);
+            mImage.setVisibility(View.VISIBLE);
+        }
+        mText.setText(text);
+        mButtonText.setText(btnTxt);
+    }
+
+    /**
+     * @return an average abs of the specified buffer.
+     */
+    private static int getAverageAbs(ShortBuffer buffer, int start, int i, int npw) {
+        int from = start + i * npw;
+        int end = from + npw;
+        int total = 0;
+        for (int x = from; x < end; x++) {
+            total += Math.abs(buffer.get(x));
+        }
+        return total / npw;
+    }
+
+
+    /**
+     * Shows waveform of input audio.
+     *
+     * Copied from version in VoiceSearch's RecognitionActivity.
+     *
+     * TODO: adjust stroke width based on the size of data.
+     * TODO: use dip rather than pixels.
+     */
+    private void showWave(ShortBuffer waveBuffer, int startPosition, int endPosition) {
+        final int w = ((View) mImage.getParent()).getWidth();
+        final int h = mImage.getHeight();
+        if (w <= 0 || h <= 0) {
+            // view is not visible this time. Skip drawing.
+            return;
+        }
+        final Bitmap b = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
+        final Canvas c = new Canvas(b);
+        final Paint paint = new Paint();
+        paint.setColor(0xFFFFFFFF); // 0xAARRGGBB
+        paint.setAntiAlias(true);
+        paint.setStyle(Paint.Style.STROKE);
+        paint.setAlpha(0x90);
+
+        final PathEffect effect = new CornerPathEffect(3);
+        paint.setPathEffect(effect);
+
+        final int numSamples = waveBuffer.remaining();
+        int endIndex;
+        if (endPosition == 0) {
+            endIndex = numSamples;
+        } else {
+            endIndex = Math.min(endPosition, numSamples);
+        }
+
+        int startIndex = startPosition - 2000; // include 250ms before speech
+        if (startIndex < 0) {
+            startIndex = 0;
+        }
+        final int numSamplePerWave = 200;  // 8KHz 25ms = 200 samples
+        final float scale = 10.0f / 65536.0f;
+
+        final int count = (endIndex - startIndex) / numSamplePerWave;
+        final float deltaX = 1.0f * w / count;
+        int yMax = h / 2 - 10;
+        Path path = new Path();
+        c.translate(0, yMax);
+        float x = 0;
+        path.moveTo(x, 0);
+        yMax -= 10;
+        for (int i = 0; i < count; i++) {
+            final int avabs = getAverageAbs(waveBuffer, startIndex, i , numSamplePerWave);
+            int sign = ( (i & 01) == 0) ? -1 : 1;
+            final float y = Math.min(yMax, avabs * h * scale) * sign;
+            path.lineTo(x, y);
+            x += deltaX;
+            path.lineTo(x, y);
+        }
+        if (deltaX > 4) {
+            paint.setStrokeWidth(3);
+        } else {
+            paint.setStrokeWidth(Math.max(1, (int) (deltaX -.05)));
+        }
+        c.drawPath(path, paint);
+        mImage.setImageBitmap(b);
+        mImage.setVisibility(View.VISIBLE);
+        MarginLayoutParams mProgressParams = (MarginLayoutParams)mProgress.getLayoutParams();
+        mProgressParams.topMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+                -h / 2 - 18, mContext.getResources().getDisplayMetrics());
+
+        // Tweak the padding manually to fill out the whole view horizontally.
+        // TODO: Do this in the xml layout instead.
+        ((View) mImage.getParent()).setPadding(4, ((View) mImage.getParent()).getPaddingTop(), 3,
+                ((View) mImage.getParent()).getPaddingBottom());
+        mProgress.setLayoutParams(mProgressParams);
+    }
+
+
+    public void finish() {
+        mUiHandler.post(new Runnable() {
+            public void run() {
+                mState = State.READY;
+                exitWorking();
+            }
+          });
+    }
+
+    private void exitWorking() {
+        mProgress.setVisibility(View.GONE);
+        mImage.setVisibility(View.VISIBLE);
+    }
+}
diff --git a/java/src/com/android/inputmethod/voice/SettingsUtil.java b/java/src/com/android/inputmethod/voice/SettingsUtil.java
new file mode 100644
index 0000000..abf5204
--- /dev/null
+++ b/java/src/com/android/inputmethod/voice/SettingsUtil.java
@@ -0,0 +1,113 @@
+/*
+ * 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.ContentResolver;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.Settings;
+import android.util.Log;
+
+/**
+ * Utility for retrieving settings from Settings.Secure.
+ */
+public class SettingsUtil {
+    /**
+     * A whitespace-separated list of supported locales for voice input from the keyboard.
+     */
+    public static final String LATIN_IME_VOICE_INPUT_SUPPORTED_LOCALES =
+            "latin_ime_voice_input_supported_locales";
+
+    /**
+     * A whitespace-separated list of recommended app packages for voice input from the
+     * keyboard.
+     */
+    public static final String LATIN_IME_VOICE_INPUT_RECOMMENDED_PACKAGES =
+            "latin_ime_voice_input_recommended_packages";
+
+    /**
+     * The maximum number of unique days to show the swipe hint for voice input.
+     */
+    public static final String LATIN_IME_VOICE_INPUT_SWIPE_HINT_MAX_DAYS =
+            "latin_ime_voice_input_swipe_hint_max_days";
+
+    /**
+     * The maximum number of times to show the punctuation hint for voice input.
+     */
+    public static final String LATIN_IME_VOICE_INPUT_PUNCTUATION_HINT_MAX_DISPLAYS =
+            "latin_ime_voice_input_punctuation_hint_max_displays";
+
+    /**
+     * Endpointer parameters for voice input from the keyboard.
+     */
+    public static final String LATIN_IME_SPEECH_MINIMUM_LENGTH_MILLIS =
+            "latin_ime_speech_minimum_length_millis";
+    public static final String LATIN_IME_SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS =
+            "latin_ime_speech_input_complete_silence_length_millis";
+    public static final String LATIN_IME_SPEECH_INPUT_POSSIBLY_COMPLETE_SILENCE_LENGTH_MILLIS =
+            "latin_ime_speech_input_possibly_complete_silence_length_millis";
+
+    /**
+     * Min and max volume levels that can be displayed on the "speak now" screen.
+     */
+    public static final String LATIN_IME_MIN_MICROPHONE_LEVEL =
+            "latin_ime_min_microphone_level";
+    public static final String LATIN_IME_MAX_MICROPHONE_LEVEL =
+            "latin_ime_max_microphone_level";
+
+    /**
+     * The number of sentence-level alternates to request of the server.
+     */
+    public static final String LATIN_IME_MAX_VOICE_RESULTS = "latin_ime_max_voice_results";
+
+    /**
+     * Get a string-valued setting.
+     *
+     * @param cr The content resolver to use
+     * @param key The setting to look up
+     * @param defaultValue The default value to use if none can be found
+     * @return The value of the setting, or defaultValue if it couldn't be found
+     */
+    public static String getSettingsString(ContentResolver cr, String key, String defaultValue) {
+        String result = Settings.Secure.getString(cr, key);
+        return (result == null) ? defaultValue : result;
+    }
+
+    /**
+     * Get an int-valued setting.
+     *
+     * @param cr The content resolver to use
+     * @param key The setting to look up
+     * @param defaultValue The default value to use if the setting couldn't be found or parsed
+     * @return The value of the setting, or defaultValue if it couldn't be found or parsed
+     */
+    public static int getSettingsInt(ContentResolver cr, String key, int defaultValue) {
+        return Settings.Secure.getInt(cr, key, defaultValue);
+    }
+
+    /**
+     * Get a float-valued setting.
+     *
+     * @param cr The content resolver to use
+     * @param key The setting to look up
+     * @param defaultValue The default value to use if the setting couldn't be found or parsed
+     * @return The value of the setting, or defaultValue if it couldn't be found or parsed
+     */
+    public static float getSettingsFloat(ContentResolver cr, String key, float defaultValue) {
+        return Settings.Secure.getFloat(cr, key, defaultValue);
+    }
+}
diff --git a/java/src/com/android/inputmethod/voice/VoiceInput.java b/java/src/com/android/inputmethod/voice/VoiceInput.java
new file mode 100644
index 0000000..e881856
--- /dev/null
+++ b/java/src/com/android/inputmethod/voice/VoiceInput.java
@@ -0,0 +1,508 @@
+/*
+ * 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.R;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.speech.RecognitionListener;
+import android.speech.RecognitionManager;
+import android.speech.RecognizerIntent;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * Speech recognition input, including both user interface and a background
+ * process to stream audio to the network recognizer. This class supplies a
+ * View (getView()), which it updates as recognition occurs. The user of this
+ * class is responsible for making the view visible to the user, as well as
+ * handling various events returned through UiListener.
+ */
+public class VoiceInput implements OnClickListener {
+    private static final String TAG = "VoiceInput";
+    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 DEFAULT_RECOMMENDED_PACKAGES =
+            "com.android.mms " +
+            "com.google.android.gm " +
+            "com.google.android.talk " +
+            "com.google.android.apps.googlevoice " +
+            "com.android.email " +
+            "com.android.browser ";
+
+    // 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;
+
+    // Dummy word suggestion which means "delete current word"
+    public static final String DELETE_SYMBOL = " \u00D7 ";  // times symbol
+
+    private Whitelist mRecommendedList;
+    private Whitelist mBlacklist;
+
+    private VoiceInputLogger mLogger;
+
+    // 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 =
+            "android.speech.extras.SPEECH_INPUT_MINIMUM_LENGTH_MILLIS";
+    private static final String EXTRA_SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS =
+            "android.speech.extras.SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS";
+    private static final String EXTRA_SPEECH_INPUT_POSSIBLY_COMPLETE_SILENCE_LENGTH_MILLIS =
+            "android.speech.extras.SPEECH_INPUT_POSSIBLY_COMPLETE_SILENCE_LENGTH_MILLIS";
+
+    // The usual endpointer default value for input complete silence length is 0.5 seconds,
+    // but that's used for things like voice search. For dictation-like voice input like this,
+    // we go with a more liberal value of 1 second. This value will only be used if a value
+    // is not provided from Gservices.
+    private static final String INPUT_COMPLETE_SILENCE_LENGTH_DEFAULT_VALUE_MILLIS = "1000";
+
+    // Used to record part of that state for logging purposes.
+    public static final int DEFAULT = 0;
+    public static final int LISTENING = 1;
+    public static final int WORKING = 2;
+    public static final int ERROR = 3;
+
+    private int mState = DEFAULT;
+    
+    private final static int MSG_CLOSE_ERROR_DIALOG = 1;
+
+    private final Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            if (msg.what == MSG_CLOSE_ERROR_DIALOG) {
+                mState = DEFAULT;
+                mRecognitionView.finish();
+                mUiListener.onCancelVoice();
+            }
+        }
+    };
+
+    /**
+     * Events relating to the recognition UI. You must implement these.
+     */
+    public interface UiListener {
+
+        /**
+         * @param recognitionResults a set of transcripts for what the user
+         *   spoke, sorted by likelihood.
+         */
+        public void onVoiceResults(
+            List<String> recognitionResults,
+            Map<String, List<CharSequence>> alternatives);
+
+        /**
+         * Called when the user cancels speech recognition.
+         */
+        public void onCancelVoice();
+    }
+
+    private RecognitionManager mRecognitionManager;
+    private RecognitionListener mRecognitionListener;
+    private RecognitionView mRecognitionView;
+    private UiListener mUiListener;
+    private Context mContext;
+
+    /**
+     * @param context the service or activity in which we're running.
+     * @param uiHandler object to receive events from VoiceInput.
+     */
+    public VoiceInput(Context context, UiListener uiHandler) {
+        mLogger = VoiceInputLogger.getLogger(context);
+        mRecognitionListener = new ImeRecognitionListener();
+        mRecognitionManager = RecognitionManager.createRecognitionManager(context);
+        mRecognitionManager.setRecognitionListener(mRecognitionListener);
+        mUiListener = uiHandler;
+        mContext = context;
+        newView();
+
+        String recommendedPackages = SettingsUtil.getSettingsString(
+                context.getContentResolver(),
+                SettingsUtil.LATIN_IME_VOICE_INPUT_RECOMMENDED_PACKAGES,
+                DEFAULT_RECOMMENDED_PACKAGES);
+
+        mRecommendedList = new Whitelist();
+        for (String recommendedPackage : recommendedPackages.split("\\s+")) {
+            mRecommendedList.addApp(recommendedPackage);
+        }
+
+        mBlacklist = new Whitelist();
+        mBlacklist.addApp("com.android.setupwizard");
+    }
+
+    /**
+     * The configuration of the IME changed and may have caused the views to be layed out
+     * again. Restore the state of the recognition view.
+     */
+    public void onConfigurationChanged() {
+        mRecognitionView.restoreState();
+    }
+
+    /**
+     * @return true if field is blacklisted for voice
+     */
+    public boolean isBlacklistedField(FieldContext context) {
+        return mBlacklist.matches(context);
+    }
+
+    /**
+     * Used to decide whether to show voice input hints for this field, etc.
+     *
+     * @return true if field is recommended for voice
+     */
+    public boolean isRecommendedField(FieldContext context) {
+        return mRecommendedList.matches(context);
+    }
+
+    /**
+     * Start listening for speech from the user. This will grab the microphone
+     * and start updating the view provided by getView(). It is the caller's
+     * responsibility to ensure that the view is visible to the user at this stage.
+     *
+     * @param context the same FieldContext supplied to voiceIsEnabled()
+     * @param swipe whether this voice input was started by swipe, for logging purposes
+     */
+    public void startListening(FieldContext context, boolean swipe) {
+        mState = DEFAULT;
+        
+        Locale locale = Locale.getDefault();
+        String localeString = locale.getLanguage() + "-" + locale.getCountry();
+
+        mLogger.start(localeString, swipe);
+
+        mState = LISTENING;
+
+        mRecognitionView.showInitializing();
+        startListeningAfterInitialization(context);
+    }
+
+    /**
+     * Called only when the recognition manager's initialization completed
+     *
+     * @param context context with which {@link #startListening(FieldContext, boolean)} was executed
+     */
+    private void startListeningAfterInitialization(FieldContext context) {
+        Intent intent = makeIntent();
+        intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, "");
+        intent.putExtra(EXTRA_RECOGNITION_CONTEXT, context.getBundle());
+        intent.putExtra(EXTRA_CALLING_PACKAGE, "VoiceIME");
+        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();
+        putEndpointerExtra(
+                cr,
+                intent,
+                SettingsUtil.LATIN_IME_SPEECH_MINIMUM_LENGTH_MILLIS,
+                EXTRA_SPEECH_MINIMUM_LENGTH_MILLIS,
+                null  /* rely on endpointer default */);
+        putEndpointerExtra(
+                cr,
+                intent,
+                SettingsUtil.LATIN_IME_SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS,
+                EXTRA_SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS,
+                INPUT_COMPLETE_SILENCE_LENGTH_DEFAULT_VALUE_MILLIS
+                /* our default value is different from the endpointer's */);
+        putEndpointerExtra(
+                cr,
+                intent,
+                SettingsUtil.
+                        LATIN_IME_SPEECH_INPUT_POSSIBLY_COMPLETE_SILENCE_LENGTH_MILLIS,
+                EXTRA_SPEECH_INPUT_POSSIBLY_COMPLETE_SILENCE_LENGTH_MILLIS,
+                null  /* rely on endpointer default */);
+
+        mRecognitionManager.startListening(intent);
+    }
+
+    /**
+     * Gets the value of the provided Gservices key, attempts to parse it into a long,
+     * and if successful, puts the long value as an extra in the provided intent.
+     */
+    private void putEndpointerExtra(ContentResolver cr, Intent i,
+            String gservicesKey, String intentExtraKey, String defaultValue) {
+        long l = -1;
+        String s = SettingsUtil.getSettingsString(cr, gservicesKey, defaultValue);
+        if (s != null) {
+            try {
+                l = Long.valueOf(s);
+            } catch (NumberFormatException e) {
+                Log.e(TAG, "could not parse value for " + gservicesKey + ": " + s);
+            }
+        }
+
+        if (l != -1) i.putExtra(intentExtraKey, l);
+    }
+
+    public void destroy() {
+        mRecognitionManager.destroy();
+    }
+
+    /**
+     * Creates a new instance of the view that is returned by {@link #getView()}
+     * Clients should use this when a previously returned view is stuck in a
+     * layout that is being thrown away and a new one is need to show to the
+     * user.
+     */
+    public void newView() {
+        mRecognitionView = new RecognitionView(mContext, this);
+    }
+
+    /**
+     * @return a view that shows the recognition flow--e.g., "Speak now" and
+     * "working" dialogs.
+     */
+    public View getView() {
+        return mRecognitionView.getView();
+    }
+
+    /**
+     * Handle the cancel button.
+     */
+    public void onClick(View view) {
+        switch(view.getId()) {
+            case R.id.button:
+                cancel();
+                break;
+        }
+    }
+
+    public void logTextModified() {
+        mLogger.textModified();
+    }
+
+    public void logKeyboardWarningDialogShown() {
+        mLogger.keyboardWarningDialogShown();
+    }
+
+    public void logKeyboardWarningDialogDismissed() {
+        mLogger.keyboardWarningDialogDismissed();
+    }
+
+    public void logKeyboardWarningDialogOk() {
+        mLogger.keyboardWarningDialogOk();
+    }
+
+    public void logKeyboardWarningDialogCancel() {
+        mLogger.keyboardWarningDialogCancel();
+    }
+
+    public void logSwipeHintDisplayed() {
+        mLogger.swipeHintDisplayed();
+    }
+
+    public void logPunctuationHintDisplayed() {
+        mLogger.punctuationHintDisplayed();
+    }
+
+    public void logVoiceInputDelivered() {
+        mLogger.voiceInputDelivered();
+    }
+
+    public void logNBestChoose(int index) {
+        mLogger.nBestChoose(index);
+    }
+
+    public void logInputEnded() {
+        mLogger.inputEnded();
+    }
+
+    public void flushLogs() {
+        mLogger.flush();
+    }
+
+    private static Intent makeIntent() {
+        Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
+
+        // On Cupcake, use VoiceIMEHelper since VoiceSearch doesn't support.
+        // On Donut, always use VoiceSearch, since VoiceIMEHelper and
+        // VoiceSearch may conflict.
+        if (Build.VERSION.RELEASE.equals("1.5")) {
+            intent = intent.setClassName(
+              "com.google.android.voiceservice",
+              "com.google.android.voiceservice.IMERecognitionService");
+        } else {
+            intent = intent.setClassName(
+              "com.google.android.voicesearch",
+              "com.google.android.voicesearch.RecognitionService");
+        }
+
+        return intent;
+    }
+
+    /**
+     * Cancel in-progress speech recognition.
+     */
+    public void cancel() {
+        switch (mState) {
+        case LISTENING:
+            mLogger.cancelDuringListening();
+            break;
+        case WORKING:
+            mLogger.cancelDuringWorking();
+            break;
+        case ERROR:
+            mLogger.cancelDuringError();
+            break;
+        }
+        mState = DEFAULT;
+
+        // Remove all pending tasks (e.g., timers to cancel voice input)
+        mHandler.removeMessages(MSG_CLOSE_ERROR_DIALOG);
+
+        mRecognitionManager.cancel();
+        mUiListener.onCancelVoice();
+        mRecognitionView.finish();
+    }
+
+    private int getErrorStringId(int errorType, boolean endpointed) {
+        switch (errorType) {
+            // We use CLIENT_ERROR to signify that voice search is not available on the device.
+            case RecognitionManager.ERROR_CLIENT:
+                return R.string.voice_not_installed;
+            case RecognitionManager.ERROR_NETWORK:
+                return R.string.voice_network_error;
+            case RecognitionManager.ERROR_NETWORK_TIMEOUT:
+                return endpointed ?
+                        R.string.voice_network_error : R.string.voice_too_much_speech;
+            case RecognitionManager.ERROR_AUDIO:
+                return R.string.voice_audio_error;
+            case RecognitionManager.ERROR_SERVER:
+                return R.string.voice_server_error;
+            case RecognitionManager.ERROR_SPEECH_TIMEOUT:
+                return R.string.voice_speech_timeout;
+            case RecognitionManager.ERROR_NO_MATCH:
+                return R.string.voice_no_match;
+            default: return R.string.voice_error;
+        }
+    }
+
+    private void onError(int errorType, boolean endpointed) {
+        Log.i(TAG, "error " + errorType);
+        mLogger.error(errorType);
+        onError(mContext.getString(getErrorStringId(errorType, endpointed)));
+    }
+
+    private void onError(String error) {
+        mState = ERROR;
+        mRecognitionView.showError(error);
+        // Wait a couple seconds and then automatically dismiss message.
+        mHandler.sendMessageDelayed(Message.obtain(mHandler, MSG_CLOSE_ERROR_DIALOG), 2000);
+    }
+
+    private class ImeRecognitionListener implements RecognitionListener {
+        // Waveform data
+        final ByteArrayOutputStream mWaveBuffer = new ByteArrayOutputStream();
+        int mSpeechStart;
+        private boolean mEndpointed = false;
+
+        public void onReadyForSpeech(Bundle noiseParams) {
+            mRecognitionView.showListening();
+        }
+
+        public void onBeginningOfSpeech() {
+            mEndpointed = false;
+            mSpeechStart = mWaveBuffer.size();
+        }
+
+        public void onRmsChanged(float rmsdB) {
+            mRecognitionView.updateVoiceMeter(rmsdB);
+        }
+
+        public void onBufferReceived(byte[] buf) {
+            try {
+                mWaveBuffer.write(buf);
+            } catch (IOException e) {}
+        }
+
+        public void onEndOfSpeech() {
+            mEndpointed = true;
+            mState = WORKING;
+            mRecognitionView.showWorking(mWaveBuffer, mSpeechStart, mWaveBuffer.size());
+        }
+
+        public void onError(int errorType) {
+            mState = ERROR;
+            VoiceInput.this.onError(errorType, mEndpointed);
+        }
+
+        public void onResults(Bundle resultsBundle) {
+            List<String> results = resultsBundle
+                    .getStringArrayList(RecognitionManager.RESULTS_RECOGNITION);
+            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]);
+                            }
+                        }
+                    }
+                }
+            }
+
+            if (results.size() > 5) {
+                results = results.subList(0, 5);
+            }
+            mUiListener.onVoiceResults(results, alternatives);
+            mRecognitionView.finish();
+        }
+
+        public void onPartialResults(final Bundle partialResults) {
+            // currently - do nothing
+        }
+
+        public void onEvent(int eventType, Bundle params) {
+            // do nothing - reserved for events that might be added in the future
+        }
+    }
+}
diff --git a/java/src/com/android/inputmethod/voice/VoiceInputLogger.java b/java/src/com/android/inputmethod/voice/VoiceInputLogger.java
new file mode 100644
index 0000000..6590333
--- /dev/null
+++ b/java/src/com/android/inputmethod/voice/VoiceInputLogger.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2008 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.common.speech.LoggingEvents;
+
+import android.content.Context;
+import android.content.Intent;
+
+/**
+ * Provides the logging facility for voice input events. This fires broadcasts back to
+ * the voice search app which then logs on our behalf.
+ *
+ * Note that debug console logging does not occur in this class. If you want to
+ * see console output of these logging events, there is a boolean switch to turn
+ * on on the VoiceSearch side.
+ */
+public class VoiceInputLogger {
+    private static final String TAG = VoiceInputLogger.class.getSimpleName();
+
+    private static VoiceInputLogger sVoiceInputLogger;
+    
+    private final Context mContext;
+    
+    // The base intent used to form all broadcast intents to the logger
+    // in VoiceSearch.
+    private final Intent mBaseIntent;
+    
+    /**
+     * Returns the singleton of the logger.
+     *
+     * @param contextHint a hint context used when creating the logger instance.
+     * Ignored if the singleton instance already exists.
+     */
+    public static synchronized VoiceInputLogger getLogger(Context contextHint) {
+        if (sVoiceInputLogger == null) {
+            sVoiceInputLogger = new VoiceInputLogger(contextHint);
+        }
+        return sVoiceInputLogger;
+    }
+
+    public VoiceInputLogger(Context context) {
+        mContext = context;
+        
+        mBaseIntent = new Intent(LoggingEvents.ACTION_LOG_EVENT);
+        mBaseIntent.putExtra(LoggingEvents.EXTRA_APP_NAME, LoggingEvents.VoiceIme.APP_NAME);
+    }
+    
+    private Intent newLoggingBroadcast(int event) {
+        Intent i = new Intent(mBaseIntent);
+        i.putExtra(LoggingEvents.EXTRA_EVENT, event);
+        return i;
+    }
+
+    public void flush() {
+        Intent i = new Intent(mBaseIntent);
+        i.putExtra(LoggingEvents.EXTRA_FLUSH, true);
+        mContext.sendBroadcast(i);
+    }
+    
+    public void keyboardWarningDialogShown() {
+        mContext.sendBroadcast(newLoggingBroadcast(
+                LoggingEvents.VoiceIme.KEYBOARD_WARNING_DIALOG_SHOWN));
+    }
+    
+    public void keyboardWarningDialogDismissed() {
+        mContext.sendBroadcast(newLoggingBroadcast(
+                LoggingEvents.VoiceIme.KEYBOARD_WARNING_DIALOG_DISMISSED));
+    }
+
+    public void keyboardWarningDialogOk() {
+        mContext.sendBroadcast(newLoggingBroadcast(
+                LoggingEvents.VoiceIme.KEYBOARD_WARNING_DIALOG_OK));
+    }
+
+    public void keyboardWarningDialogCancel() {
+        mContext.sendBroadcast(newLoggingBroadcast(
+                LoggingEvents.VoiceIme.KEYBOARD_WARNING_DIALOG_CANCEL));
+    }
+
+    public void settingsWarningDialogShown() {
+        mContext.sendBroadcast(newLoggingBroadcast(
+                LoggingEvents.VoiceIme.SETTINGS_WARNING_DIALOG_SHOWN));
+    }
+    
+    public void settingsWarningDialogDismissed() {
+        mContext.sendBroadcast(newLoggingBroadcast(
+                LoggingEvents.VoiceIme.SETTINGS_WARNING_DIALOG_DISMISSED));
+    }
+
+    public void settingsWarningDialogOk() {
+        mContext.sendBroadcast(newLoggingBroadcast(
+                LoggingEvents.VoiceIme.SETTINGS_WARNING_DIALOG_OK));
+    }
+
+    public void settingsWarningDialogCancel() {
+        mContext.sendBroadcast(newLoggingBroadcast(
+                LoggingEvents.VoiceIme.SETTINGS_WARNING_DIALOG_CANCEL));
+    }
+    
+    public void swipeHintDisplayed() {
+        mContext.sendBroadcast(newLoggingBroadcast(LoggingEvents.VoiceIme.SWIPE_HINT_DISPLAYED));
+    }
+    
+    public void cancelDuringListening() {
+        mContext.sendBroadcast(newLoggingBroadcast(LoggingEvents.VoiceIme.CANCEL_DURING_LISTENING));
+    }
+
+    public void cancelDuringWorking() {
+        mContext.sendBroadcast(newLoggingBroadcast(LoggingEvents.VoiceIme.CANCEL_DURING_WORKING));
+    }
+
+    public void cancelDuringError() {
+        mContext.sendBroadcast(newLoggingBroadcast(LoggingEvents.VoiceIme.CANCEL_DURING_ERROR));
+    }
+    
+    public void punctuationHintDisplayed() {
+        mContext.sendBroadcast(newLoggingBroadcast(
+                LoggingEvents.VoiceIme.PUNCTUATION_HINT_DISPLAYED));
+    }
+    
+    public void error(int code) {
+        Intent i = newLoggingBroadcast(LoggingEvents.VoiceIme.ERROR);
+        i.putExtra(LoggingEvents.VoiceIme.EXTRA_ERROR_CODE, code);
+        mContext.sendBroadcast(i);
+    }
+
+    public void start(String locale, boolean swipe) {
+        Intent i = newLoggingBroadcast(LoggingEvents.VoiceIme.START);
+        i.putExtra(LoggingEvents.VoiceIme.EXTRA_START_LOCALE, locale);
+        i.putExtra(LoggingEvents.VoiceIme.EXTRA_START_SWIPE, swipe);
+        i.putExtra(LoggingEvents.EXTRA_TIMESTAMP, System.currentTimeMillis());
+        mContext.sendBroadcast(i);
+    }
+    
+    public void voiceInputDelivered() {
+        mContext.sendBroadcast(newLoggingBroadcast(LoggingEvents.VoiceIme.VOICE_INPUT_DELIVERED));
+    }
+
+    public void textModified() {
+        mContext.sendBroadcast(newLoggingBroadcast(LoggingEvents.VoiceIme.TEXT_MODIFIED));
+    }
+
+    public void nBestChoose(int index) {
+        Intent i = newLoggingBroadcast(LoggingEvents.VoiceIme.N_BEST_CHOOSE);
+        i.putExtra(LoggingEvents.VoiceIme.EXTRA_N_BEST_CHOOSE_INDEX, index);
+        mContext.sendBroadcast(i);
+    }
+    
+    public void inputEnded() {
+        mContext.sendBroadcast(newLoggingBroadcast(LoggingEvents.VoiceIme.INPUT_ENDED));
+    }
+    
+    public void voiceInputSettingEnabled() {
+        mContext.sendBroadcast(newLoggingBroadcast(
+                LoggingEvents.VoiceIme.VOICE_INPUT_SETTING_ENABLED));
+    }
+    
+    public void voiceInputSettingDisabled() {
+        mContext.sendBroadcast(newLoggingBroadcast(
+                LoggingEvents.VoiceIme.VOICE_INPUT_SETTING_DISABLED));
+    }
+}
diff --git a/java/src/com/android/inputmethod/voice/WaveformImage.java b/java/src/com/android/inputmethod/voice/WaveformImage.java
new file mode 100644
index 0000000..08d87c8
--- /dev/null
+++ b/java/src/com/android/inputmethod/voice/WaveformImage.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2008-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.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+
+import java.io.ByteArrayOutputStream;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.ShortBuffer;
+
+/**
+ * Utility class to draw a waveform into a bitmap, given a byte array
+ * that represents the waveform as a sequence of 16-bit integers.
+ * Adapted from RecognitionActivity.java.
+ */
+public class WaveformImage {
+    private static final int SAMPLING_RATE = 8000;
+
+    private WaveformImage() {}
+
+    public static Bitmap drawWaveform(
+        ByteArrayOutputStream waveBuffer, int w, int h, int start, int end) {
+        final Bitmap b = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
+        final Canvas c = new Canvas(b);
+        final Paint paint = new Paint();
+        paint.setColor(0xFFFFFFFF); // 0xRRGGBBAA
+        paint.setAntiAlias(true);
+        paint.setStrokeWidth(0);
+
+        final ShortBuffer buf = ByteBuffer
+            .wrap(waveBuffer.toByteArray())
+            .order(ByteOrder.nativeOrder())
+            .asShortBuffer();
+        buf.position(0);
+
+        final int numSamples = waveBuffer.size() / 2;
+        final int delay = (SAMPLING_RATE * 100 / 1000);
+        int endIndex = end / 2 + delay;
+        if (end == 0 || endIndex >= numSamples) {
+            endIndex = numSamples;
+        }
+        int index = start / 2 - delay;
+        if (index < 0) {
+            index = 0;
+        }
+        final int size = endIndex - index;
+        int numSamplePerPixel = 32;
+        int delta = size / (numSamplePerPixel * w);
+        if (delta == 0) {
+            numSamplePerPixel = size / w;
+            delta = 1;
+        }
+
+        final float scale = 3.5f / 65536.0f;
+        // do one less column to make sure we won't read past
+        // the buffer.
+        try {
+            for (int i = 0; i < w - 1 ; i++) {
+                final float x = i;
+                for (int j = 0; j < numSamplePerPixel; j++) {
+                    final short s = buf.get(index);
+                    final float y = (h / 2) - (s * h * scale);
+                    c.drawPoint(x, y, paint);
+                    index += delta;
+                }
+            }
+        } catch (IndexOutOfBoundsException e) {
+            // this can happen, but we don't care
+        }
+
+        return b;
+    }
+}
diff --git a/java/src/com/android/inputmethod/voice/Whitelist.java b/java/src/com/android/inputmethod/voice/Whitelist.java
new file mode 100644
index 0000000..167b688
--- /dev/null
+++ b/java/src/com/android/inputmethod/voice/Whitelist.java
@@ -0,0 +1,67 @@
+/*
+ * 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.os.Bundle;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A set of text fields where speech has been explicitly enabled.
+ */
+public class Whitelist {
+    private List<Bundle> mConditions;
+
+    public Whitelist() {
+        mConditions = new ArrayList<Bundle>();
+    }
+
+    public Whitelist(List<Bundle> conditions) {
+        this.mConditions = conditions;
+    }
+
+    public void addApp(String app) {
+        Bundle bundle = new Bundle();
+        bundle.putString("packageName", app);
+        mConditions.add(bundle);
+    }
+
+    /**
+     * @return true if the field is a member of the whitelist.
+     */
+    public boolean matches(FieldContext context) {
+        for (Bundle condition : mConditions) {
+            if (matches(condition, context.getBundle())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * @return true of all values in condition are matched by a value
+     *     in target.
+     */
+    private boolean matches(Bundle condition, Bundle target) {
+        for (String key : condition.keySet()) {
+          if (!condition.getString(key).equals(target.getString(key))) {
+            return false;
+          }
+        }
+        return true;
+    }
+}
diff --git a/java/src/com/google/android/voicesearch/LatinIMEWithVoice.java b/java/src/com/google/android/voicesearch/LatinIMEWithVoice.java
new file mode 100644
index 0000000..8a339d1
--- /dev/null
+++ b/java/src/com/google/android/voicesearch/LatinIMEWithVoice.java
@@ -0,0 +1,29 @@
+/*
+ *
+ * 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
new file mode 100644
index 0000000..a53cebf
--- /dev/null
+++ b/java/src/com/google/android/voicesearch/LatinIMEWithVoiceSettings.java
@@ -0,0 +1,5 @@
+package com.google.android.voicesearch;
+
+import com.android.inputmethod.latin.LatinIMESettings;
+
+public class LatinIMEWithVoiceSettings extends LatinIMESettings {}