Merge "Add a flag to demote completed suggestions"
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 505e41b..c274a6c 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -50,6 +50,7 @@
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libjni_latinime_intermediates)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libjni_latinime_intermediates)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libjni_latinime_intermediates)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libjni_latinime_intermediates)
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
 # ************************************************
diff --git a/java/AndroidManifest.xml b/java/AndroidManifest.xml
index 2baae54..b052532 100644
--- a/java/AndroidManifest.xml
+++ b/java/AndroidManifest.xml
@@ -6,7 +6,6 @@
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
     <uses-permission android:name="android.permission.READ_USER_DICTIONARY" />
     <uses-permission android:name="android.permission.WRITE_USER_DICTIONARY" />
-    <uses-permission android:name="android.permission.RECORD_AUDIO" />
     <uses-permission android:name="android.permission.READ_CONTACTS" />
 
     <application android:label="@string/english_ime_name"
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml
index 2a76321..34ce527 100644
--- a/java/res/values/attrs.xml
+++ b/java/res/values/attrs.xml
@@ -34,7 +34,9 @@
         <attr name="moreSuggestionsViewStyle" format="reference" />
         <attr name="suggestionBackgroundStyle" format="reference" />
         <attr name="suggestionPreviewBackgroundStyle" format="reference" />
-        </declare-styleable>
+        <!-- Touch position correction -->
+        <attr name="touchPositionCorrectionData" format="reference" />
+    </declare-styleable>
 
     <declare-styleable name="KeyboardView">
         <!-- Image for the key. This image needs to be a StateListDrawable, with the following
diff --git a/java/res/values/config.xml b/java/res/values/config.xml
index 9992478..bcce581 100644
--- a/java/res/values/config.xml
+++ b/java/res/values/config.xml
@@ -82,8 +82,10 @@
              will be subject to auto-correction. -->
         <item>0</item>
     </string-array>
-    <!-- Threshold of the normalized score of the best suggestion for the spell checker to declare a word to be a typo -->
-    <string name="spellchecker_typo_threshold_value" translatable="false">0.11</string>
+    <!-- Threshold of the normalized score of the best suggestion for the spell checker to declare a word to be "likely" -->
+    <string name="spellchecker_likely_threshold_value" translatable="false">0.11</string>
+    <!-- Threshold of the normalized score of any dictionary lookup to be offered as a suggestion by the spell checker -->
+    <string name="spellchecker_suggestion_threshold_value" translatable="false">0.03</string>
     <!--  Screen metrics for logging.
             0 = "mdpi phone screen"
             1 = "hdpi phone screen"
diff --git a/java/res/values/themes-basic-highcontrast.xml b/java/res/values/themes-basic-highcontrast.xml
index 9127235..bc3c847 100644
--- a/java/res/values/themes-basic-highcontrast.xml
+++ b/java/res/values/themes-basic-highcontrast.xml
@@ -28,5 +28,6 @@
         <item name="moreSuggestionsViewStyle">@style/MoreSuggestionsViewStyle</item>
         <item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle</item>
         <item name="suggestionPreviewBackgroundStyle">@style/SuggestionPreviewBackgroundStyle</item>
+        <item name="touchPositionCorrectionData">@array/touch_position_correction_data_empty</item>
     </style>
 </resources>
diff --git a/java/res/values/themes-basic.xml b/java/res/values/themes-basic.xml
index 6c0e16e..29cb9cc 100644
--- a/java/res/values/themes-basic.xml
+++ b/java/res/values/themes-basic.xml
@@ -28,5 +28,6 @@
         <item name="moreSuggestionsViewStyle">@style/MoreSuggestionsViewStyle</item>
         <item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle</item>
         <item name="suggestionPreviewBackgroundStyle">@style/SuggestionPreviewBackgroundStyle</item>
+        <item name="touchPositionCorrectionData">@array/touch_position_correction_data_empty</item>
     </style>
 </resources>
diff --git a/java/res/values/themes-gingerbread.xml b/java/res/values/themes-gingerbread.xml
index 43bff50..c4a0f80 100644
--- a/java/res/values/themes-gingerbread.xml
+++ b/java/res/values/themes-gingerbread.xml
@@ -28,5 +28,6 @@
         <item name="moreSuggestionsViewStyle">@style/MoreSuggestionsViewStyle</item>
         <item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle</item>
         <item name="suggestionPreviewBackgroundStyle">@style/SuggestionPreviewBackgroundStyle</item>
+        <item name="touchPositionCorrectionData">@array/touch_position_correction_data_gingerbread</item>
     </style>
 </resources>
diff --git a/java/res/values/themes-ics.xml b/java/res/values/themes-ics.xml
index 1235d4e..dd2b6a3 100644
--- a/java/res/values/themes-ics.xml
+++ b/java/res/values/themes-ics.xml
@@ -28,5 +28,6 @@
         <item name="moreSuggestionsViewStyle">@style/MoreSuggestionsViewStyle.IceCreamSandwich</item>
         <item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle.IceCreamSandwich</item>
         <item name="suggestionPreviewBackgroundStyle">@style/SuggestionPreviewBackgroundStyle.IceCreamSandwich</item>
+        <item name="touchPositionCorrectionData">@array/touch_position_correction_data_ice_cream_sandwich</item>
     </style>
 </resources>
diff --git a/java/res/values/themes-stone-bold.xml b/java/res/values/themes-stone-bold.xml
index 6e25f41..6e864be 100644
--- a/java/res/values/themes-stone-bold.xml
+++ b/java/res/values/themes-stone-bold.xml
@@ -28,5 +28,6 @@
         <item name="moreSuggestionsViewStyle">@style/MoreSuggestionsViewStyle</item>
         <item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle</item>
         <item name="suggestionPreviewBackgroundStyle">@style/SuggestionPreviewBackgroundStyle</item>
+        <item name="touchPositionCorrectionData">@array/touch_position_correction_data_empty</item>
     </style>
 </resources>
diff --git a/java/res/values/themes-stone.xml b/java/res/values/themes-stone.xml
index 3cbda81..64c5570 100644
--- a/java/res/values/themes-stone.xml
+++ b/java/res/values/themes-stone.xml
@@ -28,5 +28,6 @@
         <item name="moreSuggestionsViewStyle">@style/MoreSuggestionsViewStyle</item>
         <item name="suggestionBackgroundStyle">@style/SuggestionBackgroundStyle</item>
         <item name="suggestionPreviewBackgroundStyle">@style/SuggestionPreviewBackgroundStyle</item>
+        <item name="touchPositionCorrectionData">@array/touch_position_correction_data_empty</item>
     </style>
 </resources>
diff --git a/java/res/values/touch-position-correction.xml b/java/res/values/touch-position-correction.xml
new file mode 100644
index 0000000..0a0e4e5
--- /dev/null
+++ b/java/res/values/touch-position-correction.xml
@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2011, 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">
+    <!--
+        An entry of the touch_position_correction word should be:
+        1. (float) (touch_center_x - key_center_x) / key_width
+        2. (float) (touch_center_y - key_center_y) / key_height
+        3. (float) sweet_spot_radius / (key_width^2 + key_height^2)
+     -->
+
+    <string-array
+        name="touch_position_correction_data_empty"
+        translatable="false"
+    >
+        <!-- empty -->
+    </string-array>
+
+    <string-array
+        name="touch_position_correction_data_gingerbread"
+        translatable="false"
+    >
+        <!-- First row -->
+        <item>0.0091285</item>
+        <item>0.1193203</item>
+        <item>0.1622607</item>
+
+        <!-- Second row -->
+        <item>-0.0233128</item>
+        <item>0.1379798</item>
+        <item>0.1585229</item>
+
+        <!-- Third row -->
+        <item>-0.0080185</item>
+        <item>0.1911477</item>
+        <item>0.1570948</item>
+    </string-array>
+
+    <string-array
+        name="touch_position_correction_data_ice_cream_sandwich"
+        translatable="false"
+    >
+        <!-- First row -->
+        <item>0.0038756</item>
+        <item>-0.0005677</item>
+        <item>0.1577026</item>
+
+        <!-- Second row -->
+        <item>-0.0236678</item>
+        <item>0.0381731</item>
+        <item>0.1529972</item>
+
+        <!-- Third row -->
+        <item>-0.0086827</item>
+        <item>0.0880847</item>
+        <item>0.1522819</item>
+    </string-array>
+</resources>
\ No newline at end of file
diff --git a/java/res/xml/kbd_rows_number.xml b/java/res/xml/kbd_rows_number.xml
index 21d0656..90ac568 100644
--- a/java/res/xml/kbd_rows_number.xml
+++ b/java/res/xml/kbd_rows_number.xml
@@ -36,6 +36,7 @@
                     latin:keyStyle="num2KeyStyle" />
                 <Key
                     latin:keyStyle="num3KeyStyle" />
+                <Spacer />
             </Row>
             <Row>
                 <Key
@@ -44,6 +45,7 @@
                     latin:keyStyle="num5KeyStyle" />
                 <Key
                     latin:keyStyle="num6KeyStyle" />
+                <Spacer />
             </Row>
             <Row>
                 <Key
diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java
index 045f0d7..9e4c1ea 100644
--- a/java/src/com/android/inputmethod/keyboard/Keyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java
@@ -138,7 +138,8 @@
 
         mProximityInfo = new ProximityInfo(
                 params.GRID_WIDTH, params.GRID_HEIGHT, mOccupiedWidth, mOccupiedHeight,
-                mMostCommonKeyWidth, mKeys);
+                mMostCommonKeyWidth, mMostCommonKeyHeight, mKeys, params.mTouchPositionCorrectionXs,
+                params.mTouchPositionCorrectionYs, params.mTouchPositionCorrectionRadii);
     }
 
     public ProximityInfo getProximityInfo() {
diff --git a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
index 71b46d6..d35b1a9 100644
--- a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
+++ b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
@@ -16,6 +16,9 @@
 
 package com.android.inputmethod.keyboard;
 
+import android.graphics.Rect;
+
+import com.android.inputmethod.keyboard.Key;
 import com.android.inputmethod.latin.Utils;
 import com.android.inputmethod.latin.spellcheck.SpellCheckerProximityInfo;
 
@@ -27,9 +30,9 @@
     public static final int MAX_PROXIMITY_CHARS_SIZE = 16;
     /** Number of key widths from current touch point to search for nearest keys. */
     private static float SEARCH_DISTANCE = 1.2f;
-    private static final int UNKNOWN_THEME = -1;
     private static final int[] EMPTY_INT_ARRAY = new int[0];
 
+    private final int mKeyHeight;
     private final int mGridWidth;
     private final int mGridHeight;
     private final int mGridSize;
@@ -40,8 +43,13 @@
     private final int mKeyboardHeight;
     private final int[][] mGridNeighbors;
 
-    ProximityInfo(
-            int gridWidth, int gridHeight, int minWidth, int height, int keyWidth, List<Key> keys) {
+    private final float[] mTouchPositionCorrectionXs;
+    private final float[] mTouchPositionCorrectionYs;
+    private final float[] mTouchPositionCorrectionRadii;
+
+    ProximityInfo(int gridWidth, int gridHeight, int minWidth, int height, int keyWidth,
+            int keyHeight, List<Key> keys, float[] touchPositionCorrectionXs,
+            float[] touchPositionCorrectionYs, float[] touchPositionCorrectionRadii) {
         mGridWidth = gridWidth;
         mGridHeight = gridHeight;
         mGridSize = mGridWidth * mGridHeight;
@@ -49,6 +57,10 @@
         mCellHeight = (height + mGridHeight - 1) / mGridHeight;
         mKeyboardMinWidth = minWidth;
         mKeyboardHeight = height;
+        mKeyHeight = keyHeight;
+        mTouchPositionCorrectionXs = touchPositionCorrectionXs;
+        mTouchPositionCorrectionYs = touchPositionCorrectionYs;
+        mTouchPositionCorrectionRadii = touchPositionCorrectionRadii;
         mGridNeighbors = new int[mGridSize][];
         if (minWidth == 0 || height == 0) {
             // No proximity required. Keyboard might be mini keyboard.
@@ -58,7 +70,7 @@
     }
 
     public static ProximityInfo createDummyProximityInfo() {
-        return new ProximityInfo(1, 1, 1, 1, 1, Collections.<Key>emptyList());
+        return new ProximityInfo(1, 1, 1, 1, 1, 1, Collections.<Key>emptyList(), null, null, null);
     }
 
     public static ProximityInfo createSpellCheckerProximityInfo() {
@@ -67,7 +79,7 @@
                 spellCheckerProximityInfo.setProximityInfoNative(
                         SpellCheckerProximityInfo.ROW_SIZE,
                         480, 300, 10, 3, SpellCheckerProximityInfo.PROXIMITY,
-                        0, null, null, null, null, null, UNKNOWN_THEME);
+                        0, null, null, null, null, null, null, null, null);
         return spellCheckerProximityInfo;
     }
 
@@ -78,7 +90,8 @@
     private native int setProximityInfoNative(int maxProximityCharsSize, int displayWidth,
             int displayHeight, int gridWidth, int gridHeight, int[] proximityCharsArray,
             int keyCount, int[] keyXCoordinates, int[] keyYCoordinates,
-            int[] keyWidths, int[] keyHeights, int[] keyCharCodes, int themeId);
+            int[] keyWidths, int[] keyHeights, int[] keyCharCodes,
+            float[] sweetSpotCenterX, float[] sweetSpotCenterY, float[] sweetSpotRadii);
     private native void releaseProximityInfoNative(int nativeProximityInfo);
 
     private final void setProximityInfo(int[][] gridNeighborKeyIndexes, int keyboardWidth,
@@ -93,12 +106,11 @@
             }
         }
         final int keyCount = keys.size();
-        int[] keyXCoordinates = new int[keyCount];
-        int[] keyYCoordinates = new int[keyCount];
-        int[] keyWidths = new int[keyCount];
-        int[] keyHeights = new int[keyCount];
-        int[] keyCharCodes = new int[keyCount];
-        final int themeId = 5;    // TODO: Use real theme id.
+        final int[] keyXCoordinates = new int[keyCount];
+        final int[] keyYCoordinates = new int[keyCount];
+        final int[] keyWidths = new int[keyCount];
+        final int[] keyHeights = new int[keyCount];
+        final int[] keyCharCodes = new int[keyCount];
         for (int i = 0; i < keyCount; ++i) {
             final Key key = keys.get(i);
             keyXCoordinates[i] = key.mX;
@@ -107,10 +119,51 @@
             keyHeights[i] = key.mHeight;
             keyCharCodes[i] = key.mCode;
         }
+
+        final boolean hasTouchPositionCorrectionData =
+                mTouchPositionCorrectionXs != null
+                && mTouchPositionCorrectionYs != null
+                && mTouchPositionCorrectionRadii != null
+                && mTouchPositionCorrectionXs.length > 0
+                && mTouchPositionCorrectionYs.length > 0
+                && mTouchPositionCorrectionRadii.length > 0;
+        final float[] sweetSpotCenterXs =
+                hasTouchPositionCorrectionData ? new float[keyCount] : null;
+        final float[] sweetSpotCenterYs =
+                hasTouchPositionCorrectionData ? new float[keyCount] : null;
+        final float[] sweetSpotRadii =
+                hasTouchPositionCorrectionData ? new float[keyCount] : null;
+        if (hasTouchPositionCorrectionData) {
+            calculateSweetSpot(keys, sweetSpotCenterXs, sweetSpotCenterYs, sweetSpotRadii);
+        }
+
         mNativeProximityInfo = setProximityInfoNative(MAX_PROXIMITY_CHARS_SIZE,
                 keyboardWidth, keyboardHeight, mGridWidth, mGridHeight, proximityCharsArray,
                 keyCount, keyXCoordinates, keyYCoordinates, keyWidths, keyHeights, keyCharCodes,
-                themeId);
+                sweetSpotCenterXs, sweetSpotCenterYs, sweetSpotRadii);
+    }
+
+    private void calculateSweetSpot(List<Key> keys, float[] sweetSpotCenterXs,
+            float[] sweetSpotCenterYs, float[] sweetSpotRadii) {
+        final int keyCount = keys.size();
+        for (int i = 0; i < keyCount; ++i) {
+            final Key key = keys.get(i);
+            final Rect hitBox = key.mHitBox;
+            final int row = hitBox.top / mKeyHeight;
+            if (row < mTouchPositionCorrectionRadii.length) {
+                final float hitBoxCenterX = (hitBox.left + hitBox.right) * 0.5f;
+                final float hitBoxCenterY = (hitBox.top + hitBox.bottom) * 0.5f;
+                final float hitBoxWidth = hitBox.right - hitBox.left;
+                final float hitBoxHeight = hitBox.bottom - hitBox.top;
+                final float x = mTouchPositionCorrectionXs[row];
+                final float y = mTouchPositionCorrectionYs[row];
+                final float radius = mTouchPositionCorrectionRadii[row];
+                sweetSpotCenterXs[i] = hitBoxCenterX + x * hitBoxWidth;
+                sweetSpotCenterYs[i] = hitBoxCenterY + y * hitBoxHeight;
+                sweetSpotRadii[i] = radius
+                        * (float)Math.sqrt(hitBoxWidth * hitBoxWidth + hitBoxHeight * hitBoxHeight);
+            }
+        }
     }
 
     public int getNativeProximityInfo() {
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
index 187a1ad..46836da 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
@@ -30,6 +30,7 @@
 import com.android.inputmethod.keyboard.Key;
 import com.android.inputmethod.keyboard.Keyboard;
 import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.latin.LatinImeLogger;
 import com.android.inputmethod.latin.R;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -126,6 +127,8 @@
     private static final int DEFAULT_KEYBOARD_COLUMNS = 10;
     private static final int DEFAULT_KEYBOARD_ROWS = 4;
 
+    private static final int TOUCH_POSITION_CORRECTION_RECORD_SIZE = 3;
+
     protected final KP mParams;
     protected final Context mContext;
     protected final Resources mResources;
@@ -248,10 +251,62 @@
         mParams.mThemeId = a.getInt(R.styleable.KeyboardTheme_themeId, 0);
         a.recycle();
 
+        if (!setTouchPositionCorrectionData(context)) {
+            // In the regression test, setTouchPositionCorrectionData() fails
+            mParams.mTouchPositionCorrectionXs = null;
+            mParams.mTouchPositionCorrectionYs = null;
+            mParams.mTouchPositionCorrectionRadii = null;
+        }
+
         mParams.GRID_WIDTH = res.getInteger(R.integer.config_keyboard_grid_width);
         mParams.GRID_HEIGHT = res.getInteger(R.integer.config_keyboard_grid_height);
     }
 
+    private boolean setTouchPositionCorrectionData(Context context) {
+        final TypedArray a = context.obtainStyledAttributes(R.styleable.KeyboardTheme);
+        final int resourceId = a.getResourceId(
+                R.styleable.KeyboardTheme_touchPositionCorrectionData, 0);
+        if (resourceId == 0) {
+            // In the regression test, we cannot use theme resources
+            // TODO: Fix this
+            return false;
+        }
+        final String[] data = context.getResources().getStringArray(resourceId);
+        a.recycle();
+        final int dataLength = data.length;
+        if (dataLength % TOUCH_POSITION_CORRECTION_RECORD_SIZE != 0) {
+            if (LatinImeLogger.sDBG) {
+                throw new RuntimeException("the size of touch position correction data is invalid");
+            }
+            return false;
+        }
+        final int length = dataLength / TOUCH_POSITION_CORRECTION_RECORD_SIZE;
+        mParams.mTouchPositionCorrectionXs = new float[length];
+        mParams.mTouchPositionCorrectionYs = new float[length];
+        mParams.mTouchPositionCorrectionRadii = new float[length];
+        try {
+            for (int i = 0; i < dataLength; ++i) {
+                final int type = i % TOUCH_POSITION_CORRECTION_RECORD_SIZE;
+                final int index = i / TOUCH_POSITION_CORRECTION_RECORD_SIZE;
+                final float value = Float.parseFloat(data[i]);
+                if (type == 0) {
+                    mParams.mTouchPositionCorrectionXs[index] = value;
+                } else if (type == 1) {
+                    mParams.mTouchPositionCorrectionYs[index] = value;
+                } else {
+                    mParams.mTouchPositionCorrectionRadii[index] = value;
+                }
+            }
+        } catch (NumberFormatException e) {
+            if (LatinImeLogger.sDBG) {
+                throw new RuntimeException(
+                        "the number format for touch position correction data is invalid");
+            }
+            return false;
+        }
+        return true;
+    }
+
     public KeyboardBuilder<KP> load(KeyboardId id) {
         mParams.mId = id;
         try {
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java
index 97f58fa..d1aea72 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParams.java
@@ -68,6 +68,10 @@
     public int mMostCommonKeyHeight = 0;
     public int mMostCommonKeyWidth = 0;
 
+    public float[] mTouchPositionCorrectionXs;
+    public float[] mTouchPositionCorrectionYs;
+    public float[] mTouchPositionCorrectionRadii;
+
     protected void clearKeys() {
         mKeys.clear();
         mShiftKeys.clear();
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 48a1f8b..cf1cb8f 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -1661,9 +1661,13 @@
                 builder.setTypedWordValid(typedWordValid).setHasMinimalSuggestion(
                         autoCorrectionAvailable);
             } else {
-                final SuggestedWords previousSuggestions = mSuggestionsView.getSuggestions();
-                if (previousSuggestions == mSettingsValues.mSuggestPuncList)
-                    return;
+                SuggestedWords previousSuggestions = mSuggestionsView.getSuggestions();
+                if (previousSuggestions == mSettingsValues.mSuggestPuncList) {
+                    if (builder.size() == 0) {
+                        return;
+                    }
+                    previousSuggestions = SuggestedWords.EMPTY;
+                }
                 builder.addTypedWordAndPreviousSuggestions(typedWord, previousSuggestions);
             }
         }
diff --git a/java/src/com/android/inputmethod/latin/MoreSuggestionsView.java b/java/src/com/android/inputmethod/latin/MoreSuggestionsView.java
index 5a2eb16..51f6c04 100644
--- a/java/src/com/android/inputmethod/latin/MoreSuggestionsView.java
+++ b/java/src/com/android/inputmethod/latin/MoreSuggestionsView.java
@@ -163,8 +163,6 @@
                 - (container.getMeasuredHeight() - container.getPaddingBottom())
                 + parentView.getPaddingTop() + mCoordinates[1];
 
-        window.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
-        window.setOutsideTouchable(true);
         window.setContentView(container);
         window.setWidth(container.getMeasuredWidth());
         window.setHeight(container.getMeasuredHeight());
@@ -221,22 +219,6 @@
     };
 
     @Override
-    public boolean dispatchTouchEvent(MotionEvent me) {
-        final int index = me.getActionIndex();
-        final int id = me.getPointerId(index);
-        final PointerTracker tracker = PointerTracker.getPointerTracker(id, this);
-        final int x = (int)me.getX(index);
-        final int y = (int)me.getY(index);
-        final boolean inside = (x >= 0 && x < getWidth() && y >= 0 && y < getHeight());
-        if (inside || tracker.isInSlidingKeyInput()) {
-            return super.dispatchTouchEvent(me);
-        } else {
-            dismissMoreKeysPanel();
-            return true;
-        }
-    }
-
-    @Override
     public boolean onTouchEvent(MotionEvent me) {
         final int action = me.getAction();
         final long eventTime = me.getEventTime();
diff --git a/java/src/com/android/inputmethod/latin/SuggestionsView.java b/java/src/com/android/inputmethod/latin/SuggestionsView.java
index 9d0e42a..fe54f4a 100644
--- a/java/src/com/android/inputmethod/latin/SuggestionsView.java
+++ b/java/src/com/android/inputmethod/latin/SuggestionsView.java
@@ -27,6 +27,7 @@
 import android.graphics.Rect;
 import android.graphics.Typeface;
 import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.Message;
 import android.os.SystemClock;
@@ -506,10 +507,22 @@
         mMoreSuggestionsView = (MoreSuggestionsView)mMoreSuggestionsContainer
                 .findViewById(R.id.more_suggestions_view);
         mMoreSuggestionsBuilder = new MoreSuggestions.Builder(mMoreSuggestionsView);
-        mMoreSuggestionsWindow = new PopupWindow(context);
-        mMoreSuggestionsWindow.setWindowLayoutMode(
+
+        final PopupWindow moreWindow = new PopupWindow(context);
+        moreWindow.setWindowLayoutMode(
                 ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
-        mMoreSuggestionsWindow.setBackgroundDrawable(null);
+        moreWindow.setBackgroundDrawable(new ColorDrawable(android.R.color.transparent));
+        moreWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
+        moreWindow.setFocusable(true);
+        moreWindow.setOutsideTouchable(true);
+        moreWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {
+            @Override
+            public void onDismiss() {
+                mKeyboardView.dimEntireKeyboard(false);
+            }
+        });
+        mMoreSuggestionsWindow = moreWindow;
+
         final Resources res = context.getResources();
         mMoreSuggestionsModalTolerance = res.getDimensionPixelOffset(
                 R.dimen.more_suggestions_modal_tolerance);
@@ -517,21 +530,6 @@
                 context, mMoreSuggestionsSlidingListener);
     }
 
-    private final View.OnTouchListener mMoreSuggestionsCanceller = new View.OnTouchListener() {
-        @Override
-        public boolean onTouch(View view, MotionEvent me) {
-            if (!mMoreSuggestionsWindow.isShowing()) return false;
-
-            switch (me.getAction()) {
-            case MotionEvent.ACTION_UP:
-            case MotionEvent.ACTION_POINTER_UP:
-                return mMoreSuggestionsView.dismissMoreKeysPanel();
-            default:
-                return true;
-            }
-        }
-    };
-
     /**
      * A connection back to the input method.
      * @param listener
@@ -726,8 +724,6 @@
     private boolean dismissMoreSuggestions() {
         if (mMoreSuggestionsWindow.isShowing()) {
             mMoreSuggestionsWindow.dismiss();
-            mKeyboardView.dimEntireKeyboard(false);
-            mKeyboardView.setOnTouchListener(null);
             return true;
         }
         return false;
@@ -767,7 +763,6 @@
             mOriginX = mLastX;
             mOriginY = mLastY;
             mKeyboardView.dimEntireKeyboard(true);
-            mKeyboardView.setOnTouchListener(mMoreSuggestionsCanceller);
             for (int i = 0; i < params.mSuggestionsCountInStrip; i++) {
                 mWords.get(i).setPressed(false);
             }
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
index 2546df0..1d5986e 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
@@ -70,11 +70,17 @@
     private Map<String, Dictionary> mUserDictionaries =
             Collections.synchronizedMap(new TreeMap<String, Dictionary>());
 
-    private double mTypoThreshold;
+    // The threshold for a candidate to be offered as a suggestion.
+    private double mSuggestionThreshold;
+    // The threshold for a suggestion to be considered "likely".
+    private double mLikelyThreshold;
 
     @Override public void onCreate() {
         super.onCreate();
-        mTypoThreshold = Double.parseDouble(getString(R.string.spellchecker_typo_threshold_value));
+        mSuggestionThreshold =
+                Double.parseDouble(getString(R.string.spellchecker_suggestion_threshold_value));
+        mLikelyThreshold =
+                Double.parseDouble(getString(R.string.spellchecker_likely_threshold_value));
     }
 
     @Override
@@ -95,6 +101,9 @@
         private final int DEFAULT_SUGGESTION_LENGTH = 16;
         private final ArrayList<CharSequence> mSuggestions;
         private final int[] mScores;
+        private final String mOriginalText;
+        private final double mSuggestionThreshold;
+        private final double mLikelyThreshold;
         private final int mMaxLength;
         private int mLength = 0;
 
@@ -103,7 +112,11 @@
         private String mBestSuggestion = null;
         private int mBestScore = Integer.MIN_VALUE; // As small as possible
 
-        SuggestionsGatherer(final int maxLength) {
+        SuggestionsGatherer(final String originalText, final double suggestionThreshold,
+                final double likelyThreshold, final int maxLength) {
+            mOriginalText = originalText;
+            mSuggestionThreshold = suggestionThreshold;
+            mLikelyThreshold = likelyThreshold;
             mMaxLength = maxLength;
             mSuggestions = new ArrayList<CharSequence>(maxLength + 1);
             mScores = new int[mMaxLength];
@@ -117,28 +130,42 @@
             // if it doesn't. See documentation for binarySearch.
             final int insertIndex = positionIndex >= 0 ? positionIndex : -positionIndex - 1;
 
+            if (insertIndex == 0 && mLength >= mMaxLength) {
+                // In the future, we may want to keep track of the best suggestion score even if
+                // we are asked for 0 suggestions. In this case, we can use the following
+                // (tested) code to keep it:
+                // If the maxLength is 0 (should never be less, but if it is, it's treated as 0)
+                // then we need to keep track of the best suggestion in mBestScore and
+                // mBestSuggestion. This is so that we know whether the best suggestion makes
+                // the score cutoff, since we need to know that to return a meaningful
+                // looksLikeTypo.
+                // if (0 >= mMaxLength) {
+                //     if (score > mBestScore) {
+                //         mBestScore = score;
+                //         mBestSuggestion = new String(word, wordOffset, wordLength);
+                //     }
+                // }
+                return true;
+            }
+
+            // Compute the normalized score and skip this word if it's normalized score does not
+            // make the threshold.
+            final String wordString = new String(word, wordOffset, wordLength);
+            final double normalizedScore =
+                    Utils.calcNormalizedScore(mOriginalText, wordString, score);
+            if (normalizedScore < mSuggestionThreshold) {
+                if (DBG) Log.i(TAG, wordString + " does not make the score threshold");
+                return true;
+            }
+
             if (mLength < mMaxLength) {
                 final int copyLen = mLength - insertIndex;
                 ++mLength;
                 System.arraycopy(mScores, insertIndex, mScores, insertIndex + 1, copyLen);
-                mSuggestions.add(insertIndex, new String(word, wordOffset, wordLength));
+                mSuggestions.add(insertIndex, wordString);
             } else {
-                if (insertIndex == 0) {
-                    // If the maxLength is 0 (should never be less, but if it is, it's treated as 0)
-                    // then we need to keep track of the best suggestion in mBestScore and
-                    // mBestSuggestion. This is so that we know whether the best suggestion makes
-                    // the score cutoff, since we need to know that to return a meaningful
-                    // looksLikeTypo.
-                    if (0 >= mMaxLength) {
-                        if (score > mBestScore) {
-                            mBestScore = score;
-                            mBestSuggestion = new String(word, wordOffset, wordLength);
-                        }
-                    }
-                    return true;
-                }
                 System.arraycopy(mScores, 1, mScores, 0, insertIndex);
-                mSuggestions.add(insertIndex, new String(word, wordOffset, wordLength));
+                mSuggestions.add(insertIndex, wordString);
                 mSuggestions.remove(0);
             }
             mScores[insertIndex] = score;
@@ -146,8 +173,7 @@
             return true;
         }
 
-        public Result getResults(final CharSequence originalText, final double threshold,
-                final int capitalizeType, final Locale locale) {
+        public Result getResults(final int capitalizeType, final Locale locale) {
             final String[] gatheredSuggestions;
             final boolean hasLikelySuggestions;
             if (0 == mLength) {
@@ -160,8 +186,8 @@
                 } else {
                     gatheredSuggestions = EMPTY_STRING_ARRAY;
                     final double normalizedScore =
-                            Utils.calcNormalizedScore(originalText, mBestSuggestion, mBestScore);
-                    hasLikelySuggestions = (normalizedScore > threshold);
+                            Utils.calcNormalizedScore(mOriginalText, mBestSuggestion, mBestScore);
+                    hasLikelySuggestions = (normalizedScore > mLikelyThreshold);
                 }
             } else {
                 if (DBG) {
@@ -194,11 +220,12 @@
                 final int bestScore = mScores[mLength - 1];
                 final CharSequence bestSuggestion = mSuggestions.get(0);
                 final double normalizedScore =
-                        Utils.calcNormalizedScore(originalText, bestSuggestion, bestScore);
-                hasLikelySuggestions = (normalizedScore > threshold);
+                        Utils.calcNormalizedScore(mOriginalText, bestSuggestion, bestScore);
+                hasLikelySuggestions = (normalizedScore > mLikelyThreshold);
                 if (DBG) {
                     Log.i(TAG, "Best suggestion : " + bestSuggestion + ", score " + bestScore);
-                    Log.i(TAG, "Normalized score = " + normalizedScore + " (threshold " + threshold
+                    Log.i(TAG, "Normalized score = " + normalizedScore
+                            + " (threshold " + mLikelyThreshold
                             + ") => hasLikelySuggestions = " + hasLikelySuggestions);
                 }
             }
@@ -350,8 +377,8 @@
                 }
 
                 // TODO: Don't gather suggestions if the limit is <= 0 unless necessary
-                final SuggestionsGatherer suggestionsGatherer =
-                        new SuggestionsGatherer(suggestionsLimit);
+                final SuggestionsGatherer suggestionsGatherer = new SuggestionsGatherer(text,
+                        mService.mSuggestionThreshold, mService.mLikelyThreshold, suggestionsLimit);
                 final WordComposer composer = new WordComposer();
                 final int length = text.length();
                 for (int i = 0; i < length; ++i) {
@@ -392,8 +419,8 @@
                     }
                 }
 
-                final SuggestionsGatherer.Result result = suggestionsGatherer.getResults(text,
-                        mService.mTypoThreshold, capitalizeType, mLocale);
+                final SuggestionsGatherer.Result result = suggestionsGatherer.getResults(
+                        capitalizeType, mLocale);
 
                 if (DBG) {
                     Log.i(TAG, "Spell checking results for " + text + " with suggestion limit "
diff --git a/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp b/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp
index 07cee40..595ea2f 100644
--- a/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp
+++ b/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp
@@ -32,18 +32,27 @@
         jint maxProximityCharsSize, jint displayWidth, jint displayHeight, jint gridWidth,
         jint gridHeight, jintArray proximityCharsArray, jint keyCount,
         jintArray keyXCoordinateArray, jintArray keyYCoordinateArray, jintArray keyWidthArray,
-        jintArray keyHeightArray, jintArray keyCharCodeArray, jint themeId) {
+        jintArray keyHeightArray, jintArray keyCharCodeArray,
+        jfloatArray sweetSpotCenterXArray, jfloatArray sweetSpotCenterYArray,
+        jfloatArray sweetSpotRadiusArray) {
     jint *proximityChars = env->GetIntArrayElements(proximityCharsArray, NULL);
     jint *keyXCoordinates = safeGetIntArrayElements(env, keyXCoordinateArray);
     jint *keyYCoordinates = safeGetIntArrayElements(env, keyYCoordinateArray);
     jint *keyWidths = safeGetIntArrayElements(env, keyWidthArray);
     jint *keyHeights = safeGetIntArrayElements(env, keyHeightArray);
     jint *keyCharCodes = safeGetIntArrayElements(env, keyCharCodeArray);
+    jfloat *sweetSpotCenterXs = safeGetFloatArrayElements(env, sweetSpotCenterXArray);
+    jfloat *sweetSpotCenterYs = safeGetFloatArrayElements(env, sweetSpotCenterYArray);
+    jfloat *sweetSpotRadii = safeGetFloatArrayElements(env, sweetSpotRadiusArray);
     ProximityInfo *proximityInfo = new ProximityInfo(maxProximityCharsSize, displayWidth,
             displayHeight, gridWidth, gridHeight, (const uint32_t*)proximityChars,
             keyCount, (const int32_t*)keyXCoordinates, (const int32_t*)keyYCoordinates,
             (const int32_t*)keyWidths, (const int32_t*)keyHeights, (const int32_t*)keyCharCodes,
-            themeId);
+            (const float*)sweetSpotCenterXs, (const float*)sweetSpotCenterYs,
+            (const float*)sweetSpotRadii);
+    safeReleaseFloatArrayElements(env, sweetSpotRadiusArray, sweetSpotRadii);
+    safeReleaseFloatArrayElements(env, sweetSpotCenterYArray, sweetSpotCenterYs);
+    safeReleaseFloatArrayElements(env, sweetSpotCenterXArray, sweetSpotCenterXs);
     safeReleaseIntArrayElements(env, keyCharCodeArray, keyCharCodes);
     safeReleaseIntArrayElements(env, keyHeightArray, keyHeights);
     safeReleaseIntArrayElements(env, keyWidthArray, keyWidths);
@@ -60,7 +69,8 @@
 }
 
 static JNINativeMethod sKeyboardMethods[] = {
-    {"setProximityInfoNative", "(IIIII[II[I[I[I[I[II)I", (void*)latinime_Keyboard_setProximityInfo},
+    {"setProximityInfoNative", "(IIIII[II[I[I[I[I[I[F[F[F)I",
+            (void*)latinime_Keyboard_setProximityInfo},
     {"releaseProximityInfoNative", "(I)V", (void*)latinime_Keyboard_release}
 };
 
diff --git a/native/jni/jni_common.h b/native/jni/jni_common.h
index dbf6d3e..9548e1b 100644
--- a/native/jni/jni_common.h
+++ b/native/jni/jni_common.h
@@ -35,12 +35,26 @@
     }
 }
 
+inline jfloat *safeGetFloatArrayElements(JNIEnv *env, jfloatArray jArray) {
+    if (jArray) {
+        return env->GetFloatArrayElements(jArray, NULL);
+    } else {
+        return NULL;
+    }
+}
+
 inline void safeReleaseIntArrayElements(JNIEnv *env, jintArray jArray, jint *cArray) {
     if (jArray) {
         env->ReleaseIntArrayElements(jArray, cArray, 0);
     }
 }
 
+inline void safeReleaseFloatArrayElements(JNIEnv *env, jfloatArray jArray, jfloat *cArray) {
+    if (jArray) {
+        env->ReleaseFloatArrayElements(jArray, cArray, 0);
+    }
+}
+
 } // namespace latinime
 
 #endif // LATINIME_JNI_COMMON_H
diff --git a/native/src/defines_touch_position_correction.h b/native/src/defines_touch_position_correction.h
deleted file mode 100644
index 1229068..0000000
--- a/native/src/defines_touch_position_correction.h
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
-**
-** Copyright 2011, The Android Open Source Project
-**
-** Licensed under the Apache License, Version 2.0 (the "License");
-** you may not use this file except in compliance with the License.
-** You may obtain a copy of the License at
-**
-**     http://www.apache.org/licenses/LICENSE-2.0
-**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
-** limitations under the License.
-*/
-
-#ifndef LATINIME_DEFINES_TOUCH_POSITION_CORRECTION_H
-#define LATINIME_DEFINES_TOUCH_POSITION_CORRECTION_H
-
-#define OUTER_SWEET_SPOT_RADIUS_RATIO 1.3
-
-static const char* TOUCH_POSITION_CORRECTION_GROUPS[] = {
-        "qwertyuiop",
-        "a",
-        "sdfghjk",
-        "l",
-        "zxcvbnm",
-};
-
-// (center X) / (key width)
-static const float RELATIVE_TOUCH_CENTER_X[] = {
-        0,          // qwertyuiop
-        -0.26871,   // a
-        0,          // sdfghjk
-        0.028050,   // l
-        0,          // zxcvbnm
-};
-
-// (center Y) / (key height)
-static const float RELATIVE_TOUCH_CENTER_Y[] = {
-        0.192088,   // qwertyuiop
-        0.214100,   // a
-        0.216640,   // sdfghjk
-        0.233288,   // l
-        0.286598,   // zxcvbnm
-};
-
-// (sweet spot radius) / ((key width) + (key height))
-static const float SWEET_SPOT_RADIUS[] = {
-        0.148955,   // qwertyuiop
-        0.185437,   // a
-        0.145522,   // sdfghjk
-        0.156882,   // l
-        0.144211,   // zxcvbnm
-};
-
-#define CORRECTION_GROUP_COUNT \
-    ((int)(sizeof(TOUCH_POSITION_CORRECTION_GROUPS) / sizeof(TOUCH_POSITION_CORRECTION_GROUPS[0])))
-
-#endif // LATINIME_DEFINES_TOUCH_POSITION_CORRECTION_H
diff --git a/native/src/proximity_info.cpp b/native/src/proximity_info.cpp
index 58842b9..4ff6e0a 100644
--- a/native/src/proximity_info.cpp
+++ b/native/src/proximity_info.cpp
@@ -20,7 +20,6 @@
 
 #define LOG_TAG "LatinIME: proximity_info.cpp"
 
-#include "defines_touch_position_correction.h"
 #include "dictionary.h"
 #include "proximity_info.h"
 
@@ -38,12 +37,13 @@
         const int keyboardHeight, const int gridWidth, const int gridHeight,
         const uint32_t *proximityCharsArray, const int keyCount, const int32_t *keyXCoordinates,
         const int32_t *keyYCoordinates, const int32_t *keyWidths, const int32_t *keyHeights,
-        const int32_t *keyCharCodes, int themeId)
+        const int32_t *keyCharCodes, const float *sweetSpotCenterXs, const float *sweetSpotCenterYs,
+        const float *sweetSpotRadii)
         : MAX_PROXIMITY_CHARS_SIZE(maxProximityCharsSize), KEYBOARD_WIDTH(keyboardWidth),
           KEYBOARD_HEIGHT(keyboardHeight), GRID_WIDTH(gridWidth), GRID_HEIGHT(gridHeight),
           CELL_WIDTH((keyboardWidth + gridWidth - 1) / gridWidth),
           CELL_HEIGHT((keyboardHeight + gridHeight - 1) / gridHeight),
-          KEY_COUNT(min(keyCount, MAX_KEY_COUNT_IN_A_KEYBOARD)), THEME_ID(themeId) {
+          KEY_COUNT(min(keyCount, MAX_KEY_COUNT_IN_A_KEYBOARD)) {
     const int len = GRID_WIDTH * GRID_HEIGHT * MAX_PROXIMITY_CHARS_SIZE;
     mProximityCharsArray = new uint32_t[len];
     if (DEBUG_PROXIMITY_INFO) {
@@ -56,33 +56,24 @@
     copyOrFillZero(mKeyWidths, keyWidths, KEY_COUNT * sizeof(mKeyWidths[0]));
     copyOrFillZero(mKeyHeights, keyHeights, KEY_COUNT * sizeof(mKeyHeights[0]));
     copyOrFillZero(mKeyCharCodes, keyCharCodes, KEY_COUNT * sizeof(mKeyCharCodes[0]));
+    copyOrFillZero(mSweetSpotCenterXs, sweetSpotCenterXs,
+            KEY_COUNT * sizeof(mSweetSpotCenterXs[0]));
+    copyOrFillZero(mSweetSpotCenterYs, sweetSpotCenterYs,
+            KEY_COUNT * sizeof(mSweetSpotCenterYs[0]));
+    copyOrFillZero(mSweetSpotRadii, sweetSpotRadii, KEY_COUNT * sizeof(mSweetSpotRadii[0]));
 
-    initializeCodeToGroup();
     initializeCodeToKeyIndex();
 }
 
-// Build the reversed look up table from the char code to the index in its group.
-// see TOUCH_POSITION_CORRECTION_GROUPS
-void ProximityInfo::initializeCodeToGroup() {
-    memset(mCodeToGroup, -1, (MAX_GROUPED_CHAR_CODE + 1) * sizeof(mCodeToGroup[0]));
-    for (int i = 0; i < CORRECTION_GROUP_COUNT; ++i) {
-        const char *group = TOUCH_POSITION_CORRECTION_GROUPS[i];
-        for (int j = 0; group[j]; ++j) {
-            const int code = group[j];
-            if (0 <= code && code <= MAX_GROUPED_CHAR_CODE)
-                mCodeToGroup[code] = i;
-        }
-    }
-}
-
 // Build the reversed look up table from the char code to the index in mKeyXCoordinates,
 // mKeyYCoordinates, mKeyWidths, mKeyHeights, mKeyCharCodes.
 void ProximityInfo::initializeCodeToKeyIndex() {
-    memset(mCodeToKeyIndex, -1, (MAX_GROUPED_CHAR_CODE + 1) * sizeof(mCodeToKeyIndex[0]));
+    memset(mCodeToKeyIndex, -1, (MAX_CHAR_CODE + 1) * sizeof(mCodeToKeyIndex[0]));
     for (int i = 0; i < KEY_COUNT; ++i) {
         const int code = mKeyCharCodes[i];
-        if (0 <= code && code <= MAX_GROUPED_CHAR_CODE)
+        if (0 <= code && code <= MAX_CHAR_CODE) {
             mCodeToKeyIndex[code] = i;
+        }
     }
 }
 
@@ -210,6 +201,6 @@
 }
 
 const int ProximityInfo::MAX_KEY_COUNT_IN_A_KEYBOARD;
-const int ProximityInfo::MAX_GROUPED_CHAR_CODE;
+const int ProximityInfo::MAX_CHAR_CODE;
 
 } // namespace latinime
diff --git a/native/src/proximity_info.h b/native/src/proximity_info.h
index 3190e73..b1e8236 100644
--- a/native/src/proximity_info.h
+++ b/native/src/proximity_info.h
@@ -37,7 +37,8 @@
             const int keybaordHeight, const int gridWidth, const int gridHeight,
             const uint32_t *proximityCharsArray, const int keyCount, const int32_t *keyXCoordinates,
             const int32_t *keyYCoordinates, const int32_t *keyWidths, const int32_t *keyHeights,
-            const int32_t *keyCharCodes, int themeId);
+            const int32_t *keyCharCodes, const float *sweetSpotCenterXs,
+            const float *sweetSpotCenterYs, const float *sweetSpotRadii);
     ~ProximityInfo();
     bool hasSpaceProximity(const int x, const int y) const;
     void setInputParams(const int* inputCodes, const int inputLength);
@@ -55,11 +56,10 @@
 private:
     // The max number of the keys in one keyboard layout
     static const int MAX_KEY_COUNT_IN_A_KEYBOARD = 64;
-    // The upper limit of the char code in TOUCH_POSITION_CORRECTION_GROUP
-    static const int MAX_GROUPED_CHAR_CODE = 127;
+    // The upper limit of the char code in mCodeToKeyIndex
+    static const int MAX_CHAR_CODE = 127;
 
     int getStartIndexFromCoordinates(const int x, const int y) const;
-    void initializeCodeToGroup();
     void initializeCodeToKeyIndex();
     const int MAX_PROXIMITY_CHARS_SIZE;
     const int KEYBOARD_WIDTH;
@@ -69,7 +69,6 @@
     const int CELL_WIDTH;
     const int CELL_HEIGHT;
     const int KEY_COUNT;
-    const int THEME_ID;
     const int *mInputCodes;
     uint32_t *mProximityCharsArray;
     int32_t mKeyXCoordinates[MAX_KEY_COUNT_IN_A_KEYBOARD];
@@ -77,10 +76,12 @@
     int32_t mKeyWidths[MAX_KEY_COUNT_IN_A_KEYBOARD];
     int32_t mKeyHeights[MAX_KEY_COUNT_IN_A_KEYBOARD];
     int32_t mKeyCharCodes[MAX_KEY_COUNT_IN_A_KEYBOARD];
+    float mSweetSpotCenterXs[MAX_KEY_COUNT_IN_A_KEYBOARD];
+    float mSweetSpotCenterYs[MAX_KEY_COUNT_IN_A_KEYBOARD];
+    float mSweetSpotRadii[MAX_KEY_COUNT_IN_A_KEYBOARD];
     int mInputLength;
     unsigned short mPrimaryInputWord[MAX_WORD_LENGTH_INTERNAL];
-    int mCodeToGroup[MAX_GROUPED_CHAR_CODE + 1];
-    int mCodeToKeyIndex[MAX_GROUPED_CHAR_CODE + 1];
+    int mCodeToKeyIndex[MAX_CHAR_CODE + 1];
 };
 
 } // namespace latinime