blob: d35b1a93953f6a6280bfe7a17d6d6d593770e2af [file] [log] [blame]
satok8fbd5522011-02-22 17:28:55 +09001/*
Tadashi G. Takaoka8632bff2011-05-20 12:09:57 +09002 * Copyright (C) 2011 The Android Open Source Project
satok8fbd5522011-02-22 17:28:55 +09003 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy of
6 * the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations under
14 * the License.
15 */
16
17package com.android.inputmethod.keyboard;
18
Yusuke Nojimaad358352011-09-29 16:44:54 +090019import android.graphics.Rect;
20
21import com.android.inputmethod.keyboard.Key;
Ken Wakasaeaef1c52011-02-25 18:21:02 +090022import com.android.inputmethod.latin.Utils;
Jean Chalardf098fbb2011-08-08 13:37:11 +090023import com.android.inputmethod.latin.spellcheck.SpellCheckerProximityInfo;
Ken Wakasaeaef1c52011-02-25 18:21:02 +090024
satok817e5172011-03-04 06:06:45 -080025import java.util.Arrays;
Jean Chalard043f7842011-08-04 12:08:22 +090026import java.util.Collections;
satok817e5172011-03-04 06:06:45 -080027import java.util.List;
28
satok8fbd5522011-02-22 17:28:55 +090029public class ProximityInfo {
30 public static final int MAX_PROXIMITY_CHARS_SIZE = 16;
satok0d5494c2011-07-12 14:58:46 +090031 /** Number of key widths from current touch point to search for nearest keys. */
32 private static float SEARCH_DISTANCE = 1.2f;
33 private static final int[] EMPTY_INT_ARRAY = new int[0];
satok8fbd5522011-02-22 17:28:55 +090034
Yusuke Nojimaad358352011-09-29 16:44:54 +090035 private final int mKeyHeight;
satok8fbd5522011-02-22 17:28:55 +090036 private final int mGridWidth;
37 private final int mGridHeight;
38 private final int mGridSize;
satok0d5494c2011-07-12 14:58:46 +090039 private final int mCellWidth;
40 private final int mCellHeight;
41 // TODO: Find a proper name for mKeyboardMinWidth
42 private final int mKeyboardMinWidth;
43 private final int mKeyboardHeight;
44 private final int[][] mGridNeighbors;
satok8fbd5522011-02-22 17:28:55 +090045
Yusuke Nojimad6339632011-09-29 11:53:51 +090046 private final float[] mTouchPositionCorrectionXs;
47 private final float[] mTouchPositionCorrectionYs;
48 private final float[] mTouchPositionCorrectionRadii;
49
50 ProximityInfo(int gridWidth, int gridHeight, int minWidth, int height, int keyWidth,
Yusuke Nojimaad358352011-09-29 16:44:54 +090051 int keyHeight, List<Key> keys, float[] touchPositionCorrectionXs,
52 float[] touchPositionCorrectionYs, float[] touchPositionCorrectionRadii) {
satok8fbd5522011-02-22 17:28:55 +090053 mGridWidth = gridWidth;
54 mGridHeight = gridHeight;
55 mGridSize = mGridWidth * mGridHeight;
satok0d5494c2011-07-12 14:58:46 +090056 mCellWidth = (minWidth + mGridWidth - 1) / mGridWidth;
57 mCellHeight = (height + mGridHeight - 1) / mGridHeight;
58 mKeyboardMinWidth = minWidth;
59 mKeyboardHeight = height;
Yusuke Nojimaad358352011-09-29 16:44:54 +090060 mKeyHeight = keyHeight;
Yusuke Nojimad6339632011-09-29 11:53:51 +090061 mTouchPositionCorrectionXs = touchPositionCorrectionXs;
62 mTouchPositionCorrectionYs = touchPositionCorrectionYs;
63 mTouchPositionCorrectionRadii = touchPositionCorrectionRadii;
satok0d5494c2011-07-12 14:58:46 +090064 mGridNeighbors = new int[mGridSize][];
65 if (minWidth == 0 || height == 0) {
66 // No proximity required. Keyboard might be mini keyboard.
67 return;
68 }
69 computeNearestNeighbors(keyWidth, keys);
satok8fbd5522011-02-22 17:28:55 +090070 }
71
Jean Chalarda5627672011-08-16 17:37:18 +090072 public static ProximityInfo createDummyProximityInfo() {
Yusuke Nojimaad358352011-09-29 16:44:54 +090073 return new ProximityInfo(1, 1, 1, 1, 1, 1, Collections.<Key>emptyList(), null, null, null);
Jean Chalard043f7842011-08-04 12:08:22 +090074 }
75
Jean Chalarda5627672011-08-16 17:37:18 +090076 public static ProximityInfo createSpellCheckerProximityInfo() {
77 final ProximityInfo spellCheckerProximityInfo = createDummyProximityInfo();
Jean Chalardf098fbb2011-08-08 13:37:11 +090078 spellCheckerProximityInfo.mNativeProximityInfo =
79 spellCheckerProximityInfo.setProximityInfoNative(
80 SpellCheckerProximityInfo.ROW_SIZE,
Yusuke Nojima0e1f6562011-09-21 12:02:47 +090081 480, 300, 10, 3, SpellCheckerProximityInfo.PROXIMITY,
Yusuke Nojimaad358352011-09-29 16:44:54 +090082 0, null, null, null, null, null, null, null, null);
Jean Chalardf098fbb2011-08-08 13:37:11 +090083 return spellCheckerProximityInfo;
84 }
85
satok8fbd5522011-02-22 17:28:55 +090086 private int mNativeProximityInfo;
Ken Wakasaeaef1c52011-02-25 18:21:02 +090087 static {
88 Utils.loadNativeLibrary();
89 }
satok8fbd5522011-02-22 17:28:55 +090090 private native int setProximityInfoNative(int maxProximityCharsSize, int displayWidth,
Yusuke Nojima0e1f6562011-09-21 12:02:47 +090091 int displayHeight, int gridWidth, int gridHeight, int[] proximityCharsArray,
92 int keyCount, int[] keyXCoordinates, int[] keyYCoordinates,
Yusuke Nojimaad358352011-09-29 16:44:54 +090093 int[] keyWidths, int[] keyHeights, int[] keyCharCodes,
94 float[] sweetSpotCenterX, float[] sweetSpotCenterY, float[] sweetSpotRadii);
satok8fbd5522011-02-22 17:28:55 +090095 private native void releaseProximityInfoNative(int nativeProximityInfo);
96
satok0d5494c2011-07-12 14:58:46 +090097 private final void setProximityInfo(int[][] gridNeighborKeyIndexes, int keyboardWidth,
satok817e5172011-03-04 06:06:45 -080098 int keyboardHeight, List<Key> keys) {
satok8fbd5522011-02-22 17:28:55 +090099 int[] proximityCharsArray = new int[mGridSize * MAX_PROXIMITY_CHARS_SIZE];
satok817e5172011-03-04 06:06:45 -0800100 Arrays.fill(proximityCharsArray, KeyDetector.NOT_A_CODE);
satok8fbd5522011-02-22 17:28:55 +0900101 for (int i = 0; i < mGridSize; ++i) {
satok817e5172011-03-04 06:06:45 -0800102 final int proximityCharsLength = gridNeighborKeyIndexes[i].length;
103 for (int j = 0; j < proximityCharsLength; ++j) {
104 proximityCharsArray[i * MAX_PROXIMITY_CHARS_SIZE + j] =
105 keys.get(gridNeighborKeyIndexes[i][j]).mCode;
satok8fbd5522011-02-22 17:28:55 +0900106 }
107 }
Yusuke Nojima0e1f6562011-09-21 12:02:47 +0900108 final int keyCount = keys.size();
Yusuke Nojimaad358352011-09-29 16:44:54 +0900109 final int[] keyXCoordinates = new int[keyCount];
110 final int[] keyYCoordinates = new int[keyCount];
111 final int[] keyWidths = new int[keyCount];
112 final int[] keyHeights = new int[keyCount];
113 final int[] keyCharCodes = new int[keyCount];
Yusuke Nojima0e1f6562011-09-21 12:02:47 +0900114 for (int i = 0; i < keyCount; ++i) {
115 final Key key = keys.get(i);
116 keyXCoordinates[i] = key.mX;
117 keyYCoordinates[i] = key.mY;
118 keyWidths[i] = key.mWidth;
119 keyHeights[i] = key.mHeight;
120 keyCharCodes[i] = key.mCode;
121 }
Yusuke Nojimaad358352011-09-29 16:44:54 +0900122
123 final boolean hasTouchPositionCorrectionData =
124 mTouchPositionCorrectionXs != null
125 && mTouchPositionCorrectionYs != null
126 && mTouchPositionCorrectionRadii != null
127 && mTouchPositionCorrectionXs.length > 0
128 && mTouchPositionCorrectionYs.length > 0
129 && mTouchPositionCorrectionRadii.length > 0;
130 final float[] sweetSpotCenterXs =
131 hasTouchPositionCorrectionData ? new float[keyCount] : null;
132 final float[] sweetSpotCenterYs =
133 hasTouchPositionCorrectionData ? new float[keyCount] : null;
134 final float[] sweetSpotRadii =
135 hasTouchPositionCorrectionData ? new float[keyCount] : null;
136 if (hasTouchPositionCorrectionData) {
137 calculateSweetSpot(keys, sweetSpotCenterXs, sweetSpotCenterYs, sweetSpotRadii);
138 }
139
satok8fbd5522011-02-22 17:28:55 +0900140 mNativeProximityInfo = setProximityInfoNative(MAX_PROXIMITY_CHARS_SIZE,
Yusuke Nojima0e1f6562011-09-21 12:02:47 +0900141 keyboardWidth, keyboardHeight, mGridWidth, mGridHeight, proximityCharsArray,
Yusuke Nojima1c923d82011-09-28 11:38:34 +0900142 keyCount, keyXCoordinates, keyYCoordinates, keyWidths, keyHeights, keyCharCodes,
Yusuke Nojimaad358352011-09-29 16:44:54 +0900143 sweetSpotCenterXs, sweetSpotCenterYs, sweetSpotRadii);
144 }
145
146 private void calculateSweetSpot(List<Key> keys, float[] sweetSpotCenterXs,
147 float[] sweetSpotCenterYs, float[] sweetSpotRadii) {
148 final int keyCount = keys.size();
149 for (int i = 0; i < keyCount; ++i) {
150 final Key key = keys.get(i);
151 final Rect hitBox = key.mHitBox;
152 final int row = hitBox.top / mKeyHeight;
153 if (row < mTouchPositionCorrectionRadii.length) {
154 final float hitBoxCenterX = (hitBox.left + hitBox.right) * 0.5f;
155 final float hitBoxCenterY = (hitBox.top + hitBox.bottom) * 0.5f;
156 final float hitBoxWidth = hitBox.right - hitBox.left;
157 final float hitBoxHeight = hitBox.bottom - hitBox.top;
158 final float x = mTouchPositionCorrectionXs[row];
159 final float y = mTouchPositionCorrectionYs[row];
160 final float radius = mTouchPositionCorrectionRadii[row];
161 sweetSpotCenterXs[i] = hitBoxCenterX + x * hitBoxWidth;
162 sweetSpotCenterYs[i] = hitBoxCenterY + y * hitBoxHeight;
163 sweetSpotRadii[i] = radius
164 * (float)Math.sqrt(hitBoxWidth * hitBoxWidth + hitBoxHeight * hitBoxHeight);
165 }
166 }
satok8fbd5522011-02-22 17:28:55 +0900167 }
168
satok0d5494c2011-07-12 14:58:46 +0900169 public int getNativeProximityInfo() {
satok8fbd5522011-02-22 17:28:55 +0900170 return mNativeProximityInfo;
171 }
172
173 @Override
174 protected void finalize() throws Throwable {
175 try {
176 if (mNativeProximityInfo != 0) {
177 releaseProximityInfoNative(mNativeProximityInfo);
178 mNativeProximityInfo = 0;
179 }
180 } finally {
181 super.finalize();
182 }
183 }
satok0d5494c2011-07-12 14:58:46 +0900184
185 private void computeNearestNeighbors(int defaultWidth, List<Key> keys) {
186 final int thresholdBase = (int) (defaultWidth * SEARCH_DISTANCE);
187 final int threshold = thresholdBase * thresholdBase;
188 // Round-up so we don't have any pixels outside the grid
189 final int[] indices = new int[keys.size()];
190 final int gridWidth = mGridWidth * mCellWidth;
191 final int gridHeight = mGridHeight * mCellHeight;
192 for (int x = 0; x < gridWidth; x += mCellWidth) {
193 for (int y = 0; y < gridHeight; y += mCellHeight) {
194 final int centerX = x + mCellWidth / 2;
195 final int centerY = y + mCellHeight / 2;
196 int count = 0;
197 for (int i = 0; i < keys.size(); i++) {
198 final Key key = keys.get(i);
Tadashi G. Takaoka18453d62011-09-08 17:19:23 +0900199 if (key.isSpacer()) continue;
satok0d5494c2011-07-12 14:58:46 +0900200 if (key.squaredDistanceToEdge(centerX, centerY) < threshold)
201 indices[count++] = i;
202 }
203 final int[] cell = new int[count];
204 System.arraycopy(indices, 0, cell, 0, count);
205 mGridNeighbors[(y / mCellHeight) * mGridWidth + (x / mCellWidth)] = cell;
206 }
207 }
208 setProximityInfo(mGridNeighbors, mKeyboardMinWidth, mKeyboardHeight, keys);
209 }
210
211 public int[] getNearestKeys(int x, int y) {
212 if (mGridNeighbors == null) {
213 return EMPTY_INT_ARRAY;
214 }
215 if (x >= 0 && x < mKeyboardMinWidth && y >= 0 && y < mKeyboardHeight) {
216 int index = (y / mCellHeight) * mGridWidth + (x / mCellWidth);
217 if (index < mGridSize) {
218 return mGridNeighbors[index];
219 }
220 }
221 return EMPTY_INT_ARRAY;
222 }
satok8fbd5522011-02-22 17:28:55 +0900223}