Merge "Refactor SubtypeLocale to hold Resources"
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml
index c412635..850b1b8 100644
--- a/java/res/values/attrs.xml
+++ b/java/res/values/attrs.xml
@@ -87,10 +87,8 @@
         <attr name="keyRepeatStartTimeout" format="integer" />
         <!-- Key repeat interval in millisecond. -->
         <attr name="keyRepeatInterval" format="integer" />
-        <!-- Long press timeout of letter key in millisecond. -->
-        <attr name="longPressKeyTimeout" format="integer" />
-        <!-- Long press timeout of shift key in millisecond. -->
-        <attr name="longPressShiftKeyTimeout" format="integer" />
+        <!-- Long press timeout of shift key to shift lock in millisecond. -->
+        <attr name="longPressShiftLockTimeout" format="integer" />
         <!-- Ignore special key timeout while typing in millisecond. -->
         <attr name="ignoreAltCodeKeyTimeout" format="integer" />
         <!-- Layout resource for key press feedback.-->
diff --git a/java/res/values/config.xml b/java/res/values/config.xml
index 503e923..6b3c891 100644
--- a/java/res/values/config.xml
+++ b/java/res/values/config.xml
@@ -64,10 +64,13 @@
     <bool name="config_sliding_key_input_enabled">true</bool>
     <integer name="config_key_repeat_start_timeout">400</integer>
     <integer name="config_key_repeat_interval">50</integer>
-    <integer name="config_long_press_key_timeout">400</integer>
+    <integer name="config_default_longpress_key_timeout">300</integer>  <!-- milliseconds -->
+    <integer name="config_longpress_timeout_step">10</integer> <!-- milliseconds -->
+    <integer name="config_min_longpress_timeout">100</integer> <!-- milliseconds -->
+    <integer name="config_max_longpress_timeout">700</integer> <!-- milliseconds -->
     <!-- Long pressing shift will invoke caps-lock if > 0, never invoke caps-lock if == 0 -->
-    <integer name="config_long_press_shift_key_timeout">1200</integer>
-    <integer name="config_ignore_alt_code_key_timeout">350</integer>
+    <integer name="config_longpress_shift_lock_timeout">1200</integer> <!-- milliseconds -->
+    <integer name="config_ignore_alt_code_key_timeout">350</integer> <!-- milliseconds -->
     <!-- Showing more keys keyboard, just above the touched point if true, aligned to the key if
          false -->
     <bool name="config_show_more_keys_keyboard_at_touched_point">false</bool>
diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml
index fdae302..e39cafc 100644
--- a/java/res/values/strings.xml
+++ b/java/res/values/strings.xml
@@ -374,6 +374,8 @@
 
     <!-- Title of an option for usability study mode -->
     <string name="prefs_usability_study_mode">Usability study mode</string>
+    <!-- Title of the settings for key long press delay -->
+    <string name="prefs_key_longpress_timeout_settings">Key long press delay settings</string>
     <!-- Title of the settings for keypress vibration duration -->
     <string name="prefs_keypress_vibration_duration_settings">Keypress vibration duration settings</string>
     <!-- Title of the settings for keypress sound volume -->
diff --git a/java/res/values/styles.xml b/java/res/values/styles.xml
index c398b59..f71963a 100644
--- a/java/res/values/styles.xml
+++ b/java/res/values/styles.xml
@@ -80,8 +80,7 @@
         <item name="slidingKeyInputEnable">@bool/config_sliding_key_input_enabled</item>
         <item name="keyRepeatStartTimeout">@integer/config_key_repeat_start_timeout</item>
         <item name="keyRepeatInterval">@integer/config_key_repeat_interval</item>
-        <item name="longPressKeyTimeout">@integer/config_long_press_key_timeout</item>
-        <item name="longPressShiftKeyTimeout">@integer/config_long_press_shift_key_timeout</item>
+        <item name="longPressShiftLockTimeout">@integer/config_longpress_shift_lock_timeout</item>
         <item name="ignoreAltCodeKeyTimeout">@integer/config_ignore_alt_code_key_timeout</item>
         <item name="keyPreviewLayout">@layout/key_preview</item>
         <item name="keyPreviewOffset">@dimen/key_preview_offset</item>
diff --git a/java/res/xml/prefs.xml b/java/res/xml/prefs.xml
index cc1b52b..84e7f54 100644
--- a/java/res/xml/prefs.xml
+++ b/java/res/xml/prefs.xml
@@ -146,6 +146,13 @@
                 android:key="pref_key_preview_popup_dismiss_delay"
                 android:title="@string/key_preview_popup_dismiss_delay" />
             <com.android.inputmethod.latin.SeekBarDialogPreference
+                android:key="pref_key_longpress_timeout"
+                android:title="@string/prefs_key_longpress_timeout_settings"
+                latin:valueFormatText="@string/abbreviation_unit_milliseconds"
+                latin:minValue="@integer/config_min_longpress_timeout"
+                latin:maxValue="@integer/config_max_longpress_timeout"
+                latin:stepValue="@integer/config_longpress_timeout_step" />
+            <com.android.inputmethod.latin.SeekBarDialogPreference
                 android:key="pref_vibration_duration_settings"
                 android:title="@string/prefs_keypress_vibration_duration_settings"
                 latin:valueFormatText="@string/abbreviation_unit_milliseconds"
diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
index 9e3eb16..cf89ef2 100644
--- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
@@ -65,6 +65,7 @@
 import com.android.inputmethod.latin.LatinImeLogger;
 import com.android.inputmethod.latin.R;
 import com.android.inputmethod.latin.ResourceUtils;
+import com.android.inputmethod.latin.Settings;
 import com.android.inputmethod.latin.StaticInnerHandlerWrapper;
 import com.android.inputmethod.latin.StringUtils;
 import com.android.inputmethod.latin.SubtypeLocale;
@@ -205,8 +206,7 @@
 
         private final int mKeyRepeatStartTimeout;
         private final int mKeyRepeatInterval;
-        private final int mLongPressKeyTimeout;
-        private final int mLongPressShiftKeyTimeout;
+        private final int mLongPressShiftLockTimeout;
         private final int mIgnoreAltCodeKeyTimeout;
         private final int mGestureRecognitionUpdateTime;
 
@@ -218,10 +218,8 @@
                     R.styleable.MainKeyboardView_keyRepeatStartTimeout, 0);
             mKeyRepeatInterval = mainKeyboardViewAttr.getInt(
                     R.styleable.MainKeyboardView_keyRepeatInterval, 0);
-            mLongPressKeyTimeout = mainKeyboardViewAttr.getInt(
-                    R.styleable.MainKeyboardView_longPressKeyTimeout, 0);
-            mLongPressShiftKeyTimeout = mainKeyboardViewAttr.getInt(
-                    R.styleable.MainKeyboardView_longPressShiftKeyTimeout, 0);
+            mLongPressShiftLockTimeout = mainKeyboardViewAttr.getInt(
+                    R.styleable.MainKeyboardView_longPressShiftLockTimeout, 0);
             mIgnoreAltCodeKeyTimeout = mainKeyboardViewAttr.getInt(
                     R.styleable.MainKeyboardView_ignoreAltCodeKeyTimeout, 0);
             mGestureRecognitionUpdateTime = mainKeyboardViewAttr.getInt(
@@ -285,7 +283,7 @@
             final int delay;
             switch (code) {
             case Constants.CODE_SHIFT:
-                delay = mLongPressShiftKeyTimeout;
+                delay = mLongPressShiftLockTimeout;
                 break;
             default:
                 delay = 0;
@@ -306,15 +304,17 @@
             final int delay;
             switch (key.mCode) {
             case Constants.CODE_SHIFT:
-                delay = mLongPressShiftKeyTimeout;
+                delay = mLongPressShiftLockTimeout;
                 break;
             default:
+                final int longpressTimeout =
+                        Settings.getInstance().getCurrent().mKeyLongpressTimeout;
                 if (KeyboardSwitcher.getInstance().isInMomentarySwitchState()) {
                     // We use longer timeout for sliding finger input started from the symbols
                     // mode key.
-                    delay = mLongPressKeyTimeout * 3;
+                    delay = longpressTimeout * 3;
                 } else {
-                    delay = mLongPressKeyTimeout;
+                    delay = longpressTimeout;
                 }
                 break;
             }
diff --git a/java/src/com/android/inputmethod/latin/InputPointers.java b/java/src/com/android/inputmethod/latin/InputPointers.java
index 4d6c4f3..81c8330 100644
--- a/java/src/com/android/inputmethod/latin/InputPointers.java
+++ b/java/src/com/android/inputmethod/latin/InputPointers.java
@@ -18,8 +18,11 @@
 
 import com.android.inputmethod.annotations.UsedForTesting;
 
+import android.util.Log;
+
 // TODO: This class is not thread-safe.
 public final class InputPointers {
+    private static final String TAG = InputPointers.class.getSimpleName();
     private final int mDefaultCapacity;
     private final ResizableIntArray mXCoordinates;
     private final ResizableIntArray mYCoordinates;
@@ -126,6 +129,11 @@
     }
 
     public int[] getTimes() {
+        if (LatinImeLogger.sDBG) {
+            if (!isValidTimeStamps()) {
+                throw new RuntimeException("Time stamps are invalid.");
+            }
+        }
         return mTimes.getPrimitiveArray();
     }
 
@@ -134,4 +142,18 @@
         return "size=" + getPointerSize() + " id=" + mPointerIds + " time=" + mTimes
                 + " x=" + mXCoordinates + " y=" + mYCoordinates;
     }
+
+    private boolean isValidTimeStamps() {
+        final int[] times = mTimes.getPrimitiveArray();
+        for (int i = 1; i < getPointerSize(); ++i) {
+            if (times[i] < times[i - 1]) {
+                // dump
+                for (int j = 0; j < times.length; ++j) {
+                    Log.d(TAG, "--- (" + j + ") " + times[j]);
+                }
+                return false;
+            }
+        }
+        return true;
+    }
 }
diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java
index aa705da..408ea4a 100644
--- a/java/src/com/android/inputmethod/latin/Settings.java
+++ b/java/src/com/android/inputmethod/latin/Settings.java
@@ -55,6 +55,7 @@
     public static final String PREF_BIGRAM_PREDICTIONS = "next_word_prediction";
     public static final String PREF_GESTURE_SETTINGS = "gesture_typing_settings";
     public static final String PREF_GESTURE_INPUT = "gesture_input";
+    public static final String PREF_KEY_LONGPRESS_TIMEOUT = "pref_key_longpress_timeout";
     public static final String PREF_VIBRATION_DURATION_SETTINGS =
             "pref_vibration_duration_settings";
     public static final String PREF_KEYPRESS_SOUND_VOLUME =
@@ -175,6 +176,16 @@
                 ResourceUtils.getDeviceOverrideValue(res, R.array.keypress_volumes));
     }
 
+    public static int readKeyLongpressTimeout(final SharedPreferences prefs,
+            final Resources res) {
+        final int ms = prefs.getInt(PREF_KEY_LONGPRESS_TIMEOUT, -1);
+        return (ms >= 0) ? ms : readDefaultKeyLongpressTimeout(res);
+    }
+
+    public static int readDefaultKeyLongpressTimeout(final Resources res) {
+        return res.getInteger(R.integer.config_default_longpress_key_timeout);
+    }
+
     public static int readKeypressVibrationDuration(final SharedPreferences prefs,
             final Resources res) {
         final int ms = prefs.getInt(PREF_VIBRATION_DURATION_SETTINGS, -1);
diff --git a/java/src/com/android/inputmethod/latin/SettingsFragment.java b/java/src/com/android/inputmethod/latin/SettingsFragment.java
index 5c337ff..3ba24fb 100644
--- a/java/src/com/android/inputmethod/latin/SettingsFragment.java
+++ b/java/src/com/android/inputmethod/latin/SettingsFragment.java
@@ -164,6 +164,7 @@
             getPreferenceScreen().removePreference(gestureTypingSettings);
         }
 
+        setupKeyLongpressTimeoutSettings(prefs, res);
         setupKeypressVibrationDurationSettings(prefs, res);
         setupKeypressSoundVolumeSettings(prefs, res);
         refreshEnablingsOfKeypressSoundAndVibrationSettings(prefs, res);
@@ -298,6 +299,34 @@
         });
     }
 
+    private void setupKeyLongpressTimeoutSettings(final SharedPreferences sp,
+            final Resources res) {
+        final SeekBarDialogPreference pref = (SeekBarDialogPreference)findPreference(
+                Settings.PREF_KEY_LONGPRESS_TIMEOUT);
+        if (pref == null) {
+            return;
+        }
+        pref.setInterface(new SeekBarDialogPreference.ValueProxy() {
+            @Override
+            public void writeValue(final int value, final String key) {
+                sp.edit().putInt(key, value).apply();
+            }
+
+            @Override
+            public int readValue(final String key) {
+                return Settings.readKeyLongpressTimeout(sp, res);
+            }
+
+            @Override
+            public int readDefaultValue(final String key) {
+                return Settings.readDefaultKeyLongpressTimeout(res);
+            }
+
+            @Override
+            public void feedbackValue(final int value) {}
+        });
+    }
+
     private void setupKeypressSoundVolumeSettings(final SharedPreferences sp, final Resources res) {
         final SeekBarDialogPreference pref = (SeekBarDialogPreference)findPreference(
                 Settings.PREF_KEYPRESS_SOUND_VOLUME);
diff --git a/java/src/com/android/inputmethod/latin/SettingsValues.java b/java/src/com/android/inputmethod/latin/SettingsValues.java
index d6556d6..29e79e4 100644
--- a/java/src/com/android/inputmethod/latin/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/SettingsValues.java
@@ -59,6 +59,7 @@
     public final boolean mGestureInputEnabled;
     public final boolean mGesturePreviewTrailEnabled;
     public final boolean mGestureFloatingPreviewTextEnabled;
+    public final int mKeyLongpressTimeout;
 
     // From the input box
     public final InputAttributes mInputAttributes;
@@ -121,6 +122,7 @@
         mBigramPredictionEnabled = readBigramPredictionEnabled(prefs, res);
 
         // Compute other readable settings
+        mKeyLongpressTimeout = Settings.readKeyLongpressTimeout(prefs, res);
         mKeypressVibrationDuration = Settings.readKeypressVibrationDuration(prefs, res);
         mKeypressSoundVolume = Settings.readKeypressSoundVolume(prefs, res);
         mKeyPreviewPopupDismissDelay = Settings.readKeyPreviewPopupDismissDelay(prefs, res);
diff --git a/native/jni/src/proximity_info_params.cpp b/native/jni/src/proximity_info_params.cpp
index d62e090..5a51f62 100644
--- a/native/jni/src/proximity_info_params.cpp
+++ b/native/jni/src/proximity_info_params.cpp
@@ -25,4 +25,6 @@
 const int ProximityInfoParams::NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR =
         1 << NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR_LOG_2;
 const float ProximityInfoParams::NOT_A_DISTANCE_FLOAT = -1.0f;
+// TODO: Investigate if this is required
+const float ProximityInfoParams::SEARCH_KEY_RADIUS_RATIO = 0.95f;
 } // namespace latinime
diff --git a/native/jni/src/proximity_info_params.h b/native/jni/src/proximity_info_params.h
index 171e6b4..b941fec 100644
--- a/native/jni/src/proximity_info_params.h
+++ b/native/jni/src/proximity_info_params.h
@@ -29,6 +29,7 @@
     static const int MIN_DOUBLE_LETTER_BEELINE_SPEED_PERCENTILE;
     static const int NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR;
     static const float NOT_A_DISTANCE_FLOAT;
+    static const float SEARCH_KEY_RADIUS_RATIO;
  private:
     DISALLOW_IMPLICIT_CONSTRUCTORS(ProximityInfoParams);
     static const int NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR_LOG_2;
diff --git a/native/jni/src/proximity_info_state.cpp b/native/jni/src/proximity_info_state.cpp
index 86fe7e1..e720275 100644
--- a/native/jni/src/proximity_info_state.cpp
+++ b/native/jni/src/proximity_info_state.cpp
@@ -116,28 +116,14 @@
                     mProximityInfo->getKeyCount(), lastSavedInputSize, mSampledInputSize,
                     &mSampledInputXs, &mSampledInputYs, &mSpeedRates, &mSampledLengthCache,
                     &mDistanceCache_G, &mNearKeysVector, &mCharProbabilities);
-
-            static const float READ_FORWORD_LENGTH_SCALE = 0.95f;
-            const int readForwordLength = static_cast<int>(
-                    hypotf(mProximityInfo->getKeyboardWidth(), mProximityInfo->getKeyboardHeight())
-                            * READ_FORWORD_LENGTH_SCALE);
-            for (int i = 0; i < mSampledInputSize; ++i) {
-                if (i >= lastSavedInputSize) {
-                    mSearchKeysVector[i].reset();
-                }
-                for (int j = max(i, lastSavedInputSize); j < mSampledInputSize; ++j) {
-                    if (mSampledLengthCache[j] - mSampledLengthCache[i] >= readForwordLength) {
-                        break;
-                    }
-                    mSearchKeysVector[i] |= mNearKeysVector[j];
-                }
-            }
+            ProximityInfoStateUtils::updateSearchKeysVector(mProximityInfo, mSampledInputSize,
+                    lastSavedInputSize, &mSampledLengthCache, &mNearKeysVector, &mSearchKeysVector);
         }
     }
 
     if (DEBUG_SAMPLING_POINTS) {
         ProximityInfoStateUtils::dump(isGeometric, inputSize, xCoordinates, yCoordinates,
-                mSampledInputSize, &mSampledInputXs, &mSampledInputYs, &mSpeedRates,
+                mSampledInputSize, &mSampledInputXs, &mSampledInputYs, &mSampledTimes, &mSpeedRates,
                 &mBeelineSpeedPercentiles);
     }
     // end
diff --git a/native/jni/src/proximity_info_state_utils.cpp b/native/jni/src/proximity_info_state_utils.cpp
index 9dd3f0b..be6cde1 100644
--- a/native/jni/src/proximity_info_state_utils.cpp
+++ b/native/jni/src/proximity_info_state_utils.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <cmath>
 #include <sstream> // for debug prints
 #include <vector>
 
@@ -37,7 +38,7 @@
         if (times) {
             for (int i = 0; i < inputSize; ++i) {
                 AKLOGI("(%d) x %d, y %d, time %d",
-                        i, xCoordinates[i], yCoordinates[i], times[i]);
+                        i, inputXCoordinates[i], inputYCoordinates[i], times[i]);
             }
         }
     }
@@ -45,7 +46,10 @@
     if (times) {
         for (int i = 0; i < inputSize; ++i) {
             if (i > 0) {
-                ASSERT(times[i] >= times[i - 1]);
+                if (times[i] < times[i - 1]) {
+                    AKLOGI("Invalid time sequence. %d, %d", times[i], times[i - 1]);
+                    ASSERT(false);
+                }
             }
         }
     }
@@ -469,11 +473,10 @@
                 // This point is not used because it's too close to the previous point.
                 if (DEBUG_GEO_FULL) {
                     AKLOGI("p0: size = %zd, x = %d, y = %d, lx = %d, ly = %d, dist = %d, "
-                           "width = %d", size, x, y, mSampledInputXs.back(),
-                           mSampledInputYs.back(), ProximityInfoUtils::getDistanceInt(
-                                   x, y, mSampledInputXs.back(), mSampledInputYs.back()),
-                           mProximityInfo->getMostCommonKeyWidth()
-                                   / LAST_POINT_SKIP_DISTANCE_SCALE);
+                           "width = %d", size, x, y, sampledInputXs->back(),
+                           sampledInputYs->back(), getDistanceInt(
+                                   x, y, sampledInputXs->back(), sampledInputYs->back()),
+                           mostCommonKeyWidth / LAST_POINT_SKIP_DISTANCE_SCALE);
                 }
                 return popped;
             }
@@ -511,11 +514,12 @@
         const float averageSpeed, const int id, const int inputSize, const int *const xCoordinates,
         const int *const yCoordinates, const int *times, const int sampledInputSize,
         const std::vector<int> *const sampledInputXs,
-        const std::vector<int> *const sampledInputYs, const std::vector<int> *const inputIndice) {
+        const std::vector<int> *const sampledInputYs,
+        const std::vector<int> *const sampledInputIndices) {
     if (sampledInputSize <= 0 || averageSpeed < 0.001f) {
         if (DEBUG_SAMPLING_POINTS) {
             AKLOGI("--- invalid state: cancel. size = %d, ave = %f",
-                    mSampledInputSize, mAverageSpeed);
+                    sampledInputSize, averageSpeed);
         }
         return 1.0f;
     }
@@ -523,7 +527,7 @@
             * ProximityInfoParams::LOOKUP_RADIUS_PERCENTILE / MAX_PERCENTILE;
     const int x0 = (*sampledInputXs)[id];
     const int y0 = (*sampledInputYs)[id];
-    const int actualInputIndex = (*inputIndice)[id];
+    const int actualInputIndex = (*sampledInputIndices)[id];
     int tempTime = 0;
     int tempBeelineDistance = 0;
     int start = actualInputIndex;
@@ -582,10 +586,10 @@
     if (DEBUG_DOUBLE_LETTER) {
         AKLOGI("--- (%d, %d) double letter: start = %d, end = %d, dist = %d, time = %d,"
                 " speed = %f, ave = %f, val = %f, start time = %d, end time = %d",
-                id, mSampledInputIndice[id], start, end, beelineDistance, time,
-                (static_cast<float>(beelineDistance) / static_cast<float>(time)), mAverageSpeed,
+                id, (*sampledInputIndices)[id], start, end, beelineDistance, time,
+                (static_cast<float>(beelineDistance) / static_cast<float>(time)), averageSpeed,
                 ((static_cast<float>(beelineDistance) / static_cast<float>(time))
-                        / mAverageSpeed), adjustedStartTime, adjustedEndTime);
+                        / averageSpeed), adjustedStartTime, adjustedEndTime);
     }
     // Offset 1%
     // TODO: Detect double letter more smartly
@@ -850,7 +854,6 @@
         }
     }
 
-
     if (DEBUG_POINTS_PROBABILITY) {
         for (int i = 0; i < sampledInputSize; ++i) {
             std::stringstream sstream;
@@ -916,6 +919,29 @@
     }
 }
 
+/* static */ void ProximityInfoStateUtils::updateSearchKeysVector(
+        const ProximityInfo *const proximityInfo, const int sampledInputSize,
+        const int lastSavedInputSize,
+        const std::vector<int> *const sampledLengthCache,
+        const std::vector<NearKeycodesSet> *const nearKeysVector,
+        std::vector<NearKeycodesSet> *searchKeysVector) {
+    const int readForwordLength = static_cast<int>(
+            hypotf(proximityInfo->getKeyboardWidth(), proximityInfo->getKeyboardHeight())
+                    * ProximityInfoParams::SEARCH_KEY_RADIUS_RATIO);
+    for (int i = 0; i < sampledInputSize; ++i) {
+        if (i >= lastSavedInputSize) {
+            (*searchKeysVector)[i].reset();
+        }
+        for (int j = max(i, lastSavedInputSize); j < sampledInputSize; ++j) {
+            // TODO: Investigate if this is required. This may not fail.
+            if ((*sampledLengthCache)[j] - (*sampledLengthCache)[i] >= readForwordLength) {
+                break;
+            }
+            (*searchKeysVector)[i] |= (*nearKeysVector)[j];
+        }
+    }
+}
+
 // Decreases char probabilities of index0 by checking probabilities of a near point (index1) and
 // increases char probabilities of index1 by checking probabilities of index0.
 /* static */ bool ProximityInfoStateUtils::suppressCharProbabilities(const int mostCommonKeyWidth,
@@ -963,12 +989,13 @@
         const int *const inputXCoordinates, const int *const inputYCoordinates,
         const int sampledInputSize, const std::vector<int> *const sampledInputXs,
         const std::vector<int> *const sampledInputYs,
+        const std::vector<int> *const sampledTimes,
         const std::vector<float> *const sampledSpeedRates,
         const std::vector<int> *const sampledBeelineSpeedPercentiles) {
     if (DEBUG_GEO_FULL) {
         for (int i = 0; i < sampledInputSize; ++i) {
-            AKLOGI("Sampled(%d): x = %d, y = %d, time = %d", i, mSampledInputXs[i],
-                    mSampledInputYs[i], mSampledTimes ? mSampledTimes[i], -1);
+            AKLOGI("Sampled(%d): x = %d, y = %d, time = %d", i, (*sampledInputXs)[i],
+                    (*sampledInputYs)[i], sampledTimes ? (*sampledTimes)[i] : -1);
         }
     }
 
@@ -985,8 +1012,8 @@
     for (int i = 0; i < sampledInputSize; ++i) {
         if (isGeometric) {
             AKLOGI("%d: x = %d, y = %d, time = %d, relative speed = %.4f, beeline speed = %d",
-                    i, mSampledInputXs[i], mSampledInputYs[i], mSampledTimes[i], mSpeedRates[i],
-                    getBeelineSpeedPercentile(i));
+                    i, (*sampledInputXs)[i], (*sampledInputYs)[i], (*sampledTimes)[i],
+                    (*sampledSpeedRates)[i], (*sampledBeelineSpeedPercentiles)[i]);
         }
         sampledX << (*sampledInputXs)[i];
         sampledY << (*sampledInputYs)[i];
diff --git a/native/jni/src/proximity_info_state_utils.h b/native/jni/src/proximity_info_state_utils.h
index b67f82c..b70121a 100644
--- a/native/jni/src/proximity_info_state_utils.h
+++ b/native/jni/src/proximity_info_state_utils.h
@@ -72,6 +72,12 @@
             const std::vector<float> *const distanceCache_G,
             std::vector<NearKeycodesSet> *nearKeysVector,
             std::vector<hash_map_compat<int, float> > *charProbabilities);
+    static void updateSearchKeysVector(
+            const ProximityInfo *const proximityInfo, const int sampledInputSize,
+            const int lastSavedInputSize,
+            const std::vector<int> *const sampledLengthCache,
+            const std::vector<NearKeycodesSet> *const nearKeysVector,
+            std::vector<NearKeycodesSet> *searchKeysVector);
     static float getPointToKeyByIdLength(const float maxPointToKeyLength,
             const std::vector<float> *const distanceCache_G, const int keyCount,
             const int inputIndex, const int keyId, const float scale);
@@ -99,6 +105,7 @@
             const int *const inputXCoordinates, const int *const inputYCoordinates,
             const int sampledInputSize, const std::vector<int> *const sampledInputXs,
             const std::vector<int> *const sampledInputYs,
+            const std::vector<int> *const sampledTimes,
             const std::vector<float> *const sampledSpeedRates,
             const std::vector<int> *const sampledBeelineSpeedPercentiles);
  private: