Merge "[Rlog79a] Disallow empty feedback messages"
diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml
index 8822e8d..bab612b 100644
--- a/java/res/values/strings.xml
+++ b/java/res/values/strings.xml
@@ -313,6 +313,10 @@
     <!-- TODO: remove translatable=false attribute once text is stable -->
     <string name="research_log_uploader_name" translatable="false">Research Uploader Service</string>
 
+    <!-- Name for the research replaying service to be displayed to users.  [CHAR LIMIT=50] -->
+    <!-- TODO: remove translatable=false attribute once text is stable -->
+    <string name="research_log_replayer_name" translatable="false">Research Replayer Service</string>
+
     <!-- Preference for input language selection -->
     <string name="select_language">Input languages</string>
 
diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureFloatingPreviewText.java b/java/src/com/android/inputmethod/keyboard/internal/GestureFloatingPreviewText.java
index 28655a9..ab810a5 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/GestureFloatingPreviewText.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/GestureFloatingPreviewText.java
@@ -28,7 +28,6 @@
 import com.android.inputmethod.keyboard.PointerTracker;
 import com.android.inputmethod.latin.CoordinateUtils;
 import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.ResizableIntArray;
 import com.android.inputmethod.latin.SuggestedWords;
 
 /**
@@ -44,16 +43,17 @@
  * @attr ref R.styleable#KeyboardView_gestureFloatingPreviewRoundRadius
  */
 public class GestureFloatingPreviewText extends AbstractDrawingPreview {
-    private static final class GesturePreviewTextParams {
-        public final int mGesturePreviewTextSize;
-        public final int mGesturePreviewTextColor;
+    protected static final class GesturePreviewTextParams {
         public final int mGesturePreviewTextOffset;
         public final int mGesturePreviewTextHeight;
-        public final int mGesturePreviewColor;
         public final float mGesturePreviewHorizontalPadding;
         public final float mGesturePreviewVerticalPadding;
         public final float mGesturePreviewRoundRadius;
-        public final Paint mTextPaint;
+
+        private final int mGesturePreviewTextSize;
+        private final int mGesturePreviewTextColor;
+        private final int mGesturePreviewColor;
+        private final Paint mPaint = new Paint();
 
         private static final char[] TEXT_HEIGHT_REFERENCE_CHAR = { 'M' };
 
@@ -73,37 +73,36 @@
             mGesturePreviewRoundRadius = mainKeyboardViewAttr.getDimension(
                     R.styleable.MainKeyboardView_gestureFloatingPreviewRoundRadius, 0.0f);
 
-            final Paint textPaint = new Paint();
-            textPaint.setAntiAlias(true);
-            textPaint.setTextAlign(Align.CENTER);
-            textPaint.setTextSize(mGesturePreviewTextSize);
-            mTextPaint = textPaint;
+            final Paint textPaint = getTextPaint();
             final Rect textRect = new Rect();
             textPaint.getTextBounds(TEXT_HEIGHT_REFERENCE_CHAR, 0, 1, textRect);
             mGesturePreviewTextHeight = textRect.height();
         }
+
+        public Paint getTextPaint() {
+            mPaint.setAntiAlias(true);
+            mPaint.setTextAlign(Align.CENTER);
+            mPaint.setTextSize(mGesturePreviewTextSize);
+            mPaint.setColor(mGesturePreviewTextColor);
+            return mPaint;
+        }
+
+        public Paint getBackgroundPaint() {
+            mPaint.setColor(mGesturePreviewColor);
+            return mPaint;
+        }
     }
 
-    protected final GesturePreviewTextParams mParams;
-    protected int mPreviewWordNum;
-    protected final RectF mGesturePreviewRectangle = new RectF();
-    protected int mHighlightedWordIndex;
-
-    private static final int PREVIEW_TEXT_ARRAY_CAPACITY = 10;
-    // These variables store the positions of preview words. In multi-preview mode, the gesture
-    // floating preview at most shows PREVIEW_TEXT_ARRAY_CAPACITY words.
-    protected final ResizableIntArray mPreviewTextXArray = new ResizableIntArray(
-            PREVIEW_TEXT_ARRAY_CAPACITY);
-    protected final ResizableIntArray mPreviewTextYArray = new ResizableIntArray(
-            PREVIEW_TEXT_ARRAY_CAPACITY);
-
-    protected SuggestedWords mSuggestedWords = SuggestedWords.EMPTY;
-    public final int[] mLastPointerCoords = CoordinateUtils.newInstance();
+    private final GesturePreviewTextParams mParams;
+    private final RectF mGesturePreviewRectangle = new RectF();
+    private int mPreviewTextX;
+    private int mPreviewTextY;
+    private SuggestedWords mSuggestedWords = SuggestedWords.EMPTY;
+    private final int[] mLastPointerCoords = CoordinateUtils.newInstance();
 
     public GestureFloatingPreviewText(final View drawingView, final TypedArray typedArray) {
         super(drawingView);
         mParams = new GesturePreviewTextParams(typedArray);
-        mHighlightedWordIndex = 0;
     }
 
     public void setSuggetedWords(final SuggestedWords suggestedWords) {
@@ -114,13 +113,6 @@
         updatePreviewPosition();
     }
 
-    protected void drawText(final Canvas canvas, final String text, final float textX,
-            final float textY, final int color) {
-        final Paint paint = mParams.mTextPaint;
-        paint.setColor(color);
-        canvas.drawText(text, textX, textY, paint);
-    }
-
     @Override
     public void setPreviewPosition(final PointerTracker tracker) {
         final boolean needsToUpdateLastPointer =
@@ -142,14 +134,11 @@
                 || TextUtils.isEmpty(mSuggestedWords.getWord(0))) {
             return;
         }
-        final Paint paint = mParams.mTextPaint;
-        paint.setColor(mParams.mGesturePreviewColor);
         final float round = mParams.mGesturePreviewRoundRadius;
-        canvas.drawRoundRect(mGesturePreviewRectangle, round, round, paint);
+        canvas.drawRoundRect(
+                mGesturePreviewRectangle, round, round, mParams.getBackgroundPaint());
         final String text = mSuggestedWords.getWord(0);
-        final int textX = mPreviewTextXArray.get(0);
-        final int textY = mPreviewTextYArray.get(0);
-        drawText(canvas, text, textX, textY, mParams.mGesturePreviewTextColor);
+        canvas.drawText(text, mPreviewTextX, mPreviewTextY, mParams.getTextPaint());
     }
 
     /**
@@ -162,11 +151,10 @@
         }
         final String text = mSuggestedWords.getWord(0);
 
-        final Paint paint = mParams.mTextPaint;
         final RectF rectangle = mGesturePreviewRectangle;
 
         final int textHeight = mParams.mGesturePreviewTextHeight;
-        final float textWidth = paint.measureText(text);
+        final float textWidth = mParams.getTextPaint().measureText(text);
         final float hPad = mParams.mGesturePreviewHorizontalPadding;
         final float vPad = mParams.mGesturePreviewVerticalPadding;
         final float rectWidth = textWidth + hPad * 2.0f;
@@ -180,10 +168,8 @@
                 - mParams.mGesturePreviewTextOffset - rectHeight;
         rectangle.set(rectX, rectY, rectX + rectWidth, rectY + rectHeight);
 
-        final int textX = (int)(rectX + hPad + textWidth / 2.0f);
-        final int textY = (int)(rectY + vPad) + textHeight;
-        mPreviewTextXArray.add(0, textX);
-        mPreviewTextYArray.add(0, textY);
+        mPreviewTextX = (int)(rectX + hPad + textWidth / 2.0f);
+        mPreviewTextY = (int)(rectY + vPad) + textHeight;
         // TODO: Should narrow the invalidate region.
         getDrawingView().invalidate();
     }
diff --git a/java/src/com/android/inputmethod/research/LogStatement.java b/java/src/com/android/inputmethod/research/LogStatement.java
index 090c58e..1d83e1a 100644
--- a/java/src/com/android/inputmethod/research/LogStatement.java
+++ b/java/src/com/android/inputmethod/research/LogStatement.java
@@ -29,13 +29,12 @@
             "PointerTrackerCallListenerOnCodeInput";
     public static final String KEY_CODE = "code";
     public static final String VALUE_RESEARCH = "research";
-    public static final String TYPE_LATIN_KEYBOARD_VIEW_ON_LONG_PRESS =
-            "LatinKeyboardViewOnLongPress";
+    public static final String TYPE_MAIN_KEYBOARD_VIEW_ON_LONG_PRESS =
+            "MainKeyboardViewOnLongPress";
     public static final String ACTION = "action";
     public static final String VALUE_DOWN = "DOWN";
-    public static final String TYPE_LATIN_KEYBOARD_VIEW_PROCESS_MOTION_EVENTS =
-            "LatinKeyboardViewProcessMotionEvents";
-    public static final String KEY_LOGGING_RELATED = "loggingRelated";
+    public static final String TYPE_MOTION_EVENT = "MotionEvent";
+    public static final String KEY_IS_LOGGING_RELATED = "isLoggingRelated";
 
     // Name specifying the LogStatement type.
     private final String mType;
diff --git a/java/src/com/android/inputmethod/research/LogUnit.java b/java/src/com/android/inputmethod/research/LogUnit.java
index 608fab3..2e732fc 100644
--- a/java/src/com/android/inputmethod/research/LogUnit.java
+++ b/java/src/com/android/inputmethod/research/LogUnit.java
@@ -453,13 +453,12 @@
 
         // Look for the long press that started the invocation of the research key code input.
         final int indexOfLastLongPressBeforeResearchKey =
-                findLastIndexBefore(LogStatement.TYPE_LATIN_KEYBOARD_VIEW_ON_LONG_PRESS,
+                findLastIndexBefore(LogStatement.TYPE_MAIN_KEYBOARD_VIEW_ON_LONG_PRESS,
                         indexOfLastResearchKey);
 
         // Look for DOWN event preceding the long press
         final int indexOfLastDownEventBeforeLongPress =
-                findLastIndexContainingKeyValueBefore(
-                        LogStatement.TYPE_LATIN_KEYBOARD_VIEW_PROCESS_MOTION_EVENTS,
+                findLastIndexContainingKeyValueBefore(LogStatement.TYPE_MOTION_EVENT,
                         LogStatement.ACTION, LogStatement.VALUE_DOWN,
                         indexOfLastLongPressBeforeResearchKey);
 
@@ -471,8 +470,8 @@
             final LogStatement logStatement = mLogStatementList.get(index);
             final String type = logStatement.getType();
             final Object[] values = mValuesList.get(index);
-            if (type.equals(LogStatement.TYPE_LATIN_KEYBOARD_VIEW_PROCESS_MOTION_EVENTS)) {
-                logStatement.setValue(LogStatement.KEY_LOGGING_RELATED, values, true);
+            if (type.equals(LogStatement.TYPE_MOTION_EVENT)) {
+                logStatement.setValue(LogStatement.KEY_IS_LOGGING_RELATED, values, true);
             }
         }
         return true;
diff --git a/java/src/com/android/inputmethod/research/MotionEventReader.java b/java/src/com/android/inputmethod/research/MotionEventReader.java
index 36e75be..26a1d7f 100644
--- a/java/src/com/android/inputmethod/research/MotionEventReader.java
+++ b/java/src/com/android/inputmethod/research/MotionEventReader.java
@@ -101,7 +101,7 @@
         jsonReader.endObject();
 
         if (logStatementType != null && time != null && x != null && y != null && actionType != null
-                && logStatementType.equals("MainKeyboardViewProcessMotionEvent")
+                && logStatementType.equals("MotionEvent")
                 && !loggingRelated) {
             replayData.mActions.add(actionType);
             replayData.mXCoords.add(x);
diff --git a/java/src/com/android/inputmethod/research/Replayer.java b/java/src/com/android/inputmethod/research/Replayer.java
index 4cc2a58..611abb2 100644
--- a/java/src/com/android/inputmethod/research/Replayer.java
+++ b/java/src/com/android/inputmethod/research/Replayer.java
@@ -1,22 +1,23 @@
 /*
  * 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
- * the License at
+ * 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
+ *      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.
+ * 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.research;
 
 import android.os.Handler;
+import android.os.Looper;
 import android.os.Message;
 import android.os.SystemClock;
 import android.util.Log;
@@ -40,6 +41,14 @@
     private boolean mIsReplaying = false;
     private KeyboardSwitcher mKeyboardSwitcher;
 
+    private Replayer() {
+    }
+
+    private static final Replayer sInstance = new Replayer();
+    public static Replayer getInstance() {
+        return sInstance;
+    }
+
     public void setKeyboardSwitcher(final KeyboardSwitcher keyboardSwitcher) {
         mKeyboardSwitcher = keyboardSwitcher;
     }
@@ -49,7 +58,7 @@
     private static final int COMPLETION_TIME_MS = 500;
 
     // TODO: Support historical events and multi-touch.
-    public void replay(final ReplayData replayData) {
+    public void replay(final ReplayData replayData, final Runnable callback) {
         if (mIsReplaying) {
             return;
         }
@@ -72,7 +81,7 @@
         // The adjustment needed to translate times from the original recorded time to the current
         // time.
         final long timeAdjustment = currentStartTime - origStartTime;
-        final Handler handler = new Handler() {
+        final Handler handler = new Handler(Looper.getMainLooper()) {
             // Track the time of the most recent DOWN event, to be passed as a parameter when
             // constructing a MotionEvent.  It's initialized here to the origStartTime, but this is
             // only a precaution.  The value should be overwritten by the first ACTION_DOWN event
@@ -113,8 +122,12 @@
                 Log.d(TAG, "queuing event at " + msgTime);
             }
         }
+
         final long presentDoneTime = replayData.mTimes.get(numActions - 1) + timeAdjustment
                 + COMPLETION_TIME_MS;
         handler.sendMessageAtTime(Message.obtain(handler, MSG_DONE), presentDoneTime);
+        if (callback != null) {
+            handler.postAtTime(callback, presentDoneTime + 1);
+        }
     }
 }
diff --git a/java/src/com/android/inputmethod/research/ReplayerService.java b/java/src/com/android/inputmethod/research/ReplayerService.java
new file mode 100644
index 0000000..88d9033
--- /dev/null
+++ b/java/src/com/android/inputmethod/research/ReplayerService.java
@@ -0,0 +1,65 @@
+/*
+ * 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
+ * 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.research;
+
+import android.app.IntentService;
+import android.content.Intent;
+import android.util.Log;
+
+import com.android.inputmethod.research.MotionEventReader.ReplayData;
+
+import java.io.File;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Provide a mechanism to invoke the replayer from outside.
+ *
+ * In particular, makes access from a host possible through {@code adb am startservice}.
+ */
+public class ReplayerService extends IntentService {
+    private static final String TAG = ReplayerService.class.getSimpleName();
+    private static final String EXTRA_FILENAME = "com.android.inputmethod.research.extra.FILENAME";
+    private static final long MAX_REPLAY_TIME = TimeUnit.SECONDS.toMillis(60);
+
+    public ReplayerService() {
+        super(ReplayerService.class.getSimpleName());
+    }
+
+    @Override
+    protected void onHandleIntent(final Intent intent) {
+        final String filename = intent.getStringExtra(EXTRA_FILENAME);
+        if (filename == null) return;
+
+        final ReplayData replayData = new MotionEventReader().readMotionEventData(
+                new File(filename));
+        synchronized (this) {
+            Replayer.getInstance().replay(replayData, new Runnable() {
+                @Override
+                public void run() {
+                    synchronized (ReplayerService.this) {
+                        ReplayerService.this.notify();
+                    }
+                }
+            });
+            try {
+                wait(MAX_REPLAY_TIME);
+            } catch (InterruptedException e) {
+                Log.e(TAG, "Timeout while replaying.", e);
+            }
+        }
+    }
+}
diff --git a/java/src/com/android/inputmethod/research/ResearchLogger.java b/java/src/com/android/inputmethod/research/ResearchLogger.java
index 096f032..da41001 100644
--- a/java/src/com/android/inputmethod/research/ResearchLogger.java
+++ b/java/src/com/android/inputmethod/research/ResearchLogger.java
@@ -184,10 +184,10 @@
     private Suggest mSuggest;
     private MainKeyboardView mMainKeyboardView;
     // TODO: Check whether a superclass can be used instead of LatinIME.
-    private LatinIME mLatinIME;
+    /* package for test */ LatinIME mLatinIME;
     private final Statistics mStatistics;
     private final MotionEventReader mMotionEventReader = new MotionEventReader();
-    private final Replayer mReplayer = new Replayer();
+    private final Replayer mReplayer = Replayer.getInstance();
 
     private Intent mUploadIntent;
     private Intent mUploadNowIntent;
@@ -783,7 +783,7 @@
                 public void run() {
                     final ReplayData replayData =
                             mMotionEventReader.readMotionEventData(mUserRecordingFile);
-                    mReplayer.replay(replayData);
+                    mReplayer.replay(replayData, null);
                 }
             }, 1000);
         }
@@ -1162,7 +1162,8 @@
      *
      */
     private static final LogStatement LOGSTATEMENT_MAIN_KEYBOARD_VIEW_PROCESS_MOTION_EVENT =
-            new LogStatement("MotionEvent", true, false, "action", "MotionEvent", "loggingRelated");
+            new LogStatement("MotionEvent", true, false, "action",
+                    LogStatement.KEY_IS_LOGGING_RELATED, "motionEvent");
     public static void mainKeyboardView_processMotionEvent(final MotionEvent me, final int action,
             final long eventTime, final int index, final int id, final int x, final int y) {
         if (me != null) {
@@ -1179,7 +1180,7 @@
             }
             final ResearchLogger researchLogger = getInstance();
             researchLogger.enqueueEvent(LOGSTATEMENT_MAIN_KEYBOARD_VIEW_PROCESS_MOTION_EVENT,
-                    actionString, MotionEvent.obtain(me), false);
+                    actionString, false /* IS_LOGGING_RELATED */, MotionEvent.obtain(me));
             if (action == MotionEvent.ACTION_DOWN) {
                 // Subtract 1 from eventTime so the down event is included in the later
                 // LogUnit, not the earlier (the test is for inequality).
diff --git a/native/jni/src/dictionary.h b/native/jni/src/dictionary.h
index 83676b2..ecdddd7 100644
--- a/native/jni/src/dictionary.h
+++ b/native/jni/src/dictionary.h
@@ -31,15 +31,15 @@
 class Dictionary {
  public:
     // Taken from SuggestedWords.java
-    const static int KIND_TYPED = 0; // What user typed
-    const static int KIND_CORRECTION = 1; // Simple correction/suggestion
-    const static int KIND_COMPLETION = 2; // Completion (suggestion with appended chars)
-    const static int KIND_WHITELIST = 3; // Whitelisted word
-    const static int KIND_BLACKLIST = 4; // Blacklisted word
-    const static int KIND_HARDCODED = 5; // Hardcoded suggestion, e.g. punctuation
-    const static int KIND_APP_DEFINED = 6; // Suggested by the application
-    const static int KIND_SHORTCUT = 7; // A shortcut
-    const static int KIND_PREDICTION = 8; // A prediction (== a suggestion with no input)
+    static const int KIND_TYPED = 0; // What user typed
+    static const int KIND_CORRECTION = 1; // Simple correction/suggestion
+    static const int KIND_COMPLETION = 2; // Completion (suggestion with appended chars)
+    static const int KIND_WHITELIST = 3; // Whitelisted word
+    static const int KIND_BLACKLIST = 4; // Blacklisted word
+    static const int KIND_HARDCODED = 5; // Hardcoded suggestion, e.g. punctuation
+    static const int KIND_APP_DEFINED = 6; // Suggested by the application
+    static const int KIND_SHORTCUT = 7; // A shortcut
+    static const int KIND_PREDICTION = 8; // A prediction (== a suggestion with no input)
 
     Dictionary(void *dict, int dictSize, int mmapFd, int dictBufAdjust);
 
diff --git a/native/jni/src/proximity_info.cpp b/native/jni/src/proximity_info.cpp
index a0bad1a..d812745 100644
--- a/native/jni/src/proximity_info.cpp
+++ b/native/jni/src/proximity_info.cpp
@@ -24,11 +24,10 @@
 #include "geometry_utils.h"
 #include "jni.h"
 #include "proximity_info.h"
+#include "proximity_info_params.h"
 
 namespace latinime {
 
-/* static */ const float ProximityInfo::NOT_A_DISTANCE_FLOAT = -1.0f;
-
 static AK_FORCE_INLINE void safeGetOrFillZeroIntArrayRegion(JNIEnv *env, jintArray jArray,
         jsize len, jint *buffer) {
     if (jArray && buffer) {
@@ -129,17 +128,15 @@
 
 float ProximityInfo::getNormalizedSquaredDistanceFromCenterFloatG(
         const int keyId, const int x, const int y) const {
-    const static float verticalSweetSpotScaleForGeometric = 1.1f;
     const bool correctTouchPosition = hasTouchPositionCorrectionData();
-    const float centerX = static_cast<float>(correctTouchPosition
-            ? getSweetSpotCenterXAt(keyId)
+    const float centerX = static_cast<float>(correctTouchPosition ? getSweetSpotCenterXAt(keyId)
             : getKeyCenterXOfKeyIdG(keyId));
     const float visualKeyCenterY = static_cast<float>(getKeyCenterYOfKeyIdG(keyId));
     float centerY;
     if (correctTouchPosition) {
         const float sweetSpotCenterY = static_cast<float>(getSweetSpotCenterYAt(keyId));
         const float gapY = sweetSpotCenterY - visualKeyCenterY;
-        centerY = visualKeyCenterY + gapY * verticalSweetSpotScaleForGeometric;
+        centerY = visualKeyCenterY + gapY * ProximityInfoParams::VERTICAL_SWEET_SPOT_SCALE_G;
     } else {
         centerY = visualKeyCenterY;
     }
diff --git a/native/jni/src/proximity_info.h b/native/jni/src/proximity_info.h
index f3a68e4..d42c723 100644
--- a/native/jni/src/proximity_info.h
+++ b/native/jni/src/proximity_info.h
@@ -83,7 +83,6 @@
 
  private:
     DISALLOW_IMPLICIT_CONSTRUCTORS(ProximityInfo);
-    static const float NOT_A_DISTANCE_FLOAT;
 
     void initializeG();
     float calculateNormalizedSquaredDistance(const int keyIndex, const int inputIndex) const;
diff --git a/native/jni/src/proximity_info_params.cpp b/native/jni/src/proximity_info_params.cpp
index f7b3d4d..1410ab5 100644
--- a/native/jni/src/proximity_info_params.cpp
+++ b/native/jni/src/proximity_info_params.cpp
@@ -14,22 +14,25 @@
  * limitations under the License.
  */
 
+#include "defines.h"
 #include "proximity_info_params.h"
 
 namespace latinime {
-const int ProximityInfoParams::LOOKUP_RADIUS_PERCENTILE = 50;
-const int ProximityInfoParams::FIRST_POINT_TIME_OFFSET_MILLIS = 150;
-const int ProximityInfoParams::STRONG_DOUBLE_LETTER_TIME_MILLIS = 600;
-const int ProximityInfoParams::MIN_DOUBLE_LETTER_BEELINE_SPEED_PERCENTILE = 5;
-const int ProximityInfoParams::NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR_LOG_2 = 10;
-const int ProximityInfoParams::NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR =
-        1 << NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR_LOG_2;
 const float ProximityInfoParams::NOT_A_DISTANCE_FLOAT = -1.0f;
+const int ProximityInfoParams::MIN_DOUBLE_LETTER_BEELINE_SPEED_PERCENTILE = 5;
+const float ProximityInfoParams::VERTICAL_SWEET_SPOT_SCALE_G = 1.1f;
 
-// Per method constants
+/* Per method constants */
+// Used by ProximityInfoStateUtils::initGeometricDistanceInfos()
 const float ProximityInfoParams::NEAR_KEY_NORMALIZED_SQUARED_THRESHOLD = 4.0f;
+
+// Used by ProximityInfoStateUtils::updateNearKeysDistances()
 const float ProximityInfoParams::NEAR_KEY_THRESHOLD_FOR_DISTANCE = 2.0f;
+
+// Used by ProximityInfoStateUtils::isPrevLocalMin()
 const float ProximityInfoParams::MARGIN_FOR_PREV_LOCAL_MIN = 0.01f;
+
+// Used by ProximityInfoStateUtils::getPointScore()
 const int ProximityInfoParams::DISTANCE_BASE_SCALE = 100;
 const float ProximityInfoParams::NEAR_KEY_THRESHOLD_FOR_POINT_SCORE = 0.6f;
 const int ProximityInfoParams::CORNER_CHECK_DISTANCE_THRESHOLD_SCALE = 25;
@@ -39,6 +42,62 @@
 const float ProximityInfoParams::CORNER_SUM_ANGLE_THRESHOLD = M_PI_F / 4.0f;
 const float ProximityInfoParams::CORNER_SCORE = 1.0f;
 
+// Used by ProximityInfoStateUtils::refreshSpeedRates()
+const int ProximityInfoParams::NUM_POINTS_FOR_SPEED_CALCULATION = 2;
+
+// Used by ProximityInfoStateUtils::pushTouchPoint()
+const int ProximityInfoParams::LAST_POINT_SKIP_DISTANCE_SCALE = 4;
+
+// Used by ProximityInfoStateUtils::updateAlignPointProbabilities()
+const float ProximityInfoParams::MIN_PROBABILITY = 0.000001f;
+const float ProximityInfoParams::MAX_SKIP_PROBABILITY = 0.95f;
+const float ProximityInfoParams::SKIP_FIRST_POINT_PROBABILITY = 0.01f;
+const float ProximityInfoParams::SKIP_LAST_POINT_PROBABILITY = 0.1f;
+const float ProximityInfoParams::MIN_SPEED_RATE_FOR_SKIP_PROBABILITY = 0.15f;
+const float ProximityInfoParams::SPEED_WEIGHT_FOR_SKIP_PROBABILITY = 0.9f;
+const float ProximityInfoParams::SLOW_STRAIGHT_WEIGHT_FOR_SKIP_PROBABILITY = 0.6f;
+const float ProximityInfoParams::NEAREST_DISTANCE_WEIGHT = 0.5f;
+const float ProximityInfoParams::NEAREST_DISTANCE_BIAS = 0.5f;
+const float ProximityInfoParams::NEAREST_DISTANCE_WEIGHT_FOR_LAST = 0.6f;
+const float ProximityInfoParams::NEAREST_DISTANCE_BIAS_FOR_LAST = 0.4f;
+const float ProximityInfoParams::ANGLE_WEIGHT = 0.90f;
+const float ProximityInfoParams::DEEP_CORNER_ANGLE_THRESHOLD = M_PI_F * 60.0f / 180.0f;
+const float ProximityInfoParams::SKIP_DEEP_CORNER_PROBABILITY = 0.1f;
+const float ProximityInfoParams::CORNER_ANGLE_THRESHOLD = M_PI_F * 30.0f / 180.0f;
+const float ProximityInfoParams::STRAIGHT_ANGLE_THRESHOLD = M_PI_F * 15.0f / 180.0f;
+const float ProximityInfoParams::SKIP_CORNER_PROBABILITY = 0.4f;
+const float ProximityInfoParams::SPEED_MARGIN = 0.1f;
+const float ProximityInfoParams::CENTER_VALUE_OF_NORMALIZED_DISTRIBUTION = 0.0f;
+// TODO: The variance is critical for accuracy; thus, adjusting these parameter by machine
+// learning or something would be efficient.
+const float ProximityInfoParams::SPEEDxANGLE_WEIGHT_FOR_STANDARD_DIVIATION = 0.3f;
+const float ProximityInfoParams::MAX_SPEEDxANGLE_RATE_FOR_STANDERD_DIVIATION = 0.25f;
+const float ProximityInfoParams::SPEEDxNEAREST_WEIGHT_FOR_STANDARD_DIVIATION = 0.5f;
+const float ProximityInfoParams::MAX_SPEEDxNEAREST_RATE_FOR_STANDERD_DIVIATION = 0.15f;
+const float ProximityInfoParams::MIN_STANDERD_DIVIATION = 0.37f;
+const float ProximityInfoParams::PREV_DISTANCE_WEIGHT = 0.5f;
+const float ProximityInfoParams::NEXT_DISTANCE_WEIGHT = 0.6f;
+
+// Used by ProximityInfoStateUtils::suppressCharProbabilities()
+const float ProximityInfoParams::SUPPRESSION_LENGTH_WEIGHT = 1.5f;
+const float ProximityInfoParams::MIN_SUPPRESSION_RATE = 0.1f;
+const float ProximityInfoParams::SUPPRESSION_WEIGHT = 0.5f;
+const float ProximityInfoParams::SUPPRESSION_WEIGHT_FOR_PROBABILITY_GAIN = 0.1f;
+const float ProximityInfoParams::SKIP_PROBABALITY_WEIGHT_FOR_PROBABILITY_GAIN = 0.3f;
+
+// Used by ProximityInfoStateUtils::getMostProbableString()
+const float ProximityInfoParams::DEMOTION_LOG_PROBABILITY = 0.3f;
+
+// Used by ProximityInfoStateUtils::updateSampledSearchKeysVector()
 // TODO: Investigate if this is required
 const float ProximityInfoParams::SEARCH_KEY_RADIUS_RATIO = 0.95f;
+
+// Used by ProximityInfoStateUtils::calculateBeelineSpeedRate()
+const int ProximityInfoParams::LOOKUP_RADIUS_PERCENTILE = 50;
+const int ProximityInfoParams::FIRST_POINT_TIME_OFFSET_MILLIS = 150;
+const int ProximityInfoParams::STRONG_DOUBLE_LETTER_TIME_MILLIS = 600;
+
+// Used by ProximityInfoStateUtils::calculateNormalizedSquaredDistance()
+const int ProximityInfoParams::NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR = 1 << 10;
+
 } // namespace latinime
diff --git a/native/jni/src/proximity_info_params.h b/native/jni/src/proximity_info_params.h
index 978b999..7c26208 100644
--- a/native/jni/src/proximity_info_params.h
+++ b/native/jni/src/proximity_info_params.h
@@ -23,13 +23,9 @@
 
 class ProximityInfoParams {
  public:
-    static const int LOOKUP_RADIUS_PERCENTILE;
-    static const int FIRST_POINT_TIME_OFFSET_MILLIS;
-    static const int STRONG_DOUBLE_LETTER_TIME_MILLIS;
-    static const int MIN_DOUBLE_LETTER_BEELINE_SPEED_PERCENTILE;
-    static const int NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR;
     static const float NOT_A_DISTANCE_FLOAT;
-    static const float SEARCH_KEY_RADIUS_RATIO;
+    static const int MIN_DOUBLE_LETTER_BEELINE_SPEED_PERCENTILE;
+    static const float VERTICAL_SWEET_SPOT_SCALE_G;
 
     // Used by ProximityInfoStateUtils::initGeometricDistanceInfos()
     static const float NEAR_KEY_NORMALIZED_SQUARED_THRESHOLD;
@@ -50,9 +46,63 @@
     static const float CORNER_SUM_ANGLE_THRESHOLD;
     static const float CORNER_SCORE;
 
+    // Used by ProximityInfoStateUtils::refreshSpeedRates()
+    static const int NUM_POINTS_FOR_SPEED_CALCULATION;
+
+    // Used by ProximityInfoStateUtils::pushTouchPoint()
+    static const int LAST_POINT_SKIP_DISTANCE_SCALE;
+
+    // Used by ProximityInfoStateUtils::updateAlignPointProbabilities()
+    static const float MIN_PROBABILITY;
+    static const float MAX_SKIP_PROBABILITY;
+    static const float SKIP_FIRST_POINT_PROBABILITY;
+    static const float SKIP_LAST_POINT_PROBABILITY;
+    static const float MIN_SPEED_RATE_FOR_SKIP_PROBABILITY;
+    static const float SPEED_WEIGHT_FOR_SKIP_PROBABILITY;
+    static const float SLOW_STRAIGHT_WEIGHT_FOR_SKIP_PROBABILITY;
+    static const float NEAREST_DISTANCE_WEIGHT;
+    static const float NEAREST_DISTANCE_BIAS;
+    static const float NEAREST_DISTANCE_WEIGHT_FOR_LAST;
+    static const float NEAREST_DISTANCE_BIAS_FOR_LAST;
+    static const float ANGLE_WEIGHT;
+    static const float DEEP_CORNER_ANGLE_THRESHOLD;
+    static const float SKIP_DEEP_CORNER_PROBABILITY;
+    static const float CORNER_ANGLE_THRESHOLD;
+    static const float STRAIGHT_ANGLE_THRESHOLD;
+    static const float SKIP_CORNER_PROBABILITY;
+    static const float SPEED_MARGIN;
+    static const float CENTER_VALUE_OF_NORMALIZED_DISTRIBUTION;
+    static const float SPEEDxANGLE_WEIGHT_FOR_STANDARD_DIVIATION;
+    static const float MAX_SPEEDxANGLE_RATE_FOR_STANDERD_DIVIATION;
+    static const float SPEEDxNEAREST_WEIGHT_FOR_STANDARD_DIVIATION;
+    static const float MAX_SPEEDxNEAREST_RATE_FOR_STANDERD_DIVIATION;
+    static const float MIN_STANDERD_DIVIATION;
+    static const float PREV_DISTANCE_WEIGHT;
+    static const float NEXT_DISTANCE_WEIGHT;
+
+    // Used by ProximityInfoStateUtils::suppressCharProbabilities()
+    static const float SUPPRESSION_LENGTH_WEIGHT;
+    static const float MIN_SUPPRESSION_RATE;
+    static const float SUPPRESSION_WEIGHT;
+    static const float SUPPRESSION_WEIGHT_FOR_PROBABILITY_GAIN;
+    static const float SKIP_PROBABALITY_WEIGHT_FOR_PROBABILITY_GAIN;
+
+    // Used by ProximityInfoStateUtils::getMostProbableString()
+    static const float DEMOTION_LOG_PROBABILITY;
+
+    // Used by ProximityInfoStateUtils::updateSampledSearchKeysVector()
+    static const float SEARCH_KEY_RADIUS_RATIO;
+
+    // Used by ProximityInfoStateUtils::calculateBeelineSpeedRate()
+    static const int LOOKUP_RADIUS_PERCENTILE;
+    static const int FIRST_POINT_TIME_OFFSET_MILLIS;
+    static const int STRONG_DOUBLE_LETTER_TIME_MILLIS;
+
+    // Used by ProximityInfoStateUtils::calculateNormalizedSquaredDistance()
+    static const int NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR;
+
  private:
     DISALLOW_IMPLICIT_CONSTRUCTORS(ProximityInfoParams);
-    static const int NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR_LOG_2;
 };
 } // namespace latinime
 #endif // LATINIME_PROXIMITY_INFO_PARAMS_H
diff --git a/native/jni/src/proximity_info_state.cpp b/native/jni/src/proximity_info_state.cpp
index fbdc2c8..4c1ffb3 100644
--- a/native/jni/src/proximity_info_state.cpp
+++ b/native/jni/src/proximity_info_state.cpp
@@ -89,29 +89,28 @@
     }
 
     if (xCoordinates && yCoordinates) {
-        mSampledInputSize = ProximityInfoStateUtils::updateTouchPoints(
-                mProximityInfo->getMostCommonKeyWidth(), mProximityInfo, mMaxPointToKeyLength,
-                mInputProximities, xCoordinates, yCoordinates, times, pointerIds, inputSize,
-                isGeometric, pointerId, pushTouchPointStartIndex, &mSampledInputXs,
-                &mSampledInputYs, &mSampledTimes, &mSampledLengthCache, &mSampledInputIndice);
+        mSampledInputSize = ProximityInfoStateUtils::updateTouchPoints(mProximityInfo,
+                mMaxPointToKeyLength, mInputProximities, xCoordinates, yCoordinates, times,
+                pointerIds, inputSize, isGeometric, pointerId, pushTouchPointStartIndex,
+                &mSampledInputXs, &mSampledInputYs, &mSampledTimes, &mSampledLengthCache,
+                &mSampledInputIndice);
     }
 
     if (mSampledInputSize > 0 && isGeometric) {
-        mAverageSpeed = ProximityInfoStateUtils::refreshSpeedRates(
-                inputSize, xCoordinates, yCoordinates, times, lastSavedInputSize,
-                mSampledInputSize, &mSampledInputXs, &mSampledInputYs, &mSampledTimes,
-                &mSampledLengthCache, &mSampledInputIndice, &mSpeedRates, &mDirections);
-        ProximityInfoStateUtils::refreshBeelineSpeedRates(
-                mProximityInfo->getMostCommonKeyWidth(), mAverageSpeed, inputSize,
-                xCoordinates, yCoordinates, times, mSampledInputSize, &mSampledInputXs,
-                &mSampledInputYs, &mSampledInputIndice, &mBeelineSpeedPercentiles);
+        mAverageSpeed = ProximityInfoStateUtils::refreshSpeedRates(inputSize, xCoordinates,
+                yCoordinates, times, lastSavedInputSize, mSampledInputSize, &mSampledInputXs,
+                &mSampledInputYs, &mSampledTimes, &mSampledLengthCache, &mSampledInputIndice,
+                &mSpeedRates, &mDirections);
+        ProximityInfoStateUtils::refreshBeelineSpeedRates(mProximityInfo->getMostCommonKeyWidth(),
+                mAverageSpeed, inputSize, xCoordinates, yCoordinates, times, mSampledInputSize,
+                &mSampledInputXs, &mSampledInputYs, &mSampledInputIndice,
+                &mBeelineSpeedPercentiles);
     }
 
     if (mSampledInputSize > 0) {
-        ProximityInfoStateUtils::initGeometricDistanceInfos(
-                mProximityInfo, mProximityInfo->getKeyCount(),
-                mSampledInputSize, lastSavedInputSize, &mSampledInputXs, &mSampledInputYs,
-                &mSampledNearKeysVector, &mSampledDistanceCache_G);
+        ProximityInfoStateUtils::initGeometricDistanceInfos(mProximityInfo, mSampledInputSize,
+                lastSavedInputSize, &mSampledInputXs, &mSampledInputYs, &mSampledNearKeysVector,
+                &mSampledDistanceCache_G);
         if (isGeometric) {
             // updates probabilities of skipping or mapping each key for all points.
             ProximityInfoStateUtils::updateAlignPointProbabilities(
diff --git a/native/jni/src/proximity_info_state_utils.cpp b/native/jni/src/proximity_info_state_utils.cpp
index da3f03d..f9b69d2 100644
--- a/native/jni/src/proximity_info_state_utils.cpp
+++ b/native/jni/src/proximity_info_state_utils.cpp
@@ -38,7 +38,7 @@
     return nextStartIndex;
 }
 
-/* static */ int ProximityInfoStateUtils::updateTouchPoints(const int mostCommonKeyWidth,
+/* static */ int ProximityInfoStateUtils::updateTouchPoints(
         const ProximityInfo *const proximityInfo, const int maxPointToKeyLength,
         const int *const inputProximities, const int *const inputXCoordinates,
         const int *const inputYCoordinates, const int *const times, const int *const pointerIds,
@@ -106,15 +106,14 @@
                 const float prevAngle = getAngle(
                         inputXCoordinates[i - 2], inputYCoordinates[i - 2],
                         inputXCoordinates[i - 1], inputYCoordinates[i - 1]);
-                const float currentAngle =
-                        getAngle(inputXCoordinates[i - 1], inputYCoordinates[i - 1], x, y);
+                const float currentAngle = getAngle(
+                        inputXCoordinates[i - 1], inputYCoordinates[i - 1], x, y);
                 sumAngle += getAngleDiff(prevAngle, currentAngle);
             }
 
-            if (pushTouchPoint(mostCommonKeyWidth, proximityInfo, maxPointToKeyLength,
-                    i, c, x, y, time, isGeometric /* doSampling */,
-                    i == lastInputIndex, sumAngle, currentNearKeysDistances,
-                    prevNearKeysDistances, prevPrevNearKeysDistances,
+            if (pushTouchPoint(proximityInfo, maxPointToKeyLength, i, c, x, y, time,
+                    isGeometric /* doSampling */, i == lastInputIndex, sumAngle,
+                    currentNearKeysDistances, prevNearKeysDistances, prevPrevNearKeysDistances,
                     sampledInputXs, sampledInputYs, sampledInputTimes, sampledLengthCache,
                     sampledInputIndice)) {
                 // Previous point information was popped.
@@ -222,12 +221,13 @@
 }
 
 /* static */ void ProximityInfoStateUtils::initGeometricDistanceInfos(
-        const ProximityInfo *const proximityInfo, const int keyCount, const int sampledInputSize,
+        const ProximityInfo *const proximityInfo, const int sampledInputSize,
         const int lastSavedInputSize, const std::vector<int> *const sampledInputXs,
         const std::vector<int> *const sampledInputYs,
         std::vector<NearKeycodesSet> *SampledNearKeysVector,
         std::vector<float> *SampledDistanceCache_G) {
     SampledNearKeysVector->resize(sampledInputSize);
+    const int keyCount = proximityInfo->getKeyCount();
     SampledDistanceCache_G->resize(sampledInputSize * keyCount);
     for (int i = lastSavedInputSize; i < sampledInputSize; ++i) {
         (*SampledNearKeysVector)[i].reset();
@@ -275,10 +275,11 @@
         int duration = 0;
 
         // Calculate velocity by using distances and durations of
-        // NUM_POINTS_FOR_SPEED_CALCULATION points for both forward and backward.
-        static const int NUM_POINTS_FOR_SPEED_CALCULATION = 2;
-        for (int j = index; j < min(inputSize - 1, index + NUM_POINTS_FOR_SPEED_CALCULATION);
-                ++j) {
+        // ProximityInfoParams::NUM_POINTS_FOR_SPEED_CALCULATION points for both forward and
+        // backward.
+        const int forwardNumPoints = min(inputSize - 1,
+                index + ProximityInfoParams::NUM_POINTS_FOR_SPEED_CALCULATION);
+        for (int j = index; j < forwardNumPoints; ++j) {
             if (i < sampledInputSize - 1 && j >= (*sampledInputIndice)[i + 1]) {
                 break;
             }
@@ -286,7 +287,9 @@
                     xCoordinates[j + 1], yCoordinates[j + 1]);
             duration += times[j + 1] - times[j];
         }
-        for (int j = index - 1; j >= max(0, index - NUM_POINTS_FOR_SPEED_CALCULATION); --j) {
+        const int backwardNumPoints = max(0,
+                index - ProximityInfoParams::NUM_POINTS_FOR_SPEED_CALCULATION);
+        for (int j = index - 1; j >= backwardNumPoints; --j) {
             if (i > 0 && j < (*sampledInputIndice)[i - 1]) {
                 break;
             }
@@ -434,9 +437,8 @@
 
 // Sampling touch point and pushing information to vectors.
 // Returning if previous point is popped or not.
-/* static */ bool ProximityInfoStateUtils::pushTouchPoint(const int mostCommonKeyWidth,
-        const ProximityInfo *const proximityInfo, const int maxPointToKeyLength,
-        const int inputIndex, const int nodeCodePoint, int x, int y,
+/* static */ bool ProximityInfoStateUtils::pushTouchPoint(const ProximityInfo *const proximityInfo,
+        const int maxPointToKeyLength, const int inputIndex, const int nodeCodePoint, int x, int y,
         const int time, const bool doSampling, const bool isLastPoint, const float sumAngle,
         NearKeysDistanceMap *const currentNearKeysDistances,
         const NearKeysDistanceMap *const prevNearKeysDistances,
@@ -444,7 +446,7 @@
         std::vector<int> *sampledInputXs, std::vector<int> *sampledInputYs,
         std::vector<int> *sampledInputTimes, std::vector<int> *sampledLengthCache,
         std::vector<int> *sampledInputIndice) {
-    static const int LAST_POINT_SKIP_DISTANCE_SCALE = 4;
+    const int mostCommonKeyWidth = proximityInfo->getMostCommonKeyWidth();
 
     size_t size = sampledInputXs->size();
     bool popped = false;
@@ -465,16 +467,16 @@
         }
         // Check if the last point should be skipped.
         if (isLastPoint && size > 0) {
-            if (getDistanceInt(x, y, sampledInputXs->back(),
-                    sampledInputYs->back()) * LAST_POINT_SKIP_DISTANCE_SCALE
-                            < mostCommonKeyWidth) {
+            if (getDistanceInt(x, y, sampledInputXs->back(), sampledInputYs->back())
+                    * ProximityInfoParams::LAST_POINT_SKIP_DISTANCE_SCALE < mostCommonKeyWidth) {
                 // This point is not used because it's too close to the previous point.
                 if (DEBUG_GEO_FULL) {
                     AKLOGI("p0: size = %zd, x = %d, y = %d, lx = %d, ly = %d, dist = %d, "
                            "width = %d", size, x, y, sampledInputXs->back(),
                            sampledInputYs->back(), getDistanceInt(
                                    x, y, sampledInputXs->back(), sampledInputYs->back()),
-                           mostCommonKeyWidth / LAST_POINT_SKIP_DISTANCE_SCALE);
+                           mostCommonKeyWidth
+                                   / ProximityInfoParams::LAST_POINT_SKIP_DISTANCE_SCALE);
                 }
                 return popped;
             }
@@ -664,35 +666,14 @@
         const std::vector<float> *const SampledDistanceCache_G,
         std::vector<NearKeycodesSet> *SampledNearKeysVector,
         std::vector<hash_map_compat<int, float> > *charProbabilities) {
-    static const float MIN_PROBABILITY = 0.000001f;
-    static const float MAX_SKIP_PROBABILITY = 0.95f;
-    static const float SKIP_FIRST_POINT_PROBABILITY = 0.01f;
-    static const float SKIP_LAST_POINT_PROBABILITY = 0.1f;
-    static const float MIN_SPEED_RATE_FOR_SKIP_PROBABILITY = 0.15f;
-    static const float SPEED_WEIGHT_FOR_SKIP_PROBABILITY = 0.9f;
-    static const float SLOW_STRAIGHT_WEIGHT_FOR_SKIP_PROBABILITY = 0.6f;
-    static const float NEAREST_DISTANCE_WEIGHT = 0.5f;
-    static const float NEAREST_DISTANCE_BIAS = 0.5f;
-    static const float NEAREST_DISTANCE_WEIGHT_FOR_LAST = 0.6f;
-    static const float NEAREST_DISTANCE_BIAS_FOR_LAST = 0.4f;
-
-    static const float ANGLE_WEIGHT = 0.90f;
-    static const float DEEP_CORNER_ANGLE_THRESHOLD = M_PI_F * 60.0f / 180.0f;
-    static const float SKIP_DEEP_CORNER_PROBABILITY = 0.1f;
-    static const float CORNER_ANGLE_THRESHOLD = M_PI_F * 30.0f / 180.0f;
-    static const float STRAIGHT_ANGLE_THRESHOLD = M_PI_F * 15.0f / 180.0f;
-    static const float SKIP_CORNER_PROBABILITY = 0.4f;
-    static const float SPEED_MARGIN = 0.1f;
-    static const float CENTER_VALUE_OF_NORMALIZED_DISTRIBUTION = 0.0f;
-
     charProbabilities->resize(sampledInputSize);
     // Calculates probabilities of using a point as a correlated point with the character
     // for each point.
     for (int i = start; i < sampledInputSize; ++i) {
         (*charProbabilities)[i].clear();
-        // First, calculates skip probability. Starts form MIN_SKIP_PROBABILITY.
+        // First, calculates skip probability. Starts from MAX_SKIP_PROBABILITY.
         // Note that all values that are multiplied to this probability should be in [0.0, 1.0];
-        float skipProbability = MAX_SKIP_PROBABILITY;
+        float skipProbability = ProximityInfoParams::MAX_SKIP_PROBABILITY;
 
         const float currentAngle = getPointAngle(sampledInputXs, sampledInputYs, i);
         const float speedRate = (*sampledSpeedRates)[i];
@@ -709,78 +690,74 @@
         }
 
         if (i == 0) {
-            skipProbability *= min(1.0f, nearestKeyDistance * NEAREST_DISTANCE_WEIGHT
-                    + NEAREST_DISTANCE_BIAS);
+            skipProbability *= min(1.0f,
+                    nearestKeyDistance * ProximityInfoParams::NEAREST_DISTANCE_WEIGHT
+                            + ProximityInfoParams::NEAREST_DISTANCE_BIAS);
             // Promote the first point
-            skipProbability *= SKIP_FIRST_POINT_PROBABILITY;
+            skipProbability *= ProximityInfoParams::SKIP_FIRST_POINT_PROBABILITY;
         } else if (i == sampledInputSize - 1) {
-            skipProbability *= min(1.0f, nearestKeyDistance * NEAREST_DISTANCE_WEIGHT_FOR_LAST
-                    + NEAREST_DISTANCE_BIAS_FOR_LAST);
+            skipProbability *= min(1.0f,
+                    nearestKeyDistance * ProximityInfoParams::NEAREST_DISTANCE_WEIGHT_FOR_LAST
+                            + ProximityInfoParams::NEAREST_DISTANCE_BIAS_FOR_LAST);
             // Promote the last point
-            skipProbability *= SKIP_LAST_POINT_PROBABILITY;
+            skipProbability *= ProximityInfoParams::SKIP_LAST_POINT_PROBABILITY;
         } else {
             // If the current speed is relatively slower than adjacent keys, we promote this point.
-            if ((*sampledSpeedRates)[i - 1] - SPEED_MARGIN > speedRate
-                    && speedRate < (*sampledSpeedRates)[i + 1] - SPEED_MARGIN) {
-                if (currentAngle < CORNER_ANGLE_THRESHOLD) {
+            if ((*sampledSpeedRates)[i - 1] - ProximityInfoParams::SPEED_MARGIN > speedRate
+                    && speedRate
+                            < (*sampledSpeedRates)[i + 1] - ProximityInfoParams::SPEED_MARGIN) {
+                if (currentAngle < ProximityInfoParams::CORNER_ANGLE_THRESHOLD) {
                     skipProbability *= min(1.0f, speedRate
-                            * SLOW_STRAIGHT_WEIGHT_FOR_SKIP_PROBABILITY);
+                            * ProximityInfoParams::SLOW_STRAIGHT_WEIGHT_FOR_SKIP_PROBABILITY);
                 } else {
                     // If the angle is small enough, we promote this point more. (e.g. pit vs put)
-                    skipProbability *= min(1.0f, speedRate * SPEED_WEIGHT_FOR_SKIP_PROBABILITY
-                            + MIN_SPEED_RATE_FOR_SKIP_PROBABILITY);
+                    skipProbability *= min(1.0f,
+                            speedRate * ProximityInfoParams::SPEED_WEIGHT_FOR_SKIP_PROBABILITY
+                                    + ProximityInfoParams::MIN_SPEED_RATE_FOR_SKIP_PROBABILITY);
                 }
             }
 
-            skipProbability *= min(1.0f, speedRate * nearestKeyDistance *
-                    NEAREST_DISTANCE_WEIGHT + NEAREST_DISTANCE_BIAS);
+            skipProbability *= min(1.0f,
+                    speedRate * nearestKeyDistance * ProximityInfoParams::NEAREST_DISTANCE_WEIGHT
+                            + ProximityInfoParams::NEAREST_DISTANCE_BIAS);
 
             // Adjusts skip probability by a rate depending on angle.
             // ANGLE_RATE of skipProbability is adjusted by current angle.
-            skipProbability *= (M_PI_F - currentAngle) / M_PI_F * ANGLE_WEIGHT
-                    + (1.0f - ANGLE_WEIGHT);
-            if (currentAngle > DEEP_CORNER_ANGLE_THRESHOLD) {
-                skipProbability *= SKIP_DEEP_CORNER_PROBABILITY;
+            skipProbability *= (M_PI_F - currentAngle) / M_PI_F * ProximityInfoParams::ANGLE_WEIGHT
+                    + (1.0f - ProximityInfoParams::ANGLE_WEIGHT);
+            if (currentAngle > ProximityInfoParams::DEEP_CORNER_ANGLE_THRESHOLD) {
+                skipProbability *= ProximityInfoParams::SKIP_DEEP_CORNER_PROBABILITY;
             }
             // We assume the angle of this point is the angle for point[i], point[i - 2]
             // and point[i - 3]. The reason why we don't use the angle for point[i], point[i - 1]
             // and point[i - 2] is this angle can be more affected by the noise.
             const float prevAngle = getPointsAngle(sampledInputXs, sampledInputYs, i, i - 2, i - 3);
-            if (i >= 3 && prevAngle < STRAIGHT_ANGLE_THRESHOLD
-                    && currentAngle > CORNER_ANGLE_THRESHOLD) {
-                skipProbability *= SKIP_CORNER_PROBABILITY;
+            if (i >= 3 && prevAngle < ProximityInfoParams::STRAIGHT_ANGLE_THRESHOLD
+                    && currentAngle > ProximityInfoParams::CORNER_ANGLE_THRESHOLD) {
+                skipProbability *= ProximityInfoParams::SKIP_CORNER_PROBABILITY;
             }
         }
 
-        // probabilities must be in [0.0, MAX_SKIP_PROBABILITY];
+        // probabilities must be in [0.0, ProximityInfoParams::MAX_SKIP_PROBABILITY];
         ASSERT(skipProbability >= 0.0f);
-        ASSERT(skipProbability <= MAX_SKIP_PROBABILITY);
+        ASSERT(skipProbability <= ProximityInfoParams::MAX_SKIP_PROBABILITY);
         (*charProbabilities)[i][NOT_AN_INDEX] = skipProbability;
 
         // Second, calculates key probabilities by dividing the rest probability
         // (1.0f - skipProbability).
         const float inputCharProbability = 1.0f - skipProbability;
 
-        // TODO: The variance is critical for accuracy; thus, adjusting these parameter by machine
-        // learning or something would be efficient.
-        static const float SPEEDxANGLE_WEIGHT_FOR_STANDARD_DIVIATION = 0.3f;
-        static const float MAX_SPEEDxANGLE_RATE_FOR_STANDERD_DIVIATION = 0.25f;
-        static const float SPEEDxNEAREST_WEIGHT_FOR_STANDARD_DIVIATION = 0.5f;
-        static const float MAX_SPEEDxNEAREST_RATE_FOR_STANDERD_DIVIATION = 0.15f;
-        static const float MIN_STANDERD_DIVIATION = 0.37f;
-
         const float speedxAngleRate = min(speedRate * currentAngle / M_PI_F
-                * SPEEDxANGLE_WEIGHT_FOR_STANDARD_DIVIATION,
-                        MAX_SPEEDxANGLE_RATE_FOR_STANDERD_DIVIATION);
+                * ProximityInfoParams::SPEEDxANGLE_WEIGHT_FOR_STANDARD_DIVIATION,
+                        ProximityInfoParams::MAX_SPEEDxANGLE_RATE_FOR_STANDERD_DIVIATION);
         const float speedxNearestKeyDistanceRate = min(speedRate * nearestKeyDistance
-                * SPEEDxNEAREST_WEIGHT_FOR_STANDARD_DIVIATION,
-                        MAX_SPEEDxNEAREST_RATE_FOR_STANDERD_DIVIATION);
-        const float sigma = speedxAngleRate + speedxNearestKeyDistanceRate + MIN_STANDERD_DIVIATION;
+                * ProximityInfoParams::SPEEDxNEAREST_WEIGHT_FOR_STANDARD_DIVIATION,
+                        ProximityInfoParams::MAX_SPEEDxNEAREST_RATE_FOR_STANDERD_DIVIATION);
+        const float sigma = speedxAngleRate + speedxNearestKeyDistanceRate
+                + ProximityInfoParams::MIN_STANDERD_DIVIATION;
 
         ProximityInfoUtils::NormalDistribution
-                distribution(CENTER_VALUE_OF_NORMALIZED_DISTRIBUTION, sigma);
-        static const float PREV_DISTANCE_WEIGHT = 0.5f;
-        static const float NEXT_DISTANCE_WEIGHT = 0.6f;
+                distribution(ProximityInfoParams::CENTER_VALUE_OF_NORMALIZED_DISTRIBUTION, sigma);
         // Summing up probability densities of all near keys.
         float sumOfProbabilityDensities = 0.0f;
         for (int j = 0; j < keyCount; ++j) {
@@ -797,8 +774,9 @@
                         // points because the first touch by the user can be sloppy.
                         // So we promote the first point if the distance of that point is larger
                         // than the distance of the next point.
-                        distance = (distance + nextDistance * NEXT_DISTANCE_WEIGHT)
-                                / (1.0f + NEXT_DISTANCE_WEIGHT);
+                        distance = (distance
+                                + nextDistance * ProximityInfoParams::NEXT_DISTANCE_WEIGHT)
+                                        / (1.0f + ProximityInfoParams::NEXT_DISTANCE_WEIGHT);
                     }
                 } else if (i != 0 && i == sampledInputSize - 1) {
                     // For the first point, weighted average of distances from last point and
@@ -810,8 +788,9 @@
                         // because the last touch by the user can be sloppy. So we promote the
                         // last point if the distance of that point is larger than the distance of
                         // the previous point.
-                        distance = (distance + previousDistance * PREV_DISTANCE_WEIGHT)
-                                / (1.0f + PREV_DISTANCE_WEIGHT);
+                        distance = (distance
+                                + previousDistance * ProximityInfoParams::PREV_DISTANCE_WEIGHT)
+                                        / (1.0f + ProximityInfoParams::PREV_DISTANCE_WEIGHT);
                     }
                 }
                 // TODO: Promote the first point when the extended line from the next input is near
@@ -831,8 +810,9 @@
                     const float prevDistance = sqrtf(getPointToKeyByIdLength(
                             maxPointToKeyLength, SampledDistanceCache_G, keyCount, i + 1, j));
                     if (prevDistance < distance) {
-                        distance = (distance + prevDistance * NEXT_DISTANCE_WEIGHT)
-                                / (1.0f + NEXT_DISTANCE_WEIGHT);
+                        distance = (distance
+                                + prevDistance * ProximityInfoParams::NEXT_DISTANCE_WEIGHT)
+                                        / (1.0f + ProximityInfoParams::NEXT_DISTANCE_WEIGHT);
                     }
                 } else if (i != 0 && i == sampledInputSize - 1) {
                     // For the first point, weighted average of distances from last point and
@@ -840,8 +820,9 @@
                     const float prevDistance = sqrtf(getPointToKeyByIdLength(
                             maxPointToKeyLength, SampledDistanceCache_G, keyCount, i - 1, j));
                     if (prevDistance < distance) {
-                        distance = (distance + prevDistance * PREV_DISTANCE_WEIGHT)
-                                / (1.0f + PREV_DISTANCE_WEIGHT);
+                        distance = (distance
+                                + prevDistance * ProximityInfoParams::PREV_DISTANCE_WEIGHT)
+                                        / (1.0f + ProximityInfoParams::PREV_DISTANCE_WEIGHT);
                     }
                 }
                 const float probabilityDensity = distribution.getProbabilityDensity(distance);
@@ -905,7 +886,7 @@
             hash_map_compat<int, float>::iterator it = (*charProbabilities)[i].find(j);
             if (it == (*charProbabilities)[i].end()){
                 (*SampledNearKeysVector)[i].reset(j);
-            } else if(it->second < MIN_PROBABILITY) {
+            } else if(it->second < ProximityInfoParams::MIN_PROBABILITY) {
                 // Erases from near keys vector because it has very low probability.
                 (*SampledNearKeysVector)[i].reset(j);
                 (*charProbabilities)[i].erase(j);
@@ -949,20 +930,14 @@
         std::vector<hash_map_compat<int, float> > *charProbabilities) {
     ASSERT(0 <= index0 && index0 < sampledInputSize);
     ASSERT(0 <= index1 && index1 < sampledInputSize);
-
-    static const float SUPPRESSION_LENGTH_WEIGHT = 1.5f;
-    static const float MIN_SUPPRESSION_RATE = 0.1f;
-    static const float SUPPRESSION_WEIGHT = 0.5f;
-    static const float SUPPRESSION_WEIGHT_FOR_PROBABILITY_GAIN = 0.1f;
-    static const float SKIP_PROBABALITY_WEIGHT_FOR_PROBABILITY_GAIN = 0.3f;
-
     const float keyWidthFloat = static_cast<float>(mostCommonKeyWidth);
     const float diff = fabsf(static_cast<float>((*lengthCache)[index0] - (*lengthCache)[index1]));
-    if (diff > keyWidthFloat * SUPPRESSION_LENGTH_WEIGHT) {
+    if (diff > keyWidthFloat * ProximityInfoParams::SUPPRESSION_LENGTH_WEIGHT) {
         return false;
     }
-    const float suppressionRate = MIN_SUPPRESSION_RATE
-            + diff / keyWidthFloat / SUPPRESSION_LENGTH_WEIGHT * SUPPRESSION_WEIGHT;
+    const float suppressionRate = ProximityInfoParams::MIN_SUPPRESSION_RATE
+            + diff / keyWidthFloat / ProximityInfoParams::SUPPRESSION_LENGTH_WEIGHT
+                    * ProximityInfoParams::SUPPRESSION_WEIGHT;
     for (hash_map_compat<int, float>::iterator it = (*charProbabilities)[index0].begin();
             it != (*charProbabilities)[index0].end(); ++it) {
         hash_map_compat<int, float>::iterator it2 =  (*charProbabilities)[index1].find(it->first);
@@ -974,9 +949,10 @@
             (*charProbabilities)[index0][NOT_AN_INDEX] += suppression;
 
             // Add the probability of the same key nearby index1
-            const float probabilityGain = min(suppression * SUPPRESSION_WEIGHT_FOR_PROBABILITY_GAIN,
+            const float probabilityGain = min(suppression
+                    * ProximityInfoParams::SUPPRESSION_WEIGHT_FOR_PROBABILITY_GAIN,
                     (*charProbabilities)[index1][NOT_AN_INDEX]
-                            * SKIP_PROBABALITY_WEIGHT_FOR_PROBABILITY_GAIN);
+                            * ProximityInfoParams::SKIP_PROBABALITY_WEIGHT_FOR_PROBABILITY_GAIN);
             it2->second += probabilityGain;
             (*charProbabilities)[index1][NOT_AN_INDEX] -= probabilityGain;
         }
@@ -1020,7 +996,6 @@
         int *const codePointBuf) {
     ASSERT(sampledInputSize >= 0);
     memset(codePointBuf, 0, sizeof(codePointBuf[0]) * MAX_WORD_LENGTH);
-    static const float DEMOTION_LOG_PROBABILITY = 0.3f;
     int index = 0;
     float sumLogProbability = 0.0f;
     // TODO: Current implementation is greedy algorithm. DP would be efficient for many cases.
@@ -1030,7 +1005,7 @@
         for (hash_map_compat<int, float>::const_iterator it = (*charProbabilities)[i].begin();
                 it != (*charProbabilities)[i].end(); ++it) {
             const float logProbability = (it->first != NOT_AN_INDEX)
-                    ? it->second + DEMOTION_LOG_PROBABILITY : it->second;
+                    ? it->second + ProximityInfoParams::DEMOTION_LOG_PROBABILITY : it->second;
             if (logProbability < minLogProbability) {
                 minLogProbability = logProbability;
                 character = it->first;
diff --git a/native/jni/src/proximity_info_state_utils.h b/native/jni/src/proximity_info_state_utils.h
index c8f0aeb..af0acc7 100644
--- a/native/jni/src/proximity_info_state_utils.h
+++ b/native/jni/src/proximity_info_state_utils.h
@@ -35,9 +35,8 @@
     static int trimLastTwoTouchPoints(std::vector<int> *sampledInputXs,
             std::vector<int> *sampledInputYs, std::vector<int> *sampledInputTimes,
             std::vector<int> *sampledLengthCache, std::vector<int> *sampledInputIndice);
-    static int updateTouchPoints(const int mostCommonKeyWidth,
-            const ProximityInfo *const proximityInfo, const int maxPointToKeyLength,
-            const int *const inputProximities,
+    static int updateTouchPoints(const ProximityInfo *const proximityInfo,
+            const int maxPointToKeyLength, const int *const inputProximities,
             const int *const inputXCoordinates, const int *const inputYCoordinates,
             const int *const times, const int *const pointerIds, const int inputSize,
             const bool isGeometric, const int pointerId, const int pushTouchPointStartIndex,
@@ -65,19 +64,17 @@
             std::vector<int> *beelineSpeedPercentiles);
     static float getDirection(const std::vector<int> *const sampledInputXs,
             const std::vector<int> *const sampledInputYs, const int index0, const int index1);
-    static void updateAlignPointProbabilities(
-            const float maxPointToKeyLength, const int mostCommonKeyWidth, const int keyCount,
-            const int start, const int sampledInputSize,
-            const std::vector<int> *const sampledInputXs,
+    static void updateAlignPointProbabilities(const float maxPointToKeyLength,
+            const int mostCommonKeyWidth, const int keyCount, const int start,
+            const int sampledInputSize, const std::vector<int> *const sampledInputXs,
             const std::vector<int> *const sampledInputYs,
             const std::vector<float> *const sampledSpeedRates,
             const std::vector<int> *const sampledLengthCache,
             const std::vector<float> *const SampledDistanceCache_G,
             std::vector<NearKeycodesSet> *SampledNearKeysVector,
             std::vector<hash_map_compat<int, float> > *charProbabilities);
-    static void updateSampledSearchKeysVector(
-            const ProximityInfo *const proximityInfo, const int sampledInputSize,
-            const int lastSavedInputSize,
+    static void updateSampledSearchKeysVector(const ProximityInfo *const proximityInfo,
+            const int sampledInputSize, const int lastSavedInputSize,
             const std::vector<int> *const sampledLengthCache,
             const std::vector<NearKeycodesSet> *const SampledNearKeysVector,
             std::vector<NearKeycodesSet> *sampledSearchKeysVector);
@@ -87,22 +84,18 @@
     static float getPointToKeyByIdLength(const float maxPointToKeyLength,
             const std::vector<float> *const SampledDistanceCache_G, const int keyCount,
             const int inputIndex, const int keyId);
-    static void initGeometricDistanceInfos(
-            const ProximityInfo *const proximityInfo, const int keyCount,
+    static void initGeometricDistanceInfos(const ProximityInfo *const proximityInfo,
             const int sampledInputSize, const int lastSavedInputSize,
             const std::vector<int> *const sampledInputXs,
             const std::vector<int> *const sampledInputYs,
             std::vector<NearKeycodesSet> *SampledNearKeysVector,
             std::vector<float> *SampledDistanceCache_G);
-    static void initPrimaryInputWord(
-            const int inputSize, const int *const inputProximities, int *primaryInputWord);
-    static void initNormalizedSquaredDistances(
-            const ProximityInfo *const proximityInfo, const int inputSize,
-            const int *inputXCoordinates, const int *inputYCoordinates,
-            const int *const inputProximities,
-            const std::vector<int> *const sampledInputXs,
-            const std::vector<int> *const sampledInputYs,
-            int *normalizedSquaredDistances);
+    static void initPrimaryInputWord(const int inputSize, const int *const inputProximities,
+            int *primaryInputWord);
+    static void initNormalizedSquaredDistances(const ProximityInfo *const proximityInfo,
+            const int inputSize, const int *inputXCoordinates, const int *inputYCoordinates,
+            const int *const inputProximities, const std::vector<int> *const sampledInputXs,
+            const std::vector<int> *const sampledInputYs, int *normalizedSquaredDistances);
     static void dump(const bool isGeometric, const int inputSize,
             const int *const inputXCoordinates, const int *const inputYCoordinates,
             const int sampledInputSize, const std::vector<int> *const sampledInputXs,
@@ -117,8 +110,8 @@
             const std::vector<int> *const sampledTimes,
             const std::vector<int> *const sampledInputIndices);
     // TODO: Move to most_probable_string_utils.h
-    static float getMostProbableString(
-            const ProximityInfo *const proximityInfo, const int sampledInputSize,
+    static float getMostProbableString(const ProximityInfo *const proximityInfo,
+            const int sampledInputSize,
             const std::vector<hash_map_compat<int, float> > *const charProbabilities,
             int *const codePointBuf);
 
@@ -137,11 +130,10 @@
             const NearKeysDistanceMap *const prevNearKeysDistances,
             const NearKeysDistanceMap *const prevPrevNearKeysDistances,
             std::vector<int> *sampledInputXs, std::vector<int> *sampledInputYs);
-    static bool pushTouchPoint(const int mostCommonKeyWidth,
-            const ProximityInfo *const proximityInfo, const int maxPointToKeyLength,
-            const int inputIndex, const int nodeCodePoint, int x, int y, const int time,
-            const bool doSampling, const bool isLastPoint, const float sumAngle,
-            NearKeysDistanceMap *const currentNearKeysDistances,
+    static bool pushTouchPoint(const ProximityInfo *const proximityInfo,
+            const int maxPointToKeyLength, const int inputIndex, const int nodeCodePoint, int x,
+            int y, const int time, const bool doSampling, const bool isLastPoint,
+            const float sumAngle, NearKeysDistanceMap *const currentNearKeysDistances,
             const NearKeysDistanceMap *const prevNearKeysDistances,
             const NearKeysDistanceMap *const prevPrevNearKeysDistances,
             std::vector<int> *sampledInputXs, std::vector<int> *sampledInputYs,
@@ -153,23 +145,20 @@
             const std::vector<int> *const sampledInputXs,
             const std::vector<int> *const sampledInputYs,
             const std::vector<int> *const inputIndice);
-    static float getPointAngle(
-            const std::vector<int> *const sampledInputXs,
+    static float getPointAngle(const std::vector<int> *const sampledInputXs,
             const std::vector<int> *const sampledInputYs, const int index);
-    static float getPointsAngle(
-            const std::vector<int> *const sampledInputXs,
-            const std::vector<int> *const sampledInputYs,
-            const int index0, const int index1, const int index2);
+    static float getPointsAngle(const std::vector<int> *const sampledInputXs,
+            const std::vector<int> *const sampledInputYs, const int index0, const int index1,
+            const int index2);
     static bool suppressCharProbabilities(const int mostCommonKeyWidth,
-            const int sampledInputSize, const std::vector<int> *const lengthCache,
-            const int index0, const int index1,
-            std::vector<hash_map_compat<int, float> > *charProbabilities);
+            const int sampledInputSize, const std::vector<int> *const lengthCache, const int index0,
+            const int index1, std::vector<hash_map_compat<int, float> > *charProbabilities);
     static float calculateSquaredDistanceFromSweetSpotCenter(
             const ProximityInfo *const proximityInfo, const std::vector<int> *const sampledInputXs,
             const std::vector<int> *const sampledInputYs, const int keyIndex,
             const int inputIndex);
-     static float calculateNormalizedSquaredDistance(
-            const ProximityInfo *const proximityInfo, const std::vector<int> *const sampledInputXs,
+     static float calculateNormalizedSquaredDistance(const ProximityInfo *const proximityInfo,
+            const std::vector<int> *const sampledInputXs,
             const std::vector<int> *const sampledInputYs, const int keyIndex, const int inputIndex);
 };
 } // namespace latinime