2011-02-22 08:28:55 +00:00
|
|
|
/*
|
2011-05-20 03:09:57 +00:00
|
|
|
* Copyright (C) 2011 The Android Open Source Project
|
2011-02-22 08:28:55 +00:00
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
|
|
|
* use this file except in compliance with the License. You may obtain a copy of
|
|
|
|
* the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
|
|
* License for the specific language governing permissions and limitations under
|
|
|
|
* the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package com.android.inputmethod.keyboard;
|
|
|
|
|
2011-09-29 07:44:54 +00:00
|
|
|
import android.graphics.Rect;
|
2012-03-13 10:16:33 +00:00
|
|
|
import android.text.TextUtils;
|
2012-05-16 11:42:12 +00:00
|
|
|
import android.util.FloatMath;
|
2011-09-29 07:44:54 +00:00
|
|
|
|
2011-12-18 10:54:08 +00:00
|
|
|
import com.android.inputmethod.keyboard.Keyboard.Params.TouchPositionCorrection;
|
2012-03-08 08:07:02 +00:00
|
|
|
import com.android.inputmethod.latin.JniUtils;
|
2011-02-25 09:21:02 +00:00
|
|
|
|
2011-03-04 14:06:45 +00:00
|
|
|
import java.util.Arrays;
|
2012-01-31 08:15:43 +00:00
|
|
|
import java.util.HashMap;
|
2011-03-04 14:06:45 +00:00
|
|
|
|
2011-02-22 08:28:55 +00:00
|
|
|
public class ProximityInfo {
|
|
|
|
public static final int MAX_PROXIMITY_CHARS_SIZE = 16;
|
2011-07-12 05:58:46 +00:00
|
|
|
/** Number of key widths from current touch point to search for nearest keys. */
|
|
|
|
private static float SEARCH_DISTANCE = 1.2f;
|
2011-12-16 05:20:11 +00:00
|
|
|
private static final Key[] EMPTY_KEY_ARRAY = new Key[0];
|
2011-02-22 08:28:55 +00:00
|
|
|
|
2011-09-29 07:44:54 +00:00
|
|
|
private final int mKeyHeight;
|
2011-02-22 08:28:55 +00:00
|
|
|
private final int mGridWidth;
|
|
|
|
private final int mGridHeight;
|
|
|
|
private final int mGridSize;
|
2011-07-12 05:58:46 +00:00
|
|
|
private final int mCellWidth;
|
|
|
|
private final int mCellHeight;
|
|
|
|
// TODO: Find a proper name for mKeyboardMinWidth
|
|
|
|
private final int mKeyboardMinWidth;
|
|
|
|
private final int mKeyboardHeight;
|
2012-03-07 06:12:22 +00:00
|
|
|
private final int mMostCommonKeyWidth;
|
2011-12-16 05:20:11 +00:00
|
|
|
private final Key[][] mGridNeighbors;
|
2012-03-13 10:16:33 +00:00
|
|
|
private final String mLocaleStr;
|
2011-02-22 08:28:55 +00:00
|
|
|
|
2012-03-13 10:16:33 +00:00
|
|
|
ProximityInfo(String localeStr, int gridWidth, int gridHeight, int minWidth, int height,
|
2012-03-13 11:53:15 +00:00
|
|
|
int mostCommonKeyWidth, int mostCommonKeyHeight, final Key[] keys,
|
2012-03-19 05:47:38 +00:00
|
|
|
TouchPositionCorrection touchPositionCorrection) {
|
2012-03-13 10:16:33 +00:00
|
|
|
if (TextUtils.isEmpty(localeStr)) {
|
|
|
|
mLocaleStr = "";
|
|
|
|
} else {
|
|
|
|
mLocaleStr = localeStr;
|
|
|
|
}
|
2011-02-22 08:28:55 +00:00
|
|
|
mGridWidth = gridWidth;
|
|
|
|
mGridHeight = gridHeight;
|
|
|
|
mGridSize = mGridWidth * mGridHeight;
|
2011-07-12 05:58:46 +00:00
|
|
|
mCellWidth = (minWidth + mGridWidth - 1) / mGridWidth;
|
|
|
|
mCellHeight = (height + mGridHeight - 1) / mGridHeight;
|
|
|
|
mKeyboardMinWidth = minWidth;
|
|
|
|
mKeyboardHeight = height;
|
2012-03-07 06:12:22 +00:00
|
|
|
mKeyHeight = mostCommonKeyHeight;
|
|
|
|
mMostCommonKeyWidth = mostCommonKeyWidth;
|
2011-12-16 05:20:11 +00:00
|
|
|
mGridNeighbors = new Key[mGridSize][];
|
2011-07-12 05:58:46 +00:00
|
|
|
if (minWidth == 0 || height == 0) {
|
2012-02-08 07:12:11 +00:00
|
|
|
// No proximity required. Keyboard might be more keys keyboard.
|
2011-07-12 05:58:46 +00:00
|
|
|
return;
|
|
|
|
}
|
2012-03-07 06:12:22 +00:00
|
|
|
computeNearestNeighbors(
|
2012-03-19 05:47:38 +00:00
|
|
|
mostCommonKeyWidth, keys, touchPositionCorrection);
|
2011-02-22 08:28:55 +00:00
|
|
|
}
|
|
|
|
|
2011-08-16 08:37:18 +00:00
|
|
|
public static ProximityInfo createDummyProximityInfo() {
|
2012-03-19 05:47:38 +00:00
|
|
|
return new ProximityInfo("", 1, 1, 1, 1, 1, 1, EMPTY_KEY_ARRAY, null);
|
2011-08-04 03:08:22 +00:00
|
|
|
}
|
|
|
|
|
2012-05-11 08:03:06 +00:00
|
|
|
public static ProximityInfo createSpellCheckerProximityInfo(final int[] proximity,
|
|
|
|
int rowSize, int gridWidth, int gridHeight) {
|
2011-08-16 08:37:18 +00:00
|
|
|
final ProximityInfo spellCheckerProximityInfo = createDummyProximityInfo();
|
2011-08-08 04:37:11 +00:00
|
|
|
spellCheckerProximityInfo.mNativeProximityInfo =
|
2012-03-13 07:33:47 +00:00
|
|
|
spellCheckerProximityInfo.setProximityInfoNative("",
|
2012-05-11 08:03:06 +00:00
|
|
|
rowSize, gridWidth, gridHeight, gridWidth, gridHeight,
|
2012-03-22 08:39:27 +00:00
|
|
|
1, proximity, 0, null, null, null, null, null, null, null, null);
|
2011-08-08 04:37:11 +00:00
|
|
|
return spellCheckerProximityInfo;
|
|
|
|
}
|
|
|
|
|
2011-10-31 11:44:01 +00:00
|
|
|
private long mNativeProximityInfo;
|
2011-02-25 09:21:02 +00:00
|
|
|
static {
|
2012-03-08 08:07:02 +00:00
|
|
|
JniUtils.loadNativeLibrary();
|
2011-02-25 09:21:02 +00:00
|
|
|
}
|
2012-01-31 08:15:43 +00:00
|
|
|
|
2012-03-13 07:33:47 +00:00
|
|
|
private native long setProximityInfoNative(
|
|
|
|
String locale, int maxProximityCharsSize, int displayWidth,
|
2012-03-07 06:12:22 +00:00
|
|
|
int displayHeight, int gridWidth, int gridHeight,
|
|
|
|
int mostCommonKeyWidth, int[] proximityCharsArray,
|
2011-09-21 03:02:47 +00:00
|
|
|
int keyCount, int[] keyXCoordinates, int[] keyYCoordinates,
|
2011-09-29 07:44:54 +00:00
|
|
|
int[] keyWidths, int[] keyHeights, int[] keyCharCodes,
|
|
|
|
float[] sweetSpotCenterX, float[] sweetSpotCenterY, float[] sweetSpotRadii);
|
2012-01-31 08:15:43 +00:00
|
|
|
|
2011-10-31 11:44:01 +00:00
|
|
|
private native void releaseProximityInfoNative(long nativeProximityInfo);
|
2011-02-22 08:28:55 +00:00
|
|
|
|
2011-12-16 05:20:11 +00:00
|
|
|
private final void setProximityInfo(Key[][] gridNeighborKeys, int keyboardWidth,
|
2012-03-13 11:53:15 +00:00
|
|
|
int keyboardHeight, final Key[] keys, TouchPositionCorrection touchPositionCorrection) {
|
2011-12-16 06:32:24 +00:00
|
|
|
final int[] proximityCharsArray = new int[mGridSize * MAX_PROXIMITY_CHARS_SIZE];
|
2011-03-04 14:06:45 +00:00
|
|
|
Arrays.fill(proximityCharsArray, KeyDetector.NOT_A_CODE);
|
2011-02-22 08:28:55 +00:00
|
|
|
for (int i = 0; i < mGridSize; ++i) {
|
2011-12-16 05:20:11 +00:00
|
|
|
final int proximityCharsLength = gridNeighborKeys[i].length;
|
2011-03-04 14:06:45 +00:00
|
|
|
for (int j = 0; j < proximityCharsLength; ++j) {
|
|
|
|
proximityCharsArray[i * MAX_PROXIMITY_CHARS_SIZE + j] =
|
2011-12-16 05:20:11 +00:00
|
|
|
gridNeighborKeys[i][j].mCode;
|
2011-02-22 08:28:55 +00:00
|
|
|
}
|
|
|
|
}
|
2012-03-13 11:53:15 +00:00
|
|
|
final int keyCount = keys.length;
|
2011-09-29 07:44:54 +00:00
|
|
|
final int[] keyXCoordinates = new int[keyCount];
|
|
|
|
final int[] keyYCoordinates = new int[keyCount];
|
|
|
|
final int[] keyWidths = new int[keyCount];
|
|
|
|
final int[] keyHeights = new int[keyCount];
|
|
|
|
final int[] keyCharCodes = new int[keyCount];
|
2011-12-16 04:53:34 +00:00
|
|
|
final float[] sweetSpotCenterXs;
|
|
|
|
final float[] sweetSpotCenterYs;
|
|
|
|
final float[] sweetSpotRadii;
|
2012-03-14 09:40:19 +00:00
|
|
|
|
|
|
|
for (int i = 0; i < keyCount; ++i) {
|
|
|
|
final Key key = keys[i];
|
|
|
|
keyXCoordinates[i] = key.mX;
|
|
|
|
keyYCoordinates[i] = key.mY;
|
|
|
|
keyWidths[i] = key.mWidth;
|
|
|
|
keyHeights[i] = key.mHeight;
|
|
|
|
keyCharCodes[i] = key.mCode;
|
|
|
|
}
|
|
|
|
|
2011-12-16 04:53:34 +00:00
|
|
|
if (touchPositionCorrection != null && touchPositionCorrection.isValid()) {
|
|
|
|
sweetSpotCenterXs = new float[keyCount];
|
|
|
|
sweetSpotCenterYs = new float[keyCount];
|
|
|
|
sweetSpotRadii = new float[keyCount];
|
2012-03-13 11:53:15 +00:00
|
|
|
for (int i = 0; i < keyCount; i++) {
|
|
|
|
final Key key = keys[i];
|
2012-03-14 09:40:19 +00:00
|
|
|
final Rect hitBox = key.mHitBox;
|
|
|
|
final int row = hitBox.top / mKeyHeight;
|
|
|
|
if (row < touchPositionCorrection.mRadii.length) {
|
|
|
|
final float hitBoxCenterX = (hitBox.left + hitBox.right) * 0.5f;
|
|
|
|
final float hitBoxCenterY = (hitBox.top + hitBox.bottom) * 0.5f;
|
|
|
|
final float hitBoxWidth = hitBox.right - hitBox.left;
|
|
|
|
final float hitBoxHeight = hitBox.bottom - hitBox.top;
|
|
|
|
final float x = touchPositionCorrection.mXs[row];
|
|
|
|
final float y = touchPositionCorrection.mYs[row];
|
|
|
|
final float radius = touchPositionCorrection.mRadii[row];
|
|
|
|
sweetSpotCenterXs[i] = hitBoxCenterX + x * hitBoxWidth;
|
|
|
|
sweetSpotCenterYs[i] = hitBoxCenterY + y * hitBoxHeight;
|
2012-05-16 11:42:12 +00:00
|
|
|
sweetSpotRadii[i] = radius * FloatMath.sqrt(
|
2012-03-14 09:40:19 +00:00
|
|
|
hitBoxWidth * hitBoxWidth + hitBoxHeight * hitBoxHeight);
|
2012-03-05 09:15:29 +00:00
|
|
|
}
|
|
|
|
}
|
2011-12-16 04:53:34 +00:00
|
|
|
} else {
|
|
|
|
sweetSpotCenterXs = sweetSpotCenterYs = sweetSpotRadii = null;
|
|
|
|
}
|
|
|
|
|
2012-03-13 10:16:33 +00:00
|
|
|
mNativeProximityInfo = setProximityInfoNative(mLocaleStr, MAX_PROXIMITY_CHARS_SIZE,
|
2012-03-07 06:12:22 +00:00
|
|
|
keyboardWidth, keyboardHeight, mGridWidth, mGridHeight, mMostCommonKeyWidth,
|
|
|
|
proximityCharsArray,
|
2011-09-28 02:38:34 +00:00
|
|
|
keyCount, keyXCoordinates, keyYCoordinates, keyWidths, keyHeights, keyCharCodes,
|
2011-09-29 07:44:54 +00:00
|
|
|
sweetSpotCenterXs, sweetSpotCenterYs, sweetSpotRadii);
|
|
|
|
}
|
|
|
|
|
2011-10-31 11:44:01 +00:00
|
|
|
public long getNativeProximityInfo() {
|
2011-02-22 08:28:55 +00:00
|
|
|
return mNativeProximityInfo;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void finalize() throws Throwable {
|
|
|
|
try {
|
|
|
|
if (mNativeProximityInfo != 0) {
|
|
|
|
releaseProximityInfoNative(mNativeProximityInfo);
|
|
|
|
mNativeProximityInfo = 0;
|
|
|
|
}
|
|
|
|
} finally {
|
|
|
|
super.finalize();
|
|
|
|
}
|
|
|
|
}
|
2011-07-12 05:58:46 +00:00
|
|
|
|
2012-03-13 11:53:15 +00:00
|
|
|
private void computeNearestNeighbors(int defaultWidth, final Key[] keys,
|
2012-03-19 05:47:38 +00:00
|
|
|
TouchPositionCorrection touchPositionCorrection) {
|
2012-03-13 11:53:15 +00:00
|
|
|
final HashMap<Integer, Key> keyCodeMap = new HashMap<Integer, Key>();
|
2012-01-31 08:15:43 +00:00
|
|
|
for (final Key key : keys) {
|
|
|
|
keyCodeMap.put(key.mCode, key);
|
|
|
|
}
|
2011-07-12 05:58:46 +00:00
|
|
|
final int thresholdBase = (int) (defaultWidth * SEARCH_DISTANCE);
|
|
|
|
final int threshold = thresholdBase * thresholdBase;
|
|
|
|
// Round-up so we don't have any pixels outside the grid
|
2012-03-13 11:53:15 +00:00
|
|
|
final Key[] neighborKeys = new Key[keys.length];
|
2011-07-12 05:58:46 +00:00
|
|
|
final int gridWidth = mGridWidth * mCellWidth;
|
|
|
|
final int gridHeight = mGridHeight * mCellHeight;
|
|
|
|
for (int x = 0; x < gridWidth; x += mCellWidth) {
|
|
|
|
for (int y = 0; y < gridHeight; y += mCellHeight) {
|
|
|
|
final int centerX = x + mCellWidth / 2;
|
|
|
|
final int centerY = y + mCellHeight / 2;
|
|
|
|
int count = 0;
|
2011-12-16 05:20:11 +00:00
|
|
|
for (final Key key : keys) {
|
2011-09-08 08:19:23 +00:00
|
|
|
if (key.isSpacer()) continue;
|
2011-12-16 05:20:11 +00:00
|
|
|
if (key.squaredDistanceToEdge(centerX, centerY) < threshold) {
|
|
|
|
neighborKeys[count++] = key;
|
|
|
|
}
|
2011-07-12 05:58:46 +00:00
|
|
|
}
|
2011-12-16 05:20:11 +00:00
|
|
|
mGridNeighbors[(y / mCellHeight) * mGridWidth + (x / mCellWidth)] =
|
|
|
|
Arrays.copyOfRange(neighborKeys, 0, count);
|
2011-07-12 05:58:46 +00:00
|
|
|
}
|
|
|
|
}
|
2011-10-03 09:34:34 +00:00
|
|
|
setProximityInfo(mGridNeighbors, mKeyboardMinWidth, mKeyboardHeight, keys,
|
|
|
|
touchPositionCorrection);
|
2011-07-12 05:58:46 +00:00
|
|
|
}
|
|
|
|
|
2012-04-02 05:26:36 +00:00
|
|
|
public void fillArrayWithNearestKeyCodes(int x, int y, int primaryKeyCode, int[] dest) {
|
|
|
|
final int destLength = dest.length;
|
|
|
|
if (destLength < 1) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
int index = 0;
|
|
|
|
if (primaryKeyCode > Keyboard.CODE_SPACE) {
|
|
|
|
dest[index++] = primaryKeyCode;
|
|
|
|
}
|
|
|
|
final Key[] nearestKeys = getNearestKeys(x, y);
|
|
|
|
for (Key key : nearestKeys) {
|
|
|
|
if (index >= destLength) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
final int code = key.mCode;
|
|
|
|
if (code <= Keyboard.CODE_SPACE) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
dest[index++] = code;
|
|
|
|
}
|
|
|
|
if (index < destLength) {
|
|
|
|
dest[index] = KeyDetector.NOT_A_CODE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-12-16 05:20:11 +00:00
|
|
|
public Key[] getNearestKeys(int x, int y) {
|
2011-07-12 05:58:46 +00:00
|
|
|
if (mGridNeighbors == null) {
|
2011-12-16 05:20:11 +00:00
|
|
|
return EMPTY_KEY_ARRAY;
|
2011-07-12 05:58:46 +00:00
|
|
|
}
|
|
|
|
if (x >= 0 && x < mKeyboardMinWidth && y >= 0 && y < mKeyboardHeight) {
|
2012-01-31 08:15:43 +00:00
|
|
|
int index = (y / mCellHeight) * mGridWidth + (x / mCellWidth);
|
2011-07-12 05:58:46 +00:00
|
|
|
if (index < mGridSize) {
|
|
|
|
return mGridNeighbors[index];
|
|
|
|
}
|
|
|
|
}
|
2011-12-16 05:20:11 +00:00
|
|
|
return EMPTY_KEY_ARRAY;
|
2011-07-12 05:58:46 +00:00
|
|
|
}
|
2011-02-22 08:28:55 +00:00
|
|
|
}
|