Merge remote branch 'goog/master'

Conflicts:
	java/src/com/android/inputmethod/latin/LatinIME.java
diff --git a/java/Android.mk b/java/Android.mk
index b6bbbf8..172f35a 100755
--- a/java/Android.mk
+++ b/java/Android.mk
@@ -5,15 +5,18 @@
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
 
-LOCAL_PACKAGE_NAME := LatinIME
+LOCAL_PACKAGE_NAME := LatinIme2Google
 
-LOCAL_CERTIFICATE := shared
+LOCAL_CERTIFICATE := vendor/google/certs/app
 
-LOCAL_JNI_SHARED_LIBRARIES := libjni_latinime
+LOCAL_JNI_SHARED_LIBRARIES := libjni_latinime2
 
 LOCAL_STATIC_JAVA_LIBRARIES := android-common
 
 #LOCAL_AAPT_FLAGS := -0 .dict
+# The following flag is required because we use a different package name
+# com.google.android.inputmethod.latin in the LatinIME sandbox.
+LOCAL_AAPT_FLAGS := --custom-package com.android.inputmethod.latin
 
 LOCAL_SDK_VERSION := 8
 
diff --git a/java/AndroidManifest.xml b/java/AndroidManifest.xml
index e229bc7..d14ca32 100755
--- a/java/AndroidManifest.xml
+++ b/java/AndroidManifest.xml
@@ -1,5 +1,9 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.android.inputmethod.latin">
+        package="com.google.android.inputmethod.latin"
+        android:versionCode="21"
+        android:versionName="0.21">
+
+    <uses-sdk android:minSdkVersion="8"></uses-sdk>
 
     <original-package android:name="com.android.inputmethod.latin" />
 
@@ -10,10 +14,10 @@
     <uses-permission android:name="android.permission.READ_CONTACTS" />
 
     <application android:label="@string/english_ime_name"
-            android:backupAgent="LatinIMEBackupAgent"
+            android:backupAgent="com.android.inputmethod.latin.LatinIMEBackupAgent"
             android:killAfterRestore="false">
 
-        <service android:name="LatinIME"
+        <service android:name="com.android.inputmethod.latin.LatinIME"
                 android:label="@string/english_ime_name"
                 android:permission="android.permission.BIND_INPUT_METHOD">
             <intent-filter>
@@ -22,13 +26,13 @@
             <meta-data android:name="android.view.im" android:resource="@xml/method" />
         </service>
 
-        <activity android:name="LatinIMESettings" android:label="@string/english_ime_settings">
+        <activity android:name="com.android.inputmethod.latin.LatinIMESettings" android:label="@string/english_ime_settings">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
             </intent-filter>
         </activity>
 
-        <activity android:name="InputLanguageSelection"
+        <activity android:name="com.android.inputmethod.latin.InputLanguageSelection"
                 android:label="@string/language_selection_title">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN"/>
diff --git a/java/res/drawable-hdpi/btn_close_normal.png b/java/res/drawable-hdpi/btn_close_normal.png
new file mode 100644
index 0000000..38b49f1
--- /dev/null
+++ b/java/res/drawable-hdpi/btn_close_normal.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_close_pressed.png b/java/res/drawable-hdpi/btn_close_pressed.png
new file mode 100644
index 0000000..aa9ea49
--- /dev/null
+++ b/java/res/drawable-hdpi/btn_close_pressed.png
Binary files differ
diff --git a/java/res/drawable-hdpi/btn_close_selected.png b/java/res/drawable-hdpi/btn_close_selected.png
new file mode 100644
index 0000000..870c670
--- /dev/null
+++ b/java/res/drawable-hdpi/btn_close_selected.png
Binary files differ
diff --git a/java/res/drawable-hdpi/keyboard_key_feedback_background.9.png b/java/res/drawable-hdpi/keyboard_key_feedback_background.9.png
new file mode 100644
index 0000000..6ba42db
--- /dev/null
+++ b/java/res/drawable-hdpi/keyboard_key_feedback_background.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/keyboard_key_feedback_more_background.9.png b/java/res/drawable-hdpi/keyboard_key_feedback_more_background.9.png
new file mode 100644
index 0000000..4d0b601
--- /dev/null
+++ b/java/res/drawable-hdpi/keyboard_key_feedback_more_background.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/keyboard_popup_panel_background.9.png b/java/res/drawable-hdpi/keyboard_popup_panel_background.9.png
new file mode 100644
index 0000000..8e2461b
--- /dev/null
+++ b/java/res/drawable-hdpi/keyboard_popup_panel_background.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/keyboard_popup_panel_trans_background.9.png b/java/res/drawable-hdpi/keyboard_popup_panel_trans_background.9.png
new file mode 100644
index 0000000..fd7366e
--- /dev/null
+++ b/java/res/drawable-hdpi/keyboard_popup_panel_trans_background.9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_123_mic.png b/java/res/drawable-hdpi/sym_bkeyboard_123_mic.png
new file mode 100644
index 0000000..3e4eff6
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_123_mic.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_delete.png b/java/res/drawable-hdpi/sym_bkeyboard_delete.png
new file mode 100644
index 0000000..1d24cc8
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_delete.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_done.png b/java/res/drawable-hdpi/sym_bkeyboard_done.png
new file mode 100644
index 0000000..b77803d
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_done.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_globe.png b/java/res/drawable-hdpi/sym_bkeyboard_globe.png
new file mode 100644
index 0000000..f5dbe0c
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_globe.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_mic.png b/java/res/drawable-hdpi/sym_bkeyboard_mic.png
new file mode 100644
index 0000000..512f460
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_mic.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_num0.png b/java/res/drawable-hdpi/sym_bkeyboard_num0.png
new file mode 100644
index 0000000..678a790
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_num0.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_num1.png b/java/res/drawable-hdpi/sym_bkeyboard_num1.png
new file mode 100644
index 0000000..4e68e35
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_num1.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_num2.png b/java/res/drawable-hdpi/sym_bkeyboard_num2.png
new file mode 100644
index 0000000..546663f
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_num2.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_num3.png b/java/res/drawable-hdpi/sym_bkeyboard_num3.png
new file mode 100644
index 0000000..57f9a8d
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_num3.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_num4.png b/java/res/drawable-hdpi/sym_bkeyboard_num4.png
new file mode 100644
index 0000000..de50438
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_num4.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_num5.png b/java/res/drawable-hdpi/sym_bkeyboard_num5.png
new file mode 100644
index 0000000..1d2e1ef
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_num5.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_num6.png b/java/res/drawable-hdpi/sym_bkeyboard_num6.png
new file mode 100644
index 0000000..39788b7
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_num6.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_num7.png b/java/res/drawable-hdpi/sym_bkeyboard_num7.png
new file mode 100644
index 0000000..fff6f27
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_num7.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_num8.png b/java/res/drawable-hdpi/sym_bkeyboard_num8.png
new file mode 100644
index 0000000..8cc1a95
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_num8.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_num9.png b/java/res/drawable-hdpi/sym_bkeyboard_num9.png
new file mode 100644
index 0000000..0217425
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_num9.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_numalt.png b/java/res/drawable-hdpi/sym_bkeyboard_numalt.png
new file mode 100644
index 0000000..200714f
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_numalt.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_numpound.png b/java/res/drawable-hdpi/sym_bkeyboard_numpound.png
new file mode 100644
index 0000000..0a46122
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_numpound.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_numstar.png b/java/res/drawable-hdpi/sym_bkeyboard_numstar.png
new file mode 100644
index 0000000..ca22bd5
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_numstar.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_return.png b/java/res/drawable-hdpi/sym_bkeyboard_return.png
new file mode 100644
index 0000000..426e159
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_return.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_search.png b/java/res/drawable-hdpi/sym_bkeyboard_search.png
new file mode 100644
index 0000000..1b6f884
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_search.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_shift.png b/java/res/drawable-hdpi/sym_bkeyboard_shift.png
new file mode 100644
index 0000000..5a22dd3
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_shift.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_shift_locked.png b/java/res/drawable-hdpi/sym_bkeyboard_shift_locked.png
new file mode 100644
index 0000000..5664491
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_shift_locked.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_space.png b/java/res/drawable-hdpi/sym_bkeyboard_space.png
new file mode 100644
index 0000000..cd0ebe2
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_space.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_tab.png b/java/res/drawable-hdpi/sym_bkeyboard_tab.png
new file mode 100644
index 0000000..3466e12
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_tab.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_bkeyboard_tabprev.png b/java/res/drawable-hdpi/sym_bkeyboard_tabprev.png
new file mode 100644
index 0000000..eb4f0eb
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_bkeyboard_tabprev.png
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_close_normal.png b/java/res/drawable-mdpi/btn_close_normal.png
new file mode 100644
index 0000000..4c6e79d
--- /dev/null
+++ b/java/res/drawable-mdpi/btn_close_normal.png
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_close_pressed.png b/java/res/drawable-mdpi/btn_close_pressed.png
new file mode 100644
index 0000000..fc983af
--- /dev/null
+++ b/java/res/drawable-mdpi/btn_close_pressed.png
Binary files differ
diff --git a/java/res/drawable-mdpi/btn_close_selected.png b/java/res/drawable-mdpi/btn_close_selected.png
new file mode 100644
index 0000000..f2bf91a
--- /dev/null
+++ b/java/res/drawable-mdpi/btn_close_selected.png
Binary files differ
diff --git a/java/res/drawable-mdpi/keyboard_key_feedback_background.9.png b/java/res/drawable-mdpi/keyboard_key_feedback_background.9.png
new file mode 100644
index 0000000..2a80f09
--- /dev/null
+++ b/java/res/drawable-mdpi/keyboard_key_feedback_background.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/keyboard_key_feedback_more_background.9.png b/java/res/drawable-mdpi/keyboard_key_feedback_more_background.9.png
new file mode 100755
index 0000000..29aa285
--- /dev/null
+++ b/java/res/drawable-mdpi/keyboard_key_feedback_more_background.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/keyboard_popup_panel_background.9.png b/java/res/drawable-mdpi/keyboard_popup_panel_background.9.png
new file mode 100644
index 0000000..36d75df
--- /dev/null
+++ b/java/res/drawable-mdpi/keyboard_popup_panel_background.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/keyboard_popup_panel_trans_background.9.png b/java/res/drawable-mdpi/keyboard_popup_panel_trans_background.9.png
new file mode 100644
index 0000000..4ba2a49
--- /dev/null
+++ b/java/res/drawable-mdpi/keyboard_popup_panel_trans_background.9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_123_mic.png b/java/res/drawable-mdpi/sym_bkeyboard_123_mic.png
new file mode 100644
index 0000000..0749b5f
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_123_mic.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_delete.png b/java/res/drawable-mdpi/sym_bkeyboard_delete.png
new file mode 100644
index 0000000..1a5ff43
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_delete.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_done.png b/java/res/drawable-mdpi/sym_bkeyboard_done.png
new file mode 100644
index 0000000..05ce7c6
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_done.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_globe.png b/java/res/drawable-mdpi/sym_bkeyboard_globe.png
new file mode 100644
index 0000000..c6595cf
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_globe.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_mic.png b/java/res/drawable-mdpi/sym_bkeyboard_mic.png
new file mode 100644
index 0000000..a6cb1cc
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_mic.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_num0.png b/java/res/drawable-mdpi/sym_bkeyboard_num0.png
new file mode 100644
index 0000000..7188f9c
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_num0.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_num1.png b/java/res/drawable-mdpi/sym_bkeyboard_num1.png
new file mode 100644
index 0000000..2a31bd4
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_num1.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_num2.png b/java/res/drawable-mdpi/sym_bkeyboard_num2.png
new file mode 100644
index 0000000..c1e9cc9
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_num2.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_num3.png b/java/res/drawable-mdpi/sym_bkeyboard_num3.png
new file mode 100644
index 0000000..e998766
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_num3.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_num4.png b/java/res/drawable-mdpi/sym_bkeyboard_num4.png
new file mode 100644
index 0000000..7f0f3cc
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_num4.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_num5.png b/java/res/drawable-mdpi/sym_bkeyboard_num5.png
new file mode 100644
index 0000000..5f748b4
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_num5.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_num6.png b/java/res/drawable-mdpi/sym_bkeyboard_num6.png
new file mode 100644
index 0000000..78aae74
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_num6.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_num7.png b/java/res/drawable-mdpi/sym_bkeyboard_num7.png
new file mode 100644
index 0000000..5bb874c
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_num7.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_num8.png b/java/res/drawable-mdpi/sym_bkeyboard_num8.png
new file mode 100644
index 0000000..6b58fdc
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_num8.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_num9.png b/java/res/drawable-mdpi/sym_bkeyboard_num9.png
new file mode 100644
index 0000000..f348c92
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_num9.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_numalt.png b/java/res/drawable-mdpi/sym_bkeyboard_numalt.png
new file mode 100644
index 0000000..4fa410b
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_numalt.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_numpound.png b/java/res/drawable-mdpi/sym_bkeyboard_numpound.png
new file mode 100644
index 0000000..9126eed
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_numpound.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_numstar.png b/java/res/drawable-mdpi/sym_bkeyboard_numstar.png
new file mode 100644
index 0000000..9b9f1b9
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_numstar.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_return.png b/java/res/drawable-mdpi/sym_bkeyboard_return.png
new file mode 100644
index 0000000..e76225d
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_return.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_search.png b/java/res/drawable-mdpi/sym_bkeyboard_search.png
new file mode 100644
index 0000000..1f18015
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_search.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_shift.png b/java/res/drawable-mdpi/sym_bkeyboard_shift.png
new file mode 100644
index 0000000..c981188
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_shift.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_shift_locked.png b/java/res/drawable-mdpi/sym_bkeyboard_shift_locked.png
new file mode 100644
index 0000000..b8cebd0
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_shift_locked.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_space.png b/java/res/drawable-mdpi/sym_bkeyboard_space.png
new file mode 100644
index 0000000..4da7ee8
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_space.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_tab.png b/java/res/drawable-mdpi/sym_bkeyboard_tab.png
new file mode 100644
index 0000000..2cb991c
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_tab.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_bkeyboard_tabprev.png b/java/res/drawable-mdpi/sym_bkeyboard_tabprev.png
new file mode 100644
index 0000000..5298291
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_bkeyboard_tabprev.png
Binary files differ
diff --git a/java/res/drawable/btn_close.xml b/java/res/drawable/btn_close.xml
new file mode 100644
index 0000000..ee58138
--- /dev/null
+++ b/java/res/drawable/btn_close.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:state_pressed="false" android:state_focused="false"
+        android:drawable="@drawable/btn_close_normal" />
+
+    <item android:state_pressed="true"
+        android:drawable="@drawable/btn_close_pressed" />
+
+    <item android:state_focused="true"
+        android:drawable="@drawable/btn_close_selected" />
+</selector>
diff --git a/java/res/drawable/btn_keyboard_key2.xml b/java/res/drawable/btn_keyboard_key2.xml
new file mode 100644
index 0000000..bd745b7
--- /dev/null
+++ b/java/res/drawable/btn_keyboard_key2.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <!-- Toggle keys. Use checkable/checked state. -->
+
+    <item android:state_checkable="true" android:state_checked="true"
+          android:state_pressed="true"
+          android:drawable="@drawable/btn_keyboard_key_pressed_on" />
+    <item android:state_checkable="true" android:state_pressed="true"
+          android:drawable="@drawable/btn_keyboard_key_fulltrans_pressed" />
+    <item android:state_checkable="true" android:state_checked="true"
+          android:drawable="@drawable/btn_keyboard_key_normal_on" />
+    <item android:state_checkable="true"
+          android:drawable="@drawable/btn_keyboard_key_fulltrans_normal" />
+
+    <!-- Normal keys -->
+
+    <item android:state_pressed="true"
+          android:drawable="@drawable/btn_keyboard_key_fulltrans_pressed" />
+    <item android:drawable="@drawable/btn_keyboard_key_fulltrans_normal" />
+</selector>
diff --git a/java/res/drawable/btn_keyboard_key3.xml b/java/res/drawable/btn_keyboard_key3.xml
new file mode 100644
index 0000000..dbe82d5
--- /dev/null
+++ b/java/res/drawable/btn_keyboard_key3.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <!-- Toggle keys. Use checkable/checked state. -->
+
+    <item android:state_checkable="true" android:state_checked="true"
+          android:state_pressed="true"
+          android:drawable="@drawable/btn_keyboard_key_pressed_on" />
+    <item android:state_checkable="true" android:state_pressed="true"
+          android:drawable="@drawable/btn_keyboard_key_fulltrans_normal" />
+    <item android:state_checkable="true" android:state_checked="true"
+          android:drawable="@drawable/btn_keyboard_key_normal_on" />
+    <item android:state_checkable="true"
+          android:drawable="@drawable/btn_keyboard_key_fulltrans_pressed" />
+
+    <!-- Normal keys -->
+
+    <item android:state_pressed="true"
+          android:drawable="@drawable/btn_keyboard_key_fulltrans_normal" />
+    <item android:drawable="@drawable/btn_keyboard_key_fulltrans_pressed" />
+</selector>
diff --git a/java/res/drawable/btn_keyboard_key_ginger.xml b/java/res/drawable/btn_keyboard_key_ginger.xml
new file mode 100644
index 0000000..7477037
--- /dev/null
+++ b/java/res/drawable/btn_keyboard_key_ginger.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <!-- Toggle keys. Use checkable/checked state. -->
+
+    <item android:state_checkable="true" android:state_checked="true"
+          android:state_pressed="true"
+          android:drawable="@drawable/btn_keyboard_key_normal_on_ginger" />
+    <item android:state_checkable="true" android:state_pressed="true"
+          android:drawable="@drawable/btn_keyboard_key_normal_off_ginger" />
+    <item android:state_checkable="true" android:state_checked="true"
+          android:drawable="@drawable/btn_keyboard_key_normal_on_ginger" />
+    <item android:state_checkable="true"
+          android:drawable="@drawable/btn_keyboard_key_normal_off_ginger" />
+
+    <!-- Normal keys -->
+
+    <item android:state_pressed="true"
+          android:drawable="@drawable/btn_keyboard_key_fulltrans_pressed" />
+    <item android:drawable="@drawable/btn_keyboard_key_normal_ginger" />
+</selector>
diff --git a/java/res/drawable/btn_keyboard_key_normal_ginger.9.png b/java/res/drawable/btn_keyboard_key_normal_ginger.9.png
new file mode 100644
index 0000000..46747bd
--- /dev/null
+++ b/java/res/drawable/btn_keyboard_key_normal_ginger.9.png
Binary files differ
diff --git a/java/res/drawable/btn_keyboard_key_normal_off_ginger.9.png b/java/res/drawable/btn_keyboard_key_normal_off_ginger.9.png
new file mode 100644
index 0000000..32c8e2e
--- /dev/null
+++ b/java/res/drawable/btn_keyboard_key_normal_off_ginger.9.png
Binary files differ
diff --git a/java/res/drawable/btn_keyboard_key_normal_on_ginger.9.png b/java/res/drawable/btn_keyboard_key_normal_on_ginger.9.png
new file mode 100644
index 0000000..a9814da
--- /dev/null
+++ b/java/res/drawable/btn_keyboard_key_normal_on_ginger.9.png
Binary files differ
diff --git a/java/res/drawable/btn_keyboard_normal_metal.9.png b/java/res/drawable/btn_keyboard_normal_metal.9.png
new file mode 100644
index 0000000..f4fe0a8
--- /dev/null
+++ b/java/res/drawable/btn_keyboard_normal_metal.9.png
Binary files differ
diff --git a/java/res/drawable/btn_keyboard_toggle_off.png b/java/res/drawable/btn_keyboard_toggle_off.png
new file mode 100644
index 0000000..21399a4
--- /dev/null
+++ b/java/res/drawable/btn_keyboard_toggle_off.png
Binary files differ
diff --git a/java/res/drawable/btn_keyboard_toggle_on.png b/java/res/drawable/btn_keyboard_toggle_on.png
new file mode 100644
index 0000000..22d5683
--- /dev/null
+++ b/java/res/drawable/btn_keyboard_toggle_on.png
Binary files differ
diff --git a/java/res/drawable/btn_led_off.9.png b/java/res/drawable/btn_led_off.9.png
new file mode 100644
index 0000000..68ce7a6
--- /dev/null
+++ b/java/res/drawable/btn_led_off.9.png
Binary files differ
diff --git a/java/res/drawable/btn_led_on.9.png b/java/res/drawable/btn_led_on.9.png
new file mode 100644
index 0000000..fe77abb
--- /dev/null
+++ b/java/res/drawable/btn_led_on.9.png
Binary files differ
diff --git a/java/res/drawable/keyboard_key_feedback.xml b/java/res/drawable/keyboard_key_feedback.xml
new file mode 100644
index 0000000..159ba86
--- /dev/null
+++ b/java/res/drawable/keyboard_key_feedback.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_long_pressable="true"
+            android:drawable="@drawable/keyboard_key_feedback_more_background" />
+
+    <item android:drawable="@drawable/keyboard_key_feedback_background" />
+</selector>
diff --git a/java/res/layout-xlarge/input_trans.xml b/java/res/layout-xlarge/input_trans.xml
deleted file mode 100755
index 1dfdc15..0000000
--- a/java/res/layout-xlarge/input_trans.xml
+++ /dev/null
@@ -1,31 +0,0 @@
-<?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="@color/latinkeyboard_extension_background"
-        android:verticalCorrection="0dip"
-        android:keyTextSize="32sp"
-        android:keyBackground="@drawable/btn_keyboard_key_fulltrans"
-        />
diff --git a/java/res/layout/input.xml b/java/res/layout/input_basic.xml
similarity index 83%
rename from java/res/layout/input.xml
rename to java/res/layout/input_basic.xml
index 1d7c6f7..ffa5954 100755
--- a/java/res/layout/input.xml
+++ b/java/res/layout/input_basic.xml
@@ -20,10 +20,12 @@
 
 <com.android.inputmethod.latin.LatinKeyboardView
         xmlns:android="http://schemas.android.com/apk/res/android"
-        android:id="@android:id/keyboardView"
+        xmlns:latin="http://schemas.android.com/apk/res/com.google.android.inputmethod.latin"
+        android:id="@+id/LatinkeyboardBaseView"
         android:layout_alignParentBottom="true"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:background="@drawable/keyboard_background"
-        android:keyBackground="@drawable/btn_keyboard_key"
+
+        latin:keyBackground="@drawable/btn_keyboard_key"
         />
diff --git a/java/res/layout-xlarge/input.xml b/java/res/layout/input_basic_highcontrast.xml
old mode 100644
new mode 100755
similarity index 78%
rename from java/res/layout-xlarge/input.xml
rename to java/res/layout/input_basic_highcontrast.xml
index 6b0bb12..dc4d097
--- a/java/res/layout-xlarge/input.xml
+++ b/java/res/layout/input_basic_highcontrast.xml
@@ -20,12 +20,13 @@
 
 <com.android.inputmethod.latin.LatinKeyboardView
         xmlns:android="http://schemas.android.com/apk/res/android"
-        android:id="@android:id/keyboardView"
+        xmlns:latin="http://schemas.android.com/apk/res/com.google.android.inputmethod.latin"
+
+        android:id="@+id/LatinkeyboardBaseView"
         android:layout_alignParentBottom="true"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:background="@drawable/keyboard_background"
-        android:keyBackground="@drawable/btn_keyboard_key"
-        android:keyTextSize="32sp"
-        android:verticalCorrection="0dip"
+        android:background="@android:color/black"
+
+        latin:keyBackground="@drawable/btn_keyboard_key3"
         />
diff --git a/java/res/layout-xlarge/input.xml b/java/res/layout/input_stone_bold.xml
old mode 100644
new mode 100755
similarity index 64%
copy from java/res/layout-xlarge/input.xml
copy to java/res/layout/input_stone_bold.xml
index 6b0bb12..8cc082b
--- a/java/res/layout-xlarge/input.xml
+++ b/java/res/layout/input_stone_bold.xml
@@ -20,12 +20,19 @@
 
 <com.android.inputmethod.latin.LatinKeyboardView
         xmlns:android="http://schemas.android.com/apk/res/android"
-        android:id="@android:id/keyboardView"
+        xmlns:latin="http://schemas.android.com/apk/res/com.google.android.inputmethod.latin"
+        android:id="@+id/LatinkeyboardBaseView"
         android:layout_alignParentBottom="true"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:background="@drawable/keyboard_background"
-        android:keyBackground="@drawable/btn_keyboard_key"
-        android:keyTextSize="32sp"
-        android:verticalCorrection="0dip"
+        android:textStyle="bold"
+
+        latin:keyBackground="@drawable/btn_keyboard_key_ginger"
+        latin:keyTextSize="22dip"
+        latin:keyTextColor="@color/latinkeyboard_key_color_black"
+        latin:shadowColor="@color/latinkeyboard_key_color_white"
+        latin:keyTextStyle="bold"
+        latin:symbolColorScheme="black"
+        latin:popupLayout="@layout/input_stone_popup"
         />
diff --git a/java/res/layout-xlarge/input.xml b/java/res/layout/input_stone_normal.xml
old mode 100644
new mode 100755
similarity index 67%
copy from java/res/layout-xlarge/input.xml
copy to java/res/layout/input_stone_normal.xml
index 6b0bb12..5169350
--- a/java/res/layout-xlarge/input.xml
+++ b/java/res/layout/input_stone_normal.xml
@@ -20,12 +20,17 @@
 
 <com.android.inputmethod.latin.LatinKeyboardView
         xmlns:android="http://schemas.android.com/apk/res/android"
-        android:id="@android:id/keyboardView"
+        xmlns:latin="http://schemas.android.com/apk/res/com.google.android.inputmethod.latin"
+        android:id="@+id/LatinkeyboardBaseView"
         android:layout_alignParentBottom="true"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:background="@drawable/keyboard_background"
-        android:keyBackground="@drawable/btn_keyboard_key"
-        android:keyTextSize="32sp"
-        android:verticalCorrection="0dip"
+
+        latin:keyBackground="@drawable/btn_keyboard_key_ginger"
+        latin:keyTextSize="22dip"
+        latin:keyTextColor="@color/latinkeyboard_key_color_black"
+        latin:shadowColor="@color/latinkeyboard_key_color_white"
+        latin:symbolColorScheme="black"
+        latin:popupLayout="@layout/input_stone_popup"
         />
diff --git a/java/res/layout/input_stone_popup.xml b/java/res/layout/input_stone_popup.xml
new file mode 100755
index 0000000..e6eae5d
--- /dev/null
+++ b/java/res/layout/input_stone_popup.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<LinearLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:background="@drawable/keyboard_popup_panel_background"
+        >
+    <com.android.inputmethod.latin.LatinKeyboardBaseView
+            xmlns:latin="http://schemas.android.com/apk/res/com.google.android.inputmethod.latin"
+            android:id="@+id/LatinKeyboardBaseView"
+            android:layout_alignParentBottom="true"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:background="@drawable/keyboard_background"
+
+            latin:keyBackground="@drawable/btn_keyboard_key_ginger"
+            latin:keyTextColor="@color/latinkeyboard_key_color_black"
+            latin:shadowColor="@color/latinkeyboard_key_color_white"
+            latin:keyTextSize="22dip"
+            latin:popupLayout="@layout/input_stone_popup"
+        />
+    <ImageButton android:id="@+id/closeButton"
+        android:background="@android:color/transparent"
+        android:src="@drawable/btn_close"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:layout_marginLeft="8dp"
+        android:clickable="true"
+        />
+</LinearLayout>
\ No newline at end of file
diff --git a/java/res/layout/input_trans.xml b/java/res/layout/input_trans.xml
index f66e8cd..ee5f85e 100755
--- a/java/res/layout/input_trans.xml
+++ b/java/res/layout/input_trans.xml
@@ -20,11 +20,13 @@
 
 <com.android.inputmethod.latin.LatinKeyboardView
         xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:latin="http://schemas.android.com/apk/res/com.google.android.inputmethod.latin"
         android:id="@android:id/keyboardView"
         android:layout_alignParentBottom="true"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:background="@color/latinkeyboard_extension_background"
         android:verticalCorrection="0dip"
-        android:keyBackground="@drawable/btn_keyboard_key_fulltrans"
+
+        latin:keyBackground="@drawable/btn_keyboard_key_fulltrans"
         />
diff --git a/java/res/values-xlarge/dimens.xml b/java/res/layout/keyboard_key_preview.xml
similarity index 64%
rename from java/res/values-xlarge/dimens.xml
rename to java/res/layout/keyboard_key_preview.xml
index 4331762..64eaa65 100644
--- a/java/res/values-xlarge/dimens.xml
+++ b/java/res/layout/keyboard_key_preview.xml
@@ -18,8 +18,12 @@
 */
 -->
 
-<resources>
-    <dimen name="key_height">72dip</dimen>
-    <dimen name="candidate_strip_height">46dip</dimen>
-    <dimen name="spacebar_vertical_correction">0dip</dimen>
-</resources>
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="80sp"
+    android:textSize="40sp"
+    android:textColor="?android:attr/textColorPrimaryInverse"
+    android:minWidth="32dip"
+    android:gravity="center"
+    android:background="@drawable/keyboard_key_feedback"
+    />
diff --git a/java/res/layout/keyboard_popup_keyboard.xml b/java/res/layout/keyboard_popup_keyboard.xml
new file mode 100644
index 0000000..e6b3a41
--- /dev/null
+++ b/java/res/layout/keyboard_popup_keyboard.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<LinearLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="horizontal"
+        android:background="@drawable/keyboard_popup_panel_background"
+        >
+    <com.android.inputmethod.latin.LatinKeyboardBaseView
+            xmlns:latin="http://schemas.android.com/apk/res/com.google.android.inputmethod.latin"
+            android:id="@+id/LatinKeyboardBaseView"
+            android:layout_alignParentBottom="true"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:background="@android:color/transparent"
+
+            latin:keyPreviewLayout="@layout/keyboard_key_preview"
+            latin:keyTextSize="22sp"
+            latin:popupLayout="@layout/keyboard_popup_keyboard"
+            />
+    <ImageButton android:id="@+id/closeButton"
+        android:background="@android:color/transparent"
+        android:src="@drawable/btn_close"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:layout_marginLeft="8dp"
+        android:clickable="true"
+        />
+</LinearLayout>
\ No newline at end of file
diff --git a/java/res/values-cs/strings.xml b/java/res/values-cs/strings.xml
index 4bc1f55..e063c8a 100644
--- a/java/res/values-cs/strings.xml
+++ b/java/res/values-cs/strings.xml
@@ -20,8 +20,8 @@
 
 <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="english_ime_name" msgid="7252517407088836577">"Klávesnice Android 2"</string>
+    <string name="english_ime_settings" msgid="6661589557206947774">"Nastavení klávesnice Android 2"</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>
diff --git a/java/res/values-da/strings.xml b/java/res/values-da/strings.xml
index 750d67b..09cb8c9 100644
--- a/java/res/values-da/strings.xml
+++ b/java/res/values-da/strings.xml
@@ -20,8 +20,8 @@
 
 <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="english_ime_name" msgid="7252517407088836577">"Android-tastatur 2"</string>
+    <string name="english_ime_settings" msgid="6661589557206947774">"Indstillinger for Android-tastatur 2"</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>
diff --git a/java/res/values-de/strings.xml b/java/res/values-de/strings.xml
index ed6408d..02ed5a6 100644
--- a/java/res/values-de/strings.xml
+++ b/java/res/values-de/strings.xml
@@ -20,8 +20,8 @@
 
 <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="english_ime_name" msgid="7252517407088836577">"Android-Tastatur 2"</string>
+    <string name="english_ime_settings" msgid="6661589557206947774">"Android-Tastatur2einstellungen"</string>
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrieren b. Tastendruck"</string>
     <string name="sound_on_keypress" msgid="6093592297198243644">"Ton bei Tastendruck"</string>
     <string name="hit_correction" msgid="4855351009261318389">"Eingabefehler korrigieren"</string>
diff --git a/java/res/values-el/strings.xml b/java/res/values-el/strings.xml
index c4a5077..fbe705f 100644
--- a/java/res/values-el/strings.xml
+++ b/java/res/values-el/strings.xml
@@ -20,8 +20,8 @@
 
 <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="english_ime_name" msgid="7252517407088836577">"Πληκτρολόγιο Android 2"</string>
+    <string name="english_ime_settings" msgid="6661589557206947774">"Ρυθμίσεις πληκτρολογίου Android 2"</string>
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"Δόνηση κατά το πάτημα πλήκτρων"</string>
     <string name="sound_on_keypress" msgid="6093592297198243644">"Ήχος κατά το πάτημα πλήκτρων"</string>
     <string name="hit_correction" msgid="4855351009261318389">"Διόρθωση σφαλμάτων πληκτρολόγησης"</string>
diff --git a/java/res/values-es-rUS/strings.xml b/java/res/values-es-rUS/strings.xml
index cd17dba..150ff75 100644
--- a/java/res/values-es-rUS/strings.xml
+++ b/java/res/values-es-rUS/strings.xml
@@ -20,8 +20,8 @@
 
 <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="english_ime_name" msgid="7252517407088836577">"Teclado de Android 2"</string>
+    <string name="english_ime_settings" msgid="6661589557206947774">"Configuración de teclado de Android 2"</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>
diff --git a/java/res/values-es/strings.xml b/java/res/values-es/strings.xml
index fbe3ad3..4ef3eac 100644
--- a/java/res/values-es/strings.xml
+++ b/java/res/values-es/strings.xml
@@ -20,8 +20,8 @@
 
 <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">"Ajustes del teclado de Android"</string>
+    <string name="english_ime_name" msgid="7252517407088836577">"Teclado de Android 2"</string>
+    <string name="english_ime_settings" msgid="6661589557206947774">"Ajustes del teclado de Android 2"</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>
diff --git a/java/res/values-fr-rCA/strings.xml b/java/res/values-fr-rCA/strings.xml
index b56463e..e0d456d 100644
--- a/java/res/values-fr-rCA/strings.xml
+++ b/java/res/values-fr-rCA/strings.xml
@@ -15,5 +15,5 @@
 -->
 <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_name" msgid="7252517407088836577">"Clavier Android 2"</string>
 </resources>
diff --git a/java/res/values-fr/strings.xml b/java/res/values-fr/strings.xml
index 2cabe40..d4ec878 100644
--- a/java/res/values-fr/strings.xml
+++ b/java/res/values-fr/strings.xml
@@ -20,8 +20,8 @@
 
 <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="english_ime_name" msgid="7252517407088836577">"Clavier Android 2"</string>
+    <string name="english_ime_settings" msgid="6661589557206947774">"Paramètres du clavier Android 2"</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>
diff --git a/java/res/values-it/strings.xml b/java/res/values-it/strings.xml
index 8100175..297ea5c 100644
--- a/java/res/values-it/strings.xml
+++ b/java/res/values-it/strings.xml
@@ -20,8 +20,8 @@
 
 <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="english_ime_name" msgid="7252517407088836577">"Tastiera Android 2"</string>
+    <string name="english_ime_settings" msgid="6661589557206947774">"Impostazioni tastiera Android 2"</string>
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"Vibrazione tasti"</string>
     <string name="sound_on_keypress" msgid="6093592297198243644">"Suono tasti"</string>
     <string name="hit_correction" msgid="4855351009261318389">"Correggi errori di digitazione"</string>
diff --git a/java/res/values-ja/strings.xml b/java/res/values-ja/strings.xml
index 7867684..f77cb21 100644
--- a/java/res/values-ja/strings.xml
+++ b/java/res/values-ja/strings.xml
@@ -20,8 +20,8 @@
 
 <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="english_ime_name" msgid="7252517407088836577">"Androidキーボード 2"</string>
+    <string name="english_ime_settings" msgid="6661589557206947774">"Androidキーボード 2 の設定"</string>
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"キー操作バイブ"</string>
     <string name="sound_on_keypress" msgid="6093592297198243644">"キー操作音"</string>
     <string name="hit_correction" msgid="4855351009261318389">"入力ミス補正"</string>
diff --git a/java/res/values-ko/strings.xml b/java/res/values-ko/strings.xml
index 94d1ff4..414a149 100644
--- a/java/res/values-ko/strings.xml
+++ b/java/res/values-ko/strings.xml
@@ -20,8 +20,8 @@
 
 <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="english_ime_name" msgid="7252517407088836577">"Android 키보드 2"</string>
+    <string name="english_ime_settings" msgid="6661589557206947774">"Android 키보드 2 설정"</string>
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"키를 누를 때 진동 발생"</string>
     <string name="sound_on_keypress" msgid="6093592297198243644">"키를 누를 때 소리 발생"</string>
     <string name="hit_correction" msgid="4855351009261318389">"입력 오류 수정"</string>
diff --git a/java/res/values-nb/strings.xml b/java/res/values-nb/strings.xml
index 041d07e..5c1bbf1 100644
--- a/java/res/values-nb/strings.xml
+++ b/java/res/values-nb/strings.xml
@@ -20,8 +20,8 @@
 
 <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="english_ime_name" msgid="7252517407088836577">"Skjermtastatur 2"</string>
+    <string name="english_ime_settings" msgid="6661589557206947774">"Innstillinger for skjermtastatur 2"</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>
diff --git a/java/res/values-nl/strings.xml b/java/res/values-nl/strings.xml
index 00b197b..c5771c8 100644
--- a/java/res/values-nl/strings.xml
+++ b/java/res/values-nl/strings.xml
@@ -20,8 +20,8 @@
 
 <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="english_ime_name" msgid="7252517407088836577">"Android-toetsenbord 2"</string>
+    <string name="english_ime_settings" msgid="6661589557206947774">"Instellingen voor Android-toetsenbord 2"</string>
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"Trillen bij druk op 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>
diff --git a/java/res/values-pl/strings.xml b/java/res/values-pl/strings.xml
index 0c72727..561191d 100644
--- a/java/res/values-pl/strings.xml
+++ b/java/res/values-pl/strings.xml
@@ -20,8 +20,8 @@
 
 <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="english_ime_name" msgid="7252517407088836577">"Klawiatura Android 2"</string>
+    <string name="english_ime_settings" msgid="6661589557206947774">"Ustawienia klawiatury Android 2"</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>
diff --git a/java/res/values-pt-rPT/strings.xml b/java/res/values-pt-rPT/strings.xml
index 35a9cb7..39007c1 100644
--- a/java/res/values-pt-rPT/strings.xml
+++ b/java/res/values-pt-rPT/strings.xml
@@ -20,8 +20,8 @@
 
 <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="english_ime_name" msgid="7252517407088836577">"Teclado do Android 2"</string>
+    <string name="english_ime_settings" msgid="6661589557206947774">"Definições de teclado do Android 2"</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>
diff --git a/java/res/values-pt/strings.xml b/java/res/values-pt/strings.xml
index 235fd65..38890d8 100644
--- a/java/res/values-pt/strings.xml
+++ b/java/res/values-pt/strings.xml
@@ -20,8 +20,8 @@
 
 <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="english_ime_name" msgid="7252517407088836577">"Teclado Android 2"</string>
+    <string name="english_ime_settings" msgid="6661589557206947774">"Configurações de teclado Android 2"</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>
diff --git a/java/res/values-ru/strings.xml b/java/res/values-ru/strings.xml
index e27402c..f1a8a1c 100644
--- a/java/res/values-ru/strings.xml
+++ b/java/res/values-ru/strings.xml
@@ -20,8 +20,8 @@
 
 <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="english_ime_name" msgid="7252517407088836577">"Клавиатура Android 2"</string>
+    <string name="english_ime_settings" msgid="6661589557206947774">"Настройки клавиатуры Android 2"</string>
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"Виброотклик клавиш"</string>
     <string name="sound_on_keypress" msgid="6093592297198243644">"Звук клавиш"</string>
     <string name="hit_correction" msgid="4855351009261318389">"Исправлять опечатки"</string>
diff --git a/java/res/values-sv/strings.xml b/java/res/values-sv/strings.xml
index 9c6c221..8970a05 100644
--- a/java/res/values-sv/strings.xml
+++ b/java/res/values-sv/strings.xml
@@ -20,8 +20,8 @@
 
 <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="english_ime_name" msgid="7252517407088836577">"Androids tangentbord 2"</string>
+    <string name="english_ime_settings" msgid="6661589557206947774">"Inställningar för Androids tangentbord 2"</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>
diff --git a/java/res/values-tr/strings.xml b/java/res/values-tr/strings.xml
index 0fbdc7d..86d9569 100644
--- a/java/res/values-tr/strings.xml
+++ b/java/res/values-tr/strings.xml
@@ -20,8 +20,8 @@
 
 <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="english_ime_name" msgid="7252517407088836577">"Android klavyesi 2"</string>
+    <string name="english_ime_settings" msgid="6661589557206947774">"Android klavye 2 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>
diff --git a/java/res/values-zh-rCN/strings.xml b/java/res/values-zh-rCN/strings.xml
index 9c9b257..b0a0781 100644
--- a/java/res/values-zh-rCN/strings.xml
+++ b/java/res/values-zh-rCN/strings.xml
@@ -20,8 +20,8 @@
 
 <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="english_ime_name" msgid="7252517407088836577">"Android 键盘 2"</string>
+    <string name="english_ime_settings" msgid="6661589557206947774">"Android 键盘 2 设置"</string>
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"按键时振动"</string>
     <string name="sound_on_keypress" msgid="6093592297198243644">"按键时播放音效"</string>
     <string name="hit_correction" msgid="4855351009261318389">"纠正输入错误"</string>
diff --git a/java/res/values-zh-rTW/strings.xml b/java/res/values-zh-rTW/strings.xml
index 4f83be4..89b2eac 100644
--- a/java/res/values-zh-rTW/strings.xml
+++ b/java/res/values-zh-rTW/strings.xml
@@ -20,8 +20,8 @@
 
 <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="english_ime_name" msgid="7252517407088836577">"Android 鍵盤 2"</string>
+    <string name="english_ime_settings" msgid="6661589557206947774">"Android 鍵盤 2 設定"</string>
     <string name="vibrate_on_keypress" msgid="5258079494276955460">"按鍵時震動"</string>
     <string name="sound_on_keypress" msgid="6093592297198243644">"按鍵時播放音效"</string>
     <string name="hit_correction" msgid="4855351009261318389">"修正輸入錯誤"</string>
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml
new file mode 100644
index 0000000..e3171eb
--- /dev/null
+++ b/java/res/values/attrs.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+
+    <declare-styleable name="LatinKeyboardBaseView">
+        <!-- Default KeyboardView style. -->
+        <attr name="keyboardViewStyle" format="reference" />
+
+        <!-- Image for the key. This image needs to be a StateListDrawable, with the following
+             possible states: normal, pressed, checkable, checkable+pressed, checkable+checked,
+             checkable+checked+pressed. -->
+        <attr name="keyBackground" format="reference" />
+
+        <!-- Size of the text for character keys. -->
+        <attr name="keyTextSize" format="dimension" />
+
+        <!-- Size of the text for custom keys with some text and no icon. -->
+        <attr name="labelTextSize" format="dimension" />
+
+        <!-- Color to use for the label in a key. -->
+        <attr name="keyTextColor" format="color" />
+
+        <!-- Layout resource for key press feedback.-->
+        <attr name="keyPreviewLayout" format="reference" />
+
+        <!-- Vertical offset of the key press feedback from the key. -->
+        <attr name="keyPreviewOffset" format="dimension" />
+
+        <!-- Height of the key press feedback popup. -->
+        <attr name="keyPreviewHeight" format="dimension" />
+
+        <!-- Amount to offset the touch Y coordinate by, for bias correction. -->
+        <attr name="verticalCorrection" format="dimension" />
+
+        <!-- Layout resource for popup keyboards. -->
+        <attr name="popupLayout" format="reference" />
+
+        <attr name="shadowColor" format="color" />
+        <attr name="shadowRadius" format="float" />
+        <attr name="backgroundDimAmount" format="float" />
+
+        <attr name="keyTextStyle">
+            <flag name="normal" value="0" />
+            <flag name="bold" value="1" />
+            <flag name="italic" value="2" />
+        </attr>
+
+        <attr name="symbolColorScheme">
+            <flag name="white" value="0" />
+            <flag name="black" value="1" />
+        </attr>
+
+    </declare-styleable>
+
+</resources>
diff --git a/java/res/values/bools.xml b/java/res/values/bools.xml
index ebe2f04..a0cebbb 100644
--- a/java/res/values/bools.xml
+++ b/java/res/values/bools.xml
@@ -25,4 +25,5 @@
     <bool name="im_is_default">false</bool>
     <!-- Whether or not voice input is enabled by default. -->
     <bool name="voice_input_default">true</bool>
+    <bool name="config_swipeDisambiguation">true</bool>
 </resources>
diff --git a/java/res/values/colors.xml b/java/res/values/colors.xml
index d4bb138..343a940 100644
--- a/java/res/values/colors.xml
+++ b/java/res/values/colors.xml
@@ -22,8 +22,11 @@
     <color name="candidate_recommended">#FFE35900</color>
     <color name="candidate_other">#ff808080</color>
     <color name="latinkeyboard_transparent">#00000000</color>
-    <color name="latinkeyboard_bar_language_shadow">#80000000</color>
+    <color name="latinkeyboard_bar_language_shadow_white">#80000000</color>
+    <color name="latinkeyboard_bar_language_shadow_black">#80FFFFFF</color>
     <color name="latinkeyboard_bar_language_text">#FF808080</color>
     <color name="latinkeyboard_extension_background">#A0000000</color>
     <color name="latinkeyboard_text_color">#FF000000</color>
-</resources>
\ No newline at end of file
+    <color name="latinkeyboard_key_color_white">#FFFFFFFF</color>
+    <color name="latinkeyboard_key_color_black">#FF000000</color>
+</resources>
diff --git a/java/res/values/donottranslate.xml b/java/res/values/donottranslate.xml
index d501735..b7bfd9c 100644
--- a/java/res/values/donottranslate.xml
+++ b/java/res/values/donottranslate.xml
@@ -21,9 +21,9 @@
     <!-- Symbols that are commonly considered word separators in this language -->
     <string name="word_separators">.\u0009\u0020,;:!?\n()[]*&amp;@{}/&lt;&gt;_+=|\u0022</string>
     <!-- Symbols that are sentence separators, for purposes of making it hug the last sentence. -->
-    <string name="sentence_separators">.,!?</string>
+    <string name="sentence_separators">.,!?)</string>
     <!-- Symbols that are suggested between words -->
-    <string name="suggested_punctuations">!?,@_</string>
+    <string name="suggested_punctuations">!?,\u0022\u0027:()-/@_</string>
     <!-- Accented characters related to "d" -->
     <string name="alternates_for_d"></string>
     <!-- Accented characters related to "r" -->
diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml
index 70a5b7e..6c7aa83 100644
--- a/java/res/values/strings.xml
+++ b/java/res/values/strings.xml
@@ -19,15 +19,16 @@
 -->
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Title for Latin keyboard  -->
-    <string name="english_ime_name">Android keyboard</string>
+    <string name="english_ime_name">Android keyboard 2</string>
     <!-- Title for Latin keyboard settings activity / dialog -->
-    <string name="english_ime_settings">Android keyboard settings</string>
+    <string name="english_ime_settings">Android keyboard 2 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>
     
@@ -327,4 +328,32 @@
     
     <!-- Inform the user that a particular language has an available dictionary -->
     <string name="has_dictionary">Dictionary available</string>
+
+    <!-- Option to send logs -->
+    <string name="prefs_enable_log">Enable user feedback</string>
+    <!-- Description for sending logs -->
+    <string name="prefs_description_log">Help improve this input method editor by automatically sending usage statistics and crash reports to Google.</string>
+
+    <string name="keyboard_layout">Keyboard Theme</string>
+    <string name="layout_basic" translatable="false">Basic</string>
+    <string name="layout_high_contrast" translatable="false">Basic (High Contrast)</string>
+    <string name="layout_stone_bold"  translatable="false">Default (bold)</string>
+    <string name="layout_stone_normal"  translatable="false">Default (normal)</string>
+
+    <string-array name="keyboard_layout_modes" translatable="false">
+        <item>@string/layout_basic</item>
+        <item>@string/layout_high_contrast</item>
+        <item>@string/layout_stone_normal</item>
+        <item>@string/layout_stone_bold</item>
+    </string-array>
+
+    <string-array name="keyboard_layout_modes_values" translatable="false">
+        <item>0</item>
+        <item>1</item>
+        <item>2</item>
+        <item>3</item>
+    </string-array>
+
+    <string name="prefs_debug_mode">Debug (Temporary)</string>
+
 </resources>
diff --git a/java/res/values/styles.xml b/java/res/values/styles.xml
new file mode 100644
index 0000000..1433550
--- /dev/null
+++ b/java/res/values/styles.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <style name="LatinKeyboardBaseView">
+        <item name="android:background">@drawable/keyboard_background</item>
+
+        <item name="keyBackground">@drawable/btn_keyboard_key</item>
+        <item name="keyTextSize">22sp</item>
+        <item name="keyTextColor">#FFFFFFFF</item>
+        <item name="keyPreviewLayout">@layout/keyboard_key_preview</item>
+        <item name="keyPreviewOffset">-12dip</item>
+        <item name="keyPreviewHeight">80dip</item>
+        <item name="labelTextSize">14sp</item>
+        <item name="popupLayout">@layout/keyboard_popup_keyboard</item>
+        <item name="verticalCorrection">-10dip</item>
+        <item name="shadowColor">#BB000000</item>
+        <item name="shadowRadius">2.75</item>
+        <item name="backgroundDimAmount">0.5</item>
+        <item name="symbolColorScheme">white</item>
+    </style>
+</resources>
diff --git a/java/res/xml-de/kbd_qwerty_black.xml b/java/res/xml-de/kbd_qwerty_black.xml
new file mode 100755
index 0000000..366f871
--- /dev/null
+++ b/java/res/xml-de/kbd_qwerty_black.xml
@@ -0,0 +1,189 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
+    android:keyWidth="10%p"
+    android:horizontalGap="0px"
+    android:verticalGap="0px"
+    android:keyHeight="@dimen/key_height"
+    >
+
+    <Row>
+        <Key android:codes="113" android:keyLabel="q" android:keyEdgeFlags="left"/>
+        <Key android:codes="119" android:keyLabel="w"/>
+        <Key android:codes="101" android:keyLabel="e"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="@string/alternates_for_e"
+        />
+        <Key android:codes="114" android:keyLabel="r"/>
+        <Key android:codes="116" android:keyLabel="t"/>
+        <Key android:codes="122" android:keyLabel="z" />
+        <Key android:codes="117" android:keyLabel="u"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="@string/alternates_for_u"
+        />
+        <Key android:codes="105" android:keyLabel="i"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="@string/alternates_for_i"
+        />
+        <Key android:codes="111" android:keyLabel="o"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="@string/alternates_for_o"
+        />
+        <Key android:codes="112" android:keyLabel="p" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row>
+        <Key android:codes="97" android:keyLabel="a" android:horizontalGap="5%p"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="@string/alternates_for_a"
+                android:keyEdgeFlags="left"/>
+        <Key android:codes="115" android:keyLabel="s"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="@string/alternates_for_s"
+        />
+        <Key android:codes="100" android:keyLabel="d"/>
+        <Key android:codes="102" android:keyLabel="f"/>
+        <Key android:codes="103" android:keyLabel="g"/>
+        <Key android:codes="104" android:keyLabel="h"/>
+        <Key android:codes="106" android:keyLabel="j"/>
+        <Key android:codes="107" android:keyLabel="k"/>
+        <Key android:codes="108" android:keyLabel="l" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row>
+        <Key android:codes="-1" android:keyIcon="@drawable/sym_bkeyboard_shift"
+                android:keyWidth="15%p" android:isModifier="true"
+                android:iconPreview="@drawable/sym_keyboard_feedback_shift"
+                android:isSticky="true" android:keyEdgeFlags="left"/>
+        <Key android:codes="121" android:keyLabel="y"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="@string/alternates_for_y"
+        />
+        <Key android:codes="120" android:keyLabel="x"/>
+        <Key android:codes="99" android:keyLabel="c"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="@string/alternates_for_c"
+        />
+        <Key android:codes="118" android:keyLabel="v"/>
+        <Key android:codes="98" android:keyLabel="b"/>
+        <Key android:codes="110" android:keyLabel="n"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="@string/alternates_for_n"
+        />
+        <Key android:codes="109" android:keyLabel="m"/>
+        <Key android:codes="-5" android:keyIcon="@drawable/sym_bkeyboard_delete"
+                android:keyWidth="15%p" android:keyEdgeFlags="right"
+                android:iconPreview="@drawable/sym_keyboard_feedback_delete"
+                android:isRepeatable="true"/>
+    </Row>
+
+    <Row android:keyboardMode="@+id/mode_normal" android:rowEdgeFlags="bottom">
+        <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="_"
+                android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+        <Key android:codes="@integer/key_f1"
+                android:keyWidth="10%p"/>
+        <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+                android:iconPreview="@drawable/sym_keyboard_feedback_space"
+                android:keyWidth="40%p" android:isRepeatable="true"/>
+        <Key android:codes="46" android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+                android:keyWidth="10%p"/>
+        <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+                android:iconPreview="@drawable/sym_keyboard_feedback_return"
+                android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row android:keyboardMode="@+id/mode_url" android:rowEdgeFlags="bottom">
+        <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="_"
+                android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+        <Key android:keyLabel="/" android:keyWidth="10%p"/>
+        <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+                android:iconPreview="@drawable/sym_keyboard_feedback_space"
+                android:keyWidth="40%p" android:isRepeatable="true"/>
+        <!--Key android:keyLabel="@string/popular_domain_0"
+                android:keyOutputText="@string/popular_domain_0"
+                android:popupKeyboard="@xml/popup_domains"
+                android:keyWidth="20%p"/-->
+        <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+                android:keyWidth="10%p"/>
+        <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+                android:iconPreview="@drawable/sym_keyboard_feedback_return"
+                android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row android:keyboardMode="@+id/mode_email" android:rowEdgeFlags="bottom">
+        <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="_"
+                android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+        <Key android:keyLabel="\@"/>
+        <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+                android:iconPreview="@drawable/sym_keyboard_feedback_space"
+                android:keyWidth="40%p" android:isRepeatable="true"/>
+        <!--Key android:keyLabel="@string/popular_domain_0"
+                android:keyOutputText="@string/popular_domain_0"
+                android:popupKeyboard="@xml/popup_domains"
+                android:keyWidth="20%p"/-->
+        <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"/>
+        <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+                android:iconPreview="@drawable/sym_keyboard_feedback_return"
+                android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row android:keyboardMode="@+id/mode_im" android:rowEdgeFlags="bottom">
+        <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="_"
+                android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+        <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_bkeyboard_globe"
+                android:keyWidth="10%p"/>
+        <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+                android:iconPreview="@drawable/sym_keyboard_feedback_space"
+                android:keyWidth="40%p" android:isRepeatable="true"/>
+        <Key android:codes="46" android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+                android:keyWidth="10%p"/>
+        <Key android:keyLabel=":-)" android:keyOutputText=":-) "
+                android:popupKeyboard="@xml/popup_smileys"
+                android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row android:keyboardMode="@+id/mode_webentry" android:rowEdgeFlags="bottom">
+        <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="_"
+                android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+        <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_bkeyboard_globe"
+                android:keyWidth="10%p"/>
+        <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+                android:iconPreview="@drawable/sym_keyboard_feedback_space"
+                android:keyWidth="20%p" android:isRepeatable="true"/>
+        <Key android:codes="9" android:keyIcon="@drawable/sym_bkeyboard_tab"
+                android:iconPreview="@drawable/sym_keyboard_feedback_tab"
+                android:keyWidth="20%p"/>
+        <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"/>
+        <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+                android:iconPreview="@drawable/sym_keyboard_feedback_return"
+                android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+    </Row>
+</Keyboard>
diff --git a/java/res/xml-fr/kbd_qwerty_black.xml b/java/res/xml-fr/kbd_qwerty_black.xml
new file mode 100644
index 0000000..1b799a5
--- /dev/null
+++ b/java/res/xml-fr/kbd_qwerty_black.xml
@@ -0,0 +1,192 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
+    android:keyWidth="10%p"
+    android:horizontalGap="0px"
+    android:verticalGap="0px"
+    android:keyHeight="@dimen/key_height"
+    >
+
+    <Row>
+        <Key android:codes="97" android:keyLabel="a"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="@string/alternates_for_a"
+                android:keyEdgeFlags="left"/>
+        <Key android:codes="122" android:keyLabel="z"/>
+        <Key android:codes="101" android:keyLabel="e"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="@string/alternates_for_e"
+        />
+        <Key android:codes="114" android:keyLabel="r"/>
+        <Key android:codes="116" android:keyLabel="t"/>
+        <Key android:codes="121" android:keyLabel="y"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="@string/alternates_for_y"
+        />
+        <Key android:codes="117" android:keyLabel="u"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="@string/alternates_for_u"
+        />
+        <Key android:codes="105" android:keyLabel="i"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="@string/alternates_for_i"
+        />
+        <Key android:codes="111" android:keyLabel="o"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="@string/alternates_for_o"
+        />
+        <Key android:codes="112" android:keyLabel="p" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row>
+        <Key android:codes="113" android:keyLabel="q" android:keyEdgeFlags="left"/>
+        <Key android:codes="115" android:keyLabel="s"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="@string/alternates_for_s"
+        />
+        <Key android:codes="100" android:keyLabel="d"/>
+        <Key android:codes="102" android:keyLabel="f"/>
+        <Key android:codes="103" android:keyLabel="g"/>
+        <Key android:codes="104" android:keyLabel="h"/>
+        <Key android:codes="106" android:keyLabel="j"/>
+        <Key android:codes="107" android:keyLabel="k"/>
+        <Key android:codes="108" android:keyLabel="l"/>
+        <Key android:codes="109" android:keyLabel="m" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row>
+        <Key android:codes="-1" android:keyIcon="@drawable/sym_bkeyboard_shift"
+                android:keyWidth="15%p" android:isModifier="true"
+                android:iconPreview="@drawable/sym_keyboard_feedback_shift"
+                android:isSticky="true" android:keyEdgeFlags="left"/>
+        <Key android:codes="119" android:keyLabel="w"/>
+        <Key android:codes="120" android:keyLabel="x"/>
+        <Key android:codes="99" android:keyLabel="c"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="@string/alternates_for_c"
+        />
+        <Key android:codes="118" android:keyLabel="v"/>
+        <Key android:codes="98" android:keyLabel="b"/>
+        <Key android:codes="110" android:keyLabel="n"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="@string/alternates_for_n"
+        />
+        <!--Key android:codes="233,224,232,234" android:keyLabel="é"/-->
+        <Key android:keyLabel="\'"/>
+        <Key android:codes="-5" android:keyIcon="@drawable/sym_bkeyboard_delete"
+                android:keyWidth="15%p" android:keyEdgeFlags="right"
+                android:iconPreview="@drawable/sym_keyboard_feedback_delete"
+                android:isRepeatable="true"/>
+    </Row>
+
+    <Row android:keyboardMode="@+id/mode_normal" android:rowEdgeFlags="bottom">
+        <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="_"
+                android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+        <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_bkeyboard_globe"
+                android:keyWidth="10%p"/>
+        <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+                android:iconPreview="@drawable/sym_keyboard_feedback_space"
+                android:keyWidth="40%p" android:isRepeatable="true"/>
+        <Key android:codes="46" android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+                android:keyWidth="10%p"/>
+        <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+                android:iconPreview="@drawable/sym_keyboard_feedback_return"
+                android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row android:keyboardMode="@+id/mode_url" android:rowEdgeFlags="bottom">
+        <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="_"
+                android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+        <Key android:keyLabel="/" android:keyWidth="10%p"/>
+        <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+                android:iconPreview="@drawable/sym_keyboard_feedback_space"
+                android:keyWidth="40%p" android:isRepeatable="true"/>
+        <!--Key android:keyLabel="@string/popular_domain_0"
+                android:keyOutputText="@string/popular_domain_0"
+                android:popupKeyboard="@xml/popup_domains"
+                android:keyWidth="20%p"/-->
+        <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+                android:keyWidth="10%p"/>
+        <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+                android:iconPreview="@drawable/sym_keyboard_feedback_return"
+                android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row android:keyboardMode="@+id/mode_email" android:rowEdgeFlags="bottom">
+        <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="_"
+                android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+        <Key android:keyLabel="\@"/>
+        <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+                android:iconPreview="@drawable/sym_keyboard_feedback_space"
+                android:keyWidth="40%p" android:isRepeatable="true"/>
+        <!--Key android:keyLabel="@string/popular_domain_0"
+                android:keyOutputText="@string/popular_domain_0"
+                android:popupKeyboard="@xml/popup_domains"
+                android:keyWidth="20%p"/-->
+        <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"/>
+        <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+                android:iconPreview="@drawable/sym_keyboard_feedback_return"
+                android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row android:keyboardMode="@+id/mode_im" android:rowEdgeFlags="bottom">
+        <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="_"
+                android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+        <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_bkeyboard_globe"
+                android:keyWidth="10%p"/>
+        <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+                android:iconPreview="@drawable/sym_keyboard_feedback_space"
+                android:keyWidth="40%p" android:isRepeatable="true"/>
+        <Key android:codes="46" android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+                android:keyWidth="10%p"/>
+        <Key android:keyLabel=":-)" android:keyOutputText=":-) "
+                android:popupKeyboard="@xml/popup_smileys"
+                android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row android:keyboardMode="@+id/mode_webentry" android:rowEdgeFlags="bottom">
+        <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="_"
+                android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+        <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_bkeyboard_globe"
+                android:keyWidth="10%p"/>
+        <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+                android:iconPreview="@drawable/sym_keyboard_feedback_space"
+                android:keyWidth="20%p" android:isRepeatable="true"/>
+        <Key android:codes="9" android:keyIcon="@drawable/sym_bkeyboard_tab"
+                android:iconPreview="@drawable/sym_keyboard_feedback_tab"
+                android:keyWidth="20%p"/>
+        <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"/>
+        <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+                android:iconPreview="@drawable/sym_keyboard_feedback_return"
+                android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+    </Row>
+
+</Keyboard>
diff --git a/java/res/xml-iw/kbd_qwerty_black.xml b/java/res/xml-iw/kbd_qwerty_black.xml
new file mode 100755
index 0000000..0dcf513
--- /dev/null
+++ b/java/res/xml-iw/kbd_qwerty_black.xml
@@ -0,0 +1,164 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
+    android:keyWidth="10%p"
+    android:horizontalGap="0px"
+    android:verticalGap="0px"
+    android:keyHeight="@dimen/key_height"
+    >
+
+    <Row>
+        <Key android:keyLabel="ק"
+	     android:horizontalGap="5%p"
+	     android:keyEdgeFlags="left"/>
+        <Key android:keyLabel="ר"/>
+        <Key android:keyLabel="א"/>
+        <Key android:keyLabel="ט"/>
+        <Key android:keyLabel="ו"/>
+        <Key android:keyLabel="ן"/>
+        <Key android:keyLabel="ם"/>
+        <Key android:keyLabel="פ"/>
+        <Key android:codes="-5"
+	     android:horizontalGap="1.25%p"
+	     android:keyIcon="@drawable/sym_bkeyboard_delete"
+            android:keyWidth="13.75%p" android:keyEdgeFlags="right"
+            android:iconPreview="@drawable/sym_keyboard_feedback_delete"
+            android:isRepeatable="true"/>
+    </Row>
+
+    <Row>
+        <Key android:keyLabel="ש" android:keyEdgeFlags="left"/>
+        <Key android:keyLabel="ד"/>
+        <Key android:keyLabel="ג"/>
+        <Key android:keyLabel="כ"/>
+        <Key android:keyLabel="ע"/>
+        <Key android:keyLabel="י"/>
+        <Key android:keyLabel="ח"/>
+        <Key android:keyLabel="ל"/>
+        <Key android:keyLabel="ך"/>
+        <Key android:keyLabel="ף" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row>
+        <Key android:keyLabel="ז" android:horizontalGap="5%p" android:keyEdgeFlags="left"/>
+        <Key android:keyLabel="ס"/>
+        <Key android:keyLabel="ב"/>
+        <Key android:keyLabel="ה"/>
+        <Key android:keyLabel="נ"/>
+        <Key android:keyLabel="מ"/>
+        <Key android:keyLabel="צ"/>
+        <Key android:keyLabel="ת"/>
+        <Key android:keyLabel="ץ" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row android:keyboardMode="@+id/mode_normal" android:rowEdgeFlags="bottom">
+        <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="_"
+                android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+        <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_bkeyboard_globe"
+                android:keyWidth="10%p"/>
+        <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+                android:iconPreview="@drawable/sym_keyboard_feedback_space"
+                android:keyWidth="40%p" android:isRepeatable="true"/>
+        <Key android:codes="46" android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+                android:keyWidth="10%p"/>
+        <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+                android:iconPreview="@drawable/sym_keyboard_feedback_return"
+                android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row android:keyboardMode="@+id/mode_url" android:rowEdgeFlags="bottom">
+        <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="_"
+                android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+        <Key android:keyLabel="/" android:keyWidth="10%p"/>
+        <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+                android:iconPreview="@drawable/sym_keyboard_feedback_space"
+                android:keyWidth="40%p" android:isRepeatable="true"/>
+        <!--Key android:keyLabel="@string/popular_domain_0"
+                android:keyOutputText="@string/popular_domain_0"
+                android:popupKeyboard="@xml/popup_domains"
+                android:keyWidth="20%p"/-->
+        <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+                android:keyWidth="10%p"/>
+        <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+                android:iconPreview="@drawable/sym_keyboard_feedback_return"
+                android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row android:keyboardMode="@+id/mode_email" android:rowEdgeFlags="bottom">
+        <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="_"
+                android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+        <Key android:keyLabel="\@" android:keyWidth="10%p"/>
+        <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+                android:iconPreview="@drawable/sym_keyboard_feedback_space"
+                android:keyWidth="40%p" android:isRepeatable="true"/>
+        <!--Key android:keyLabel="@string/popular_domain_0"
+                android:keyOutputText="@string/popular_domain_0"
+                android:popupKeyboard="@xml/popup_domains"
+                android:keyWidth="20%p"/-->
+        <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"/>
+        <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+                android:iconPreview="@drawable/sym_keyboard_feedback_return"
+                android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row android:keyboardMode="@+id/mode_im" android:rowEdgeFlags="bottom">
+        <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="_"
+                android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+        <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_bkeyboard_globe"
+                android:keyWidth="10%p"/>
+        <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+                android:iconPreview="@drawable/sym_keyboard_feedback_space"
+                android:keyWidth="40%p" android:isRepeatable="true"/>
+        <Key android:codes="46" android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+                android:keyWidth="10%p"/>
+        <Key android:keyLabel=":-)" android:keyOutputText=":-) "
+                android:popupKeyboard="@xml/popup_smileys"
+                android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row android:keyboardMode="@+id/mode_webentry" android:rowEdgeFlags="bottom">
+        <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="_"
+                android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+        <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_bkeyboard_globe"
+                android:keyWidth="10%p"/>
+        <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+                android:iconPreview="@drawable/sym_keyboard_feedback_space"
+                android:keyWidth="20%p" android:isRepeatable="true"/>
+        <Key android:codes="9" android:keyIcon="@drawable/sym_bkeyboard_tab"
+                android:iconPreview="@drawable/sym_keyboard_feedback_tab"
+                android:keyWidth="20%p"/>
+        <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"/>
+        <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+                android:iconPreview="@drawable/sym_keyboard_feedback_return"
+                android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+    </Row>
+</Keyboard>
+
diff --git a/java/res/xml-ru/kbd_qwerty_black.xml b/java/res/xml-ru/kbd_qwerty_black.xml
new file mode 100755
index 0000000..4923be0
--- /dev/null
+++ b/java/res/xml-ru/kbd_qwerty_black.xml
@@ -0,0 +1,175 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
+    android:keyWidth="9.09%p"
+    android:horizontalGap="0px"
+    android:verticalGap="0px"
+    android:keyHeight="@dimen/key_height"
+    >
+
+    <Row>
+        <Key android:keyLabel="й" android:keyWidth="8.75%p"
+                android:keyEdgeFlags="left"/>
+        <Key android:keyLabel="ц"/>
+        <Key android:keyLabel="у"/>
+        <Key android:keyLabel="к"/>
+        <Key android:keyLabel="е"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="ё" />
+        <Key android:keyLabel="н"/>
+        <Key android:keyLabel="г"/>
+        <Key android:keyLabel="ш"/>
+        <Key android:keyLabel="щ"/>
+        <Key android:keyLabel="з"/>
+        <Key android:keyLabel="х" android:keyWidth="8.75%p"
+                android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row>
+        <Key android:keyLabel="ф" android:keyWidth="8.75%p"
+                android:keyEdgeFlags="left"/>
+        <Key android:keyLabel="ы"/>
+        <Key android:keyLabel="в"/>
+        <Key android:keyLabel="а"/>
+        <Key android:keyLabel="п"/>
+        <Key android:keyLabel="р"/>
+        <Key android:keyLabel="о"/>
+        <Key android:keyLabel="л"/>
+        <Key android:keyLabel="д"/>
+        <Key android:keyLabel="ж"/>
+        <Key android:keyLabel="э" android:keyWidth="8.75%p"
+                android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row android:keyWidth="8.5%p">
+        <Key android:codes="-1" android:keyIcon="@drawable/sym_bkeyboard_shift"
+                android:keyWidth="11.75%p" android:isModifier="true"
+                android:iconPreview="@drawable/sym_keyboard_feedback_shift"
+                android:isSticky="true" android:keyEdgeFlags="left"/>
+        <Key android:keyLabel="я"/>
+        <Key android:keyLabel="ч"/>
+        <Key android:keyLabel="с"/>
+        <Key android:keyLabel="м"/>
+        <Key android:keyLabel="и"/>
+        <Key android:keyLabel="т"/>
+        <Key android:keyLabel="ь"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="ъ" />
+        <Key android:keyLabel="б"/>
+        <Key android:keyLabel="ю"/>
+        <Key android:codes="-5" android:keyIcon="@drawable/sym_bkeyboard_delete"
+                android:keyWidth="11.75%p" android:keyEdgeFlags="right"
+                android:iconPreview="@drawable/sym_keyboard_feedback_delete"
+                android:isRepeatable="true"/>
+    </Row>
+
+    <Row android:keyboardMode="@+id/mode_normal" android:rowEdgeFlags="bottom">
+        <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="_"
+                android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+        <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_bkeyboard_globe"
+                android:keyWidth="10%p"/>
+        <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+                android:iconPreview="@drawable/sym_keyboard_feedback_space"
+                android:keyWidth="40%p" android:isRepeatable="true"/>
+        <Key android:codes="46" android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+                android:keyWidth="10%p"/>
+        <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+                android:iconPreview="@drawable/sym_keyboard_feedback_return"
+                android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row android:keyboardMode="@+id/mode_url" android:rowEdgeFlags="bottom">
+        <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="_"
+                android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+        <Key android:keyLabel="/" android:keyWidth="10%p"/>
+        <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+                android:iconPreview="@drawable/sym_keyboard_feedback_space"
+                android:keyWidth="40%p" android:isRepeatable="true"/>
+        <!--Key android:keyLabel="@string/popular_domain_0"
+                android:keyOutputText="@string/popular_domain_0"
+                android:popupKeyboard="@xml/popup_domains"
+                android:keyWidth="20%p"/-->
+        <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+                android:keyWidth="10%p"/>
+        <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+                android:iconPreview="@drawable/sym_keyboard_feedback_return"
+                android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row android:keyboardMode="@+id/mode_email" android:rowEdgeFlags="bottom">
+        <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="_"
+                android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+        <Key android:keyLabel="\@" android:keyWidth="10%p"/>
+        <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+                android:iconPreview="@drawable/sym_keyboard_feedback_space"
+                android:keyWidth="40%p" android:isRepeatable="true"/>
+        <!--Key android:keyLabel="@string/popular_domain_0"
+                android:keyOutputText="@string/popular_domain_0"
+                android:popupKeyboard="@xml/popup_domains"
+                android:keyWidth="20%p"/-->
+        <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"/>
+        <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+                android:iconPreview="@drawable/sym_keyboard_feedback_return"
+                android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row android:keyboardMode="@+id/mode_im" android:rowEdgeFlags="bottom">
+        <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="_"
+                android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+        <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_bkeyboard_globe"
+                android:keyWidth="10%p"/>
+        <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+                android:iconPreview="@drawable/sym_keyboard_feedback_space"
+                android:keyWidth="40%p" android:isRepeatable="true"/>
+        <Key android:codes="46" android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+                android:keyWidth="10%p"/>
+        <Key android:keyLabel=":-)" android:keyOutputText=":-) "
+                android:popupKeyboard="@xml/popup_smileys"
+                android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row android:keyboardMode="@+id/mode_webentry" android:rowEdgeFlags="bottom">
+        <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="_"
+                android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+        <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_bkeyboard_globe"
+                android:keyWidth="10%p"/>
+        <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+                android:iconPreview="@drawable/sym_keyboard_feedback_space"
+                android:keyWidth="20%p" android:isRepeatable="true"/>
+        <Key android:codes="9" android:keyIcon="@drawable/sym_bkeyboard_tab"
+                android:iconPreview="@drawable/sym_keyboard_feedback_tab"
+                android:keyWidth="20%p"/>
+        <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"/>
+        <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+                android:iconPreview="@drawable/sym_keyboard_feedback_return"
+                android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+    </Row>
+</Keyboard>
diff --git a/java/res/xml-sv/kbd_qwerty_black.xml b/java/res/xml-sv/kbd_qwerty_black.xml
new file mode 100644
index 0000000..6604bc8
--- /dev/null
+++ b/java/res/xml-sv/kbd_qwerty_black.xml
@@ -0,0 +1,215 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!--
+    Swedish Keyboard Layout
+
+    Key positioning: Svensk standard SS 66 22 41
+    Foreign letters: Svenska skrivregler (2:a uppl.) §302
+    Local additions: ۧ
+-->
+
+<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
+    android:keyWidth="9.09%p"
+    android:horizontalGap="0px"
+    android:verticalGap="0px"
+    android:keyHeight="@dimen/key_height"
+    >
+
+    <Row>
+        <Key android:codes="113" android:keyLabel="q"
+                android:keyWidth="8.75%p" android:keyEdgeFlags="left"/>
+        <Key android:codes="119" android:keyLabel="w"/>
+        <Key android:codes="101" android:keyLabel="e"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="éèêëę€"/>
+        <Key android:codes="114" android:keyLabel="r"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="ř"/>
+        <Key android:codes="116" android:keyLabel="t"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="ťþ"/>
+        <Key android:codes="121" android:keyLabel="y"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="ýÿü"/>
+        <Key android:codes="117" android:keyLabel="u"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="úùûū"/>
+        <Key android:codes="105" android:keyLabel="i"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="íìîï"/>
+        <Key android:codes="111" android:keyLabel="o"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="óòôõ"/>
+        <Key android:codes="112" android:keyLabel="p"/>
+        <Key android:keyLabel="å"
+                android:keyWidth="8.75%p" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row>
+        <Key android:codes="97" android:keyLabel="a"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="áàâąã"
+                android:keyWidth="8.75%p" android:keyEdgeFlags="left"/>
+        <Key android:codes="115" android:keyLabel="s"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="śšşß"/>
+        <Key android:codes="100" android:keyLabel="d"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="ðď"/>
+        <Key android:codes="102" android:keyLabel="f"/>
+        <Key android:codes="103" android:keyLabel="g"/>
+        <Key android:codes="104" android:keyLabel="h"/>
+        <Key android:codes="106" android:keyLabel="j"/>
+        <Key android:codes="107" android:keyLabel="k"/>
+        <Key android:codes="108" android:keyLabel="l"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="ł"/>
+        <Key android:keyLabel="ö"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="øœ"/>
+        <Key android:keyLabel="ä"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="æ"
+                android:keyWidth="8.75%p" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row android:keyWidth="10%p">
+        <Key android:codes="-1" android:keyIcon="@drawable/sym_bkeyboard_shift"
+                android:keyWidth="15%p" android:isModifier="true"
+                android:iconPreview="@drawable/sym_keyboard_feedback_shift"
+                android:isSticky="true" android:keyEdgeFlags="left"/>
+        <Key android:codes="122" android:keyLabel="z"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="źžż"/>
+        <Key android:codes="120" android:keyLabel="x"/>
+        <Key android:codes="99" android:keyLabel="c"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="çćč"/>
+        <Key android:codes="118" android:keyLabel="v"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="w"/>
+        <Key android:codes="98" android:keyLabel="b"/>
+        <Key android:codes="110" android:keyLabel="n"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="ńñň"/>
+        <Key android:codes="109" android:keyLabel="m"/>
+        <Key android:codes="-5" android:keyIcon="@drawable/sym_bkeyboard_delete"
+                android:keyWidth="15%p" android:keyEdgeFlags="right"
+                android:iconPreview="@drawable/sym_keyboard_feedback_delete"
+                android:isRepeatable="true"/>
+    </Row>
+
+    <Row android:keyboardMode="@+id/mode_normal" android:rowEdgeFlags="bottom">
+        <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="_"
+                android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+        <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_bkeyboard_mic"
+                android:iconPreview="@drawable/sym_keyboard_feedback_mic"
+                android:keyWidth="10%p"/>
+        <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+                android:iconPreview="@drawable/sym_keyboard_feedback_space"
+                android:keyWidth="40%p" android:isRepeatable="true"/>
+        <Key android:codes="46" android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+                android:keyWidth="10%p"/>
+        <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+                android:iconPreview="@drawable/sym_keyboard_feedback_return"
+                android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row android:keyboardMode="@+id/mode_url" android:rowEdgeFlags="bottom">
+        <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="_"
+                android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+        <Key android:keyLabel="/" android:keyWidth="10%p"/>
+        <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+                android:iconPreview="@drawable/sym_keyboard_feedback_space"
+                android:keyWidth="40%p" android:isRepeatable="true"/>
+        <!--Key android:keyLabel="@string/popular_domain_0"
+                android:keyOutputText="@string/popular_domain_0"
+                android:popupKeyboard="@xml/popup_domains"
+                android:keyWidth="20%p"/-->
+        <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+                android:keyWidth="10%p"/>
+        <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+                android:iconPreview="@drawable/sym_keyboard_feedback_return"
+                android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row android:keyboardMode="@+id/mode_email" android:rowEdgeFlags="bottom">
+        <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="_"
+                android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+        <Key android:keyLabel="\@"/>
+        <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+                android:iconPreview="@drawable/sym_keyboard_feedback_space"
+                android:keyWidth="40%p" android:isRepeatable="true"/>
+        <!--Key android:keyLabel="@string/popular_domain_0"
+                android:keyOutputText="@string/popular_domain_0"
+                android:popupKeyboard="@xml/popup_domains"
+                android:keyWidth="20%p"/-->
+        <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"/>
+        <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+                android:iconPreview="@drawable/sym_keyboard_feedback_return"
+                android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row android:keyboardMode="@+id/mode_im" android:rowEdgeFlags="bottom">
+        <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="_"
+                android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+        <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_bkeyboard_mic"
+                android:iconPreview="@drawable/sym_keyboard_feedback_mic"
+                android:keyWidth="10%p"/>
+        <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+                android:iconPreview="@drawable/sym_keyboard_feedback_space"
+                android:keyWidth="40%p" android:isRepeatable="true"/>
+        <Key android:codes="46" android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+                android:keyWidth="10%p"/>
+        <Key android:keyLabel=":-)" android:keyOutputText=":-) "
+                android:popupKeyboard="@xml/popup_smileys"
+                android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row android:keyboardMode="@+id/mode_webentry" android:rowEdgeFlags="bottom">
+        <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="_"
+                android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+        <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_bkeyboard_mic"
+                android:iconPreview="@drawable/sym_keyboard_feedback_mic"
+                android:keyWidth="10%p"/>
+        <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+                android:iconPreview="@drawable/sym_keyboard_feedback_space"
+                android:keyWidth="20%p" android:isRepeatable="true"/>
+        <Key android:codes="9" android:keyIcon="@drawable/sym_bkeyboard_tab"
+                android:iconPreview="@drawable/sym_keyboard_feedback_tab"
+                android:keyWidth="20%p"/>
+        <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"/>
+        <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+                android:iconPreview="@drawable/sym_keyboard_feedback_return"
+                android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+    </Row>
+
+</Keyboard>
diff --git a/java/res/xml/kbd_qwerty_black.xml b/java/res/xml/kbd_qwerty_black.xml
new file mode 100755
index 0000000..d013ae0
--- /dev/null
+++ b/java/res/xml/kbd_qwerty_black.xml
@@ -0,0 +1,207 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
+    android:keyWidth="10%p"
+    android:horizontalGap="0px"
+    android:verticalGap="0px"
+    android:keyHeight="@dimen/key_height"
+    >
+
+    <Row>
+        <Key android:codes="113" android:keyLabel="q" android:keyEdgeFlags="left"/>
+        <Key android:codes="119" android:keyLabel="w"/>
+        <Key android:codes="101" android:keyLabel="e"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="@string/alternates_for_e"
+        />
+        <Key android:codes="114" android:keyLabel="r"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="@string/alternates_for_r"/>
+        <Key android:codes="116" android:keyLabel="t"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="@string/alternates_for_t"/>
+        <Key android:codes="121" android:keyLabel="y"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="@string/alternates_for_y"
+        />
+        <Key android:codes="117" android:keyLabel="u"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="@string/alternates_for_u"
+        />
+        <Key android:codes="105" android:keyLabel="i"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="@string/alternates_for_i"
+        />
+        <Key android:codes="111" android:keyLabel="o"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="@string/alternates_for_o"
+        />
+        <Key android:codes="112" android:keyLabel="p" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row>
+        <Key android:codes="97" android:keyLabel="a" android:horizontalGap="5%p"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="@string/alternates_for_a"
+                android:keyEdgeFlags="left"/>
+        <Key android:codes="115" android:keyLabel="s"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="@string/alternates_for_s"
+        />
+        <Key android:codes="100" android:keyLabel="d"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="@string/alternates_for_d"/>
+        <Key android:codes="102" android:keyLabel="f"/>
+        <Key android:codes="103" android:keyLabel="g"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="@string/alternates_for_g"
+        />
+        <Key android:codes="104" android:keyLabel="h"/>
+        <Key android:codes="106" android:keyLabel="j"/>
+        <Key android:codes="107" android:keyLabel="k"/>
+        <Key android:codes="108" android:keyLabel="l"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="@string/alternates_for_l"
+                android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row>
+        <Key android:codes="-1" android:keyIcon="@drawable/sym_bkeyboard_shift"
+                android:keyWidth="15%p" android:isModifier="true"
+                android:iconPreview="@drawable/sym_keyboard_feedback_shift"
+                android:isSticky="true" android:keyEdgeFlags="left"/>
+        <Key android:codes="122" android:keyLabel="z"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="@string/alternates_for_z"/>
+        <Key android:codes="120" android:keyLabel="x"/>
+        <Key android:codes="99" android:keyLabel="c"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="@string/alternates_for_c"
+        />
+        <Key android:codes="118" android:keyLabel="v"/>
+        <Key android:codes="98" android:keyLabel="b"/>
+        <Key android:codes="110" android:keyLabel="n"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="@string/alternates_for_n"
+        />
+        <Key android:codes="109" android:keyLabel="m"/>
+        <Key android:codes="-5" android:keyIcon="@drawable/sym_bkeyboard_delete"
+                android:keyWidth="15%p" android:keyEdgeFlags="right"
+                android:iconPreview="@drawable/sym_keyboard_feedback_delete"
+                android:isRepeatable="true"/>
+    </Row>
+
+    <Row android:keyboardMode="@+id/mode_normal" android:rowEdgeFlags="bottom">
+        <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="_"
+                android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+        <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_bkeyboard_mic"
+                android:iconPreview="@drawable/sym_keyboard_feedback_mic"
+                android:keyWidth="10%p"/>
+        <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+                android:iconPreview="@drawable/sym_keyboard_feedback_space"
+                android:keyWidth="40%p" android:isRepeatable="true"/>
+        <Key android:codes="46" android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+                android:keyWidth="10%p"/>
+        <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+                android:iconPreview="@drawable/sym_keyboard_feedback_return"
+                android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row android:keyboardMode="@+id/mode_url" android:rowEdgeFlags="bottom">
+        <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="_"
+                android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+        <Key android:keyLabel="/" android:keyWidth="10%p"/>
+        <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+                android:iconPreview="@drawable/sym_keyboard_feedback_space"
+                android:keyWidth="40%p" android:isRepeatable="true"/>
+        <!--Key android:keyLabel="@string/popular_domain_0"
+                android:keyOutputText="@string/popular_domain_0"
+                android:popupKeyboard="@xml/popup_domains"
+                android:keyWidth="20%p"/-->
+        <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+                android:keyWidth="10%p"/>
+        <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+                android:iconPreview="@drawable/sym_keyboard_feedback_return"
+                android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row android:keyboardMode="@+id/mode_email" android:rowEdgeFlags="bottom">
+        <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="_"
+                android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+        <Key android:keyLabel="\@"/>
+        <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+                android:iconPreview="@drawable/sym_keyboard_feedback_space"
+                android:keyWidth="40%p" android:isRepeatable="true"/>
+        <!--Key android:keyLabel="@string/popular_domain_0"
+                android:keyOutputText="@string/popular_domain_0"
+                android:popupKeyboard="@xml/popup_domains"
+                android:keyWidth="20%p"/-->
+        <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"/>
+        <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+                android:iconPreview="@drawable/sym_keyboard_feedback_return"
+                android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row android:keyboardMode="@+id/mode_im" android:rowEdgeFlags="bottom">
+        <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="_"
+                android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+        <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_bkeyboard_mic"
+                android:iconPreview="@drawable/sym_keyboard_feedback_mic"
+                android:keyWidth="10%p"/>
+        <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+                android:iconPreview="@drawable/sym_keyboard_feedback_space"
+                android:keyWidth="40%p" android:isRepeatable="true"/>
+        <Key android:codes="46" android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"
+                android:keyWidth="10%p"/>
+        <Key android:keyLabel=":-)" android:keyOutputText=":-) "
+                android:popupKeyboard="@xml/popup_smileys"
+                android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row android:keyboardMode="@+id/mode_webentry" android:rowEdgeFlags="bottom">
+        <Key android:codes="-2" android:keyLabel="@string/label_symbol_key"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="_"
+                android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+        <Key android:codes="@integer/key_f1" android:keyIcon="@drawable/sym_bkeyboard_mic"
+                android:iconPreview="@drawable/sym_keyboard_feedback_mic"
+                android:keyWidth="10%p"/>
+        <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+                android:iconPreview="@drawable/sym_keyboard_feedback_space"
+                android:keyWidth="20%p" android:isRepeatable="true"/>
+        <Key android:codes="9" android:keyIcon="@drawable/sym_bkeyboard_tab"
+                android:iconPreview="@drawable/sym_keyboard_feedback_tab"
+                android:keyWidth="20%p"/>
+        <Key android:keyLabel="." android:popupKeyboard="@xml/popup_punctuation"/>
+        <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+                android:iconPreview="@drawable/sym_keyboard_feedback_return"
+                android:keyWidth="20%p" android:keyEdgeFlags="right"/>
+    </Row>
+
+</Keyboard>
diff --git a/java/res/xml/kbd_symbols_black.xml b/java/res/xml/kbd_symbols_black.xml
new file mode 100755
index 0000000..5652f7f
--- /dev/null
+++ b/java/res/xml/kbd_symbols_black.xml
@@ -0,0 +1,141 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
+    android:keyWidth="10%p"
+    android:horizontalGap="0px"
+    android:verticalGap="0px"
+    android:keyHeight="@dimen/key_height"
+    >
+
+    <Row>
+        <Key android:codes="49" android:keyLabel="1" android:keyEdgeFlags="left"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="¹½⅓¼⅛"
+        />
+        <Key android:codes="50" android:keyLabel="2"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="²⅔"
+        />
+        <Key android:codes="51" android:keyLabel="3"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="³¾⅜"
+        />
+        <Key android:codes="52" android:keyLabel="4"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="⁴"
+        />
+        <Key android:codes="53" android:keyLabel="5"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="⅝"
+        />
+        <Key android:codes="54" android:keyLabel="6"/>
+        <Key android:codes="55" android:keyLabel="7"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="⅞"
+        />
+        <Key android:codes="56" android:keyLabel="8"/>
+        <Key android:codes="57" android:keyLabel="9"/>
+        <Key android:codes="48" android:keyLabel="0"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="ⁿ∅"
+                android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row>
+        <Key android:codes="64" android:keyLabel="\@" android:keyEdgeFlags="left"/>
+        <Key android:codes="35" android:keyLabel="\#"/>
+        <Key android:codes="36" android:keyLabel="$"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="¢£€¥₣₤₱"
+        />
+        <Key android:codes="37" android:keyLabel="%"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="‰"
+        />
+        <Key android:codes="38" android:keyLabel="&amp;"/>
+        <Key android:codes="42" android:keyLabel="*"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="†‡★"
+        />
+        <Key android:codes="45" android:keyLabel="-"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="_–—"
+        />
+        <Key android:keyLabel="+"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="±"
+        />
+        <Key android:codes="40" android:keyLabel="("
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="[{&lt;"
+        />
+        <Key android:codes="41" android:keyLabel=")" android:keyEdgeFlags="right"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="]}&gt;"
+        />
+    </Row>
+
+    <Row>
+        <Key android:codes="-1" android:keyLabel="@string/label_alt_key"
+                android:keyWidth="15%p" android:isModifier="true"
+                android:isSticky="true" android:keyEdgeFlags="left"/>
+        <Key android:codes="33" android:keyLabel="!"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="¡"
+        />
+        <Key android:codes="34" android:keyLabel="&quot;"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="“”«»˝"
+        />
+        <Key android:codes="39" android:keyLabel="\'"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="‘’"
+        />
+        <Key android:codes="58" android:keyLabel=":"/>
+        <Key android:codes="59" android:keyLabel=";"/>
+        <Key android:codes="47" android:keyLabel="/" />
+        <Key android:codes="63" android:keyLabel="\?"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="¿"
+        />
+        <Key android:codes="-5" android:keyIcon="@drawable/sym_bkeyboard_delete" android:keyWidth="15%p" android:keyEdgeFlags="right"
+                android:iconPreview="@drawable/sym_keyboard_feedback_delete"
+                android:isRepeatable="true"/>
+    </Row>
+
+    <Row  android:rowEdgeFlags="bottom">
+        <Key android:codes="-2" android:keyLabel="@string/label_alpha_key"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="_"
+                android:keyWidth="20%p" android:keyEdgeFlags="left"/>
+        <Key android:codes="@integer/key_f1" android:keyWidth="10%p"/>
+        <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+                android:keyWidth="40%p"
+                android:iconPreview="@drawable/sym_keyboard_feedback_space"
+                android:isRepeatable="true"/>
+        <Key android:codes="46" android:keyLabel="."
+                android:popupKeyboard="@xml/popup_punctuation"
+                android:keyWidth="10%p"/>
+        <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return" android:keyWidth="20%p" android:keyEdgeFlags="right"
+                android:iconPreview="@drawable/sym_keyboard_feedback_return"
+                />
+    </Row>
+</Keyboard>
diff --git a/java/res/xml/kbd_symbols_shift.xml b/java/res/xml/kbd_symbols_shift.xml
index 09b5c3f..ca431fc 100755
--- a/java/res/xml/kbd_symbols_shift.xml
+++ b/java/res/xml/kbd_symbols_shift.xml
@@ -34,7 +34,10 @@
                 android:popupCharacters="♪♥♠♦♣"
         />
         <Key android:keyLabel="√"/>
-        <Key android:keyLabel="π"/>
+        <Key android:keyLabel="π"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="Π"
+        />
         <Key android:keyLabel="÷"/>
         <Key android:keyLabel="×"/>
         <Key android:keyLabel="{"/>
diff --git a/java/res/xml/kbd_symbols_shift_black.xml b/java/res/xml/kbd_symbols_shift_black.xml
new file mode 100755
index 0000000..a8acb9d
--- /dev/null
+++ b/java/res/xml/kbd_symbols_shift_black.xml
@@ -0,0 +1,107 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2010, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<Keyboard xmlns:android="http://schemas.android.com/apk/res/android"
+    android:keyWidth="10%p"
+    android:horizontalGap="0px"
+    android:verticalGap="0px"
+    android:keyHeight="@dimen/key_height"
+    >
+
+    <Row>
+        <Key android:keyLabel="~" android:keyEdgeFlags="left"/>
+        <Key android:keyLabel="`"/>
+        <Key android:keyLabel="|"/>
+        <Key android:keyLabel="•"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="♪♥♠♦♣"
+        />
+        <Key android:keyLabel="√"/>
+        <Key android:keyLabel="π"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="Π"
+        />
+        <Key android:keyLabel="÷"/>
+        <Key android:keyLabel="×"/>
+        <Key android:keyLabel="{"/>
+        <Key android:keyLabel="}" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row>
+        <Key android:codes="9" android:keyLabel="\u21E5" android:keyEdgeFlags="left"/>
+        <Key android:keyLabel="£"/>
+        <Key android:keyLabel="¢"/>
+        <Key android:keyLabel="€"/>
+        <Key android:keyLabel="°"/>
+        <Key android:keyLabel="^"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="↑↓←→"
+        />
+        <Key android:keyLabel="_"/>
+        <Key android:keyLabel="="
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="≠≈∞"
+        />
+        <Key android:keyLabel="["/>
+        <Key android:keyLabel="]" android:keyEdgeFlags="right"/>
+    </Row>
+
+    <Row>
+        <Key android:codes="-1" android:keyLabel="@string/label_alt_key"
+                android:keyWidth="15%p" android:isModifier="true"
+                android:isSticky="true" android:keyEdgeFlags="left"/>
+        <Key android:keyLabel="™"/>
+        <Key android:keyLabel="®"/>
+        <Key android:keyLabel="©"/>
+        <Key android:keyLabel="¶"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="§"
+        />
+        <Key android:keyLabel="\\"/>
+        <Key android:keyLabel="&lt;"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="≤«‹"
+        />
+        <Key android:keyLabel="&gt;"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="≥»›"
+        />
+        <Key android:codes="-5" android:keyIcon="@drawable/sym_bkeyboard_delete" android:keyWidth="15%p" android:keyEdgeFlags="right"
+                android:iconPreview="@drawable/sym_keyboard_feedback_delete"
+                android:isRepeatable="true"/>
+    </Row>
+
+    <Row android:rowEdgeFlags="bottom">
+        <Key android:codes="-2" android:keyLabel="@string/label_alpha_key" android:keyWidth="20%p"
+                android:popupKeyboard="@xml/kbd_popup_template"
+                android:popupCharacters="_"
+                android:keyEdgeFlags="left"/>
+        <Key android:keyLabel="„" android:keyWidth="10%p" />
+        <Key android:codes="32" android:keyIcon="@drawable/sym_bkeyboard_space"
+                android:keyWidth="40%p"
+                android:iconPreview="@drawable/sym_keyboard_feedback_space"
+                android:isRepeatable="true"/>
+        <Key android:keyLabel="…" android:keyWidth="10%p" />
+        <Key android:codes="10" android:keyIcon="@drawable/sym_bkeyboard_return"
+                android:keyWidth="20%p" android:keyEdgeFlags="right"
+                android:iconPreview="@drawable/sym_keyboard_feedback_return"
+                />
+    </Row>
+</Keyboard>
diff --git a/java/res/xml/prefs.xml b/java/res/xml/prefs.xml
index c93fe0a..12a1ed3 100644
--- a/java/res/xml/prefs.xml
+++ b/java/res/xml/prefs.xml
@@ -37,6 +37,14 @@
             android:defaultValue="true"
             />
 
+    <CheckBoxPreference
+            android:key="enable_logging"
+            android:title="@string/prefs_enable_log"
+            android:summary="@string/prefs_description_log"
+            android:persistent="true"
+            android:defaultValue="true"
+            />
+
     <ListPreference
             android:key="voice_mode"
             android:title="@string/voice_input"
@@ -46,6 +54,15 @@
             android:defaultValue="@string/voice_mode_main"
             />
 
+    <ListPreference
+            android:key="keyboard_layout"
+            android:title="@string/keyboard_layout"
+            android:persistent="true"
+            android:entryValues="@array/keyboard_layout_modes_values"
+            android:entries="@array/keyboard_layout_modes"
+            android:defaultValue="3"
+            />
+
     <PreferenceScreen
             android:title="@string/language_selection_title"
             android:summary="@string/language_selection_summary">
@@ -91,4 +108,11 @@
             android:dependency="auto_complete"
             />
     </PreferenceCategory>            
+
+<CheckBoxPreference
+            android:key="debug_mode"
+            android:title="@string/prefs_debug_mode"
+            android:persistent="true"
+            android:defaultValue="false"
+            />
 </PreferenceScreen>
diff --git a/java/src/com/android/inputmethod/latin/AutoDictionary.java b/java/src/com/android/inputmethod/latin/AutoDictionary.java
index 93f1985..94331d3 100644
--- a/java/src/com/android/inputmethod/latin/AutoDictionary.java
+++ b/java/src/com/android/inputmethod/latin/AutoDictionary.java
@@ -85,8 +85,8 @@
 
     private static DatabaseHelper mOpenHelper = null;
 
-    public AutoDictionary(Context context, LatinIME ime, String locale) {
-        super(context);
+    public AutoDictionary(Context context, LatinIME ime, String locale, int dicTypeId) {
+        super(context, dicTypeId);
         mIme = ime;
         mLocale = locale;
         if (mOpenHelper == null) {
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index 8d23630..a1e9adf 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -24,7 +24,6 @@
 import java.util.Arrays;
 
 import android.content.Context;
-import android.content.res.AssetManager;
 import android.util.Log;
 
 /**
@@ -40,6 +39,7 @@
     private static final int TYPED_LETTER_MULTIPLIER = 2;
     private static final boolean ENABLE_MISSED_CHARACTERS = true;
 
+    private int mDicTypeId;
     private int mNativeDict;
     private int mDictLength;
     private int[] mInputCodes = new int[MAX_WORD_LENGTH * MAX_ALTERNATIVES];
@@ -53,9 +53,9 @@
 
     static {
         try {
-            System.loadLibrary("jni_latinime");
+            System.loadLibrary("jni_latinime2");
         } catch (UnsatisfiedLinkError ule) {
-            Log.e("BinaryDictionary", "Could not load native library jni_latinime");
+            Log.e("BinaryDictionary", "Could not load native library jni_latinime2");
         }
     }
 
@@ -64,10 +64,11 @@
      * @param context application context for reading resources
      * @param resId the resource containing the raw binary dictionary
      */
-    public BinaryDictionary(Context context, int resId) {
+    public BinaryDictionary(Context context, int resId, int dicTypeId) {
         if (resId != 0) {
             loadDictionary(context, resId);
         }
+        mDicTypeId = dicTypeId;
     }
 
     /**
@@ -75,7 +76,7 @@
      * @param context application context for reading resources
      * @param byteBuffer a ByteBuffer containing the binary dictionary
      */
-    public BinaryDictionary(Context context, ByteBuffer byteBuffer) {
+    public BinaryDictionary(Context context, ByteBuffer byteBuffer, int dicTypeId) {
         if (byteBuffer != null) {
             if (byteBuffer.isDirect()) {
                 mNativeDictDirectBuffer = byteBuffer;
@@ -88,9 +89,11 @@
             mNativeDict = openNative(mNativeDictDirectBuffer,
                     TYPED_LETTER_MULTIPLIER, FULL_WORD_FREQ_MULTIPLIER);
         }
+        mDicTypeId = dicTypeId;
     }
 
-    private native int openNative(ByteBuffer bb, int typedLetterMultiplier, int fullWordMultiplier);
+    private native int openNative(ByteBuffer bb, int typedLetterMultiplier,
+            int fullWordMultiplier);
     private native void closeNative(int dict);
     private native boolean isValidWordNative(int nativeData, char[] word, int wordLength);
     private native int getSuggestionsNative(int dict, int[] inputCodes, int codesSize, 
@@ -144,7 +147,7 @@
             }
             if (len > 0) {
                 callback.addWord(mOutputChars_bigrams, start, len, mFrequencies_bigrams[j],
-                        DataType.BIGRAM);
+                        mDicTypeId, DataType.BIGRAM);
             }
         }
     }
@@ -194,7 +197,8 @@
                 len++;
             }
             if (len > 0) {
-                callback.addWord(mOutputChars, start, len, mFrequencies[j], DataType.UNIGRAM);
+                callback.addWord(mOutputChars, start, len, mFrequencies[j], mDicTypeId,
+                        DataType.UNIGRAM);
             }
         }
     }
diff --git a/java/src/com/android/inputmethod/latin/CandidateView.java b/java/src/com/android/inputmethod/latin/CandidateView.java
index 93966a8..faf72c9 100755
--- a/java/src/com/android/inputmethod/latin/CandidateView.java
+++ b/java/src/com/android/inputmethod/latin/CandidateView.java
@@ -83,7 +83,6 @@
     private int mDescent;
     private boolean mScrolled;
     private boolean mShowingAddToDictionary;
-    private CharSequence mWordToAddToDictionary;
     private CharSequence mAddToDictionaryHint;
 
     private int mTargetScrollX;
@@ -178,7 +177,7 @@
                 if (scrollX < 0) {
                     scrollX = 0;
                 }
-                if (distanceX > 0 && scrollX + width > mTotalWidth) {                    
+                if (distanceX > 0 && scrollX + width > mTotalWidth) {
                     scrollX -= (int) distanceX;
                 }
                 mTargetScrollX = scrollX;
@@ -231,7 +230,6 @@
         }
         int x = 0;
         final int count = Math.min(mSuggestions.size(), MAX_SUGGESTIONS);
-        final int width = getWidth();
         final Rect bgPadding = mBgPadding;
         final Paint paint = mPaint;
         final int touchX = mTouchX;
@@ -336,7 +334,6 @@
     }
 
     public void showAddToDictionaryHint(CharSequence word) {
-        mWordToAddToDictionary = word;
         ArrayList<CharSequence> suggestions = new ArrayList<CharSequence>();
         suggestions.add(word);
         suggestions.add(mAddToDictionaryHint);
@@ -387,8 +384,14 @@
             mScrolled = true;
         }
     }
-    
+
+    /* package */ List<CharSequence> getSuggestions() {
+        return mSuggestions;
+    }
+
     public void clear() {
+        // Don't call mSuggestions.clear() because it's being used for logging
+        // in LatinIME.pickSuggestionManually().
         mSuggestions = EMPTY_LIST;
         mTouchX = OUT_OF_BOUNDS;
         mSelectedString = null;
@@ -423,7 +426,11 @@
             if (y <= 0) {
                 // Fling up!?
                 if (mSelectedString != null) {
+                    // If there are completions from the application, we don't change the state to
+                    // STATE_PICKED_SUGGESTION
                     if (!mShowingCompletions) {
+                        // This "acceptedSuggestion" will not be counted as a word because
+                        // it will be counted in pickSuggestion instead.
                         TextEntryState.acceptedSuggestion(mSuggestions.get(0),
                                 mSelectedString);
                     }
diff --git a/java/src/com/android/inputmethod/latin/ContactsDictionary.java b/java/src/com/android/inputmethod/latin/ContactsDictionary.java
index 15edb70..f5ff865 100644
--- a/java/src/com/android/inputmethod/latin/ContactsDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ContactsDictionary.java
@@ -20,7 +20,6 @@
 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;
 
@@ -37,21 +36,23 @@
 
     private long mLastLoadedContacts;
 
-    public ContactsDictionary(Context context) {
-        super(context);
+    public ContactsDictionary(Context context, int dicTypeId) {
+        super(context, dicTypeId);
         // Perform a managed query. The Activity will handle closing and requerying the cursor
         // when needed.
         ContentResolver cres = context.getContentResolver();
 
-        cres.registerContentObserver(Contacts.CONTENT_URI, true, mObserver = new ContentObserver(null) {
-            @Override
-            public void onChange(boolean self) {
-                setRequiresReload(true);
-            }
-        });
+        cres.registerContentObserver(
+                Contacts.CONTENT_URI, true,mObserver = new ContentObserver(null) {
+                    @Override
+                    public void onChange(boolean self) {
+                        setRequiresReload(true);
+                    }
+                });
         loadDictionary();
     }
 
+    @Override
     public synchronized void close() {
         if (mObserver != null) {
             getContext().getContentResolver().unregisterContentObserver(mObserver);
diff --git a/java/src/com/android/inputmethod/latin/Dictionary.java b/java/src/com/android/inputmethod/latin/Dictionary.java
index 54317c8..a02edee 100644
--- a/java/src/com/android/inputmethod/latin/Dictionary.java
+++ b/java/src/com/android/inputmethod/latin/Dictionary.java
@@ -51,10 +51,11 @@
          * @param wordLength length of valid characters in the character array
          * @param frequency the frequency of occurence. This is normalized between 1 and 255, but
          * can exceed those limits
+         * @param dicTypeId of the dictionary where word was from
          * @param dataType tells type of this data
          * @return true if the word was added, false if no more words are required
          */
-        boolean addWord(char[] word, int wordOffset, int wordLength, int frequency,
+        boolean addWord(char[] word, int wordOffset, int wordLength, int frequency, int dicTypeId,
                 DataType dataType);
     }
 
diff --git a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
index 6f4d925..d8a9547 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
@@ -16,11 +16,8 @@
 
 package com.android.inputmethod.latin;
 
-import com.android.inputmethod.latin.Dictionary.WordCallback;
-
 import android.content.Context;
 import android.os.AsyncTask;
-import android.os.SystemClock;
 
 /**
  * Base class for an in-memory dictionary that can grow dynamically and can
@@ -29,6 +26,7 @@
 public class ExpandableDictionary extends Dictionary {
     private Context mContext;
     private char[] mWordBuilder = new char[MAX_WORD_LENGTH];
+    private int mDicTypeId;
     private int mMaxDepth;
     private int mInputLength;
     private int[] mNextLettersFrequencies;
@@ -75,10 +73,11 @@
 
     private int[][] mCodes;
 
-    ExpandableDictionary(Context context) {
+    ExpandableDictionary(Context context, int dicTypeId) {
         mContext = context;
         clearDictionary();
         mCodes = new int[MAX_WORD_LENGTH][];
+        mDicTypeId = dicTypeId;
     }
 
     public void loadDictionary() {
@@ -267,7 +266,8 @@
             if (completion) {
                 word[depth] = c;
                 if (terminal) {
-                    if (!callback.addWord(word, 0, depth + 1, freq * snr, DataType.UNIGRAM)) {
+                    if (!callback.addWord(word, 0, depth + 1, freq * snr, mDicTypeId,
+                                DataType.UNIGRAM)) {
                         return;
                     }
                     // Add to frequency of next letters for predictive correction
@@ -305,7 +305,7 @@
                                         || !same(word, depth + 1, codes.getTypedWord())) {
                                     int finalFreq = freq * snr * addedAttenuation;
                                     if (skipPos < 0) finalFreq *= FULL_WORD_FREQ_MULTIPLIER;
-                                    callback.addWord(word, 0, depth + 1, finalFreq,
+                                    callback.addWord(word, 0, depth + 1, finalFreq, mDicTypeId,
                                             DataType.UNIGRAM);
                                 }
                             }
diff --git a/java/src/com/android/inputmethod/latin/InputLanguageSelection.java b/java/src/com/android/inputmethod/latin/InputLanguageSelection.java
index 5e835e5..718fda1 100644
--- a/java/src/com/android/inputmethod/latin/InputLanguageSelection.java
+++ b/java/src/com/android/inputmethod/latin/InputLanguageSelection.java
@@ -99,7 +99,7 @@
         boolean haveDictionary = false;
         conf.locale = locale;
         res.updateConfiguration(conf, res.getDisplayMetrics());
-        BinaryDictionary bd = new BinaryDictionary(this, R.raw.main);
+        BinaryDictionary bd = new BinaryDictionary(this, R.raw.main, Suggest.DIC_MAIN);
         // Is the dictionary larger than a placeholder? Arbitrarily chose a lower limit of
         // 4000-5000 words, whereas the LARGE_DICTIONARY is about 20000+ words.
         if (bd.getSize() > Suggest.LARGE_DICTIONARY_THRESHOLD / 4) {
diff --git a/java/src/com/android/inputmethod/latin/KeyboardSwitcher.java b/java/src/com/android/inputmethod/latin/KeyboardSwitcher.java
index 1a19644..bbde232 100644
--- a/java/src/com/android/inputmethod/latin/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/latin/KeyboardSwitcher.java
@@ -21,12 +21,15 @@
 import java.util.Map;
 
 import android.content.Context;
+import android.content.SharedPreferences;
 import android.content.res.Configuration;
 import android.content.res.Resources;
-import android.inputmethodservice.InputMethodService;
+import android.preference.PreferenceManager;
+import android.view.InflateException;
 
-public class KeyboardSwitcher {
+public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceChangeListener {
 
+    public static final int MODE_NONE = 0;
     public static final int MODE_TEXT = 1;
     public static final int MODE_SYMBOLS = 2;
     public static final int MODE_PHONE = 3;
@@ -45,6 +48,12 @@
     public static final int KEYBOARDMODE_IM = R.id.mode_im;
     public static final int KEYBOARDMODE_WEB = R.id.mode_webentry;
 
+    public static final String DEFAULT_LAYOUT_ID = "3";
+    public static final String PREF_KEYBOARD_LAYOUT = "keyboard_layout";
+    private static final int[] LAYOUTS = new int [] {
+        R.layout.input_basic, R.layout.input_basic_highcontrast, R.layout.input_stone_normal,
+        R.layout.input_stone_bold};
+
     private static final int SYMBOLS_MODE_STATE_NONE = 0;
     private static final int SYMBOLS_MODE_STATE_BEGIN = 1;
     private static final int SYMBOLS_MODE_STATE_SYMBOL = 2;
@@ -57,9 +66,8 @@
         KEYBOARDMODE_IM,
         KEYBOARDMODE_WEB};
 
-    //LatinIME mContext;
     Context mContext;
-    InputMethodService mInputMethodService;
+    LatinIME mInputMethodService;
     
     private KeyboardId mSymbolsId;
     private KeyboardId mSymbolsShiftedId;
@@ -67,7 +75,7 @@
     private KeyboardId mCurrentId;
     private Map<KeyboardId, LatinKeyboard> mKeyboards;
 
-    private int mMode; /** One of the MODE_XXX values */
+    private int mMode = MODE_NONE; /** One of the MODE_XXX values */
     private int mImeOptions;
     private int mTextMode = MODE_TEXT_QWERTY;
     private boolean mIsSymbols;
@@ -79,13 +87,19 @@
     private int mLastDisplayWidth;
     private LanguageSwitcher mLanguageSwitcher;
     private Locale mInputLocale;
-    private boolean mEnableMultipleLanguages;
 
-    KeyboardSwitcher(Context context, InputMethodService ims) {
+    private int mLayoutId;
+
+    KeyboardSwitcher(Context context, LatinIME ims) {
         mContext = context;
+
+        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ims);
+        mLayoutId = Integer.valueOf(prefs.getString(PREF_KEYBOARD_LAYOUT, DEFAULT_LAYOUT_ID));
+        prefs.registerOnSharedPreferenceChangeListener(this);
+
         mKeyboards = new HashMap<KeyboardId, LatinKeyboard>();
-        mSymbolsId = new KeyboardId(R.xml.kbd_symbols, false);
-        mSymbolsShiftedId = new KeyboardId(R.xml.kbd_symbols_shift, false);
+        mSymbolsId = makeSymbolsId(false);
+        mSymbolsShiftedId = makeSymbolsShiftedId(false);
         mInputMethodService = ims;
     }
 
@@ -98,13 +112,22 @@
     void setLanguageSwitcher(LanguageSwitcher languageSwitcher) {
         mLanguageSwitcher = languageSwitcher;
         mInputLocale = mLanguageSwitcher.getInputLocale();
-        mEnableMultipleLanguages = mLanguageSwitcher.getLocaleCount() > 1;
     }
 
     void setInputView(LatinKeyboardView inputView) {
         mInputView = inputView;
     }
-    
+
+    private KeyboardId makeSymbolsId(boolean hasVoice) {
+        return new KeyboardId(
+                isBlackSym() ? R.xml.kbd_symbols_black : R.xml.kbd_symbols, hasVoice);
+    }
+
+    private KeyboardId makeSymbolsShiftedId(boolean hasVoice) {
+        return new KeyboardId(
+                isBlackSym() ? R.xml.kbd_symbols_shift_black : R.xml.kbd_symbols_shift, hasVoice);
+    }
+
     void makeKeyboards(boolean forceCreate) {
         if (forceCreate) mKeyboards.clear();
         // Configuration change is coming after the keyboard gets recreated. So don't rely on that.
@@ -114,9 +137,8 @@
         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);
+        mSymbolsId = makeSymbolsId(mHasVoice && !mVoiceOnPrimary);
+        mSymbolsShiftedId = makeSymbolsShiftedId(mHasVoice && !mVoiceOnPrimary);
     }
 
     /**
@@ -140,6 +162,7 @@
             this(xml, 0, false, hasVoice);
         }
 
+        @Override
         public boolean equals(Object other) {
             return other instanceof KeyboardId && equals((KeyboardId) other);
         }
@@ -150,6 +173,7 @@
               && other.mEnableShiftLock == this.mEnableShiftLock;
         }
 
+        @Override
         public int hashCode() {
             return (mXml + 1) * (mKeyboardMode + 1) * (mEnableShiftLock ? 2 : 1)
                     * (mHasVoice ? 4 : 8);
@@ -173,8 +197,14 @@
     void setKeyboardMode(int mode, int imeOptions, boolean enableVoice) {
         mSymbolsModeState = SYMBOLS_MODE_STATE_NONE;
         mPreferSymbols = mode == MODE_SYMBOLS;
-        setKeyboardMode(mode == MODE_SYMBOLS ? MODE_TEXT : mode, imeOptions, enableVoice,
-                mPreferSymbols);
+        if (mode == MODE_SYMBOLS) {
+            mode = MODE_TEXT;
+        }
+        try {
+            setKeyboardMode(mode, imeOptions, enableVoice, mPreferSymbols);
+        } catch (RuntimeException e) {
+            LatinImeLogger.logOnException(mode + "," + imeOptions + "," + mPreferSymbols, e);
+        }
     }
 
     void setKeyboardMode(int mode, int imeOptions, boolean enableVoice, boolean isSymbols) {
@@ -188,8 +218,8 @@
 
         mInputView.setPreviewEnabled(true);
         KeyboardId id = getKeyboardId(mode, imeOptions, isSymbols);
-
-        LatinKeyboard keyboard = getKeyboard(id);
+        LatinKeyboard keyboard = null;
+        keyboard = getKeyboard(id);
 
         if (mode == MODE_PHONE) {
             mInputView.setPhoneKeyboard(keyboard);
@@ -201,6 +231,7 @@
         keyboard.setShifted(false);
         keyboard.setShiftLocked(keyboard.isShiftLocked());
         keyboard.setImeOptions(mContext.getResources(), mMode, imeOptions);
+        keyboard.setBlackFlag(isBlackSym());
     }
 
     private LatinKeyboard getKeyboard(KeyboardId id) {
@@ -212,8 +243,10 @@
             orig.updateConfiguration(conf, null);
             LatinKeyboard keyboard = new LatinKeyboard(
                 mContext, id.mXml, id.mKeyboardMode);
-            keyboard.setVoiceMode(hasVoiceButton(id.mXml == R.xml.kbd_symbols), mHasVoice);
+            keyboard.setVoiceMode(hasVoiceButton(id.mXml == R.xml.kbd_symbols
+                    || id.mXml == R.xml.kbd_symbols_black), mHasVoice);
             keyboard.setLanguageSwitcher(mLanguageSwitcher);
+            keyboard.setBlackFlag(isBlackSym());
             if (id.mKeyboardMode == KEYBOARDMODE_NORMAL
                     || id.mKeyboardMode == KEYBOARDMODE_URL
                     || id.mKeyboardMode == KEYBOARDMODE_IM
@@ -236,31 +269,35 @@
 
     private KeyboardId getKeyboardId(int mode, int imeOptions, boolean isSymbols) {
         boolean hasVoice = hasVoiceButton(isSymbols);
+        // TODO: generalize for any KeyboardId
+        int keyboardRowsResId = isBlackSym() ? R.xml.kbd_qwerty_black : R.xml.kbd_qwerty;
         if (isSymbols) {
             return (mode == MODE_PHONE)
-                ? new KeyboardId(R.xml.kbd_phone_symbols, hasVoice)
-                : new KeyboardId(R.xml.kbd_symbols, hasVoice);
+                ? new KeyboardId(R.xml.kbd_phone_symbols, hasVoice) : makeSymbolsId(hasVoice);
         }
         switch (mode) {
+            case MODE_NONE:
+                LatinImeLogger.logOnWarning(
+                        "getKeyboardId:" + mode + "," + imeOptions + "," + isSymbols);
+                /* fall through */
             case MODE_TEXT:
-                if (mTextMode == MODE_TEXT_QWERTY) {
-                    return new KeyboardId(R.xml.kbd_qwerty, KEYBOARDMODE_NORMAL, true, hasVoice);
-                } else if (mTextMode == MODE_TEXT_ALPHA) {
+                if (mTextMode == MODE_TEXT_ALPHA) {
                     return new KeyboardId(R.xml.kbd_alpha, KEYBOARDMODE_NORMAL, true, hasVoice);
                 }
-                break;
+                // Normally mTextMode should be MODE_TEXT_QWERTY.
+                return new KeyboardId(keyboardRowsResId, KEYBOARDMODE_NORMAL, true, hasVoice);
             case MODE_SYMBOLS:
-                return new KeyboardId(R.xml.kbd_symbols, hasVoice);
+                return makeSymbolsId(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);
+                return new KeyboardId(keyboardRowsResId, KEYBOARDMODE_URL, true, hasVoice);
             case MODE_EMAIL:
-                return new KeyboardId(R.xml.kbd_qwerty, KEYBOARDMODE_EMAIL, true, hasVoice);
+                return new KeyboardId(keyboardRowsResId, KEYBOARDMODE_EMAIL, true, hasVoice);
             case MODE_IM:
-                return new KeyboardId(R.xml.kbd_qwerty, KEYBOARDMODE_IM, true, hasVoice);
+                return new KeyboardId(keyboardRowsResId, KEYBOARDMODE_IM, true, hasVoice);
             case MODE_WEB:
-                return new KeyboardId(R.xml.kbd_qwerty, KEYBOARDMODE_WEB, true, hasVoice);
+                return new KeyboardId(keyboardRowsResId, KEYBOARDMODE_WEB, true, hasVoice);
         }
         return null;
     }
@@ -273,19 +310,6 @@
         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;
     }
@@ -348,4 +372,63 @@
         }
         return false;
     }
+
+    public LatinKeyboardView getInputView() {
+        return mInputView;
+    }
+
+    public void recreateInputView() {
+        changeLatinKeyboardView(mLayoutId, true);
+    }
+
+    private void changeLatinKeyboardView(int newLayout, boolean forceReset) {
+        if (mLayoutId != newLayout || mInputView == null || forceReset) {
+            if (mInputView != null) {
+                mInputView.closing();
+            }
+            if (LAYOUTS.length <= newLayout) {
+                newLayout = Integer.valueOf(DEFAULT_LAYOUT_ID);
+            }
+
+            LatinIMEUtil.GCUtils.getInstance().reset();
+            boolean tryGC = true;
+            for (int i = 0; i < LatinIMEUtil.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) {
+                try {
+                    mInputView = (LatinKeyboardView) mInputMethodService.getLayoutInflater(
+                            ).inflate(LAYOUTS[newLayout], null);
+                    tryGC = false;
+                } catch (OutOfMemoryError e) {
+                    tryGC = LatinIMEUtil.GCUtils.getInstance().tryGCOrWait(
+                            mLayoutId + "," + newLayout, e);
+                } catch (InflateException e) {
+                    tryGC = LatinIMEUtil.GCUtils.getInstance().tryGCOrWait(
+                            mLayoutId + "," + newLayout, e);
+                }
+            }
+            mInputView.setExtentionLayoutResId(LAYOUTS[newLayout]);
+            mInputView.setOnKeyboardActionListener(mInputMethodService);
+            mLayoutId = newLayout;
+        }
+        mInputMethodService.mHandler.post(new Runnable() {
+            public void run() {
+                if (mInputView != null) {
+                    mInputMethodService.setInputView(mInputView);
+                }
+                mInputMethodService.updateInputViewShown();
+            }});
+    }
+
+    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+        if (PREF_KEYBOARD_LAYOUT.equals(key)) {
+            changeLatinKeyboardView(
+                    Integer.valueOf(sharedPreferences.getString(key, DEFAULT_LAYOUT_ID)), false);
+        }
+    }
+
+    public boolean isBlackSym () {
+        if (mInputView != null && mInputView.getSymbolColorSheme() == 1) {
+            return true;
+        }
+        return false;
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 783805e..8d54572 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -31,7 +31,6 @@
 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;
@@ -73,7 +72,7 @@
  * Input method implementation for Qwerty'ish keyboard.
  */
 public class LatinIME extends InputMethodService
-        implements KeyboardView.OnKeyboardActionListener,
+        implements LatinKeyboardBaseView.OnKeyboardActionListener,
         VoiceInput.UiListener,
         SharedPreferences.OnSharedPreferenceChangeListener {
     private static final String TAG = "LatinIME";
@@ -148,7 +147,7 @@
     private static final int POS_SETTINGS = 0;
     private static final int POS_METHOD = 1;
 
-    private LatinKeyboardView mInputView;
+    //private LatinKeyboardView mInputView;
     private CandidateViewContainer mCandidateViewContainer;
     private CandidateView mCandidateView;
     private Suggest mSuggest;
@@ -228,8 +227,9 @@
     private final float FX_VOLUME = -1.0f;
     private boolean mSilentMode;
 
-    private String mWordSeparators;
+    /* package */ String mWordSeparators;
     private String mSentenceSeparators;
+    private String mSuggestPuncs;
     private VoiceInput mVoiceInput;
     private VoiceResults mVoiceResults = new VoiceResults();
     private long mSwipeTriggerTimeMillis;
@@ -310,8 +310,9 @@
                     break;
                 case MSG_START_TUTORIAL:
                     if (mTutorial == null) {
-                        if (mInputView.isShown()) {
-                            mTutorial = new Tutorial(LatinIME.this, mInputView);
+                        if (mKeyboardSwitcher.getInputView().isShown()) {
+                            mTutorial = new Tutorial(
+                                    LatinIME.this, mKeyboardSwitcher.getInputView());
                             mTutorial.start();
                         } else {
                             // Try again soon if the view is not yet showing
@@ -334,6 +335,7 @@
     };
 
     @Override public void onCreate() {
+        LatinImeLogger.init(this);
         super.onCreate();
         //setStatusIcon(R.drawable.ime_qwerty);
         mResources = getResources();
@@ -349,7 +351,18 @@
         if (inputLanguage == null) {
             inputLanguage = conf.locale.toString();
         }
-        initSuggest(inputLanguage);
+
+        LatinIMEUtil.GCUtils.getInstance().reset();
+        boolean tryGC = true;
+        for (int i = 0; i < LatinIMEUtil.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) {
+            try {
+                initSuggest(inputLanguage);
+                tryGC = false;
+            } catch (OutOfMemoryError e) {
+                tryGC = LatinIMEUtil.GCUtils.getInstance().tryGCOrWait(inputLanguage, e);
+            }
+        }
+
         mOrientation = conf.orientation;
         initSuggestPuncList();
 
@@ -390,12 +403,12 @@
         if (mUserDictionary != null) mUserDictionary.close();
         mUserDictionary = new UserDictionary(this, mInputLocale);
         if (mContactsDictionary == null) {
-            mContactsDictionary = new ContactsDictionary(this);
+            mContactsDictionary = new ContactsDictionary(this, Suggest.DIC_CONTACTS);
         }
         if (mAutoDictionary != null) {
             mAutoDictionary.close();
         }
-        mAutoDictionary = new AutoDictionary(this, this, mInputLocale);
+        mAutoDictionary = new AutoDictionary(this, this, mInputLocale, Suggest.DIC_AUTO);
         mSuggest.setUserDictionary(mUserDictionary);
         mSuggest.setContactsDictionary(mContactsDictionary);
         mSuggest.setAutoDictionary(mAutoDictionary);
@@ -409,12 +422,18 @@
 
     @Override
     public void onDestroy() {
-        mUserDictionary.close();
-        mContactsDictionary.close();
+        if (mUserDictionary != null) {
+            mUserDictionary.close();
+        }
+        if (mContactsDictionary != null) {
+            mContactsDictionary.close();
+        }
         unregisterReceiver(mReceiver);
-        if (VOICE_INSTALLED) {
+        if (VOICE_INSTALLED && mVoiceInput != null) {
             mVoiceInput.destroy();
         }
+        LatinImeLogger.commit();
+        LatinImeLogger.onDestroy();
         super.onDestroy();
     }
 
@@ -454,15 +473,12 @@
 
     @Override
     public View onCreateInputView() {
-        mInputView = (LatinKeyboardView) getLayoutInflater().inflate(
-                R.layout.input, null);
-        mKeyboardSwitcher.setInputView(mInputView);
+        mKeyboardSwitcher.recreateInputView();
         mKeyboardSwitcher.makeKeyboards(true);
-        mInputView.setOnKeyboardActionListener(this);
         mKeyboardSwitcher.setKeyboardMode(
                 KeyboardSwitcher.MODE_TEXT, 0,
                 shouldShowVoiceButton(makeFieldContext(), getCurrentInputEditorInfo()));
-        return mInputView;
+        return mKeyboardSwitcher.getInputView();
     }
 
     @Override
@@ -479,8 +495,9 @@
 
     @Override
     public void onStartInputView(EditorInfo attribute, boolean restarting) {
+        LatinKeyboardView inputView = mKeyboardSwitcher.getInputView();
         // In landscape mode, this method gets called without the input view being created.
-        if (mInputView == null) {
+        if (inputView == null) {
             return;
         }
 
@@ -588,7 +605,7 @@
                         attribute.imeOptions, enableVoiceButton);
                 updateShiftKeyState(attribute);
         }
-        mInputView.closing();
+        inputView.closing();
         mComposing.setLength(0);
         mPredicting = false;
         mDeleteCount = 0;
@@ -604,7 +621,7 @@
 
         updateCorrectionMode();
 
-        mInputView.setProximityCorrectionEnabled(true);
+        inputView.setProximityCorrectionEnabled(true);
         mPredictionOn = mPredictionOn && (mCorrectionMode > 0 || mShowSuggestions);
         checkTutorial(attribute.privateImeOptions);
         if (TRACE) Debug.startMethodTracing("/data/trace/latinime");
@@ -614,6 +631,8 @@
     public void onFinishInput() {
         super.onFinishInput();
 
+        LatinImeLogger.commit();
+
         if (VOICE_INSTALLED && !mConfigurationChanging) {
             if (mAfterVoiceInput) {
                 mVoiceInput.flushAllTextModificationCounters();
@@ -622,8 +641,8 @@
             mVoiceInput.flushLogs();
             mVoiceInput.cancel();
         }
-        if (mInputView != null) {
-            mInputView.closing();
+        if (mKeyboardSwitcher.getInputView() != null) {
+            mKeyboardSwitcher.getInputView().closing();
         }
         if (mAutoDictionary != null) mAutoDictionary.flushPendingWrites();
     }
@@ -695,7 +714,9 @@
         mLastSelectionEnd = newSelEnd;
 
 
+        // TODO: Uncomment this block when we enable re-editing feature
         // If a word is selected
+        /*
         if (isPredictionOn() && mJustRevertedSeparator == null
                 && (candidatesStart == candidatesEnd || newSelStart != oldSelStart)
                 && (newSelStart < newSelEnd - 1 || (!mPredicting))
@@ -706,10 +727,13 @@
                 abortCorrection(false);
             }
         }
+        */
     }
 
     @Override
     public void hideWindow() {
+        LatinImeLogger.commit();
+
         if (TRACE) Debug.stopMethodTracing();
         if (mOptionsDialog != null && mOptionsDialog.isShowing()) {
             mOptionsDialog.dismiss();
@@ -752,6 +776,7 @@
                 CompletionInfo ci = completions[i];
                 if (ci != null) stringList.add(ci.getText());
             }
+            // When in fullscreen mode, show completions generated by the application
             setSuggestions(stringList, true, true, true);
             mBestWord = null;
             setCandidatesViewShown(isCandidateStripVisible() || mCompletionOn);
@@ -763,7 +788,8 @@
         // TODO: Remove this if we support candidates with hard keyboard
         if (onEvaluateInputViewShown()) {
             // Show the candidates view only if input view is showing
-            super.setCandidatesViewShown(shown && mInputView != null && mInputView.isShown());
+            super.setCandidatesViewShown(shown && mKeyboardSwitcher.getInputView() != null
+                    && mKeyboardSwitcher.getInputView().isShown());
         }
     }
 
@@ -792,8 +818,8 @@
     public boolean onKeyDown(int keyCode, KeyEvent event) {
         switch (keyCode) {
             case KeyEvent.KEYCODE_BACK:
-                if (event.getRepeatCount() == 0 && mInputView != null) {
-                    if (mInputView.handleBack()) {
+                if (event.getRepeatCount() == 0 && mKeyboardSwitcher.getInputView() != null) {
+                    if (mKeyboardSwitcher.getInputView().handleBack()) {
                         return true;
                     } else if (mTutorial != null) {
                         mTutorial.close();
@@ -825,8 +851,10 @@
                 if (mTutorial != null) {
                     return true;
                 }
+                LatinKeyboardView inputView = mKeyboardSwitcher.getInputView();
                 // Enable shift key and DPAD to do selections
-                if (mInputView != null && mInputView.isShown() && mInputView.isShifted()) {
+                if (inputView != null && inputView.isShown()
+                        && inputView.isShifted()) {
                     event = new KeyEvent(event.getDownTime(), event.getEventTime(),
                             event.getAction(), event.getKeyCode(), event.getRepeatCount(),
                             event.getDeviceId(), event.getScanCode(),
@@ -859,7 +887,8 @@
             mKeyboardSwitcher = new KeyboardSwitcher(this, this);
         }
         mKeyboardSwitcher.setLanguageSwitcher(mLanguageSwitcher);
-        if (mInputView != null) {
+        if (mKeyboardSwitcher.getInputView() != null
+                && mKeyboardSwitcher.getKeyboardMode() != KeyboardSwitcher.MODE_NONE) {
             mKeyboardSwitcher.setVoiceMode(mEnableVoice && mEnableVoiceButton, mVoiceOnPrimary);
         }
         mKeyboardSwitcher.makeKeyboards(true);
@@ -887,9 +916,10 @@
 
     public void updateShiftKeyState(EditorInfo attr) {
         InputConnection ic = getCurrentInputConnection();
-        if (attr != null && mInputView != null && mKeyboardSwitcher.isAlphabetMode()
-                && ic != null) {
-            mInputView.setShifted(mCapsLock || getCursorCapsMode(ic, attr) != 0);
+        if (attr != null && mKeyboardSwitcher.getInputView() != null
+                && mKeyboardSwitcher.isAlphabetMode() && ic != null) {
+            mKeyboardSwitcher.getInputView().setShifted(
+                    mCapsLock || getCursorCapsMode(ic, attr) != 0);
         }
     }
 
@@ -1002,6 +1032,7 @@
             case Keyboard.KEYCODE_DELETE:
                 handleBackspace();
                 mDeleteCount++;
+                LatinImeLogger.logOnDelete();
                 break;
             case Keyboard.KEYCODE_SHIFT:
                 handleShift();
@@ -1042,6 +1073,7 @@
                 if (primaryCode != KEYCODE_ENTER) {
                     mJustAddedAutoSpace = false;
                 }
+                LatinImeLogger.logOnInputChar((char)primaryCode);
                 if (isWordSeparator(primaryCode)) {
                     handleSeparator(primaryCode);
                 } else {
@@ -1141,7 +1173,8 @@
         if (mKeyboardSwitcher.isAlphabetMode()) {
             // Alphabet keyboard
             checkToggleCapsLock();
-            mInputView.setShifted(mCapsLock || !mInputView.isShifted());
+            mKeyboardSwitcher.getInputView().setShifted(mCapsLock
+                    || !mKeyboardSwitcher.getInputView().isShifted());
         } else {
             mKeyboardSwitcher.toggleShift();
         }
@@ -1173,16 +1206,21 @@
                 mWord.reset();
             }
         }
-        if (mInputView.isShifted()) {
-            // TODO: This doesn't work with ß, need to fix it in the next release.
+        if (mKeyboardSwitcher.getInputView().isShifted()) {
+            // TODO: This doesn't work with [beta], need to fix it in the next release.
             if (keyCodes == null || keyCodes[0] < Character.MIN_CODE_POINT
                     || keyCodes[0] > Character.MAX_CODE_POINT) {
                 return;
             }
-            primaryCode = new String(keyCodes, 0, 1).toUpperCase().charAt(0);
+            primaryCode = keyCodes[0];
+            if (mKeyboardSwitcher.isAlphabetMode()) {
+                primaryCode = Character.toUpperCase(primaryCode);
+            }
         }
         if (mPredicting) {
-            if (mInputView.isShifted() && mComposing.length() == 0) {
+            if (mKeyboardSwitcher.getInputView().isShifted()
+                    && mKeyboardSwitcher.isAlphabetMode()
+                    && mComposing.length() == 0) {
                 mWord.setCapitalized(true);
             }
             mComposing.append((char) primaryCode);
@@ -1231,8 +1269,7 @@
                     (mJustRevertedSeparator == null
                             || mJustRevertedSeparator.length() == 0
                             || mJustRevertedSeparator.charAt(0) != primaryCode)) {
-                pickDefaultSuggestion();
-                pickedDefault = true;
+                pickedDefault = pickDefaultSuggestion();
                 // Picked the suggestion by the space key.  We consider this
                 // as "added an auto space".
                 if (primaryCode == KEYCODE_SPACE) {
@@ -1262,8 +1299,8 @@
         } else if (isPredictionOn() && primaryCode == KEYCODE_SPACE) {
             doubleSpace();
         }
-        if (pickedDefault && mBestWord != null) {
-            TextEntryState.acceptedDefault(mWord.getTypedWord(), mBestWord);
+        if (pickedDefault) {
+            TextEntryState.backToAcceptedDefault(mWord.getTypedWord());
         }
         updateShiftKeyState(getCurrentInputEditorInfo());
         if (ic != null) {
@@ -1277,7 +1314,7 @@
             mVoiceInput.cancel();
         }
         requestHideSelf(0);
-        mInputView.closing();
+        mKeyboardSwitcher.getInputView().closing();
         TextEntryState.endSession();
     }
 
@@ -1294,7 +1331,7 @@
     }
 
     private void checkToggleCapsLock() {
-        if (mInputView.getKeyboard().isShifted()) {
+        if (mKeyboardSwitcher.getInputView().getKeyboard().isShifted()) {
             toggleCapsLock();
         }
     }
@@ -1302,7 +1339,8 @@
     private void toggleCapsLock() {
         mCapsLock = !mCapsLock;
         if (mKeyboardSwitcher.isAlphabetMode()) {
-            ((LatinKeyboard) mInputView.getKeyboard()).setShiftLocked(mCapsLock);
+            ((LatinKeyboard) mKeyboardSwitcher.getInputView().getKeyboard()).setShiftLocked(
+                    mCapsLock);
         }
     }
 
@@ -1335,8 +1373,8 @@
       mHandler.post(new Runnable() {
           public void run() {
               mRecognizing = false;
-              if (mInputView != null) {
-                setInputView(mInputView);
+              if (mKeyboardSwitcher.getInputView() != null) {
+                setInputView(mKeyboardSwitcher.getInputView());
               }
               updateInputViewShown();
           }});
@@ -1435,7 +1473,7 @@
 
         Window window = mVoiceWarningDialog.getWindow();
         WindowManager.LayoutParams lp = window.getAttributes();
-        lp.token = mInputView.getWindowToken();
+        lp.token = mKeyboardSwitcher.getInputView().getWindowToken();
         lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
         window.setAttributes(lp);
         window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
@@ -1471,7 +1509,8 @@
 
         final List<CharSequence> nBest = new ArrayList<CharSequence>();
         boolean capitalizeFirstWord = preferCapitalization()
-                || (mKeyboardSwitcher.isAlphabetMode() && mInputView.isShifted());
+                || (mKeyboardSwitcher.isAlphabetMode()
+                        && mKeyboardSwitcher.getInputView().isShifted());
         for (String c : mVoiceResults.candidates) {
             if (capitalizeFirstWord) {
                 c = Character.toUpperCase(c.charAt(0)) + c.substring(1, c.length());
@@ -1526,7 +1565,8 @@
     private void updateSuggestions() {
         mSuggestionShouldReplaceCurrentWord = false;
 
-        ((LatinKeyboard) mInputView.getKeyboard()).setPreferredLetters(null);
+        LatinKeyboardView inputView = mKeyboardSwitcher.getInputView();
+        ((LatinKeyboard) inputView.getKeyboard()).setPreferredLetters(null);
 
         // Check if we have a suggestion engine attached.
         if ((mSuggest == null || !isPredictionOn()) && !mVoiceInputHighlighted) {
@@ -1541,13 +1581,13 @@
     }
 
     private List<CharSequence> getTypedSuggestions(WordComposer word) {
-        List<CharSequence> stringList = mSuggest.getSuggestions(mInputView, word, false, null);
+        List<CharSequence> stringList = mSuggest.getSuggestions(mKeyboardSwitcher.getInputView(), word, false, null);
         return stringList;
     }
 
     private void showCorrections(WordAlternatives alternatives) {
         List<CharSequence> stringList = alternatives.getAlternatives();
-        ((LatinKeyboard) mInputView.getKeyboard()).setPreferredLetters(null);
+        ((LatinKeyboard) mKeyboardSwitcher.getInputView().getKeyboard()).setPreferredLetters(null);
         showSuggestions(stringList, alternatives.getOriginalWord(), false, false);
     }
 
@@ -1555,21 +1595,22 @@
         //long startTime = System.currentTimeMillis(); // TIME MEASUREMENT!
         // TODO Maybe need better way of retrieving previous word
         CharSequence prevWord = EditingUtil.getPreviousWord(getCurrentInputConnection());
-        List<CharSequence> stringList = mSuggest.getSuggestions(mInputView, word, false,
+        List<CharSequence> stringList = mSuggest.getSuggestions(mKeyboardSwitcher.getInputView(), word, false,
                 prevWord);
         //long stopTime = System.currentTimeMillis(); // TIME MEASUREMENT!
         //Log.d("LatinIME","Suggest Total Time - " + (stopTime - startTime));
 
         int[] nextLettersFrequencies = mSuggest.getNextLettersFrequencies();
 
-        ((LatinKeyboard) mInputView.getKeyboard()).setPreferredLetters(nextLettersFrequencies);
+        ((LatinKeyboard) mKeyboardSwitcher.getInputView().getKeyboard()).setPreferredLetters(
+                nextLettersFrequencies);
 
         boolean correctionAvailable = !mInputTypeNoAutoCorrect && mSuggest.hasMinimalCorrection();
         //|| mCorrectionMode == mSuggest.CORRECTION_FULL;
         CharSequence typedWord = word.getTypedWord();
         // If we're in basic correct
         boolean typedWordValid = mSuggest.isValidWord(typedWord) ||
-                (preferCapitalization() 
+                (preferCapitalization()
                         && mSuggest.isValidWord(typedWord.toString().toLowerCase()));
         if (mCorrectionMode == Suggest.CORRECTION_FULL) {
             correctionAvailable |= typedWordValid;
@@ -1596,7 +1637,7 @@
         setCandidatesViewShown(isCandidateStripVisible() || mCompletionOn);
     }
 
-    private void pickDefaultSuggestion() {
+    private boolean pickDefaultSuggestion() {
         // Complete any pending candidate query first
         if (mHandler.hasMessages(MSG_UPDATE_SUGGESTIONS)) {
             mHandler.removeMessages(MSG_UPDATE_SUGGESTIONS);
@@ -1608,11 +1649,14 @@
             pickSuggestion(mBestWord, false);
             // Add the word to the auto dictionary if it's not a known word
             checkAddToDictionary(mBestWord, AutoDictionary.FREQUENCY_FOR_TYPED);
+            return true;
         }
+        return false;
     }
 
     public void pickSuggestionManually(int index, CharSequence suggestion) {
         if (mAfterVoiceInput && mShowingVoiceSuggestions) mVoiceInput.logNBestChoose(index);
+        List<CharSequence> suggestions = mCandidateView.getSuggestions();
 
         if (mAfterVoiceInput && !mShowingVoiceSuggestions) {
             mVoiceInput.flushAllTextModificationCounters();
@@ -1643,7 +1687,12 @@
         }
 
         // If this is a punctuation, apply it through the normal key press
-        if (suggestion.length() == 1 && isWordSeparator(suggestion.charAt(0))) {
+        if (suggestion.length() == 1 && (isWordSeparator(suggestion.charAt(0))
+                || isSuggestedPunctuation(suggestion.charAt(0)))) {
+            // Word separators are suggested before the user inputs something.
+            // So, LatinImeLogger logs "" as a user's input.
+            LatinImeLogger.logOnManualSuggestion(
+                    "", suggestion.toString(), index, suggestions);
             onKey(suggestion.charAt(0), null);
             if (ic != null) {
                 ic.endBatchEdit();
@@ -1656,6 +1705,8 @@
         if (index == 0) {
             checkAddToDictionary(suggestion, AutoDictionary.FREQUENCY_FOR_PICKED);
         }
+        LatinImeLogger.logOnManualSuggestion(mComposing.toString(), suggestion.toString(),
+                index, suggestions);
         TextEntryState.acceptedSuggestion(mComposing.toString(), suggestion);
         // Follow it with a space
         if (mAutoSpace && !correcting) {
@@ -1706,10 +1757,12 @@
      *            word.
      */
     private void pickSuggestion(CharSequence suggestion, boolean correcting) {
+        LatinKeyboardView inputView = mKeyboardSwitcher.getInputView();
         if (mCapsLock) {
             suggestion = suggestion.toString().toUpperCase();
         } else if (preferCapitalization()
-                || (mKeyboardSwitcher.isAlphabetMode() && mInputView.isShifted())) {
+                || (mKeyboardSwitcher.isAlphabetMode()
+                        && inputView.isShifted())) {
             suggestion = suggestion.toString().toUpperCase().charAt(0)
                     + suggestion.subSequence(1, suggestion.length()).toString();
         }
@@ -1731,7 +1784,7 @@
         saveWordInHistory(suggestion);
         mPredicting = false;
         mCommittedLength = suggestion.length();
-        ((LatinKeyboard) mInputView.getKeyboard()).setPreferredLetters(null);
+        ((LatinKeyboard) inputView.getKeyboard()).setPreferredLetters(null);
         setNextSuggestions();
         updateShiftKeyState(getCurrentInputEditorInfo());
     }
@@ -1919,7 +1972,7 @@
         return separators.contains(String.valueOf((char)code));
     }
 
-    public boolean isSentenceSeparator(int code) {
+    private boolean isSentenceSeparator(int code) {
         return mSentenceSeparators.contains(String.valueOf((char)code));
     }
 
@@ -1943,7 +1996,7 @@
             ClipboardManager cm = ((ClipboardManager)getSystemService(CLIPBOARD_SERVICE));
             CharSequence text = cm.getText();
             if (!TextUtils.isEmpty(text)) {
-                mInputView.startPlaying(text.toString());
+                mKeyboardSwitcher.getInputView().startPlaying(text.toString());
             }
         }
     }
@@ -1994,7 +2047,7 @@
 
     public void onRelease(int primaryCode) {
         // Reset any drag flags in the keyboard
-        ((LatinKeyboard) mInputView.getKeyboard()).keyReleased();
+        ((LatinKeyboard) mKeyboardSwitcher.getInputView().getKeyboard()).keyReleased();
         //vibrate();
     }
 
@@ -2046,7 +2099,7 @@
         // if mAudioManager is null, we don't have the ringer state yet
         // mAudioManager will be set by updateRingerMode
         if (mAudioManager == null) {
-            if (mInputView != null) {
+            if (mKeyboardSwitcher.getInputView() != null) {
                 updateRingerMode();
             }
         }
@@ -2073,8 +2126,9 @@
         if (!mVibrateOn) {
             return;
         }
-        if (mInputView != null) {
-            mInputView.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP,
+        if (mKeyboardSwitcher.getInputView() != null) {
+            mKeyboardSwitcher.getInputView().performHapticFeedback(
+                    HapticFeedbackConstants.KEYBOARD_TAP,
                     HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
         }
     }
@@ -2192,14 +2246,18 @@
 
     private void initSuggestPuncList() {
         mSuggestPuncList = new ArrayList<CharSequence>();
-        String suggestPuncs = mResources.getString(R.string.suggested_punctuations);
-        if (suggestPuncs != null) {
-            for (int i = 0; i < suggestPuncs.length(); i++) {
-                mSuggestPuncList.add(suggestPuncs.subSequence(i, i + 1));
+        mSuggestPuncs = mResources.getString(R.string.suggested_punctuations);
+        if (mSuggestPuncs != null) {
+            for (int i = 0; i < mSuggestPuncs.length(); i++) {
+                mSuggestPuncList.add(mSuggestPuncs.subSequence(i, i + 1));
             }
         }
     }
 
+    private boolean isSuggestedPunctuation(int code) {
+        return mSuggestPuncs.contains(String.valueOf((char)code));
+    }
+
     private void showOptionsMenu() {
         AlertDialog.Builder builder = new AlertDialog.Builder(this);
         builder.setCancelable(true);
@@ -2228,7 +2286,7 @@
         mOptionsDialog = builder.create();
         Window window = mOptionsDialog.getWindow();
         WindowManager.LayoutParams lp = window.getAttributes();
-        lp.token = mInputView.getWindowToken();
+        lp.token = mKeyboardSwitcher.getInputView().getWindowToken();
         lp.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
         window.setAttributes(lp);
         window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
@@ -2238,7 +2296,8 @@
     private void changeKeyboardMode() {
         mKeyboardSwitcher.toggleSymbols();
         if (mCapsLock && mKeyboardSwitcher.isAlphabetMode()) {
-            ((LatinKeyboard) mInputView.getKeyboard()).setShiftLocked(mCapsLock);
+            ((LatinKeyboard) mKeyboardSwitcher.getInputView().getKeyboard()).setShiftLocked(
+                    mCapsLock);
         }
 
         updateShiftKeyState(getCurrentInputEditorInfo());
diff --git a/java/src/com/android/inputmethod/latin/LatinIMESettings.java b/java/src/com/android/inputmethod/latin/LatinIMESettings.java
index 21b9674..806ef00 100644
--- a/java/src/com/android/inputmethod/latin/LatinIMESettings.java
+++ b/java/src/com/android/inputmethod/latin/LatinIMESettings.java
@@ -24,13 +24,13 @@
 import android.app.backup.BackupManager;
 import android.content.DialogInterface;
 import android.content.SharedPreferences;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
 import android.os.Bundle;
 import android.preference.CheckBoxPreference;
 import android.preference.ListPreference;
-import android.preference.Preference;
 import android.preference.PreferenceActivity;
 import android.preference.PreferenceGroup;
-import android.preference.Preference.OnPreferenceClickListener;
 import android.speech.SpeechRecognizer;
 import android.text.AutoText;
 import android.util.Log;
@@ -43,11 +43,9 @@
         DialogInterface.OnDismissListener {
 
     private static final String QUICK_FIXES_KEY = "quick_fixes";
-    private static final String SHOW_SUGGESTIONS_KEY = "show_suggestions";
     private static final String PREDICTION_SETTINGS_KEY = "prediction_settings";
     private static final String VOICE_SETTINGS_KEY = "voice_mode";
-    private static final String VOICE_ON_PRIMARY_KEY = "voice_on_main";
-    private static final String VOICE_SERVER_KEY = "voice_server_url";
+    private static final String DEBUG_MODE_KEY = "debug_mode";
 
     private static final String TAG = "LatinIMESettings";
 
@@ -55,7 +53,7 @@
     private static final int VOICE_INPUT_CONFIRM_DIALOG = 0;
 
     private CheckBoxPreference mQuickFixes;
-    private CheckBoxPreference mShowSuggestions;
+    private CheckBoxPreference mDebugMode;
     private ListPreference mVoicePreference;
     private boolean mVoiceOn;
 
@@ -69,7 +67,6 @@
         super.onCreate(icicle);
         addPreferencesFromResource(R.xml.prefs);
         mQuickFixes = (CheckBoxPreference) findPreference(QUICK_FIXES_KEY);
-        mShowSuggestions = (CheckBoxPreference) findPreference(SHOW_SUGGESTIONS_KEY);
         mVoicePreference = (ListPreference) findPreference(VOICE_SETTINGS_KEY);
         SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
         prefs.registerOnSharedPreferenceChangeListener(this);
@@ -77,6 +74,9 @@
         mVoiceModeOff = getString(R.string.voice_mode_off);
         mVoiceOn = !(prefs.getString(VOICE_SETTINGS_KEY, mVoiceModeOff).equals(mVoiceModeOff));
         mLogger = VoiceInputLogger.getLogger(this);
+
+        mDebugMode = (CheckBoxPreference) findPreference(DEBUG_MODE_KEY);
+        updateDebugMode(mDebugMode.isChecked());
     }
 
     @Override
@@ -110,11 +110,35 @@
                     .equals(mVoiceModeOff)) {
                 showVoiceConfirmation();
             }
+        } else if (key.equals(DEBUG_MODE_KEY)) {
+            updateDebugMode(prefs.getBoolean(DEBUG_MODE_KEY, false));
         }
         mVoiceOn = !(prefs.getString(VOICE_SETTINGS_KEY, mVoiceModeOff).equals(mVoiceModeOff));
         updateVoiceModeSummary();
     }
 
+    private void updateDebugMode(boolean isDebugMode) {
+        if (mDebugMode == null) {
+            return;
+        }
+        String version = "";
+        try {
+            PackageInfo info = getPackageManager().getPackageInfo(getPackageName(), 0);
+            version = "Version " + info.versionName;
+        } catch (NameNotFoundException e) {
+            Log.e(TAG, "Could not find version info.");
+        }
+        if (!isDebugMode) {
+            mDebugMode.setEnabled(false);
+            mDebugMode.setTitle(version);
+            mDebugMode.setSummary("");
+        } else {
+            mDebugMode.setEnabled(true);
+            mDebugMode.setTitle(getResources().getString(R.string.prefs_debug_mode));
+            mDebugMode.setSummary(version);
+        }
+    }
+
     private void showVoiceConfirmation() {
         mOkClicked = false;
         showDialog(VOICE_INPUT_CONFIRM_DIALOG);
diff --git a/java/src/com/android/inputmethod/latin/LatinIMEUtil.java b/java/src/com/android/inputmethod/latin/LatinIMEUtil.java
new file mode 100644
index 0000000..838b4fe
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/LatinIMEUtil.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin;
+
+import android.os.AsyncTask;
+import android.text.format.DateUtils;
+import android.util.Log;
+
+public class LatinIMEUtil {
+
+    /**
+     * Cancel an {@link AsyncTask}.
+     *
+     * @param mayInterruptIfRunning <tt>true</tt> if the thread executing this
+     *        task should be interrupted; otherwise, in-progress tasks are allowed
+     *        to complete.
+     */
+    public static void cancelTask(AsyncTask<?, ?, ?> task, boolean mayInterruptIfRunning) {
+        if (task != null && task.getStatus() != AsyncTask.Status.FINISHED) {
+            task.cancel(mayInterruptIfRunning);
+        }
+    }
+
+    public static class GCUtils {
+        private static final String TAG = "GCUtils";
+        public static final int GC_TRY_COUNT = 2;
+        // GC_TRY_LOOP_MAX is used for the hard limit of GC wait,
+        // GC_TRY_LOOP_MAX should be greater than GC_TRY_COUNT.
+        public static final int GC_TRY_LOOP_MAX = 5;
+        private static final long GC_INTERVAL = DateUtils.SECOND_IN_MILLIS;
+        private static GCUtils sInstance = new GCUtils();
+        private int mGCTryCount = 0;
+
+        public static GCUtils getInstance() {
+            return sInstance;
+        }
+
+        public void reset() {
+            mGCTryCount = 0;
+        }
+
+        public boolean tryGCOrWait(String metaData, Throwable t) {
+            if (LatinImeLogger.sDBG) {
+                Log.d(TAG, "Encountered Exception or Error. Try GC.");
+            }
+            if (mGCTryCount == 0) {
+                System.gc();
+            }
+            if (++mGCTryCount > GC_TRY_COUNT) {
+                LatinImeLogger.logOnException(metaData, t);
+                return false;
+            } else {
+                try {
+                    Thread.sleep(GC_INTERVAL);
+                    return true;
+                } catch (InterruptedException e) {
+                    Log.e(TAG, "Sleep was interrupted.");
+                    LatinImeLogger.logOnException(metaData, t);
+                    return false;
+                }
+            }
+        }
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/LatinImeLogger.java b/java/src/com/android/inputmethod/latin/LatinImeLogger.java
new file mode 100644
index 0000000..e889950
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/LatinImeLogger.java
@@ -0,0 +1,770 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.AsyncTask;
+import android.os.DropBoxManager;
+import android.preference.PreferenceManager;
+import android.text.TextUtils;
+import android.text.format.DateUtils;
+import android.util.Log;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+
+public class LatinImeLogger implements SharedPreferences.OnSharedPreferenceChangeListener {
+    private static final String TAG = "LatinIMELogs";
+    public static boolean sDBG = false;
+    private static boolean sLOGPRINT = false;
+    // SUPPRESS_EXCEPTION should be true when released to public.
+    private static final boolean SUPPRESS_EXCEPTION = true;
+    // DEFAULT_LOG_ENABLED should be false when released to public.
+    private static final boolean DEFAULT_LOG_ENABLED = true;
+
+    private static final long MINIMUMSENDINTERVAL = 300 * DateUtils.SECOND_IN_MILLIS; // 300 sec
+    private static final long MINIMUMCOUNTINTERVAL = 20 * DateUtils.SECOND_IN_MILLIS; // 20 sec
+    private static final long MINIMUMSENDSIZE = 40;
+    private static final char SEPARATER = ';';
+    private static final char NULL_CHAR = '\uFFFC';
+    private static final int EXCEPTION_MAX_LENGTH = 400;
+
+    private static final int ID_MANUALSUGGESTION = 0;
+    private static final int ID_AUTOSUGGESTIONCANCELLED = 1;
+    private static final int ID_AUTOSUGGESTION = 2;
+    private static final int ID_INPUT_COUNT = 3;
+    private static final int ID_DELETE_COUNT = 4;
+    private static final int ID_WORD_COUNT = 5;
+    private static final int ID_ACTUAL_CHAR_COUNT = 6;
+    private static final int ID_THEME_ID = 7;
+    private static final int ID_SETTING_AUTO_COMPLETE = 8;
+    private static final int ID_VERSION = 9;
+    private static final int ID_EXCEPTION = 10;
+    private static final int ID_MANUALSUGGESTIONCOUNT = 11;
+    private static final int ID_AUTOSUGGESTIONCANCELLEDCOUNT = 12;
+    private static final int ID_AUTOSUGGESTIONCOUNT = 13;
+    private static final int ID_LANGUAGES = 14;
+
+    private static final String PREF_ENABLE_LOG = "enable_logging";
+    private static final String PREF_DEBUG_MODE = "debug_mode";
+    private static final String PREF_AUTO_COMPLETE = "auto_complete";
+
+    public static boolean sLogEnabled = true;
+    /* package */ static LatinImeLogger sLatinImeLogger = new LatinImeLogger();
+    // Store the last auto suggested word.
+    // This is required for a cancellation log of auto suggestion of that word.
+    /* package */ static String sLastAutoSuggestBefore;
+    /* package */ static String sLastAutoSuggestAfter;
+    /* package */ static String sLastAutoSuggestSeparator;
+    private static int sLastAutoSuggestDicTypeId;
+    private static HashMap<String, Integer> sSuggestDicMap = new HashMap<String, Integer>();
+    private static DebugKeyEnabler sDebugKeyEnabler = new DebugKeyEnabler();
+
+    private ArrayList<LogEntry> mLogBuffer = null;
+    private ArrayList<LogEntry> mPrivacyLogBuffer = null;
+    /* package */ RingCharBuffer mRingCharBuffer = null;
+
+    private Context mContext = null;
+    private DropBoxManager mDropBox = null;
+    private AddTextToDropBoxTask mAddTextToDropBoxTask;
+    private long mLastTimeActive;
+    private long mLastTimeSend;
+    private long mLastTimeCountEntry;
+
+    private String mThemeId;
+    private String mSelectedLanguages;
+    private String mCurrentLanguage;
+    private int mDeleteCount;
+    private int mInputCount;
+    private int mWordCount;
+    private int[] mAutoSuggestCountPerDic = new int[Suggest.DIC_TYPE_LAST_ID + 1];
+    private int[] mManualSuggestCountPerDic = new int[Suggest.DIC_TYPE_LAST_ID + 1];
+    private int[] mAutoCancelledCountPerDic = new int[Suggest.DIC_TYPE_LAST_ID + 1];
+    private int mActualCharCount;
+
+    private static class LogEntry implements Comparable<LogEntry> {
+        public final int mTag;
+        public final String[] mData;
+        public long mTime;
+
+        public LogEntry (long time, int tag, String[] data) {
+            mTag = tag;
+            mTime = time;
+            mData = data;
+        }
+
+        public int compareTo(LogEntry log2) {
+            if (mData.length == 0 && log2.mData.length == 0) {
+                return 0;
+            } else if (mData.length == 0) {
+                return 1;
+            } else if (log2.mData.length == 0) {
+                return -1;
+            }
+            return log2.mData[0].compareTo(mData[0]);
+        }
+    }
+
+    private class AddTextToDropBoxTask extends AsyncTask<Void, Void, Void> {
+        private final DropBoxManager mDropBox;
+        private final long mTime;
+        private final String mData;
+        public AddTextToDropBoxTask(DropBoxManager db, long time, String data) {
+            mDropBox = db;
+            mTime = time;
+            mData = data;
+        }
+        @Override
+        protected Void doInBackground(Void... params) {
+            if (sLOGPRINT) {
+                Log.d(TAG, "Commit log: " + mData);
+            }
+            mDropBox.addText(TAG, mData);
+            return null;
+        }
+        @Override
+        protected void onPostExecute(Void v) {
+            mLastTimeSend = mTime;
+        }
+    }
+
+    private void initInternal(Context context) {
+        mContext = context;
+        mDropBox = (DropBoxManager) mContext.getSystemService(Context.DROPBOX_SERVICE);
+        mLastTimeSend = System.currentTimeMillis();
+        mLastTimeActive = mLastTimeSend;
+        mLastTimeCountEntry = mLastTimeSend;
+        mDeleteCount = 0;
+        mInputCount = 0;
+        mWordCount = 0;
+        mActualCharCount = 0;
+        Arrays.fill(mAutoSuggestCountPerDic, 0);
+        Arrays.fill(mManualSuggestCountPerDic, 0);
+        Arrays.fill(mAutoCancelledCountPerDic, 0);
+        mLogBuffer = new ArrayList<LogEntry>();
+        mPrivacyLogBuffer = new ArrayList<LogEntry>();
+        mRingCharBuffer = new RingCharBuffer(context);
+        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+        sLogEnabled = prefs.getBoolean(PREF_ENABLE_LOG, DEFAULT_LOG_ENABLED);
+        mThemeId = prefs.getString(KeyboardSwitcher.PREF_KEYBOARD_LAYOUT,
+                KeyboardSwitcher.DEFAULT_LAYOUT_ID);
+        mSelectedLanguages = prefs.getString(LatinIME.PREF_SELECTED_LANGUAGES, "");
+        mCurrentLanguage = prefs.getString(LatinIME.PREF_INPUT_LANGUAGE, "");
+        sLOGPRINT = prefs.getBoolean(PREF_DEBUG_MODE, sLOGPRINT);
+        sDBG = sLOGPRINT;
+        prefs.registerOnSharedPreferenceChangeListener(this);
+    }
+
+    /**
+     * Clear all logged data
+     */
+    private void reset() {
+        mDeleteCount = 0;
+        mInputCount = 0;
+        mWordCount = 0;
+        mActualCharCount = 0;
+        Arrays.fill(mAutoSuggestCountPerDic, 0);
+        Arrays.fill(mManualSuggestCountPerDic, 0);
+        Arrays.fill(mAutoCancelledCountPerDic, 0);
+        mLogBuffer.clear();
+        mPrivacyLogBuffer.clear();
+        mRingCharBuffer.reset();
+    }
+
+    public void destroy() {
+        LatinIMEUtil.cancelTask(mAddTextToDropBoxTask, false);
+    }
+
+    /**
+     * Check if the input string is safe as an entry or not.
+     */
+    private static boolean checkStringDataSafe(String s) {
+        if (sDBG) {
+            Log.d(TAG, "Check String safety: " + s);
+        }
+        for (int i = 0; i < s.length(); ++i) {
+            if (Character.isDigit(s.charAt(i))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private void addCountEntry(long time) {
+        if (sLOGPRINT) {
+            Log.d(TAG, "Log counts. (4)");
+        }
+        mLogBuffer.add(new LogEntry (time, ID_DELETE_COUNT,
+                new String[] {String.valueOf(mDeleteCount)}));
+        mLogBuffer.add(new LogEntry (time, ID_INPUT_COUNT,
+                new String[] {String.valueOf(mInputCount)}));
+        mLogBuffer.add(new LogEntry (time, ID_WORD_COUNT,
+                new String[] {String.valueOf(mWordCount)}));
+        mLogBuffer.add(new LogEntry (time, ID_ACTUAL_CHAR_COUNT,
+                new String[] {String.valueOf(mActualCharCount)}));
+        mDeleteCount = 0;
+        mInputCount = 0;
+        mWordCount = 0;
+        mActualCharCount = 0;
+        mLastTimeCountEntry = time;
+    }
+
+    private void addSuggestionCountEntry(long time) {
+        if (sLOGPRINT) {
+            Log.d(TAG, "log suggest counts. (1)");
+        }
+        String[] s = new String[mAutoSuggestCountPerDic.length];
+        for (int i = 0; i < s.length; ++i) {
+            s[i] = String.valueOf(mAutoSuggestCountPerDic[i]);
+        }
+        mLogBuffer.add(new LogEntry(time, ID_AUTOSUGGESTIONCOUNT, s));
+
+        s = new String[mAutoCancelledCountPerDic.length];
+        for (int i = 0; i < s.length; ++i) {
+            s[i] = String.valueOf(mAutoCancelledCountPerDic[i]);
+        }
+        mLogBuffer.add(new LogEntry(time, ID_AUTOSUGGESTIONCANCELLEDCOUNT, s));
+
+        s = new String[mManualSuggestCountPerDic.length];
+        for (int i = 0; i < s.length; ++i) {
+            s[i] = String.valueOf(mManualSuggestCountPerDic[i]);
+        }
+        mLogBuffer.add(new LogEntry(time, ID_MANUALSUGGESTIONCOUNT, s));
+
+        Arrays.fill(mAutoSuggestCountPerDic, 0);
+        Arrays.fill(mManualSuggestCountPerDic, 0);
+        Arrays.fill(mAutoCancelledCountPerDic, 0);
+    }
+
+    private void addThemeIdEntry(long time) {
+        if (sLOGPRINT) {
+            Log.d(TAG, "Log theme Id. (1)");
+        }
+        // TODO: Not to convert theme ID here. Currently "2" is treated as "6" in a log server.
+        if (mThemeId.equals("2")) {
+            mThemeId = "6";
+        } else if (mThemeId.equals("3")) {
+            mThemeId = "7";
+        }
+        mLogBuffer.add(new LogEntry (time, ID_THEME_ID,
+                new String[] {mThemeId}));
+    }
+
+    private void addLanguagesEntry(long time) {
+        if (sLOGPRINT) {
+            Log.d(TAG, "Log language settings. (1)");
+        }
+        // CurrentLanguage and SelectedLanguages will be blank if user doesn't use multi-language
+        // switching.
+        if (TextUtils.isEmpty(mCurrentLanguage)) {
+            mCurrentLanguage = mContext.getResources().getConfiguration().locale.toString();
+        }
+        mLogBuffer.add(new LogEntry (time, ID_LANGUAGES,
+                new String[] {mCurrentLanguage , mSelectedLanguages}));
+    }
+
+    private void addSettingsEntry(long time) {
+        if (sLOGPRINT) {
+            Log.d(TAG, "Log settings. (1)");
+        }
+        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
+        mLogBuffer.add(new LogEntry (time, ID_SETTING_AUTO_COMPLETE,
+                new String[] {String.valueOf(prefs.getBoolean(PREF_AUTO_COMPLETE,
+                        mContext.getResources().getBoolean(R.bool.enable_autocorrect)))}));
+    }
+
+    private void addVersionNameEntry(long time) {
+        if (sLOGPRINT) {
+            Log.d(TAG, "Log Version. (1)");
+        }
+        try {
+            PackageInfo info = mContext.getPackageManager().getPackageInfo(
+                    mContext.getPackageName(), 0);
+            mLogBuffer.add(new LogEntry (time, ID_VERSION,
+                    new String[] {String.valueOf(info.versionCode), info.versionName}));
+        } catch (NameNotFoundException e) {
+            Log.e(TAG, "Could not find version name.");
+        }
+    }
+
+    private void addExceptionEntry(long time, String[] data) {
+        if (sLOGPRINT) {
+            Log.d(TAG, "Log Exception. (1)");
+        }
+        mLogBuffer.add(new LogEntry(time, ID_EXCEPTION, data));
+    }
+
+    private void flushPrivacyLogSafely() {
+        if (sLOGPRINT) {
+            Log.d(TAG, "Log obfuscated data. (" + mPrivacyLogBuffer.size() + ")");
+        }
+        long now = System.currentTimeMillis();
+        Collections.sort(mPrivacyLogBuffer);
+        for (LogEntry l: mPrivacyLogBuffer) {
+            l.mTime = now;
+            mLogBuffer.add(l);
+        }
+        mPrivacyLogBuffer.clear();
+    }
+
+    /**
+     * Add an entry
+     * @param tag
+     * @param data
+     */
+    private void addData(int tag, Object data) {
+        switch (tag) {
+            case ID_DELETE_COUNT:
+                if (((mLastTimeActive - mLastTimeCountEntry) > MINIMUMCOUNTINTERVAL)
+                        || (mDeleteCount == 0 && mInputCount == 0)) {
+                    addCountEntry(mLastTimeActive);
+                }
+                mDeleteCount += (Integer)data;
+                break;
+            case ID_INPUT_COUNT:
+                if (((mLastTimeActive - mLastTimeCountEntry) > MINIMUMCOUNTINTERVAL)
+                        || (mDeleteCount == 0 && mInputCount == 0)) {
+                    addCountEntry(mLastTimeActive);
+                }
+                mInputCount += (Integer)data;
+                break;
+            case ID_MANUALSUGGESTION:
+            case ID_AUTOSUGGESTION:
+                ++mWordCount;
+                String[] dataStrings = (String[]) data;
+                if (dataStrings.length < 2) {
+                    if (sDBG) {
+                        Log.e(TAG, "The length of logged string array is invalid.");
+                    }
+                    break;
+                }
+                mActualCharCount += dataStrings[1].length();
+                if (checkStringDataSafe(dataStrings[0]) && checkStringDataSafe(dataStrings[1])) {
+                    mPrivacyLogBuffer.add(
+                            new LogEntry (System.currentTimeMillis(), tag, dataStrings));
+                } else {
+                    if (sDBG) {
+                        Log.d(TAG, "Skipped to add an entry because data is unsafe.");
+                    }
+                }
+                break;
+            case ID_AUTOSUGGESTIONCANCELLED:
+                --mWordCount;
+                dataStrings = (String[]) data;
+                if (dataStrings.length < 2) {
+                    if (sDBG) {
+                        Log.e(TAG, "The length of logged string array is invalid.");
+                    }
+                    break;
+                }
+                mActualCharCount -= dataStrings[1].length();
+                if (checkStringDataSafe(dataStrings[0]) && checkStringDataSafe(dataStrings[1])) {
+                    mPrivacyLogBuffer.add(
+                            new LogEntry (System.currentTimeMillis(), tag, dataStrings));
+                } else {
+                    if (sDBG) {
+                        Log.d(TAG, "Skipped to add an entry because data is unsafe.");
+                    }
+                }
+                break;
+            case ID_EXCEPTION:
+                dataStrings = (String[]) data;
+                if (dataStrings.length < 2) {
+                    if (sDBG) {
+                        Log.e(TAG, "The length of logged string array is invalid.");
+                    }
+                    break;
+                }
+                addExceptionEntry(System.currentTimeMillis(), dataStrings);
+                break;
+            default:
+                if (sDBG) {
+                    Log.e(TAG, "Log Tag is not entried.");
+                }
+                break;
+        }
+    }
+
+    private void commitInternal() {
+        // if there is no log entry in mLogBuffer, will not send logs to DropBox.
+        if (!mLogBuffer.isEmpty() && (mAddTextToDropBoxTask == null
+                || mAddTextToDropBoxTask.getStatus() == AsyncTask.Status.FINISHED)) {
+            if (sLOGPRINT) {
+                Log.d(TAG, "Commit (" + mLogBuffer.size() + ")");
+            }
+            flushPrivacyLogSafely();
+            long now = System.currentTimeMillis();
+            addCountEntry(now);
+            addThemeIdEntry(now);
+            addLanguagesEntry(now);
+            addSettingsEntry(now);
+            addVersionNameEntry(now);
+            addSuggestionCountEntry(now);
+            String s = LogSerializer.createStringFromEntries(mLogBuffer);
+            reset();
+            mAddTextToDropBoxTask = (AddTextToDropBoxTask) new AddTextToDropBoxTask(
+                    mDropBox, now, s).execute();
+        }
+    }
+
+    private void commitInternalAndStopSelf() {
+        if (sDBG) {
+            Log.e(TAG, "Exception was thrown and let's die.");
+        }
+        commitInternal();
+        LatinIME ime = ((LatinIME) mContext);
+        ime.hideWindow();
+        ime.stopSelf();
+    }
+
+    private synchronized void sendLogToDropBox(int tag, Object s) {
+        long now = System.currentTimeMillis();
+        if (sDBG) {
+            String out = "";
+            if (s instanceof String[]) {
+                for (String str: ((String[]) s)) {
+                    out += str + ",";
+                }
+            } else if (s instanceof Integer) {
+                out += (Integer) s;
+            }
+            Log.d(TAG, "SendLog: " + tag + ";" + out + ", will be sent after "
+                    + (- (now - mLastTimeSend - MINIMUMSENDINTERVAL) / 1000) + " sec.");
+        }
+        if (now - mLastTimeActive > MINIMUMSENDINTERVAL) {
+            // Send a log before adding an log entry if the last data is too old.
+            commitInternal();
+            addData(tag, s);
+        } else if (now - mLastTimeSend > MINIMUMSENDINTERVAL) {
+            // Send a log after adding an log entry.
+            addData(tag, s);
+            commitInternal();
+        } else {
+            addData(tag, s);
+        }
+        mLastTimeActive = now;
+    }
+
+    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+        if (PREF_ENABLE_LOG.equals(key)) {
+            if (sharedPreferences.getBoolean(key, DEFAULT_LOG_ENABLED)) {
+                sLogEnabled = (mContext != null);
+            } else {
+                sLogEnabled = false;
+            }
+            if (sDebugKeyEnabler.check()) {
+                sharedPreferences.edit().putBoolean(PREF_DEBUG_MODE, true).commit();
+            }
+        } else if (KeyboardSwitcher.PREF_KEYBOARD_LAYOUT.equals(key)) {
+            mThemeId = sharedPreferences.getString(KeyboardSwitcher.PREF_KEYBOARD_LAYOUT,
+                    KeyboardSwitcher.DEFAULT_LAYOUT_ID);
+            addThemeIdEntry(mLastTimeActive);
+        } else if (PREF_DEBUG_MODE.equals(key)) {
+            sLOGPRINT = sharedPreferences.getBoolean(PREF_DEBUG_MODE, sLOGPRINT);
+            sDBG = sLOGPRINT;
+        } else if (LatinIME.PREF_INPUT_LANGUAGE.equals(key)) {
+            mCurrentLanguage = sharedPreferences.getString(LatinIME.PREF_INPUT_LANGUAGE, "");
+            addLanguagesEntry(mLastTimeActive);
+        } else if (LatinIME.PREF_INPUT_LANGUAGE.equals(key)) {
+            mSelectedLanguages = sharedPreferences.getString(LatinIME.PREF_SELECTED_LANGUAGES, "");
+        }
+    }
+
+    public static void init(Context context) {
+        sLatinImeLogger.initInternal(context);
+    }
+
+    public static void commit() {
+        if (sLogEnabled) {
+            if (System.currentTimeMillis() - sLatinImeLogger.mLastTimeActive > MINIMUMCOUNTINTERVAL
+                        || (sLatinImeLogger.mLogBuffer.size()
+                                + sLatinImeLogger.mPrivacyLogBuffer.size() > MINIMUMSENDSIZE)) {
+                sLatinImeLogger.commitInternal();
+            }
+        }
+    }
+
+    public static void onDestroy() {
+        sLatinImeLogger.commitInternal();
+        sLatinImeLogger.destroy();
+    }
+
+    // TODO: Handle CharSequence instead of String
+    public static void logOnManualSuggestion(String before, String after, int position
+            , List<CharSequence> suggestions) {
+        if (sLogEnabled) {
+            // log punctuation
+            if (before.length() == 0 && after.length() == 1) {
+                sLatinImeLogger.sendLogToDropBox(ID_MANUALSUGGESTION, new String[] {
+                        before, after, String.valueOf(position), ""});
+            } else if (!sSuggestDicMap.containsKey(after)) {
+                if (sDBG) {
+                    Log.e(TAG, "logOnManualSuggestion was cancelled: from unknown dic.");
+                }
+            } else {
+                int dicTypeId = sSuggestDicMap.get(after);
+                sLatinImeLogger.mManualSuggestCountPerDic[dicTypeId]++;
+                if (dicTypeId != Suggest.DIC_MAIN) {
+                    if (sDBG) {
+                        Log.d(TAG, "logOnManualSuggestion was cancelled: not from main dic.");
+                    }
+                    before = "";
+                    after = "";
+                }
+                // TODO: Don't send a log if this doesn't come from Main Dictionary.
+                {
+                    if (before.equals(after)) {
+                        before = "";
+                        after = "";
+                    }
+                    String[] strings = new String[3 + suggestions.size()];
+                    strings[0] = before;
+                    strings[1] = after;
+                    strings[2] = String.valueOf(position);
+                    for (int i = 0; i < suggestions.size(); ++i) {
+                        String s = suggestions.get(i).toString();
+                        strings[i + 3] = sSuggestDicMap.containsKey(s) ? s : "";
+                    }
+                    sLatinImeLogger.sendLogToDropBox(ID_MANUALSUGGESTION, strings);
+                }
+            }
+            sSuggestDicMap.clear();
+        }
+    }
+
+    public static void logOnAutoSuggestion(String before, String after) {
+        if (sLogEnabled) {
+            if (!sSuggestDicMap.containsKey(after)) {
+                if (sDBG) {
+                    Log.e(TAG, "logOnAutoSuggestion was cancelled: from unknown dic.");
+                }
+            } else {
+                String separator = String.valueOf(sLatinImeLogger.mRingCharBuffer.getLastChar());
+                sLastAutoSuggestDicTypeId = sSuggestDicMap.get(after);
+                sLatinImeLogger.mAutoSuggestCountPerDic[sLastAutoSuggestDicTypeId]++;
+                if (sLastAutoSuggestDicTypeId != Suggest.DIC_MAIN) {
+                    if (sDBG) {
+                        Log.d(TAG, "logOnAutoSuggestion was cancelled: not from main dic.");
+                    }
+                    before = "";
+                    after = "";
+                }
+                // TODO: Not to send a log if this doesn't come from Main Dictionary.
+                {
+                    if (before.equals(after)) {
+                        before = "";
+                        after = "";
+                    }
+                    String[] strings = new String[] {before, after, separator};
+                    sLatinImeLogger.sendLogToDropBox(ID_AUTOSUGGESTION, strings);
+                }
+                synchronized (LatinImeLogger.class) {
+                    sLastAutoSuggestBefore = before;
+                    sLastAutoSuggestAfter = after;
+                    sLastAutoSuggestSeparator = separator;
+                }
+            }
+            sSuggestDicMap.clear();
+        }
+    }
+
+    public static void logOnAutoSuggestionCanceled() {
+        if (sLogEnabled) {
+            sLatinImeLogger.mAutoCancelledCountPerDic[sLastAutoSuggestDicTypeId]++;
+            if (sLastAutoSuggestBefore != null && sLastAutoSuggestAfter != null) {
+                String[] strings = new String[] {
+                        sLastAutoSuggestBefore, sLastAutoSuggestAfter, sLastAutoSuggestSeparator};
+                sLatinImeLogger.sendLogToDropBox(ID_AUTOSUGGESTIONCANCELLED, strings);
+            }
+            synchronized (LatinImeLogger.class) {
+                sLastAutoSuggestBefore = "";
+                sLastAutoSuggestAfter = "";
+                sLastAutoSuggestSeparator = "";
+            }
+        }
+    }
+
+    public static void logOnDelete() {
+        if (sLogEnabled) {
+            String mLastWord = sLatinImeLogger.mRingCharBuffer.getLastString();
+            if (!TextUtils.isEmpty(mLastWord)
+                    && mLastWord.equalsIgnoreCase(sLastAutoSuggestBefore)) {
+                logOnAutoSuggestionCanceled();
+            }
+            sLatinImeLogger.mRingCharBuffer.pop();
+            sLatinImeLogger.sendLogToDropBox(ID_DELETE_COUNT, 1);
+        }
+    }
+
+    public static void logOnInputChar(char c) {
+        if (sLogEnabled) {
+            sLatinImeLogger.mRingCharBuffer.push(c);
+            sLatinImeLogger.sendLogToDropBox(ID_INPUT_COUNT, 1);
+        }
+    }
+
+    public static void logOnException(String metaData, Throwable e) {
+        if (sLogEnabled) {
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            PrintStream ps = new PrintStream(baos);
+            e.printStackTrace(ps);
+            String exceptionString = URLEncoder.encode(new String(baos.toByteArray(), 0,
+                    Math.min(EXCEPTION_MAX_LENGTH, baos.size())));
+            sLatinImeLogger.sendLogToDropBox(
+                    ID_EXCEPTION, new String[] {metaData, exceptionString});
+            if (sDBG) {
+                Log.e(TAG, "Exception: " + new String(baos.toByteArray())+ ":" + exceptionString);
+            }
+            if (SUPPRESS_EXCEPTION) {
+                sLatinImeLogger.commitInternalAndStopSelf();
+            } else {
+                sLatinImeLogger.commitInternal();
+                if (e instanceof RuntimeException) {
+                    throw (RuntimeException) e;
+                } else if (e instanceof Error) {
+                    throw (Error) e;
+                }
+            }
+        }
+    }
+
+    public static void logOnWarning(String warning) {
+        if (sLogEnabled) {
+            sLatinImeLogger.sendLogToDropBox(
+                    ID_EXCEPTION, new String[] {warning, ""});
+        }
+    }
+
+    public static void onStartSuggestion() {
+        if (sLogEnabled) {
+            sSuggestDicMap.clear();
+        }
+    }
+
+    public static void onAddSuggestedWord(String word, int typeId) {
+        if (sLogEnabled) {
+            sSuggestDicMap.put(word, typeId);
+        }
+    }
+
+    private static class LogSerializer {
+        private static void appendWithLength(StringBuffer sb, String data) {
+            sb.append(data.length());
+            sb.append(SEPARATER);
+            sb.append(data);
+            sb.append(SEPARATER);
+        }
+
+        private static void appendLogEntry(StringBuffer sb, String time, String tag,
+                String[] data) {
+            if (data.length > 0) {
+                appendWithLength(sb, String.valueOf(data.length + 2));
+                appendWithLength(sb, time);
+                appendWithLength(sb, tag);
+                for (String s: data) {
+                    appendWithLength(sb, s);
+                }
+            }
+        }
+
+        public static String createStringFromEntries(ArrayList<LogEntry> logs) {
+            StringBuffer sb = new StringBuffer();
+            for (LogEntry log: logs) {
+                appendLogEntry(sb, String.valueOf(log.mTime), String.valueOf(log.mTag), log.mData);
+            }
+            return sb.toString();
+        }
+    }
+
+    /* package */ static class RingCharBuffer {
+        final int BUFSIZE = 20;
+        private Context mContext;
+        private int mEnd = 0;
+        /* package */ int length = 0;
+        private char[] mCharBuf = new char[BUFSIZE];
+
+        public RingCharBuffer(Context context) {
+            mContext = context;
+        }
+
+        private int normalize(int in) {
+            int ret = in % BUFSIZE;
+            return ret < 0 ? ret + BUFSIZE : ret;
+        }
+        public void push(char c) {
+            mCharBuf[mEnd] = c;
+            mEnd = normalize(mEnd + 1);
+            if (length < BUFSIZE) {
+                ++length;
+            }
+        }
+        public char pop() {
+            if (length < 1) {
+                return NULL_CHAR;
+            } else {
+                mEnd = normalize(mEnd - 1);
+                --length;
+                return mCharBuf[mEnd];
+            }
+        }
+        public char getLastChar() {
+            if (length < 1) {
+                return NULL_CHAR;
+            } else {
+                return mCharBuf[normalize(mEnd - 1)];
+            }
+        }
+        public String getLastString() {
+            StringBuffer sb = new StringBuffer();
+            for (int i = 0; i < length; ++i) {
+                char c = mCharBuf[normalize(mEnd - 1 - i)];
+                if (!((LatinIME)mContext).isWordSeparator(c)) {
+                    sb.append(c);
+                } else {
+                    break;
+                }
+            }
+            return sb.reverse().toString();
+        }
+        public void reset() {
+            length = 0;
+        }
+    }
+
+    private static class DebugKeyEnabler {
+        private int mCounter = 0;
+        private long mLastTime = 0;
+        public boolean check() {
+            if (System.currentTimeMillis() - mLastTime > 10 * 1000) {
+                mCounter = 0;
+                mLastTime = System.currentTimeMillis();
+            } else if (++mCounter >= 10) {
+                return true;
+            }
+            return false;
+        }
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/LatinKeyboard.java b/java/src/com/android/inputmethod/latin/LatinKeyboard.java
index ea6b74e..db4d167 100644
--- a/java/src/com/android/inputmethod/latin/LatinKeyboard.java
+++ b/java/src/com/android/inputmethod/latin/LatinKeyboard.java
@@ -81,8 +81,10 @@
     private int mPrefLetterY;
     private int mPrefDistance;
 
-    private int mExtensionResId; 
-    
+    private int mExtensionResId;
+    // TODO: generalize for any keyboardId
+    private boolean mIsBlackSym;
+
     private static final int SHIFT_OFF = 0;
     private static final int SHIFT_ON = 1;
     private static final int SHIFT_LOCKED = 2;
@@ -121,7 +123,8 @@
         setDefaultBounds(m123MicPreviewIcon);
         sSpacebarVerticalCorrection = res.getDimensionPixelOffset(
                 R.dimen.spacebar_vertical_correction);
-        mIsAlphaKeyboard = xmlLayoutResId == R.xml.kbd_qwerty;
+        mIsAlphaKeyboard = xmlLayoutResId == R.xml.kbd_qwerty
+                || xmlLayoutResId == R.xml.kbd_qwerty_black;
         mSpaceKeyIndex = indexOf((int) ' ');
     }
 
@@ -177,8 +180,8 @@
                 case EditorInfo.IME_ACTION_SEARCH:
                     mEnterKey.iconPreview = res.getDrawable(
                             R.drawable.sym_keyboard_feedback_search);
-                    mEnterKey.icon = res.getDrawable(
-                            R.drawable.sym_keyboard_search);
+                    mEnterKey.icon = res.getDrawable(mIsBlackSym ?
+                            R.drawable.sym_bkeyboard_search : R.drawable.sym_keyboard_search);
                     mEnterKey.label = null;
                     break;
                 case EditorInfo.IME_ACTION_SEND:
@@ -196,8 +199,8 @@
                     } else {
                         mEnterKey.iconPreview = res.getDrawable(
                                 R.drawable.sym_keyboard_feedback_return);
-                        mEnterKey.icon = res.getDrawable(
-                                R.drawable.sym_keyboard_return);
+                        mEnterKey.icon = res.getDrawable(mIsBlackSym ?
+                                R.drawable.sym_bkeyboard_return : R.drawable.sym_keyboard_return);
                         mEnterKey.label = null;
                     }
                     break;
@@ -271,6 +274,10 @@
         }
     }
 
+    /* package */ boolean isAlphaKeyboard() {
+        return mIsAlphaKeyboard;
+    }
+
     public void setExtension(int resId) {
         mExtensionResId = resId;
     }
@@ -279,6 +286,26 @@
         return mExtensionResId;
     }
 
+    public void setBlackFlag(boolean f) {
+        mIsBlackSym = f;
+        if (f) {
+            mShiftLockIcon = mRes.getDrawable(R.drawable.sym_bkeyboard_shift_locked);
+            mSpaceIcon = mRes.getDrawable(R.drawable.sym_bkeyboard_space);
+            mMicIcon = mRes.getDrawable(R.drawable.sym_bkeyboard_mic);
+            m123MicIcon = mRes.getDrawable(R.drawable.sym_bkeyboard_123_mic);
+        } else {
+            mShiftLockIcon = mRes.getDrawable(R.drawable.sym_keyboard_shift_locked);
+            mSpaceIcon = mRes.getDrawable(R.drawable.sym_keyboard_space);
+            mMicIcon = mRes.getDrawable(R.drawable.sym_keyboard_mic);
+            m123MicIcon = mRes.getDrawable(R.drawable.sym_keyboard_123_mic);
+        }
+        updateF1Key();
+        if (mSpaceKey != null) {
+            mSpaceKey.icon = mSpaceIcon;
+            updateSpaceBarForLocale(f);
+        }
+    }
+
     private void setDefaultBounds(Drawable drawable) {
         drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
     }
@@ -316,22 +343,23 @@
         }
     }
 
-    private void updateSpaceBarForLocale() {
+    private void updateSpaceBarForLocale(boolean isBlack) {
         if (mLocale != null) {
             // Create the graphic for spacebar
             Bitmap buffer = Bitmap.createBitmap(mSpaceKey.width, mSpaceIcon.getIntrinsicHeight(),
                     Bitmap.Config.ARGB_8888);
             Canvas canvas = new Canvas(buffer);
-            drawSpaceBar(canvas, buffer.getWidth(), buffer.getHeight(), 255);
+            drawSpaceBar(canvas, buffer.getWidth(), buffer.getHeight(), 255, isBlack);
             mSpaceKey.icon = new BitmapDrawable(mRes, buffer);
             mSpaceKey.repeatable = mLanguageSwitcher.getLocaleCount() < 2;
         } else {
-            mSpaceKey.icon = mRes.getDrawable(R.drawable.sym_keyboard_space);
+            mSpaceKey.icon = isBlack ? mRes.getDrawable(R.drawable.sym_bkeyboard_space)
+                : mRes.getDrawable(R.drawable.sym_keyboard_space);
             mSpaceKey.repeatable = true;
         }
     }
 
-    private void drawSpaceBar(Canvas canvas, int width, int height, int opacity) {
+    private void drawSpaceBar(Canvas canvas, int width, int height, int opacity, boolean isBlack) {
         canvas.drawColor(mRes.getColor(R.color.latinkeyboard_transparent), PorterDuff.Mode.CLEAR);
         Paint paint = new Paint();
         paint.setAntiAlias(true);
@@ -341,12 +369,14 @@
         paint.setTextAlign(Align.CENTER);
         final String language = getInputLanguage(mSpaceKey.width, paint);
         final int ascent = (int) -paint.ascent();
-        paint.setColor(mRes.getColor(R.color.latinkeyboard_bar_language_shadow));
-        canvas.drawText(language,
-                width / 2, ascent - 1, paint);
+
+        int shadowColor = isBlack ? mRes.getColor(R.color.latinkeyboard_bar_language_shadow_black)
+                : mRes.getColor(R.color.latinkeyboard_bar_language_shadow_white);
+
+        paint.setColor(shadowColor);
+        canvas.drawText(language, width / 2, ascent - 1, paint);
         paint.setColor(mRes.getColor(R.color.latinkeyboard_bar_language_text));
-        canvas.drawText(language,
-                width / 2, ascent, paint);
+        canvas.drawText(language, width / 2, ascent, paint);
         // Put arrows on either side of the text
         if (mLanguageSwitcher.getLocaleCount() > 1) {
             Rect bounds = new Rect();
@@ -431,7 +461,7 @@
         }
         if (mLocale != null && mLocale.equals(locale)) return;
         mLocale = locale;
-        updateSpaceBarForLocale();
+        updateSpaceBarForLocale(mIsBlackSym);
     }
 
     boolean isCurrentlyInSpace() {
@@ -677,7 +707,7 @@
             mTextPaint = new TextPaint();
             int textSize = getTextSizeFromTheme(android.R.style.TextAppearance_Medium, 18);
             mTextPaint.setTextSize(textSize);
-            mTextPaint.setColor(0);
+            mTextPaint.setColor(R.color.latinkeyboard_transparent);
             mTextPaint.setTextAlign(Align.CENTER);
             mTextPaint.setAlpha(255);
             mTextPaint.setAntiAlias(true);
diff --git a/java/src/com/android/inputmethod/latin/LatinKeyboardBaseView.java b/java/src/com/android/inputmethod/latin/LatinKeyboardBaseView.java
new file mode 100644
index 0000000..4205aad
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/LatinKeyboardBaseView.java
@@ -0,0 +1,1511 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.Rect;
+import android.graphics.Typeface;
+import android.graphics.Paint.Align;
+import android.graphics.Region.Op;
+import android.graphics.drawable.Drawable;
+import android.inputmethodservice.Keyboard;
+import android.inputmethodservice.Keyboard.Key;
+import android.os.Handler;
+import android.os.Message;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.view.GestureDetector;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup.LayoutParams;
+import android.widget.PopupWindow;
+import android.widget.TextView;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A view that renders a virtual {@link LatinKeyboard}. It handles rendering of keys and
+ * detecting key presses and touch movements.
+ *
+ * @attr ref R.styleable#LatinKeyboardBaseView_keyBackground
+ * @attr ref R.styleable#LatinKeyboardBaseView_keyPreviewLayout
+ * @attr ref R.styleable#LatinKeyboardBaseView_keyPreviewOffset
+ * @attr ref R.styleable#LatinKeyboardBaseView_labelTextSize
+ * @attr ref R.styleable#LatinKeyboardBaseView_keyTextSize
+ * @attr ref R.styleable#LatinKeyboardBaseView_keyTextColor
+ * @attr ref R.styleable#LatinKeyboardBaseView_verticalCorrection
+ * @attr ref R.styleable#LatinKeyboardBaseView_popupLayout
+ */
+public class LatinKeyboardBaseView extends View implements View.OnClickListener {
+
+    public interface OnKeyboardActionListener {
+
+        /**
+         * Called when the user presses a key. This is sent before the
+         * {@link #onKey} is called. For keys that repeat, this is only
+         * called once.
+         *
+         * @param primaryCode
+         *            the unicode of the key being pressed. If the touch is
+         *            not on a valid key, the value will be zero.
+         */
+        void onPress(int primaryCode);
+
+        /**
+         * Called when the user releases a key. This is sent after the
+         * {@link #onKey} is called. For keys that repeat, this is only
+         * called once.
+         *
+         * @param primaryCode
+         *            the code of the key that was released
+         */
+        void onRelease(int primaryCode);
+
+        /**
+         * Send a key press to the listener.
+         *
+         * @param primaryCode
+         *            this is the key that was pressed
+         * @param keyCodes
+         *            the codes for all the possible alternative keys with
+         *            the primary code being the first. If the primary key
+         *            code is a single character such as an alphabet or
+         *            number or symbol, the alternatives will include other
+         *            characters that may be on the same key or adjacent
+         *            keys. These codes are useful to correct for
+         *            accidental presses of a key adjacent to the intended
+         *            key.
+         */
+        void onKey(int primaryCode, int[] keyCodes);
+
+        /**
+         * Sends a sequence of characters to the listener.
+         *
+         * @param text
+         *            the sequence of characters to be displayed.
+         */
+        void onText(CharSequence text);
+
+        /**
+         * Called when the user quickly moves the finger from right to
+         * left.
+         */
+        void swipeLeft();
+
+        /**
+         * Called when the user quickly moves the finger from left to
+         * right.
+         */
+        void swipeRight();
+
+        /**
+         * Called when the user quickly moves the finger from up to down.
+         */
+        void swipeDown();
+
+        /**
+         * Called when the user quickly moves the finger from down to up.
+         */
+        void swipeUp();
+    }
+
+    private static final boolean DEBUG = false;
+    private static final int NOT_A_KEY = -1;
+    private static final int[] KEY_DELETE = { Keyboard.KEYCODE_DELETE };
+    private static final int[] LONG_PRESSABLE_STATE_SET = { android.R.attr.state_long_pressable };
+
+    private Keyboard mKeyboard;
+    private int mCurrentKeyIndex = NOT_A_KEY;
+    private int mLabelTextSize;
+    private int mKeyTextSize;
+    private int mKeyTextColor;
+    private float mShadowRadius;
+    private int mShadowColor;
+    private float mBackgroundDimAmount;
+
+    private TextView mPreviewText;
+    private PopupWindow mPreviewPopup;
+    private int mPreviewTextSizeLarge;
+    private int mPreviewOffset;
+    private int mPreviewHeight;
+    private int[] mOffsetInWindow;
+
+    private PopupWindow mPopupKeyboard;
+    private View mMiniKeyboardContainer;
+    private LatinKeyboardBaseView mMiniKeyboard;
+    private boolean mMiniKeyboardOnScreen;
+    private View mPopupParent;
+    private int mMiniKeyboardOffsetX;
+    private int mMiniKeyboardOffsetY;
+    private Map<Key,View> mMiniKeyboardCache;
+    private int[] mWindowOffset;
+    private Key[] mKeys;
+    private Typeface mKeyTextStyle = Typeface.DEFAULT;
+    private int mSymbolColorScheme = 0;
+
+    /** Listener for {@link OnKeyboardActionListener}. */
+    private OnKeyboardActionListener mKeyboardActionListener;
+
+    private static final int MSG_SHOW_PREVIEW = 1;
+    private static final int MSG_REMOVE_PREVIEW = 2;
+    private static final int MSG_REPEAT = 3;
+    private static final int MSG_LONGPRESS = 4;
+
+    private static final int DELAY_BEFORE_PREVIEW = 0;
+    private static final int DELAY_AFTER_PREVIEW = 70;
+    private static final int DEBOUNCE_TIME = 70;
+
+    private int mVerticalCorrection;
+    private int mProximityThreshold;
+    private int mKeyDebounceThreshold;
+    private static final int KEY_DEBOUNCE_FACTOR = 6;
+
+    private boolean mPreviewCentered = false;
+    private boolean mShowPreview = true;
+    private boolean mShowTouchPoints = true;
+    private int mPopupPreviewX;
+    private int mPopupPreviewY;
+    private int mWindowY;
+
+    private int mLastX;
+    private int mLastY;
+    private int mStartX;
+    private int mStartY;
+
+    private boolean mProximityCorrectOn;
+
+    private Paint mPaint;
+    private Rect mPadding;
+
+    private long mDownTime;
+    private long mLastMoveTime;
+    private int mLastKey;
+    private int mLastCodeX;
+    private int mLastCodeY;
+    private int mCurrentKey = NOT_A_KEY;
+    private int mDownKey = NOT_A_KEY;
+    private long mLastKeyTime;
+    private long mCurrentKeyTime;
+    private int[] mKeyIndices = new int[12];
+    private GestureDetector mGestureDetector;
+    private int mPopupX;
+    private int mPopupY;
+    private int mRepeatKeyIndex = NOT_A_KEY;
+    private int mPopupLayout;
+    private boolean mAbortKey;
+    private Key mInvalidatedKey;
+    private Rect mClipRegion = new Rect(0, 0, 0, 0);
+    private boolean mPossiblePoly;
+    private SwipeTracker mSwipeTracker = new SwipeTracker();
+    private int mSwipeThreshold;
+    private boolean mDisambiguateSwipe;
+
+    // Variables for dealing with multiple pointers
+    private int mOldPointerCount = 1;
+    private float mOldPointerX;
+    private float mOldPointerY;
+
+    private Drawable mKeyBackground;
+
+    private static final int REPEAT_INTERVAL = 50; // ~20 keys per second
+    private static final int REPEAT_START_DELAY = 400;
+    private static final int LONGPRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout();
+
+    private static int MAX_NEARBY_KEYS = 12;
+    private int[] mDistances = new int[MAX_NEARBY_KEYS];
+
+    // For multi-tap
+    private int mLastSentIndex;
+    private int mTapCount;
+    private long mLastTapTime;
+    private boolean mInMultiTap;
+    private static final int MULTITAP_INTERVAL = 800; // milliseconds
+    private StringBuilder mPreviewLabel = new StringBuilder(1);
+
+    /** Whether the keyboard bitmap needs to be redrawn before it's blitted. **/
+    private boolean mDrawPending;
+    /** The dirty region in the keyboard bitmap */
+    private Rect mDirtyRect = new Rect();
+    /** The keyboard bitmap for faster updates */
+    private Bitmap mBuffer;
+    /** Notes if the keyboard just changed, so that we could possibly reallocate the mBuffer. */
+    private boolean mKeyboardChanged;
+    /** The canvas for the above mutable keyboard bitmap */
+    private Canvas mCanvas;
+
+    Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_SHOW_PREVIEW:
+                    showKey(msg.arg1);
+                    break;
+                case MSG_REMOVE_PREVIEW:
+                    mPreviewText.setVisibility(INVISIBLE);
+                    break;
+                case MSG_REPEAT:
+                    if (repeatKey()) {
+                        Message repeat = Message.obtain(this, MSG_REPEAT);
+                        sendMessageDelayed(repeat, REPEAT_INTERVAL);
+                    }
+                    break;
+                case MSG_LONGPRESS:
+                    openPopupIfRequired((MotionEvent) msg.obj);
+                    break;
+            }
+        }
+    };
+
+    public LatinKeyboardBaseView(Context context, AttributeSet attrs) {
+        this(context, attrs, R.attr.keyboardViewStyle);
+    }
+
+    public LatinKeyboardBaseView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+
+        TypedArray a = context.obtainStyledAttributes(
+                attrs, R.styleable.LatinKeyboardBaseView, defStyle, R.style.LatinKeyboardBaseView);
+        LayoutInflater inflate =
+                (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        int previewLayout = 0;
+        int keyTextSize = 0;
+
+        int n = a.getIndexCount();
+
+        for (int i = 0; i < n; i++) {
+            int attr = a.getIndex(i);
+
+            switch (attr) {
+            case R.styleable.LatinKeyboardBaseView_keyBackground:
+                mKeyBackground = a.getDrawable(attr);
+                break;
+            case R.styleable.LatinKeyboardBaseView_verticalCorrection:
+                mVerticalCorrection = a.getDimensionPixelOffset(attr, 0);
+                break;
+            case R.styleable.LatinKeyboardBaseView_keyPreviewLayout:
+                previewLayout = a.getResourceId(attr, 0);
+                break;
+            case R.styleable.LatinKeyboardBaseView_keyPreviewOffset:
+                mPreviewOffset = a.getDimensionPixelOffset(attr, 0);
+                break;
+            case R.styleable.LatinKeyboardBaseView_keyPreviewHeight:
+                mPreviewHeight = a.getDimensionPixelSize(attr, 80);
+                break;
+            case R.styleable.LatinKeyboardBaseView_keyTextSize:
+                mKeyTextSize = a.getDimensionPixelSize(attr, 18);
+                break;
+            case R.styleable.LatinKeyboardBaseView_keyTextColor:
+                mKeyTextColor = a.getColor(attr, 0xFF000000);
+                break;
+            case R.styleable.LatinKeyboardBaseView_labelTextSize:
+                mLabelTextSize = a.getDimensionPixelSize(attr, 14);
+                break;
+            case R.styleable.LatinKeyboardBaseView_popupLayout:
+                mPopupLayout = a.getResourceId(attr, 0);
+                break;
+            case R.styleable.LatinKeyboardBaseView_shadowColor:
+                mShadowColor = a.getColor(attr, 0);
+                break;
+            case R.styleable.LatinKeyboardBaseView_shadowRadius:
+                mShadowRadius = a.getFloat(attr, 0f);
+                break;
+            // TODO: Use Theme (android.R.styleable.Theme_backgroundDimAmount)
+            case R.styleable.LatinKeyboardBaseView_backgroundDimAmount:
+                mBackgroundDimAmount = a.getFloat(attr, 0.5f);
+                break;
+            //case android.R.styleable.
+            case R.styleable.LatinKeyboardBaseView_keyTextStyle:
+                int textStyle = a.getInt(attr, 0);
+                switch (textStyle) {
+                    case 0:
+                        mKeyTextStyle = Typeface.DEFAULT;
+                        break;
+                    case 1:
+                        mKeyTextStyle = Typeface.DEFAULT_BOLD;
+                        break;
+                    default:
+                        mKeyTextStyle = Typeface.defaultFromStyle(textStyle);
+                        break;
+                }
+                break;
+            case R.styleable.LatinKeyboardBaseView_symbolColorScheme:
+                mSymbolColorScheme = a.getInt(attr, 0);
+                break;
+            }
+        }
+
+        mPreviewPopup = new PopupWindow(context);
+        if (previewLayout != 0) {
+            mPreviewText = (TextView) inflate.inflate(previewLayout, null);
+            mPreviewTextSizeLarge = (int) mPreviewText.getTextSize();
+            mPreviewPopup.setContentView(mPreviewText);
+            mPreviewPopup.setBackgroundDrawable(null);
+        } else {
+            mShowPreview = false;
+        }
+
+        mPreviewPopup.setTouchable(false);
+
+        mPopupKeyboard = new PopupWindow(context);
+        mPopupKeyboard.setBackgroundDrawable(null);
+        //mPopupKeyboard.setClippingEnabled(false);
+
+        mPopupParent = this;
+        //mPredicting = true;
+
+        mPaint = new Paint();
+        mPaint.setAntiAlias(true);
+        mPaint.setTextSize(keyTextSize);
+        mPaint.setTextAlign(Align.CENTER);
+        mPaint.setAlpha(255);
+
+        mPadding = new Rect(0, 0, 0, 0);
+        mMiniKeyboardCache = new HashMap<Key,View>();
+        mKeyBackground.getPadding(mPadding);
+
+        mSwipeThreshold = (int) (500 * getResources().getDisplayMetrics().density);
+        // TODO: Refer frameworks/base/core/res/res/values/config.xml
+        mDisambiguateSwipe = getResources().getBoolean(R.bool.config_swipeDisambiguation);
+        resetMultiTap();
+        initGestureDetector();
+    }
+
+    private void initGestureDetector() {
+        mGestureDetector = new GestureDetector(
+                getContext(), new GestureDetector.SimpleOnGestureListener() {
+            @Override
+            public boolean onFling(MotionEvent me1, MotionEvent me2,
+                    float velocityX, float velocityY) {
+                if (mPossiblePoly) return false;
+                final float absX = Math.abs(velocityX);
+                final float absY = Math.abs(velocityY);
+                float deltaX = me2.getX() - me1.getX();
+                float deltaY = me2.getY() - me1.getY();
+                int travelX = getWidth() / 2; // Half the keyboard width
+                int travelY = getHeight() / 2; // Half the keyboard height
+                mSwipeTracker.computeCurrentVelocity(1000);
+                final float endingVelocityX = mSwipeTracker.getXVelocity();
+                final float endingVelocityY = mSwipeTracker.getYVelocity();
+                boolean sendDownKey = false;
+                if (velocityX > mSwipeThreshold && absY < absX && deltaX > travelX) {
+                    if (mDisambiguateSwipe && endingVelocityX < velocityX / 4) {
+                        sendDownKey = true;
+                    } else {
+                        swipeRight();
+                        return true;
+                    }
+                } else if (velocityX < -mSwipeThreshold && absY < absX && deltaX < -travelX) {
+                    if (mDisambiguateSwipe && endingVelocityX > velocityX / 4) {
+                        sendDownKey = true;
+                    } else {
+                        swipeLeft();
+                        return true;
+                    }
+                } else if (velocityY < -mSwipeThreshold && absX < absY && deltaY < -travelY) {
+                    if (mDisambiguateSwipe && endingVelocityY > velocityY / 4) {
+                        sendDownKey = true;
+                    } else {
+                        swipeUp();
+                        return true;
+                    }
+                } else if (velocityY > mSwipeThreshold && absX < absY / 2 && deltaY > travelY) {
+                    if (mDisambiguateSwipe && endingVelocityY < velocityY / 4) {
+                        sendDownKey = true;
+                    } else {
+                        swipeDown();
+                        return true;
+                    }
+                }
+
+                if (sendDownKey) {
+                    detectAndSendKey(mDownKey, mStartX, mStartY, me1.getEventTime());
+                }
+                return false;
+            }
+        });
+
+        mGestureDetector.setIsLongpressEnabled(false);
+    }
+
+    public void setOnKeyboardActionListener(OnKeyboardActionListener listener) {
+        mKeyboardActionListener = listener;
+    }
+
+    /**
+     * Returns the {@link OnKeyboardActionListener} object.
+     * @return the listener attached to this keyboard
+     */
+    protected OnKeyboardActionListener getOnKeyboardActionListener() {
+        return mKeyboardActionListener;
+    }
+
+    /**
+     * Attaches a keyboard to this view. The keyboard can be switched at any time and the
+     * view will re-layout itself to accommodate the keyboard.
+     * @see Keyboard
+     * @see #getKeyboard()
+     * @param keyboard the keyboard to display in this view
+     */
+    public void setKeyboard(Keyboard keyboard) {
+        if (mKeyboard != null) {
+            showPreview(NOT_A_KEY);
+        }
+        // Remove any pending messages
+        removeMessages();
+        mKeyboard = keyboard;
+        List<Key> keys = mKeyboard.getKeys();
+        mKeys = keys.toArray(new Key[keys.size()]);
+        requestLayout();
+        // Hint to reallocate the buffer if the size changed
+        mKeyboardChanged = true;
+        invalidateAllKeys();
+        computeProximityThreshold(keyboard);
+        mMiniKeyboardCache.clear();
+        // Not really necessary to do every time, but will free up views
+        // Switching to a different keyboard should abort any pending keys so that the key up
+        // doesn't get delivered to the old or new keyboard
+        mAbortKey = true; // Until the next ACTION_DOWN
+    }
+
+    /**
+     * Returns the current keyboard being displayed by this view.
+     * @return the currently attached keyboard
+     * @see #setKeyboard(Keyboard)
+     */
+    public Keyboard getKeyboard() {
+        return mKeyboard;
+    }
+
+    /**
+     * Sets the state of the shift key of the keyboard, if any.
+     * @param shifted whether or not to enable the state of the shift key
+     * @return true if the shift key state changed, false if there was no change
+     */
+    public boolean setShifted(boolean shifted) {
+        if (mKeyboard != null) {
+            if (mKeyboard.setShifted(shifted)) {
+                // The whole keyboard probably needs to be redrawn
+                invalidateAllKeys();
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns the state of the shift key of the keyboard, if any.
+     * @return true if the shift is in a pressed state, false otherwise. If there is
+     * no shift key on the keyboard or there is no keyboard attached, it returns false.
+     */
+    public boolean isShifted() {
+        if (mKeyboard != null) {
+            return mKeyboard.isShifted();
+        }
+        return false;
+    }
+
+    /**
+     * Enables or disables the key feedback popup. This is a popup that shows a magnified
+     * version of the depressed key. By default the preview is enabled.
+     * @param previewEnabled whether or not to enable the key feedback popup
+     * @see #isPreviewEnabled()
+     */
+    public void setPreviewEnabled(boolean previewEnabled) {
+        mShowPreview = previewEnabled;
+    }
+
+    /**
+     * Returns the enabled state of the key feedback popup.
+     * @return whether or not the key feedback popup is enabled
+     * @see #setPreviewEnabled(boolean)
+     */
+    public boolean isPreviewEnabled() {
+        return mShowPreview;
+    }
+
+    public int getSymbolColorSheme() {
+        return mSymbolColorScheme;
+    }
+
+    public void setVerticalCorrection(int verticalOffset) {
+    }
+
+    public void setPopupParent(View v) {
+        mPopupParent = v;
+    }
+
+    public void setPopupOffset(int x, int y) {
+        mMiniKeyboardOffsetX = x;
+        mMiniKeyboardOffsetY = y;
+        if (mPreviewPopup.isShowing()) {
+            mPreviewPopup.dismiss();
+        }
+    }
+
+    /**
+     * When enabled, calls to {@link OnKeyboardActionListener#onKey} will include key
+     * codes for adjacent keys.  When disabled, only the primary key code will be
+     * reported.
+     * @param enabled whether or not the proximity correction is enabled
+     */
+    public void setProximityCorrectionEnabled(boolean enabled) {
+        mProximityCorrectOn = enabled;
+    }
+
+    /**
+     * Returns true if proximity correction is enabled.
+     */
+    public boolean isProximityCorrectionEnabled() {
+        return mProximityCorrectOn;
+    }
+
+    /**
+     * Popup keyboard close button clicked.
+     * @hide
+     */
+    public void onClick(View v) {
+        dismissPopupKeyboard();
+    }
+
+    protected CharSequence adjustCase(CharSequence label) {
+        if (mKeyboard.isShifted() && label != null && label.length() < 3
+                && Character.isLowerCase(label.charAt(0))) {
+            label = label.toString().toUpperCase();
+        }
+        return label;
+    }
+
+    @Override
+    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        // Round up a little
+        if (mKeyboard == null) {
+            setMeasuredDimension(
+                    getPaddingLeft() + getPaddingRight(), getPaddingTop() + getPaddingBottom());
+        } else {
+            int width = mKeyboard.getMinWidth() + getPaddingLeft() + getPaddingRight();
+            if (MeasureSpec.getSize(widthMeasureSpec) < width + 10) {
+                width = MeasureSpec.getSize(widthMeasureSpec);
+            }
+            setMeasuredDimension(
+                    width, mKeyboard.getHeight() + getPaddingTop() + getPaddingBottom());
+        }
+    }
+
+    /**
+     * Compute the average distance between adjacent keys (horizontally and vertically)
+     * and square it to get the proximity threshold. We use a square here and in computing
+     * the touch distance from a key's center to avoid taking a square root.
+     * @param keyboard
+     */
+    private void computeProximityThreshold(Keyboard keyboard) {
+        if (keyboard == null) return;
+        final Key[] keys = mKeys;
+        if (keys == null) return;
+        int length = keys.length;
+        int dimensionSum = 0;
+        for (int i = 0; i < length; i++) {
+            Key key = keys[i];
+            dimensionSum += Math.min(key.width, key.height) + key.gap;
+        }
+        if (dimensionSum < 0 || length == 0) return;
+        mProximityThreshold = (int) (dimensionSum * 1.4f / length);
+        mProximityThreshold *= mProximityThreshold; // Square it
+
+        // 1/KEY_DEBOUNCE_FACTOR of distance between adjacent keys
+        mKeyDebounceThreshold = mProximityThreshold / KEY_DEBOUNCE_FACTOR;
+    }
+
+    @Override
+    public void onSizeChanged(int w, int h, int oldw, int oldh) {
+        super.onSizeChanged(w, h, oldw, oldh);
+        // Release the buffer, if any and it will be reallocated on the next draw
+        mBuffer = null;
+    }
+
+    @Override
+    public void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+        if (mDrawPending || mBuffer == null || mKeyboardChanged) {
+            onBufferDraw();
+        }
+        canvas.drawBitmap(mBuffer, 0, 0, null);
+    }
+
+    private void onBufferDraw() {
+        if (mBuffer == null || mKeyboardChanged) {
+            if (mBuffer == null || mKeyboardChanged &&
+                    (mBuffer.getWidth() != getWidth() || mBuffer.getHeight() != getHeight())) {
+                // Make sure our bitmap is at least 1x1
+                final int width = Math.max(1, getWidth());
+                final int height = Math.max(1, getHeight());
+                mBuffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+                mCanvas = new Canvas(mBuffer);
+            }
+            invalidateAllKeys();
+            mKeyboardChanged = false;
+        }
+        final Canvas canvas = mCanvas;
+        canvas.clipRect(mDirtyRect, Op.REPLACE);
+
+        if (mKeyboard == null) return;
+
+        final Paint paint = mPaint;
+        final Drawable keyBackground = mKeyBackground;
+        final Rect clipRegion = mClipRegion;
+        final Rect padding = mPadding;
+        final int kbdPaddingLeft = getPaddingLeft();
+        final int kbdPaddingTop = getPaddingTop();
+        final Key[] keys = mKeys;
+        final Key invalidKey = mInvalidatedKey;
+
+        paint.setColor(mKeyTextColor);
+        boolean drawSingleKey = false;
+        if (invalidKey != null && canvas.getClipBounds(clipRegion)) {
+          // Is clipRegion completely contained within the invalidated key?
+          if (invalidKey.x + kbdPaddingLeft - 1 <= clipRegion.left &&
+                  invalidKey.y + kbdPaddingTop - 1 <= clipRegion.top &&
+                  invalidKey.x + invalidKey.width + kbdPaddingLeft + 1 >= clipRegion.right &&
+                  invalidKey.y + invalidKey.height + kbdPaddingTop + 1 >= clipRegion.bottom) {
+              drawSingleKey = true;
+          }
+        }
+        canvas.drawColor(0x00000000, PorterDuff.Mode.CLEAR);
+        final int keyCount = keys.length;
+        for (int i = 0; i < keyCount; i++) {
+            final Key key = keys[i];
+            if (drawSingleKey && invalidKey != key) {
+                continue;
+            }
+            int[] drawableState = key.getCurrentDrawableState();
+            keyBackground.setState(drawableState);
+
+            // Switch the character to uppercase if shift is pressed
+            String label = key.label == null? null : adjustCase(key.label).toString();
+
+            final Rect bounds = keyBackground.getBounds();
+            if (key.width != bounds.right ||
+                    key.height != bounds.bottom) {
+                keyBackground.setBounds(0, 0, key.width, key.height);
+            }
+            canvas.translate(key.x + kbdPaddingLeft, key.y + kbdPaddingTop);
+            keyBackground.draw(canvas);
+
+            if (label != null) {
+                // For characters, use large font. For labels like "Done", use small font.
+                if (label.length() > 1 && key.codes.length < 2) {
+                    paint.setTextSize(mLabelTextSize);
+                    paint.setTypeface(Typeface.DEFAULT_BOLD);
+                } else {
+                    paint.setTextSize(mKeyTextSize);
+                    paint.setTypeface(mKeyTextStyle);
+                }
+                // Draw a drop shadow for the text
+                paint.setShadowLayer(mShadowRadius, 0, 0, mShadowColor);
+                // Draw the text
+                canvas.drawText(label,
+                    (key.width - padding.left - padding.right) / 2
+                            + padding.left,
+                    (key.height - padding.top - padding.bottom) / 2
+                            + (paint.getTextSize() - paint.descent()) / 2 + padding.top,
+                    paint);
+                // Turn off drop shadow
+                paint.setShadowLayer(0, 0, 0, 0);
+            } else if (key.icon != null) {
+                final int drawableX = (key.width - padding.left - padding.right
+                                - key.icon.getIntrinsicWidth()) / 2 + padding.left;
+                final int drawableY = (key.height - padding.top - padding.bottom
+                        - key.icon.getIntrinsicHeight()) / 2 + padding.top;
+                canvas.translate(drawableX, drawableY);
+                key.icon.setBounds(0, 0,
+                        key.icon.getIntrinsicWidth(), key.icon.getIntrinsicHeight());
+                key.icon.draw(canvas);
+                canvas.translate(-drawableX, -drawableY);
+            }
+            canvas.translate(-key.x - kbdPaddingLeft, -key.y - kbdPaddingTop);
+        }
+        mInvalidatedKey = null;
+        // Overlay a dark rectangle to dim the keyboard
+        if (mMiniKeyboardOnScreen) {
+            paint.setColor((int) (mBackgroundDimAmount * 0xFF) << 24);
+            canvas.drawRect(0, 0, getWidth(), getHeight(), paint);
+        }
+
+        if (DEBUG) {
+            if (mShowTouchPoints) {
+                paint.setAlpha(128);
+                paint.setColor(0xFFFF0000);
+                canvas.drawCircle(mStartX, mStartY, 3, paint);
+                canvas.drawLine(mStartX, mStartY, mLastX, mLastY, paint);
+                paint.setColor(0xFF0000FF);
+                canvas.drawCircle(mLastX, mLastY, 3, paint);
+                paint.setColor(0xFF00FF00);
+                canvas.drawCircle((mStartX + mLastX) / 2, (mStartY + mLastY) / 2, 2, paint);
+            }
+        }
+
+        mDrawPending = false;
+        mDirtyRect.setEmpty();
+    }
+
+    private int getKeyIndices(int x, int y, int[] allKeys) {
+        final Key[] keys = mKeys;
+        int primaryIndex = NOT_A_KEY;
+        int closestKey = NOT_A_KEY;
+        int closestKeyDist = mProximityThreshold + 1;
+        java.util.Arrays.fill(mDistances, Integer.MAX_VALUE);
+        int [] nearestKeyIndices = mKeyboard.getNearestKeys(x, y);
+        final int keyCount = nearestKeyIndices.length;
+        for (int i = 0; i < keyCount; i++) {
+            final Key key = keys[nearestKeyIndices[i]];
+            int dist = 0;
+            boolean isInside = key.isInside(x,y);
+            if (isInside) {
+                primaryIndex = nearestKeyIndices[i];
+            }
+
+            if (((mProximityCorrectOn
+                    && (dist = key.squaredDistanceFrom(x, y)) < mProximityThreshold)
+                    || isInside)
+                    && key.codes[0] > 32) {
+                // Find insertion point
+                final int nCodes = key.codes.length;
+                if (dist < closestKeyDist) {
+                    closestKeyDist = dist;
+                    closestKey = nearestKeyIndices[i];
+                }
+
+                if (allKeys == null) continue;
+
+                for (int j = 0; j < mDistances.length; j++) {
+                    if (mDistances[j] > dist) {
+                        // Make space for nCodes codes
+                        System.arraycopy(mDistances, j, mDistances, j + nCodes,
+                                mDistances.length - j - nCodes);
+                        System.arraycopy(allKeys, j, allKeys, j + nCodes,
+                                allKeys.length - j - nCodes);
+                        for (int c = 0; c < nCodes; c++) {
+                            allKeys[j + c] = key.codes[c];
+                            mDistances[j + c] = dist;
+                        }
+                        break;
+                    }
+                }
+            }
+        }
+        if (primaryIndex == NOT_A_KEY) {
+            primaryIndex = closestKey;
+        }
+        return primaryIndex;
+    }
+
+    private void detectAndSendKey(int index, int x, int y, long eventTime) {
+        if (index != NOT_A_KEY && index < mKeys.length) {
+            final Key key = mKeys[index];
+            if (key.text != null) {
+                mKeyboardActionListener.onText(key.text);
+                mKeyboardActionListener.onRelease(NOT_A_KEY);
+            } else {
+                int code = key.codes[0];
+                //TextEntryState.keyPressedAt(key, x, y);
+                int[] codes = new int[MAX_NEARBY_KEYS];
+                Arrays.fill(codes, NOT_A_KEY);
+                getKeyIndices(x, y, codes);
+                // Multi-tap
+                if (mInMultiTap) {
+                    if (mTapCount != -1) {
+                        mKeyboardActionListener.onKey(Keyboard.KEYCODE_DELETE, KEY_DELETE);
+                    } else {
+                        mTapCount = 0;
+                    }
+                    code = key.codes[mTapCount];
+                }
+                mKeyboardActionListener.onKey(code, codes);
+                mKeyboardActionListener.onRelease(code);
+            }
+            mLastSentIndex = index;
+            mLastTapTime = eventTime;
+        }
+    }
+
+    /**
+     * Handle multi-tap keys by producing the key label for the current multi-tap state.
+     */
+    private CharSequence getPreviewText(Key key) {
+        if (mInMultiTap) {
+            // Multi-tap
+            mPreviewLabel.setLength(0);
+            mPreviewLabel.append((char) key.codes[mTapCount < 0 ? 0 : mTapCount]);
+            return adjustCase(mPreviewLabel);
+        } else {
+            return adjustCase(key.label);
+        }
+    }
+
+    private void showPreview(int keyIndex) {
+        int oldKeyIndex = mCurrentKeyIndex;
+        final PopupWindow previewPopup = mPreviewPopup;
+
+        mCurrentKeyIndex = keyIndex;
+        // Release the old key and press the new key
+        final Key[] keys = mKeys;
+        if (oldKeyIndex != mCurrentKeyIndex) {
+            if (oldKeyIndex != NOT_A_KEY && keys.length > oldKeyIndex) {
+                keys[oldKeyIndex].onReleased(mCurrentKeyIndex == NOT_A_KEY);
+                invalidateKey(oldKeyIndex);
+            }
+            if (mCurrentKeyIndex != NOT_A_KEY && keys.length > mCurrentKeyIndex) {
+                keys[mCurrentKeyIndex].onPressed();
+                invalidateKey(mCurrentKeyIndex);
+            }
+        }
+        // If key changed and preview is on ...
+        if (oldKeyIndex != mCurrentKeyIndex && mShowPreview) {
+            mHandler.removeMessages(MSG_SHOW_PREVIEW);
+            if (previewPopup.isShowing()) {
+                if (keyIndex == NOT_A_KEY) {
+                    mHandler.sendMessageDelayed(mHandler
+                            .obtainMessage(MSG_REMOVE_PREVIEW),
+                            DELAY_AFTER_PREVIEW);
+                }
+            }
+            if (keyIndex != NOT_A_KEY) {
+                if (previewPopup.isShowing() && mPreviewText.getVisibility() == VISIBLE) {
+                    // Show right away, if it's already visible and finger is moving around
+                    showKey(keyIndex);
+                } else {
+                    mHandler.sendMessageDelayed(
+                            mHandler.obtainMessage(MSG_SHOW_PREVIEW, keyIndex, 0),
+                            DELAY_BEFORE_PREVIEW);
+                }
+            }
+        }
+    }
+
+    private void showKey(final int keyIndex) {
+        final PopupWindow previewPopup = mPreviewPopup;
+        final Key[] keys = mKeys;
+        if (keyIndex < 0 || keyIndex >= mKeys.length) return;
+        Key key = keys[keyIndex];
+        if (key.icon != null) {
+            mPreviewText.setCompoundDrawables(null, null, null,
+                    key.iconPreview != null ? key.iconPreview : key.icon);
+            mPreviewText.setText(null);
+        } else {
+            mPreviewText.setCompoundDrawables(null, null, null, null);
+            mPreviewText.setText(getPreviewText(key));
+            if (key.label.length() > 1 && key.codes.length < 2) {
+                mPreviewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mKeyTextSize);
+                mPreviewText.setTypeface(Typeface.DEFAULT_BOLD);
+            } else {
+                mPreviewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mPreviewTextSizeLarge);
+                mPreviewText.setTypeface(mKeyTextStyle);
+            }
+        }
+        mPreviewText.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
+                MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
+        int popupWidth = Math.max(mPreviewText.getMeasuredWidth(), key.width
+                + mPreviewText.getPaddingLeft() + mPreviewText.getPaddingRight());
+        final int popupHeight = mPreviewHeight;
+        LayoutParams lp = mPreviewText.getLayoutParams();
+        if (lp != null) {
+            lp.width = popupWidth;
+            lp.height = popupHeight;
+        }
+        if (!mPreviewCentered) {
+            mPopupPreviewX = key.x - mPreviewText.getPaddingLeft() + getPaddingLeft();
+            mPopupPreviewY = key.y - popupHeight + mPreviewOffset;
+        } else {
+            // TODO: Fix this if centering is brought back
+            mPopupPreviewX = 160 - mPreviewText.getMeasuredWidth() / 2;
+            mPopupPreviewY = - mPreviewText.getMeasuredHeight();
+        }
+        mHandler.removeMessages(MSG_REMOVE_PREVIEW);
+        if (mOffsetInWindow == null) {
+            mOffsetInWindow = new int[2];
+            getLocationInWindow(mOffsetInWindow);
+            mOffsetInWindow[0] += mMiniKeyboardOffsetX; // Offset may be zero
+            mOffsetInWindow[1] += mMiniKeyboardOffsetY; // Offset may be zero
+            int[] mWindowLocation = new int[2];
+            getLocationOnScreen(mWindowLocation);
+            mWindowY = mWindowLocation[1];
+        }
+        // Set the preview background state
+        mPreviewText.getBackground().setState(
+                key.popupResId != 0 ? LONG_PRESSABLE_STATE_SET : EMPTY_STATE_SET);
+        mPopupPreviewX += mOffsetInWindow[0];
+        mPopupPreviewY += mOffsetInWindow[1];
+
+        // If the popup cannot be shown above the key, put it on the side
+        if (mPopupPreviewY + mWindowY < 0) {
+            // If the key you're pressing is on the left side of the keyboard, show the popup on
+            // the right, offset by enough to see at least one key to the left/right.
+            if (key.x + key.width <= getWidth() / 2) {
+                mPopupPreviewX += (int) (key.width * 2.5);
+            } else {
+                mPopupPreviewX -= (int) (key.width * 2.5);
+            }
+            mPopupPreviewY += popupHeight;
+        }
+
+        if (previewPopup.isShowing()) {
+            previewPopup.update(mPopupPreviewX, mPopupPreviewY,
+                    popupWidth, popupHeight);
+        } else {
+            previewPopup.setWidth(popupWidth);
+            previewPopup.setHeight(popupHeight);
+            previewPopup.showAtLocation(mPopupParent, Gravity.NO_GRAVITY,
+                    mPopupPreviewX, mPopupPreviewY);
+        }
+        mPreviewText.setVisibility(VISIBLE);
+    }
+
+    /**
+     * Requests a redraw of the entire keyboard. Calling {@link #invalidate} is not sufficient
+     * because the keyboard renders the keys to an off-screen buffer and an invalidate() only
+     * draws the cached buffer.
+     * @see #invalidateKey(int)
+     */
+    public void invalidateAllKeys() {
+        mDirtyRect.union(0, 0, getWidth(), getHeight());
+        mDrawPending = true;
+        invalidate();
+    }
+
+    /**
+     * Invalidates a key so that it will be redrawn on the next repaint. Use this method if only
+     * one key is changing it's content. Any changes that affect the position or size of the key
+     * may not be honored.
+     * @param keyIndex the index of the key in the attached {@link Keyboard}.
+     * @see #invalidateAllKeys
+     */
+    public void invalidateKey(int keyIndex) {
+        if (mKeys == null) return;
+        if (keyIndex < 0 || keyIndex >= mKeys.length) {
+            return;
+        }
+        final Key key = mKeys[keyIndex];
+        mInvalidatedKey = key;
+        mDirtyRect.union(key.x + getPaddingLeft(), key.y + getPaddingTop(),
+                key.x + key.width + getPaddingLeft(), key.y + key.height + getPaddingTop());
+        onBufferDraw();
+        invalidate(key.x + getPaddingLeft(), key.y + getPaddingTop(),
+                key.x + key.width + getPaddingLeft(), key.y + key.height + getPaddingTop());
+    }
+
+    private boolean openPopupIfRequired(MotionEvent me) {
+        // Check if we have a popup layout specified first.
+        if (mPopupLayout == 0) {
+            return false;
+        }
+        if (mCurrentKey < 0 || mCurrentKey >= mKeys.length) {
+            return false;
+        }
+
+        Key popupKey = mKeys[mCurrentKey];
+        boolean result = onLongPress(popupKey);
+        if (result) {
+            mAbortKey = true;
+            showPreview(NOT_A_KEY);
+        }
+        return result;
+    }
+
+    /**
+     * Called when a key is long pressed. By default this will open any popup keyboard associated
+     * with this key through the attributes popupLayout and popupCharacters.
+     * @param popupKey the key that was long pressed
+     * @return true if the long press is handled, false otherwise. Subclasses should call the
+     * method on the base class if the subclass doesn't wish to handle the call.
+     */
+    protected boolean onLongPress(Key popupKey) {
+        int popupKeyboardId = popupKey.popupResId;
+
+        if (popupKeyboardId != 0) {
+            mMiniKeyboardContainer = mMiniKeyboardCache.get(popupKey);
+            if (mMiniKeyboardContainer == null) {
+                LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(
+                        Context.LAYOUT_INFLATER_SERVICE);
+                mMiniKeyboardContainer = inflater.inflate(mPopupLayout, null);
+                mMiniKeyboard = (LatinKeyboardBaseView) mMiniKeyboardContainer.findViewById(
+                       R.id.LatinKeyboardBaseView);
+                View closeButton = mMiniKeyboardContainer.findViewById(
+                        R.id.closeButton);
+                if (closeButton != null) closeButton.setOnClickListener(this);
+                mMiniKeyboard.setOnKeyboardActionListener(new OnKeyboardActionListener() {
+                    public void onKey(int primaryCode, int[] keyCodes) {
+                        mKeyboardActionListener.onKey(primaryCode, keyCodes);
+                        dismissPopupKeyboard();
+                    }
+
+                    public void onText(CharSequence text) {
+                        mKeyboardActionListener.onText(text);
+                        dismissPopupKeyboard();
+                    }
+
+                    public void swipeLeft() { }
+                    public void swipeRight() { }
+                    public void swipeUp() { }
+                    public void swipeDown() { }
+                    public void onPress(int primaryCode) {
+                        mKeyboardActionListener.onPress(primaryCode);
+                    }
+                    public void onRelease(int primaryCode) {
+                        mKeyboardActionListener.onRelease(primaryCode);
+                    }
+                });
+                //mInputView.setSuggest(mSuggest);
+                Keyboard keyboard;
+                if (popupKey.popupCharacters != null) {
+                    keyboard = new Keyboard(getContext(), popupKeyboardId,
+                            popupKey.popupCharacters, -1, getPaddingLeft() + getPaddingRight());
+                } else {
+                    keyboard = new Keyboard(getContext(), popupKeyboardId);
+                }
+                mMiniKeyboard.setKeyboard(keyboard);
+                mMiniKeyboard.setPopupParent(this);
+                mMiniKeyboardContainer.measure(
+                        MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST),
+                        MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.AT_MOST));
+
+                mMiniKeyboardCache.put(popupKey, mMiniKeyboardContainer);
+            } else {
+                mMiniKeyboard = (LatinKeyboardBaseView) mMiniKeyboardContainer.findViewById(
+                        R.id.LatinKeyboardBaseView);
+            }
+            if (mWindowOffset == null) {
+                mWindowOffset = new int[2];
+                getLocationInWindow(mWindowOffset);
+            }
+            mPopupX = popupKey.x + getPaddingLeft();
+            mPopupY = popupKey.y + getPaddingTop();
+            mPopupX = mPopupX + popupKey.width - mMiniKeyboardContainer.getMeasuredWidth();
+            mPopupY = mPopupY - mMiniKeyboardContainer.getMeasuredHeight();
+            final int x = mPopupX + mMiniKeyboardContainer.getPaddingRight() + mWindowOffset[0];
+            final int y = mPopupY + mMiniKeyboardContainer.getPaddingBottom() + mWindowOffset[1];
+            mMiniKeyboard.setPopupOffset(x < 0 ? 0 : x, y);
+            mMiniKeyboard.setShifted(isShifted());
+            mPopupKeyboard.setContentView(mMiniKeyboardContainer);
+            mPopupKeyboard.setWidth(mMiniKeyboardContainer.getMeasuredWidth());
+            mPopupKeyboard.setHeight(mMiniKeyboardContainer.getMeasuredHeight());
+            mPopupKeyboard.showAtLocation(this, Gravity.NO_GRAVITY, x, y);
+            mMiniKeyboardOnScreen = true;
+            //mMiniKeyboard.onTouchEvent(getTranslatedEvent(me));
+            invalidateAllKeys();
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent me) {
+        // Convert multi-pointer up/down events to single up/down events to
+        // deal with the typical multi-pointer behavior of two-thumb typing
+        final int pointerCount = me.getPointerCount();
+        final int action = me.getAction();
+        boolean result = false;
+        final long now = me.getEventTime();
+
+        if (pointerCount != mOldPointerCount) {
+            if (pointerCount == 1) {
+                // Send a down event for the latest pointer
+                MotionEvent down = MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN,
+                        me.getX(), me.getY(), me.getMetaState());
+                result = onModifiedTouchEvent(down, false);
+                down.recycle();
+                // If it's an up action, then deliver the up as well.
+                if (action == MotionEvent.ACTION_UP) {
+                    result = onModifiedTouchEvent(me, true);
+                }
+            } else {
+                // Send an up event for the last pointer
+                MotionEvent up = MotionEvent.obtain(now, now, MotionEvent.ACTION_UP,
+                        mOldPointerX, mOldPointerY, me.getMetaState());
+                result = onModifiedTouchEvent(up, true);
+                up.recycle();
+            }
+        } else {
+            if (pointerCount == 1) {
+                result = onModifiedTouchEvent(me, false);
+                mOldPointerX = me.getX();
+                mOldPointerY = me.getY();
+            } else {
+                // Don't do anything when 2 pointers are down and moving.
+                result = true;
+            }
+        }
+        mOldPointerCount = pointerCount;
+
+        return result;
+    }
+
+    private boolean isMinorMoveForKeyDebounce(int x, int y) {
+        // TODO: Check the coordinate against each key border.  The current
+        // logic is pretty simple.
+        return ((x - mLastCodeX) * (x - mLastCodeX) +
+                (y - mLastCodeY) * (y - mLastCodeY)) < mKeyDebounceThreshold;
+    }
+
+    private boolean onModifiedTouchEvent(MotionEvent me, boolean possiblePoly) {
+        int touchX = (int) me.getX() - getPaddingLeft();
+        int touchY = (int) me.getY() + mVerticalCorrection - getPaddingTop();
+        final int action = me.getAction();
+        final long eventTime = me.getEventTime();
+        int keyIndex = getKeyIndices(touchX, touchY, null);
+        mPossiblePoly = possiblePoly;
+
+        // Track the last few movements to look for spurious swipes.
+        if (action == MotionEvent.ACTION_DOWN) mSwipeTracker.clear();
+        mSwipeTracker.addMovement(me);
+
+        // Ignore all motion events until a DOWN.
+        if (mAbortKey
+                && action != MotionEvent.ACTION_DOWN && action != MotionEvent.ACTION_CANCEL) {
+            return true;
+        }
+
+        if (mGestureDetector.onTouchEvent(me)) {
+            showPreview(NOT_A_KEY);
+            mHandler.removeMessages(MSG_REPEAT);
+            mHandler.removeMessages(MSG_LONGPRESS);
+            return true;
+        }
+
+        // Needs to be called after the gesture detector gets a turn, as it may have
+        // displayed the mini keyboard
+        if (mMiniKeyboardOnScreen && action != MotionEvent.ACTION_CANCEL) {
+            return true;
+        }
+
+        switch (action) {
+            case MotionEvent.ACTION_DOWN:
+                mAbortKey = false;
+                mStartX = touchX;
+                mStartY = touchY;
+                mLastCodeX = touchX;
+                mLastCodeY = touchY;
+                mLastKeyTime = 0;
+                mCurrentKeyTime = 0;
+                mLastKey = NOT_A_KEY;
+                mCurrentKey = keyIndex;
+                mDownKey = keyIndex;
+                mDownTime = me.getEventTime();
+                mLastMoveTime = mDownTime;
+                checkMultiTap(eventTime, keyIndex);
+                mKeyboardActionListener.onPress(keyIndex != NOT_A_KEY ?
+                        mKeys[keyIndex].codes[0] : 0);
+                if (mCurrentKey >= 0 && mKeys[mCurrentKey].repeatable) {
+                    mRepeatKeyIndex = mCurrentKey;
+                    Message msg = mHandler.obtainMessage(MSG_REPEAT);
+                    mHandler.sendMessageDelayed(msg, REPEAT_START_DELAY);
+                    repeatKey();
+                    // Delivering the key could have caused an abort
+                    if (mAbortKey) {
+                        mRepeatKeyIndex = NOT_A_KEY;
+                        break;
+                    }
+                }
+                if (mCurrentKey != NOT_A_KEY) {
+                    Message msg = mHandler.obtainMessage(MSG_LONGPRESS, me);
+                    mHandler.sendMessageDelayed(msg, LONGPRESS_TIMEOUT);
+                }
+                showPreview(keyIndex);
+                break;
+
+            case MotionEvent.ACTION_MOVE:
+                boolean continueLongPress = false;
+                if (keyIndex != NOT_A_KEY) {
+                    if (mCurrentKey == NOT_A_KEY) {
+                        mCurrentKey = keyIndex;
+                        mCurrentKeyTime = eventTime - mDownTime;
+                    } else {
+                        if (keyIndex == mCurrentKey
+                            || isMinorMoveForKeyDebounce(touchX, touchY)) {
+                            mCurrentKeyTime += eventTime - mLastMoveTime;
+                            continueLongPress = true;
+                        } else if (mRepeatKeyIndex == NOT_A_KEY) {
+                            resetMultiTap();
+                            mLastKey = mCurrentKey;
+                            mLastCodeX = mLastX;
+                            mLastCodeY = mLastY;
+                            mLastKeyTime =
+                                    mCurrentKeyTime + eventTime - mLastMoveTime;
+                            mCurrentKey = keyIndex;
+                            mCurrentKeyTime = 0;
+                        }
+                    }
+                }
+                if (!continueLongPress) {
+                    // Cancel old longpress
+                    mHandler.removeMessages(MSG_LONGPRESS);
+                    // Start new longpress if key has changed
+                    if (keyIndex != NOT_A_KEY) {
+                        Message msg = mHandler.obtainMessage(MSG_LONGPRESS, me);
+                        mHandler.sendMessageDelayed(msg, LONGPRESS_TIMEOUT);
+                    }
+                }
+                showPreview(mCurrentKey);
+                mLastMoveTime = eventTime;
+                break;
+
+            case MotionEvent.ACTION_UP:
+                removeMessages();
+                if (keyIndex == mCurrentKey) {
+                    mCurrentKeyTime += eventTime - mLastMoveTime;
+                } else {
+                    resetMultiTap();
+                    mLastKey = mCurrentKey;
+                    mLastKeyTime = mCurrentKeyTime + eventTime - mLastMoveTime;
+                    mCurrentKey = keyIndex;
+                    mCurrentKeyTime = 0;
+                }
+                if (mCurrentKeyTime < mLastKeyTime && mCurrentKeyTime < DEBOUNCE_TIME
+                        && mLastKey != NOT_A_KEY) {
+                    mCurrentKey = mLastKey;
+                    touchX = mLastCodeX;
+                    touchY = mLastCodeY;
+                }
+                showPreview(NOT_A_KEY);
+                Arrays.fill(mKeyIndices, NOT_A_KEY);
+                // If we're not on a repeating key (which sends on a DOWN event)
+                if (mRepeatKeyIndex == NOT_A_KEY && !mMiniKeyboardOnScreen && !mAbortKey) {
+                    detectAndSendKey(mCurrentKey, touchX, touchY, eventTime);
+                }
+                invalidateKey(keyIndex);
+                mRepeatKeyIndex = NOT_A_KEY;
+                break;
+            case MotionEvent.ACTION_CANCEL:
+                removeMessages();
+                dismissPopupKeyboard();
+                mAbortKey = true;
+                showPreview(NOT_A_KEY);
+                invalidateKey(mCurrentKey);
+                break;
+        }
+        mLastX = touchX;
+        mLastY = touchY;
+        return true;
+    }
+
+    private boolean repeatKey() {
+        Key key = mKeys[mRepeatKeyIndex];
+        detectAndSendKey(mCurrentKey, key.x, key.y, mLastTapTime);
+        return true;
+    }
+
+    protected void swipeRight() {
+        mKeyboardActionListener.swipeRight();
+    }
+
+    protected void swipeLeft() {
+        mKeyboardActionListener.swipeLeft();
+    }
+
+    protected void swipeUp() {
+        mKeyboardActionListener.swipeUp();
+    }
+
+    protected void swipeDown() {
+        mKeyboardActionListener.swipeDown();
+    }
+
+    public void closing() {
+        if (mPreviewPopup.isShowing()) {
+            mPreviewPopup.dismiss();
+        }
+        removeMessages();
+
+        dismissPopupKeyboard();
+        mBuffer = null;
+        mCanvas = null;
+        mMiniKeyboardCache.clear();
+    }
+
+    private void removeMessages() {
+        mHandler.removeMessages(MSG_REPEAT);
+        mHandler.removeMessages(MSG_LONGPRESS);
+        mHandler.removeMessages(MSG_SHOW_PREVIEW);
+    }
+
+    @Override
+    public void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        closing();
+    }
+
+    private void dismissPopupKeyboard() {
+        if (mPopupKeyboard.isShowing()) {
+            mPopupKeyboard.dismiss();
+            mMiniKeyboardOnScreen = false;
+            invalidateAllKeys();
+        }
+    }
+
+    public boolean handleBack() {
+        if (mPopupKeyboard.isShowing()) {
+            dismissPopupKeyboard();
+            return true;
+        }
+        return false;
+    }
+
+    private void resetMultiTap() {
+        mLastSentIndex = NOT_A_KEY;
+        mTapCount = 0;
+        mLastTapTime = -1;
+        mInMultiTap = false;
+    }
+
+    private void checkMultiTap(long eventTime, int keyIndex) {
+        if (keyIndex == NOT_A_KEY) return;
+        Key key = mKeys[keyIndex];
+        if (key.codes.length > 1) {
+            mInMultiTap = true;
+            if (eventTime < mLastTapTime + MULTITAP_INTERVAL
+                    && keyIndex == mLastSentIndex) {
+                mTapCount = (mTapCount + 1) % key.codes.length;
+                return;
+            } else {
+                mTapCount = -1;
+                return;
+            }
+        }
+        if (eventTime > mLastTapTime + MULTITAP_INTERVAL || keyIndex != mLastSentIndex) {
+            resetMultiTap();
+        }
+    }
+
+    private static class SwipeTracker {
+
+        static final int NUM_PAST = 4;
+        static final int LONGEST_PAST_TIME = 200;
+
+        final float mPastX[] = new float[NUM_PAST];
+        final float mPastY[] = new float[NUM_PAST];
+        final long mPastTime[] = new long[NUM_PAST];
+
+        float mYVelocity;
+        float mXVelocity;
+
+        public void clear() {
+            mPastTime[0] = 0;
+        }
+
+        public void addMovement(MotionEvent ev) {
+            long time = ev.getEventTime();
+            final int N = ev.getHistorySize();
+            for (int i=0; i<N; i++) {
+                addPoint(ev.getHistoricalX(i), ev.getHistoricalY(i),
+                        ev.getHistoricalEventTime(i));
+            }
+            addPoint(ev.getX(), ev.getY(), time);
+        }
+
+        private void addPoint(float x, float y, long time) {
+            int drop = -1;
+            int i;
+            final long[] pastTime = mPastTime;
+            for (i=0; i<NUM_PAST; i++) {
+                if (pastTime[i] == 0) {
+                    break;
+                } else if (pastTime[i] < time-LONGEST_PAST_TIME) {
+                    drop = i;
+                }
+            }
+            if (i == NUM_PAST && drop < 0) {
+                drop = 0;
+            }
+            if (drop == i) drop--;
+            final float[] pastX = mPastX;
+            final float[] pastY = mPastY;
+            if (drop >= 0) {
+                final int start = drop+1;
+                final int count = NUM_PAST-drop-1;
+                System.arraycopy(pastX, start, pastX, 0, count);
+                System.arraycopy(pastY, start, pastY, 0, count);
+                System.arraycopy(pastTime, start, pastTime, 0, count);
+                i -= (drop+1);
+            }
+            pastX[i] = x;
+            pastY[i] = y;
+            pastTime[i] = time;
+            i++;
+            if (i < NUM_PAST) {
+                pastTime[i] = 0;
+            }
+        }
+
+        public void computeCurrentVelocity(int units) {
+            computeCurrentVelocity(units, Float.MAX_VALUE);
+        }
+
+        public void computeCurrentVelocity(int units, float maxVelocity) {
+            final float[] pastX = mPastX;
+            final float[] pastY = mPastY;
+            final long[] pastTime = mPastTime;
+
+            final float oldestX = pastX[0];
+            final float oldestY = pastY[0];
+            final long oldestTime = pastTime[0];
+            float accumX = 0;
+            float accumY = 0;
+            int N=0;
+            while (N < NUM_PAST) {
+                if (pastTime[N] == 0) {
+                    break;
+                }
+                N++;
+            }
+
+            for (int i=1; i < N; i++) {
+                final int dur = (int)(pastTime[i] - oldestTime);
+                if (dur == 0) continue;
+                float dist = pastX[i] - oldestX;
+                float vel = (dist/dur) * units;   // pixels/frame.
+                if (accumX == 0) accumX = vel;
+                else accumX = (accumX + vel) * .5f;
+
+                dist = pastY[i] - oldestY;
+                vel = (dist/dur) * units;   // pixels/frame.
+                if (accumY == 0) accumY = vel;
+                else accumY = (accumY + vel) * .5f;
+            }
+            mXVelocity = accumX < 0.0f ? Math.max(accumX, -maxVelocity)
+                    : Math.min(accumX, maxVelocity);
+            mYVelocity = accumY < 0.0f ? Math.max(accumY, -maxVelocity)
+                    : Math.min(accumY, maxVelocity);
+        }
+
+        public float getXVelocity() {
+            return mXVelocity;
+        }
+
+        public float getYVelocity() {
+            return mYVelocity;
+        }
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/LatinKeyboardView.java b/java/src/com/android/inputmethod/latin/LatinKeyboardView.java
index 323f4bf..bce2cde 100644
--- a/java/src/com/android/inputmethod/latin/LatinKeyboardView.java
+++ b/java/src/com/android/inputmethod/latin/LatinKeyboardView.java
@@ -22,7 +22,6 @@
 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;
@@ -32,7 +31,7 @@
 import android.view.MotionEvent;
 import android.widget.PopupWindow;
 
-public class LatinKeyboardView extends KeyboardView {
+public class LatinKeyboardView extends LatinKeyboardBaseView {
 
     static final int KEYCODE_OPTIONS = -100;
     static final int KEYCODE_SHIFT_LONGPRESS = -101;
@@ -65,6 +64,8 @@
     /** The y coordinate of the last row */
     private int mLastRowY;
 
+    private int mExtensionLayoutResId = 0;
+
     public LatinKeyboardView(Context context, AttributeSet attrs) {
         super(context, attrs);
     }
@@ -77,6 +78,10 @@
         mPhoneKeyboard = phoneKeyboard;
     }
 
+    public void setExtentionLayoutResId (int id) {
+        mExtensionLayoutResId = id;
+    }
+
     @Override
     public void setKeyboard(Keyboard k) {
         super.setKeyboard(k);
@@ -106,6 +111,19 @@
         }
     }
 
+    @Override
+    protected CharSequence adjustCase(CharSequence label) {
+        Keyboard keyboard = getKeyboard();
+        if (keyboard.isShifted()
+                && keyboard instanceof LatinKeyboard
+                && ((LatinKeyboard) keyboard).isAlphaKeyboard()
+                && label != null && label.length() < 3
+                && Character.isLowerCase(label.charAt(0))) {
+            label = label.toString().toUpperCase();
+        }
+        return label;
+    }
+
     /**
      * This function checks to see if we need to handle any sudden jumps in the pointer location
      * that could be due to a multi-touch being treated as a move by the firmware or hardware.
@@ -294,7 +312,8 @@
             mExtensionPopup.setBackgroundDrawable(null);
             LayoutInflater li = (LayoutInflater) getContext().getSystemService(
                     Context.LAYOUT_INFLATER_SERVICE);
-            mExtension = (LatinKeyboardView) li.inflate(R.layout.input_trans, null);
+            mExtension = (LatinKeyboardView) li.inflate(mExtensionLayoutResId == 0 ?
+                    R.layout.input_trans : mExtensionLayoutResId, null);
             mExtension.setExtensionType(true);
             mExtension.setOnKeyboardActionListener(
                     new ExtensionKeyboardListener(getOnKeyboardActionListener()));
@@ -465,7 +484,16 @@
 
     @Override
     public void draw(Canvas c) {
-        super.draw(c);
+        LatinIMEUtil.GCUtils.getInstance().reset();
+        boolean tryGC = true;
+        for (int i = 0; i < LatinIMEUtil.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) {
+            try {
+                super.draw(c);
+                tryGC = false;
+            } catch (OutOfMemoryError e) {
+                tryGC = LatinIMEUtil.GCUtils.getInstance().tryGCOrWait("LatinKeyboardView", e);
+            }
+        }
         if (DEBUG_AUTO_PLAY) {
             if (mPlaying) {
                 mHandler2.removeMessages(MSG_TOUCH_DOWN);
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index 3e6090c..6705e9a 100755
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -53,6 +53,14 @@
      */
     public static final int MAXIMUM_BIGRAM_FREQUENCY = 127;
 
+    public static final int DIC_USER_TYPED = 0;
+    public static final int DIC_MAIN = 1;
+    public static final int DIC_USER = 2;
+    public static final int DIC_AUTO = 3;
+    public static final int DIC_CONTACTS = 4;
+    // If you add a type of dictionary, increment DIC_TYPE_LAST_ID
+    public static final int DIC_TYPE_LAST_ID = 4;
+
     static final int LARGE_DICTIONARY_THRESHOLD = 200 * 1000;
 
     private BinaryDictionary mMainDict;
@@ -88,12 +96,12 @@
     private int mCorrectionMode = CORRECTION_BASIC;
 
     public Suggest(Context context, int dictionaryResId) {
-        mMainDict = new BinaryDictionary(context, dictionaryResId);
+        mMainDict = new BinaryDictionary(context, dictionaryResId, DIC_MAIN);
         initPool();
     }
 
     public Suggest(Context context, ByteBuffer byteBuffer) {
-        mMainDict = new BinaryDictionary(context, byteBuffer);
+        mMainDict = new BinaryDictionary(context, byteBuffer, DIC_MAIN);
         initPool();
     }
 
@@ -196,6 +204,7 @@
      */
     public List<CharSequence> getSuggestions(View view, WordComposer wordComposer, 
             boolean includeTypedWordIfValid, CharSequence prevWordForBigram) {
+        LatinImeLogger.onStartSuggestion();
         mHaveCorrection = false;
         mCapitalize = wordComposer.isCapitalized();
         collectGarbage(mSuggestions, mPrefMaxSuggestions);
@@ -207,10 +216,12 @@
         if (mOriginalWord != null) {
             mOriginalWord = mOriginalWord.toString();
             mLowerOriginalWord = mOriginalWord.toString().toLowerCase();
+            LatinImeLogger.onAddSuggestedWord(mOriginalWord.toString(), Suggest.DIC_USER_TYPED);
         } else {
             mLowerOriginalWord = "";
         }
 
+        // Search the dictionary only if there are at least 2 characters
         if (wordComposer.size() == 1 && (mCorrectionMode == CORRECTION_FULL_BIGRAM
                 || mCorrectionMode == CORRECTION_BASIC)) {
             // At first character, just get the bigrams
@@ -356,7 +367,7 @@
     }
 
     public boolean addWord(final char[] word, final int offset, final int length, int freq,
-            final Dictionary.DataType dataType) {
+            final int dicTypeId, final Dictionary.DataType dataType) {
         ArrayList<CharSequence> suggestions;
         int[] priorities;
         int prefMaxSuggestions;
@@ -404,7 +415,7 @@
                 pos++;
             }
         }
-        
+
         if (pos >= prefMaxSuggestions) {
             return true;
         }
@@ -430,6 +441,8 @@
             if (garbage instanceof StringBuilder) {
                 mStringPool.add(garbage);
             }
+        } else {
+            LatinImeLogger.onAddSuggestedWord(sb.toString(), dicTypeId);
         }
         return true;
     }
diff --git a/java/src/com/android/inputmethod/latin/TextEntryState.java b/java/src/com/android/inputmethod/latin/TextEntryState.java
index 8fd9b71..224423c 100644
--- a/java/src/com/android/inputmethod/latin/TextEntryState.java
+++ b/java/src/com/android/inputmethod/latin/TextEntryState.java
@@ -132,8 +132,23 @@
         sTypedChars += typedWord.length();
         sActualChars += actualWord.length();
         sState = STATE_ACCEPTED_DEFAULT;
+        LatinImeLogger.logOnAutoSuggestion(typedWord.toString(), actualWord.toString());
     }
-    
+
+    // STATE_ACCEPTED_DEFAULT will be changed to other sub-states
+    // (see "case STATE_ACCEPTED_DEFAULT" in typedCharacter() below),
+    // and should be restored back to STATE_ACCEPTED_DEFAULT after processing for each sub-state.
+    public static void backToAcceptedDefault(CharSequence typedWord) {
+        if (typedWord == null) return;
+        switch (sState) {
+            case STATE_SPACE_AFTER_ACCEPTED:
+            case STATE_PUNCTUATION_AFTER_ACCEPTED:
+            case STATE_IN_WORD:
+                sState = STATE_ACCEPTED_DEFAULT;
+                break;
+        }
+    }
+
     public static void acceptedTyped(CharSequence typedWord) {
         sWordNotInDictionaryCount++;
         sState = STATE_PICKED_SUGGESTION;
@@ -211,6 +226,7 @@
         if (sState == STATE_ACCEPTED_DEFAULT) {
             sState = STATE_UNDO_COMMIT;
             sAutoSuggestUndoneCount++;
+            LatinImeLogger.logOnAutoSuggestionCanceled();
         } else if (sState == STATE_UNDO_COMMIT) {
             sState = STATE_IN_WORD;
         }
diff --git a/java/src/com/android/inputmethod/latin/UserDictionary.java b/java/src/com/android/inputmethod/latin/UserDictionary.java
index e8ca33a..3315cf6 100644
--- a/java/src/com/android/inputmethod/latin/UserDictionary.java
+++ b/java/src/com/android/inputmethod/latin/UserDictionary.java
@@ -38,7 +38,7 @@
     private String mLocale;
 
     public UserDictionary(Context context, String locale) {
-        super(context);
+        super(context, Suggest.DIC_USER);
         mLocale = locale;
         // Perform a managed query. The Activity will handle closing and requerying the cursor
         // when needed.
@@ -54,6 +54,7 @@
         loadDictionary();
     }
 
+    @Override
     public synchronized void close() {
         if (mObserver != null) {
             getContext().getContentResolver().unregisterContentObserver(mObserver);
diff --git a/native/Android.mk b/native/Android.mk
index 6bad9d6..de4f7eb 100644
--- a/native/Android.mk
+++ b/native/Android.mk
@@ -1,7 +1,7 @@
 LOCAL_PATH := $(call my-dir)
 include $(CLEAR_VARS)
 
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/src
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/src $(JNI_H_INCLUDE)
 
 LOCAL_SRC_FILES := \
 	jni/com_android_inputmethod_latin_BinaryDictionary.cpp \
@@ -16,8 +16,10 @@
   LOCAL_C_INCLUDES += $(JNI_H_INCLUDE)
 endif
 
-LOCAL_MODULE := libjni_latinime
+LOCAL_PRELINK_MODULE := false
 
-LOCAL_MODULE_TAGS := user
+LOCAL_MODULE := libjni_latinime2
+
+LOCAL_MODULE_TAGS := optional
 
 include $(BUILD_SHARED_LIBRARY)
diff --git a/tests/Android.mk b/tests/Android.mk
index fba7a8d..60e82d5 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -10,8 +10,8 @@
 # Include all test java files.
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_PACKAGE_NAME := LatinIMETests
+LOCAL_PACKAGE_NAME := LatinIME2Tests
 
-LOCAL_INSTRUMENTATION_FOR := LatinIME
+LOCAL_INSTRUMENTATION_FOR := LatinIme2Google
 
 include $(BUILD_PACKAGE)
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index 210e814..66cecee 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -27,7 +27,7 @@
     </application>
 
     <instrumentation android:name="android.test.InstrumentationTestRunner"
-        android:targetPackage="com.android.inputmethod.latin"
+        android:targetPackage="com.google.android.inputmethod.latin"
         android:label="LatinIME tests">
     </instrumentation>
 </manifest>
diff --git a/tests/src/com/android/inputmethod/latin/ImeLoggerTests.java b/tests/src/com/android/inputmethod/latin/ImeLoggerTests.java
new file mode 100644
index 0000000..234559b
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/ImeLoggerTests.java
@@ -0,0 +1,59 @@
+package com.android.inputmethod.latin;
+
+import android.test.ServiceTestCase;
+
+public class ImeLoggerTests extends ServiceTestCase<LatinIME> {
+
+    private static final String WORD_SEPARATORS
+            = ".\u0009\u0020,;:!?\n()[]*&@{}<>;_+=|\\u0022";
+
+    public ImeLoggerTests() {
+        super(LatinIME.class);
+    }
+    static LatinImeLogger sLogger;
+    @Override
+    protected void setUp() {
+        try {
+            super.setUp();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        setupService();
+        // startService(null); // can't be started because VoiceInput can't be found.
+        final LatinIME context = getService();
+        context.mWordSeparators = WORD_SEPARATORS;
+        LatinImeLogger.init(context);
+        sLogger = LatinImeLogger.sLatinImeLogger;
+    }
+    /*********************** Tests *********************/
+    public void testRingBuffer() {
+        for (int i = 0; i < sLogger.mRingCharBuffer.BUFSIZE * 2; ++i) {
+            LatinImeLogger.logOnDelete();
+        }
+        assertEquals("", sLogger.mRingCharBuffer.getLastString());
+        LatinImeLogger.logOnInputChar('t');
+        LatinImeLogger.logOnInputChar('g');
+        LatinImeLogger.logOnInputChar('i');
+        LatinImeLogger.logOnInputChar('s');
+        LatinImeLogger.logOnInputChar(' ');
+        LatinImeLogger.logOnAutoSuggestion("tgis", "this");
+        LatinImeLogger.logOnInputChar(' ');
+        LatinImeLogger.logOnDelete();
+        assertEquals("", sLogger.mRingCharBuffer.getLastString());
+        LatinImeLogger.logOnDelete();
+        assertEquals("tgis", sLogger.mRingCharBuffer.getLastString());
+        assertEquals("tgis", LatinImeLogger.sLastAutoSuggestBefore);
+        LatinImeLogger.logOnAutoSuggestionCanceled();
+        assertEquals("", LatinImeLogger.sLastAutoSuggestBefore);
+        LatinImeLogger.logOnDelete();
+        assertEquals("tgi", sLogger.mRingCharBuffer.getLastString());
+        for (int i = 0; i < sLogger.mRingCharBuffer.BUFSIZE * 2; ++i) {
+            LatinImeLogger.logOnDelete();
+        }
+        assertEquals("", sLogger.mRingCharBuffer.getLastString());
+        for (int i = 0; i < sLogger.mRingCharBuffer.BUFSIZE * 2; ++i) {
+            LatinImeLogger.logOnInputChar('a');
+        }
+        assertEquals(sLogger.mRingCharBuffer.BUFSIZE, sLogger.mRingCharBuffer.length);
+    }
+}
diff --git a/tests/src/com/android/inputmethod/latin/tests/SuggestTests.java b/tests/src/com/android/inputmethod/latin/tests/SuggestTests.java
index 5972064..318cc7c 100644
--- a/tests/src/com/android/inputmethod/latin/tests/SuggestTests.java
+++ b/tests/src/com/android/inputmethod/latin/tests/SuggestTests.java
@@ -278,9 +278,12 @@
      * Are accented forms of words suggested as corrections?
      */
     public void testAccents() {
-        assertTrue(isDefaultCorrection("nino", "ni\u00F1o")); // niño
-        assertTrue(isDefaultCorrection("nimo", "ni\u00F1o")); // niño
-        assertTrue(isDefaultCorrection("maria", "Mar\u00EDa")); // María
+        // ni<LATIN SMALL LETTER N WITH TILDE>o
+        assertTrue(isDefaultCorrection("nino", "ni\u00F1o"));
+        // ni<LATIN SMALL LETTER N WITH TILDE>o
+        assertTrue(isDefaultCorrection("nimo", "ni\u00F1o"));
+        // Mar<LATIN SMALL LETTER I WITH ACUTE>a
+        assertTrue(isDefaultCorrection("maria", "Mar\u00EDa"));
     }
 
     /**