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