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