Merge "Refactor key drawing code in KeyboardView"
diff --git a/java/res/layout-sw768dp/recognition_status.xml b/java/res/layout-sw768dp/recognition_status.xml
index 40bc098..d476651 100644
--- a/java/res/layout-sw768dp/recognition_status.xml
+++ b/java/res/layout-sw768dp/recognition_status.xml
@@ -26,8 +26,8 @@
             xmlns:android="http://schemas.android.com/apk/res/android"
             android:id="@+id/popup_layout"
             android:orientation="vertical"
-            android:layout_height="371dip"
-            android:layout_width="500dip"
+            android:layout_height="371dp"
+            android:layout_width="500dp"
             android:layout_centerInParent="true"
             android:background="@drawable/vs_dialog_red">
         <TextView
@@ -36,13 +36,13 @@
                 android:layout_height="wrap_content"
                 android:layout_width="wrap_content"
                 android:singleLine="true"
-                android:layout_marginTop="10dip"
+                android:layout_marginTop="10dp"
                 android:textSize="28sp"
                 android:textColor="#ffffff"
                 android:layout_gravity="center"
                 android:visibility="invisible"/>
         <RelativeLayout
-                android:layout_height="0dip"
+                android:layout_height="0dp"
                 android:layout_width="match_parent"
                 android:layout_weight="1.0">
             <com.android.inputmethod.deprecated.voice.SoundIndicator
@@ -65,8 +65,8 @@
                     android:id="@+id/progress"
                     android:indeterminate="true"
                     android:indeterminateOnly="false"
-                    android:layout_height="60dip"
-                    android:layout_width="60dip"
+                    android:layout_height="60dp"
+                    android:layout_width="60dp"
                     android:layout_centerInParent="true"
                     android:visibility="gone"/>
         </RelativeLayout>
@@ -82,14 +82,14 @@
                 android:layout_width="wrap_content"
                 android:singleLine="true"
                 android:textSize="14sp"
-                android:layout_marginBottom="3dip"
+                android:layout_marginBottom="3dp"
                 android:layout_gravity="center"
                 android:textColor="#ffffff"
                 android:visibility="invisible"/>
         <Button
                 android:id="@+id/button"
                 android:layout_width="match_parent"
-                android:layout_height="54dip"
+                android:layout_height="54dp"
                 android:singleLine="true"
                 android:focusable="true"
                 android:text="@string/cancel"
diff --git a/java/res/layout/input_view.xml b/java/res/layout/input_view.xml
index 0a8d71f..3863534 100644
--- a/java/res/layout/input_view.xml
+++ b/java/res/layout/input_view.xml
@@ -28,7 +28,7 @@
     <View
         android:id="@+id/key_preview_backing"
         android:layout_width="match_parent"
-        android:layout_height="0dip" />
+        android:layout_height="0dp" />
 
     <!-- On tablets, the suggestions strip is centered with horizontal paddings on both sides
          because width of the landscape mode is too long for the suggestions strip. This
diff --git a/java/res/layout/key_preview.xml b/java/res/layout/key_preview.xml
index b620d07..6ed892e 100644
--- a/java/res/layout/key_preview.xml
+++ b/java/res/layout/key_preview.xml
@@ -20,8 +20,8 @@
 
 <TextView xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="wrap_content"
-    android:layout_height="80sp"
-    android:textSize="40sp"
-    android:minWidth="32dip"
+    android:layout_height="80dp"
+    android:textSize="40dp"
+    android:minWidth="32dp"
     android:gravity="center"
     />
diff --git a/java/res/layout/recognition_status.xml b/java/res/layout/recognition_status.xml
index a2ddb7c..5787888 100644
--- a/java/res/layout/recognition_status.xml
+++ b/java/res/layout/recognition_status.xml
@@ -26,8 +26,8 @@
             xmlns:android="http://schemas.android.com/apk/res/android"
             android:id="@+id/popup_layout"
             android:orientation="vertical"
-            android:layout_height="371dip"
-            android:layout_width="500dip"
+            android:layout_height="371dp"
+            android:layout_width="500dp"
             android:layout_centerInParent="true"
             android:background="@drawable/vs_dialog_red">
         <TextView
@@ -36,13 +36,13 @@
                 android:layout_height="wrap_content"
                 android:layout_width="wrap_content"
                 android:singleLine="true"
-                android:layout_marginTop="10dip"
+                android:layout_marginTop="10dp"
                 android:textSize="20sp"
                 android:textColor="#ffffff"
                 android:layout_gravity="center"
                 android:visibility="invisible"/>
         <RelativeLayout
-                android:layout_height="0dip"
+                android:layout_height="0dp"
                 android:layout_width="match_parent"
                 android:layout_weight="1.0">
             <com.android.inputmethod.deprecated.voice.SoundIndicator
@@ -65,8 +65,8 @@
                     android:id="@+id/progress"
                     android:indeterminate="true"
                     android:indeterminateOnly="false"
-                    android:layout_height="60dip"
-                    android:layout_width="60dip"
+                    android:layout_height="60dp"
+                    android:layout_width="60dp"
                     android:layout_centerInParent="true"
                     android:visibility="gone"/>
         </RelativeLayout>
@@ -82,15 +82,15 @@
                 android:layout_width="wrap_content"
                 android:singleLine="true"
                 android:textSize="15sp"
-                android:layout_marginTop="3dip"
-                android:layout_marginBottom="3dip"
+                android:layout_marginTop="3dp"
+                android:layout_marginBottom="3dp"
                 android:layout_gravity="center"
                 android:textColor="#ffffff"
                 android:visibility="invisible"/>
         <Button
                 android:id="@+id/button"
                 android:layout_width="match_parent"
-                android:layout_height="30dip"
+                android:layout_height="30dp"
                 android:singleLine="true"
                 android:focusable="true"
                 android:text="@string/cancel"
diff --git a/java/res/layout/sound_effect_volume_dialog.xml b/java/res/layout/sound_effect_volume_dialog.xml
index c5b2f10..2946630 100644
--- a/java/res/layout/sound_effect_volume_dialog.xml
+++ b/java/res/layout/sound_effect_volume_dialog.xml
@@ -23,22 +23,22 @@
         android:orientation="vertical"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_margin="10dip">
+        android:layout_margin="10dp">
     <LinearLayout
         android:orientation="horizontal"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:gravity="center_horizontal"
-        android:layout_margin="10dip">
+        android:layout_margin="10dp">
         <TextView android:id="@+id/sound_effect_volume_value"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:textSize="20dip"/>
+            android:textSize="20dp"/>
     </LinearLayout>
     <SeekBar
         android:id="@+id/sound_effect_volume_bar"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:max="100"
-        android:layout_margin="10dip"/>
+        android:layout_margin="10dp"/>
 </LinearLayout>
diff --git a/java/res/layout/suggestion_info.xml b/java/res/layout/suggestion_info.xml
index a364d46..a4ad6df 100644
--- a/java/res/layout/suggestion_info.xml
+++ b/java/res/layout/suggestion_info.xml
@@ -22,6 +22,6 @@
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:textSize="6sp"
+    android:textSize="6dp"
     android:textColor="@android:color/white"
     style="?attr/suggestionBackgroundStyle" />
diff --git a/java/res/layout/suggestion_preview.xml b/java/res/layout/suggestion_preview.xml
index 3c026ae..856447b 100644
--- a/java/res/layout/suggestion_preview.xml
+++ b/java/res/layout/suggestion_preview.xml
@@ -21,8 +21,8 @@
 <TextView xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:textSize="18sp"
+    android:textSize="18dp"
     android:textColor="?android:attr/textColorPrimaryInverse"
-    android:minWidth="32dip"
+    android:minWidth="32dp"
     android:gravity="center"
     style="?attr/suggestionPreviewBackgroundStyle" />
diff --git a/java/res/layout/vibration_settings_dialog.xml b/java/res/layout/vibration_settings_dialog.xml
index 981ba9b..c9fb6ec 100644
--- a/java/res/layout/vibration_settings_dialog.xml
+++ b/java/res/layout/vibration_settings_dialog.xml
@@ -23,27 +23,27 @@
         android:orientation="vertical"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_margin="10dip">
+        android:layout_margin="10dp">
     <LinearLayout
         android:orientation="horizontal"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:gravity="center_horizontal"
-        android:layout_margin="10dip">
+        android:layout_margin="10dp">
         <TextView android:id="@+id/vibration_value"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:textSize="20dip"/>
+            android:textSize="20dp"/>
         <TextView
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:text="@string/settings_ms"
-            android:textSize="20dip"/>
+            android:textSize="20dp"/>
     </LinearLayout>
     <SeekBar
         android:id="@+id/vibration_settings"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:max="250"
-        android:layout_margin="10dip"/>
+        android:layout_margin="10dp"/>
 </LinearLayout>
diff --git a/java/res/layout/voice_punctuation_hint.xml b/java/res/layout/voice_punctuation_hint.xml
index 629a7f2..b32feb6 100644
--- a/java/res/layout/voice_punctuation_hint.xml
+++ b/java/res/layout/voice_punctuation_hint.xml
@@ -29,8 +29,8 @@
     <ImageView android:id="@+id/image"
             android:layout_height="wrap_content"
             android:layout_width="wrap_content"
-            android:paddingLeft="8dip"
-            android:paddingRight="8dip"
+            android:paddingLeft="8dp"
+            android:paddingRight="8dp"
             android:layout_gravity="center_horizontal"
             android:src="@drawable/ic_suggest_strip_microphone"
     />
@@ -39,8 +39,8 @@
             android:text="@string/voice_punctuation_hint"
             android:layout_height="wrap_content"
             android:layout_width="wrap_content"
-            android:paddingTop="2dip"
-            android:paddingRight="3dip"
+            android:paddingTop="2dp"
+            android:paddingRight="3dp"
             android:textSize="13sp"
             android:textColor="#888888"
             android:layout_gravity="center_horizontal"
diff --git a/java/res/layout/voice_swipe_hint.xml b/java/res/layout/voice_swipe_hint.xml
index 4e8859a..b1e27c2 100644
--- a/java/res/layout/voice_swipe_hint.xml
+++ b/java/res/layout/voice_swipe_hint.xml
@@ -25,14 +25,14 @@
         android:layout_height="wrap_content"
         android:background="@drawable/keyboard_suggest_strip"
         android:gravity="center_horizontal"
-        android:paddingTop="2dip">
+        android:paddingTop="2dp">
 
     <TextView android:id="@+id/text"
             android:text="@string/voice_swipe_hint"
             android:layout_height="wrap_content"
             android:layout_width="wrap_content"
-            android:paddingTop="10dip"
-            android:paddingRight="6dip"
+            android:paddingTop="10dp"
+            android:paddingRight="6dp"
             android:textSize="13sp"
             android:textColor="#888888"
             android:layout_gravity="center_horizontal"
diff --git a/java/res/values-en/additional-proximitychars.xml b/java/res/values-en/additional-proximitychars.xml
deleted file mode 100644
index a5ff4a9..0000000
--- a/java/res/values-en/additional-proximitychars.xml
+++ /dev/null
@@ -1,62 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-**
-** Copyright 2012, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
--->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-
-    <string-array name="additional_proximitychars">
-        <!-- Empty entry terminates the proximity chars array. -->
-
-        <!-- Additional proximity chars for a -->
-        <item>a</item>
-        <item>e</item>
-        <item>i</item>
-        <item>o</item>
-        <item>u</item>
-        <item></item>
-        <!-- Additional proximity chars for e -->
-        <item>e</item>
-        <item>a</item>
-        <item>i</item>
-        <item>o</item>
-        <item>u</item>
-        <item></item>
-        <!-- Additional proximity chars for i -->
-        <item>i</item>
-        <item>a</item>
-        <item>e</item>
-        <item>o</item>
-        <item>u</item>
-        <item></item>
-        <!-- Additional proximity chars for o -->
-        <item>o</item>
-        <item>a</item>
-        <item>e</item>
-        <item>i</item>
-        <item>u</item>
-        <item></item>
-        <!-- Additional proximity chars for u -->
-        <item>u</item>
-        <item>a</item>
-        <item>e</item>
-        <item>i</item>
-        <item>o</item>
-        <item></item>
-    </string-array>
-
-</resources>
diff --git a/java/res/values-land/dimens.xml b/java/res/values-land/dimens.xml
index 73192d9..6cdcf89 100644
--- a/java/res/values-land/dimens.xml
+++ b/java/res/values-land/dimens.xml
@@ -44,7 +44,7 @@
     <fraction name="key_horizontal_gap_ics">1.020%p</fraction>
 
     <!-- left or right padding of label alignment -->
-    <dimen name="key_label_horizontal_padding">8dip</dimen>
+    <dimen name="key_label_horizontal_padding">8dp</dimen>
 
     <fraction name="key_letter_ratio">65%</fraction>
     <fraction name="key_large_letter_ratio">74%</fraction>
@@ -60,8 +60,8 @@
     <!-- popup_key_height x -0.5 -->
     <dimen name="more_keys_keyboard_vertical_correction_ics">-0.140in</dimen>
 
-    <dimen name="suggestions_strip_height">36dip</dimen>
-    <dimen name="more_suggestions_row_height">36dip</dimen>
+    <dimen name="suggestions_strip_height">36dp</dimen>
+    <dimen name="more_suggestions_row_height">36dp</dimen>
     <integer name="max_more_suggestions_row">2</integer>
     <fraction name="min_more_suggestions_width">60%</fraction>
     <!-- Amount of allowance for selecting keys in a mini popup keyboard by sliding finger. -->
diff --git a/java/res/values-sw600dp-land/dimens.xml b/java/res/values-sw600dp-land/dimens.xml
index 68083b5..5dce47e 100644
--- a/java/res/values-sw600dp-land/dimens.xml
+++ b/java/res/values-sw600dp-land/dimens.xml
@@ -40,7 +40,7 @@
     <dimen name="popup_key_height">13.0mm</dimen>
 
     <!-- left or right padding of label alignment -->
-    <dimen name="key_label_horizontal_padding">18dip</dimen>
+    <dimen name="key_label_horizontal_padding">18dp</dimen>
 
     <fraction name="key_letter_ratio">45%</fraction>
     <fraction name="key_large_letter_ratio">48%</fraction>
diff --git a/java/res/values-sw600dp/dimens.xml b/java/res/values-sw600dp/dimens.xml
index a861024..0f256a3 100644
--- a/java/res/values-sw600dp/dimens.xml
+++ b/java/res/values-sw600dp/dimens.xml
@@ -40,7 +40,7 @@
 
     <fraction name="keyboard_bottom_padding_ics">0.0%p</fraction>
 
-    <dimen name="more_keys_keyboard_key_horizontal_padding">6dip</dimen>
+    <dimen name="more_keys_keyboard_key_horizontal_padding">6dp</dimen>
     <!-- Amount of allowance for selecting keys in a mini popup keyboard by sliding finger. -->
     <!-- popup_key_height x 1.2 -->
     <dimen name="more_keys_keyboard_slide_allowance">15.6mm</dimen>
@@ -48,7 +48,7 @@
     <dimen name="more_keys_keyboard_vertical_correction">-13.0mm</dimen>
 
     <!-- left or right padding of label alignment -->
-    <dimen name="key_label_horizontal_padding">6dip</dimen>
+    <dimen name="key_label_horizontal_padding">6dp</dimen>
     <dimen name="key_hint_letter_padding">3dp</dimen>
     <dimen name="key_uppercase_letter_padding">3dp</dimen>
 
@@ -63,18 +63,17 @@
     <dimen name="key_preview_height">15.0mm</dimen>
     <dimen name="key_preview_offset">0.1in</dimen>
 
-    <dimen name="key_preview_height_ics">15.0mm</dimen>
     <dimen name="key_preview_offset_ics">0.05in</dimen>
     <!-- popup_key_height x -0.5 -->
     <dimen name="more_keys_keyboard_vertical_correction_ics">-5mm</dimen>
 
-    <dimen name="suggestions_strip_height">44dip</dimen>
-    <dimen name="more_suggestions_row_height">44dip</dimen>
+    <dimen name="suggestions_strip_height">44dp</dimen>
+    <dimen name="more_suggestions_row_height">44dp</dimen>
     <integer name="max_more_suggestions_row">6</integer>
     <fraction name="min_more_suggestions_width">90%</fraction>
     <dimen name="suggestions_strip_padding">15.0mm</dimen>
     <dimen name="suggestion_min_width">0.3in</dimen>
-    <dimen name="suggestion_padding">12dip</dimen>
-    <dimen name="suggestion_text_size">22dip</dimen>
-    <dimen name="more_suggestions_hint_text_size">33dip</dimen>
+    <dimen name="suggestion_padding">12dp</dimen>
+    <dimen name="suggestion_text_size">22dp</dimen>
+    <dimen name="more_suggestions_hint_text_size">33dp</dimen>
 </resources>
diff --git a/java/res/values-sw768dp-land/dimens.xml b/java/res/values-sw768dp-land/dimens.xml
index ef39f43..535c623 100644
--- a/java/res/values-sw768dp-land/dimens.xml
+++ b/java/res/values-sw768dp-land/dimens.xml
@@ -44,7 +44,7 @@
     <dimen name="popup_key_height">13.0mm</dimen>
 
     <!-- left or right padding of label alignment -->
-    <dimen name="key_label_horizontal_padding">18dip</dimen>
+    <dimen name="key_label_horizontal_padding">18dp</dimen>
 
     <fraction name="key_letter_ratio">43%</fraction>
     <fraction name="key_large_letter_ratio">42%</fraction>
@@ -55,7 +55,6 @@
     <fraction name="spacebar_text_ratio">24.00%</fraction>
     <dimen name="key_preview_height">17.0mm</dimen>
 
-    <dimen name="key_preview_height_ics">26.5mm</dimen>
     <dimen name="key_preview_offset_ics">0.05in</dimen>
 
     <dimen name="suggestions_strip_padding">40.0mm</dimen>
diff --git a/java/res/values-sw768dp/dimens.xml b/java/res/values-sw768dp/dimens.xml
index a9ad030..7aefa26 100644
--- a/java/res/values-sw768dp/dimens.xml
+++ b/java/res/values-sw768dp/dimens.xml
@@ -43,7 +43,7 @@
 
     <dimen name="popup_key_height">10.0mm</dimen>
 
-    <dimen name="more_keys_keyboard_key_horizontal_padding">12dip</dimen>
+    <dimen name="more_keys_keyboard_key_horizontal_padding">12dp</dimen>
     <!-- Amount of allowance for selecting keys in a mini popup keyboard by sliding finger. -->
     <!-- popup_key_height x 1.2 -->
     <dimen name="more_keys_keyboard_slide_allowance">15.6mm</dimen>
@@ -51,7 +51,7 @@
     <dimen name="more_keys_keyboard_vertical_correction">-13.0mm</dimen>
 
     <!-- left or right padding of label alignment -->
-    <dimen name="key_label_horizontal_padding">6dip</dimen>
+    <dimen name="key_label_horizontal_padding">6dp</dimen>
     <dimen name="key_hint_letter_padding">3dp</dimen>
     <dimen name="key_uppercase_letter_padding">3dp</dimen>
 
@@ -66,18 +66,17 @@
     <dimen name="key_preview_height">15.0mm</dimen>
     <dimen name="key_preview_offset">0.1in</dimen>
 
-    <dimen name="key_preview_height_ics">15.0mm</dimen>
     <dimen name="key_preview_offset_ics">0.05in</dimen>
     <!-- popup_key_height x -0.5 -->
     <dimen name="more_keys_keyboard_vertical_correction_ics">-5mm</dimen>
 
-    <dimen name="suggestions_strip_height">44dip</dimen>
-    <dimen name="more_suggestions_row_height">44dip</dimen>
+    <dimen name="suggestions_strip_height">44dp</dimen>
+    <dimen name="more_suggestions_row_height">44dp</dimen>
     <integer name="max_more_suggestions_row">6</integer>
     <fraction name="min_more_suggestions_width">90%</fraction>
     <dimen name="suggestions_strip_padding">15.0mm</dimen>
-    <dimen name="suggestion_min_width">46dip</dimen>
-    <dimen name="suggestion_padding">8dip</dimen>
-    <dimen name="suggestion_text_size">22dip</dimen>
-    <dimen name="more_suggestions_hint_text_size">33dip</dimen>
+    <dimen name="suggestion_min_width">46dp</dimen>
+    <dimen name="suggestion_padding">8dp</dimen>
+    <dimen name="suggestion_text_size">22dp</dimen>
+    <dimen name="more_suggestions_hint_text_size">33dp</dimen>
 </resources>
diff --git a/java/res/values/dimens.xml b/java/res/values/dimens.xml
index 37720ce..5f74fa7 100644
--- a/java/res/values/dimens.xml
+++ b/java/res/values/dimens.xml
@@ -26,8 +26,8 @@
 
     <dimen name="popup_key_height">0.330in</dimen>
 
-    <dimen name="more_keys_keyboard_horizontal_edges_padding">16dip</dimen>
-    <dimen name="more_keys_keyboard_key_horizontal_padding">8dip</dimen>
+    <dimen name="more_keys_keyboard_horizontal_edges_padding">16dp</dimen>
+    <dimen name="more_keys_keyboard_key_horizontal_padding">8dp</dimen>
 
     <fraction name="keyboard_top_padding">1.556%p</fraction>
     <fraction name="keyboard_bottom_padding">4.669%p</fraction>
@@ -48,7 +48,7 @@
     <fraction name="keyboard_bottom_padding_ics">4.669%p</fraction>
     <fraction name="key_bottom_gap_ics">6.127%p</fraction>
     <fraction name="key_horizontal_gap_ics">1.739%p</fraction>
-    <dimen name="more_keys_keyboard_horizontal_edges_padding_ics">4dip</dimen>
+    <dimen name="more_keys_keyboard_horizontal_edges_padding_ics">4dp</dimen>
 
     <!-- Amount of allowance for selecting keys in a mini popup keyboard by sliding finger. -->
     <!-- popup_key_height x 1.2 -->
@@ -67,33 +67,32 @@
     <fraction name="key_uppercase_letter_ratio">35%</fraction>
     <fraction name="key_preview_text_ratio">82%</fraction>
     <fraction name="spacebar_text_ratio">33.735%</fraction>
-    <dimen name="key_preview_height">80sp</dimen>
+    <dimen name="key_preview_height">80dp</dimen>
     <dimen name="key_preview_offset">0.1in</dimen>
 
-    <dimen name="key_label_horizontal_padding">4dip</dimen>
+    <dimen name="key_label_horizontal_padding">4dp</dimen>
     <dimen name="key_hint_letter_padding">1dp</dimen>
     <dimen name="key_popup_hint_letter_padding">2dp</dimen>
     <dimen name="key_uppercase_letter_padding">2dp</dimen>
 
-    <dimen name="key_preview_height_ics">80sp</dimen>
     <dimen name="key_preview_offset_ics">0.05in</dimen>
     <!-- popup_key_height x -0.5 -->
     <dimen name="more_keys_keyboard_vertical_correction_ics">-0.165in</dimen>
 
-    <dimen name="suggestions_strip_height">40dip</dimen>
-    <dimen name="more_suggestions_key_horizontal_padding">12dip</dimen>
-    <dimen name="more_suggestions_row_height">40dip</dimen>
-    <dimen name="more_suggestions_bottom_gap">6dip</dimen>
+    <dimen name="suggestions_strip_height">40dp</dimen>
+    <dimen name="more_suggestions_key_horizontal_padding">12dp</dimen>
+    <dimen name="more_suggestions_row_height">40dp</dimen>
+    <dimen name="more_suggestions_bottom_gap">6dp</dimen>
     <dimen name="more_suggestions_modal_tolerance">0.2in</dimen>
     <dimen name="more_suggestions_slide_allowance">0.1in</dimen>
     <integer name="max_more_suggestions_row">6</integer>
     <fraction name="min_more_suggestions_width">90%</fraction>
     <fraction name="more_suggestions_info_ratio">18%</fraction>
-    <dimen name="suggestions_strip_padding">0dip</dimen>
-    <dimen name="suggestion_min_width">44dip</dimen>
-    <dimen name="suggestion_padding">6dip</dimen>
-    <dimen name="suggestion_text_size">18dip</dimen>
-    <dimen name="more_suggestions_hint_text_size">27dip</dimen>
+    <dimen name="suggestions_strip_padding">0dp</dimen>
+    <dimen name="suggestion_min_width">44dp</dimen>
+    <dimen name="suggestion_padding">6dp</dimen>
+    <dimen name="suggestion_text_size">18dp</dimen>
+    <dimen name="more_suggestions_hint_text_size">27dp</dimen>
     <integer name="suggestions_count_in_strip">3</integer>
     <integer name="center_suggestion_percentile">36</integer>
 </resources>
diff --git a/java/res/values/styles.xml b/java/res/values/styles.xml
index 7d8b7dd..5628f27 100644
--- a/java/res/values/styles.xml
+++ b/java/res/values/styles.xml
@@ -95,9 +95,9 @@
         name="MoreKeysKeyboard"
         parent="Keyboard"
     >
-        <item name="keyboardTopPadding">0dip</item>
-        <item name="keyboardBottomPadding">0dip</item>
-        <item name="horizontalGap">0dip</item>
+        <item name="keyboardTopPadding">0dp</item>
+        <item name="keyboardBottomPadding">0dp</item>
+        <item name="horizontalGap">0dp</item>
     </style>
     <style
         name="MoreKeysKeyboardView"
@@ -205,9 +205,9 @@
         name="MoreKeysKeyboard.Stone"
         parent="Keyboard.Stone"
     >
-        <item name="keyboardTopPadding">0dip</item>
-        <item name="keyboardBottomPadding">0dip</item>
-        <item name="horizontalGap">0dip</item>
+        <item name="keyboardTopPadding">0dp</item>
+        <item name="keyboardBottomPadding">0dp</item>
+        <item name="horizontalGap">0dp</item>
     </style>
     <style
         name="MoreKeysKeyboardView.Stone"
@@ -274,9 +274,9 @@
         name="MoreKeysKeyboard.Gingerbread"
         parent="Keyboard.Gingerbread"
     >
-        <item name="keyboardTopPadding">0dip</item>
-        <item name="keyboardBottomPadding">0dip</item>
-        <item name="horizontalGap">0dip</item>
+        <item name="keyboardTopPadding">0dp</item>
+        <item name="keyboardBottomPadding">0dp</item>
+        <item name="horizontalGap">0dp</item>
     </style>
     <style
         name="MoreKeysKeyboardView.Gingerbread"
@@ -315,7 +315,6 @@
         <item name="keyPreviewBackgroundWidth">@dimen/keyboard_key_feedback_background_holo_width</item>
         <item name="keyPreviewBackgroundHeight">@dimen/keyboard_key_feedback_background_holo_height</item>
         <item name="keyPreviewTextColor">#FFFFFFFF</item>
-        <item name="keyPreviewHeight">@dimen/key_preview_height_ics</item>
         <item name="keyPreviewOffset">@dimen/key_preview_offset_ics</item>
         <item name="shadowColor">#00000000</item>
         <item name="shadowRadius">0.0</item>
@@ -334,9 +333,9 @@
         name="MoreKeysKeyboard.IceCreamSandwich"
         parent="Keyboard.IceCreamSandwich"
     >
-        <item name="keyboardTopPadding">0dip</item>
-        <item name="keyboardBottomPadding">0dip</item>
-        <item name="horizontalGap">0dip</item>
+        <item name="keyboardTopPadding">0dp</item>
+        <item name="keyboardBottomPadding">0dp</item>
+        <item name="horizontalGap">0dp</item>
     </style>
     <style
         name="MoreKeysKeyboardView.IceCreamSandwich"
diff --git a/java/res/values/sudden-jumping-touch-event-device-list.xml b/java/res/values/sudden-jumping-touch-event-device-list.xml
index ba828a7..543992a 100644
--- a/java/res/values/sudden-jumping-touch-event-device-list.xml
+++ b/java/res/values/sudden-jumping-touch-event-device-list.xml
@@ -19,9 +19,9 @@
 -->
 <resources>
     <string-array name="sudden_jumping_touch_event_device_list" translatable="false">
-        <!-- Nexus One -->
-        <item>mahimahi</item>
-        <!-- Droid -->
-        <item>sholes</item>
+        <!-- "Build.HARDWARE,true" that needs "sudden jump touch event" hack.
+             See {@link com.android.inputmethod.keyboard.SuddenJumpingTouchEventHandler}. -->
+        <item>mahimahi,true</item> <!-- Nexus One -->
+        <item>sholes,true</item> <!-- Droid -->
     </string-array>
 </resources>
diff --git a/java/res/xml-hi/keyboard_set.xml b/java/res/xml-hi/keyboard_set.xml
new file mode 100644
index 0000000..804323a
--- /dev/null
+++ b/java/res/xml-hi/keyboard_set.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<KeyboardSet
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+    latin:keyboardLocale="hi">
+    <Element
+        latin:elementName="alphabet"
+        latin:elementKeyboard="@xml/kbd_hindi" />
+    <Element
+        latin:elementName="symbols"
+        latin:elementKeyboard="@xml/kbd_symbols" />
+    <Element
+        latin:elementName="symbolsShifted"
+        latin:elementKeyboard="@xml/kbd_symbols_shift" />
+    <Element
+        latin:elementName="phone"
+        latin:elementKeyboard="@xml/kbd_phone" />
+    <Element
+        latin:elementName="phoneSymbols"
+        latin:elementKeyboard="@xml/kbd_phone_symbols" />
+    <Element
+        latin:elementName="number"
+        latin:elementKeyboard="@xml/kbd_number" />
+</KeyboardSet>
diff --git a/java/res/xml-sw600dp/rows_hindi.xml b/java/res/xml-sw600dp/rows_hindi.xml
new file mode 100644
index 0000000..6bec7f0
--- /dev/null
+++ b/java/res/xml-sw600dp/rows_hindi.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <include
+        latin:keyboardLayout="@xml/key_styles_common" />
+    <Row
+        latin:keyWidth="7.6%p"
+    >
+        <include
+            latin:keyboardLayout="@xml/rowkeys_hindi1"
+            latin:keyLabelFlags="disableAdditionalMoreKeys|disableKeyHintLabel" />
+        <Key
+            latin:keyStyle="deleteKeyStyle"
+            latin:keyWidth="fillBoth" />
+    </Row>
+    <Row
+        latin:keyWidth="7.6%p"
+    >
+        <include
+            latin:keyboardLayout="@xml/rowkeys_hindi2"
+            latin:keyXPos="4.500%p" />
+        <Key
+            latin:keyStyle="enterKeyStyle"
+            latin:keyWidth="fillBoth" />
+    </Row>
+    <Row
+        latin:keyWidth="6.923%p"
+    >
+        <Key
+            latin:keyStyle="shiftKeyStyle"
+            latin:keyWidth="10.0%p" />
+        <include
+            latin:keyboardLayout="@xml/rowkeys_hindi3" />
+        <include
+            latin:keyboardLayout="@xml/keys_comma_period" />
+        <include
+            latin:keyboardLayout="@xml/key_smiley" />
+    </Row>
+    <include
+        latin:keyboardLayout="@xml/row_qwerty4" />
+</merge>
diff --git a/java/res/xml-sw768dp/rows_hindi.xml b/java/res/xml-sw768dp/rows_hindi.xml
new file mode 100644
index 0000000..bb18842
--- /dev/null
+++ b/java/res/xml-sw768dp/rows_hindi.xml
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <include
+        latin:keyboardLayout="@xml/key_styles_common" />
+    <Row
+        latin:keyWidth="7.333%p"
+    >
+        <Key
+            latin:keyStyle="tabKeyStyle"
+            latin:keyLabelFlags="alignLeft" />
+        <include
+            latin:keyboardLayout="@xml/rowkeys_hindi1"
+            latin:keyLabelFlags="disableAdditionalMoreKeys|disableKeyHintLabel" />
+        <Key
+            latin:keyStyle="deleteKeyStyle"
+            latin:keyWidth="fillBoth" />
+    </Row>
+    <Row
+        latin:keyWidth="7.194%p"
+    >
+        <Key
+            latin:keyStyle="toSymbolKeyStyle"
+            latin:keyLabelFlags="alignLeft"
+            latin:keyWidth="9.375%p" />
+        <include
+            latin:keyboardLayout="@xml/rowkeys_hindi2" />
+        <Key
+            latin:keyStyle="enterKeyStyle"
+            latin:keyWidth="fillBoth" />
+    </Row>
+    <Row
+        latin:keyWidth="7.135%p"
+    >
+        <Key
+            latin:keyStyle="shiftKeyStyle"
+            latin:keyWidth="14.375%p" />
+        <include
+            latin:keyboardLayout="@xml/rowkeys_hindi3" />
+        <include
+            latin:keyboardLayout="@xml/keys_comma_period" />
+    </Row>
+    <include
+        latin:keyboardLayout="@xml/row_qwerty4" />
+</merge>
diff --git a/java/res/values/additional-proximitychars.xml b/java/res/xml/kbd_hindi.xml
similarity index 78%
rename from java/res/values/additional-proximitychars.xml
rename to java/res/xml/kbd_hindi.xml
index 03d10d5..0e69e3a 100644
--- a/java/res/values/additional-proximitychars.xml
+++ b/java/res/xml/kbd_hindi.xml
@@ -17,7 +17,10 @@
 ** limitations under the License.
 */
 -->
-<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
-    <string-array name="additional_proximitychars">
-    </string-array>
-</resources>
+
+<Keyboard
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <include
+        latin:keyboardLayout="@xml/rows_hindi" />
+</Keyboard>
diff --git a/java/res/xml/method.xml b/java/res/xml/method.xml
index cdcea0e..077574f 100644
--- a/java/res/xml/method.xml
+++ b/java/res/xml/method.xml
@@ -38,6 +38,7 @@
     fr: French/azerty
     fr_CA: French Canada/qwerty
     fr_CH: French Switzerland/qwertz
+    hi: Hindi/hindi
     hr: Croatian/qwertz
     hu: Hungarian/qwertz
     it: Italian/qwerty
@@ -171,6 +172,12 @@
     />
     <subtype android:icon="@drawable/ic_subtype_keyboard"
             android:label="@string/subtype_generic"
+            android:imeSubtypeLocale="hi"
+            android:imeSubtypeMode="keyboard"
+            android:imeSubtypeExtraValue="SupportTouchPositionCorrection"
+    />
+    <subtype android:icon="@drawable/ic_subtype_keyboard"
+            android:label="@string/subtype_generic"
             android:imeSubtypeLocale="hr"
             android:imeSubtypeMode="keyboard"
             android:imeSubtypeExtraValue="AsciiCapable,SupportTouchPositionCorrection"
diff --git a/java/res/xml/rowkeys_hindi1.xml b/java/res/xml/rowkeys_hindi1.xml
new file mode 100644
index 0000000..4f7a414
--- /dev/null
+++ b/java/res/xml/rowkeys_hindi1.xml
@@ -0,0 +1,197 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <switch>
+        <case
+            latin:keyboardSetElement="alphabetManualShifted|alphabetShiftLockShifted"
+        >
+            <!-- U+0914: "औ" DEVANAGARI LETTER AU
+                 U+0912/U+0902: "ऒं" DEVANAGARI LETTER SHORT O//DEVANAGARI SIGN ANUSVARA
+                 U+0967: "१" DEVANAGARI DIGIT ONE -->
+            <Key
+                latin:keyLabel="&#x0914;"
+                latin:moreKeys="&#x0912;&#x0902;,%"
+                latin:keyHintLabel="1"
+                latin:additionalMoreKeys="&#x0967;,1" />
+            <!-- U+0910: "ऐ" DEVANAGARI LETTER AI
+                 U+0910/U+0902: "ऐं" DEVANAGARI LETTER AI/DEVANAGARI SIGN ANUSVARA
+                 U+0968: "२" DEVANAGARI DIGIT TWO -->
+            <Key
+                latin:keyLabel="&#x0910;"
+                latin:moreKeys="&#x0910;&#x0902;,%"
+                latin:keyHintLabel="2"
+                latin:additionalMoreKeys="&#x0968;,2" />
+            <!-- U+0906: "आ" DEVANAGARI LETTER AA
+                 U+0906/U+0902: "आं" DEVANAGARI LETTER AA/DEVANAGARI SIGN ANUSVARA
+                 U+0906/U+0901: "आँ" DEVANAGARI LETTER AA/DEVANAGARI SIGN CANDRABINDU
+                 U+0969: "३" DEVANAGARI DIGIT THREE -->
+            <Key
+                latin:keyLabel="&#x0906;"
+                latin:moreKeys="&#x0906;&#x0902;,&#x0906;&#x0901;,%"
+                latin:keyHintLabel="3"
+                latin:additionalMoreKeys="&#x0969;,3" />
+            <!-- U+0908: "ई" DEVANAGARI LETTER II
+                 U+0908/U+0902: "ईं" DEVANAGARI LETTER II/DEVANAGARI SIGN ANUSVARA
+                 U+096A: "४" DEVANAGARI DIGIT FOUR -->
+            <Key
+                latin:keyLabel="&#x0908;"
+                latin:moreKeys="&#x0908;&#x0902;,%"
+                latin:keyHintLabel="4"
+                latin:additionalMoreKeys="&#x096A;,4" />
+            <!-- U+090A: "ऊ" DEVANAGARI LETTER UU
+                 U+090A/U+0902: "ऊं" DEVANAGARI LETTER UU/DEVANAGARI SIGN ANUSVARA
+                 U+090A/U+0901: "ऊँ" DEVANAGARI LETTER UU/DEVANAGARI SIGN CANDRABINDU
+                 U+096B: "५" DEVANAGARI DIGIT FIVE -->
+            <Key
+                latin:keyLabel="&#x090A;"
+                latin:moreKeys="&#x090A;&#x0902;,&#x090A;&#x0901;,%"
+                latin:keyHintLabel="5"
+                latin:additionalMoreKeys="&#x096B;,5" />
+            <!-- U+092D: "भ" DEVANAGARI LETTER BHA
+                 U+096C: "६" DEVANAGARI DIGIT SIX -->
+            <Key
+                latin:keyLabel="&#x092D;"
+                latin:keyHintLabel="6"
+                latin:additionalMoreKeys="&#x096C;,6" />
+            <!-- U+0903: "ः" DEVANAGARI SIGN VISARGA
+                 U+096D: "७" DEVANAGARI DIGIT SEVEN -->
+            <Key
+                latin:keyLabel="&#x0903;"
+                latin:keyHintLabel="7"
+                latin:additionalMoreKeys="&#x096D;,7" />
+            <!-- U+0918: "घ" DEVANAGARI LETTER GHA
+                 U+096E: "८" DEVANAGARI DIGIT EIGHT -->
+            <Key
+                latin:keyLabel="&#x0918;"
+                latin:keyHintLabel="8"
+                latin:additionalMoreKeys="&#x096E;,8" />
+            <!-- U+0927: "ध" DEVANAGARI LETTER DHA
+                 U+0915/U+094D/U+0937: "क्ष" DEVANAGARI LETTER KA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER SSA
+                 U+0936/U+094D/U+0930: "श्र" DEVANAGARI LETTER SHA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER RA
+                 U+096F: "९" DEVANAGARI DIGIT NINE -->
+            <Key
+                latin:keyLabel="&#x0927;"
+                latin:moreKeys="&#x0915;&#x094D;&#x0937;,&#x0936;&#x094D;&#x0930;,%"
+                latin:keyHintLabel="9"
+                latin:additionalMoreKeys="9" />
+            <!-- U+091D: "झ" DEVANAGARI LETTER JHA
+                 U+0966: "०" DEVANAGARI DIGIT ZERO -->
+            <Key
+                latin:keyLabel="&#x091D;"
+                latin:keyHintLabel="0"
+                latin:additionalMoreKeys="&#x0966;,0" />
+            <!-- U+0922: "ढ" DEVANAGARI LETTER DDHA -->
+            <Key
+                latin:keyLabel="&#x0922;" />
+        </case>
+        <default>
+            <!-- U+094C: "ौ" DEVANAGARI VOWEL SIGN AU
+                 U+094C/U+0902: "ौं" DEVANAGARI VOWEL SIGN AU/DEVANAGARI SIGN ANUSVARA
+                 U+0967: "१" DEVANAGARI DIGIT ONE -->
+            <Key
+                latin:keyLabel="&#x094C;"
+                latin:moreKeys="&#x094C;&#x0902;,%"
+                latin:keyHintLabel="1"
+                latin:additionalMoreKeys="&#x0967;,1" />
+            <!-- U+0948: "ै" DEVANAGARI VOWEL SIGN AI
+                 U+0948/U+0902: "ैं" DEVANAGARI VOWEL SIGN AI/DEVANAGARI SIGN ANUSVARA
+                 U+0968: "२" DEVANAGARI DIGIT TWO -->
+            <Key
+                latin:keyLabel="&#x0948;"
+                latin:moreKeys="&#x0948;&#x0902;,%"
+                latin:keyHintLabel="2"
+                latin:additionalMoreKeys="&#x0968;,2" />
+            <!-- U+093E: "ा" DEVANAGARI VOWEL SIGN AA
+                 U+093E/U+0902: "ां" DEVANAGARI VOWEL SIGN AA/DEVANAGARI SIGN ANUSVARA
+                 U+093E/U+0901: "ाँ" DEVANAGARI VOWEL SIGN AA/DEVANAGARI SIGN CANDRABINDU
+                 U+0969: "३" DEVANAGARI DIGIT THREE -->
+            <Key
+                latin:keyLabel="&#x093E;"
+                latin:moreKeys="&#x093E;&#x0902;,&#x093E;&#x0901;,%"
+                latin:keyHintLabel="3"
+                latin:additionalMoreKeys="&#x0969;,3" />
+            <!-- U+0940: "ी" DEVANAGARI VOWEL SIGN II
+                 U+0940/U+0902: "ीं" DEVANAGARI VOWEL SIGN II/DEVANAGARI SIGN ANUSVARA
+                 U+096A: "४" DEVANAGARI DIGIT FOUR -->
+            <Key
+                latin:keyLabel="&#x0940;"
+                latin:moreKeys="&#x0940;&#x0902;,%"
+                latin:keyHintLabel="4"
+                latin:additionalMoreKeys="&#x096A;,4" />
+            <!-- U+0942: "ू" DEVANAGARI VOWEL SIGN UU
+                 U+0942/U+0902: "ूं" DEVANAGARI VOWEL SIGN UU/DEVANAGARI SIGN ANUSVARA
+                 U+0942/U+0901: "ूँ" DEVANAGARI VOWEL SIGN UU/DEVANAGARI SIGN CANDRABINDU
+                 U+096B: "५" DEVANAGARI DIGIT FIVE -->
+            <Key
+                latin:keyLabel="&#x0942;"
+                latin:moreKeys="&#x0942;&#x0902;,&#x0942;&#x0901;,%"
+                latin:keyHintLabel="5"
+                latin:additionalMoreKeys="&#x096B;,5" />
+            <!-- U+092C: "ब" DEVANAGARI LETTER BA
+                 U+092C/U+0952: "ब॒" DEVANAGARI LETTER BA/DEVANAGARI STRESS SIGN ANUDATTA -->
+            <Key
+                latin:keyLabel="&#x092C;"
+                latin:moreKeys="&#x092C;&#x0952;,%"
+                latin:keyHintLabel="6"
+                latin:additionalMoreKeys="&#x096C;,6" />
+            <!-- U+0939: "ह" DEVANAGARI LETTER HA
+                 U+096D: "७" DEVANAGARI DIGIT SEVEN -->
+            <Key
+                latin:keyLabel="&#x0939;"
+                latin:keyHintLabel="7"
+                latin:additionalMoreKeys="&#x096D;,7" />
+            <!-- U+0917: "ग" DEVANAGARI LETTER GA
+                 U+091C/U+094D/U+091E: "ज्ञ" DEVANAGARI LETTER JA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER NYA
+                 U+0917/U+093C: "ग़" DEVANAGARI LETTER GA/DEVANAGARI SIGN NUKTA
+                 U+0917/U+0952: "ग॒" DEVANAGARI LETTER GA/DEVANAGARI STRESS SIGN ANUDATTA
+                 U+096E: "८" DEVANAGARI DIGIT EIGHT -->
+            <Key
+                latin:keyLabel="&#x0917;"
+                latin:moreKeys="&#x091C;&#x094D;&#x091E;,&#x0917;&#x093C;,&#x0917;&#x0952;,%"
+                latin:keyHintLabel="8"
+                latin:additionalMoreKeys="&#x096E;,8" />
+            <!-- U+0926: "द" DEVANAGARI LETTER DA
+                 U+096F: "९" DEVANAGARI DIGIT NINE -->
+            <Key
+                latin:keyLabel="&#x0926;"
+                latin:keyHintLabel="9"
+                latin:additionalMoreKeys="9" />
+            <!-- U+091C: "ज" DEVANAGARI LETTER JA
+                 U+091C/U+0952: "ज॒" DEVANAGARI LETTER JA/DEVANAGARI STRESS SIGN ANUDATTA
+                 U+091C/U+094D/U+091E: "ज्ञ" DEVANAGARI LETTER JA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER NYA
+                 U+091C/U+093C: "ज़" DEVANAGARI LETTER JA/DEVANAGARI SIGN NUKTA
+                 U+0966: "०" DEVANAGARI DIGIT ZERO -->
+            <Key
+                latin:keyLabel="&#x091C;"
+                latin:moreKeys="&#x091C;&#x0952;,&#x091C;&#x094D;&#x091E;,&#x091C;&#x093C;,%"
+                latin:keyHintLabel="0"
+                latin:additionalMoreKeys="&#x0966;,0" />
+            <!-- U+0921: "ड" DEVANAGARI LETTER DDA
+                 U+0921/U+0952: "ड॒" DEVANAGARI LETTER DDA/DEVANAGARI STRESS SIGN ANUDATTA
+                 U+0921/U+093C: "ड़" DEVANAGARI LETTER DDA/DEVANAGARI SIGN NUKTA -->
+            <Key
+                latin:keyLabel="&#x0921;"
+                latin:moreKeys="&#x0921;&#x0952;,&#x0921;&#x093C;" />
+         </default>
+    </switch>
+</merge>
diff --git a/java/res/xml/rowkeys_hindi2.xml b/java/res/xml/rowkeys_hindi2.xml
new file mode 100644
index 0000000..1bf6fd4
--- /dev/null
+++ b/java/res/xml/rowkeys_hindi2.xml
@@ -0,0 +1,142 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <switch>
+        <case
+            latin:keyboardSetElement="alphabetManualShifted|alphabetShiftLockShifted"
+        >
+            <!-- U+0913: "ओ" DEVANAGARI LETTER O
+                 U+0913/U+0902: "ओं" DEVANAGARI LETTER O/DEVANAGARI SIGN ANUSVARA
+                 U+0911: "ऑ" DEVANAGARI LETTER CANDRA O
+                 U+0912: "ऒ" DEVANAGARI LETTER SHORT O -->
+            <Key
+                latin:keyLabel="&#x0913;"
+                latin:moreKeys="&#x0913;&#x0902;,&#x0911;,&#x0912;" />
+            <!-- U+090F: "ए" DEVANAGARI LETTER E
+                 U+090F/U+0902: "एं" DEVANAGARI LETTER E/DEVANAGARI SIGN ANUSVARA
+                 U+090F/U+0901: "एँ" DEVANAGARI LETTER E/DEVANAGARI SIGN CANDRABINDU
+                 U+090D: "ऍ" DEVANAGARI LETTER CANDRA E
+                 U+090E: "ऎ" DEVANAGARI LETTER SHORT E -->
+            <Key
+                latin:keyLabel="&#x090F;"
+                latin:moreKeys="&#x090F;&#x0902;,&#x090F;&#x0901;,&#x090D;,&#x090E;" />
+            <!-- U+0905: "अ" DEVANAGARI LETTER A
+                 U+0905/U+0902: "अं" DEVANAGARI LETTER A/DEVANAGARI SIGN ANUSVARA
+                 U+0905/U+0901: "अँ" DEVANAGARI LETTER A/DEVANAGARI SIGN CANDRABINDU -->
+            <Key
+                latin:keyLabel="&#x0905;"
+                latin:moreKeys="&#x0905;&#x0902;,&#x0905;&#x0901;" />
+            <!-- U+0907: "इ" DEVANAGARI LETTER I
+                 U+0907/U+0902: "इं" DEVANAGARI LETTER I/DEVANAGARI SIGN ANUSVARA
+                 U+0907/U+0901: "इं" DEVANAGARI LETTER I/DEVANAGARI SIGN CANDRABINDU -->
+            <Key
+                latin:keyLabel="&#x0907;"
+                latin:moreKeys="&#x0907;&#x0902;,&#x0907;&#x0901;" />
+            <!-- U+0909: "उ" DEVANAGARI LETTER U
+                 U+0909/U+0902: "उं" DEVANAGARI LETTER U/DEVANAGARI SIGN ANUSVARA
+                 U+0909/U+0901: "उँ" DEVANAGARI LETTER U/DEVANAGARI SIGN CANDRABINDU -->
+            <Key
+                latin:keyLabel="&#x0909;"
+                latin:moreKeys="&#x0909;&#x0902;,&#x0909;&#x0901;" />
+            <!-- U+092B: "फ" DEVANAGARI LETTER PHA
+                 U+092B/U+093C: "फ़" DEVANAGARI LETTER PHA/DEVANAGARI SIGN NUKTA -->
+            <Key
+                latin:keyLabel="&#x092B;"
+                latin:moreKeys="&#x092B;&#x093C;" />
+            <!-- U+0931: "ऱ" DEVANAGARI LETTER RRA
+                 U+094D/U+0930: "्र" DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER RA
+                 U+0930/U+094D: "र्" DEVANAGARI LETTER RA/DEVANAGARI SIGN VIRAMA -->
+            <Key
+                latin:keyLabel="&#x0931;"
+                latin:moreKeys="&#x094D;&#x0930;,&#x0930;&#x094D;" />
+            <!-- U+0916: "ख" DEVANAGARI LETTER KHA
+                 U+0916/U+093C: "ख़" DEVANAGARI LETTER KHA/DEVANAGARI SIGN NUKTA -->
+            <Key
+                latin:keyLabel="&#x0916;"
+                latin:moreKeys="&#x0916;&#x093C;" />
+            <!-- U+0925: "थ" DEVANAGARI LETTER THA -->
+            <Key
+                latin:keyLabel="&#x0925;" />
+            <!-- U+091B: "छ" DEVANAGARI LETTER CHA -->
+            <Key
+                latin:keyLabel="&#x091B;" />
+            <!-- U+0920: "ठ" DEVANAGARI LETTER TTHA -->
+            <Key
+                latin:keyLabel="&#x0920;" />
+        </case>
+        <default>
+            <!-- U+094B: "ो" DEVANAGARI VOWEL SIGN O
+                 U+094B/U+0902: "қं" DEVANAGARI VOWEL SIGN O/DEVANAGARI SIGN ANUSVARA
+                 U+0949: "ॉ" DEVANAGARI VOWEL SIGN CANDRA O
+                 U+094A: "ॊ" DEVANAGARI VOWEL SIGN SHORT O -->
+            <Key
+                latin:keyLabel="&#x094B;"
+                latin:moreKeys="&#x094B;&#x0902;,&#x0949;,&#x094A;" />
+            <!-- U+0947: "े" DEVANAGARI VOWEL SIGN E
+                 U+0947/U+0902: "ें" DEVANAGARI VOWEL SIGN E/DEVANAGARI SIGN ANUSVARA -->
+            <Key
+                latin:keyLabel="&#x0947;"
+                latin:moreKeys="&#x0947;&#x0902;" />
+            <!-- U+094D: "्" DEVANAGARI SIGN VIRAMA -->
+            <Key
+                latin:keyLabel="&#x094D;" />
+            <!-- U+093F: "ि" DEVANAGARI VOWEL SIGN I
+                 U+093F/U+0902: "िं" DEVANAGARI VOWEL SIGN I/DEVANAGARI SIGN ANUSVARA -->
+            <Key
+                latin:keyLabel="&#x093F;"
+                latin:moreKeys="&#x093F;&#x0902;" />
+            <!-- U+0941: "ु" DEVANAGARI VOWEL SIGN U
+                 U+0941/U+0902: "ुं" DEVANAGARI VOWEL SIGN U/DEVANAGARI SIGN ANUSVARA
+                 U+0941/U+0901: "ुँ" DEVANAGARI VOWEL SIGN U/DEVANAGARI SIGN CANDRABINDU -->
+            <Key
+                latin:keyLabel="&#x0941;"
+                latin:moreKeys="&#x0941;&#x0902;,&#x0941;&#x0901;" />
+            <!-- U+092A: "प" DEVANAGARI LETTER PA -->
+            <Key
+                latin:keyLabel="&#x092A;" />
+            <!-- U+0930: "र" DEVANAGARI LETTER RA
+                 U+090B: "ऋ" DEVANAGARI LETTER VOCALIC R
+                 U+0930/U+093C: "ऱ" DEVANAGARI LETTER RA/DEVANAGARI SIGN NUKTA
+                 U+0960: "ॠ" DEVANAGARI LETTER VOCALIC RR -->
+            <Key
+                latin:keyLabel="&#x0930;"
+                latin:moreKeys="&#x090B;,&#x0930;&#x093C;,&#x0960;" />
+            <!-- U+0915: "क" DEVANAGARI LETTER KA
+                 U+0915/U+093C: "क़" DEVANAGARI LETTER KA/DEVANAGARI SIGN NUKTA -->
+            <Key
+                latin:keyLabel="&#x0915;"
+                latin:moreKeys="&#x0915;&#x093C;" />
+            <!-- U+0924: "त" DEVANAGARI LETTER TA
+                 U+0924/U+094D/U+0930: "त्र" DEVANAGARI LETTER TA/DEVANAGARI SIGN VIRAMA/DEVANAGARI LETTER RA -->
+            <Key
+                latin:keyLabel="&#x0924;"
+                latin:moreKeys="&#x0924;&#x094D;&#x0930;" />
+            <!-- U+091A: "च" DEVANAGARI LETTER CA -->
+            <Key
+                latin:keyLabel="&#x091A;" />
+            <!-- U+091F: "ट" DEVANAGARI LETTER TTA -->
+            <Key
+                latin:keyLabel="&#x091F;" />
+         </default>
+    </switch>
+</merge>
diff --git a/java/res/xml/rowkeys_hindi3.xml b/java/res/xml/rowkeys_hindi3.xml
new file mode 100644
index 0000000..e6b430f
--- /dev/null
+++ b/java/res/xml/rowkeys_hindi3.xml
@@ -0,0 +1,113 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <switch>
+        <case
+            latin:keyboardSetElement="alphabetManualShifted|alphabetShiftLockShifted"
+        >
+            <!-- U+0911: "ऑ" DEVANAGARI LETTER CANDRA O -->
+            <Key
+                latin:keyLabel="&#x0911;" />
+            <!-- U+090E: "ऎ" DEVANAGARI LETTER SHORT E -->
+            <Key
+                latin:keyLabel="&#x090E;" />
+            <!-- U+0901: "ँ" DEVANAGARI SIGN CANDRABINDU
+                 U+0945: "ॅ" DEVANAGARI VOWEL SIGN CANDRA E-->
+            <Key
+                latin:keyLabel="&#x0901;"
+                latin:moreKeys="&#x0945;" />
+            <!-- U+0923: "ण" DEVANAGARI LETTER NNA -->
+            <Key
+                latin:keyLabel="&#x0923;" />
+            <!-- U+0929: "ऩ" DEVANAGARI LETTER NNNA -->
+            <Key
+                latin:keyLabel="&#x0929;" />
+            <!-- U+0933: "ळ" DEVANAGARI LETTER LLA
+                 U+0934: "ऴ" DEVANAGARI LETTER LLLA -->
+            <Key
+                latin:keyLabel="&#x0933;"
+                latin:moreKeys="&#x0934;" />
+            <!-- U+0936: "श" DEVANAGARI LETTER SHA -->
+            <Key
+                latin:keyLabel="&#x0936;" />
+            <!-- U+0937: "ष" DEVANAGARI LETTER SSA -->
+            <Key
+                latin:keyLabel="&#x0937;" />
+            <!-- U+0943: "ृ" DEVANAGARI VOWEL SIGN VOCALIC R
+                 U+0944: "ॄ" DEVANAGARI VOWEL SIGN VOCALIC RR -->
+            <Key
+                latin:keyLabel="&#x0943;"
+                latin:moreKeys="&#x0944;" />
+            <!-- U+091E: "ञ" DEVANAGARI LETTER NYA -->
+            <Key
+                latin:keyLabel="&#x091E;" />
+        </case>
+        <default>
+            <!-- U+0949: "ॉ" DEVANAGARI VOWEL SIGN CANDRA O -->
+            <Key
+                latin:keyLabel="&#x0949;" />
+            <!-- U+0946: "ॆ" DEVANAGARI VOWEL SIGN SHORT E -->
+            <Key
+                latin:keyLabel="&#x0946;" />
+            <!-- U+0902: "ं" DEVANAGARI SIGN ANUSVARA -->
+            <Key
+                latin:keyLabel="&#x0902;" />
+            <!-- U+092E: "म" DEVANAGARI LETTER MA
+                 U+0950: "ॐ" DEVANAGARI OM -->
+            <Key
+                latin:keyLabel="&#x092E;"
+                latin:moreKeys="&#x0950;" />
+            <!-- U+0928: "न" DEVANAGARI LETTER NA
+                 U+091E: "ञ" DEVANAGARI LETTER NYA
+                 U+0919: "ङ" DEVANAGARI LETTER NGA
+                 U+0928/U+093C: "ऩ" DEVANAGARI LETTER NA/DEVANAGARI SIGN NUKTA -->
+            <Key
+                latin:keyLabel="&#x0928;"
+                latin:moreKeys="&#x091E;,&#x0919;,&#x0928;&#x093C;" />
+            <!-- U+0935: "व" DEVANAGARI LETTER VA -->
+            <Key
+                latin:keyLabel="&#x0935;" />
+            <!-- U+0932: "ल" DEVANAGARI LETTER LA
+                 U+090C: "ऌ" DEVANAGARI LETTER VOCALIC L
+                 U+0961: "ॡ" DEVANAGARI LETTER VOCALIC LL -->
+            <Key
+                latin:keyLabel="&#x0932;"
+                latin:moreKeys="&#x090C;,&#x0961;" />
+            <!-- U+0938: "स" DEVANAGARI LETTER SA -->
+            <Key
+                latin:keyLabel="&#x0938;" />
+            <!-- U+092F: "य" DEVANAGARI LETTER YA
+                 U+095F: "य़" DEVANAGARI LETTER YYA -->
+            <Key
+                latin:keyLabel="&#x092F;"
+                latin:moreKeys="&#x095F;" />
+            <!-- U+093C: "़" DEVANAGARI SIGN NUKTA
+                 U+097D: "ॽ" DEVANAGARI LETTER GLOTTAL STOP
+                 U+0970: "॰" DEVANAGARI ABBREVIATION SIGN
+                 U+093D: "ऽ" DEVANAGARI SIGN AVAGRAHA -->
+            <Key
+                latin:keyLabel="&#x093C;"
+                latin:moreKeys="&#x097D;,&#x0970;,&#x093D;" />
+         </default>
+    </switch>
+</merge>
diff --git a/java/res/xml/rows_hindi.xml b/java/res/xml/rows_hindi.xml
new file mode 100644
index 0000000..42d89b5
--- /dev/null
+++ b/java/res/xml/rows_hindi.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<merge
+    xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+    <include
+        latin:keyboardLayout="@xml/key_styles_common" />
+    <Row
+        latin:keyWidth="9.091%p"
+    >
+        <include
+            latin:keyboardLayout="@xml/rowkeys_hindi1" />
+    </Row>
+    <Row
+            latin:keyWidth="9.091%p"
+    >
+        <include
+            latin:keyboardLayout="@xml/rowkeys_hindi2" />
+    </Row>
+    <Row
+        latin:keyWidth="7.65%p"
+    >
+        <Key
+            latin:keyStyle="shiftKeyStyle"
+            latin:keyWidth="11.75%p" />
+        <include
+            latin:keyboardLayout="@xml/rowkeys_hindi3" />
+        <Key
+            latin:keyStyle="deleteKeyStyle"
+            latin:keyWidth="fillRight" />
+    </Row>
+    <include
+        latin:keyboardLayout="@xml/row_qwerty4" />
+</merge>
diff --git a/java/src/com/android/inputmethod/keyboard/KeyDetector.java b/java/src/com/android/inputmethod/keyboard/KeyDetector.java
index ea3f623..13e909c 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyDetector.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyDetector.java
@@ -16,17 +16,9 @@
 
 package com.android.inputmethod.keyboard;
 
-import android.util.Log;
-
-import java.util.Arrays;
-import java.util.List;
 
 public class KeyDetector {
-    private static final String TAG = KeyDetector.class.getSimpleName();
-    private static final boolean DEBUG = false;
-
     public static final int NOT_A_CODE = -1;
-    private static final int ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE = 2;
 
     private final int mKeyHysteresisDistanceSquared;
 
@@ -34,12 +26,6 @@
     private int mCorrectionX;
     private int mCorrectionY;
     private boolean mProximityCorrectOn;
-    private int mProximityThresholdSquare;
-
-    // working area
-    private static final int MAX_NEARBY_KEYS = 12;
-    private final int[] mDistances = new int[MAX_NEARBY_KEYS];
-    private final Key[] mNeighborKeys = new Key[MAX_NEARBY_KEYS];
 
     /**
      * This class handles key detection.
@@ -57,8 +43,6 @@
         mCorrectionX = (int)correctionX;
         mCorrectionY = (int)correctionY;
         mKeyboard = keyboard;
-        final int threshold = keyboard.mMostCommonKeyWidth;
-        mProximityThresholdSquare = threshold * threshold;
     }
 
     public int getKeyHysteresisDistanceSquared() {
@@ -87,169 +71,11 @@
         return mProximityCorrectOn;
     }
 
-    public void setProximityThreshold(int threshold) {
-        mProximityThresholdSquare = threshold * threshold;
-    }
-
     public boolean alwaysAllowsSlidingInput() {
         return false;
     }
 
     /**
-     * Computes maximum size of the array that can contain all nearby key codes returned by
-     * {@link #getNearbyCodes}.
-     *
-     * @return Returns maximum size of the array that can contain all nearby key codes returned
-     *         by {@link #getNearbyCodes}.
-     */
-    protected int getMaxNearbyKeys() {
-        return MAX_NEARBY_KEYS;
-    }
-
-    /**
-     * Allocates array that can hold all key codes returned by {@link #getNearbyCodes}
-     * method. The maximum size of the array should be computed by {@link #getMaxNearbyKeys}.
-     *
-     * @return Allocates and returns an array that can hold all key codes returned by
-     *         {@link #getNearbyCodes} method. All elements in the returned array are
-     *         initialized by {@link #NOT_A_CODE} value.
-     */
-    public int[] newCodeArray() {
-        int[] codes = new int[getMaxNearbyKeys()];
-        Arrays.fill(codes, NOT_A_CODE);
-        return codes;
-    }
-
-    private void initializeNearbyKeys() {
-        Arrays.fill(mDistances, Integer.MAX_VALUE);
-        Arrays.fill(mNeighborKeys, null);
-    }
-
-    /**
-     * Insert the key into nearby keys buffer and sort nearby keys by ascending order of distance.
-     * If the distance of two keys are the same, the key which the point is on should be considered
-     * as a closer one.
-     *
-     * @param key the key to be inserted into the nearby keys buffer.
-     * @param distance distance between the key's edge and user touched point.
-     * @param isOnKey true if the point is on the key.
-     * @return order of the key in the nearby buffer, 0 if it is the nearest key.
-     */
-    private int sortNearbyKeys(Key key, int distance, boolean isOnKey) {
-        final int[] distances = mDistances;
-        final Key[] neighborKeys = mNeighborKeys;
-        for (int insertPos = 0; insertPos < distances.length; insertPos++) {
-            final int comparingDistance = distances[insertPos];
-            if (distance < comparingDistance || (distance == comparingDistance && isOnKey)) {
-                final int nextPos = insertPos + 1;
-                if (nextPos < distances.length) {
-                    System.arraycopy(distances, insertPos, distances, nextPos,
-                            distances.length - nextPos);
-                    System.arraycopy(neighborKeys, insertPos, neighborKeys, nextPos,
-                            neighborKeys.length - nextPos);
-                }
-                distances[insertPos] = distance;
-                neighborKeys[insertPos] = key;
-                return insertPos;
-            }
-        }
-        return distances.length;
-    }
-
-    private void getNearbyKeyCodes(final int primaryCode, final int[] allCodes) {
-        final Key[] neighborKeys = mNeighborKeys;
-        final int maxCodesSize = allCodes.length;
-
-        // allCodes[0] should always have the key code even if it is a non-letter key.
-        if (neighborKeys[0] == null) {
-            allCodes[0] = NOT_A_CODE;
-            return;
-        }
-
-        int numCodes = 0;
-        for (int j = 0; j < neighborKeys.length && numCodes < maxCodesSize; j++) {
-            final Key key = neighborKeys[j];
-            if (key == null)
-                break;
-            final int code = key.mCode;
-            // filter out a non-letter key from nearby keys
-            if (code < Keyboard.CODE_SPACE)
-                continue;
-            allCodes[numCodes++] = code;
-        }
-        if (maxCodesSize <= numCodes) {
-            return;
-        }
-
-        final int code = (primaryCode == NOT_A_CODE) ? allCodes[0] : primaryCode;
-        if (code == NOT_A_CODE) {
-            return;
-        }
-        final List<Integer> additionalChars = mKeyboard.getAdditionalProximityChars().get(code);
-        if (additionalChars == null || additionalChars.size() == 0) {
-            return;
-        }
-        int currentCodesSize = numCodes;
-        allCodes[numCodes++] = ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE;
-        if (maxCodesSize <= numCodes) {
-            return;
-        }
-        // TODO: This is O(N^2). Assuming additionalChars.size() is up to 4 or 5.
-        for (int i = 0; i < additionalChars.size(); ++i) {
-            final int additionalChar = additionalChars.get(i);
-            boolean contains = false;
-            for (int j = 0; j < currentCodesSize; ++j) {
-                if (additionalChar == allCodes[j]) {
-                    contains = true;
-                    break;
-                }
-            }
-            if (!contains) {
-                allCodes[numCodes++] = additionalChar;
-                if (maxCodesSize <= numCodes) {
-                    return;
-                }
-            }
-        }
-    }
-
-    /**
-     * Finds all possible nearby key codes around a touch event point and returns the nearest key.
-     * The algorithm to determine the nearby keys depends on the threshold set by
-     * {@link #setProximityThreshold(int)} and the mode set by
-     * {@link #setProximityCorrectionEnabled(boolean)}.
-     *
-     * @param x The x-coordinate of a touch point
-     * @param y The y-coordinate of a touch point
-     * @param allCodes All nearby key codes except functional key are returned in this array
-     */
-    // TODO: Move this method to native code.
-    public void getNearbyCodes(int x, int y, final int[] allCodes) {
-        final int touchX = getTouchX(x);
-        final int touchY = getTouchY(y);
-
-        initializeNearbyKeys();
-        Key primaryKey = null;
-        for (final Key key : mKeyboard.getNearestKeys(touchX, touchY)) {
-            final boolean isOnKey = key.isOnKey(touchX, touchY);
-            final int distance = key.squaredDistanceToEdge(touchX, touchY);
-            if (isOnKey || (mProximityCorrectOn && distance < mProximityThresholdSquare)) {
-                final int insertedPosition = sortNearbyKeys(key, distance, isOnKey);
-                if (insertedPosition == 0 && isOnKey) {
-                    primaryKey = key;
-                }
-            }
-        }
-
-        getNearbyKeyCodes(primaryKey != null ? primaryKey.mCode : NOT_A_CODE, allCodes);
-        if (DEBUG) {
-            Log.d(TAG, "x=" + x + " y=" + y
-                    + " primary=" + printableCode(primaryKey)
-                    + " codes=" + printableCodes(allCodes));
-        }
-    }
-
-    /**
      * Detect the key whose hitbox the touch point is in.
      *
      * @param x The x-coordinate of a touch point
@@ -260,12 +86,19 @@
         final int touchX = getTouchX(x);
         final int touchY = getTouchY(y);
 
-        for (final Key key : mKeyboard.getNearestKeys(touchX, touchY)) {
-            if (key.isOnKey(touchX, touchY)) {
-                return key;
+        int minDistance = Integer.MAX_VALUE;
+        Key primaryKey = null;
+        for (final Key key: mKeyboard.getNearestKeys(touchX, touchY)) {
+            final boolean isOnKey = key.isOnKey(touchX, touchY);
+            final int distance = key.squaredDistanceToEdge(touchX, touchY);
+            // To take care of hitbox overlaps, we compare mCode here too.
+            if (primaryKey == null || distance < minDistance
+                    || (distance == minDistance && isOnKey && key.mCode > primaryKey.mCode)) {
+                minDistance = distance;
+                primaryKey = key;
             }
         }
-        return null;
+        return primaryKey;
     }
 
     public static String printableCode(Key key) {
@@ -277,14 +110,9 @@
         boolean addDelimiter = false;
         for (final int code : codes) {
             if (code == NOT_A_CODE) break;
-            if (code == ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE) {
-                sb.append(" | ");
-                addDelimiter = false;
-            } else {
-                if (addDelimiter) sb.append(", ");
-                sb.append(Keyboard.printableCode(code));
-                addDelimiter = true;
-            }
+            if (addDelimiter) sb.append(", ");
+            sb.append(Keyboard.printableCode(code));
+            addDelimiter = true;
         }
         return "[" + sb + "]";
     }
diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java
index 973f64b..2b1cc43 100644
--- a/java/src/com/android/inputmethod/keyboard/Keyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java
@@ -20,7 +20,6 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
-import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.util.Log;
@@ -42,8 +41,6 @@
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
 
 /**
  * Loads an XML description of a keyboard and stores the attributes of the keys. A keyboard
@@ -133,8 +130,6 @@
 
     private final ProximityInfo mProximityInfo;
 
-    private final Map<Integer, List<Integer>> mAdditionalProximityChars;
-
     public Keyboard(Params params) {
         mId = params.mId;
         mThemeId = params.mThemeId;
@@ -153,12 +148,10 @@
         mAltCodeKeysWhileTyping = params.mAltCodeKeysWhileTyping.toArray(
                 new Key[params.mAltCodeKeysWhileTyping.size()]);
         mIconsSet = params.mIconsSet;
-        mAdditionalProximityChars = params.mAdditionalProximityChars;
 
         mProximityInfo = new ProximityInfo(params.mId.mLocale.toString(),
                 params.GRID_WIDTH, params.GRID_HEIGHT, mOccupiedWidth, mOccupiedHeight,
-                mMostCommonKeyWidth, mMostCommonKeyHeight, mKeys, params.mTouchPositionCorrection,
-                params.mAdditionalProximityChars);
+                mMostCommonKeyWidth, mMostCommonKeyHeight, mKeys, params.mTouchPositionCorrection);
     }
 
     public ProximityInfo getProximityInfo() {
@@ -230,9 +223,6 @@
         public final ArrayList<Key> mShiftKeys = new ArrayList<Key>();
         public final ArrayList<Key> mAltCodeKeysWhileTyping = new ArrayList<Key>();
         public final KeyboardIconsSet mIconsSet = new KeyboardIconsSet();
-        // TODO: Should be in Key instead of Keyboard.Params?
-        public final Map<Integer, List<Integer>> mAdditionalProximityChars =
-                new HashMap<Integer, List<Integer>>();
 
         public KeyboardSet.KeysCache mKeysCache;
 
@@ -368,10 +358,6 @@
         return mProximityInfo.getNearestKeys(adjustedX, adjustedY);
     }
 
-    public Map<Integer, List<Integer>> getAdditionalProximityChars() {
-        return mAdditionalProximityChars;
-    }
-
     public static String printableCode(int code) {
         switch (code) {
         case CODE_SHIFT: return "shift";
@@ -630,7 +616,6 @@
             mParams = params;
 
             setTouchPositionCorrectionData(context, params);
-            setAdditionalProximityChars(context, params);
 
             params.GRID_WIDTH = res.getInteger(R.integer.config_keyboard_grid_width);
             params.GRID_HEIGHT = res.getInteger(R.integer.config_keyboard_grid_height);
@@ -653,25 +638,6 @@
             params.mTouchPositionCorrection.load(data);
         }
 
-        private static void setAdditionalProximityChars(Context context, Params params) {
-            final String[] additionalChars =
-                    context.getResources().getStringArray(R.array.additional_proximitychars);
-            int currentPrimaryIndex = 0;
-            for (int i = 0; i < additionalChars.length; ++i) {
-                final String additionalChar = additionalChars[i];
-                if (TextUtils.isEmpty(additionalChar)) {
-                    currentPrimaryIndex = 0;
-                } else if (currentPrimaryIndex == 0) {
-                    currentPrimaryIndex = additionalChar.charAt(0);
-                    params.mAdditionalProximityChars.put(
-                            currentPrimaryIndex, new ArrayList<Integer>());
-                } else if (currentPrimaryIndex != 0) {
-                    final int c = additionalChar.charAt(0);
-                    params.mAdditionalProximityChars.get(currentPrimaryIndex).add(c);
-                }
-            }
-        }
-
         public void setAutoGenerate(KeyboardSet.KeysCache keysCache) {
             mParams.mKeysCache = keysCache;
         }
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardSet.java
index 680ff0d..52096c8 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSet.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSet.java
@@ -42,7 +42,6 @@
 import java.lang.ref.SoftReference;
 import java.util.HashMap;
 import java.util.Locale;
-import java.util.Map;
 
 /**
  * This class represents a set of keyboards. Each of them represents a different keyboard
@@ -75,7 +74,7 @@
     }
 
     public static class KeysCache {
-        private final Map<Key, Key> mMap;
+        private final HashMap<Key, Key> mMap;
 
         public KeysCache() {
             mMap = new HashMap<Key, Key>();
@@ -108,7 +107,7 @@
         int mOrientation;
         int mWidth;
         // KeyboardSet element id to keyboard layout XML id map.
-        final Map<Integer, Integer> mKeyboardSetElementIdToXmlIdMap =
+        final HashMap<Integer, Integer> mKeyboardSetElementIdToXmlIdMap =
                 new HashMap<Integer, Integer>();
         Params() {}
     }
diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
index 3438425..b869059e 100644
--- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java
@@ -480,7 +480,6 @@
         super.setKeyboard(keyboard);
         mKeyDetector.setKeyboard(
                 keyboard, -getPaddingLeft(), -getPaddingTop() + mVerticalCorrection);
-        mKeyDetector.setProximityThreshold(keyboard.mMostCommonKeyWidth);
         PointerTracker.setKeyDetector(mKeyDetector);
         mTouchScreenRegulator.setKeyboard(keyboard);
         mMoreKeysPanelCache.clear();
diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java b/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java
index 6c8d020..cd4e300 100644
--- a/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java
+++ b/java/src/com/android/inputmethod/keyboard/MoreKeysDetector.java
@@ -33,34 +33,6 @@
     }
 
     @Override
-    protected int getMaxNearbyKeys() {
-        // No nearby key will be returned.
-        return 1;
-    }
-
-    @Override
-    public void getNearbyCodes(int x, int y, final int[] allCodes) {
-        final int touchX = getTouchX(x);
-        final int touchY = getTouchY(y);
-
-        Key nearestKey = null;
-        int nearestDist = (y < 0) ? mSlideAllowanceSquareTop : mSlideAllowanceSquare;
-        for (final Key key : getKeyboard().mKeys) {
-            final int dist = key.squaredDistanceToEdge(touchX, touchY);
-            if (dist < nearestDist) {
-                nearestKey = key;
-                nearestDist = dist;
-            }
-        }
-
-        if (nearestKey != null) {
-            allCodes[0] = nearestKey.mCode;
-        } else {
-            allCodes[0] = NOT_A_CODE;
-        }
-    }
-
-    @Override
     public Key detectHitKey(int x, int y) {
         final int touchX = getTouchX(x);
         final int touchY = getTouchY(y);
diff --git a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
index 61d75e2..442413c 100644
--- a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
+++ b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
@@ -24,10 +24,7 @@
 import com.android.inputmethod.latin.spellcheck.SpellCheckerProximityInfo;
 
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
 
 public class ProximityInfo {
     public static final int MAX_PROXIMITY_CHARS_SIZE = 16;
@@ -50,8 +47,7 @@
 
     ProximityInfo(String localeStr, int gridWidth, int gridHeight, int minWidth, int height,
             int mostCommonKeyWidth, int mostCommonKeyHeight, final Key[] keys,
-            TouchPositionCorrection touchPositionCorrection,
-            Map<Integer, List<Integer>> additionalProximityChars) {
+            TouchPositionCorrection touchPositionCorrection) {
         if (TextUtils.isEmpty(localeStr)) {
             mLocaleStr = "";
         } else {
@@ -72,12 +68,11 @@
             return;
         }
         computeNearestNeighbors(
-                mostCommonKeyWidth, keys, touchPositionCorrection, additionalProximityChars);
+                mostCommonKeyWidth, keys, touchPositionCorrection);
     }
 
     public static ProximityInfo createDummyProximityInfo() {
-        return new ProximityInfo("", 1, 1, 1, 1, 1, 1, EMPTY_KEY_ARRAY, null,
-                Collections.<Integer, List<Integer>> emptyMap());
+        return new ProximityInfo("", 1, 1, 1, 1, 1, 1, EMPTY_KEY_ARRAY, null);
     }
 
     public static ProximityInfo createSpellCheckerProximityInfo(final int[] proximity) {
@@ -184,8 +179,7 @@
     }
 
     private void computeNearestNeighbors(int defaultWidth, final Key[] keys,
-            TouchPositionCorrection touchPositionCorrection,
-            Map<Integer, List<Integer>> additionalProximityChars) {
+            TouchPositionCorrection touchPositionCorrection) {
         final HashMap<Integer, Key> keyCodeMap = new HashMap<Integer, Key>();
         for (final Key key : keys) {
             keyCodeMap.put(key.mCode, key);
@@ -207,27 +201,6 @@
                         neighborKeys[count++] = key;
                     }
                 }
-                int currentCodesSize = count;
-                for (int i = 0; i < currentCodesSize; ++i) {
-                    final int c = neighborKeys[i].mCode;
-                    final List<Integer> additionalChars = additionalProximityChars.get(c);
-                    if (additionalChars == null || additionalChars.size() == 0) {
-                        continue;
-                    }
-                    for (int j = 0; j < additionalChars.size(); ++j) {
-                        final int additionalChar = additionalChars.get(j);
-                        boolean contains = false;
-                        for (int k = 0; k < count; ++k) {
-                            if(additionalChar == neighborKeys[k].mCode) {
-                                contains = true;
-                                break;
-                            }
-                        }
-                        if (!contains) {
-                            neighborKeys[count++] = keyCodeMap.get(additionalChar);
-                        }
-                    }
-                }
                 mGridNeighbors[(y / mCellHeight) * mGridWidth + (x / mCellWidth)] =
                         Arrays.copyOfRange(neighborKeys, 0, count);
             }
diff --git a/java/src/com/android/inputmethod/keyboard/SuddenJumpingTouchEventHandler.java b/java/src/com/android/inputmethod/keyboard/SuddenJumpingTouchEventHandler.java
index 62a9259..347383f 100644
--- a/java/src/com/android/inputmethod/keyboard/SuddenJumpingTouchEventHandler.java
+++ b/java/src/com/android/inputmethod/keyboard/SuddenJumpingTouchEventHandler.java
@@ -17,12 +17,12 @@
 package com.android.inputmethod.keyboard;
 
 import android.content.Context;
-import android.os.Build;
 import android.util.Log;
 import android.view.MotionEvent;
 
 import com.android.inputmethod.latin.LatinImeLogger;
 import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.Utils;
 
 public class SuddenJumpingTouchEventHandler {
     private static final String TAG = SuddenJumpingTouchEventHandler.class.getSimpleName();
@@ -49,18 +49,8 @@
 
     public SuddenJumpingTouchEventHandler(Context context, ProcessMotionEvent view) {
         mView = view;
-        final String[] deviceList = context.getResources().getStringArray(
-                R.array.sudden_jumping_touch_event_device_list);
-        mNeedsSuddenJumpingHack = needsSuddenJumpingHack(Build.HARDWARE, deviceList);
-    }
-
-    private static boolean needsSuddenJumpingHack(String deviceName, String[] deviceList) {
-        for (String device : deviceList) {
-            if (device.equalsIgnoreCase(deviceName)) {
-                return true;
-            }
-        }
-        return false;
+        mNeedsSuddenJumpingHack = Boolean.parseBoolean(Utils.getDeviceOverrideValue(
+                context.getResources(), R.array.sudden_jumping_touch_event_device_list, "false"));
     }
 
     public void setKeyboard(Keyboard newKeyboard) {
diff --git a/java/src/com/android/inputmethod/latin/AutoCorrection.java b/java/src/com/android/inputmethod/latin/AutoCorrection.java
index 9c35f8f..ef88f99 100644
--- a/java/src/com/android/inputmethod/latin/AutoCorrection.java
+++ b/java/src/com/android/inputmethod/latin/AutoCorrection.java
@@ -20,7 +20,7 @@
 import android.util.Log;
 
 import java.util.ArrayList;
-import java.util.Map;
+import java.util.HashMap;
 
 public class AutoCorrection {
     private static final boolean DBG = LatinImeLogger.sDBG;
@@ -30,7 +30,7 @@
         // Purely static class: can't instantiate.
     }
 
-    public static CharSequence computeAutoCorrectionWord(Map<String, Dictionary> dictionaries,
+    public static CharSequence computeAutoCorrectionWord(HashMap<String, Dictionary> dictionaries,
             WordComposer wordComposer, ArrayList<CharSequence> suggestions, int[] sortedScores,
             CharSequence consideredWord, double autoCorrectionThreshold,
             CharSequence whitelistedWord) {
@@ -47,7 +47,7 @@
     }
 
     public static boolean isValidWord(
-            Map<String, Dictionary> dictionaries, CharSequence word, boolean ignoreCase) {
+            HashMap<String, Dictionary> dictionaries, CharSequence word, boolean ignoreCase) {
         if (TextUtils.isEmpty(word)) {
             return false;
         }
@@ -72,7 +72,7 @@
     }
 
     public static boolean allowsToBeAutoCorrected(
-            Map<String, Dictionary> dictionaries, CharSequence word, boolean ignoreCase) {
+            HashMap<String, Dictionary> dictionaries, CharSequence word, boolean ignoreCase) {
         final WhitelistDictionary whitelistDictionary =
                 (WhitelistDictionary)dictionaries.get(Suggest.DICT_KEY_WHITELIST);
         // If "word" is in the whitelist dictionary, it should not be auto corrected.
@@ -87,9 +87,9 @@
         return whiteListedWord != null;
     }
 
-    private static boolean hasAutoCorrectionForConsideredWord(Map<String, Dictionary> dictionaries,
-            WordComposer wordComposer, ArrayList<CharSequence> suggestions,
-            CharSequence consideredWord) {
+    private static boolean hasAutoCorrectionForConsideredWord(
+            HashMap<String, Dictionary> dictionaries, WordComposer wordComposer,
+            ArrayList<CharSequence> suggestions, CharSequence consideredWord) {
         if (TextUtils.isEmpty(consideredWord)) return false;
         return wordComposer.size() > 1 && suggestions.size() > 0
                 && !allowsToBeAutoCorrected(dictionaries, consideredWord, false);
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index 31ff4e7..dfc8c8e 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -149,6 +149,9 @@
         int count = getBigramsNative(mNativeDict, chars, chars.length, mInputCodes, codesSize,
                 mOutputChars_bigrams, mBigramScores, MAX_WORD_LENGTH, MAX_BIGRAMS,
                 MAX_PROXIMITY_CHARS_SIZE);
+        if (count > MAX_BIGRAMS) {
+            count = MAX_BIGRAMS;
+        }
 
         for (int j = 0; j < count; ++j) {
             if (codesSize > 0 && mBigramScores[j] < 1) break;
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
index 9ffc7d0..8ec4405 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
@@ -196,8 +196,8 @@
             } finally {
                 // Ignore exceptions while closing files.
                 try {
-                    // afd.close() will close inputStream, we should not call inputStream.close().
-                    if (null != afd) afd.close();
+                    // inputStream.close() will close afd, we should not call afd.close().
+                    if (null != inputStream) inputStream.close();
                 } catch (Exception e) {
                     Log.e(TAG, "Exception while closing a cross-process file descriptor : " + e);
                 }
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
index 79441c5..1c24cd1 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
@@ -25,7 +25,6 @@
 
 import java.io.File;
 import java.util.ArrayList;
-import java.util.List;
 import java.util.Locale;
 
 /**
@@ -264,9 +263,9 @@
      * - Gets a file name from the fallback resource passed as an argument.
      * If that fails:
      * - Returns null.
-     * @return The address of a valid file, or null.
+     * @return The list of addresses of valid dictionary files, or null.
      */
-    public static List<AssetFileAddress> getDictionaryFiles(final Locale locale,
+    public static ArrayList<AssetFileAddress> getDictionaryFiles(final Locale locale,
             final Context context, final int fallbackResId) {
 
         // cacheWordListsFromContentProvider returns the list of files it copied to local
diff --git a/java/src/com/android/inputmethod/latin/DebugSettings.java b/java/src/com/android/inputmethod/latin/DebugSettings.java
index 870b33f..23d63b4 100644
--- a/java/src/com/android/inputmethod/latin/DebugSettings.java
+++ b/java/src/com/android/inputmethod/latin/DebugSettings.java
@@ -30,7 +30,7 @@
 public class DebugSettings extends PreferenceActivity
         implements SharedPreferences.OnSharedPreferenceChangeListener {
 
-    private static final String TAG = "DebugSettings";
+    private static final String TAG = DebugSettings.class.getSimpleName();
     private static final String DEBUG_MODE_KEY = "debug_mode";
     public static final String FORCE_NON_DISTINCT_MULTITOUCH_KEY = "force_non_distinct_multitouch";
 
diff --git a/java/src/com/android/inputmethod/latin/DictionaryCollection.java b/java/src/com/android/inputmethod/latin/DictionaryCollection.java
index c19a5a7..5de770a 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryCollection.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryCollection.java
@@ -22,7 +22,6 @@
 
 import java.util.Collection;
 import java.util.Collections;
-import java.util.List;
 import java.util.concurrent.CopyOnWriteArrayList;
 
 /**
@@ -30,7 +29,7 @@
  */
 public class DictionaryCollection extends Dictionary {
     private final String TAG = DictionaryCollection.class.getSimpleName();
-    protected final List<Dictionary> mDictionaries;
+    protected final CopyOnWriteArrayList<Dictionary> mDictionaries;
 
     public DictionaryCollection() {
         mDictionaries = new CopyOnWriteArrayList<Dictionary>();
diff --git a/java/src/com/android/inputmethod/latin/DictionaryFactory.java b/java/src/com/android/inputmethod/latin/DictionaryFactory.java
index 7a81f7b..77c685c 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryFactory.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryFactory.java
@@ -22,8 +22,8 @@
 import android.util.Log;
 
 import java.io.File;
+import java.util.ArrayList;
 import java.util.LinkedList;
-import java.util.List;
 import java.util.Locale;
 
 /**
@@ -52,8 +52,8 @@
             return new DictionaryCollection(createBinaryDictionary(context, fallbackResId, locale));
         }
 
-        final List<Dictionary> dictList = new LinkedList<Dictionary>();
-        final List<AssetFileAddress> assetFileList =
+        final LinkedList<Dictionary> dictList = new LinkedList<Dictionary>();
+        final ArrayList<AssetFileAddress> assetFileList =
                 BinaryDictionaryGetter.getDictionaryFiles(locale, context, fallbackResId);
         if (null != assetFileList) {
             for (final AssetFileAddress f : assetFileList) {
diff --git a/java/src/com/android/inputmethod/latin/InputAttributes.java b/java/src/com/android/inputmethod/latin/InputAttributes.java
index f7afed2..06c70c4 100644
--- a/java/src/com/android/inputmethod/latin/InputAttributes.java
+++ b/java/src/com/android/inputmethod/latin/InputAttributes.java
@@ -95,6 +95,7 @@
         }
     }
 
+    @SuppressWarnings("unused")
     private void dumpFlags(final int inputType) {
         Log.i(TAG, "Input class:");
         final int inputClass = inputType & InputType.TYPE_MASK_CLASS;
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 7c08377..e67f0ea 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -73,7 +73,6 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.List;
 import java.util.Locale;
 
 /**
@@ -203,8 +202,7 @@
     private boolean mShouldSwitchToLastSubtype = true;
 
     private UserDictionary mUserDictionary;
-    private UserBigramDictionary mUserBigramDictionary;
-    private UserUnigramDictionary mUserUnigramDictionary;
+    private UserHistoryDictionary mUserHistoryDictionary;
     private boolean mIsUserDictionaryAvailable;
 
     private LastComposedWord mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
@@ -528,13 +526,9 @@
 
         resetContactsDictionary(oldContactsDictionary);
 
-        mUserUnigramDictionary
-                = new UserUnigramDictionary(this, this, localeStr, Suggest.DIC_USER_UNIGRAM);
-        mSuggest.setUserUnigramDictionary(mUserUnigramDictionary);
-
-        mUserBigramDictionary
-                = new UserBigramDictionary(this, this, localeStr, Suggest.DIC_USER_BIGRAM);
-        mSuggest.setUserBigramDictionary(mUserBigramDictionary);
+        mUserHistoryDictionary
+                = new UserHistoryDictionary(this, this, localeStr, Suggest.DIC_USER_HISTORY);
+        mSuggest.setUserHistoryDictionary(mUserHistoryDictionary);
 
         LocaleUtils.setSystemLocale(res, savedLocale);
     }
@@ -776,8 +770,7 @@
 
         KeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
         if (inputView != null) inputView.closing();
-        if (mUserUnigramDictionary != null) mUserUnigramDictionary.flushPendingWrites();
-        if (mUserBigramDictionary != null) mUserBigramDictionary.flushPendingWrites();
+        if (mUserHistoryDictionary != null) mUserHistoryDictionary.flushPendingWrites();
     }
 
     private void onFinishInputViewInternal(boolean finishingInput) {
@@ -923,7 +916,7 @@
                 return;
             }
 
-            final List<SuggestedWords.SuggestedWordInfo> applicationSuggestedWords =
+            final ArrayList<SuggestedWords.SuggestedWordInfo> applicationSuggestedWords =
                     SuggestedWords.getFromApplicationSpecifiedCompletions(
                             applicationSpecifiedCompletions);
             final SuggestedWords suggestedWords = new SuggestedWords(
@@ -1295,7 +1288,7 @@
             mDeleteCount++;
             mExpectingUpdateSelection = true;
             mShouldSwitchToLastSubtype = true;
-            LatinImeLogger.logOnDelete();
+            LatinImeLogger.logOnDelete(x, y);
             break;
         case Keyboard.CODE_SHIFT:
         case Keyboard.CODE_SWITCH_ALPHA_SYMBOL:
@@ -1995,9 +1988,6 @@
         setSuggestionStripShown(isSuggestionsStripVisible());
     }
 
-    /**
-     * Adds to the UserBigramDictionary and/or UserUnigramDictionary
-     */
     private void addToUserHistoryDictionary(final CharSequence suggestion) {
         if (suggestion == null || suggestion.length() < 1) return;
 
@@ -2009,19 +1999,16 @@
             return;
         }
 
-        if (null != mUserUnigramDictionary) {
-            mUserUnigramDictionary.addUnigram(suggestion.toString());
-        }
-
-        if (mUserBigramDictionary != null) {
+        if (mUserHistoryDictionary != null) {
             final InputConnection ic = getCurrentInputConnection();
+            final CharSequence prevWord;
             if (null != ic) {
-                final CharSequence prevWord =
-                        EditingUtils.getPreviousWord(ic, mSettingsValues.mWordSeparators);
-                if (null != prevWord) {
-                    mUserBigramDictionary.addBigramPair(prevWord.toString(), suggestion.toString());
-                }
+                prevWord = EditingUtils.getPreviousWord(ic, mSettingsValues.mWordSeparators);
+            } else {
+                prevWord = null;
             }
+            mUserHistoryDictionary.addToUserHistory(null == prevWord ? null : prevWord.toString(),
+                    suggestion.toString());
         }
     }
 
diff --git a/java/src/com/android/inputmethod/latin/LatinImeLogger.java b/java/src/com/android/inputmethod/latin/LatinImeLogger.java
index 5390ee3..079f3b5 100644
--- a/java/src/com/android/inputmethod/latin/LatinImeLogger.java
+++ b/java/src/com/android/inputmethod/latin/LatinImeLogger.java
@@ -22,8 +22,6 @@
 
 import com.android.inputmethod.keyboard.Keyboard;
 
-import java.util.List;
-
 public class LatinImeLogger implements SharedPreferences.OnSharedPreferenceChangeListener {
 
     public static boolean sDBG = false;
@@ -53,7 +51,7 @@
     public static void logOnAutoCorrectionCancelled() {
     }
 
-    public static void logOnDelete() {
+    public static void logOnDelete(int x, int y) {
     }
 
     public static void logOnInputChar() {
diff --git a/java/src/com/android/inputmethod/latin/SettingsValues.java b/java/src/com/android/inputmethod/latin/SettingsValues.java
index 4346a36..c1335fd 100644
--- a/java/src/com/android/inputmethod/latin/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/SettingsValues.java
@@ -19,19 +19,15 @@
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.content.res.Resources;
-import android.os.Build;
 import android.util.Log;
 import android.view.inputmethod.EditorInfo;
 
-import com.android.inputmethod.compat.InputMethodInfoCompatWrapper;
 import com.android.inputmethod.compat.InputTypeCompatUtils;
 import com.android.inputmethod.compat.VibratorCompatWrapper;
 import com.android.inputmethod.keyboard.internal.KeySpecParser;
 
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
 import java.util.Locale;
 
 public class SettingsValues {
@@ -325,14 +321,8 @@
             return volume;
         }
 
-        final String[] volumePerHardwareList = res.getStringArray(R.array.keypress_volumes);
-        final String hardwarePrefix = Build.HARDWARE + ",";
-        for (final String element : volumePerHardwareList) {
-            if (element.startsWith(hardwarePrefix)) {
-                return Float.parseFloat(element.substring(element.lastIndexOf(',') + 1));
-            }
-        }
-        return -1.0f;
+        return Float.parseFloat(
+                Utils.getDeviceOverrideValue(res, R.array.keypress_volumes, "-1.0f"));
     }
 
     // Likewise
@@ -343,15 +333,9 @@
         if (ms >= 0) {
             return ms;
         }
-        final String[] durationPerHardwareList = res.getStringArray(
-                R.array.keypress_vibration_durations);
-        final String hardwarePrefix = Build.HARDWARE + ",";
-        for (final String element : durationPerHardwareList) {
-            if (element.startsWith(hardwarePrefix)) {
-                return (int)Long.parseLong(element.substring(element.lastIndexOf(',') + 1));
-            }
-        }
-        return -1;
+
+        return Integer.parseInt(
+                Utils.getDeviceOverrideValue(res, R.array.keypress_vibration_durations, "-1"));
     }
 
     // Likewise
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index 69754d7..9ae2506 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -30,15 +30,12 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
 
 /**
  * This class loads a dictionary and provides a list of suggestions for a given sequence of
  * characters. This includes corrections and completions.
  */
 public class Suggest implements Dictionary.WordCallback {
-
     public static final String TAG = Suggest.class.getSimpleName();
 
     public static final int APPROX_MAX_WORD_LENGTH = 32;
@@ -65,9 +62,8 @@
     public static final int DIC_USER_TYPED = 0;
     public static final int DIC_MAIN = 1;
     public static final int DIC_USER = 2;
-    public static final int DIC_USER_UNIGRAM = 3;
+    public static final int DIC_USER_HISTORY = 3;
     public static final int DIC_CONTACTS = 4;
-    public static final int DIC_USER_BIGRAM = 5;
     public static final int DIC_WHITELIST = 6;
     // If you add a type of dictionary, increment DIC_TYPE_LAST_ID
     // TODO: this value seems unused. Remove it?
@@ -76,10 +72,10 @@
     public static final String DICT_KEY_CONTACTS = "contacts";
     // User dictionary, the system-managed one.
     public static final String DICT_KEY_USER = "user";
-    // User unigram dictionary, internal to LatinIME
-    public static final String DICT_KEY_USER_UNIGRAM = "user_unigram";
-    // User bigram dictionary, internal to LatinIME
-    public static final String DICT_KEY_USER_BIGRAM = "user_bigram";
+    // User history dictionary for the unigram map, internal to LatinIME
+    public static final String DICT_KEY_USER_HISTORY_UNIGRAM = "history_unigram";
+    // User history dictionary for the bigram map, internal to LatinIME
+    public static final String DICT_KEY_USER_HISTORY_BIGRAM = "history_bigram";
     public static final String DICT_KEY_WHITELIST ="whitelist";
 
     private static final boolean DBG = LatinImeLogger.sDBG;
@@ -87,8 +83,10 @@
     private Dictionary mMainDict;
     private ContactsDictionary mContactsDict;
     private WhitelistDictionary mWhiteListDictionary;
-    private final Map<String, Dictionary> mUnigramDictionaries = new HashMap<String, Dictionary>();
-    private final Map<String, Dictionary> mBigramDictionaries = new HashMap<String, Dictionary>();
+    private final HashMap<String, Dictionary> mUnigramDictionaries =
+            new HashMap<String, Dictionary>();
+    private final HashMap<String, Dictionary> mBigramDictionaries =
+            new HashMap<String, Dictionary>();
 
     private int mPrefMaxSuggestions = 18;
 
@@ -142,7 +140,7 @@
         initWhitelistAndAutocorrectAndPool(context, locale);
     }
 
-    private static void addOrReplaceDictionary(Map<String, Dictionary> dictionaries, String key,
+    private static void addOrReplaceDictionary(HashMap<String, Dictionary> dictionaries, String key,
             Dictionary dict) {
         final Dictionary oldDict = (dict == null)
                 ? dictionaries.remove(key)
@@ -177,7 +175,7 @@
         return mContactsDict;
     }
 
-    public Map<String, Dictionary> getUnigramDictionaries() {
+    public HashMap<String, Dictionary> getUnigramDictionaries() {
         return mUnigramDictionaries;
     }
 
@@ -204,12 +202,11 @@
         addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_CONTACTS, contactsDictionary);
     }
 
-    public void setUserUnigramDictionary(Dictionary userUnigramDictionary) {
-        addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_USER_UNIGRAM, userUnigramDictionary);
-    }
-
-    public void setUserBigramDictionary(Dictionary userBigramDictionary) {
-        addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_USER_BIGRAM, userBigramDictionary);
+    public void setUserHistoryDictionary(Dictionary userHistoryDictionary) {
+        addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_USER_HISTORY_UNIGRAM,
+                userHistoryDictionary);
+        addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_USER_HISTORY_BIGRAM,
+                userHistoryDictionary);
     }
 
     public void setAutoCorrectionThreshold(double threshold) {
@@ -348,7 +345,7 @@
             // At second character typed, search the unigrams (scores being affected by bigrams)
             for (final String key : mUnigramDictionaries.keySet()) {
                 // Skip UserUnigramDictionary and WhitelistDictionary to lookup
-                if (key.equals(DICT_KEY_USER_UNIGRAM) || key.equals(DICT_KEY_WHITELIST))
+                if (key.equals(DICT_KEY_USER_HISTORY_UNIGRAM) || key.equals(DICT_KEY_WHITELIST))
                     continue;
                 final Dictionary dictionary = mUnigramDictionaries.get(key);
                 dictionary.getWords(wordComposerForLookup, this, proximityInfo);
@@ -563,7 +560,7 @@
     }
 
     public void close() {
-        final Set<Dictionary> dictionaries = new HashSet<Dictionary>();
+        final HashSet<Dictionary> dictionaries = new HashSet<Dictionary>();
         dictionaries.addAll(mUnigramDictionaries.values());
         dictionaries.addAll(mBigramDictionaries.values());
         for (final Dictionary dictionary : dictionaries) {
diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java
index b63bc6c..ef8e58e 100644
--- a/java/src/com/android/inputmethod/latin/SuggestedWords.java
+++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java
@@ -21,22 +21,20 @@
 
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.HashSet;
-import java.util.List;
 
 public class SuggestedWords {
     public static final SuggestedWords EMPTY = new SuggestedWords(
-            Collections.<SuggestedWordInfo>emptyList(), false, false, false, false, false);
+            new ArrayList<SuggestedWordInfo>(0), false, false, false, false, false);
 
     public final boolean mTypedWordValid;
     public final boolean mHasAutoCorrectionCandidate;
     public final boolean mIsPunctuationSuggestions;
     public final boolean mAllowsToBeAutoCorrected;
     public final boolean mIsObsoleteSuggestions;
-    private final List<SuggestedWordInfo> mSuggestedWordInfoList;
+    private final ArrayList<SuggestedWordInfo> mSuggestedWordInfoList;
 
-    public SuggestedWords(final List<SuggestedWordInfo> suggestedWordInfoList,
+    public SuggestedWords(final ArrayList<SuggestedWordInfo> suggestedWordInfoList,
             final boolean typedWordValid,
             final boolean hasAutoCorrectionCandidate,
             final boolean allowsToBeAutoCorrected,
@@ -82,7 +80,7 @@
     }
 
     public static ArrayList<SuggestedWordInfo> getFromCharSequenceList(
-            final List<CharSequence> wordList) {
+            final ArrayList<CharSequence> wordList) {
         final ArrayList<SuggestedWordInfo> result = new ArrayList<SuggestedWordInfo>();
         for (CharSequence word : wordList) {
             if (null != word) result.add(new SuggestedWordInfo(word));
@@ -90,7 +88,7 @@
         return result;
     }
 
-    public static List<SuggestedWordInfo> getFromApplicationSpecifiedCompletions(
+    public static ArrayList<SuggestedWordInfo> getFromApplicationSpecifiedCompletions(
             final CompletionInfo[] infos) {
         final ArrayList<SuggestedWordInfo> result = new ArrayList<SuggestedWordInfo>();
         for (CompletionInfo info : infos) {
diff --git a/java/src/com/android/inputmethod/latin/UserBigramDictionary.java b/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java
similarity index 80%
rename from java/src/com/android/inputmethod/latin/UserBigramDictionary.java
rename to java/src/com/android/inputmethod/latin/UserHistoryDictionary.java
index 52a31f2..4e79846 100644
--- a/java/src/com/android/inputmethod/latin/UserBigramDictionary.java
+++ b/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java
@@ -31,12 +31,11 @@
 import java.util.Iterator;
 
 /**
- * Stores all the pairs user types in databases. Prune the database if the size
- * gets too big. Unlike AutoDictionary, it even stores the pairs that are already
- * in the dictionary.
+ * Locally gathers stats about the words user types and various other signals like auto-correction
+ * cancellation or manual picks. This allows the keyboard to adapt to the typist over time.
  */
-public class UserBigramDictionary extends ExpandableDictionary {
-    private static final String TAG = "UserBigramDictionary";
+public class UserHistoryDictionary extends ExpandableDictionary {
+    private static final String TAG = "UserHistoryDictionary";
 
     /** Any pair being typed or picked */
     private static final int FREQUENCY_FOR_TYPED = 2;
@@ -45,14 +44,14 @@
     private static final int FREQUENCY_MAX = 127;
 
     /** Maximum number of pairs. Pruning will start when databases goes above this number. */
-    private static int sMaxUserBigrams = 10000;
+    private static int sMaxHistoryBigrams = 10000;
 
     /**
      * When it hits maximum bigram pair, it will delete until you are left with
-     * only (sMaxUserBigrams - sDeleteUserBigrams) pairs.
+     * only (sMaxHistoryBigrams - sDeleteHistoryBigrams) pairs.
      * Do not keep this number small to avoid deleting too often.
      */
-    private static int sDeleteUserBigrams = 1000;
+    private static int sDeleteHistoryBigrams = 1000;
 
     /**
      * Database version should increase if the database structure changes
@@ -64,7 +63,7 @@
     /** Name of the words table in the database */
     private static final String MAIN_TABLE_NAME = "main";
     // TODO: Consume less space by using a unique id for locale instead of the whole
-    // 2-5 character string. (Same TODO from AutoDictionary)
+    // 2-5 character string.
     private static final String MAIN_COLUMN_ID = BaseColumns._ID;
     private static final String MAIN_COLUMN_WORD1 = "word1";
     private static final String MAIN_COLUMN_WORD2 = "word2";
@@ -114,8 +113,16 @@
 
         @Override
         public boolean equals(Object bigram) {
-            Bigram bigram2 = (Bigram) bigram;
-            return (mWord1.equals(bigram2.mWord1) && mWord2.equals(bigram2.mWord2));
+            if (!(bigram instanceof Bigram)) {
+                return false;
+            }
+            final Bigram bigram2 = (Bigram) bigram;
+            final boolean eq1 =
+                    mWord1 == null ? bigram2.mWord1 == null : mWord1.equals(bigram2.mWord1);
+            if (!eq1) {
+                return false;
+            }
+            return mWord2 == null ? bigram2.mWord2 == null : mWord2.equals(bigram2.mWord2);
         }
 
         @Override
@@ -124,15 +131,15 @@
         }
     }
 
-    public void setDatabaseMax(int maxUserBigram) {
-        sMaxUserBigrams = maxUserBigram;
+    public void setDatabaseMax(int maxHistoryBigram) {
+        sMaxHistoryBigrams = maxHistoryBigram;
     }
 
-    public void setDatabaseDelete(int deleteUserBigram) {
-        sDeleteUserBigrams = deleteUserBigram;
+    public void setDatabaseDelete(int deleteHistoryBigram) {
+        sDeleteHistoryBigrams = deleteHistoryBigram;
     }
 
-    public UserBigramDictionary(Context context, LatinIME ime, String locale, int dicTypeId) {
+    public UserHistoryDictionary(Context context, LatinIME ime, String locale, int dicTypeId) {
         super(context, dicTypeId);
         mIme = ime;
         mLocale = locale;
@@ -155,19 +162,39 @@
     }
 
     /**
-     * Pair will be added to the userbigram database.
+     * Return whether the passed charsequence is in the dictionary.
      */
-    public int addBigramPair(String word1, String word2) {
+    @Override
+    public boolean isValidWord(final CharSequence word) {
+        // TODO: figure out what is the correct thing to do here.
+        return false;
+    }
+
+    /**
+     * Pair will be added to the user history dictionary.
+     *
+     * The first word may be null. That means we don't know the context, in other words,
+     * it's only a unigram. The first word may also be an empty string : this means start
+     * context, as in beginning of a sentence for example.
+     * The second word may not be null (a NullPointerException would be thrown).
+     */
+    public int addToUserHistory(final String word1, String word2) {
         // remove caps if second word is autocapitalized
         if (mIme != null && mIme.isAutoCapitalized()) {
             word2 = Character.toLowerCase(word2.charAt(0)) + word2.substring(1);
         }
+        super.addWord(word2, FREQUENCY_FOR_TYPED);
         // Do not insert a word as a bigram of itself
-        if (word1.equals(word2)) {
+        if (word2.equals(word1)) {
             return 0;
         }
 
-        int freq = super.addBigram(word1, word2, FREQUENCY_FOR_TYPED);
+        int freq;
+        if (null == word1) {
+            freq = FREQUENCY_FOR_TYPED;
+        } else {
+            freq = super.addBigram(word1, word2, FREQUENCY_FOR_TYPED);
+        }
         if (freq > FREQUENCY_MAX) freq = FREQUENCY_MAX;
         synchronized (mPendingWritesLock) {
             if (freq == FREQUENCY_FOR_TYPED || mPendingWrites.isEmpty()) {
@@ -225,7 +252,10 @@
                     int frequency = cursor.getInt(frequencyIndex);
                     // Safeguard against adding really long words. Stack may overflow due
                     // to recursive lookup
-                    if (word1.length() < MAX_WORD_LENGTH && word2.length() < MAX_WORD_LENGTH) {
+                    if (null == word1) {
+                        super.addWord(word2, frequency);
+                    } else if (word1.length() < MAX_WORD_LENGTH
+                            && word2.length() < MAX_WORD_LENGTH) {
                         super.setBigram(word1, word2, frequency);
                     }
                     cursor.moveToNext();
@@ -324,8 +354,8 @@
             try {
                 int totalRowCount = c.getCount();
                 // prune out old data if we have too much data
-                if (totalRowCount > sMaxUserBigrams) {
-                    int numDeleteRows = (totalRowCount - sMaxUserBigrams) + sDeleteUserBigrams;
+                if (totalRowCount > sMaxHistoryBigrams) {
+                    int numDeleteRows = (totalRowCount - sMaxHistoryBigrams) + sDeleteHistoryBigrams;
                     int pairIdColumnId = c.getColumnIndex(FREQ_COLUMN_PAIR_ID);
                     c.moveToFirst();
                     int count = 0;
@@ -367,13 +397,23 @@
             // Write all the entries to the db
             Iterator<Bigram> iterator = mMap.iterator();
             while (iterator.hasNext()) {
+                // TODO: this process of making a text search for each pair each time
+                // is terribly inefficient. Optimize this.
                 Bigram bi = iterator.next();
 
                 // find pair id
-                Cursor c = db.query(MAIN_TABLE_NAME, new String[] { MAIN_COLUMN_ID },
-                        MAIN_COLUMN_WORD1 + "=? AND " + MAIN_COLUMN_WORD2 + "=? AND "
-                        + MAIN_COLUMN_LOCALE + "=?",
-                        new String[] { bi.mWord1, bi.mWord2, mLocale }, null, null, null);
+                final Cursor c;
+                if (null != bi.mWord1) {
+                    c = db.query(MAIN_TABLE_NAME, new String[] { MAIN_COLUMN_ID },
+                            MAIN_COLUMN_WORD1 + "=? AND " + MAIN_COLUMN_WORD2 + "=? AND "
+                            + MAIN_COLUMN_LOCALE + "=?",
+                            new String[] { bi.mWord1, bi.mWord2, mLocale }, null, null, null);
+                } else {
+                    c = db.query(MAIN_TABLE_NAME, new String[] { MAIN_COLUMN_ID },
+                            MAIN_COLUMN_WORD1 + " IS NULL AND " + MAIN_COLUMN_WORD2 + "=? AND "
+                            + MAIN_COLUMN_LOCALE + "=?",
+                            new String[] { bi.mWord2, mLocale }, null, null, null);
+                }
 
                 int pairId;
                 if (c.moveToFirst()) {
diff --git a/java/src/com/android/inputmethod/latin/UserUnigramDictionary.java b/java/src/com/android/inputmethod/latin/UserUnigramDictionary.java
deleted file mode 100644
index 2fc395c..0000000
--- a/java/src/com/android/inputmethod/latin/UserUnigramDictionary.java
+++ /dev/null
@@ -1,276 +0,0 @@
-/*
- * 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.ContentValues;
-import android.content.Context;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteOpenHelper;
-import android.database.sqlite.SQLiteQueryBuilder;
-import android.os.AsyncTask;
-import android.provider.BaseColumns;
-import android.util.Log;
-
-import java.util.HashMap;
-import java.util.Map.Entry;
-import java.util.Set;
-
-/**
- * This class (inherited from the old AutoDictionary) is used for user history
- * based dictionary. It stores words that the user typed to supply a provision
- * for suggesting and re-ordering of candidates.
- */
-public class UserUnigramDictionary extends ExpandableDictionary {
-    static final boolean ENABLE_USER_UNIGRAM_DICTIONARY = false;
-
-    // Weight added to a user picking a new word from the suggestion strip
-    static final int FREQUENCY_FOR_PICKED = 3;
-    // Weight added to a user typing a new word that doesn't get corrected (or is reverted)
-    static final int FREQUENCY_FOR_TYPED = 1;
-    // If the user touches a typed word 2 times or more, it will become valid.
-    private static final int VALIDITY_THRESHOLD = 2 * FREQUENCY_FOR_PICKED;
-
-    private LatinIME mIme;
-    // Locale for which this user unigram dictionary is storing words
-    private String mLocale;
-
-    private HashMap<String,Integer> mPendingWrites = new HashMap<String,Integer>();
-    private final Object mPendingWritesLock = new Object();
-
-    // TODO: we should probably change the database name
-    private static final String DATABASE_NAME = "auto_dict.db";
-    private static final int DATABASE_VERSION = 1;
-
-    // These are the columns in the dictionary
-    // TODO: Consume less space by using a unique id for locale instead of the whole
-    // 2-5 character string.
-    private static final String COLUMN_ID = BaseColumns._ID;
-    private static final String COLUMN_WORD = "word";
-    private static final String COLUMN_FREQUENCY = "freq";
-    private static final String COLUMN_LOCALE = "locale";
-
-    /** Sort by descending order of frequency. */
-    public static final String DEFAULT_SORT_ORDER = COLUMN_FREQUENCY + " DESC";
-
-    /** Name of the words table in the database */
-    private static final String USER_UNIGRAM_DICT_TABLE_NAME = "words";
-
-    private static HashMap<String, String> sDictProjectionMap;
-
-    static {
-        if (ENABLE_USER_UNIGRAM_DICTIONARY) {
-            sDictProjectionMap = new HashMap<String, String>();
-            sDictProjectionMap.put(COLUMN_ID, COLUMN_ID);
-            sDictProjectionMap.put(COLUMN_WORD, COLUMN_WORD);
-            sDictProjectionMap.put(COLUMN_FREQUENCY, COLUMN_FREQUENCY);
-            sDictProjectionMap.put(COLUMN_LOCALE, COLUMN_LOCALE);
-        }
-    }
-
-    private static DatabaseHelper sOpenHelper = null;
-
-    public UserUnigramDictionary(Context context, LatinIME ime, String locale, int dicTypeId) {
-        super(context, dicTypeId);
-        // Super must be first statement of the constructor... I'd like not to do it if the
-        // user unigram dictionary is not enabled, but Java won't let me.
-        if (!ENABLE_USER_UNIGRAM_DICTIONARY) return;
-        mIme = ime;
-        mLocale = locale;
-        if (sOpenHelper == null) {
-            sOpenHelper = new DatabaseHelper(getContext());
-        }
-        if (mLocale != null && mLocale.length() > 1) {
-            loadDictionary();
-        }
-    }
-
-    @Override
-    public synchronized boolean isValidWord(CharSequence word) {
-        if (!ENABLE_USER_UNIGRAM_DICTIONARY) return false;
-        final int frequency = getWordFrequency(word);
-        return frequency >= VALIDITY_THRESHOLD;
-    }
-
-    @Override
-    public void close() {
-        super.close();
-        if (!ENABLE_USER_UNIGRAM_DICTIONARY) return;
-        flushPendingWrites();
-        // Don't close the database as locale changes will require it to be reopened anyway
-        // Also, the database is written to somewhat frequently, so it needs to be kept alive
-        // throughout the life of the process.
-        // mOpenHelper.close();
-    }
-
-    @Override
-    public void loadDictionaryAsync() {
-        if (!ENABLE_USER_UNIGRAM_DICTIONARY) return;
-        // Load the words that correspond to the current input locale
-        final Cursor cursor = query(COLUMN_LOCALE + "=?", new String[] { mLocale });
-        if (null == cursor) return;
-        try {
-            if (cursor.moveToFirst()) {
-                int wordIndex = cursor.getColumnIndex(COLUMN_WORD);
-                int frequencyIndex = cursor.getColumnIndex(COLUMN_FREQUENCY);
-                while (!cursor.isAfterLast()) {
-                    String word = cursor.getString(wordIndex);
-                    int frequency = cursor.getInt(frequencyIndex);
-                    // Safeguard against adding really long words. Stack may overflow due
-                    // to recursive lookup
-                    if (word.length() < getMaxWordLength()) {
-                        super.addWord(word, frequency);
-                    }
-                    cursor.moveToNext();
-                }
-            }
-        } finally {
-            cursor.close();
-        }
-    }
-
-    public void addUnigram(String newWord) {
-        if (!ENABLE_USER_UNIGRAM_DICTIONARY) return;
-        final int addFrequency = FREQUENCY_FOR_TYPED;
-        String word = newWord;
-        final int length = word.length();
-        // Don't add very short or very long words.
-        if (length < 2 || length > getMaxWordLength()) return;
-        if (mIme.isAutoCapitalized()) {
-            // Remove caps before adding
-            word = Character.toLowerCase(word.charAt(0)) + word.substring(1);
-        }
-        int freq = getWordFrequency(word);
-        freq = freq < 0 ? addFrequency : freq + addFrequency;
-        super.addWord(word, freq);
-
-        synchronized (mPendingWritesLock) {
-            // Write a null frequency if it is to be deleted from the db
-            mPendingWrites.put(word, freq == 0 ? null : new Integer(freq));
-        }
-    }
-
-    /**
-     * Schedules a background thread to write any pending words to the database.
-     */
-    public void flushPendingWrites() {
-        if (!ENABLE_USER_UNIGRAM_DICTIONARY) return;
-        synchronized (mPendingWritesLock) {
-            // Nothing pending? Return
-            if (mPendingWrites.isEmpty()) return;
-            // Create a background thread to write the pending entries
-            new UpdateDbTask(sOpenHelper, mPendingWrites, mLocale).execute();
-            // Create a new map for writing new entries into while the old one is written to db
-            mPendingWrites = new HashMap<String, Integer>();
-        }
-    }
-
-    /**
-     * This class helps open, create, and upgrade the database file.
-     */
-    private static class DatabaseHelper extends SQLiteOpenHelper {
-
-        DatabaseHelper(Context context) {
-            super(context, DATABASE_NAME, null, DATABASE_VERSION);
-        }
-
-        @Override
-        public void onCreate(SQLiteDatabase db) {
-            db.execSQL("CREATE TABLE " + USER_UNIGRAM_DICT_TABLE_NAME + " ("
-                    + COLUMN_ID + " INTEGER PRIMARY KEY,"
-                    + COLUMN_WORD + " TEXT,"
-                    + COLUMN_FREQUENCY + " INTEGER,"
-                    + COLUMN_LOCALE + " TEXT"
-                    + ");");
-        }
-
-        @Override
-        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
-            Log.w("UserUnigramDictionary", "Upgrading database from version " + oldVersion + " to "
-                    + newVersion + ", which will destroy all old data");
-            db.execSQL("DROP TABLE IF EXISTS " + USER_UNIGRAM_DICT_TABLE_NAME);
-            onCreate(db);
-        }
-    }
-
-    private static Cursor query(String selection, String[] selectionArgs) {
-        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
-        qb.setTables(USER_UNIGRAM_DICT_TABLE_NAME);
-        qb.setProjectionMap(sDictProjectionMap);
-
-        // Get the database and run the query
-        try {
-            SQLiteDatabase db = sOpenHelper.getReadableDatabase();
-            Cursor c = qb.query(db, null, selection, selectionArgs, null, null,
-                    DEFAULT_SORT_ORDER);
-            return c;
-        } catch (android.database.sqlite.SQLiteCantOpenDatabaseException e) {
-            // Can't open the database : presumably we can't access storage. That may happen
-            // when the device is wedged; do a best effort to still start the keyboard.
-            return null;
-        }
-    }
-
-    /**
-     * Async task to write pending words to the database so that it stays in sync with
-     * the in-memory trie.
-     */
-    private static class UpdateDbTask extends AsyncTask<Void, Void, Void> {
-        private final HashMap<String, Integer> mMap;
-        private final DatabaseHelper mDbHelper;
-        private final String mLocale;
-
-        public UpdateDbTask(DatabaseHelper openHelper, HashMap<String, Integer> pendingWrites,
-                String locale) {
-            mMap = pendingWrites;
-            mLocale = locale;
-            mDbHelper = openHelper;
-        }
-
-        @Override
-        protected Void doInBackground(Void... v) {
-            SQLiteDatabase db = null;
-            try {
-                db = mDbHelper.getWritableDatabase();
-            } catch (android.database.sqlite.SQLiteCantOpenDatabaseException e) {
-                // With no access to the DB, this is moot. Do nothing: we'll exit through the
-                // test for null == db.
-            }
-            if (null == db) return null;
-            // Write all the entries to the db
-            Set<Entry<String,Integer>> mEntries = mMap.entrySet();
-            for (Entry<String,Integer> entry : mEntries) {
-                Integer freq = entry.getValue();
-                db.delete(USER_UNIGRAM_DICT_TABLE_NAME, COLUMN_WORD + "=? AND " + COLUMN_LOCALE
-                        + "=?", new String[] { entry.getKey(), mLocale });
-                if (freq != null) {
-                    db.insert(USER_UNIGRAM_DICT_TABLE_NAME, null,
-                            getContentValues(entry.getKey(), freq, mLocale));
-                }
-            }
-            return null;
-        }
-
-        private static ContentValues getContentValues(String word, int frequency, String locale) {
-            ContentValues values = new ContentValues(4);
-            values.put(COLUMN_WORD, word);
-            values.put(COLUMN_FREQUENCY, frequency);
-            values.put(COLUMN_LOCALE, locale);
-            return values;
-        }
-    }
-}
diff --git a/java/src/com/android/inputmethod/latin/Utils.java b/java/src/com/android/inputmethod/latin/Utils.java
index a7de47c..7086345 100644
--- a/java/src/com/android/inputmethod/latin/Utils.java
+++ b/java/src/com/android/inputmethod/latin/Utils.java
@@ -19,9 +19,11 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
+import android.content.res.Resources;
 import android.inputmethodservice.InputMethodService;
 import android.net.Uri;
 import android.os.AsyncTask;
+import android.os.Build;
 import android.os.Environment;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -43,6 +45,7 @@
 import java.nio.channels.FileChannel;
 import java.text.SimpleDateFormat;
 import java.util.Date;
+import java.util.HashMap;
 
 public class Utils {
     private Utils() {
@@ -260,8 +263,8 @@
             }
         }
 
-        public static void writeBackSpace() {
-            UsabilityStudyLogUtils.getInstance().write("<backspace>\t0\t0");
+        public static void writeBackSpace(int x, int y) {
+            UsabilityStudyLogUtils.getInstance().write("<backspace>\t" + x + "\t" + y);
         }
 
         public void writeChar(char c, int x, int y) {
@@ -465,4 +468,23 @@
         if (TextUtils.isEmpty(info)) return null;
         return info;
     }
+
+    private static final String HARDWARE_PREFIX = Build.HARDWARE + ",";
+    private static final HashMap<Integer, String> sDeviceOverrideValueMap =
+            new HashMap<Integer, String>();
+
+    public static String getDeviceOverrideValue(Resources res, int overrideResId, String defValue) {
+        final Integer key = overrideResId;
+        if (!sDeviceOverrideValueMap.containsKey(key)) {
+            String overrideValue = defValue;
+            for (final String element : res.getStringArray(overrideResId)) {
+                if (element.startsWith(HARDWARE_PREFIX)) {
+                    overrideValue = element.substring(HARDWARE_PREFIX.length());
+                    break;
+                }
+            }
+            sDeviceOverrideValueMap.put(key, overrideValue);
+        }
+        return sDeviceOverrideValueMap.get(key);
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
index a960931..9f23f17 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -140,8 +140,9 @@
             keyX = x;
             keyY = y;
         } else {
-            codes = keyDetector.newCodeArray();
-            keyDetector.getNearbyCodes(x, y, codes);
+            final Key key = keyDetector.detectHitKey(x, y);
+            // TODO: Pass an integer instead of an integer array
+            codes = new int[] { key != null ? key.mCode : NOT_A_CODE };
             keyX = keyDetector.getTouchX(x);
             keyY = keyDetector.getTouchY(y);
         }
@@ -202,9 +203,8 @@
             if (key.mCode == codePoint) {
                 final int x = key.mX + key.mWidth / 2;
                 final int y = key.mY + key.mHeight / 2;
-                final int[] codes = keyDetector.newCodeArray();
-                keyDetector.getNearbyCodes(x, y, codes);
-                add(codePoint, codes, x, y);
+                // TODO: Pass an integer instead of an integer array
+                add(codePoint, new int[] { key.mCode }, x, y);
                 return;
             }
         }
@@ -216,7 +216,7 @@
      * Set the currently composing word to the one passed as an argument.
      * This will register NOT_A_COORDINATE for X and Ys, and use the passed keyboard for proximity.
      */
-    public void setComposingWord(final CharSequence word, final Keyboard keyboard,
+    private void setComposingWord(final CharSequence word, final Keyboard keyboard,
             final KeyDetector keyDetector) {
         reset();
         final int length = word.length();
@@ -233,7 +233,6 @@
         final KeyDetector keyDetector = new KeyDetector(0);
         keyDetector.setKeyboard(keyboard, 0, 0);
         keyDetector.setProximityCorrectionEnabled(true);
-        keyDetector.setProximityThreshold(keyboard.mMostCommonKeyWidth);
         setComposingWord(word, keyboard, keyDetector);
     }
 
diff --git a/java/src/com/android/inputmethod/latin/XmlParseUtils.java b/java/src/com/android/inputmethod/latin/XmlParseUtils.java
index e14c71c..481cdfa 100644
--- a/java/src/com/android/inputmethod/latin/XmlParseUtils.java
+++ b/java/src/com/android/inputmethod/latin/XmlParseUtils.java
@@ -24,6 +24,10 @@
 import java.io.IOException;
 
 public class XmlParseUtils {
+    private XmlParseUtils() {
+        // This utility class is not publicly instantiable.
+    }
+
     @SuppressWarnings("serial")
     public static class ParseException extends XmlPullParserException {
         public ParseException(String msg, XmlPullParser parser) {
diff --git a/tools/makedict/src/com/android/inputmethod/latin/UnsupportedFormatException.java b/java/src/com/android/inputmethod/latin/makedict/Dummy.java
similarity index 63%
copy from tools/makedict/src/com/android/inputmethod/latin/UnsupportedFormatException.java
copy to java/src/com/android/inputmethod/latin/makedict/Dummy.java
index 511ad56..27ea5ac 100644
--- a/tools/makedict/src/com/android/inputmethod/latin/UnsupportedFormatException.java
+++ b/java/src/com/android/inputmethod/latin/makedict/Dummy.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2012 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  * use this file except in compliance with the License. You may obtain a copy of
@@ -14,13 +14,8 @@
  * the License.
  */
 
-package com.android.inputmethod.latin;
+package com.android.inputmethod.latin.makedict;
 
-/**
- * Simple exception thrown when a file format is not recognized.
- */
-public class UnsupportedFormatException extends Exception {
-    public UnsupportedFormatException(String description) {
-        super(description);
-    }
+public class Dummy {
+
 }
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionsView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionsView.java
index f6c1b16..06fda44 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionsView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionsView.java
@@ -65,7 +65,6 @@
 import com.android.inputmethod.latin.Utils;
 
 import java.util.ArrayList;
-import java.util.List;
 
 public class SuggestionsView extends RelativeLayout implements OnClickListener,
         OnLongClickListener {
@@ -143,9 +142,9 @@
         public final float mMinMoreSuggestionsWidth;
         public final int mMoreSuggestionsBottomGap;
 
-        private final List<TextView> mWords;
-        private final List<View> mDividers;
-        private final List<TextView> mInfos;
+        private final ArrayList<TextView> mWords;
+        private final ArrayList<View> mDividers;
+        private final ArrayList<TextView> mInfos;
 
         private final int mColorValidTypedWord;
         private final int mColorTypedWord;
@@ -173,7 +172,7 @@
         private final TextView mHintToSaveView;
 
         public SuggestionsViewParams(Context context, AttributeSet attrs, int defStyle,
-                List<TextView> words, List<View> dividers, List<TextView> infos) {
+                ArrayList<TextView> words, ArrayList<View> dividers, ArrayList<TextView> infos) {
             mWords = words;
             mDividers = dividers;
             mInfos = infos;
diff --git a/native/src/bigram_dictionary.cpp b/native/src/bigram_dictionary.cpp
index 84048d7..fa69de8 100644
--- a/native/src/bigram_dictionary.cpp
+++ b/native/src/bigram_dictionary.cpp
@@ -137,9 +137,10 @@
         // codesSize == 0 means we are trying to find bigram predictions.
         if (codesSize < 1 || checkFirstCharacter(bigramBuffer)) {
             const int frequency = UnigramDictionary::MASK_ATTRIBUTE_FREQUENCY & bigramFlags;
-            addWordBigram(bigramBuffer, length, frequency);
+            if (addWordBigram(bigramBuffer, length, frequency)) {
+                ++bigramCount;
+            }
         }
-        ++bigramCount;
     } while (0 != (UnigramDictionary::FLAG_ATTRIBUTE_HAS_NEXT & bigramFlags));
     return bigramCount;
 }
diff --git a/native/src/proximity_info.cpp b/native/src/proximity_info.cpp
index dd60ed6..4d03f32 100644
--- a/native/src/proximity_info.cpp
+++ b/native/src/proximity_info.cpp
@@ -55,6 +55,7 @@
           mTouchPositionCorrectionEnabled(false) {
     const int proximityGridLength = GRID_WIDTH * GRID_HEIGHT * MAX_PROXIMITY_CHARS_SIZE;
     mProximityCharsArray = new int32_t[proximityGridLength];
+    mInputCodes = new int32_t[MAX_PROXIMITY_CHARS_SIZE * MAX_WORD_LENGTH_INTERNAL];
     if (DEBUG_PROXIMITY_INFO) {
         AKLOGI("Create proximity info array %d", proximityGridLength);
     }
@@ -96,6 +97,7 @@
 ProximityInfo::~ProximityInfo() {
     delete[] mNormalizedSquaredDistances;
     delete[] mProximityCharsArray;
+    delete[] mInputCodes;
 }
 
 inline int ProximityInfo::getStartIndexFromCoordinates(const int x, const int y) const {
@@ -138,7 +140,7 @@
 int ProximityInfo::squaredDistanceToEdge(const int keyId, const int x, const int y) {
     const int left = mKeyXCoordinates[keyId];
     const int top = mKeyYCoordinates[keyId];
-    const int right = left + mKeyWidths[keyId] + 1;
+    const int right = left + mKeyWidths[keyId];
     const int bottom = top + mKeyHeights[keyId];
     const int edgeX = x < left ? left : (x > right ? right : x);
     const int edgeY = y < top ? top : (y > bottom ? bottom : y);
@@ -157,10 +159,10 @@
         if (c < KEYCODE_SPACE || c == primaryKey) {
             continue;
         }
-        int keyIndex = getKeyIndex(c);
+        const int keyIndex = getKeyIndex(c);
         const bool onKey = isOnKey(keyIndex, x, y);
         const int distance = squaredDistanceToEdge(keyIndex, x, y);
-        if (onKey || distance < MOST_COMMON_KEY_WIDTH_SQUARE) {
+        if (c >= KEYCODE_SPACE && (onKey || distance < MOST_COMMON_KEY_WIDTH_SQUARE)) {
             inputCodes[insertPos++] = c;
             if (insertPos >= MAX_PROXIMITY_CHARS_SIZE) {
                 if (DEBUG_DICT) {
@@ -170,17 +172,17 @@
             }
         }
     }
-    inputCodes[insertPos++] = ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE;
-    if (insertPos >= MAX_PROXIMITY_CHARS_SIZE) {
-        if (DEBUG_DICT) {
-            assert(false);
-        }
-        return;
-    }
-
     const int additionalProximitySize =
             AdditionalProximityChars::getAdditionalCharsSize(&mLocaleStr, primaryKey);
     if (additionalProximitySize > 0) {
+        inputCodes[insertPos++] = ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE;
+        if (insertPos >= MAX_PROXIMITY_CHARS_SIZE) {
+            if (DEBUG_DICT) {
+                assert(false);
+            }
+            return;
+        }
+
         const int32_t* additionalProximityChars =
                 AdditionalProximityChars::getAdditionalChars(&mLocaleStr, primaryKey);
         for (int j = 0; j < additionalProximitySize; ++j) {
@@ -204,13 +206,46 @@
         }
     }
     // Add a delimiter for the proximity characters
-    inputCodes[insertPos] = 0;
+    for (int i = insertPos; i < MAX_PROXIMITY_CHARS_SIZE; ++i) {
+        inputCodes[i] = NOT_A_CODE;
+    }
 }
 
 // TODO: Calculate nearby codes here.
-void ProximityInfo::setInputParams(const int* inputCodes, const int inputLength,
+void ProximityInfo::setInputParams(const int32_t* inputCodes, const int inputLength,
         const int* xCoordinates, const int* yCoordinates) {
-    mInputCodes = inputCodes;
+    memset(mInputCodes, 0,
+            MAX_WORD_LENGTH_INTERNAL * MAX_PROXIMITY_CHARS_SIZE * sizeof(mInputCodes[0]));
+
+    for (int i = 0; i < inputLength; ++i) {
+        const int32_t primaryKey = inputCodes[i * MAX_PROXIMITY_CHARS_SIZE];
+        const int x = xCoordinates[i];
+        const int y = yCoordinates[i];
+        int *proximities = &mInputCodes[i * MAX_PROXIMITY_CHARS_SIZE];
+        calculateNearbyKeyCodes(x, y, primaryKey, proximities);
+    }
+
+    if (DEBUG_PROXIMITY_CHARS) {
+        for (int i = 0; i < inputLength; ++i) {
+            AKLOGI("---");
+            for (int j = 0; j < MAX_PROXIMITY_CHARS_SIZE; ++j) {
+                int icc = mInputCodes[i * MAX_PROXIMITY_CHARS_SIZE + j];
+                int icfjc = inputCodes[i * MAX_PROXIMITY_CHARS_SIZE + j];
+                icc+= 0;
+                icfjc += 0;
+                AKLOGI("--- (%d)%c,%c", i, icc, icfjc);
+                AKLOGI("---             A<%d>,B<%d>", icc, icfjc);
+            }
+        }
+    }
+    //Keep for debug, sorry
+    //for (int i = 0; i < MAX_WORD_LENGTH_INTERNAL * MAX_PROXIMITY_CHARS_SIZE; ++i) {
+    //if (i < inputLength * MAX_PROXIMITY_CHARS_SIZE) {
+    //mInputCodes[i] = mInputCodesFromJava[i];
+    //} else {
+    // mInputCodes[i] = 0;
+    // }
+    //}
     mInputXCoordinates = xCoordinates;
     mInputYCoordinates = yCoordinates;
     mTouchPositionCorrectionEnabled =
@@ -246,8 +281,9 @@
         }
         for (int j = 0; j < MAX_PROXIMITY_CHARS_SIZE && proximityChars[j] > 0; ++j) {
             const int currentChar = proximityChars[j];
-            const int keyIndex = getKeyIndex(currentChar);
-            const float squaredDistance = calculateNormalizedSquaredDistance(keyIndex, i);
+            const float squaredDistance = hasInputCoordinates()
+                    ? calculateNormalizedSquaredDistance(getKeyIndex(currentChar), i)
+                    : NOT_A_DISTANCE_FLOAT;
             if (squaredDistance >= 0.0f) {
                 mNormalizedSquaredDistances[i * MAX_PROXIMITY_CHARS_SIZE + j] =
                         (int)(squaredDistance * NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR);
@@ -267,7 +303,6 @@
 
 float ProximityInfo::calculateNormalizedSquaredDistance(
         const int keyIndex, const int inputIndex) const {
-    static const float NOT_A_DISTANCE_FLOAT = -1.0f;
     if (keyIndex == NOT_A_INDEX) {
         return NOT_A_DISTANCE_FLOAT;
     }
@@ -282,8 +317,12 @@
     return squaredDistance / squaredRadius;
 }
 
+bool ProximityInfo::hasInputCoordinates() const {
+    return mInputXCoordinates && mInputYCoordinates;
+}
+
 int ProximityInfo::getKeyIndex(const int c) const {
-    if (KEY_COUNT == 0 || !mInputXCoordinates || !mInputYCoordinates) {
+    if (KEY_COUNT == 0) {
         // We do not have the coordinate data
         return NOT_A_INDEX;
     }
diff --git a/native/src/proximity_info.h b/native/src/proximity_info.h
index 1a47aff..cdc5a3d 100644
--- a/native/src/proximity_info.h
+++ b/native/src/proximity_info.h
@@ -53,7 +53,7 @@
             const float *sweetSpotCenterYs, const float *sweetSpotRadii);
     ~ProximityInfo();
     bool hasSpaceProximity(const int x, const int y) const;
-    void setInputParams(const int *inputCodes, const int inputLength,
+    void setInputParams(const int32_t *inputCodes, const int inputLength,
             const int *xCoordinates, const int *yCoordinates);
     const int* getProximityCharsAt(const int index) const;
     unsigned short getPrimaryCharAt(const int index) const;
@@ -77,12 +77,15 @@
     static const int MAX_KEY_COUNT_IN_A_KEYBOARD = 64;
     // The upper limit of the char code in mCodeToKeyIndex
     static const int MAX_CHAR_CODE = 127;
+    static const float NOT_A_DISTANCE_FLOAT = -1.0f;
+    static const int NOT_A_CODE = -1;
 
     int getStartIndexFromCoordinates(const int x, const int y) const;
     void initializeCodeToKeyIndex();
     float calculateNormalizedSquaredDistance(const int keyIndex, const int inputIndex) const;
     float calculateSquaredDistanceFromSweetSpotCenter(
             const int keyIndex, const int inputIndex) const;
+    bool hasInputCoordinates() const;
     int getKeyIndex(const int c) const;
     bool hasSweetSpotData(const int keyIndex) const {
         // When there are no calibration data for a key,
@@ -105,7 +108,9 @@
     const int KEY_COUNT;
     const bool HAS_TOUCH_POSITION_CORRECTION_DATA;
     const std::string mLocaleStr;
-    const int *mInputCodes;
+    // TODO: remove this
+    const int *mInputCodesFromJava;
+    int32_t *mInputCodes;
     const int *mInputXCoordinates;
     const int *mInputYCoordinates;
     bool mTouchPositionCorrectionEnabled;
diff --git a/tools/makedict/etc/manifest.txt b/tools/makedict/etc/manifest.txt
index 948609d..4f085e7 100644
--- a/tools/makedict/etc/manifest.txt
+++ b/tools/makedict/etc/manifest.txt
@@ -1 +1 @@
-Main-Class: com.android.inputmethod.latin.DictionaryMaker
+Main-Class: com.android.inputmethod.latin.makedict.DictionaryMaker
diff --git a/tools/makedict/src/com/android/inputmethod/latin/BinaryDictInputOutput.java b/tools/makedict/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java
similarity index 99%
rename from tools/makedict/src/com/android/inputmethod/latin/BinaryDictInputOutput.java
rename to tools/makedict/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java
index 485dba1..42dd4df 100644
--- a/tools/makedict/src/com/android/inputmethod/latin/BinaryDictInputOutput.java
+++ b/tools/makedict/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java
@@ -14,11 +14,11 @@
  * the License.
  */
 
-package com.android.inputmethod.latin;
+package com.android.inputmethod.latin.makedict;
 
-import com.android.inputmethod.latin.FusionDictionary.CharGroup;
-import com.android.inputmethod.latin.FusionDictionary.Node;
-import com.android.inputmethod.latin.FusionDictionary.WeightedString;
+import com.android.inputmethod.latin.makedict.FusionDictionary.CharGroup;
+import com.android.inputmethod.latin.makedict.FusionDictionary.Node;
+import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
 
 import java.io.FileNotFoundException;
 import java.io.IOException;
diff --git a/tools/makedict/src/com/android/inputmethod/latin/CharGroupInfo.java b/tools/makedict/src/com/android/inputmethod/latin/makedict/CharGroupInfo.java
similarity index 96%
rename from tools/makedict/src/com/android/inputmethod/latin/CharGroupInfo.java
rename to tools/makedict/src/com/android/inputmethod/latin/makedict/CharGroupInfo.java
index 759cd45..444b117 100644
--- a/tools/makedict/src/com/android/inputmethod/latin/CharGroupInfo.java
+++ b/tools/makedict/src/com/android/inputmethod/latin/makedict/CharGroupInfo.java
@@ -14,7 +14,7 @@
  * the License.
  */
 
-package com.android.inputmethod.latin;
+package com.android.inputmethod.latin.makedict;
 
 import java.util.ArrayList;
 
diff --git a/tools/makedict/src/com/android/inputmethod/latin/DictionaryMaker.java b/tools/makedict/src/com/android/inputmethod/latin/makedict/DictionaryMaker.java
similarity index 99%
rename from tools/makedict/src/com/android/inputmethod/latin/DictionaryMaker.java
rename to tools/makedict/src/com/android/inputmethod/latin/makedict/DictionaryMaker.java
index fbfa424..5e39215 100644
--- a/tools/makedict/src/com/android/inputmethod/latin/DictionaryMaker.java
+++ b/tools/makedict/src/com/android/inputmethod/latin/makedict/DictionaryMaker.java
@@ -14,7 +14,7 @@
  * the License.
  */
 
-package com.android.inputmethod.latin;
+package com.android.inputmethod.latin.makedict;
 
 import java.io.File;
 import java.io.FileInputStream;
diff --git a/tools/makedict/src/com/android/inputmethod/latin/FusionDictionary.java b/tools/makedict/src/com/android/inputmethod/latin/makedict/FusionDictionary.java
similarity index 99%
rename from tools/makedict/src/com/android/inputmethod/latin/FusionDictionary.java
rename to tools/makedict/src/com/android/inputmethod/latin/makedict/FusionDictionary.java
index 08143d3..d196721 100644
--- a/tools/makedict/src/com/android/inputmethod/latin/FusionDictionary.java
+++ b/tools/makedict/src/com/android/inputmethod/latin/makedict/FusionDictionary.java
@@ -14,7 +14,7 @@
  * the License.
  */
 
-package com.android.inputmethod.latin;
+package com.android.inputmethod.latin.makedict;
 
 import java.util.ArrayList;
 import java.util.Arrays;
diff --git a/tools/makedict/src/com/android/inputmethod/latin/MakedictLog.java b/tools/makedict/src/com/android/inputmethod/latin/makedict/MakedictLog.java
similarity index 95%
rename from tools/makedict/src/com/android/inputmethod/latin/MakedictLog.java
rename to tools/makedict/src/com/android/inputmethod/latin/makedict/MakedictLog.java
index badb2ff..cff8d6f 100644
--- a/tools/makedict/src/com/android/inputmethod/latin/MakedictLog.java
+++ b/tools/makedict/src/com/android/inputmethod/latin/makedict/MakedictLog.java
@@ -14,7 +14,7 @@
  * the License.
  */
 
-package com.android.inputmethod.latin;
+package com.android.inputmethod.latin.makedict;
 
 /**
  * Wrapper to redirect log events to the right output medium.
diff --git a/tools/makedict/src/com/android/inputmethod/latin/PendingAttribute.java b/tools/makedict/src/com/android/inputmethod/latin/makedict/PendingAttribute.java
similarity index 95%
rename from tools/makedict/src/com/android/inputmethod/latin/PendingAttribute.java
rename to tools/makedict/src/com/android/inputmethod/latin/makedict/PendingAttribute.java
index cf66eef..5b41d27 100644
--- a/tools/makedict/src/com/android/inputmethod/latin/PendingAttribute.java
+++ b/tools/makedict/src/com/android/inputmethod/latin/makedict/PendingAttribute.java
@@ -14,7 +14,7 @@
  * the License.
  */
 
-package com.android.inputmethod.latin;
+package com.android.inputmethod.latin.makedict;
 
 /**
  * A not-yet-resolved attribute.
diff --git a/tools/makedict/src/com/android/inputmethod/latin/UnsupportedFormatException.java b/tools/makedict/src/com/android/inputmethod/latin/makedict/UnsupportedFormatException.java
similarity index 94%
rename from tools/makedict/src/com/android/inputmethod/latin/UnsupportedFormatException.java
rename to tools/makedict/src/com/android/inputmethod/latin/makedict/UnsupportedFormatException.java
index 511ad56..bd42fb8 100644
--- a/tools/makedict/src/com/android/inputmethod/latin/UnsupportedFormatException.java
+++ b/tools/makedict/src/com/android/inputmethod/latin/makedict/UnsupportedFormatException.java
@@ -14,7 +14,7 @@
  * the License.
  */
 
-package com.android.inputmethod.latin;
+package com.android.inputmethod.latin.makedict;
 
 /**
  * Simple exception thrown when a file format is not recognized.
diff --git a/tools/makedict/src/com/android/inputmethod/latin/Word.java b/tools/makedict/src/com/android/inputmethod/latin/makedict/Word.java
similarity index 94%
rename from tools/makedict/src/com/android/inputmethod/latin/Word.java
rename to tools/makedict/src/com/android/inputmethod/latin/makedict/Word.java
index cf6116f..c2c01e1 100644
--- a/tools/makedict/src/com/android/inputmethod/latin/Word.java
+++ b/tools/makedict/src/com/android/inputmethod/latin/makedict/Word.java
@@ -14,9 +14,9 @@
  * the License.
  */
 
-package com.android.inputmethod.latin;
+package com.android.inputmethod.latin.makedict;
 
-import com.android.inputmethod.latin.FusionDictionary.WeightedString;
+import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
 
 import java.util.ArrayList;
 
diff --git a/tools/makedict/src/com/android/inputmethod/latin/XmlDictInputOutput.java b/tools/makedict/src/com/android/inputmethod/latin/makedict/XmlDictInputOutput.java
similarity index 98%
rename from tools/makedict/src/com/android/inputmethod/latin/XmlDictInputOutput.java
rename to tools/makedict/src/com/android/inputmethod/latin/makedict/XmlDictInputOutput.java
index 77c5366..483473b 100644
--- a/tools/makedict/src/com/android/inputmethod/latin/XmlDictInputOutput.java
+++ b/tools/makedict/src/com/android/inputmethod/latin/makedict/XmlDictInputOutput.java
@@ -14,9 +14,9 @@
  * the License.
  */
 
-package com.android.inputmethod.latin;
+package com.android.inputmethod.latin.makedict;
 
-import com.android.inputmethod.latin.FusionDictionary.WeightedString;
+import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
 
 import java.io.IOException;
 import java.io.InputStream;
diff --git a/tools/makedict/tests/com/android/inputmethod/latin/BinaryDictInputOutputTest.java b/tools/makedict/tests/com/android/inputmethod/latin/BinaryDictInputOutputTest.java
index 6ac046b..9682c9b 100644
--- a/tools/makedict/tests/com/android/inputmethod/latin/BinaryDictInputOutputTest.java
+++ b/tools/makedict/tests/com/android/inputmethod/latin/BinaryDictInputOutputTest.java
@@ -14,9 +14,9 @@
  * limitations under the License.
  */
 
-package com.android.inputmethod.latin;
+package com.android.inputmethod.latin.makedict;
 
-import com.android.inputmethod.latin.FusionDictionary.Node;
+import com.android.inputmethod.latin.makedict.FusionDictionary.Node;
 
 import java.util.ArrayList;