18fbd55229243cb66c03d5ea1f79dfb39f596590dsatok/*
28632bff2d5a8e1160989008dea6eff4b94b065ddTadashi G. Takaoka * Copyright (C) 2011 The Android Open Source Project
38fbd55229243cb66c03d5ea1f79dfb39f596590dsatok *
48fbd55229243cb66c03d5ea1f79dfb39f596590dsatok * Licensed under the Apache License, Version 2.0 (the "License"); you may not
58fbd55229243cb66c03d5ea1f79dfb39f596590dsatok * use this file except in compliance with the License. You may obtain a copy of
68fbd55229243cb66c03d5ea1f79dfb39f596590dsatok * the License at
78fbd55229243cb66c03d5ea1f79dfb39f596590dsatok *
88fbd55229243cb66c03d5ea1f79dfb39f596590dsatok * http://www.apache.org/licenses/LICENSE-2.0
98fbd55229243cb66c03d5ea1f79dfb39f596590dsatok *
108fbd55229243cb66c03d5ea1f79dfb39f596590dsatok * Unless required by applicable law or agreed to in writing, software
118fbd55229243cb66c03d5ea1f79dfb39f596590dsatok * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
128fbd55229243cb66c03d5ea1f79dfb39f596590dsatok * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
138fbd55229243cb66c03d5ea1f79dfb39f596590dsatok * License for the specific language governing permissions and limitations under
148fbd55229243cb66c03d5ea1f79dfb39f596590dsatok * the License.
158fbd55229243cb66c03d5ea1f79dfb39f596590dsatok */
168fbd55229243cb66c03d5ea1f79dfb39f596590dsatok
178fbd55229243cb66c03d5ea1f79dfb39f596590dsatokpackage com.android.inputmethod.keyboard;
188fbd55229243cb66c03d5ea1f79dfb39f596590dsatok
19ad35835baccb4101c3d8766fadbf4d127e41b6cbYusuke Nojimaimport android.graphics.Rect;
20ad35835baccb4101c3d8766fadbf4d127e41b6cbYusuke Nojima
21294e1b4a5abb86e58deefc8eee40e6b661524b28Yusuke Nojimaimport com.android.inputmethod.keyboard.internal.KeyboardParams.TouchPositionCorrection;
22eaef1c500703b4ee378821884c7b108815ed2983Ken Wakasaimport com.android.inputmethod.latin.Utils;
23f098fbbef324df034cc04de04d9b5fe6657238c7Jean Chalardimport com.android.inputmethod.latin.spellcheck.SpellCheckerProximityInfo;
24eaef1c500703b4ee378821884c7b108815ed2983Ken Wakasa
25817e517e463cb32726ff5a62196ac8744848e29bsatokimport java.util.Arrays;
26043f7841985916717f4fa821fe3e423daf3ff2f5Jean Chalardimport java.util.Collections;
27817e517e463cb32726ff5a62196ac8744848e29bsatokimport java.util.List;
28817e517e463cb32726ff5a62196ac8744848e29bsatok
298fbd55229243cb66c03d5ea1f79dfb39f596590dsatokpublic class ProximityInfo {
308fbd55229243cb66c03d5ea1f79dfb39f596590dsatok    public static final int MAX_PROXIMITY_CHARS_SIZE = 16;
310d5494c66ac3e5947040e8148091163a1c8716f7satok    /** Number of key widths from current touch point to search for nearest keys. */
320d5494c66ac3e5947040e8148091163a1c8716f7satok    private static float SEARCH_DISTANCE = 1.2f;
330d5494c66ac3e5947040e8148091163a1c8716f7satok    private static final int[] EMPTY_INT_ARRAY = new int[0];
348fbd55229243cb66c03d5ea1f79dfb39f596590dsatok
35ad35835baccb4101c3d8766fadbf4d127e41b6cbYusuke Nojima    private final int mKeyHeight;
368fbd55229243cb66c03d5ea1f79dfb39f596590dsatok    private final int mGridWidth;
378fbd55229243cb66c03d5ea1f79dfb39f596590dsatok    private final int mGridHeight;
388fbd55229243cb66c03d5ea1f79dfb39f596590dsatok    private final int mGridSize;
390d5494c66ac3e5947040e8148091163a1c8716f7satok    private final int mCellWidth;
400d5494c66ac3e5947040e8148091163a1c8716f7satok    private final int mCellHeight;
410d5494c66ac3e5947040e8148091163a1c8716f7satok    // TODO: Find a proper name for mKeyboardMinWidth
420d5494c66ac3e5947040e8148091163a1c8716f7satok    private final int mKeyboardMinWidth;
430d5494c66ac3e5947040e8148091163a1c8716f7satok    private final int mKeyboardHeight;
440d5494c66ac3e5947040e8148091163a1c8716f7satok    private final int[][] mGridNeighbors;
458fbd55229243cb66c03d5ea1f79dfb39f596590dsatok
46d6339639c39cbe0a833361623bf6963cff526784Yusuke Nojima    ProximityInfo(int gridWidth, int gridHeight, int minWidth, int height, int keyWidth,
47294e1b4a5abb86e58deefc8eee40e6b661524b28Yusuke Nojima            int keyHeight, List<Key> keys, TouchPositionCorrection touchPositionCorrection) {
488fbd55229243cb66c03d5ea1f79dfb39f596590dsatok        mGridWidth = gridWidth;
498fbd55229243cb66c03d5ea1f79dfb39f596590dsatok        mGridHeight = gridHeight;
508fbd55229243cb66c03d5ea1f79dfb39f596590dsatok        mGridSize = mGridWidth * mGridHeight;
510d5494c66ac3e5947040e8148091163a1c8716f7satok        mCellWidth = (minWidth + mGridWidth - 1) / mGridWidth;
520d5494c66ac3e5947040e8148091163a1c8716f7satok        mCellHeight = (height + mGridHeight - 1) / mGridHeight;
530d5494c66ac3e5947040e8148091163a1c8716f7satok        mKeyboardMinWidth = minWidth;
540d5494c66ac3e5947040e8148091163a1c8716f7satok        mKeyboardHeight = height;
55ad35835baccb4101c3d8766fadbf4d127e41b6cbYusuke Nojima        mKeyHeight = keyHeight;
560d5494c66ac3e5947040e8148091163a1c8716f7satok        mGridNeighbors = new int[mGridSize][];
570d5494c66ac3e5947040e8148091163a1c8716f7satok        if (minWidth == 0 || height == 0) {
580d5494c66ac3e5947040e8148091163a1c8716f7satok            // No proximity required. Keyboard might be mini keyboard.
590d5494c66ac3e5947040e8148091163a1c8716f7satok            return;
600d5494c66ac3e5947040e8148091163a1c8716f7satok        }
61294e1b4a5abb86e58deefc8eee40e6b661524b28Yusuke Nojima        computeNearestNeighbors(keyWidth, keys, touchPositionCorrection);
628fbd55229243cb66c03d5ea1f79dfb39f596590dsatok    }
638fbd55229243cb66c03d5ea1f79dfb39f596590dsatok
64a562767a14c7bbac95b25e69e360fc28d6ce9e33Jean Chalard    public static ProximityInfo createDummyProximityInfo() {
65294e1b4a5abb86e58deefc8eee40e6b661524b28Yusuke Nojima        return new ProximityInfo(1, 1, 1, 1, 1, 1, Collections.<Key>emptyList(), null);
66043f7841985916717f4fa821fe3e423daf3ff2f5Jean Chalard    }
67043f7841985916717f4fa821fe3e423daf3ff2f5Jean Chalard
68a562767a14c7bbac95b25e69e360fc28d6ce9e33Jean Chalard    public static ProximityInfo createSpellCheckerProximityInfo() {
69a562767a14c7bbac95b25e69e360fc28d6ce9e33Jean Chalard        final ProximityInfo spellCheckerProximityInfo = createDummyProximityInfo();
70f098fbbef324df034cc04de04d9b5fe6657238c7Jean Chalard        spellCheckerProximityInfo.mNativeProximityInfo =
71f098fbbef324df034cc04de04d9b5fe6657238c7Jean Chalard                spellCheckerProximityInfo.setProximityInfoNative(
72f098fbbef324df034cc04de04d9b5fe6657238c7Jean Chalard                        SpellCheckerProximityInfo.ROW_SIZE,
730e1f656c1be7f2916cf57c94d99b001795856270Yusuke Nojima                        480, 300, 10, 3, SpellCheckerProximityInfo.PROXIMITY,
74ad35835baccb4101c3d8766fadbf4d127e41b6cbYusuke Nojima                        0, null, null, null, null, null, null, null, null);
75f098fbbef324df034cc04de04d9b5fe6657238c7Jean Chalard        return spellCheckerProximityInfo;
76f098fbbef324df034cc04de04d9b5fe6657238c7Jean Chalard    }
77f098fbbef324df034cc04de04d9b5fe6657238c7Jean Chalard
788fbd55229243cb66c03d5ea1f79dfb39f596590dsatok    private int mNativeProximityInfo;
79eaef1c500703b4ee378821884c7b108815ed2983Ken Wakasa    static {
80eaef1c500703b4ee378821884c7b108815ed2983Ken Wakasa        Utils.loadNativeLibrary();
81eaef1c500703b4ee378821884c7b108815ed2983Ken Wakasa    }
828fbd55229243cb66c03d5ea1f79dfb39f596590dsatok    private native int setProximityInfoNative(int maxProximityCharsSize, int displayWidth,
830e1f656c1be7f2916cf57c94d99b001795856270Yusuke Nojima            int displayHeight, int gridWidth, int gridHeight, int[] proximityCharsArray,
840e1f656c1be7f2916cf57c94d99b001795856270Yusuke Nojima            int keyCount, int[] keyXCoordinates, int[] keyYCoordinates,
85ad35835baccb4101c3d8766fadbf4d127e41b6cbYusuke Nojima            int[] keyWidths, int[] keyHeights, int[] keyCharCodes,
86ad35835baccb4101c3d8766fadbf4d127e41b6cbYusuke Nojima            float[] sweetSpotCenterX, float[] sweetSpotCenterY, float[] sweetSpotRadii);
878fbd55229243cb66c03d5ea1f79dfb39f596590dsatok    private native void releaseProximityInfoNative(int nativeProximityInfo);
888fbd55229243cb66c03d5ea1f79dfb39f596590dsatok
890d5494c66ac3e5947040e8148091163a1c8716f7satok    private final void setProximityInfo(int[][] gridNeighborKeyIndexes, int keyboardWidth,
90294e1b4a5abb86e58deefc8eee40e6b661524b28Yusuke Nojima            int keyboardHeight, List<Key> keys,
91294e1b4a5abb86e58deefc8eee40e6b661524b28Yusuke Nojima            TouchPositionCorrection touchPositionCorrection) {
928fbd55229243cb66c03d5ea1f79dfb39f596590dsatok        int[] proximityCharsArray = new int[mGridSize * MAX_PROXIMITY_CHARS_SIZE];
93817e517e463cb32726ff5a62196ac8744848e29bsatok        Arrays.fill(proximityCharsArray, KeyDetector.NOT_A_CODE);
948fbd55229243cb66c03d5ea1f79dfb39f596590dsatok        for (int i = 0; i < mGridSize; ++i) {
95817e517e463cb32726ff5a62196ac8744848e29bsatok            final int proximityCharsLength = gridNeighborKeyIndexes[i].length;
96817e517e463cb32726ff5a62196ac8744848e29bsatok            for (int j = 0; j < proximityCharsLength; ++j) {
97817e517e463cb32726ff5a62196ac8744848e29bsatok                proximityCharsArray[i * MAX_PROXIMITY_CHARS_SIZE + j] =
98817e517e463cb32726ff5a62196ac8744848e29bsatok                        keys.get(gridNeighborKeyIndexes[i][j]).mCode;
998fbd55229243cb66c03d5ea1f79dfb39f596590dsatok            }
1008fbd55229243cb66c03d5ea1f79dfb39f596590dsatok        }
1010e1f656c1be7f2916cf57c94d99b001795856270Yusuke Nojima        final int keyCount = keys.size();
102ad35835baccb4101c3d8766fadbf4d127e41b6cbYusuke Nojima        final int[] keyXCoordinates = new int[keyCount];
103ad35835baccb4101c3d8766fadbf4d127e41b6cbYusuke Nojima        final int[] keyYCoordinates = new int[keyCount];
104ad35835baccb4101c3d8766fadbf4d127e41b6cbYusuke Nojima        final int[] keyWidths = new int[keyCount];
105ad35835baccb4101c3d8766fadbf4d127e41b6cbYusuke Nojima        final int[] keyHeights = new int[keyCount];
106ad35835baccb4101c3d8766fadbf4d127e41b6cbYusuke Nojima        final int[] keyCharCodes = new int[keyCount];
1070e1f656c1be7f2916cf57c94d99b001795856270Yusuke Nojima        for (int i = 0; i < keyCount; ++i) {
1080e1f656c1be7f2916cf57c94d99b001795856270Yusuke Nojima            final Key key = keys.get(i);
1090e1f656c1be7f2916cf57c94d99b001795856270Yusuke Nojima            keyXCoordinates[i] = key.mX;
1100e1f656c1be7f2916cf57c94d99b001795856270Yusuke Nojima            keyYCoordinates[i] = key.mY;
1110e1f656c1be7f2916cf57c94d99b001795856270Yusuke Nojima            keyWidths[i] = key.mWidth;
1120e1f656c1be7f2916cf57c94d99b001795856270Yusuke Nojima            keyHeights[i] = key.mHeight;
1130e1f656c1be7f2916cf57c94d99b001795856270Yusuke Nojima            keyCharCodes[i] = key.mCode;
1140e1f656c1be7f2916cf57c94d99b001795856270Yusuke Nojima        }
115ad35835baccb4101c3d8766fadbf4d127e41b6cbYusuke Nojima
116294e1b4a5abb86e58deefc8eee40e6b661524b28Yusuke Nojima        float[] sweetSpotCenterXs = null;
117294e1b4a5abb86e58deefc8eee40e6b661524b28Yusuke Nojima        float[] sweetSpotCenterYs = null;
118294e1b4a5abb86e58deefc8eee40e6b661524b28Yusuke Nojima        float[] sweetSpotRadii = null;
119294e1b4a5abb86e58deefc8eee40e6b661524b28Yusuke Nojima
120294e1b4a5abb86e58deefc8eee40e6b661524b28Yusuke Nojima        if (touchPositionCorrection != null && touchPositionCorrection.isValid()) {
121294e1b4a5abb86e58deefc8eee40e6b661524b28Yusuke Nojima            sweetSpotCenterXs = new float[keyCount];
122294e1b4a5abb86e58deefc8eee40e6b661524b28Yusuke Nojima            sweetSpotCenterYs = new float[keyCount];
123294e1b4a5abb86e58deefc8eee40e6b661524b28Yusuke Nojima            sweetSpotRadii = new float[keyCount];
124294e1b4a5abb86e58deefc8eee40e6b661524b28Yusuke Nojima            calculateSweetSpot(keys, touchPositionCorrection,
125294e1b4a5abb86e58deefc8eee40e6b661524b28Yusuke Nojima                    sweetSpotCenterXs, sweetSpotCenterYs, sweetSpotRadii);
126ad35835baccb4101c3d8766fadbf4d127e41b6cbYusuke Nojima        }
127ad35835baccb4101c3d8766fadbf4d127e41b6cbYusuke Nojima
1288fbd55229243cb66c03d5ea1f79dfb39f596590dsatok        mNativeProximityInfo = setProximityInfoNative(MAX_PROXIMITY_CHARS_SIZE,
1290e1f656c1be7f2916cf57c94d99b001795856270Yusuke Nojima                keyboardWidth, keyboardHeight, mGridWidth, mGridHeight, proximityCharsArray,
1301c923d8581fb2af76add7f00802cea4d26ac4e84Yusuke Nojima                keyCount, keyXCoordinates, keyYCoordinates, keyWidths, keyHeights, keyCharCodes,
131ad35835baccb4101c3d8766fadbf4d127e41b6cbYusuke Nojima                sweetSpotCenterXs, sweetSpotCenterYs, sweetSpotRadii);
132ad35835baccb4101c3d8766fadbf4d127e41b6cbYusuke Nojima    }
133ad35835baccb4101c3d8766fadbf4d127e41b6cbYusuke Nojima
134294e1b4a5abb86e58deefc8eee40e6b661524b28Yusuke Nojima    private void calculateSweetSpot(List<Key> keys, TouchPositionCorrection touchPositionCorrection,
135294e1b4a5abb86e58deefc8eee40e6b661524b28Yusuke Nojima            float[] sweetSpotCenterXs, float[] sweetSpotCenterYs, float[] sweetSpotRadii) {
136ad35835baccb4101c3d8766fadbf4d127e41b6cbYusuke Nojima        final int keyCount = keys.size();
137294e1b4a5abb86e58deefc8eee40e6b661524b28Yusuke Nojima        final float[] xs = touchPositionCorrection.mXs;
138294e1b4a5abb86e58deefc8eee40e6b661524b28Yusuke Nojima        final float[] ys = touchPositionCorrection.mYs;
139294e1b4a5abb86e58deefc8eee40e6b661524b28Yusuke Nojima        final float[] radii = touchPositionCorrection.mRadii;
140ad35835baccb4101c3d8766fadbf4d127e41b6cbYusuke Nojima        for (int i = 0; i < keyCount; ++i) {
141ad35835baccb4101c3d8766fadbf4d127e41b6cbYusuke Nojima            final Key key = keys.get(i);
142ad35835baccb4101c3d8766fadbf4d127e41b6cbYusuke Nojima            final Rect hitBox = key.mHitBox;
143ad35835baccb4101c3d8766fadbf4d127e41b6cbYusuke Nojima            final int row = hitBox.top / mKeyHeight;
144294e1b4a5abb86e58deefc8eee40e6b661524b28Yusuke Nojima            if (row < radii.length) {
145ad35835baccb4101c3d8766fadbf4d127e41b6cbYusuke Nojima                final float hitBoxCenterX = (hitBox.left + hitBox.right) * 0.5f;
146ad35835baccb4101c3d8766fadbf4d127e41b6cbYusuke Nojima                final float hitBoxCenterY = (hitBox.top + hitBox.bottom) * 0.5f;
147ad35835baccb4101c3d8766fadbf4d127e41b6cbYusuke Nojima                final float hitBoxWidth = hitBox.right - hitBox.left;
148ad35835baccb4101c3d8766fadbf4d127e41b6cbYusuke Nojima                final float hitBoxHeight = hitBox.bottom - hitBox.top;
149294e1b4a5abb86e58deefc8eee40e6b661524b28Yusuke Nojima                final float x = xs[row];
150294e1b4a5abb86e58deefc8eee40e6b661524b28Yusuke Nojima                final float y = ys[row];
151294e1b4a5abb86e58deefc8eee40e6b661524b28Yusuke Nojima                final float radius = radii[row];
152ad35835baccb4101c3d8766fadbf4d127e41b6cbYusuke Nojima                sweetSpotCenterXs[i] = hitBoxCenterX + x * hitBoxWidth;
153ad35835baccb4101c3d8766fadbf4d127e41b6cbYusuke Nojima                sweetSpotCenterYs[i] = hitBoxCenterY + y * hitBoxHeight;
154ad35835baccb4101c3d8766fadbf4d127e41b6cbYusuke Nojima                sweetSpotRadii[i] = radius
155ad35835baccb4101c3d8766fadbf4d127e41b6cbYusuke Nojima                        * (float)Math.sqrt(hitBoxWidth * hitBoxWidth + hitBoxHeight * hitBoxHeight);
156ad35835baccb4101c3d8766fadbf4d127e41b6cbYusuke Nojima            }
157ad35835baccb4101c3d8766fadbf4d127e41b6cbYusuke Nojima        }
1588fbd55229243cb66c03d5ea1f79dfb39f596590dsatok    }
1598fbd55229243cb66c03d5ea1f79dfb39f596590dsatok
1600d5494c66ac3e5947040e8148091163a1c8716f7satok    public int getNativeProximityInfo() {
1618fbd55229243cb66c03d5ea1f79dfb39f596590dsatok        return mNativeProximityInfo;
1628fbd55229243cb66c03d5ea1f79dfb39f596590dsatok    }
1638fbd55229243cb66c03d5ea1f79dfb39f596590dsatok
1648fbd55229243cb66c03d5ea1f79dfb39f596590dsatok    @Override
1658fbd55229243cb66c03d5ea1f79dfb39f596590dsatok    protected void finalize() throws Throwable {
1668fbd55229243cb66c03d5ea1f79dfb39f596590dsatok        try {
1678fbd55229243cb66c03d5ea1f79dfb39f596590dsatok            if (mNativeProximityInfo != 0) {
1688fbd55229243cb66c03d5ea1f79dfb39f596590dsatok                releaseProximityInfoNative(mNativeProximityInfo);
1698fbd55229243cb66c03d5ea1f79dfb39f596590dsatok                mNativeProximityInfo = 0;
1708fbd55229243cb66c03d5ea1f79dfb39f596590dsatok            }
1718fbd55229243cb66c03d5ea1f79dfb39f596590dsatok        } finally {
1728fbd55229243cb66c03d5ea1f79dfb39f596590dsatok            super.finalize();
1738fbd55229243cb66c03d5ea1f79dfb39f596590dsatok        }
1748fbd55229243cb66c03d5ea1f79dfb39f596590dsatok    }
1750d5494c66ac3e5947040e8148091163a1c8716f7satok
176294e1b4a5abb86e58deefc8eee40e6b661524b28Yusuke Nojima    private void computeNearestNeighbors(int defaultWidth, List<Key> keys,
177294e1b4a5abb86e58deefc8eee40e6b661524b28Yusuke Nojima            TouchPositionCorrection touchPositionCorrection) {
1780d5494c66ac3e5947040e8148091163a1c8716f7satok        final int thresholdBase = (int) (defaultWidth * SEARCH_DISTANCE);
1790d5494c66ac3e5947040e8148091163a1c8716f7satok        final int threshold = thresholdBase * thresholdBase;
1800d5494c66ac3e5947040e8148091163a1c8716f7satok        // Round-up so we don't have any pixels outside the grid
1810d5494c66ac3e5947040e8148091163a1c8716f7satok        final int[] indices = new int[keys.size()];
1820d5494c66ac3e5947040e8148091163a1c8716f7satok        final int gridWidth = mGridWidth * mCellWidth;
1830d5494c66ac3e5947040e8148091163a1c8716f7satok        final int gridHeight = mGridHeight * mCellHeight;
1840d5494c66ac3e5947040e8148091163a1c8716f7satok        for (int x = 0; x < gridWidth; x += mCellWidth) {
1850d5494c66ac3e5947040e8148091163a1c8716f7satok            for (int y = 0; y < gridHeight; y += mCellHeight) {
1860d5494c66ac3e5947040e8148091163a1c8716f7satok                final int centerX = x + mCellWidth / 2;
1870d5494c66ac3e5947040e8148091163a1c8716f7satok                final int centerY = y + mCellHeight / 2;
1880d5494c66ac3e5947040e8148091163a1c8716f7satok                int count = 0;
1890d5494c66ac3e5947040e8148091163a1c8716f7satok                for (int i = 0; i < keys.size(); i++) {
1900d5494c66ac3e5947040e8148091163a1c8716f7satok                    final Key key = keys.get(i);
19118453d69e0ef7631500826bf4e0b6f684c948cb3Tadashi G. Takaoka                    if (key.isSpacer()) continue;
1920d5494c66ac3e5947040e8148091163a1c8716f7satok                    if (key.squaredDistanceToEdge(centerX, centerY) < threshold)
1930d5494c66ac3e5947040e8148091163a1c8716f7satok                        indices[count++] = i;
1940d5494c66ac3e5947040e8148091163a1c8716f7satok                }
1950d5494c66ac3e5947040e8148091163a1c8716f7satok                final int[] cell = new int[count];
1960d5494c66ac3e5947040e8148091163a1c8716f7satok                System.arraycopy(indices, 0, cell, 0, count);
1970d5494c66ac3e5947040e8148091163a1c8716f7satok                mGridNeighbors[(y / mCellHeight) * mGridWidth + (x / mCellWidth)] = cell;
1980d5494c66ac3e5947040e8148091163a1c8716f7satok            }
1990d5494c66ac3e5947040e8148091163a1c8716f7satok        }
200294e1b4a5abb86e58deefc8eee40e6b661524b28Yusuke Nojima        setProximityInfo(mGridNeighbors, mKeyboardMinWidth, mKeyboardHeight, keys,
201294e1b4a5abb86e58deefc8eee40e6b661524b28Yusuke Nojima                touchPositionCorrection);
2020d5494c66ac3e5947040e8148091163a1c8716f7satok    }
2030d5494c66ac3e5947040e8148091163a1c8716f7satok
2040d5494c66ac3e5947040e8148091163a1c8716f7satok    public int[] getNearestKeys(int x, int y) {
2050d5494c66ac3e5947040e8148091163a1c8716f7satok        if (mGridNeighbors == null) {
2060d5494c66ac3e5947040e8148091163a1c8716f7satok            return EMPTY_INT_ARRAY;
2070d5494c66ac3e5947040e8148091163a1c8716f7satok        }
2080d5494c66ac3e5947040e8148091163a1c8716f7satok        if (x >= 0 && x < mKeyboardMinWidth && y >= 0 && y < mKeyboardHeight) {
2090d5494c66ac3e5947040e8148091163a1c8716f7satok            int index = (y /  mCellHeight) * mGridWidth + (x / mCellWidth);
2100d5494c66ac3e5947040e8148091163a1c8716f7satok            if (index < mGridSize) {
2110d5494c66ac3e5947040e8148091163a1c8716f7satok                return mGridNeighbors[index];
2120d5494c66ac3e5947040e8148091163a1c8716f7satok            }
2130d5494c66ac3e5947040e8148091163a1c8716f7satok        }
2140d5494c66ac3e5947040e8148091163a1c8716f7satok        return EMPTY_INT_ARRAY;
2150d5494c66ac3e5947040e8148091163a1c8716f7satok    }
2168fbd55229243cb66c03d5ea1f79dfb39f596590dsatok}
217