Merge "fix findWordInTree." into jb-mr1-dev
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index f0f5cd3..cdaffa6 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -18,6 +18,7 @@
 
 import android.content.Context;
 import android.text.TextUtils;
+import android.util.SparseArray;
 
 import com.android.inputmethod.keyboard.ProximityInfo;
 import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
@@ -51,6 +52,7 @@
     private static final int TYPED_LETTER_MULTIPLIER = 2;
 
     private long mNativeDict;
+    private final Locale mLocale;
     private final int[] mInputCodePoints = new int[MAX_WORD_LENGTH];
     // TODO: The below should be int[] mOutputCodePoints
     private final char[] mOutputChars = new char[MAX_WORD_LENGTH * MAX_RESULTS];
@@ -59,7 +61,22 @@
     private final int[] mOutputTypes = new int[MAX_RESULTS];
 
     private final boolean mUseFullEditDistance;
-    private final DicTraverseSession mDicTraverseSession;
+
+    private final SparseArray<DicTraverseSession> mDicTraverseSessions =
+            new SparseArray<DicTraverseSession>();
+    private DicTraverseSession getTraverseSession(int traverseSessionId) {
+        DicTraverseSession traverseSession = mDicTraverseSessions.get(traverseSessionId);
+        if (traverseSession == null) {
+            synchronized(mDicTraverseSessions) {
+                traverseSession = mDicTraverseSessions.get(traverseSessionId);
+                if (traverseSession == null) {
+                    traverseSession = new DicTraverseSession(mLocale, mNativeDict);
+                    mDicTraverseSessions.put(traverseSessionId, traverseSession);
+                }
+            }
+        }
+        return traverseSession;
+    }
 
     /**
      * Constructor for the binary dictionary. This is supposed to be called from the
@@ -76,10 +93,9 @@
             final String filename, final long offset, final long length,
             final boolean useFullEditDistance, final Locale locale, final String dictType) {
         super(dictType);
+        mLocale = locale;
         mUseFullEditDistance = useFullEditDistance;
         loadDictionary(filename, offset, length);
-        mDicTraverseSession = new DicTraverseSession(locale);
-        mDicTraverseSession.initSession(mNativeDict);
     }
 
     static {
@@ -109,7 +125,14 @@
     @Override
     public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
             final CharSequence prevWord, final ProximityInfo proximityInfo) {
+        return getSuggestionsWithSessionId(composer, prevWord, proximityInfo, 0);
+    }
+
+    @Override
+    public ArrayList<SuggestedWordInfo> getSuggestionsWithSessionId(final WordComposer composer,
+            final CharSequence prevWord, final ProximityInfo proximityInfo, int sessionId) {
         if (!isValidDictionary()) return null;
+
         Arrays.fill(mInputCodePoints, WordComposer.NOT_A_CODE);
         // TODO: toLowerCase in the native code
         final int[] prevWordCodePointArray = (null == prevWord)
@@ -128,7 +151,7 @@
         final int codesSize = isGesture ? ips.getPointerSize() : composerSize;
         // proximityInfo and/or prevWordForBigrams may not be null.
         final int tmpCount = getSuggestionsNative(mNativeDict,
-                proximityInfo.getNativeProximityInfo(), mDicTraverseSession.getSession(),
+                proximityInfo.getNativeProximityInfo(), getTraverseSession(sessionId).getSession(),
                 ips.getXCoordinates(), ips.getYCoordinates(), ips.getTimes(), ips.getPointerIds(),
                 mInputCodePoints, codesSize, 0 /* commitPoint */, isGesture, prevWordCodePointArray,
                 mUseFullEditDistance, mOutputChars, mOutputScores, mSpaceIndices, mOutputTypes);
@@ -187,7 +210,13 @@
 
     @Override
     public synchronized void close() {
-        mDicTraverseSession.close();
+        for (int i = 0; i < mDicTraverseSessions.size(); ++i) {
+            final int key = mDicTraverseSessions.keyAt(i);
+            final DicTraverseSession traverseSession = mDicTraverseSessions.get(key);
+            if (traverseSession != null) {
+                traverseSession.close();
+            }
+        }
         closeInternal();
     }
 
diff --git a/java/src/com/android/inputmethod/latin/DicTraverseSession.java b/java/src/com/android/inputmethod/latin/DicTraverseSession.java
index c768153..359da72 100644
--- a/java/src/com/android/inputmethod/latin/DicTraverseSession.java
+++ b/java/src/com/android/inputmethod/latin/DicTraverseSession.java
@@ -22,6 +22,7 @@
     static {
         JniUtils.loadNativeLibrary();
     }
+
     private native long setDicTraverseSessionNative(String locale);
     private native void initDicTraverseSessionNative(long nativeDicTraverseSession,
             long dictionary, int[] previousWord, int previousWordLength);
@@ -29,9 +30,10 @@
 
     private long mNativeDicTraverseSession;
 
-    public DicTraverseSession(Locale locale) {
+    public DicTraverseSession(Locale locale, long dictionary) {
         mNativeDicTraverseSession = createNativeDicTraverseSession(
                 locale != null ? locale.toString() : "");
+        initSession(dictionary);
     }
 
     public long getSession() {
diff --git a/java/src/com/android/inputmethod/latin/Dictionary.java b/java/src/com/android/inputmethod/latin/Dictionary.java
index 60fe17b..2565dfc 100644
--- a/java/src/com/android/inputmethod/latin/Dictionary.java
+++ b/java/src/com/android/inputmethod/latin/Dictionary.java
@@ -62,6 +62,13 @@
     abstract public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
             final CharSequence prevWord, final ProximityInfo proximityInfo);
 
+    // The default implementation of this method ignores sessionId.
+    // Subclasses that want to use sessionId need to override this method.
+    public ArrayList<SuggestedWordInfo> getSuggestionsWithSessionId(final WordComposer composer,
+            final CharSequence prevWord, final ProximityInfo proximityInfo, int sessionId) {
+        return getSuggestions(composer, prevWord, proximityInfo);
+    }
+
     /**
      * Checks if the given word occurs in the dictionary
      * @param word the word to search for. The search should be case-insensitive.
diff --git a/java/src/com/android/inputmethod/latin/InputAttributes.java b/java/src/com/android/inputmethod/latin/InputAttributes.java
index e561f59..7bcda9b 100644
--- a/java/src/com/android/inputmethod/latin/InputAttributes.java
+++ b/java/src/com/android/inputmethod/latin/InputAttributes.java
@@ -29,10 +29,12 @@
     final public boolean mInputTypeNoAutoCorrect;
     final public boolean mIsSettingsSuggestionStripOn;
     final public boolean mApplicationSpecifiedCompletionOn;
+    final private int mInputType;
 
     public InputAttributes(final EditorInfo editorInfo, final boolean isFullscreenMode) {
         final int inputType = null != editorInfo ? editorInfo.inputType : 0;
         final int inputClass = inputType & InputType.TYPE_MASK_CLASS;
+        mInputType = inputType;
         if (inputClass != InputType.TYPE_CLASS_TEXT) {
             // If we are not looking at a TYPE_CLASS_TEXT field, the following strange
             // cases may arise, so we do a couple sanity checks for them. If it's a
@@ -93,6 +95,10 @@
         }
     }
 
+    public boolean isSameInputType(final EditorInfo editorInfo) {
+        return editorInfo.inputType == mInputType;
+    }
+
     @SuppressWarnings("unused")
     private void dumpFlags(final int inputType) {
         Log.i(TAG, "Input class:");
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 446d44e..884e6db 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -670,7 +670,11 @@
             accessUtils.onStartInputViewInternal(mainKeyboardView, editorInfo, restarting);
         }
 
-        if (!restarting) {
+        final boolean selectionChanged = mLastSelectionStart != editorInfo.initialSelStart
+                || mLastSelectionEnd != editorInfo.initialSelEnd;
+        final boolean inputTypeChanged = !mCurrentSettings.isSameInputType(editorInfo);
+        final boolean isDifferentTextField = !restarting || inputTypeChanged;
+        if (isDifferentTextField) {
             mSubtypeSwitcher.updateParametersOnStartInputView();
         }
 
@@ -679,9 +683,7 @@
         updateFullscreenMode();
         mApplicationSpecifiedCompletions = null;
 
-        final boolean selectionChanged = mLastSelectionStart != editorInfo.initialSelStart
-                || mLastSelectionEnd != editorInfo.initialSelEnd;
-        if (!restarting || selectionChanged) {
+        if (isDifferentTextField || selectionChanged) {
             // If the selection changed, we reset the input state. Essentially, we come here with
             // restarting == true when the app called setText() or similar. We should reset the
             // state if the app set the text to something else, but keep it if it set a suggestion
@@ -696,7 +698,7 @@
             }
         }
 
-        if (!restarting) {
+        if (isDifferentTextField) {
             mainKeyboardView.closing();
             loadSettings();
 
diff --git a/java/src/com/android/inputmethod/latin/SettingsValues.java b/java/src/com/android/inputmethod/latin/SettingsValues.java
index 0843bdb..c8755be 100644
--- a/java/src/com/android/inputmethod/latin/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/SettingsValues.java
@@ -417,6 +417,10 @@
         prefs.edit().putString(Settings.PREF_LAST_USER_DICTIONARY_WRITE_TIME, newStr).apply();
     }
 
+    public boolean isSameInputType(final EditorInfo editorInfo) {
+        return mInputAttributes.isSameInputType(editorInfo);
+    }
+
     // For debug.
     public String getInputAttributesDebugString() {
         return mInputAttributes.toString();
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index 8a2341d..1f43c6d 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -169,9 +169,17 @@
     public SuggestedWords getSuggestedWords(
             final WordComposer wordComposer, CharSequence prevWordForBigram,
             final ProximityInfo proximityInfo, final boolean isCorrectionEnabled) {
+        return getSuggestedWordsWithSessionId(
+                wordComposer, prevWordForBigram, proximityInfo, isCorrectionEnabled, 0);
+    }
+
+    public SuggestedWords getSuggestedWordsWithSessionId(
+            final WordComposer wordComposer, CharSequence prevWordForBigram,
+            final ProximityInfo proximityInfo, final boolean isCorrectionEnabled, int sessionId) {
         LatinImeLogger.onStartSuggestion(prevWordForBigram);
         if (wordComposer.isBatchMode()) {
-            return getSuggestedWordsForBatchInput(wordComposer, prevWordForBigram, proximityInfo);
+            return getSuggestedWordsForBatchInput(
+                    wordComposer, prevWordForBigram, proximityInfo, sessionId);
         } else {
             return getSuggestedWordsForTypingInput(wordComposer, prevWordForBigram, proximityInfo,
                     isCorrectionEnabled);
@@ -306,7 +314,7 @@
     // Retrieves suggestions for the batch input.
     private SuggestedWords getSuggestedWordsForBatchInput(
             final WordComposer wordComposer, CharSequence prevWordForBigram,
-            final ProximityInfo proximityInfo) {
+            final ProximityInfo proximityInfo, int sessionId) {
         final BoundedTreeSet suggestionsSet = new BoundedTreeSet(sSuggestedWordInfoComparator,
                 MAX_SUGGESTIONS);
 
@@ -318,8 +326,8 @@
                 continue;
             }
             final Dictionary dictionary = mDictionaries.get(key);
-            suggestionsSet.addAll(dictionary.getSuggestions(
-                    wordComposer, prevWordForBigram, proximityInfo));
+            suggestionsSet.addAll(dictionary.getSuggestionsWithSessionId(
+                    wordComposer, prevWordForBigram, proximityInfo, sessionId));
         }
 
         final ArrayList<SuggestedWordInfo> suggestionsContainer =
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
index 06f5db7..317fe7c 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
@@ -194,7 +194,7 @@
                 DictAndProximity dictInfo = null;
                 try {
                     dictInfo = mDictionaryPool.pollWithDefaultTimeout();
-                    if (null == dictInfo) {
+                    if (!DictionaryPool.isAValidDictionary(dictInfo)) {
                         return AndroidSpellCheckerService.getNotInDictEmptySuggestions();
                     }
                     return dictInfo.mDictionary.isValidWord(inText)
@@ -237,7 +237,7 @@
             DictAndProximity dictInfo = null;
             try {
                 dictInfo = mDictionaryPool.pollWithDefaultTimeout();
-                if (null == dictInfo) {
+                if (!DictionaryPool.isAValidDictionary(dictInfo)) {
                     return AndroidSpellCheckerService.getNotInDictEmptySuggestions();
                 }
                 final ArrayList<SuggestedWordInfo> suggestions =
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java
index 83f82fa..218eab7 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java
@@ -18,6 +18,12 @@
 
 import android.util.Log;
 
+import com.android.inputmethod.keyboard.ProximityInfo;
+import com.android.inputmethod.latin.Dictionary;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+import com.android.inputmethod.latin.WordComposer;
+
+import java.util.ArrayList;
 import java.util.Locale;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
@@ -39,6 +45,26 @@
     private final Locale mLocale;
     private int mSize;
     private volatile boolean mClosed;
+    final static ArrayList<SuggestedWordInfo> noSuggestions = new ArrayList<SuggestedWordInfo>();
+    private final static DictAndProximity dummyDict = new DictAndProximity(
+            new Dictionary(Dictionary.TYPE_MAIN) {
+                @Override
+                public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
+                        final CharSequence prevWord, final ProximityInfo proximityInfo) {
+                    return noSuggestions;
+                }
+                @Override
+                public boolean isValidWord(CharSequence word) {
+                    // This is never called. However if for some strange reason it ever gets
+                    // called, returning true is less destructive (it will not underline the
+                    // word in red).
+                    return true;
+                }
+            }, null);
+
+    static public boolean isAValidDictionary(final DictAndProximity dictInfo) {
+        return null != dictInfo && dummyDict != dictInfo;
+    }
 
     public DictionaryPool(final int maxSize, final AndroidSpellCheckerService service,
             final Locale locale) {
@@ -98,7 +124,7 @@
     public boolean offer(final DictAndProximity dict) {
         if (mClosed) {
             dict.mDictionary.close();
-            return false;
+            return super.offer(dummyDict);
         } else {
             return super.offer(dict);
         }
diff --git a/native/jni/src/correction.cpp b/native/jni/src/correction.cpp
index e55da01..7513b30 100644
--- a/native/jni/src/correction.cpp
+++ b/native/jni/src/correction.cpp
@@ -230,7 +230,7 @@
 }
 
 // TODO: remove
-int Correction::getInputIndex() {
+int Correction::getInputIndex() const {
     return mInputIndex;
 }
 
diff --git a/native/jni/src/correction.h b/native/jni/src/correction.h
index 57e7b71..9899f6e 100644
--- a/native/jni/src/correction.h
+++ b/native/jni/src/correction.h
@@ -38,63 +38,8 @@
         NOT_ON_TERMINAL
     } CorrectionType;
 
-    /////////////////////////
-    // static inline utils //
-    /////////////////////////
-
-    static const int TWO_31ST_DIV_255 = S_INT_MAX / 255;
-    static inline int capped255MultForFullMatchAccentsOrCapitalizationDifference(const int num) {
-        return (num < TWO_31ST_DIV_255 ? 255 * num : S_INT_MAX);
-    }
-
-    static const int TWO_31ST_DIV_2 = S_INT_MAX / 2;
-    inline static void multiplyIntCapped(const int multiplier, int *base) {
-        const int temp = *base;
-        if (temp != S_INT_MAX) {
-            // Branch if multiplier == 2 for the optimization
-            if (multiplier < 0) {
-                if (DEBUG_DICT) {
-                    assert(false);
-                }
-                AKLOGI("--- Invalid multiplier: %d", multiplier);
-            } else if (multiplier == 0) {
-                *base = 0;
-            } else if (multiplier == 2) {
-                *base = TWO_31ST_DIV_2 >= temp ? temp << 1 : S_INT_MAX;
-            } else {
-                // TODO: This overflow check gives a wrong answer when, for example,
-                //       temp = 2^16 + 1 and multiplier = 2^17 + 1.
-                //       Fix this behavior.
-                const int tempRetval = temp * multiplier;
-                *base = tempRetval >= temp ? tempRetval : S_INT_MAX;
-            }
-        }
-    }
-
-    inline static int powerIntCapped(const int base, const int n) {
-        if (n <= 0) return 1;
-        if (base == 2) {
-            return n < 31 ? 1 << n : S_INT_MAX;
-        } else {
-            int ret = base;
-            for (int i = 1; i < n; ++i) multiplyIntCapped(base, &ret);
-            return ret;
-        }
-    }
-
-    inline static void multiplyRate(const int rate, int *freq) {
-        if (*freq != S_INT_MAX) {
-            if (*freq > 1000000) {
-                *freq /= 100;
-                multiplyIntCapped(rate, freq);
-            } else {
-                multiplyIntCapped(rate, freq);
-                *freq /= 100;
-            }
-        }
-    }
-
     Correction() {};
+    virtual ~Correction();
     void resetCorrection();
     void initCorrection(
             const ProximityInfo *pi, const int inputLength, const int maxWordLength);
@@ -108,27 +53,7 @@
     bool sameAsTyped();
     bool initProcessState(const int index);
 
-    int getInputIndex();
-
-    virtual ~Correction();
-    int getSpaceProximityPos() const {
-        return mSpaceProximityPos;
-    }
-    int getMissingSpacePos() const {
-        return mMissingSpacePos;
-    }
-
-    int getSkipPos() const {
-        return mSkipPos;
-    }
-
-    int getExcessivePos() const {
-        return mExcessivePos;
-    }
-
-    int getTransposedPos() const {
-        return mTransposedPos;
-    }
+    int getInputIndex() const;
 
     bool needsToPrune() const;
 
@@ -195,6 +120,81 @@
 
  private:
     DISALLOW_COPY_AND_ASSIGN(Correction);
+
+    /////////////////////////
+    // static inline utils //
+    /////////////////////////
+    static const int TWO_31ST_DIV_255 = S_INT_MAX / 255;
+    static inline int capped255MultForFullMatchAccentsOrCapitalizationDifference(const int num) {
+        return (num < TWO_31ST_DIV_255 ? 255 * num : S_INT_MAX);
+    }
+
+    static const int TWO_31ST_DIV_2 = S_INT_MAX / 2;
+    inline static void multiplyIntCapped(const int multiplier, int *base) {
+        const int temp = *base;
+        if (temp != S_INT_MAX) {
+            // Branch if multiplier == 2 for the optimization
+            if (multiplier < 0) {
+                if (DEBUG_DICT) {
+                    assert(false);
+                }
+                AKLOGI("--- Invalid multiplier: %d", multiplier);
+            } else if (multiplier == 0) {
+                *base = 0;
+            } else if (multiplier == 2) {
+                *base = TWO_31ST_DIV_2 >= temp ? temp << 1 : S_INT_MAX;
+            } else {
+                // TODO: This overflow check gives a wrong answer when, for example,
+                //       temp = 2^16 + 1 and multiplier = 2^17 + 1.
+                //       Fix this behavior.
+                const int tempRetval = temp * multiplier;
+                *base = tempRetval >= temp ? tempRetval : S_INT_MAX;
+            }
+        }
+    }
+
+    inline static int powerIntCapped(const int base, const int n) {
+        if (n <= 0) return 1;
+        if (base == 2) {
+            return n < 31 ? 1 << n : S_INT_MAX;
+        } else {
+            int ret = base;
+            for (int i = 1; i < n; ++i) multiplyIntCapped(base, &ret);
+            return ret;
+        }
+    }
+
+    inline static void multiplyRate(const int rate, int *freq) {
+        if (*freq != S_INT_MAX) {
+            if (*freq > 1000000) {
+                *freq /= 100;
+                multiplyIntCapped(rate, freq);
+            } else {
+                multiplyIntCapped(rate, freq);
+                *freq /= 100;
+            }
+        }
+    }
+
+    inline int getSpaceProximityPos() const {
+        return mSpaceProximityPos;
+    }
+    inline int getMissingSpacePos() const {
+        return mMissingSpacePos;
+    }
+
+    inline int getSkipPos() const {
+        return mSkipPos;
+    }
+
+    inline int getExcessivePos() const {
+        return mExcessivePos;
+    }
+
+    inline int getTransposedPos() const {
+        return mTransposedPos;
+    }
+
     inline void incrementInputIndex();
     inline void incrementOutputIndex();
     inline void startToTraverseAllNodes();
diff --git a/native/jni/src/dic_traverse_wrapper.cpp b/native/jni/src/dic_traverse_wrapper.cpp
index 1f7dcbf..88ca9fa 100644
--- a/native/jni/src/dic_traverse_wrapper.cpp
+++ b/native/jni/src/dic_traverse_wrapper.cpp
@@ -19,8 +19,8 @@
 #include "dic_traverse_wrapper.h"
 
 namespace latinime {
-void *(*DicTraverseWrapper::sDicTraverseSessionFactoryMethod)(JNIEnv *env, jstring locale) = 0;
+void *(*DicTraverseWrapper::sDicTraverseSessionFactoryMethod)(JNIEnv *, jstring) = 0;
 void (*DicTraverseWrapper::sDicTraverseSessionReleaseMethod)(void *) = 0;
 void (*DicTraverseWrapper::sDicTraverseSessionInitMethod)(
-        void *, Dictionary *, const int *, const int) = 0;
+        void *, const Dictionary *const, const int *, const int) = 0;
 } // namespace latinime
diff --git a/native/jni/src/dic_traverse_wrapper.h b/native/jni/src/dic_traverse_wrapper.h
index 8396d00..2923824 100644
--- a/native/jni/src/dic_traverse_wrapper.h
+++ b/native/jni/src/dic_traverse_wrapper.h
@@ -34,7 +34,7 @@
         return 0;
     }
     static void initDicTraverseSession(void *traverseSession,
-            Dictionary *dictionary, const int *prevWord, const int prevWordLength) {
+            const Dictionary *const dictionary, const int *prevWord, const int prevWordLength) {
         if (sDicTraverseSessionInitMethod) {
             sDicTraverseSessionInitMethod(traverseSession, dictionary, prevWord, prevWordLength);
         }
@@ -45,11 +45,11 @@
         }
     }
     static void setTraverseSessionFactoryMethod(
-            void *(*factoryMethod)(JNIEnv *env, jstring locale)) {
+            void *(*factoryMethod)(JNIEnv *, jstring)) {
         sDicTraverseSessionFactoryMethod = factoryMethod;
     }
     static void setTraverseSessionInitMethod(
-            void (*initMethod)(void *, Dictionary *, const int *, const int)) {
+            void (*initMethod)(void *, const Dictionary *const, const int *, const int)) {
         sDicTraverseSessionInitMethod = initMethod;
     }
     static void setTraverseSessionReleaseMethod(void (*releaseMethod)(void *)) {
@@ -58,7 +58,8 @@
  private:
     DISALLOW_IMPLICIT_CONSTRUCTORS(DicTraverseWrapper);
     static void *(*sDicTraverseSessionFactoryMethod)(JNIEnv *, jstring);
-    static void (*sDicTraverseSessionInitMethod)(void *, Dictionary *, const int *, const int);
+    static void (*sDicTraverseSessionInitMethod)(
+            void *, const Dictionary *const, const int *, const int);
     static void (*sDicTraverseSessionReleaseMethod)(void *);
 };
 int register_DicTraverseSession(JNIEnv *env);
diff --git a/native/jni/src/dictionary.cpp b/native/jni/src/dictionary.cpp
index 158c3fb..8ce8c8b 100644
--- a/native/jni/src/dictionary.cpp
+++ b/native/jni/src/dictionary.cpp
@@ -60,7 +60,7 @@
         int *codes, int codesSize, int *prevWordChars,
         int prevWordLength, int commitPoint, bool isGesture,
         bool useFullEditDistance, unsigned short *outWords,
-        int *frequencies, int *spaceIndices, int *outputTypes) {
+        int *frequencies, int *spaceIndices, int *outputTypes) const {
     int result = 0;
     if (isGesture) {
         DicTraverseWrapper::initDicTraverseSession(
diff --git a/native/jni/src/dictionary.h b/native/jni/src/dictionary.h
index fd9e770..e9a03ce 100644
--- a/native/jni/src/dictionary.h
+++ b/native/jni/src/dictionary.h
@@ -48,7 +48,7 @@
             int *ycoordinates, int *times, int *pointerIds, int *codes, int codesSize,
             int *prevWordChars, int prevWordLength, int commitPoint, bool isGesture,
             bool useFullEditDistance, unsigned short *outWords,
-            int *frequencies, int *spaceIndices, int *outputTypes);
+            int *frequencies, int *spaceIndices, int *outputTypes) const;
 
     int getBigrams(const int32_t *word, int length, int *codes, int codesSize,
             unsigned short *outWords, int *frequencies, int *outputTypes) const;
diff --git a/native/jni/src/gesture/gesture_decoder_wrapper.h b/native/jni/src/gesture/gesture_decoder_wrapper.h
index f8bfe7c..2f6220b 100644
--- a/native/jni/src/gesture/gesture_decoder_wrapper.h
+++ b/native/jni/src/gesture/gesture_decoder_wrapper.h
@@ -39,7 +39,8 @@
 
     int getSuggestions(ProximityInfo *pInfo, void *traverseSession, int *inputXs, int *inputYs,
             int *times, int *pointerIds, int *codes, int inputSize, int commitPoint,
-            unsigned short *outWords, int *frequencies, int *outputIndices, int *outputTypes) {
+            unsigned short *outWords, int *frequencies, int *outputIndices,
+            int *outputTypes) const {
         if (!mIncrementalDecoderInterface) {
             return 0;
         }
diff --git a/native/jni/src/gesture/incremental_decoder_interface.h b/native/jni/src/gesture/incremental_decoder_interface.h
index 04f0095..d1395aa 100644
--- a/native/jni/src/gesture/incremental_decoder_interface.h
+++ b/native/jni/src/gesture/incremental_decoder_interface.h
@@ -31,7 +31,7 @@
     virtual int getSuggestions(ProximityInfo *pInfo, void *traverseSession,
             int *inputXs, int *inputYs, int *times, int *pointerIds, int *codes,
             int inputSize, int commitPoint, unsigned short *outWords, int *frequencies,
-            int *outputIndices, int *outputTypes) = 0;
+            int *outputIndices, int *outputTypes) const = 0;
     IncrementalDecoderInterface() { };
     virtual ~IncrementalDecoderInterface() { };
  private:
diff --git a/native/jni/src/gesture/incremental_decoder_wrapper.h b/native/jni/src/gesture/incremental_decoder_wrapper.h
index 5cb2ee3..1f4d8a0 100644
--- a/native/jni/src/gesture/incremental_decoder_wrapper.h
+++ b/native/jni/src/gesture/incremental_decoder_wrapper.h
@@ -39,7 +39,8 @@
 
     int getSuggestions(ProximityInfo *pInfo, void *traverseSession, int *inputXs, int *inputYs,
             int *times, int *pointerIds, int *codes, int inputSize, int commitPoint,
-            unsigned short *outWords, int *frequencies, int *outputIndices, int *outputTypes) {
+            unsigned short *outWords, int *frequencies, int *outputIndices,
+            int *outputTypes) const {
         if (!mIncrementalDecoderInterface) {
             return 0;
         }
diff --git a/native/jni/src/proximity_info.h b/native/jni/src/proximity_info.h
index 8a407e7..7ee633b 100644
--- a/native/jni/src/proximity_info.h
+++ b/native/jni/src/proximity_info.h
@@ -41,21 +41,12 @@
     float getNormalizedSquaredDistanceFromCenterFloat(
             const int keyId, const int x, const int y) const;
     bool sameAsTyped(const unsigned short *word, int length) const;
-    int squaredDistanceToEdge(const int keyId, const int x, const int y) const;
-    bool isOnKey(const int keyId, const int x, const int y) const {
-        if (keyId < 0) return true; // NOT_A_ID is -1, but return whenever < 0 just in case
-        const int left = mKeyXCoordinates[keyId];
-        const int top = mKeyYCoordinates[keyId];
-        const int right = left + mKeyWidths[keyId] + 1;
-        const int bottom = top + mKeyHeights[keyId];
-        return left < right && top < bottom && x >= left && x < right && y >= top && y < bottom;
-    }
     int getKeyIndex(const int c) const;
     int getKeyCode(const int keyIndex) const;
     bool hasSweetSpotData(const int keyIndex) const {
         // When there are no calibration data for a key,
         // the radius of the key is assigned to zero.
-        return mSweetSpotRadii[keyIndex] > 0.0;
+        return mSweetSpotRadii[keyIndex] > 0.0f;
     }
     float getSweetSpotRadiiAt(int keyIndex) const {
         return mSweetSpotRadii[keyIndex];
@@ -111,10 +102,6 @@
     float getKeyCenterYOfIdG(int keyId) const;
     int getKeyKeyDistanceG(int key0, int key1) const;
 
-    // Returns the keyboard key-center information.
-    void getCenters(int *centersX, int *centersY, int *codeToKeyIndex, int *keyToCodeIndex,
-            int *keyCount, int *keyWidth) const;
-
  private:
     DISALLOW_IMPLICIT_CONSTRUCTORS(ProximityInfo);
     // The max number of the keys in one keyboard layout
@@ -131,6 +118,15 @@
     float calculateSquaredDistanceFromSweetSpotCenter(
             const int keyIndex, const int inputIndex) const;
     bool hasInputCoordinates() const;
+    int squaredDistanceToEdge(const int keyId, const int x, const int y) const;
+    bool isOnKey(const int keyId, const int x, const int y) const {
+        if (keyId < 0) return true; // NOT_A_ID is -1, but return whenever < 0 just in case
+        const int left = mKeyXCoordinates[keyId];
+        const int top = mKeyYCoordinates[keyId];
+        const int right = left + mKeyWidths[keyId] + 1;
+        const int bottom = top + mKeyHeights[keyId];
+        return left < right && top < bottom && x >= left && x < right && y >= top && y < bottom;
+    }
 
     const int MAX_PROXIMITY_CHARS_SIZE;
     const int KEYBOARD_WIDTH;
diff --git a/native/jni/src/proximity_info_state.h b/native/jni/src/proximity_info_state.h
index 474c407..2fb0ae0 100644
--- a/native/jni/src/proximity_info_state.h
+++ b/native/jni/src/proximity_info_state.h
@@ -48,9 +48,6 @@
     // Defined here                        //
     /////////////////////////////////////////
     ProximityInfoState() {};
-    inline const int *getProximityCharsAt(const int index) const {
-        return mInputCodes + (index * MAX_PROXIMITY_CHARS_SIZE_INTERNAL);
-    }
 
     inline unsigned short getPrimaryCharAt(const int index) const {
         return getProximityCharsAt(index)[0];
@@ -194,6 +191,10 @@
         return mInputXCoordinates && mInputYCoordinates;
     }
 
+    inline const int *getProximityCharsAt(const int index) const {
+        return mInputCodes + (index * MAX_PROXIMITY_CHARS_SIZE_INTERNAL);
+    }
+
     // const
     const ProximityInfo *mProximityInfo;
     bool mHasTouchPositionCorrectionData;