Merge branch 'lineage-21.0' of https://github.com/LineageOS/android_packages_inputmethods_LatinIME into leaf-3.2

Change-Id: I1adaaf70dcc1e3e3c368235366014ce530ba805b
diff --git a/java/AndroidManifest.xml b/java/AndroidManifest.xml
index bc63c6a..633c68b 100644
--- a/java/AndroidManifest.xml
+++ b/java/AndroidManifest.xml
@@ -62,7 +62,7 @@
     </queries>
 
     <application android:label="@string/english_ime_name"
-         android:icon="@mipmap/ic_launcher"
+         android:icon="@drawable/ic_launcher_keyboard"
          android:supportsRtl="true"
          android:allowBackup="true"
          android:defaultToDeviceProtectedStorage="true"
@@ -100,7 +100,7 @@
         <activity android:name=".setup.SetupActivity"
              android:theme="@style/platformActivityTheme"
              android:label="@string/english_ime_name"
-             android:icon="@mipmap/ic_launcher"
+             android:icon="@drawable/ic_launcher_keyboard"
              android:launchMode="singleTask"
              android:noHistory="true"
              android:exported="true">
diff --git a/java/res/drawable-hdpi/ic_launcher_keyboard.png b/java/res/drawable-hdpi/ic_launcher_keyboard.png
new file mode 100644
index 0000000..3a01e61
--- /dev/null
+++ b/java/res/drawable-hdpi/ic_launcher_keyboard.png
Binary files differ
diff --git a/java/res/drawable-mdpi/ic_launcher_keyboard.png b/java/res/drawable-mdpi/ic_launcher_keyboard.png
new file mode 100644
index 0000000..574da25
--- /dev/null
+++ b/java/res/drawable-mdpi/ic_launcher_keyboard.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/ic_launcher_keyboard.png b/java/res/drawable-xhdpi/ic_launcher_keyboard.png
new file mode 100644
index 0000000..1769501
--- /dev/null
+++ b/java/res/drawable-xhdpi/ic_launcher_keyboard.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/ic_launcher_keyboard.png b/java/res/drawable-xxhdpi/ic_launcher_keyboard.png
new file mode 100644
index 0000000..624c82e
--- /dev/null
+++ b/java/res/drawable-xxhdpi/ic_launcher_keyboard.png
Binary files differ
diff --git a/java/res/drawable-xxxhdpi/ic_launcher_keyboard.png b/java/res/drawable-xxxhdpi/ic_launcher_keyboard.png
new file mode 100644
index 0000000..39636a1
--- /dev/null
+++ b/java/res/drawable-xxxhdpi/ic_launcher_keyboard.png
Binary files differ
diff --git a/java/res/drawable/ic_launcher_background.xml b/java/res/drawable/ic_launcher_background.xml
deleted file mode 100644
index 4c5c90e..0000000
--- a/java/res/drawable/ic_launcher_background.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     SPDX-FileCopyrightText: 2022 The LineageOS Project
-     SPDX-License-Identifier: Apache-2.0
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="108dp"
-    android:height="108dp"
-    android:viewportWidth="108"
-    android:viewportHeight="108">
-  <path
-      android:pathData="M0,0h108v108h-108z"
-      android:fillColor="#ffffff"/>
-  <path
-      android:pathData="M30,61C30,60.448 30.448,60 31,60H37C37.552,60 38,60.448 38,61V67C38,67.552 37.552,68 37,68H32C30.895,68 30,67.105 30,66V61Z"
-      android:fillColor="#359944"/>
-  <path
-      android:pathData="M70,61C70,60.448 70.448,60 71,60H77C77.552,60 78,60.448 78,61V66C78,67.105 77.105,68 76,68H71C70.448,68 70,67.552 70,67V61Z"
-      android:fillColor="#1F6C37"/>
-</vector>
diff --git a/java/res/drawable/ic_launcher_foreground.xml b/java/res/drawable/ic_launcher_foreground.xml
deleted file mode 100644
index 133977b..0000000
--- a/java/res/drawable/ic_launcher_foreground.xml
+++ /dev/null
@@ -1,34 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     SPDX-FileCopyrightText: 2022 The LineageOS Project
-     SPDX-License-Identifier: Apache-2.0
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:aapt="http://schemas.android.com/aapt"
-    android:width="108dp"
-    android:height="108dp"
-    android:viewportWidth="108"
-    android:viewportHeight="108">
-  <group>
-    <clip-path
-        android:pathData="M0,0h108v108h-108z"/>
-    <path
-        android:pathData="M30,42C30,40.895 30.895,40 32,40H37C37.552,40 38,40.448 38,41V47C38,47.552 37.552,48 37,48H31C30.448,48 30,47.552 30,47V42ZM40,51C40,50.448 40.448,50 41,50H47C47.552,50 48,50.448 48,51V57C48,57.552 47.552,58 47,58H41C40.448,58 40,57.552 40,57V51ZM41,40C40.448,40 40,40.448 40,41V47C40,47.552 40.448,48 41,48H47C47.552,48 48,47.552 48,47V41C48,40.448 47.552,40 47,40H41ZM50,51C50,50.448 50.448,50 51,50H57C57.552,50 58,50.448 58,51V57C58,57.552 57.552,58 57,58H51C50.448,58 50,57.552 50,57V51ZM51,40C50.448,40 50,40.448 50,41V47C50,47.552 50.448,48 51,48H57C57.552,48 58,47.552 58,47V41C58,40.448 57.552,40 57,40H51ZM40,61C40,60.448 40.448,60 41,60H67C67.552,60 68,60.448 68,61V67C68,67.552 67.552,68 67,68H41C40.448,68 40,67.552 40,67V61ZM61,50C60.448,50 60,50.448 60,51V57C60,57.552 60.448,58 61,58H67C67.552,58 68,57.552 68,57V51C68,50.448 67.552,50 67,50H61ZM60,41C60,40.448 60.448,40 61,40H67C67.552,40 68,40.448 68,41V47C68,47.552 67.552,48 67,48H61C60.448,48 60,47.552 60,47V41ZM71,40C70.448,40 70,40.448 70,41V47C70,47.552 70.448,48 71,48H77C77.552,48 78,47.552 78,47V42C78,40.895 77.105,40 76,40H71Z"
-        android:fillColor="#87BF7B"
-        android:fillType="evenOdd"/>
-    <path
-        android:pathData="M28.54,28.54m-72,0a72,72 0,1 1,144 0a72,72 0,1 1,-144 0"
-        android:fillAlpha="0.6">
-      <aapt:attr name="android:fillColor">
-        <gradient
-            android:gradientRadius="72"
-            android:centerX="28.54"
-            android:centerY="28.54"
-            android:type="radial">
-          <item android:offset="0" android:color="#19FFFFFF"/>
-          <item android:offset="1" android:color="#00FFFFFF"/>
-        </gradient>
-      </aapt:attr>
-    </path>
-  </group>
-</vector>
diff --git a/java/res/mipmap-anydpi-v26/ic_launcher.xml b/java/res/mipmap-anydpi-v26/ic_launcher.xml
deleted file mode 100644
index 3d90bc5..0000000
--- a/java/res/mipmap-anydpi-v26/ic_launcher.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-     SPDX-FileCopyrightText: 2022 The LineageOS Project
-     SPDX-License-Identifier: Apache-2.0
--->
-<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
-    <background android:drawable="@drawable/ic_launcher_background"/>
-    <foreground android:drawable="@drawable/ic_launcher_foreground"/>
-</adaptive-icon>
diff --git a/java/res/values/gesture-input.xml b/java/res/values/gesture-input.xml
index 235616f..445a389 100644
--- a/java/res/values/gesture-input.xml
+++ b/java/res/values/gesture-input.xml
@@ -18,5 +18,5 @@
 */
 -->
 <resources>
-    <bool name="config_gesture_input_enabled_by_build_config">false</bool>
+    <bool name="config_gesture_input_enabled_by_build_config">true</bool>
 </resources>
diff --git a/java/src/com/android/inputmethod/latin/define/JniLibName.java b/java/src/com/android/inputmethod/latin/define/JniLibName.java
index 83ed5eb..abfc36d 100644
--- a/java/src/com/android/inputmethod/latin/define/JniLibName.java
+++ b/java/src/com/android/inputmethod/latin/define/JniLibName.java
@@ -22,5 +22,4 @@
     }
 
     public static final String JNI_LIB_NAME = "jni_latinime";
-    public static final String JNI_LIB_NAME2 = "jni_latinimegoogle";
 }
diff --git a/java/src/com/android/inputmethod/latin/settings/Settings.java b/java/src/com/android/inputmethod/latin/settings/Settings.java
index 803153e..57ea5ed 100644
--- a/java/src/com/android/inputmethod/latin/settings/Settings.java
+++ b/java/src/com/android/inputmethod/latin/settings/Settings.java
@@ -31,7 +31,6 @@
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.common.StringUtils;
 import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils;
-import com.android.inputmethod.latin.utils.JniUtils;
 import com.android.inputmethod.latin.utils.ResourceUtils;
 import com.android.inputmethod.latin.utils.RunInLocale;
 import com.android.inputmethod.latin.utils.StatsUtils;
@@ -49,7 +48,6 @@
     public static final String SCREEN_ACCOUNTS = "screen_accounts";
     public static final String SCREEN_THEME = "screen_theme";
     public static final String SCREEN_DEBUG = "screen_debug";
-    public static final String SCREEN_GESTURE = "screen_gesture";
     // In the same order as xml/prefs.xml
     public static final String PREF_AUTO_CAP = "auto_cap";
     public static final String PREF_VIBRATE_ON = "vibrate_on";
@@ -242,9 +240,6 @@
     }
 
     public static boolean readFromBuildConfigIfGestureInputEnabled(final Resources res) {
-        if (!JniUtils.sHaveGestureLib) {
-            return false;
-        }
         return res.getBoolean(R.bool.config_gesture_input_enabled_by_build_config);
     }
 
diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java
index 3690219..8c9ab58 100644
--- a/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java
+++ b/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java
@@ -33,7 +33,6 @@
 import com.android.inputmethod.latin.define.ProductionFlags;
 import com.android.inputmethod.latin.utils.ApplicationUtils;
 import com.android.inputmethod.latin.utils.FeedbackUtils;
-import com.android.inputmethod.latin.utils.JniUtils;
 import com.android.inputmethodcommon.InputMethodSettingsFragment;
 
 public final class SettingsFragment extends InputMethodSettingsFragment {
@@ -59,10 +58,6 @@
             final Preference accountsPreference = findPreference(Settings.SCREEN_ACCOUNTS);
             preferenceScreen.removePreference(accountsPreference);
         }
-        if (!JniUtils.sHaveGestureLib) {
-            final Preference gesturePreference = findPreference(Settings.SCREEN_GESTURE);
-            preferenceScreen.removePreference(gesturePreference);
-        }
     }
 
     @Override
diff --git a/java/src/com/android/inputmethod/latin/utils/JniUtils.java b/java/src/com/android/inputmethod/latin/utils/JniUtils.java
index 458c169..e7fdafa 100644
--- a/java/src/com/android/inputmethod/latin/utils/JniUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/JniUtils.java
@@ -23,18 +23,11 @@
 public final class JniUtils {
     private static final String TAG = JniUtils.class.getSimpleName();
 
-    public static boolean sHaveGestureLib = false;
     static {
         try {
-            System.loadLibrary(JniLibName.JNI_LIB_NAME2);
-            sHaveGestureLib = true;
-        } catch (UnsatisfiedLinkError ue) {
-            Log.e(TAG, "Could not load native library " + JniLibName.JNI_LIB_NAME2, ue);
-            try {
-                System.loadLibrary(JniLibName.JNI_LIB_NAME);
-            } catch (UnsatisfiedLinkError ule) {
-                Log.e(TAG, "Could not load native library " + JniLibName.JNI_LIB_NAME, ule);
-            }
+            System.loadLibrary(JniLibName.JNI_LIB_NAME);
+        } catch (UnsatisfiedLinkError ule) {
+            Log.e(TAG, "Could not load native library " + JniLibName.JNI_LIB_NAME, ule);
         }
     }
 
diff --git a/native/jni/Android.bp b/native/jni/Android.bp
index ab14632..4d2ec8b 100644
--- a/native/jni/Android.bp
+++ b/native/jni/Android.bp
@@ -81,7 +81,6 @@
         "src/suggest/core/session/dic_traverse_session.cpp",
         "src/suggest/core/result/suggestion_results.cpp",
         "src/suggest/core/result/suggestions_output_utils.cpp",
-        "src/suggest/policyimpl/gesture/gesture_suggest_policy_factory.cpp",
         "src/suggest/policyimpl/typing/scoring_params.cpp",
         "src/suggest/policyimpl/typing/typing_scoring.cpp",
         "src/suggest/policyimpl/typing/typing_suggest_policy.cpp",
@@ -93,6 +92,13 @@
         "src/utils/log_utils.cpp",
         "src/utils/time_keeper.cpp",
 
+	// Gesture
+        "src/suggest/policyimpl/gesture/gesture_scoring.cpp",
+        "src/suggest/policyimpl/gesture/gesture_suggest_policy.cpp",
+        "src/suggest/policyimpl/gesture/gesture_traversal.cpp",
+        "src/suggest/policyimpl/gesture/gesture_weighting.cpp",
+        "src/suggest/policyimpl/gesture/scoring_params_g.cpp",
+
         // BACKWARD_V402
         "src/dictionary/structure/backward/v402/ver4_dict_buffers.cpp",
         "src/dictionary/structure/backward/v402/ver4_dict_constants.cpp",
diff --git a/native/jni/src/defines.h b/native/jni/src/defines.h
index 1531b6c..8685452 100644
--- a/native/jni/src/defines.h
+++ b/native/jni/src/defines.h
@@ -338,5 +338,6 @@
     CT_NEW_WORD_SPACE_OMISSION,
     // Create new word with space substitution
     CT_NEW_WORD_SPACE_SUBSTITUTION,
+    CT_SKIP,
 } CorrectionType;
 #endif // LATINIME_DEFINES_H
diff --git a/native/jni/src/suggest/core/policy/traversal.h b/native/jni/src/suggest/core/policy/traversal.h
index 5b6616d..42fb40e 100644
--- a/native/jni/src/suggest/core/policy/traversal.h
+++ b/native/jni/src/suggest/core/policy/traversal.h
@@ -30,6 +30,8 @@
     virtual bool isOmission(const DicTraverseSession *const traverseSession,
             const DicNode *const dicNode, const DicNode *const childDicNode,
             const bool allowsErrorCorrections) const = 0;
+    virtual bool isSkip(const DicTraverseSession *const traverseSession,
+            const DicNode *const dicNode) const = 0;
     virtual bool isSpaceSubstitutionTerminal(const DicTraverseSession *const traverseSession,
             const DicNode *const dicNode) const = 0;
     virtual bool isSpaceOmissionTerminal(const DicTraverseSession *const traverseSession,
diff --git a/native/jni/src/suggest/core/policy/weighting.cpp b/native/jni/src/suggest/core/policy/weighting.cpp
index 450203d..368db04 100644
--- a/native/jni/src/suggest/core/policy/weighting.cpp
+++ b/native/jni/src/suggest/core/policy/weighting.cpp
@@ -134,6 +134,8 @@
         return weighting->getInsertionCost(traverseSession, parentDicNode, dicNode);
     case CT_TRANSPOSITION:
         return weighting->getTranspositionCost(traverseSession, parentDicNode, dicNode);
+    case CT_SKIP:
+        return weighting->getSkipCost(traverseSession, dicNode);
     default:
         return 0.0f;
     }
@@ -199,8 +201,67 @@
             return 2; /* look ahead + skip the current char */
         case CT_TRANSPOSITION:
             return 2; /* look ahead + skip the current char */
+        case CT_SKIP:
+            return 1;
         default:
             return 0;
     }
 }
+
+
+    /* static */ void Weighting::addCostAndForwardInputIndexGesture(const Weighting *const weighting,
+                                                                    const CorrectionType correctionType, const DicTraverseSession *const traverseSession,
+                                                                    const DicNode *const parentDicNode, DicNode *const dicNode,
+                                                                    MultiBigramMap *const multiBigramMap) {
+        const int inputSize = traverseSession->getInputSize();
+        DicNode_InputStateG inputStateG;
+        inputStateG.mNeedsToUpdateInputStateG = false; // Don't use input info by default
+        const float spatialCost = Weighting::getSpatialCost(weighting, correctionType,
+                                                            traverseSession, parentDicNode, dicNode, &inputStateG);
+        const float languageCost = Weighting::getLanguageCostGesture(weighting, correctionType,
+                                                                     traverseSession, parentDicNode, dicNode, multiBigramMap);
+        const ErrorTypeUtils::ErrorType errorType = weighting->getErrorType(correctionType,
+                                                                            traverseSession, parentDicNode, dicNode);
+        if (inputStateG.mNeedsToUpdateInputStateG) {
+            dicNode->updateInputIndexG(&inputStateG);
+        } else {
+            dicNode->forwardInputIndex(0 /*pointerId*/, getForwardInputCount(correctionType), false);
+        }
+        dicNode->addCost(spatialCost, languageCost, weighting->needsToNormalizeCompoundDistance(),
+                         inputSize, errorType);
+    }
+
+    /* static */ float Weighting::getLanguageCostGesture(const Weighting *const weighting,
+                                                         const CorrectionType correctionType, const DicTraverseSession *const traverseSession,
+                                                         const DicNode *const parentDicNode, const DicNode *const dicNode,
+                                                         MultiBigramMap *const multiBigramMap) {
+        switch(correctionType) {
+            case CT_OMISSION:
+                return 0.0f;
+            case CT_SUBSTITUTION:
+                return 0.0f;
+            case CT_NEW_WORD_SPACE_OMISSION:
+                return 0;
+            case CT_MATCH:
+                return 0.0f;
+            case CT_COMPLETION:
+                return 0.0f;
+            case CT_TERMINAL: {
+                const float languageImprobability =
+                        DicNodeUtils::getBigramNodeImprobability(
+                                traverseSession->getDictionaryStructurePolicy(), dicNode, multiBigramMap);
+                return weighting->getTerminalLanguageCost(traverseSession, dicNode, languageImprobability);
+            }
+            case CT_TERMINAL_INSERTION:
+                return 0.0f;
+            case CT_NEW_WORD_SPACE_SUBSTITUTION:
+                return 0;
+            case CT_INSERTION:
+                return 0.0f;
+            case CT_TRANSPOSITION:
+                return 0.0f;
+            default:
+                return 0.0f;
+        }
+    }
 }  // namespace latinime
diff --git a/native/jni/src/suggest/core/policy/weighting.h b/native/jni/src/suggest/core/policy/weighting.h
index 863c4ea..dc437b3 100644
--- a/native/jni/src/suggest/core/policy/weighting.h
+++ b/native/jni/src/suggest/core/policy/weighting.h
@@ -34,6 +34,11 @@
             const DicTraverseSession *const traverseSession,
             const DicNode *const parentDicNode, DicNode *const dicNode,
             MultiBigramMap *const multiBigramMap);
+    static void addCostAndForwardInputIndexGesture(const Weighting *const weighting,
+            const CorrectionType correctionType,
+            const DicTraverseSession *const traverseSession,
+            const DicNode *const parentDicNode, DicNode *const dicNode,
+            MultiBigramMap *const multiBigramMap);
 
  protected:
     virtual float getTerminalSpatialCost(const DicTraverseSession *const traverseSession,
@@ -85,6 +90,9 @@
     virtual float getSpaceSubstitutionCost(const DicTraverseSession *const traverseSession,
             const DicNode *const dicNode) const = 0;
 
+    virtual float getSkipCost(const DicTraverseSession *const traverseSession,
+                              const DicNode *const dicNode) const = 0;
+
     virtual ErrorTypeUtils::ErrorType getErrorType(const CorrectionType correctionType,
             const DicTraverseSession *const traverseSession,
             const DicNode *const parentDicNode, const DicNode *const dicNode) const = 0;
@@ -103,6 +111,10 @@
             const CorrectionType correctionType, const DicTraverseSession *const traverseSession,
             const DicNode *const parentDicNode, const DicNode *const dicNode,
             MultiBigramMap *const multiBigramMap);
+    static float getLanguageCostGesture(const Weighting *const weighting,
+            const CorrectionType correctionType, const DicTraverseSession *const traverseSession,
+            const DicNode *const parentDicNode, const DicNode *const dicNode,
+            MultiBigramMap *const multiBigramMap);
     // TODO: Move to TypingWeighting and GestureWeighting?
     static int getForwardInputCount(const CorrectionType correctionType);
 };
diff --git a/native/jni/src/suggest/core/result/suggestions_output_utils.cpp b/native/jni/src/suggest/core/result/suggestions_output_utils.cpp
index 7c37241..2312c7c 100644
--- a/native/jni/src/suggest/core/result/suggestions_output_utils.cpp
+++ b/native/jni/src/suggest/core/result/suggestions_output_utils.cpp
@@ -27,11 +27,94 @@
 #include "suggest/core/result/suggestion_results.h"
 #include "suggest/core/session/dic_traverse_session.h"
 #include "suggest/core/suggest_options.h"
+#include "defines.h"
 
 namespace latinime {
 
 const int SuggestionsOutputUtils::MIN_LEN_FOR_MULTI_WORD_AUTOCORRECT = 16;
 
+    /* static */ void SuggestionsOutputUtils::outputSuggestionsGesture(
+            const Scoring *const scoringPolicy, DicTraverseSession *traverseSession,
+            const float weightOfLangModelVsSpatialModel,
+            SuggestionResults *const outSuggestionResults) {
+#if DEBUG_EVALUATE_MOST_PROBABLE_STRING
+        const int terminalSize = 0;
+#else
+        const int terminalSize = traverseSession->getDicTraverseCache()->terminalSize();
+#endif
+        std::vector<DicNode> terminals(terminalSize);
+        for (int index = terminalSize - 1; index >= 0; --index) {
+            traverseSession->getDicTraverseCache()->popTerminal(&terminals[index]);
+        }
+
+        const bool outputSecondWordFirstLetterInputIndex =
+                traverseSession->isOnlyOnePointerUsed(0 /* pointerId */);
+        const bool boostExactMatches = traverseSession->getDictionaryStructurePolicy()->
+                getHeaderStructurePolicy()->shouldBoostExactMatches();
+
+        // Output suggestion results here
+        for (auto &terminalDicNode : terminals) {
+            outputSuggestionsOfDicNodeGesture(scoringPolicy, traverseSession, &terminalDicNode,
+                                       1.0, boostExactMatches,
+                                       false, outputSecondWordFirstLetterInputIndex, outSuggestionResults);
+        }
+    }
+
+    /* static */ void SuggestionsOutputUtils::outputSuggestionsOfDicNodeGesture(
+            const Scoring *const scoringPolicy, DicTraverseSession *traverseSession,
+            const DicNode *const terminalDicNode, const float weightOfLangModelVsSpatialModel,
+            const bool boostExactMatches, const bool forceCommitMultiWords,
+            const bool outputSecondWordFirstLetterInputIndex,
+            SuggestionResults *const outSuggestionResults) {
+
+
+        const WordAttributes wordAttributes = traverseSession->getDictionaryStructurePolicy()
+                ->getWordAttributesInContext(terminalDicNode->getPrevWordIds(),
+                                             terminalDicNode->getWordId(), nullptr /* multiBigramMap */);
+        const bool isExactMatch =
+                ErrorTypeUtils::isExactMatch(terminalDicNode->getContainedErrorTypes());
+        const bool isExactMatchWithIntentionalOmission =
+                ErrorTypeUtils::isExactMatchWithIntentionalOmission(
+                        terminalDicNode->getContainedErrorTypes());
+        // TODO: Decide whether the word should be auto-corrected or not here.
+        const bool isAppropriateForAutoCorrection = !ErrorTypeUtils::isMissingExplicitAccent(
+                terminalDicNode->getContainedErrorTypes());
+        const int outputTypeFlags =
+                (wordAttributes.isPossiblyOffensive() ? Dictionary::KIND_FLAG_POSSIBLY_OFFENSIVE : 0)
+                | ((isExactMatch && boostExactMatches) ? Dictionary::KIND_FLAG_EXACT_MATCH : 0)
+                | (isExactMatchWithIntentionalOmission ?
+                   Dictionary::KIND_FLAG_EXACT_MATCH_WITH_INTENTIONAL_OMISSION : 0)
+                | (isAppropriateForAutoCorrection ?
+                   Dictionary::KIND_FLAG_APPROPRIATE_FOR_AUTOCORRECTION : 0);
+        // Entries that are blacklisted or do not represent a word should not be output.
+        const bool isValidWord = !(wordAttributes.isBlacklisted() || wordAttributes.isNotAWord());
+
+        const bool shouldBlockThisWord = shouldBlockWord(traverseSession->getSuggestOptions(),
+                                                         terminalDicNode, wordAttributes, true /* isLastWord */);
+
+        // Increase output score of top typing suggestion to ensure autocorrection.
+        // TODO: Better integration with java side autocorrection logic.
+        const float compoundDistance = terminalDicNode->getCompoundDistance(1);
+        const int finalScore = compoundDistance * -10000.0f;
+
+        // Don't output invalid or blocked offensive words. However, we still need to submit their
+        // shortcuts if any.
+        if (isValidWord && !shouldBlockThisWord) {
+            int codePoints[MAX_WORD_LENGTH];
+            terminalDicNode->outputResult(codePoints);
+            const int indexToPartialCommit = outputSecondWordFirstLetterInputIndex ?
+                                             terminalDicNode->getSecondWordFirstInputIndex(
+                                                     traverseSession->getProximityInfoState(0)) :
+                                             NOT_AN_INDEX;
+
+            outSuggestionResults->addSuggestion(codePoints,
+                                                terminalDicNode->getTotalNodeCodePointCount(),
+                                                finalScore, Dictionary::KIND_CORRECTION | outputTypeFlags,
+                                                indexToPartialCommit, computeFirstWordConfidence(terminalDicNode));
+        }
+
+    }
+
 /* static */ void SuggestionsOutputUtils::outputSuggestions(
         const Scoring *const scoringPolicy, DicTraverseSession *traverseSession,
         const float weightOfLangModelVsSpatialModel,
diff --git a/native/jni/src/suggest/core/result/suggestions_output_utils.h b/native/jni/src/suggest/core/result/suggestions_output_utils.h
index bcb75a4..fc0d04e 100644
--- a/native/jni/src/suggest/core/result/suggestions_output_utils.h
+++ b/native/jni/src/suggest/core/result/suggestions_output_utils.h
@@ -45,12 +45,22 @@
             DicTraverseSession *traverseSession, const float weightOfLangModelVsSpatialModel,
             SuggestionResults *const outSuggestionResults);
 
+    static void outputSuggestionsGesture(const Scoring *const scoringPolicy,
+            DicTraverseSession *traverseSession, const float weightOfLangModelVsSpatialModel,
+            SuggestionResults *const outSuggestionResults);
+
  private:
     DISALLOW_IMPLICIT_CONSTRUCTORS(SuggestionsOutputUtils);
 
     // Inputs longer than this will autocorrect if the suggestion is multi-word
     static const int MIN_LEN_FOR_MULTI_WORD_AUTOCORRECT;
 
+    static void outputSuggestionsOfDicNodeGesture(const Scoring *const scoringPolicy,
+           DicTraverseSession *traverseSession, const DicNode *const terminalDicNode,
+           const float weightOfLangModelVsSpatialModel, const bool boostExactMatches,
+           const bool forceCommitMultiWords, const bool outputSecondWordFirstLetterInputIndex,
+           SuggestionResults *const outSuggestionResults);
+
     static void outputSuggestionsOfDicNode(const Scoring *const scoringPolicy,
             DicTraverseSession *traverseSession, const DicNode *const terminalDicNode,
             const float weightOfLangModelVsSpatialModel, const bool boostExactMatches,
diff --git a/native/jni/src/suggest/core/session/dic_traverse_session.cpp b/native/jni/src/suggest/core/session/dic_traverse_session.cpp
index d7dd5a0..4dc0e38 100644
--- a/native/jni/src/suggest/core/session/dic_traverse_session.cpp
+++ b/native/jni/src/suggest/core/session/dic_traverse_session.cpp
@@ -66,7 +66,8 @@
         const int maxPointerCount) {
     ASSERT(1 <= maxPointerCount && maxPointerCount <= MAX_POINTER_COUNT_G);
     mInputSize = 0;
-    for (int i = 0; i < maxPointerCount; ++i) {
+    // change MAX_POINTER_COUNT_G to MAX_POINTER_COUNT, not support 2 pointers
+    for (int i = 0; i < MAX_POINTER_COUNT; ++i) {
         mProximityInfoStates[i].initInputParams(i, maxSpatialDistance, getProximityInfo(),
                 inputCodePoints, inputSize, inputXs, inputYs, times, pointerIds,
                 // Right now the line below is trying to figure out whether this is a gesture by
diff --git a/native/jni/src/suggest/core/suggest.cpp b/native/jni/src/suggest/core/suggest.cpp
index 52fa5a5..95e9987 100644
--- a/native/jni/src/suggest/core/suggest.cpp
+++ b/native/jni/src/suggest/core/suggest.cpp
@@ -52,6 +52,7 @@
     PROF_INIT;
     PROF_TIMER_START(0);
     const float maxSpatialDistance = TRAVERSAL->getMaxSpatialDistance();
+    const bool isGesture = TRAVERSAL->getMaxPointerCount() == MAX_POINTER_COUNT_G;
     DicTraverseSession *tSession = static_cast<DicTraverseSession *>(traverseSession);
     tSession->setupForGetSuggestions(pInfo, inputCodePoints, inputSize, inputXs, inputYs, times,
             pointerIds, maxSpatialDistance, TRAVERSAL->getMaxPointerCount());
@@ -59,19 +60,36 @@
 
     initializeSearch(tSession);
     PROF_TIMER_END(0);
-    PROF_TIMER_START(1);
 
-    // keep expanding search dicNodes until all have terminated.
-    while (tSession->getDicTraverseCache()->activeSize() > 0) {
-        expandCurrentDicNodes(tSession);
-        tSession->getDicTraverseCache()->advanceActiveDicNodes();
-        tSession->getDicTraverseCache()->advanceInputIndex(inputSize);
+    if (isGesture) {
+        PROF_TIMER_START(1);
+
+        // keep expanding search dicNodes until all have terminated.
+        while (tSession->getDicTraverseCache()->activeSize() > 0) {
+            expandCurrentDicNodesGesture(tSession);
+            tSession->getDicTraverseCache()->advanceActiveDicNodes();
+            tSession->getDicTraverseCache()->advanceInputIndex(inputSize);
+        }
+        PROF_TIMER_END(1);
+        PROF_TIMER_START(2);
+        SuggestionsOutputUtils::outputSuggestionsGesture(
+                SCORING, tSession, weightOfLangModelVsSpatialModel, outSuggestionResults);
+        PROF_TIMER_END(2);
+    } else {
+        PROF_TIMER_START(1);
+
+        // keep expanding search dicNodes until all have terminated.
+        while (tSession->getDicTraverseCache()->activeSize() > 0) {
+            expandCurrentDicNodes(tSession);
+            tSession->getDicTraverseCache()->advanceActiveDicNodes();
+            tSession->getDicTraverseCache()->advanceInputIndex(inputSize);
+        }
+        PROF_TIMER_END(1);
+        PROF_TIMER_START(2);
+        SuggestionsOutputUtils::outputSuggestions(
+                SCORING, tSession, weightOfLangModelVsSpatialModel, outSuggestionResults);
+        PROF_TIMER_END(2);
     }
-    PROF_TIMER_END(1);
-    PROF_TIMER_START(2);
-    SuggestionsOutputUtils::outputSuggestions(
-            SCORING, tSession, weightOfLangModelVsSpatialModel, outSuggestionResults);
-    PROF_TIMER_END(2);
 }
 
 /**
@@ -441,4 +459,141 @@
         traverseSession->getDicTraverseCache()->copyPushNextActive(&newDicNode);
     }
 }
+
+    //for gesture
+    void Suggest::expandCurrentDicNodesGesture(DicTraverseSession *traverseSession) const {
+        const int inputSize = traverseSession->getInputSize();
+        DicNodeVector childDicNodes(TRAVERSAL->getDefaultExpandDicNodeSize());
+        DicNode correctionDicNode;
+
+        while (traverseSession->getDicTraverseCache()->activeSize() > 0) {
+            DicNode dicNode;
+            traverseSession->getDicTraverseCache()->popActive(&dicNode);
+
+            if (dicNode.isTotalInputSizeExceedingLimit()) {
+                return;
+            }
+
+            // Only consider typing error corrections if the normalized compound distance is
+            // below a spatial distance threshold.
+            // NOTE: the threshold may need to be updated if scoring model changes.
+            const bool allowsErrorCorrections = TRAVERSAL->allowsErrorCorrections(&dicNode);
+            const int point0Index = dicNode.getInputIndex(0);
+            const bool isCompletion = dicNode.isCompletion(inputSize);
+
+            //const bool isSkip = TRAVERSAL->isSkip(traverseSession, &dicNode);
+            if (point0Index > 0 && TRAVERSAL->isSkip(traverseSession, &dicNode)) {
+                correctionDicNode.initByCopy(&dicNode);
+                processDicNodeAsSkipGesture(traverseSession, &correctionDicNode);
+            }
+
+            childDicNodes.clear();
+            DicNodeUtils::getAllChildDicNodes(&dicNode,
+                                              traverseSession->getDictionaryStructurePolicy(),
+                                              &childDicNodes);
+            const int childDicNodesSize = childDicNodes.getSizeAndLock();
+            for (int i = 0; i < childDicNodesSize; ++i) {
+                DicNode *const childDicNode = childDicNodes[i];
+                if (isCompletion) {
+                    // Handle forward lookahead when the lexicon letter exceeds the input size.
+                    processDicNodeAsMatchGesture(traverseSession, childDicNode);
+                    continue;
+                }
+
+                if(childDicNode->canBeIntentionalOmission()) {
+                    processExpandedDicNodeGesture(traverseSession, childDicNode);
+                    continue;
+                }
+
+                if (point0Index > 0 && TRAVERSAL->isOmission(traverseSession, &dicNode, childDicNode,
+                                                              allowsErrorCorrections)) {
+                    // TODO: (Gesture) Change weight between omission and substitution errors
+                    // TODO: (Gesture) Terminal node should not be handled as omission
+                    correctionDicNode.initByCopy(childDicNode);
+                    processDicNodeAsOmissionGesture(traverseSession, &correctionDicNode);
+                }
+                const ProximityType proximityType = TRAVERSAL->getProximityType(
+                        traverseSession, &dicNode, childDicNode);
+                switch (proximityType) {
+                    // TODO: Consider the difference of proximityType here
+                    case MATCH_CHAR:
+                        processDicNodeAsMatchGesture(traverseSession, childDicNode);
+                        break;
+                    default:
+                        // Just drop this dicNode and do nothing.
+                        break;
+                }
+            }
+
+        }
+    }
+
+    void Suggest::processDicNodeAsSkipGesture(DicTraverseSession *traverseSession,
+                                              DicNode *dicNode) const {
+        Weighting::addCostAndForwardInputIndexGesture(WEIGHTING, CT_SKIP, traverseSession,
+                                                      0 /* parentDicNode */, dicNode, 0 /* multiBigramMap */);
+        processExpandedDicNodeGesture(traverseSession, dicNode);
+    }
+
+    void Suggest::processDicNodeAsMatchGesture(DicTraverseSession *traverseSession,
+                                               DicNode *childDicNode) const {
+        weightChildNodeGesture(traverseSession, childDicNode);
+        processExpandedDicNodeGesture(traverseSession, childDicNode);
+    }
+
+    void Suggest::processDicNodeAsOmissionGesture(DicTraverseSession *traverseSession, DicNode *dicNode) const {
+        DicNodeVector childDicNodes;
+        DicNodeUtils::getAllChildDicNodes(
+                dicNode, traverseSession->getDictionaryStructurePolicy(), &childDicNodes);
+
+        const int size = childDicNodes.getSizeAndLock();
+        for (int i = 0; i < size; i++) {
+            DicNode *const childDicNode = childDicNodes[i];
+            // Treat this word as omission
+            if (!TRAVERSAL->isPossibleOmissionChildNode(traverseSession, dicNode, childDicNode)) {
+                continue;
+            }
+
+            Weighting::addCostAndForwardInputIndexGesture(WEIGHTING, CT_OMISSION, traverseSession,
+                                                   dicNode, childDicNode, 0 /* multiBigramMap */);
+            weightChildNodeGesture(traverseSession, childDicNode);
+            processExpandedDicNodeGesture(traverseSession, childDicNode);
+        }
+    }
+
+    void Suggest::weightChildNodeGesture(DicTraverseSession *traverseSession, DicNode *dicNode) const {
+        const int inputSize = traverseSession->getInputSize();
+        if (dicNode->isCompletion(inputSize)) {
+            Weighting::addCostAndForwardInputIndexGesture(WEIGHTING, CT_COMPLETION, traverseSession,
+                                                          0 /* parentDicNode */, dicNode, 0 /* multiBigramMap */);
+        } else {
+            Weighting::addCostAndForwardInputIndexGesture(WEIGHTING, CT_MATCH, traverseSession,
+                                                          0 /* parentDicNode */, dicNode, 0 /* multiBigramMap */);
+        }
+    }
+
+    void Suggest::processExpandedDicNodeGesture(
+            DicTraverseSession *traverseSession, DicNode *dicNode) const {
+        processTerminalDicNodeGesture(traverseSession, dicNode);
+        if (dicNode->getCompoundDistance() < static_cast<float>(MAX_VALUE_FOR_WEIGHTING)) {
+            if (dicNode->hasChildren()) {
+                traverseSession->getDicTraverseCache()->copyPushNextActive(dicNode);
+            }
+        }
+    }
+
+    void Suggest::processTerminalDicNodeGesture(
+            DicTraverseSession *traverseSession, DicNode *dicNode) const {
+        if (dicNode->getCompoundDistance() >= static_cast<float>(MAX_VALUE_FOR_WEIGHTING)) {
+            return;
+        }
+        if (!dicNode->isTerminalDicNode()) {
+            return;
+        }
+        // Create a non-cached node here.
+        DicNode terminalDicNode(*dicNode);
+        Weighting::addCostAndForwardInputIndexGesture(WEIGHTING, CT_TERMINAL, traverseSession, 0,
+                                               &terminalDicNode, traverseSession->getMultiBigramMap());
+        traverseSession->getDicTraverseCache()->copyPushTerminal(&terminalDicNode);
+    }
 } // namespace latinime
diff --git a/native/jni/src/suggest/core/suggest.h b/native/jni/src/suggest/core/suggest.h
index 65d5918..39ae104 100644
--- a/native/jni/src/suggest/core/suggest.h
+++ b/native/jni/src/suggest/core/suggest.h
@@ -73,6 +73,15 @@
     void processDicNodeAsMatch(DicTraverseSession *traverseSession,
             DicNode *childDicNode) const;
 
+    void expandCurrentDicNodesGesture(DicTraverseSession *traverseSession) const;
+    void processDicNodeAsOmissionGesture(DicTraverseSession *traverseSession, DicNode *dicNode) const;
+    void processDicNodeAsMatchGesture(DicTraverseSession *traverseSession,
+                               DicNode *childDicNode) const;
+    void processDicNodeAsSkipGesture(DicTraverseSession *traverseSession, DicNode *dicNode) const;
+    void processExpandedDicNodeGesture(DicTraverseSession *traverseSession, DicNode *dicNode) const;
+    void processTerminalDicNodeGesture(DicTraverseSession *traverseSession, DicNode *dicNode) const;
+    void weightChildNodeGesture(DicTraverseSession *traverseSession, DicNode *dicNode) const;
+
     static const int MIN_CONTINUOUS_SUGGESTION_INPUT_SIZE;
 
     const Traversal *const TRAVERSAL;
diff --git a/native/jni/src/suggest/policyimpl/gesture/gesture_scoring.cpp b/native/jni/src/suggest/policyimpl/gesture/gesture_scoring.cpp
new file mode 100644
index 0000000..ba56915
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/gesture/gesture_scoring.cpp
@@ -0,0 +1,10 @@
+//
+// Created by msj on 2017/3/9.
+//
+
+
+#include "suggest/policyimpl/gesture/gesture_scoring.h"
+
+namespace latinime {
+    const GestureScoring GestureScoring::sInstance;
+}  // namespace latinime
\ No newline at end of file
diff --git a/native/jni/src/suggest/policyimpl/gesture/gesture_scoring.h b/native/jni/src/suggest/policyimpl/gesture/gesture_scoring.h
new file mode 100644
index 0000000..574c00e
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/gesture/gesture_scoring.h
@@ -0,0 +1,108 @@
+//
+// Created by msj on 2017/3/9.
+//
+
+#ifndef GOOGLEKEYBOARDV7_GESTURE_SCORING_H
+#define GOOGLEKEYBOARDV7_GESTURE_SCORING_H
+
+#include "defines.h"
+#include "suggest/core/dictionary/error_type_utils.h"
+#include "suggest/core/policy/scoring.h"
+#include "suggest/core/session/dic_traverse_session.h"
+#include "suggest/policyimpl/typing/scoring_params.h"
+
+namespace latinime {
+
+    class DicNode;
+    class DicTraverseSession;
+
+    class GestureScoring : public Scoring {
+    public:
+        static const GestureScoring *getInstance() { return &sInstance; }
+
+        AK_FORCE_INLINE void getMostProbableString(const DicTraverseSession *const traverseSession,
+                                                   const float weightOfLangModelVsSpatialModel,
+                                                   SuggestionResults *const outSuggestionResults) const {}
+
+        AK_FORCE_INLINE float getAdjustedWeightOfLangModelVsSpatialModel(
+                DicTraverseSession *const traverseSession, DicNode *const terminals,
+                const int size) const {
+            return 1.0f;
+        }
+
+        AK_FORCE_INLINE int calculateFinalScore(const float compoundDistance, const int inputSize,
+                                                const ErrorTypeUtils::ErrorType containedErrorTypes, const bool forceCommit,
+                                                const bool boostExactMatches, const bool hasProbabilityZero) const {
+            const float maxDistance = ScoringParams::DISTANCE_WEIGHT_LANGUAGE
+                                      + static_cast<float>(inputSize) * ScoringParams::TYPING_MAX_OUTPUT_SCORE_PER_INPUT;
+            float score = ScoringParams::TYPING_BASE_OUTPUT_SCORE - compoundDistance / maxDistance;
+            if (forceCommit) {
+                score += ScoringParams::AUTOCORRECT_OUTPUT_THRESHOLD;
+            }
+            if (hasProbabilityZero) {
+                // Previously, when both legitimate 0-frequency words (such as distracters) and
+                // offensive words were encoded in the same way, distracters would never show up
+                // when the user blocked offensive words (the default setting, as well as the
+                // setting for regression tests).
+                //
+                // When b/11031090 was fixed and a separate encoding was used for offensive words,
+                // 0-frequency words would no longer be blocked when they were an "exact match"
+                // (where case mismatches and accent mismatches would be considered an "exact
+                // match"). The exact match boosting functionality meant that, for example, when
+                // the user typed "mt" they would be suggested the word "Mt", although they most
+                // probably meant to type "my".
+                //
+                // For this reason, we introduced this change, which does the following:
+                // * Defines the "perfect match" as a really exact match, with no room for case or
+                // accent mismatches
+                // * When the target word has probability zero (as "Mt" does, because it is a
+                // distracter), ONLY boost its score if it is a perfect match.
+                //
+                // By doing this, when the user types "mt", the word "Mt" will NOT be boosted, and
+                // they will get "my". However, if the user makes an explicit effort to type "Mt",
+                // we do boost the word "Mt" so that the user's input is not autocorrected to "My".
+                if (boostExactMatches && ErrorTypeUtils::isPerfectMatch(containedErrorTypes)) {
+                    score += ScoringParams::PERFECT_MATCH_PROMOTION;
+                }
+            } else {
+                if (boostExactMatches && ErrorTypeUtils::isExactMatch(containedErrorTypes)) {
+                    score += ScoringParams::EXACT_MATCH_PROMOTION;
+                    if ((ErrorTypeUtils::MATCH_WITH_WRONG_CASE & containedErrorTypes) != 0) {
+                        score -= ScoringParams::CASE_ERROR_PENALTY_FOR_EXACT_MATCH;
+                    }
+                    if ((ErrorTypeUtils::MATCH_WITH_MISSING_ACCENT & containedErrorTypes) != 0) {
+                        score -= ScoringParams::ACCENT_ERROR_PENALTY_FOR_EXACT_MATCH;
+                    }
+                    if ((ErrorTypeUtils::MATCH_WITH_DIGRAPH & containedErrorTypes) != 0) {
+                        score -= ScoringParams::DIGRAPH_PENALTY_FOR_EXACT_MATCH;
+                    }
+                }
+            }
+            return static_cast<int>(score * SUGGEST_INTERFACE_OUTPUT_SCALE);
+        }
+
+        AK_FORCE_INLINE float getDoubleLetterDemotionDistanceCost(
+                const DicNode *const terminalDicNode) const {
+            return 0.0f;
+        }
+
+        AK_FORCE_INLINE bool autoCorrectsToMultiWordSuggestionIfTop() const {
+            return true;
+        }
+
+        AK_FORCE_INLINE bool sameAsTyped(const DicTraverseSession *const traverseSession,
+                                         const DicNode *const dicNode) const {
+            return traverseSession->getProximityInfoState(0)->sameAsTyped(
+                    dicNode->getOutputWordBuf(), dicNode->getNodeCodePointCount());
+        }
+
+    private:
+        DISALLOW_COPY_AND_ASSIGN(GestureScoring);
+        static const GestureScoring sInstance;
+
+        GestureScoring() {}
+        ~GestureScoring() {}
+    };
+} // namespace latinime
+
+#endif //GOOGLEKEYBOARDV7_GESTURE_SCORING_H
diff --git a/native/jni/src/suggest/policyimpl/gesture/gesture_suggest_policy.cpp b/native/jni/src/suggest/policyimpl/gesture/gesture_suggest_policy.cpp
new file mode 100644
index 0000000..13c51e9
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/gesture/gesture_suggest_policy.cpp
@@ -0,0 +1,10 @@
+//
+// Created by msj on 2017/3/9.
+//
+
+#include "suggest/policyimpl/gesture/gesture_suggest_policy.h"
+
+namespace latinime {
+    const GestureSuggestPolicy GestureSuggestPolicy::sInstance;
+} // namespace latinime
+
diff --git a/native/jni/src/suggest/policyimpl/gesture/gesture_suggest_policy.h b/native/jni/src/suggest/policyimpl/gesture/gesture_suggest_policy.h
new file mode 100644
index 0000000..8d035a9
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/gesture/gesture_suggest_policy.h
@@ -0,0 +1,50 @@
+//
+// Created by msj on 2017/3/9.
+//
+
+#ifndef GOOGLEKEYBOARDV7_GESTURE_SUGGEST_POLICY_H
+#define GOOGLEKEYBOARDV7_GESTURE_SUGGEST_POLICY_H
+
+#include "defines.h"
+#include "suggest/core/policy/suggest_policy.h"
+#include "suggest/policyimpl/gesture/gesture_scoring.h"
+#include "suggest/policyimpl/gesture/gesture_traversal.h"
+#include "suggest/policyimpl/gesture/gesture_weighting.h"
+
+namespace latinime {
+
+    class Scoring;
+
+    class Traversal;
+
+    class Weighting;
+
+    class GestureSuggestPolicy : public SuggestPolicy {
+    public:
+        static const GestureSuggestPolicy *getInstance() { return &sInstance; }
+
+        GestureSuggestPolicy() { }
+
+        virtual ~GestureSuggestPolicy() { }
+
+        AK_FORCE_INLINE const Traversal *getTraversal() const {
+            return GestureTraversal::getInstance();
+        }
+
+        AK_FORCE_INLINE const Scoring *getScoring() const {
+            return GestureScoring::getInstance();
+        }
+
+        AK_FORCE_INLINE const Weighting *getWeighting() const {
+            return GestureWeighting::getInstance();
+        }
+
+    private:
+        DISALLOW_COPY_AND_ASSIGN(GestureSuggestPolicy);
+
+        static const GestureSuggestPolicy sInstance;
+    };
+
+} // namespace latinime
+
+#endif //GOOGLEKEYBOARDV7_GESTURE_SUGGEST_POLICY_H
diff --git a/native/jni/src/suggest/policyimpl/gesture/gesture_suggest_policy_factory.cpp b/native/jni/src/suggest/policyimpl/gesture/gesture_suggest_policy_factory.cpp
deleted file mode 100644
index 6d31739..0000000
--- a/native/jni/src/suggest/policyimpl/gesture/gesture_suggest_policy_factory.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * 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.
- */
-
-#include "gesture_suggest_policy_factory.h"
-
-namespace latinime {
-    const SuggestPolicy *(*GestureSuggestPolicyFactory::sGestureSuggestFactoryMethod)() = 0;
-} // namespace latinime
diff --git a/native/jni/src/suggest/policyimpl/gesture/gesture_suggest_policy_factory.h b/native/jni/src/suggest/policyimpl/gesture/gesture_suggest_policy_factory.h
index 509b01f..6052d9f 100644
--- a/native/jni/src/suggest/policyimpl/gesture/gesture_suggest_policy_factory.h
+++ b/native/jni/src/suggest/policyimpl/gesture/gesture_suggest_policy_factory.h
@@ -18,27 +18,22 @@
 #define LATINIME_GESTURE_SUGGEST_POLICY_FACTORY_H
 
 #include "defines.h"
+#include "gesture_suggest_policy.h"
 
 namespace latinime {
 
 class SuggestPolicy;
 
-class GestureSuggestPolicyFactory {
- public:
-    static void setGestureSuggestPolicyFactoryMethod(const SuggestPolicy *(*factoryMethod)()) {
-        sGestureSuggestFactoryMethod = factoryMethod;
-    }
+    class SuggestPolicy;
 
-    static const SuggestPolicy *getGestureSuggestPolicy() {
-        if (!sGestureSuggestFactoryMethod) {
-            return 0;
+    class GestureSuggestPolicyFactory {
+    public:
+        static const SuggestPolicy *getGestureSuggestPolicy() {
+            return GestureSuggestPolicy::getInstance();
         }
-        return sGestureSuggestFactoryMethod();
-    }
 
- private:
-    DISALLOW_COPY_AND_ASSIGN(GestureSuggestPolicyFactory);
-    static const SuggestPolicy *(*sGestureSuggestFactoryMethod)();
-};
+    private:
+        DISALLOW_COPY_AND_ASSIGN(GestureSuggestPolicyFactory);
+    };
 } // namespace latinime
 #endif // LATINIME_GESTURE_SUGGEST_POLICY_FACTORY_H
diff --git a/native/jni/src/suggest/policyimpl/gesture/gesture_traversal.cpp b/native/jni/src/suggest/policyimpl/gesture/gesture_traversal.cpp
new file mode 100644
index 0000000..bfe046e
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/gesture/gesture_traversal.cpp
@@ -0,0 +1,10 @@
+//
+// Created by msj on 2017/3/9.
+//
+
+#include "suggest/policyimpl/gesture/gesture_traversal.h"
+
+namespace latinime {
+    const bool GestureTraversal::CORRECT_OMISSION = true;
+    const GestureTraversal GestureTraversal::sInstance;
+}  // namespace latinime
\ No newline at end of file
diff --git a/native/jni/src/suggest/policyimpl/gesture/gesture_traversal.h b/native/jni/src/suggest/policyimpl/gesture/gesture_traversal.h
new file mode 100644
index 0000000..fc298db
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/gesture/gesture_traversal.h
@@ -0,0 +1,190 @@
+//
+// Created by msj on 2017/3/9.
+//
+
+#ifndef GOOGLEKEYBOARDV7_GESTURE_TRAVERSAL_H
+#define GOOGLEKEYBOARDV7_GESTURE_TRAVERSAL_H
+
+
+#include <cstdint>
+
+#include "defines.h"
+#include "suggest/core/dicnode/dic_node.h"
+#include "suggest/core/dicnode/dic_node_vector.h"
+#include "suggest/core/layout/proximity_info_state.h"
+#include "suggest/core/layout/proximity_info_utils.h"
+#include "suggest/core/policy/traversal.h"
+#include "suggest/core/session/dic_traverse_session.h"
+#include "suggest/core/suggest_options.h"
+#include "suggest/policyimpl/typing/scoring_params.h"
+#include "suggest/policyimpl/gesture/scoring_params_g.h"
+#include "utils/char_utils.h"
+
+namespace latinime {
+    class GestureTraversal : public Traversal {
+    public:
+        static const GestureTraversal *getInstance() { return &sInstance; }
+
+        int getMaxPointerCount() const {
+            return MAX_POINTER_COUNT_G;
+        }
+
+        float getMaxSpatialDistance() const {
+            return ScoringParams::MAX_SPATIAL_DISTANCE;
+        }
+
+        int getDefaultExpandDicNodeSize() const {
+            return DicNodeVector::DEFAULT_NODES_SIZE_FOR_OPTIMIZATION;
+        }
+
+        bool allowsErrorCorrections(const DicNode *const dicNode) const {
+            return dicNode->getNormalizedSpatialDistance()
+                   < ScoringParamsG::NORMALIZED_SPATIAL_DISTANCE_THRESHOLD_FOR_EDIT;
+        }
+
+        bool isSkip(const DicTraverseSession *const traverseSession,
+                    const DicNode *const dicNode) const {
+            const int inputSize = traverseSession->getInputSize();
+            const int point0Index = dicNode->getInputIndex(0);
+            if(point0Index < inputSize) {
+                const float probability = traverseSession->getProximityInfoState(0)->getProbability(point0Index, NOT_AN_INDEX);
+                if(probability < ScoringParamsG::THRESHOLD_FOR_SKIP) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        bool isOmission(const DicTraverseSession *const traverseSession,
+                                        const DicNode *const dicNode, const DicNode *const childDicNode,
+                                        const bool allowsErrorCorrections) const {
+            if (!CORRECT_OMISSION) {
+                return false;
+            }
+            // Note: Always consider intentional omissions (like apostrophes) since they are common.
+            const bool canConsiderOmission = allowsErrorCorrections;
+            if (!canConsiderOmission) {
+                return false;
+            }
+            const int inputSize = traverseSession->getInputSize();
+            // TODO: Don't refer to isCompletion?
+            if (dicNode->isCompletion(inputSize)) {
+                return false;
+            }
+            if (dicNode->canBeIntentionalOmission()) {
+                return true;
+            }
+            const int point0Index = dicNode->getInputIndex(0);
+            const int currentBaseLowerCodePoint =
+                    CharUtils::toBaseLowerCase(childDicNode->getNodeCodePoint());
+            const int typedBaseLowerCodePoint =
+                    CharUtils::toBaseLowerCase(traverseSession->getProximityInfoState(0)
+                                                       ->getPrimaryCodePointAt(point0Index));
+            return (currentBaseLowerCodePoint != typedBaseLowerCodePoint);
+        }
+
+        ProximityType getProximityType(
+                const DicTraverseSession *const traverseSession, const DicNode *const dicNode,
+                const DicNode *const childDicNode) const {
+            return traverseSession->getProximityInfoState(0)->getProximityTypeG(
+                    dicNode->getInputIndex(0), childDicNode->getNodeCodePoint());
+        }
+
+        int getMaxCacheSize(const int inputSize, const float weightForLocale) const {
+            if (inputSize <= 1) {
+                return ScoringParamsG::MAX_CACHE_DIC_NODE_SIZE_FOR_SINGLE_POINT;
+            }
+            if (weightForLocale < ScoringParams::LOCALE_WEIGHT_THRESHOLD_FOR_SMALL_CACHE_SIZE) {
+                return ScoringParams::MAX_CACHE_DIC_NODE_SIZE_FOR_LOW_PROBABILITY_LOCALE;
+            }
+            return ScoringParamsG::MAX_CACHE_DIC_NODE_SIZE;
+        }
+
+        int getTerminalCacheSize() const {
+            return MAX_RESULTS;
+        }
+
+        bool isPossibleOmissionChildNode(
+                const DicTraverseSession *const traverseSession, const DicNode *const parentDicNode,
+                const DicNode *const dicNode) const {
+            const ProximityType proximityType =
+                    getProximityType(traverseSession, parentDicNode, dicNode);
+            if (!ProximityInfoUtils::isMatchOrProximityChar(proximityType)) {
+                return false;
+            }
+            return true;
+        }
+
+
+
+
+
+
+
+
+
+
+
+
+
+        AK_FORCE_INLINE bool isSpaceSubstitutionTerminal(
+                const DicTraverseSession *const traverseSession, const DicNode *const dicNode) const {
+            return false;
+        }
+
+        AK_FORCE_INLINE bool isSpaceOmissionTerminal(
+                const DicTraverseSession *const traverseSession, const DicNode *const dicNode) const {
+            return false;
+        }
+
+        AK_FORCE_INLINE bool shouldDepthLevelCache(
+                const DicTraverseSession *const traverseSession) const {
+            const int inputSize = traverseSession->getInputSize();
+            return traverseSession->isCacheBorderForTyping(inputSize);
+        }
+
+        AK_FORCE_INLINE bool shouldNodeLevelCache(
+                const DicTraverseSession *const traverseSession, const DicNode *const dicNode) const {
+            return false;
+        }
+
+        AK_FORCE_INLINE bool canDoLookAheadCorrection(
+                const DicTraverseSession *const traverseSession, const DicNode *const dicNode) const {
+            const int inputSize = traverseSession->getInputSize();
+            return dicNode->canDoLookAheadCorrection(inputSize);
+        }
+
+        AK_FORCE_INLINE bool needsToTraverseAllUserInput() const {
+            return true;
+        }
+
+
+
+
+
+
+
+        AK_FORCE_INLINE bool isGoodToTraverseNextWord(const DicNode *const dicNode,
+                                                      const int probability) const {
+            if (probability < ScoringParams::THRESHOLD_NEXT_WORD_PROBABILITY) {
+                return false;
+            }
+            const bool shortCappedWord = dicNode->getNodeCodePointCount()
+                                         < ScoringParams::THRESHOLD_SHORT_WORD_LENGTH && dicNode->isFirstCharUppercase();
+            return !shortCappedWord
+                   || probability >= ScoringParams::THRESHOLD_NEXT_WORD_PROBABILITY_FOR_CAPPED;
+        }
+
+    private:
+        DISALLOW_COPY_AND_ASSIGN(GestureTraversal);
+        static const bool CORRECT_OMISSION;
+        static const GestureTraversal sInstance;
+
+        GestureTraversal() {}
+        ~GestureTraversal() {}
+    };
+} // namespace latinime
+
+
+
+#endif //GOOGLEKEYBOARDV7_GESTURE_TRAVERSAL_H
diff --git a/native/jni/src/suggest/policyimpl/gesture/gesture_weighting.cpp b/native/jni/src/suggest/policyimpl/gesture/gesture_weighting.cpp
new file mode 100644
index 0000000..da12418
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/gesture/gesture_weighting.cpp
@@ -0,0 +1,97 @@
+//
+// Created by msj on 2017/3/9.
+//
+
+#include "suggest/policyimpl/gesture/gesture_weighting.h"
+
+#include "suggest/core/dicnode/dic_node.h"
+#include "suggest/core/layout/proximity_info.h"
+#include "suggest/policyimpl/typing/scoring_params.h"
+
+namespace latinime {
+
+    const GestureWeighting GestureWeighting::sInstance;
+
+    ErrorTypeUtils::ErrorType GestureWeighting::getErrorType(const CorrectionType correctionType,
+                                                            const DicTraverseSession *const traverseSession, const DicNode *const parentDicNode,
+                                                            const DicNode *const dicNode) const {
+        switch (correctionType) {
+            case CT_MATCH:
+                if (isProximityDicNode(traverseSession, dicNode)) {
+                    return ErrorTypeUtils::PROXIMITY_CORRECTION;
+                } else if (dicNode->isInDigraph()) {
+                    return ErrorTypeUtils::MATCH_WITH_DIGRAPH;
+                } else {
+                    // Compare the node code point with original primary code point on the keyboard.
+                    const ProximityInfoState *const pInfoState =
+                            traverseSession->getProximityInfoState(0);
+                    const int primaryCodePoint = pInfoState->getPrimaryCodePointAt(
+                            dicNode->getInputIndex(0));
+                    const int nodeCodePoint = dicNode->getNodeCodePoint();
+                    const int keyIndex = traverseSession->getProximityInfo()->getKeyIndexOf(
+                            primaryCodePoint);
+                    // TODO: Check whether the input code point is on the keyboard.
+                    if (primaryCodePoint == nodeCodePoint) {
+                        // Node code point is same as original code point on the keyboard.
+                        return ErrorTypeUtils::NOT_AN_ERROR;
+                    } else if (CharUtils::toLowerCase(primaryCodePoint) ==
+                               CharUtils::toLowerCase(nodeCodePoint)) {
+                        // Only cases of the code points are different.
+                        return ErrorTypeUtils::MATCH_WITH_WRONG_CASE;
+                    } else if (primaryCodePoint == CharUtils::toBaseCodePoint(nodeCodePoint)) {
+                        // Node code point is a variant of original code point.
+                        return ErrorTypeUtils::MATCH_WITH_MISSING_ACCENT;
+                    } else if (CharUtils::toBaseCodePoint(primaryCodePoint)
+                               == CharUtils::toBaseCodePoint(nodeCodePoint)) {
+                        // Base code points are the same but the code point is intentionally input.
+                        if (keyIndex == NOT_AN_INDEX) {
+                            return ErrorTypeUtils::MATCH_WITH_MISSING_EXPLICIT_ACCENT;
+                        }
+                        return ErrorTypeUtils::MATCH_WITH_WRONG_ACCENT;
+                    } else if (CharUtils::toLowerCase(primaryCodePoint)
+                               == CharUtils::toBaseLowerCase(nodeCodePoint)) {
+                        // Node code point is a variant of original code point and the cases are also
+                        // different.
+                        return ErrorTypeUtils::MATCH_WITH_MISSING_ACCENT
+                               | ErrorTypeUtils::MATCH_WITH_WRONG_CASE;
+                    } else {
+                        if (keyIndex == NOT_AN_INDEX) {
+                            return ErrorTypeUtils::MATCH_WITH_MISSING_EXPLICIT_ACCENT
+                                   | ErrorTypeUtils::MATCH_WITH_WRONG_CASE;
+                        }
+                        // Base code points are the same and the cases are different.
+                        return ErrorTypeUtils::MATCH_WITH_WRONG_ACCENT
+                               | ErrorTypeUtils::MATCH_WITH_WRONG_CASE;
+                    }
+                }
+                break;
+            case CT_ADDITIONAL_PROXIMITY:
+                // TODO: Change to EDIT_CORRECTION.
+                return ErrorTypeUtils::PROXIMITY_CORRECTION;
+            case CT_OMISSION:
+                if (parentDicNode->canBeIntentionalOmission()) {
+                    return ErrorTypeUtils::INTENTIONAL_OMISSION;
+                } else {
+                    return ErrorTypeUtils::EDIT_CORRECTION;
+                }
+                break;
+            case CT_SUBSTITUTION:
+                // TODO: Quit settng PROXIMITY_CORRECTION.
+                return ErrorTypeUtils::EDIT_CORRECTION | ErrorTypeUtils::PROXIMITY_CORRECTION;
+            case CT_INSERTION:
+            case CT_TERMINAL_INSERTION:
+            case CT_TRANSPOSITION:
+                return ErrorTypeUtils::EDIT_CORRECTION;
+            case CT_NEW_WORD_SPACE_OMISSION:
+            case CT_NEW_WORD_SPACE_SUBSTITUTION:
+                return ErrorTypeUtils::NEW_WORD;
+            case CT_TERMINAL:
+                return ErrorTypeUtils::NOT_AN_ERROR;
+            case CT_COMPLETION:
+                return ErrorTypeUtils::COMPLETION;
+            default:
+                return ErrorTypeUtils::NOT_AN_ERROR;
+        }
+    }
+}  // namespace latinime
+
diff --git a/native/jni/src/suggest/policyimpl/gesture/gesture_weighting.h b/native/jni/src/suggest/policyimpl/gesture/gesture_weighting.h
new file mode 100644
index 0000000..869acae
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/gesture/gesture_weighting.h
@@ -0,0 +1,194 @@
+//
+// Created by msj on 2017/3/9.
+//
+
+#ifndef GOOGLEKEYBOARDV7_GESTURE_WEIGHTING_H
+#define GOOGLEKEYBOARDV7_GESTURE_WEIGHTING_H
+
+
+
+#include "defines.h"
+#include "suggest/core/dicnode/dic_node_utils.h"
+#include "suggest/core/dictionary/error_type_utils.h"
+#include "suggest/core/layout/touch_position_correction_utils.h"
+#include "suggest/core/layout/proximity_info.h"
+#include "suggest/core/policy/weighting.h"
+#include "suggest/core/session/dic_traverse_session.h"
+#include "suggest/policyimpl/typing/scoring_params.h"
+#include "suggest/policyimpl/gesture/scoring_params_g.h"
+#include "utils/char_utils.h"
+
+namespace latinime {
+
+    class DicNode;
+    struct DicNode_InputStateG;
+    class MultiBigramMap;
+
+    class GestureWeighting : public Weighting {
+    public:
+        static const GestureWeighting *getInstance() { return &sInstance; }
+
+    protected:
+        float getTerminalSpatialCost(const DicTraverseSession *const traverseSession,
+                                     const DicNode *const dicNode) const {
+            const int point0index = dicNode->getInputIndex(0);
+            const int tmpInputSize = traverseSession->getInputSize();
+            float cost = 0;
+            for (int i = point0index; i < tmpInputSize; i++) {
+                cost += traverseSession->getProximityInfoState(0)->getProbability(i, NOT_AN_INDEX);
+            }
+            if (point0index > tmpInputSize) {
+                cost += (point0index - tmpInputSize) * ScoringParamsG::DISTANCE_WEIGHT_EXCEEDING_INPUT_SIZE;
+            }
+
+            return cost;
+        }
+
+        float getTerminalLanguageCost(const DicTraverseSession *const traverseSession,
+                                      const DicNode *const dicNode, const float dicNodeLanguageImprobability) const {
+            return dicNodeLanguageImprobability * ScoringParamsG::DISTANCE_WEIGHT_LANGUAGE;
+        }
+
+        float getCompletionCost(const DicTraverseSession *const traverseSession,
+                                const DicNode *const dicNode) const {
+            // The auto completion starts when the input index is same as the input size
+            const bool firstCompletion = dicNode->getInputIndex(0)
+                                         == traverseSession->getInputSize();
+            // TODO: Change the cost for the first completion for the gesture?
+            const float cost = firstCompletion ? ScoringParamsG::COST_FIRST_COMPLETION
+                                               : ScoringParamsG::COST_COMPLETION;
+            return cost;
+        }
+
+        float getSkipCost(const DicTraverseSession *const traverseSession,
+                          const DicNode *const dicNode) const {
+            const int pointIndex = dicNode->getInputIndex(0);
+
+            float probability = traverseSession->getProximityInfoState(0)->getProbability(pointIndex, NOT_AN_INDEX);
+
+            return probability;
+        }
+
+        float getOmissionCost(const DicNode *const parentDicNode, const DicNode *const dicNode) const {
+            return (dicNode->isSameNodeCodePoint(parentDicNode))
+                   ? ScoringParamsG::OMISSION_COST_SAME_CHAR
+                   : ScoringParamsG::OMISSION_COST;
+        }
+
+        float getMatchedCost(const DicTraverseSession *const traverseSession,
+                             const DicNode *const dicNode, DicNode_InputStateG *inputStateG) const {
+            const int pointIndex = dicNode->getInputIndex(0);
+            const int baseChar = CharUtils::toBaseLowerCase(dicNode->getNodeCodePoint());
+            const int keyId = traverseSession->getProximityInfo()->getKeyIndexOf(baseChar);
+            const float probability = traverseSession-> getProximityInfoState(0)->getProbability(pointIndex, keyId);
+
+            return probability;
+        }
+
+        bool isProximityDicNode(const DicTraverseSession *const traverseSession,
+                                const DicNode *const dicNode) const {
+            const int pointIndex = dicNode->getInputIndex(0);
+            const int primaryCodePoint = CharUtils::toBaseLowerCase(
+                    traverseSession->getProximityInfoState(0)->getPrimaryCodePointAt(pointIndex));
+            const int dicNodeChar = CharUtils::toBaseLowerCase(dicNode->getNodeCodePoint());
+            return primaryCodePoint != dicNodeChar;
+        }
+
+        float getTranspositionCost(const DicTraverseSession *const traverseSession,
+                                   const DicNode *const parentDicNode, const DicNode *const dicNode) const {
+            const int16_t parentPointIndex = parentDicNode->getInputIndex(0);
+            const int prevCodePoint = parentDicNode->getNodeCodePoint();
+            const float distance1 = traverseSession->getProximityInfoState(0)->getPointToKeyLength(
+                    parentPointIndex + 1, CharUtils::toBaseLowerCase(prevCodePoint));
+            const int codePoint = dicNode->getNodeCodePoint();
+            const float distance2 = traverseSession->getProximityInfoState(0)->getPointToKeyLength(
+                    parentPointIndex, CharUtils::toBaseLowerCase(codePoint));
+            const float distance = distance1 + distance2;
+            const float weightedLengthDistance =
+                    distance * ScoringParams::DISTANCE_WEIGHT_LENGTH;
+            return ScoringParams::TRANSPOSITION_COST + weightedLengthDistance;
+        }
+
+        float getInsertionCost(const DicTraverseSession *const traverseSession,
+                               const DicNode *const parentDicNode, const DicNode *const dicNode) const {
+            const int16_t insertedPointIndex = parentDicNode->getInputIndex(0);
+            const int prevCodePoint = traverseSession->getProximityInfoState(0)->getPrimaryCodePointAt(
+                    insertedPointIndex);
+            const int currentCodePoint = dicNode->getNodeCodePoint();
+            const bool sameCodePoint = prevCodePoint == currentCodePoint;
+            const bool existsAdjacentProximityChars = traverseSession->getProximityInfoState(0)
+                    ->existsAdjacentProximityChars(insertedPointIndex);
+            const float dist = traverseSession->getProximityInfoState(0)->getPointToKeyLength(
+                    insertedPointIndex + 1, CharUtils::toBaseLowerCase(dicNode->getNodeCodePoint()));
+            const float weightedDistance = dist * ScoringParams::DISTANCE_WEIGHT_LENGTH;
+            const bool singleChar = dicNode->getNodeCodePointCount() == 1;
+            float cost = (singleChar ? ScoringParams::INSERTION_COST_FIRST_CHAR : 0.0f);
+            if (sameCodePoint) {
+                cost += ScoringParams::INSERTION_COST_SAME_CHAR;
+            } else if (existsAdjacentProximityChars) {
+                cost += ScoringParams::INSERTION_COST_PROXIMITY_CHAR;
+            } else {
+                cost += ScoringParams::INSERTION_COST;
+            }
+            return cost + weightedDistance;
+        }
+
+        float getSpaceOmissionCost(const DicTraverseSession *const traverseSession,
+                                   const DicNode *const dicNode, DicNode_InputStateG *inputStateG) const {
+            const float cost = ScoringParams::SPACE_OMISSION_COST;
+            return cost * traverseSession->getMultiWordCostMultiplier();
+        }
+
+        float getNewWordBigramLanguageCost(const DicTraverseSession *const traverseSession,
+                                           const DicNode *const dicNode,
+                                           MultiBigramMap *const multiBigramMap) const {
+            return DicNodeUtils::getBigramNodeImprobability(
+                    traverseSession->getDictionaryStructurePolicy(),
+                    dicNode, multiBigramMap) * ScoringParams::DISTANCE_WEIGHT_LANGUAGE;
+        }
+
+        float getTerminalInsertionCost(const DicTraverseSession *const traverseSession,
+                                       const DicNode *const dicNode) const {
+            const int inputIndex = dicNode->getInputIndex(0);
+            const int inputSize = traverseSession->getInputSize();
+            ASSERT(inputIndex < inputSize);
+            // TODO: Implement more efficient logic
+            return  ScoringParams::TERMINAL_INSERTION_COST * (inputSize - inputIndex);
+        }
+
+        AK_FORCE_INLINE bool needsToNormalizeCompoundDistance() const {
+            return false;
+        }
+
+        AK_FORCE_INLINE float getAdditionalProximityCost() const {
+            return ScoringParams::ADDITIONAL_PROXIMITY_COST;
+        }
+
+        AK_FORCE_INLINE float getSubstitutionCost() const {
+            return ScoringParams::SUBSTITUTION_COST;
+        }
+
+        AK_FORCE_INLINE float getSpaceSubstitutionCost(const DicTraverseSession *const traverseSession,
+                                                       const DicNode *const dicNode) const {
+            const int inputIndex = dicNode->getInputIndex(0);
+            const float distanceToSpaceKey = traverseSession->getProximityInfoState(0)
+                    ->getPointToKeyLength(inputIndex, KEYCODE_SPACE);
+            const float cost = ScoringParams::SPACE_SUBSTITUTION_COST * distanceToSpaceKey;
+            return cost * traverseSession->getMultiWordCostMultiplier();
+        }
+
+        ErrorTypeUtils::ErrorType getErrorType(const CorrectionType correctionType,
+                                               const DicTraverseSession *const traverseSession,
+                                               const DicNode *const parentDicNode, const DicNode *const dicNode) const;
+
+    private:
+        DISALLOW_COPY_AND_ASSIGN(GestureWeighting);
+        static const GestureWeighting sInstance;
+
+        GestureWeighting() {}
+        ~GestureWeighting() {}
+    };
+} // namespace latinime
+
+
+#endif //GOOGLEKEYBOARDV7_GESTURE_WEIGHTING_H
diff --git a/native/jni/src/suggest/policyimpl/gesture/scoring_params_g.cpp b/native/jni/src/suggest/policyimpl/gesture/scoring_params_g.cpp
new file mode 100644
index 0000000..28712cc
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/gesture/scoring_params_g.cpp
@@ -0,0 +1,30 @@
+//
+// Created by msj on 2017/3/14.
+//
+
+#include "suggest/policyimpl/gesture/scoring_params_g.h"
+
+namespace latinime {
+    const float ScoringParamsG::COST_FIRST_COMPLETION = 1.0f;
+    const float ScoringParamsG::COST_COMPLETION = 0.5f;
+    const float ScoringParamsG::NORMALIZED_SPATIAL_DISTANCE_THRESHOLD_FOR_EDIT = 0.5f;
+
+    const float ScoringParamsG::OMISSION_COST_SAME_CHAR = 0.2f;
+    const float ScoringParamsG::OMISSION_COST = 1.5f;
+
+    // 候选项中超出采样长度的长度的惩罚权重
+    const float ScoringParamsG::DISTANCE_WEIGHT_EXCEEDING_INPUT_SIZE = 1.0f;
+
+
+
+    // 这个是 词频补 的权重,和距离信息的负对数组成最终的得分
+    const float ScoringParamsG::DISTANCE_WEIGHT_LANGUAGE = 4.0f;
+
+    // 优先队列的长度,影响遍历词典的 检全率
+    const int ScoringParamsG::MAX_CACHE_DIC_NODE_SIZE = 80;
+    const int ScoringParamsG::MAX_CACHE_DIC_NODE_SIZE_FOR_SINGLE_POINT = 310;
+
+    // 该点是否跳过的阈值,含义为概率的负对数
+    const float ScoringParamsG::THRESHOLD_FOR_SKIP = 1.5f;
+} // namespace latinime
+
diff --git a/native/jni/src/suggest/policyimpl/gesture/scoring_params_g.h b/native/jni/src/suggest/policyimpl/gesture/scoring_params_g.h
new file mode 100644
index 0000000..d681fba
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/gesture/scoring_params_g.h
@@ -0,0 +1,35 @@
+//
+// Created by msj on 2017/3/14.
+//
+
+#ifndef GOOGLEKEYBOARDV7_SCORING_PARAMS_G_H
+#define GOOGLEKEYBOARDV7_SCORING_PARAMS_G_H
+
+#include "defines.h"
+
+namespace latinime {
+    class ScoringParamsG {
+    public:
+        static const float COST_FIRST_COMPLETION;
+        static const float COST_COMPLETION;
+        static const float NORMALIZED_SPATIAL_DISTANCE_THRESHOLD_FOR_EDIT;
+
+        static const float OMISSION_COST_SAME_CHAR;
+        static const float OMISSION_COST;
+
+        static const float DISTANCE_WEIGHT_EXCEEDING_INPUT_SIZE;
+
+        static const float DISTANCE_WEIGHT_LANGUAGE;
+
+        static const int MAX_CACHE_DIC_NODE_SIZE;
+        static const int MAX_CACHE_DIC_NODE_SIZE_FOR_SINGLE_POINT;
+
+        static const float THRESHOLD_FOR_SKIP;
+    private:
+        DISALLOW_IMPLICIT_CONSTRUCTORS(ScoringParamsG);
+    };
+} // namespace latinime
+
+
+
+#endif //GOOGLEKEYBOARDV7_SCORING_PARAMS_G_H
diff --git a/native/jni/src/suggest/policyimpl/typing/typing_traversal.h b/native/jni/src/suggest/policyimpl/typing/typing_traversal.h
index b9b6314..0d381db 100644
--- a/native/jni/src/suggest/policyimpl/typing/typing_traversal.h
+++ b/native/jni/src/suggest/policyimpl/typing/typing_traversal.h
@@ -73,6 +73,11 @@
         return (currentBaseLowerCodePoint != typedBaseLowerCodePoint);
     }
 
+    AK_FORCE_INLINE bool isSkip(const DicTraverseSession *const traverseSession,
+                const DicNode *const dicNode) const {
+        return false;
+    }
+
     AK_FORCE_INLINE bool isSpaceSubstitutionTerminal(
             const DicTraverseSession *const traverseSession, const DicNode *const dicNode) const {
         if (!CORRECT_NEW_WORD_SPACE_SUBSTITUTION) {
diff --git a/native/jni/src/suggest/policyimpl/typing/typing_weighting.h b/native/jni/src/suggest/policyimpl/typing/typing_weighting.h
index 1338ac8..ff402b2 100644
--- a/native/jni/src/suggest/policyimpl/typing/typing_weighting.h
+++ b/native/jni/src/suggest/policyimpl/typing/typing_weighting.h
@@ -52,6 +52,11 @@
         return cost;
     }
 
+    float getSkipCost(const DicTraverseSession *const traverseSession,
+                      const DicNode *const dicNode) const {
+        return 0;
+    }
+
     float getOmissionCost(const DicNode *const parentDicNode, const DicNode *const dicNode) const {
         const bool isZeroCostOmission = parentDicNode->isZeroCostOmission();
         const bool isIntentionalOmission = parentDicNode->canBeIntentionalOmission();