• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
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 
17 package com.android.inputmethod.keyboard;
18 
19 import android.graphics.Rect;
20 import android.text.TextUtils;
21 import android.util.FloatMath;
22 
23 import com.android.inputmethod.keyboard.Keyboard.Params.TouchPositionCorrection;
24 import com.android.inputmethod.latin.JniUtils;
25 
26 import java.util.Arrays;
27 import java.util.HashMap;
28 
29 public class ProximityInfo {
30     public static final int MAX_PROXIMITY_CHARS_SIZE = 16;
31     /** 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 Key[] EMPTY_KEY_ARRAY = new Key[0];
34 
35     private final int mKeyHeight;
36     private final int mGridWidth;
37     private final int mGridHeight;
38     private final int mGridSize;
39     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 mMostCommonKeyWidth;
45     private final Key[] mKeys;
46     private final TouchPositionCorrection mTouchPositionCorrection;
47     private final Key[][] mGridNeighbors;
48     private final String mLocaleStr;
49 
ProximityInfo(String localeStr, int gridWidth, int gridHeight, int minWidth, int height, int mostCommonKeyWidth, int mostCommonKeyHeight, final Key[] keys, TouchPositionCorrection touchPositionCorrection)50     ProximityInfo(String localeStr, int gridWidth, int gridHeight, int minWidth, int height,
51             int mostCommonKeyWidth, int mostCommonKeyHeight, final Key[] keys,
52             TouchPositionCorrection touchPositionCorrection) {
53         if (TextUtils.isEmpty(localeStr)) {
54             mLocaleStr = "";
55         } else {
56             mLocaleStr = localeStr;
57         }
58         mGridWidth = gridWidth;
59         mGridHeight = gridHeight;
60         mGridSize = mGridWidth * mGridHeight;
61         mCellWidth = (minWidth + mGridWidth - 1) / mGridWidth;
62         mCellHeight = (height + mGridHeight - 1) / mGridHeight;
63         mKeyboardMinWidth = minWidth;
64         mKeyboardHeight = height;
65         mKeyHeight = mostCommonKeyHeight;
66         mMostCommonKeyWidth = mostCommonKeyWidth;
67         mKeys = keys;
68         mTouchPositionCorrection = touchPositionCorrection;
69         mGridNeighbors = new Key[mGridSize][];
70         if (minWidth == 0 || height == 0) {
71             // No proximity required. Keyboard might be more keys keyboard.
72             return;
73         }
74         computeNearestNeighbors();
75         mNativeProximityInfo = createNativeProximityInfo();
76     }
77 
78     // TODO: Remove this public constructor when the native part of the ProximityInfo becomes
79     // immutable.
80     // This public constructor aims only for test purpose.
ProximityInfo(ProximityInfo o)81     public ProximityInfo(ProximityInfo o) {
82         mLocaleStr = o.mLocaleStr;
83         mGridWidth = o.mGridWidth;
84         mGridHeight = o.mGridHeight;
85         mGridSize = o.mGridSize;
86         mCellWidth = o.mCellWidth;
87         mCellHeight = o.mCellHeight;
88         mKeyboardMinWidth = o.mKeyboardMinWidth;
89         mKeyboardHeight = o.mKeyboardHeight;
90         mKeyHeight = o.mKeyHeight;
91         mMostCommonKeyWidth = o.mMostCommonKeyWidth;
92         mKeys = o.mKeys;
93         mTouchPositionCorrection = o.mTouchPositionCorrection;
94         mGridNeighbors = new Key[mGridSize][];
95         computeNearestNeighbors();
96         mNativeProximityInfo = createNativeProximityInfo();
97     }
98 
createDummyProximityInfo()99     public static ProximityInfo createDummyProximityInfo() {
100         return new ProximityInfo("", 1, 1, 1, 1, 1, 1, EMPTY_KEY_ARRAY, null);
101     }
102 
createSpellCheckerProximityInfo(final int[] proximity, int rowSize, int gridWidth, int gridHeight)103     public static ProximityInfo createSpellCheckerProximityInfo(final int[] proximity,
104             int rowSize, int gridWidth, int gridHeight) {
105         final ProximityInfo spellCheckerProximityInfo = createDummyProximityInfo();
106         spellCheckerProximityInfo.mNativeProximityInfo =
107                 spellCheckerProximityInfo.setProximityInfoNative("",
108                         rowSize, gridWidth, gridHeight, gridWidth, gridHeight,
109                         1, proximity, 0, null, null, null, null, null, null, null, null);
110         return spellCheckerProximityInfo;
111     }
112 
113     private long mNativeProximityInfo;
114     static {
JniUtils.loadNativeLibrary()115         JniUtils.loadNativeLibrary();
116     }
117 
setProximityInfoNative( String locale, int maxProximityCharsSize, int displayWidth, int displayHeight, int gridWidth, int gridHeight, int mostCommonKeyWidth, int[] proximityCharsArray, int keyCount, int[] keyXCoordinates, int[] keyYCoordinates, int[] keyWidths, int[] keyHeights, int[] keyCharCodes, float[] sweetSpotCenterX, float[] sweetSpotCenterY, float[] sweetSpotRadii)118     private native long setProximityInfoNative(
119             String locale, int maxProximityCharsSize, int displayWidth,
120             int displayHeight, int gridWidth, int gridHeight,
121             int mostCommonKeyWidth, int[] proximityCharsArray,
122             int keyCount, int[] keyXCoordinates, int[] keyYCoordinates,
123             int[] keyWidths, int[] keyHeights, int[] keyCharCodes,
124             float[] sweetSpotCenterX, float[] sweetSpotCenterY, float[] sweetSpotRadii);
125 
releaseProximityInfoNative(long nativeProximityInfo)126     private native void releaseProximityInfoNative(long nativeProximityInfo);
127 
createNativeProximityInfo()128     private final long createNativeProximityInfo() {
129         final Key[][] gridNeighborKeys = mGridNeighbors;
130         final int keyboardWidth = mKeyboardMinWidth;
131         final int keyboardHeight = mKeyboardHeight;
132         final Key[] keys = mKeys;
133         final TouchPositionCorrection touchPositionCorrection = mTouchPositionCorrection;
134         final int[] proximityCharsArray = new int[mGridSize * MAX_PROXIMITY_CHARS_SIZE];
135         Arrays.fill(proximityCharsArray, KeyDetector.NOT_A_CODE);
136         for (int i = 0; i < mGridSize; ++i) {
137             final int proximityCharsLength = gridNeighborKeys[i].length;
138             for (int j = 0; j < proximityCharsLength; ++j) {
139                 proximityCharsArray[i * MAX_PROXIMITY_CHARS_SIZE + j] =
140                         gridNeighborKeys[i][j].mCode;
141             }
142         }
143         final int keyCount = keys.length;
144         final int[] keyXCoordinates = new int[keyCount];
145         final int[] keyYCoordinates = new int[keyCount];
146         final int[] keyWidths = new int[keyCount];
147         final int[] keyHeights = new int[keyCount];
148         final int[] keyCharCodes = new int[keyCount];
149         final float[] sweetSpotCenterXs;
150         final float[] sweetSpotCenterYs;
151         final float[] sweetSpotRadii;
152 
153         for (int i = 0; i < keyCount; ++i) {
154             final Key key = keys[i];
155             keyXCoordinates[i] = key.mX;
156             keyYCoordinates[i] = key.mY;
157             keyWidths[i] = key.mWidth;
158             keyHeights[i] = key.mHeight;
159             keyCharCodes[i] = key.mCode;
160         }
161 
162         if (touchPositionCorrection != null && touchPositionCorrection.isValid()) {
163             sweetSpotCenterXs = new float[keyCount];
164             sweetSpotCenterYs = new float[keyCount];
165             sweetSpotRadii = new float[keyCount];
166             for (int i = 0; i < keyCount; i++) {
167                 final Key key = keys[i];
168                 final Rect hitBox = key.mHitBox;
169                 final int row = hitBox.top / mKeyHeight;
170                 if (row < touchPositionCorrection.mRadii.length) {
171                     final int hitBoxWidth = hitBox.width();
172                     final int hitBoxHeight = hitBox.height();
173                     final float x = touchPositionCorrection.mXs[row];
174                     final float y = touchPositionCorrection.mYs[row];
175                     final float radius = touchPositionCorrection.mRadii[row];
176                     sweetSpotCenterXs[i] = hitBox.exactCenterX() + x * hitBoxWidth;
177                     sweetSpotCenterYs[i] = hitBox.exactCenterY() + y * hitBoxHeight;
178                     sweetSpotRadii[i] = radius * FloatMath.sqrt(
179                             hitBoxWidth * hitBoxWidth + hitBoxHeight * hitBoxHeight);
180                 }
181             }
182         } else {
183             sweetSpotCenterXs = sweetSpotCenterYs = sweetSpotRadii = null;
184         }
185 
186         return setProximityInfoNative(mLocaleStr, MAX_PROXIMITY_CHARS_SIZE,
187                 keyboardWidth, keyboardHeight, mGridWidth, mGridHeight, mMostCommonKeyWidth,
188                 proximityCharsArray,
189                 keyCount, keyXCoordinates, keyYCoordinates, keyWidths, keyHeights, keyCharCodes,
190                 sweetSpotCenterXs, sweetSpotCenterYs, sweetSpotRadii);
191     }
192 
getNativeProximityInfo()193     public long getNativeProximityInfo() {
194         return mNativeProximityInfo;
195     }
196 
197     @Override
finalize()198     protected void finalize() throws Throwable {
199         try {
200             if (mNativeProximityInfo != 0) {
201                 releaseProximityInfoNative(mNativeProximityInfo);
202                 mNativeProximityInfo = 0;
203             }
204         } finally {
205             super.finalize();
206         }
207     }
208 
computeNearestNeighbors()209     private void computeNearestNeighbors() {
210         final int defaultWidth = mMostCommonKeyWidth;
211         final Key[] keys = mKeys;
212         final HashMap<Integer, Key> keyCodeMap = new HashMap<Integer, Key>();
213         for (final Key key : keys) {
214             keyCodeMap.put(key.mCode, key);
215         }
216         final int thresholdBase = (int) (defaultWidth * SEARCH_DISTANCE);
217         final int threshold = thresholdBase * thresholdBase;
218         // Round-up so we don't have any pixels outside the grid
219         final Key[] neighborKeys = new Key[keys.length];
220         final int gridWidth = mGridWidth * mCellWidth;
221         final int gridHeight = mGridHeight * mCellHeight;
222         for (int x = 0; x < gridWidth; x += mCellWidth) {
223             for (int y = 0; y < gridHeight; y += mCellHeight) {
224                 final int centerX = x + mCellWidth / 2;
225                 final int centerY = y + mCellHeight / 2;
226                 int count = 0;
227                 for (final Key key : keys) {
228                     if (key.isSpacer()) continue;
229                     if (key.squaredDistanceToEdge(centerX, centerY) < threshold) {
230                         neighborKeys[count++] = key;
231                     }
232                 }
233                 mGridNeighbors[(y / mCellHeight) * mGridWidth + (x / mCellWidth)] =
234                         Arrays.copyOfRange(neighborKeys, 0, count);
235             }
236         }
237     }
238 
fillArrayWithNearestKeyCodes(int x, int y, int primaryKeyCode, int[] dest)239     public void fillArrayWithNearestKeyCodes(int x, int y, int primaryKeyCode, int[] dest) {
240         final int destLength = dest.length;
241         if (destLength < 1) {
242             return;
243         }
244         int index = 0;
245         if (primaryKeyCode > Keyboard.CODE_SPACE) {
246             dest[index++] = primaryKeyCode;
247         }
248         final Key[] nearestKeys = getNearestKeys(x, y);
249         for (Key key : nearestKeys) {
250             if (index >= destLength) {
251                 break;
252             }
253             final int code = key.mCode;
254             if (code <= Keyboard.CODE_SPACE) {
255                 break;
256             }
257             dest[index++] = code;
258         }
259         if (index < destLength) {
260             dest[index] = KeyDetector.NOT_A_CODE;
261         }
262     }
263 
getNearestKeys(int x, int y)264     public Key[] getNearestKeys(int x, int y) {
265         if (mGridNeighbors == null) {
266             return EMPTY_KEY_ARRAY;
267         }
268         if (x >= 0 && x < mKeyboardMinWidth && y >= 0 && y < mKeyboardHeight) {
269             int index = (y / mCellHeight) * mGridWidth + (x / mCellWidth);
270             if (index < mGridSize) {
271                 return mGridNeighbors[index];
272             }
273         }
274         return EMPTY_KEY_ARRAY;
275     }
276 }
277