Refactor getKeyIndexAndNearbyCodes into independent class

Bug: 2910379
Change-Id: Ib26a898ea108ee4292f9f91b6d7d10c1fc380fd1
diff --git a/java/src/com/android/inputmethod/latin/LatinKeyboardBaseView.java b/java/src/com/android/inputmethod/latin/LatinKeyboardBaseView.java
index 346daa3..ee9b6bb 100644
--- a/java/src/com/android/inputmethod/latin/LatinKeyboardBaseView.java
+++ b/java/src/com/android/inputmethod/latin/LatinKeyboardBaseView.java
@@ -43,7 +43,6 @@
 import android.widget.PopupWindow;
 import android.widget.TextView;
 
-import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -143,7 +142,7 @@
     public static final int NOT_A_TOUCH_COORDINATE = -1;
 
     private static final boolean DEBUG = false;
-    private static final int NOT_A_KEY = -1;
+    static final int NOT_A_KEY = -1;
     private static final int[] KEY_DELETE = { Keyboard.KEYCODE_DELETE };
     private static final int[] LONG_PRESSABLE_STATE_SET = { android.R.attr.state_long_pressable };
 
@@ -184,7 +183,7 @@
     private static final int DEBOUNCE_TIME = 70;
 
     private int mVerticalCorrection;
-    private int mProximityThreshold;
+    private ProximityKeyDetector mProximityKeyDetector = new ProximityKeyDetector();
 
     private boolean mPreviewCentered = false;
     private boolean mShowPreview = true;
@@ -193,13 +192,10 @@
     private int mPopupPreviewY;
     private int mWindowY;
 
-    private boolean mProximityCorrectOn;
-
     private Paint mPaint;
     private Rect mPadding;
 
     private int mCurrentKey = NOT_A_KEY;
-    private int mDownKey = NOT_A_KEY;
     private int mStartX;
     private int mStartY;
 
@@ -227,9 +223,6 @@
     private static final int REPEAT_START_DELAY = 400;
     private static final int LONGPRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout();
 
-    private static int MAX_NEARBY_KEYS = 12;
-    private int[] mDistances = new int[MAX_NEARBY_KEYS];
-
     // For multi-tap
     private int mLastSentIndex;
     private int mTapCount;
@@ -625,6 +618,7 @@
         LatinImeLogger.onSetKeyboard(mKeyboard);
         List<Key> keys = mKeyboard.getKeys();
         mKeys = keys.toArray(new Key[keys.size()]);
+        mProximityKeyDetector.setKeyboard(keyboard, mKeys);
         requestLayout();
         // Hint to reallocate the buffer if the size changed
         mKeyboardChanged = true;
@@ -719,14 +713,14 @@
      * @param enabled whether or not the proximity correction is enabled
      */
     public void setProximityCorrectionEnabled(boolean enabled) {
-        mProximityCorrectOn = enabled;
+        mProximityKeyDetector.setProximityCorrectionEnabled(enabled);
     }
 
     /**
      * Returns true if proximity correction is enabled.
      */
     public boolean isProximityCorrectionEnabled() {
-        return mProximityCorrectOn;
+        return mProximityKeyDetector.isProximityCorrectionEnabled();
     }
 
     /**
@@ -778,8 +772,7 @@
             dimensionSum += Math.min(key.width, key.height) + key.gap;
         }
         if (dimensionSum < 0 || length == 0) return;
-        mProximityThreshold = (int) (dimensionSum * 1.4f / length);
-        mProximityThreshold *= mProximityThreshold; // Square it
+        mProximityKeyDetector.setProximityThreshold((int) (dimensionSum * 1.4f / length));
 
         final float hysteresisPixel = getContext().getResources()
                 .getDimension(R.dimen.key_debounce_hysteresis_distance);
@@ -920,54 +913,6 @@
         mDirtyRect.setEmpty();
     }
 
-    private int getKeyIndexAndNearbyCodes(int x, int y, int[] allKeys) {
-        final Key[] keys = mKeys;
-        int primaryIndex = NOT_A_KEY;
-        int closestKey = NOT_A_KEY;
-        int closestKeyDist = mProximityThreshold + 1;
-        Arrays.fill(mDistances, Integer.MAX_VALUE);
-        int [] nearestKeyIndices = mKeyboard.getNearestKeys(x, y);
-        final int keyCount = nearestKeyIndices.length;
-        for (int i = 0; i < keyCount; i++) {
-            final Key key = keys[nearestKeyIndices[i]];
-            int dist = 0;
-            boolean isInside = key.isInside(x,y);
-            if (isInside) {
-                primaryIndex = nearestKeyIndices[i];
-            }
-
-            if (((mProximityCorrectOn
-                    && (dist = key.squaredDistanceFrom(x, y)) < mProximityThreshold)
-                    || isInside)
-                    && key.codes[0] > 32) {
-                // Find insertion point
-                final int nCodes = key.codes.length;
-                if (dist < closestKeyDist) {
-                    closestKeyDist = dist;
-                    closestKey = nearestKeyIndices[i];
-                }
-
-                if (allKeys == null) continue;
-
-                for (int j = 0; j < mDistances.length; j++) {
-                    if (mDistances[j] > dist) {
-                        // Make space for nCodes codes
-                        System.arraycopy(mDistances, j, mDistances, j + nCodes,
-                                mDistances.length - j - nCodes);
-                        System.arraycopy(allKeys, j, allKeys, j + nCodes,
-                                allKeys.length - j - nCodes);
-                        System.arraycopy(key.codes, 0, allKeys, j, nCodes);
-                        Arrays.fill(mDistances, j, j + nCodes, dist);
-                        break;
-                    }
-                }
-            }
-        }
-        if (primaryIndex == NOT_A_KEY) {
-            primaryIndex = closestKey;
-        }
-        return primaryIndex;
-    }
 
     private void detectAndSendKey(int index, int x, int y, long eventTime) {
         if (index != NOT_A_KEY && index < mKeys.length) {
@@ -978,9 +923,8 @@
             } else {
                 int code = key.codes[0];
                 //TextEntryState.keyPressedAt(key, x, y);
-                int[] codes = new int[MAX_NEARBY_KEYS];
-                Arrays.fill(codes, NOT_A_KEY);
-                getKeyIndexAndNearbyCodes(x, y, codes);
+                int[] codes = mProximityKeyDetector.newCodeArray();
+                mProximityKeyDetector.getKeyIndexAndNearbyCodes(x, y, codes);
                 // Multi-tap
                 if (mInMultiTap) {
                     if (mTapCount != -1) {
@@ -1352,13 +1296,12 @@
         int touchY = (int) me.getY() + mVerticalCorrection - getPaddingTop();
         final int action = me.getAction();
         final long eventTime = me.getEventTime();
-        int keyIndex = getKeyIndexAndNearbyCodes(touchX, touchY, null);
+        int keyIndex = mProximityKeyDetector.getKeyIndexAndNearbyCodes(touchX, touchY, null);
 
         switch (action) {
             case MotionEvent.ACTION_DOWN:
                 mAbortKey = false;
                 mCurrentKey = keyIndex;
-                mDownKey = keyIndex;
                 mStartX = touchX;
                 mStartY = touchY;
                 mDebouncer.startMoveDebouncing(touchX, touchY);
diff --git a/java/src/com/android/inputmethod/latin/ProximityKeyDetector.java b/java/src/com/android/inputmethod/latin/ProximityKeyDetector.java
new file mode 100644
index 0000000..eae2d7f
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/ProximityKeyDetector.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.inputmethod.latin;
+
+import android.inputmethodservice.Keyboard;
+import android.inputmethodservice.Keyboard.Key;
+
+import java.util.Arrays;
+
+class ProximityKeyDetector {
+    private static final int MAX_NEARBY_KEYS = 12;
+
+    private Keyboard mKeyboard;
+    private Key[] mKeys;
+
+    private boolean mProximityCorrectOn;
+    private int mProximityThresholdSquare;
+
+    // working area
+    private int[] mDistances = new int[MAX_NEARBY_KEYS];
+
+    public void setKeyboard(Keyboard keyboard, Key[] keys) {
+        if (keyboard == null || keys == null)
+            throw new NullPointerException();
+        mKeyboard = keyboard;
+        mKeys = keys;
+    }
+
+    public void setProximityCorrectionEnabled(boolean enabled) {
+        mProximityCorrectOn = enabled;
+    }
+
+    public boolean isProximityCorrectionEnabled() {
+        return mProximityCorrectOn;
+    }
+
+    public void setProximityThreshold(int threshold) {
+        mProximityThresholdSquare = threshold * threshold;
+    }
+
+    public int[] newCodeArray() {
+        int[] codes = new int[MAX_NEARBY_KEYS];
+        Arrays.fill(codes, LatinKeyboardBaseView.NOT_A_KEY);
+        return codes;
+    }
+
+    public int getKeyIndexAndNearbyCodes(int x, int y, int[] allKeys) {
+        final Key[] keys = mKeys;
+        if (keys == null)
+            throw new IllegalStateException("keyboard isn't set");
+        // mKeyboard is guaranteed not null at setKeybaord() method
+        int primaryIndex = LatinKeyboardBaseView.NOT_A_KEY;
+        int closestKey = LatinKeyboardBaseView.NOT_A_KEY;
+        int closestKeyDist = mProximityThresholdSquare + 1;
+        int[] distances = mDistances;
+        Arrays.fill(distances, Integer.MAX_VALUE);
+        int [] nearestKeyIndices = mKeyboard.getNearestKeys(x, y);
+        final int keyCount = nearestKeyIndices.length;
+        for (int i = 0; i < keyCount; i++) {
+            final Key key = keys[nearestKeyIndices[i]];
+            int dist = 0;
+            boolean isInside = key.isInside(x,y);
+            if (isInside) {
+                primaryIndex = nearestKeyIndices[i];
+            }
+
+            if (((mProximityCorrectOn
+                    && (dist = key.squaredDistanceFrom(x, y)) < mProximityThresholdSquare)
+                    || isInside)
+                    && key.codes[0] > 32) {
+                // Find insertion point
+                final int nCodes = key.codes.length;
+                if (dist < closestKeyDist) {
+                    closestKeyDist = dist;
+                    closestKey = nearestKeyIndices[i];
+                }
+
+                if (allKeys == null) continue;
+
+                for (int j = 0; j < distances.length; j++) {
+                    if (distances[j] > dist) {
+                        // Make space for nCodes codes
+                        System.arraycopy(distances, j, distances, j + nCodes,
+                                distances.length - j - nCodes);
+                        System.arraycopy(allKeys, j, allKeys, j + nCodes,
+                                allKeys.length - j - nCodes);
+                        System.arraycopy(key.codes, 0, allKeys, j, nCodes);
+                        Arrays.fill(distances, j, j + nCodes, dist);
+                        break;
+                    }
+                }
+            }
+        }
+        if (primaryIndex == LatinKeyboardBaseView.NOT_A_KEY) {
+            primaryIndex = closestKey;
+        }
+        return primaryIndex;
+    }
+}
\ No newline at end of file