18fbd55229243cb66c03d5ea1f79dfb39f596590dsatok/* 28632bff2d5a8e1160989008dea6eff4b94b065ddTadashi G. Takaoka * Copyright (C) 2011 The Android Open Source Project 38fbd55229243cb66c03d5ea1f79dfb39f596590dsatok * 48aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * Licensed under the Apache License, Version 2.0 (the "License"); 58aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * you may not use this file except in compliance with the License. 68aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * You may obtain a copy of the License at 78fbd55229243cb66c03d5ea1f79dfb39f596590dsatok * 88aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * http://www.apache.org/licenses/LICENSE-2.0 98fbd55229243cb66c03d5ea1f79dfb39f596590dsatok * 108fbd55229243cb66c03d5ea1f79dfb39f596590dsatok * Unless required by applicable law or agreed to in writing, software 118aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * distributed under the License is distributed on an "AS IS" BASIS, 128aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 138aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * See the License for the specific language governing permissions and 148aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * limitations under the License. 158fbd55229243cb66c03d5ea1f79dfb39f596590dsatok */ 168fbd55229243cb66c03d5ea1f79dfb39f596590dsatok 178fbd55229243cb66c03d5ea1f79dfb39f596590dsatokpackage com.android.inputmethod.keyboard; 188fbd55229243cb66c03d5ea1f79dfb39f596590dsatok 19ad35835baccb4101c3d8766fadbf4d127e41b6cbYusuke Nojimaimport android.graphics.Rect; 207ef1dabd92a9dae042965cd10d08a2cd47455dccsatokimport android.text.TextUtils; 21f9aa99c1c94908c47aed17ba47795668d68d4088Tadashi G. Takaokaimport android.util.Log; 22ad35835baccb4101c3d8766fadbf4d127e41b6cbYusuke Nojima 2335ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaokaimport com.android.inputmethod.keyboard.internal.TouchPositionCorrection; 24ac78633be28e8990fc3b3a8de192c80966e746e3Tadashi G. Takaokaimport com.android.inputmethod.latin.Constants; 25e28eba5074664d5716b8e58b8d0a235746b261ebKen Wakasaimport com.android.inputmethod.latin.utils.JniUtils; 26eaef1c500703b4ee378821884c7b108815ed2983Ken Wakasa 27817e517e463cb32726ff5a62196ac8744848e29bsatokimport java.util.Arrays; 28817e517e463cb32726ff5a62196ac8744848e29bsatok 296e3f30726621ff9b64977d67ba9df955024efa04Tadashi G. Takaokapublic class ProximityInfo { 30f9aa99c1c94908c47aed17ba47795668d68d4088Tadashi G. Takaoka private static final String TAG = ProximityInfo.class.getSimpleName(); 31f9aa99c1c94908c47aed17ba47795668d68d4088Tadashi G. Takaoka private static final boolean DEBUG = false; 32f9aa99c1c94908c47aed17ba47795668d68d4088Tadashi G. Takaoka 336c22439bf80da08576e86c1282afc5cfa431e235Ken Wakasa // Must be equal to MAX_PROXIMITY_CHARS_SIZE in native/jni/src/defines.h 348fbd55229243cb66c03d5ea1f79dfb39f596590dsatok public static final int MAX_PROXIMITY_CHARS_SIZE = 16; 350d5494c66ac3e5947040e8148091163a1c8716f7satok /** Number of key widths from current touch point to search for nearest keys. */ 36f9aa99c1c94908c47aed17ba47795668d68d4088Tadashi G. Takaoka private static final float SEARCH_DISTANCE = 1.2f; 370c0ca874febee38fb5cb2c85c11ddd46cdf2b859Tadashi G. Takaoka private static final Key[] EMPTY_KEY_ARRAY = new Key[0]; 3817752016713b92a55e9c2356d07b7ed51c67416bTadashi G. Takaoka private static final float DEFAULT_TOUCH_POSITION_CORRECTION_RADIUS = 0.15f; 398fbd55229243cb66c03d5ea1f79dfb39f596590dsatok 408fbd55229243cb66c03d5ea1f79dfb39f596590dsatok private final int mGridWidth; 418fbd55229243cb66c03d5ea1f79dfb39f596590dsatok private final int mGridHeight; 428fbd55229243cb66c03d5ea1f79dfb39f596590dsatok private final int mGridSize; 430d5494c66ac3e5947040e8148091163a1c8716f7satok private final int mCellWidth; 440d5494c66ac3e5947040e8148091163a1c8716f7satok private final int mCellHeight; 450d5494c66ac3e5947040e8148091163a1c8716f7satok // TODO: Find a proper name for mKeyboardMinWidth 460d5494c66ac3e5947040e8148091163a1c8716f7satok private final int mKeyboardMinWidth; 470d5494c66ac3e5947040e8148091163a1c8716f7satok private final int mKeyboardHeight; 48a70ee6e3b3fe65acab205b935ebd52e7bb0eccb8satok private final int mMostCommonKeyWidth; 4917752016713b92a55e9c2356d07b7ed51c67416bTadashi G. Takaoka private final int mMostCommonKeyHeight; 502866da88a781c501c6f83cfd91eca0d95e25f6e5Tadashi G. Takaoka private final Key[] mKeys; 510c0ca874febee38fb5cb2c85c11ddd46cdf2b859Tadashi G. Takaoka private final Key[][] mGridNeighbors; 527ef1dabd92a9dae042965cd10d08a2cd47455dccsatok private final String mLocaleStr; 538fbd55229243cb66c03d5ea1f79dfb39f596590dsatok 5435ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaoka ProximityInfo(final String localeStr, final int gridWidth, final int gridHeight, 5535ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaoka final int minWidth, final int height, final int mostCommonKeyWidth, 5635ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaoka final int mostCommonKeyHeight, final Key[] keys, 5735ff94547c16c84c5b6fafdae0b4a683be782b97Tadashi G. Takaoka final TouchPositionCorrection touchPositionCorrection) { 587ef1dabd92a9dae042965cd10d08a2cd47455dccsatok if (TextUtils.isEmpty(localeStr)) { 597ef1dabd92a9dae042965cd10d08a2cd47455dccsatok mLocaleStr = ""; 607ef1dabd92a9dae042965cd10d08a2cd47455dccsatok } else { 617ef1dabd92a9dae042965cd10d08a2cd47455dccsatok mLocaleStr = localeStr; 627ef1dabd92a9dae042965cd10d08a2cd47455dccsatok } 638fbd55229243cb66c03d5ea1f79dfb39f596590dsatok mGridWidth = gridWidth; 648fbd55229243cb66c03d5ea1f79dfb39f596590dsatok mGridHeight = gridHeight; 658fbd55229243cb66c03d5ea1f79dfb39f596590dsatok mGridSize = mGridWidth * mGridHeight; 660d5494c66ac3e5947040e8148091163a1c8716f7satok mCellWidth = (minWidth + mGridWidth - 1) / mGridWidth; 670d5494c66ac3e5947040e8148091163a1c8716f7satok mCellHeight = (height + mGridHeight - 1) / mGridHeight; 680d5494c66ac3e5947040e8148091163a1c8716f7satok mKeyboardMinWidth = minWidth; 690d5494c66ac3e5947040e8148091163a1c8716f7satok mKeyboardHeight = height; 7017752016713b92a55e9c2356d07b7ed51c67416bTadashi G. Takaoka mMostCommonKeyHeight = mostCommonKeyHeight; 71a70ee6e3b3fe65acab205b935ebd52e7bb0eccb8satok mMostCommonKeyWidth = mostCommonKeyWidth; 722866da88a781c501c6f83cfd91eca0d95e25f6e5Tadashi G. Takaoka mKeys = keys; 730c0ca874febee38fb5cb2c85c11ddd46cdf2b859Tadashi G. Takaoka mGridNeighbors = new Key[mGridSize][]; 740d5494c66ac3e5947040e8148091163a1c8716f7satok if (minWidth == 0 || height == 0) { 752affaf91a04d63e0994102299816014a8bbe11e1Tadashi G. Takaoka // No proximity required. Keyboard might be more keys keyboard. 760d5494c66ac3e5947040e8148091163a1c8716f7satok return; 770d5494c66ac3e5947040e8148091163a1c8716f7satok } 782866da88a781c501c6f83cfd91eca0d95e25f6e5Tadashi G. Takaoka computeNearestNeighbors(); 796ca50d99208efdbcad96b3260fe7592bf95a6b00Tadashi G. Takaoka mNativeProximityInfo = createNativeProximityInfo(touchPositionCorrection); 802866da88a781c501c6f83cfd91eca0d95e25f6e5Tadashi G. Takaoka } 812866da88a781c501c6f83cfd91eca0d95e25f6e5Tadashi G. Takaoka 825fdcd7d5cd9d39d41568aa1412a4b1a866c05d3aTadashi G. Takaoka private long mNativeProximityInfo; 83eaef1c500703b4ee378821884c7b108815ed2983Ken Wakasa static { 84cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka JniUtils.loadNativeLibrary(); 85eaef1c500703b4ee378821884c7b108815ed2983Ken Wakasa } 86e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok 87308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka // TODO: Stop passing proximityCharsArray 886c22439bf80da08576e86c1282afc5cfa431e235Ken Wakasa private static native long setProximityInfoNative(String locale, 895db594abbad2d9e8d2cf1aa6e417aa50ffc5dfc1Ken Wakasa int displayWidth, int displayHeight, int gridWidth, int gridHeight, 9087fdde6ec48844ddbb482c50fbda226c63ca5e85Keisuke Kuroynagi int mostCommonKeyWidth, int mostCommonKeyHeight, int[] proximityCharsArray, 9187fdde6ec48844ddbb482c50fbda226c63ca5e85Keisuke Kuroynagi int keyCount, int[] keyXCoordinates, int[] keyYCoordinates, int[] keyWidths, 9287fdde6ec48844ddbb482c50fbda226c63ca5e85Keisuke Kuroynagi int[] keyHeights, int[] keyCharCodes, float[] sweetSpotCenterXs, 9387fdde6ec48844ddbb482c50fbda226c63ca5e85Keisuke Kuroynagi float[] sweetSpotCenterYs, float[] sweetSpotRadii); 94e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok 955db594abbad2d9e8d2cf1aa6e417aa50ffc5dfc1Ken Wakasa private static native void releaseProximityInfoNative(long nativeProximityInfo); 968fbd55229243cb66c03d5ea1f79dfb39f596590dsatok 97308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka private static boolean needsProximityInfo(final Key key) { 98308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka // Don't include special keys into ProximityInfo. 997dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka return key.getCode() >= Constants.CODE_SPACE; 100308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka } 101308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka 102308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka private static int getProximityInfoKeysCount(final Key[] keys) { 103308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka int count = 0; 104308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka for (final Key key : keys) { 105308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka if (needsProximityInfo(key)) { 106308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka count++; 107308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka } 108308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka } 109308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka return count; 110308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka } 111308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka 112308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka private long createNativeProximityInfo(final TouchPositionCorrection touchPositionCorrection) { 1132866da88a781c501c6f83cfd91eca0d95e25f6e5Tadashi G. Takaoka final Key[][] gridNeighborKeys = mGridNeighbors; 1144a019a9c96b3a628265ef49f5522f20aeb5856cfTadashi G. Takaoka final int[] proximityCharsArray = new int[mGridSize * MAX_PROXIMITY_CHARS_SIZE]; 115ac78633be28e8990fc3b3a8de192c80966e746e3Tadashi G. Takaoka Arrays.fill(proximityCharsArray, Constants.NOT_A_CODE); 1168fbd55229243cb66c03d5ea1f79dfb39f596590dsatok for (int i = 0; i < mGridSize; ++i) { 1170c0ca874febee38fb5cb2c85c11ddd46cdf2b859Tadashi G. Takaoka final int proximityCharsLength = gridNeighborKeys[i].length; 118308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka int infoIndex = i * MAX_PROXIMITY_CHARS_SIZE; 119817e517e463cb32726ff5a62196ac8744848e29bsatok for (int j = 0; j < proximityCharsLength; ++j) { 120308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka final Key neighborKey = gridNeighborKeys[i][j]; 121308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka // Excluding from proximityCharsArray 122308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka if (!needsProximityInfo(neighborKey)) { 123308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka continue; 124308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka } 1257dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka proximityCharsArray[infoIndex] = neighborKey.getCode(); 126308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka infoIndex++; 127308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka } 128308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka } 129308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka if (DEBUG) { 130308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka final StringBuilder sb = new StringBuilder(); 131308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka for (int i = 0; i < mGridSize; i++) { 132308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka sb.setLength(0); 133308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka for (int j = 0; j < MAX_PROXIMITY_CHARS_SIZE; j++) { 134308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka final int code = proximityCharsArray[i * MAX_PROXIMITY_CHARS_SIZE + j]; 135308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka if (code == Constants.NOT_A_CODE) { 136308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka break; 137308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka } 138308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka if (sb.length() > 0) sb.append(" "); 139308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka sb.append(Constants.printableCode(code)); 140308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka } 141308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka Log.d(TAG, "proxmityChars["+i+"]: " + sb); 1428fbd55229243cb66c03d5ea1f79dfb39f596590dsatok } 1438fbd55229243cb66c03d5ea1f79dfb39f596590dsatok } 144308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka 145308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka final Key[] keys = mKeys; 146308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka final int keyCount = getProximityInfoKeysCount(keys); 147ad35835baccb4101c3d8766fadbf4d127e41b6cbYusuke Nojima final int[] keyXCoordinates = new int[keyCount]; 148ad35835baccb4101c3d8766fadbf4d127e41b6cbYusuke Nojima final int[] keyYCoordinates = new int[keyCount]; 149ad35835baccb4101c3d8766fadbf4d127e41b6cbYusuke Nojima final int[] keyWidths = new int[keyCount]; 150ad35835baccb4101c3d8766fadbf4d127e41b6cbYusuke Nojima final int[] keyHeights = new int[keyCount]; 151ad35835baccb4101c3d8766fadbf4d127e41b6cbYusuke Nojima final int[] keyCharCodes = new int[keyCount]; 1526cb72b0f36e0fc58988d01b75948e456280b27a4Tadashi G. Takaoka final float[] sweetSpotCenterXs; 1536cb72b0f36e0fc58988d01b75948e456280b27a4Tadashi G. Takaoka final float[] sweetSpotCenterYs; 1546cb72b0f36e0fc58988d01b75948e456280b27a4Tadashi G. Takaoka final float[] sweetSpotRadii; 1552d5c40d8733d0ae0a9280682bba856c67c96344dsatok 156308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka for (int infoIndex = 0, keyIndex = 0; keyIndex < keys.length; keyIndex++) { 157308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka final Key key = keys[keyIndex]; 158308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka // Excluding from key coordinate arrays 159308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka if (!needsProximityInfo(key)) { 160308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka continue; 161308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka } 1627dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka keyXCoordinates[infoIndex] = key.getX(); 1637dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka keyYCoordinates[infoIndex] = key.getY(); 1647dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka keyWidths[infoIndex] = key.getWidth(); 1657dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka keyHeights[infoIndex] = key.getHeight(); 1667dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka keyCharCodes[infoIndex] = key.getCode(); 167308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka infoIndex++; 1682d5c40d8733d0ae0a9280682bba856c67c96344dsatok } 1692d5c40d8733d0ae0a9280682bba856c67c96344dsatok 1706cb72b0f36e0fc58988d01b75948e456280b27a4Tadashi G. Takaoka if (touchPositionCorrection != null && touchPositionCorrection.isValid()) { 171f9aa99c1c94908c47aed17ba47795668d68d4088Tadashi G. Takaoka if (DEBUG) { 172f9aa99c1c94908c47aed17ba47795668d68d4088Tadashi G. Takaoka Log.d(TAG, "touchPositionCorrection: ON"); 173f9aa99c1c94908c47aed17ba47795668d68d4088Tadashi G. Takaoka } 1746cb72b0f36e0fc58988d01b75948e456280b27a4Tadashi G. Takaoka sweetSpotCenterXs = new float[keyCount]; 1756cb72b0f36e0fc58988d01b75948e456280b27a4Tadashi G. Takaoka sweetSpotCenterYs = new float[keyCount]; 1766cb72b0f36e0fc58988d01b75948e456280b27a4Tadashi G. Takaoka sweetSpotRadii = new float[keyCount]; 177f9aa99c1c94908c47aed17ba47795668d68d4088Tadashi G. Takaoka final int rows = touchPositionCorrection.getRows(); 17817752016713b92a55e9c2356d07b7ed51c67416bTadashi G. Takaoka final float defaultRadius = DEFAULT_TOUCH_POSITION_CORRECTION_RADIUS 17917752016713b92a55e9c2356d07b7ed51c67416bTadashi G. Takaoka * (float)Math.hypot(mMostCommonKeyWidth, mMostCommonKeyHeight); 180308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka for (int infoIndex = 0, keyIndex = 0; keyIndex < keys.length; keyIndex++) { 181308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka final Key key = keys[keyIndex]; 182308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka // Excluding from touch position correction arrays 183308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka if (!needsProximityInfo(key)) { 184308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka continue; 185308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka } 1867dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka final Rect hitBox = key.getHitBox(); 187308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka sweetSpotCenterXs[infoIndex] = hitBox.exactCenterX(); 188308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka sweetSpotCenterYs[infoIndex] = hitBox.exactCenterY(); 189308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka sweetSpotRadii[infoIndex] = defaultRadius; 19017752016713b92a55e9c2356d07b7ed51c67416bTadashi G. Takaoka final int row = hitBox.top / mMostCommonKeyHeight; 191f9aa99c1c94908c47aed17ba47795668d68d4088Tadashi G. Takaoka if (row < rows) { 19260ee192b07a9725e97b3105acfc1eea5f5f25b9fTadashi G. Takaoka final int hitBoxWidth = hitBox.width(); 19360ee192b07a9725e97b3105acfc1eea5f5f25b9fTadashi G. Takaoka final int hitBoxHeight = hitBox.height(); 19417752016713b92a55e9c2356d07b7ed51c67416bTadashi G. Takaoka final float hitBoxDiagonal = (float)Math.hypot(hitBoxWidth, hitBoxHeight); 195308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka sweetSpotCenterXs[infoIndex] += 196308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka touchPositionCorrection.getX(row) * hitBoxWidth; 197308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka sweetSpotCenterYs[infoIndex] += 198308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka touchPositionCorrection.getY(row) * hitBoxHeight; 199308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka sweetSpotRadii[infoIndex] = 200308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka touchPositionCorrection.getRadius(row) * hitBoxDiagonal; 201370674422a47f20979b1fd30aaaed4f2423b28a3satok } 202f9aa99c1c94908c47aed17ba47795668d68d4088Tadashi G. Takaoka if (DEBUG) { 203f9aa99c1c94908c47aed17ba47795668d68d4088Tadashi G. Takaoka Log.d(TAG, String.format( 204308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka " [%2d] row=%d x/y/r=%7.2f/%7.2f/%5.2f %s code=%s", infoIndex, row, 205308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka sweetSpotCenterXs[infoIndex], sweetSpotCenterYs[infoIndex], 206308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka sweetSpotRadii[infoIndex], (row < rows ? "correct" : "default"), 2077dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka Constants.printableCode(key.getCode()))); 208f9aa99c1c94908c47aed17ba47795668d68d4088Tadashi G. Takaoka } 209308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka infoIndex++; 210370674422a47f20979b1fd30aaaed4f2423b28a3satok } 2116cb72b0f36e0fc58988d01b75948e456280b27a4Tadashi G. Takaoka } else { 2126cb72b0f36e0fc58988d01b75948e456280b27a4Tadashi G. Takaoka sweetSpotCenterXs = sweetSpotCenterYs = sweetSpotRadii = null; 213f9aa99c1c94908c47aed17ba47795668d68d4088Tadashi G. Takaoka if (DEBUG) { 214f9aa99c1c94908c47aed17ba47795668d68d4088Tadashi G. Takaoka Log.d(TAG, "touchPositionCorrection: OFF"); 215f9aa99c1c94908c47aed17ba47795668d68d4088Tadashi G. Takaoka } 2166cb72b0f36e0fc58988d01b75948e456280b27a4Tadashi G. Takaoka } 2176cb72b0f36e0fc58988d01b75948e456280b27a4Tadashi G. Takaoka 218308aaff80cf7b2c17d286aaab4cc223269b2bc52Tadashi G. Takaoka // TODO: Stop passing proximityCharsArray 2196c22439bf80da08576e86c1282afc5cfa431e235Ken Wakasa return setProximityInfoNative(mLocaleStr, mKeyboardMinWidth, mKeyboardHeight, 22087fdde6ec48844ddbb482c50fbda226c63ca5e85Keisuke Kuroynagi mGridWidth, mGridHeight, mMostCommonKeyWidth, mMostCommonKeyHeight, 22187fdde6ec48844ddbb482c50fbda226c63ca5e85Keisuke Kuroynagi proximityCharsArray, keyCount, keyXCoordinates, keyYCoordinates, keyWidths, 22287fdde6ec48844ddbb482c50fbda226c63ca5e85Keisuke Kuroynagi keyHeights, keyCharCodes, sweetSpotCenterXs, sweetSpotCenterYs, sweetSpotRadii); 223ad35835baccb4101c3d8766fadbf4d127e41b6cbYusuke Nojima } 224ad35835baccb4101c3d8766fadbf4d127e41b6cbYusuke Nojima 2255fdcd7d5cd9d39d41568aa1412a4b1a866c05d3aTadashi G. Takaoka public long getNativeProximityInfo() { 2268fbd55229243cb66c03d5ea1f79dfb39f596590dsatok return mNativeProximityInfo; 2278fbd55229243cb66c03d5ea1f79dfb39f596590dsatok } 2288fbd55229243cb66c03d5ea1f79dfb39f596590dsatok 2298fbd55229243cb66c03d5ea1f79dfb39f596590dsatok @Override 2308fbd55229243cb66c03d5ea1f79dfb39f596590dsatok protected void finalize() throws Throwable { 2318fbd55229243cb66c03d5ea1f79dfb39f596590dsatok try { 2328fbd55229243cb66c03d5ea1f79dfb39f596590dsatok if (mNativeProximityInfo != 0) { 2338fbd55229243cb66c03d5ea1f79dfb39f596590dsatok releaseProximityInfoNative(mNativeProximityInfo); 2348fbd55229243cb66c03d5ea1f79dfb39f596590dsatok mNativeProximityInfo = 0; 2358fbd55229243cb66c03d5ea1f79dfb39f596590dsatok } 2368fbd55229243cb66c03d5ea1f79dfb39f596590dsatok } finally { 2378fbd55229243cb66c03d5ea1f79dfb39f596590dsatok super.finalize(); 2388fbd55229243cb66c03d5ea1f79dfb39f596590dsatok } 2398fbd55229243cb66c03d5ea1f79dfb39f596590dsatok } 2400d5494c66ac3e5947040e8148091163a1c8716f7satok 2412866da88a781c501c6f83cfd91eca0d95e25f6e5Tadashi G. Takaoka private void computeNearestNeighbors() { 2422866da88a781c501c6f83cfd91eca0d95e25f6e5Tadashi G. Takaoka final int defaultWidth = mMostCommonKeyWidth; 243d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard final int keyCount = mKeys.length; 244d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard final int gridSize = mGridNeighbors.length; 245d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard final int threshold = (int) (defaultWidth * SEARCH_DISTANCE); 246d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard final int thresholdSquared = threshold * threshold; 2470d5494c66ac3e5947040e8148091163a1c8716f7satok // Round-up so we don't have any pixels outside the grid 248a7d0ab80a043f222f0e781dc8070cd0d68422b64Jean Chalard final int lastPixelXCoordinate = mGridWidth * mCellWidth - 1; 249a7d0ab80a043f222f0e781dc8070cd0d68422b64Jean Chalard final int lastPixelYCoordinate = mGridHeight * mCellHeight - 1; 250d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard 251d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard // For large layouts, 'neighborsFlatBuffer' is about 80k of memory: gridSize is usually 512, 252d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard // keycount is about 40 and a pointer to a Key is 4 bytes. This contains, for each cell, 253d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard // enough space for as many keys as there are on the keyboard. Hence, every 254d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard // keycount'th element is the start of a new cell, and each of these virtual subarrays 255d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard // start empty with keycount spaces available. This fills up gradually in the loop below. 256d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard // Since in the practice each cell does not have a lot of neighbors, most of this space is 257d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard // actually just empty padding in this fixed-size buffer. 258d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard final Key[] neighborsFlatBuffer = new Key[gridSize * keyCount]; 259d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard final int[] neighborCountPerCell = new int[gridSize]; 260d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard final int halfCellWidth = mCellWidth / 2; 261d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard final int halfCellHeight = mCellHeight / 2; 262d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard for (final Key key : mKeys) { 263d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard if (key.isSpacer()) continue; 264d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard 265d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard/* HOW WE PRE-SELECT THE CELLS (iterate over only the relevant cells, instead of all of them) 266d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard 267d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard We want to compute the distance for keys that are in the cells that are close enough to the 268d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard key border, as this method is performance-critical. These keys are represented with 'star' 269d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard background on the diagram below. Let's consider the Y case first. 270d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard 271d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard We want to select the cells which center falls between the top of the key minus the threshold, 272d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard and the bottom of the key plus the threshold. 273d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard topPixelWithinThreshold is key.mY - threshold, and bottomPixelWithinThreshold is 274d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard key.mY + key.mHeight + threshold. 275d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard 276d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard Then we need to compute the center of the top row that we need to evaluate, as we'll iterate 277d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard from there. 278d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard 279d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard(0,0)----> x 280d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard| .-------------------------------------------. 281d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard| | | | | | | | | | | | | 282d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard| |---+---+---+---+---+---+---+---+---+---+---| .- top of top cell (aligned on the grid) 283d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard| | | | | | | | | | | | | | 284d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard| |-----------+---+---+---+---+---+---+---+---|---' v 285d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard| | | | |***|***|*_________________________ topPixelWithinThreshold | yDeltaToGrid 286d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard| |---+---+---+-----^-+-|-+---+---+---+---+---| ^ 287d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard| | | | |***|*|*|*|*|***|***| | | | ______________________________________ 288d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalardv |---+---+--threshold--|-+---+---+---+---+---| | 289d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard | | | |***|*|*|*|*|***|***| | | | | Starting from key.mY, we substract 290d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalardy |---+---+---+---+-v-+-|-+---+---+---+---+---| | thresholdBase and get the top pixel 291d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard | | | |***|**########------------------- key.mY | within the threshold. We align that on 292d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard |---+---+---+---+--#+---+-#-+---+---+---+---| | the grid by computing the delta to the 293d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard | | | |***|**#|***|*#*|***| | | | | grid, and get the top of the top cell. 294d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard |---+---+---+---+--#+---+-#-+---+---+---+---| | 295d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard | | | |***|**########*|***| | | | | Adding half the cell height to the top 296d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard |---+---+---+---+---+-|-+---+---+---+---+---| | of the top cell, we get the middle of 297d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard | | | |***|***|*|*|***|***| | | | | the top cell (yMiddleOfTopCell). 298d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard |---+---+---+---+---+-|-+---+---+---+---+---| | 299d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard | | | |***|***|*|*|***|***| | | | | 300d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard |---+---+---+---+---+-|________________________ yEnd | Since we only want to add the key to 301d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard | | | | | | | (bottomPixelWithinThreshold) | the proximity if it's close enough to 302d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard |---+---+---+---+---+---+---+---+---+---+---| | the center of the cell, we only need 303d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard | | | | | | | | | | | | | to compute for these cells where 304d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard '---'---'---'---'---'---'---'---'---'---'---' | topPixelWithinThreshold is above the 305d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard (positive x,y) | center of the cell. This is the case 306d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard | when yDeltaToGrid is less than half 307d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard [Zoomed in diagram] | the height of the cell. 308d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard +-------+-------+-------+-------+-------+ | 309d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard | | | | | | | On the zoomed in diagram, on the right 310d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard | | | | | | | the topPixelWithinThreshold (represented 311d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard | | | | | | top of | with an = sign) is below and we can skip 312d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard +-------+-------+-------+--v----+-------+ .. top cell | this cell, while on the left it's above 313d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard | | = topPixelWT | | yDeltaToGrid | and we need to compute for this cell. 314d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard |..yStart.|.....|.......|..|....|.......|... y middle | Thus, if yDeltaToGrid is more than half 315d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard | (left)| | | ^ = | | of top cell | the height of the cell, we start the 316d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard +-------+-|-----+-------+----|--+-------+ | iteration one cell below the top cell, 317d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard | | | | | | | | | else we start it on the top cell. This 318d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard |.......|.|.....|.......|....|..|.....yStart (right) | is stored in yStart. 319d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard 320d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard Since we only want to go up to bottomPixelWithinThreshold, and we only iterate on the center 321d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard of the keys, we can stop as soon as the y value exceeds bottomPixelThreshold, so we don't 322d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard have to align this on the center of the key. Hence, we don't need a separate value for 323d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard bottomPixelWithinThreshold and call this yEnd right away. 324d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard*/ 3257dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka final int keyX = key.getX(); 3267dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka final int keyY = key.getY(); 3277dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka final int topPixelWithinThreshold = keyY - threshold; 328d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard final int yDeltaToGrid = topPixelWithinThreshold % mCellHeight; 329d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard final int yMiddleOfTopCell = topPixelWithinThreshold - yDeltaToGrid + halfCellHeight; 330d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard final int yStart = Math.max(halfCellHeight, 331d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard yMiddleOfTopCell + (yDeltaToGrid <= halfCellHeight ? 0 : mCellHeight)); 332a7d0ab80a043f222f0e781dc8070cd0d68422b64Jean Chalard final int yEnd = Math.min(lastPixelYCoordinate, keyY + key.getHeight() + threshold); 333d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard 3347dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka final int leftPixelWithinThreshold = keyX - threshold; 335d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard final int xDeltaToGrid = leftPixelWithinThreshold % mCellWidth; 336d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard final int xMiddleOfLeftCell = leftPixelWithinThreshold - xDeltaToGrid + halfCellWidth; 337d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard final int xStart = Math.max(halfCellWidth, 338d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard xMiddleOfLeftCell + (xDeltaToGrid <= halfCellWidth ? 0 : mCellWidth)); 339a7d0ab80a043f222f0e781dc8070cd0d68422b64Jean Chalard final int xEnd = Math.min(lastPixelXCoordinate, keyX + key.getWidth() + threshold); 340d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard 341d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard int baseIndexOfCurrentRow = (yStart / mCellHeight) * mGridWidth + (xStart / mCellWidth); 342d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard for (int centerY = yStart; centerY <= yEnd; centerY += mCellHeight) { 343d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard int index = baseIndexOfCurrentRow; 344d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard for (int centerX = xStart; centerX <= xEnd; centerX += mCellWidth) { 345a7d0ab80a043f222f0e781dc8070cd0d68422b64Jean Chalard if (key.squaredDistanceToEdge(centerX, centerY) < thresholdSquared) { 346d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard neighborsFlatBuffer[index * keyCount + neighborCountPerCell[index]] = key; 347d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard ++neighborCountPerCell[index]; 3480c0ca874febee38fb5cb2c85c11ddd46cdf2b859Tadashi G. Takaoka } 349d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard ++index; 3500d5494c66ac3e5947040e8148091163a1c8716f7satok } 351d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard baseIndexOfCurrentRow += mGridWidth; 3520d5494c66ac3e5947040e8148091163a1c8716f7satok } 3530d5494c66ac3e5947040e8148091163a1c8716f7satok } 354d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard 355d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard for (int i = 0; i < gridSize; ++i) { 356d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard final int base = i * keyCount; 357d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard mGridNeighbors[i] = 358d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard Arrays.copyOfRange(neighborsFlatBuffer, base, base + neighborCountPerCell[i]); 359d55baf06d96aff0b67979419c37ac3ebdc44768cJean Chalard } 3600d5494c66ac3e5947040e8148091163a1c8716f7satok } 3610d5494c66ac3e5947040e8148091163a1c8716f7satok 36217752016713b92a55e9c2356d07b7ed51c67416bTadashi G. Takaoka public void fillArrayWithNearestKeyCodes(final int x, final int y, final int primaryKeyCode, 36317752016713b92a55e9c2356d07b7ed51c67416bTadashi G. Takaoka final int[] dest) { 364209dd09e5a534b5819c70fbc5cc1ef056f77d1a3satok final int destLength = dest.length; 365209dd09e5a534b5819c70fbc5cc1ef056f77d1a3satok if (destLength < 1) { 366209dd09e5a534b5819c70fbc5cc1ef056f77d1a3satok return; 367209dd09e5a534b5819c70fbc5cc1ef056f77d1a3satok } 368209dd09e5a534b5819c70fbc5cc1ef056f77d1a3satok int index = 0; 369240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka if (primaryKeyCode > Constants.CODE_SPACE) { 370209dd09e5a534b5819c70fbc5cc1ef056f77d1a3satok dest[index++] = primaryKeyCode; 371209dd09e5a534b5819c70fbc5cc1ef056f77d1a3satok } 372209dd09e5a534b5819c70fbc5cc1ef056f77d1a3satok final Key[] nearestKeys = getNearestKeys(x, y); 373209dd09e5a534b5819c70fbc5cc1ef056f77d1a3satok for (Key key : nearestKeys) { 374209dd09e5a534b5819c70fbc5cc1ef056f77d1a3satok if (index >= destLength) { 375209dd09e5a534b5819c70fbc5cc1ef056f77d1a3satok break; 376209dd09e5a534b5819c70fbc5cc1ef056f77d1a3satok } 3777dc60f9db729e93cb591492574a436418c553ebfTadashi G. Takaoka final int code = key.getCode(); 378240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka if (code <= Constants.CODE_SPACE) { 379209dd09e5a534b5819c70fbc5cc1ef056f77d1a3satok break; 380209dd09e5a534b5819c70fbc5cc1ef056f77d1a3satok } 381209dd09e5a534b5819c70fbc5cc1ef056f77d1a3satok dest[index++] = code; 382209dd09e5a534b5819c70fbc5cc1ef056f77d1a3satok } 383209dd09e5a534b5819c70fbc5cc1ef056f77d1a3satok if (index < destLength) { 384ac78633be28e8990fc3b3a8de192c80966e746e3Tadashi G. Takaoka dest[index] = Constants.NOT_A_CODE; 385209dd09e5a534b5819c70fbc5cc1ef056f77d1a3satok } 386209dd09e5a534b5819c70fbc5cc1ef056f77d1a3satok } 387209dd09e5a534b5819c70fbc5cc1ef056f77d1a3satok 38817752016713b92a55e9c2356d07b7ed51c67416bTadashi G. Takaoka public Key[] getNearestKeys(final int x, final int y) { 3890d5494c66ac3e5947040e8148091163a1c8716f7satok if (mGridNeighbors == null) { 3900c0ca874febee38fb5cb2c85c11ddd46cdf2b859Tadashi G. Takaoka return EMPTY_KEY_ARRAY; 3910d5494c66ac3e5947040e8148091163a1c8716f7satok } 3920d5494c66ac3e5947040e8148091163a1c8716f7satok if (x >= 0 && x < mKeyboardMinWidth && y >= 0 && y < mKeyboardHeight) { 393e05b3f4b3a57dcf99ade35bfbc1e1cdc3c3e476csatok int index = (y / mCellHeight) * mGridWidth + (x / mCellWidth); 3940d5494c66ac3e5947040e8148091163a1c8716f7satok if (index < mGridSize) { 3950d5494c66ac3e5947040e8148091163a1c8716f7satok return mGridNeighbors[index]; 3960d5494c66ac3e5947040e8148091163a1c8716f7satok } 3970d5494c66ac3e5947040e8148091163a1c8716f7satok } 3980c0ca874febee38fb5cb2c85c11ddd46cdf2b859Tadashi G. Takaoka return EMPTY_KEY_ARRAY; 3990d5494c66ac3e5947040e8148091163a1c8716f7satok } 4008fbd55229243cb66c03d5ea1f79dfb39f596590dsatok} 401