• 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 
22 import com.android.inputmethod.keyboard.internal.TouchPositionCorrection;
23 import com.android.inputmethod.latin.Constants;
24 import com.android.inputmethod.latin.JniUtils;
25 
26 import java.util.Arrays;
27 
28 public final class ProximityInfo {
29     /** MAX_PROXIMITY_CHARS_SIZE must be the same as MAX_PROXIMITY_CHARS_SIZE_INTERNAL
30      * in defines.h */
31     public static final int MAX_PROXIMITY_CHARS_SIZE = 16;
32     /** Number of key widths from current touch point to search for nearest keys. */
33     private static float SEARCH_DISTANCE = 1.2f;
34     private static final Key[] EMPTY_KEY_ARRAY = new Key[0];
35     private static final float DEFAULT_TOUCH_POSITION_CORRECTION_RADIUS = 0.15f;
36 
37     private final int mGridWidth;
38     private final int mGridHeight;
39     private final int mGridSize;
40     private final int mCellWidth;
41     private final int mCellHeight;
42     // TODO: Find a proper name for mKeyboardMinWidth
43     private final int mKeyboardMinWidth;
44     private final int mKeyboardHeight;
45     private final int mMostCommonKeyWidth;
46     private final int mMostCommonKeyHeight;
47     private final Key[] mKeys;
48     private final Key[][] mGridNeighbors;
49     private final String mLocaleStr;
50 
ProximityInfo(final String localeStr, final int gridWidth, final int gridHeight, final int minWidth, final int height, final int mostCommonKeyWidth, final int mostCommonKeyHeight, final Key[] keys, final TouchPositionCorrection touchPositionCorrection)51     ProximityInfo(final String localeStr, final int gridWidth, final int gridHeight,
52             final int minWidth, final int height, final int mostCommonKeyWidth,
53             final int mostCommonKeyHeight, final Key[] keys,
54             final TouchPositionCorrection touchPositionCorrection) {
55         if (TextUtils.isEmpty(localeStr)) {
56             mLocaleStr = "";
57         } else {
58             mLocaleStr = localeStr;
59         }
60         mGridWidth = gridWidth;
61         mGridHeight = gridHeight;
62         mGridSize = mGridWidth * mGridHeight;
63         mCellWidth = (minWidth + mGridWidth - 1) / mGridWidth;
64         mCellHeight = (height + mGridHeight - 1) / mGridHeight;
65         mKeyboardMinWidth = minWidth;
66         mKeyboardHeight = height;
67         mMostCommonKeyHeight = mostCommonKeyHeight;
68         mMostCommonKeyWidth = mostCommonKeyWidth;
69         mKeys = keys;
70         mGridNeighbors = new Key[mGridSize][];
71         if (minWidth == 0 || height == 0) {
72             // No proximity required. Keyboard might be more keys keyboard.
73             return;
74         }
75         computeNearestNeighbors();
76         mNativeProximityInfo = createNativeProximityInfo(touchPositionCorrection);
77     }
78 
createDummyProximityInfo()79     public static ProximityInfo createDummyProximityInfo() {
80         return new ProximityInfo("", 1, 1, 1, 1, 1, 1, EMPTY_KEY_ARRAY, null);
81     }
82 
createSpellCheckerProximityInfo(final int[] proximity, final int rowSize, final int gridWidth, final int gridHeight)83     public static ProximityInfo createSpellCheckerProximityInfo(final int[] proximity,
84             final int rowSize, final int gridWidth, final int gridHeight) {
85         final ProximityInfo spellCheckerProximityInfo = createDummyProximityInfo();
86         spellCheckerProximityInfo.mNativeProximityInfo =
87                 spellCheckerProximityInfo.setProximityInfoNative("",
88                         rowSize, gridWidth, gridHeight, gridWidth, gridHeight,
89                         1, proximity, 0, null, null, null, null, null, null, null, null);
90         return spellCheckerProximityInfo;
91     }
92 
93     private long mNativeProximityInfo;
94     static {
JniUtils.loadNativeLibrary()95         JniUtils.loadNativeLibrary();
96     }
97 
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)98     private native long setProximityInfoNative(
99             String locale, int maxProximityCharsSize, int displayWidth,
100             int displayHeight, int gridWidth, int gridHeight,
101             int mostCommonKeyWidth, int[] proximityCharsArray,
102             int keyCount, int[] keyXCoordinates, int[] keyYCoordinates,
103             int[] keyWidths, int[] keyHeights, int[] keyCharCodes,
104             float[] sweetSpotCenterX, float[] sweetSpotCenterY, float[] sweetSpotRadii);
105 
releaseProximityInfoNative(long nativeProximityInfo)106     private native void releaseProximityInfoNative(long nativeProximityInfo);
107 
createNativeProximityInfo( final TouchPositionCorrection touchPositionCorrection)108     private final long createNativeProximityInfo(
109             final TouchPositionCorrection touchPositionCorrection) {
110         final Key[][] gridNeighborKeys = mGridNeighbors;
111         final int keyboardWidth = mKeyboardMinWidth;
112         final int keyboardHeight = mKeyboardHeight;
113         final Key[] keys = mKeys;
114         final int[] proximityCharsArray = new int[mGridSize * MAX_PROXIMITY_CHARS_SIZE];
115         Arrays.fill(proximityCharsArray, Constants.NOT_A_CODE);
116         for (int i = 0; i < mGridSize; ++i) {
117             final int proximityCharsLength = gridNeighborKeys[i].length;
118             for (int j = 0; j < proximityCharsLength; ++j) {
119                 proximityCharsArray[i * MAX_PROXIMITY_CHARS_SIZE + j] =
120                         gridNeighborKeys[i][j].mCode;
121             }
122         }
123         final int keyCount = keys.length;
124         final int[] keyXCoordinates = new int[keyCount];
125         final int[] keyYCoordinates = new int[keyCount];
126         final int[] keyWidths = new int[keyCount];
127         final int[] keyHeights = new int[keyCount];
128         final int[] keyCharCodes = new int[keyCount];
129         final float[] sweetSpotCenterXs;
130         final float[] sweetSpotCenterYs;
131         final float[] sweetSpotRadii;
132 
133         for (int i = 0; i < keyCount; ++i) {
134             final Key key = keys[i];
135             keyXCoordinates[i] = key.mX;
136             keyYCoordinates[i] = key.mY;
137             keyWidths[i] = key.mWidth;
138             keyHeights[i] = key.mHeight;
139             keyCharCodes[i] = key.mCode;
140         }
141 
142         if (touchPositionCorrection != null && touchPositionCorrection.isValid()) {
143             sweetSpotCenterXs = new float[keyCount];
144             sweetSpotCenterYs = new float[keyCount];
145             sweetSpotRadii = new float[keyCount];
146             final float defaultRadius = DEFAULT_TOUCH_POSITION_CORRECTION_RADIUS
147                     * (float)Math.hypot(mMostCommonKeyWidth, mMostCommonKeyHeight);
148             for (int i = 0; i < keyCount; i++) {
149                 final Key key = keys[i];
150                 final Rect hitBox = key.mHitBox;
151                 sweetSpotCenterXs[i] = hitBox.exactCenterX();
152                 sweetSpotCenterYs[i] = hitBox.exactCenterY();
153                 sweetSpotRadii[i] = defaultRadius;
154                 final int row = hitBox.top / mMostCommonKeyHeight;
155                 if (row < touchPositionCorrection.getRows()) {
156                     final int hitBoxWidth = hitBox.width();
157                     final int hitBoxHeight = hitBox.height();
158                     final float hitBoxDiagonal = (float)Math.hypot(hitBoxWidth, hitBoxHeight);
159                     sweetSpotCenterXs[i] += touchPositionCorrection.getX(row) * hitBoxWidth;
160                     sweetSpotCenterYs[i] += touchPositionCorrection.getY(row) * hitBoxHeight;
161                     sweetSpotRadii[i] = touchPositionCorrection.getRadius(row) * hitBoxDiagonal;
162                 }
163             }
164         } else {
165             sweetSpotCenterXs = sweetSpotCenterYs = sweetSpotRadii = null;
166         }
167 
168         return setProximityInfoNative(mLocaleStr, MAX_PROXIMITY_CHARS_SIZE,
169                 keyboardWidth, keyboardHeight, mGridWidth, mGridHeight, mMostCommonKeyWidth,
170                 proximityCharsArray,
171                 keyCount, keyXCoordinates, keyYCoordinates, keyWidths, keyHeights, keyCharCodes,
172                 sweetSpotCenterXs, sweetSpotCenterYs, sweetSpotRadii);
173     }
174 
getNativeProximityInfo()175     public long getNativeProximityInfo() {
176         return mNativeProximityInfo;
177     }
178 
179     @Override
finalize()180     protected void finalize() throws Throwable {
181         try {
182             if (mNativeProximityInfo != 0) {
183                 releaseProximityInfoNative(mNativeProximityInfo);
184                 mNativeProximityInfo = 0;
185             }
186         } finally {
187             super.finalize();
188         }
189     }
190 
computeNearestNeighbors()191     private void computeNearestNeighbors() {
192         final int defaultWidth = mMostCommonKeyWidth;
193         final Key[] keys = mKeys;
194         final int thresholdBase = (int) (defaultWidth * SEARCH_DISTANCE);
195         final int threshold = thresholdBase * thresholdBase;
196         // Round-up so we don't have any pixels outside the grid
197         final Key[] neighborKeys = new Key[keys.length];
198         final int gridWidth = mGridWidth * mCellWidth;
199         final int gridHeight = mGridHeight * mCellHeight;
200         for (int x = 0; x < gridWidth; x += mCellWidth) {
201             for (int y = 0; y < gridHeight; y += mCellHeight) {
202                 final int centerX = x + mCellWidth / 2;
203                 final int centerY = y + mCellHeight / 2;
204                 int count = 0;
205                 for (final Key key : keys) {
206                     if (key.isSpacer()) continue;
207                     if (key.squaredDistanceToEdge(centerX, centerY) < threshold) {
208                         neighborKeys[count++] = key;
209                     }
210                 }
211                 mGridNeighbors[(y / mCellHeight) * mGridWidth + (x / mCellWidth)] =
212                         Arrays.copyOfRange(neighborKeys, 0, count);
213             }
214         }
215     }
216 
fillArrayWithNearestKeyCodes(final int x, final int y, final int primaryKeyCode, final int[] dest)217     public void fillArrayWithNearestKeyCodes(final int x, final int y, final int primaryKeyCode,
218             final int[] dest) {
219         final int destLength = dest.length;
220         if (destLength < 1) {
221             return;
222         }
223         int index = 0;
224         if (primaryKeyCode > Keyboard.CODE_SPACE) {
225             dest[index++] = primaryKeyCode;
226         }
227         final Key[] nearestKeys = getNearestKeys(x, y);
228         for (Key key : nearestKeys) {
229             if (index >= destLength) {
230                 break;
231             }
232             final int code = key.mCode;
233             if (code <= Keyboard.CODE_SPACE) {
234                 break;
235             }
236             dest[index++] = code;
237         }
238         if (index < destLength) {
239             dest[index] = Constants.NOT_A_CODE;
240         }
241     }
242 
getNearestKeys(final int x, final int y)243     public Key[] getNearestKeys(final int x, final int y) {
244         if (mGridNeighbors == null) {
245             return EMPTY_KEY_ARRAY;
246         }
247         if (x >= 0 && x < mKeyboardMinWidth && y >= 0 && y < mKeyboardHeight) {
248             int index = (y / mCellHeight) * mGridWidth + (x / mCellWidth);
249             if (index < mGridSize) {
250                 return mGridNeighbors[index];
251             }
252         }
253         return EMPTY_KEY_ARRAY;
254     }
255 }
256