• 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 
21 import com.android.inputmethod.keyboard.internal.KeyboardParams.TouchPositionCorrection;
22 import com.android.inputmethod.latin.Utils;
23 import com.android.inputmethod.latin.spellcheck.SpellCheckerProximityInfo;
24 
25 import java.util.Arrays;
26 import java.util.Collections;
27 import java.util.List;
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 int[] EMPTY_INT_ARRAY = new int[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[][] mGridNeighbors;
45 
ProximityInfo(int gridWidth, int gridHeight, int minWidth, int height, int keyWidth, int keyHeight, List<Key> keys, TouchPositionCorrection touchPositionCorrection)46     ProximityInfo(int gridWidth, int gridHeight, int minWidth, int height, int keyWidth,
47             int keyHeight, List<Key> keys, TouchPositionCorrection touchPositionCorrection) {
48         mGridWidth = gridWidth;
49         mGridHeight = gridHeight;
50         mGridSize = mGridWidth * mGridHeight;
51         mCellWidth = (minWidth + mGridWidth - 1) / mGridWidth;
52         mCellHeight = (height + mGridHeight - 1) / mGridHeight;
53         mKeyboardMinWidth = minWidth;
54         mKeyboardHeight = height;
55         mKeyHeight = keyHeight;
56         mGridNeighbors = new int[mGridSize][];
57         if (minWidth == 0 || height == 0) {
58             // No proximity required. Keyboard might be mini keyboard.
59             return;
60         }
61         computeNearestNeighbors(keyWidth, keys, touchPositionCorrection);
62     }
63 
createDummyProximityInfo()64     public static ProximityInfo createDummyProximityInfo() {
65         return new ProximityInfo(1, 1, 1, 1, 1, 1, Collections.<Key>emptyList(), null);
66     }
67 
createSpellCheckerProximityInfo()68     public static ProximityInfo createSpellCheckerProximityInfo() {
69         final ProximityInfo spellCheckerProximityInfo = createDummyProximityInfo();
70         spellCheckerProximityInfo.mNativeProximityInfo =
71                 spellCheckerProximityInfo.setProximityInfoNative(
72                         SpellCheckerProximityInfo.ROW_SIZE,
73                         480, 300, 10, 3, SpellCheckerProximityInfo.PROXIMITY,
74                         0, null, null, null, null, null, null, null, null);
75         return spellCheckerProximityInfo;
76     }
77 
78     private int mNativeProximityInfo;
79     static {
Utils.loadNativeLibrary()80         Utils.loadNativeLibrary();
81     }
setProximityInfoNative(int maxProximityCharsSize, int displayWidth, int displayHeight, int gridWidth, int gridHeight, int[] proximityCharsArray, int keyCount, int[] keyXCoordinates, int[] keyYCoordinates, int[] keyWidths, int[] keyHeights, int[] keyCharCodes, float[] sweetSpotCenterX, float[] sweetSpotCenterY, float[] sweetSpotRadii)82     private native int setProximityInfoNative(int maxProximityCharsSize, int displayWidth,
83             int displayHeight, int gridWidth, int gridHeight, int[] proximityCharsArray,
84             int keyCount, int[] keyXCoordinates, int[] keyYCoordinates,
85             int[] keyWidths, int[] keyHeights, int[] keyCharCodes,
86             float[] sweetSpotCenterX, float[] sweetSpotCenterY, float[] sweetSpotRadii);
releaseProximityInfoNative(int nativeProximityInfo)87     private native void releaseProximityInfoNative(int nativeProximityInfo);
88 
setProximityInfo(int[][] gridNeighborKeyIndexes, int keyboardWidth, int keyboardHeight, List<Key> keys, TouchPositionCorrection touchPositionCorrection)89     private final void setProximityInfo(int[][] gridNeighborKeyIndexes, int keyboardWidth,
90             int keyboardHeight, List<Key> keys,
91             TouchPositionCorrection touchPositionCorrection) {
92         int[] proximityCharsArray = new int[mGridSize * MAX_PROXIMITY_CHARS_SIZE];
93         Arrays.fill(proximityCharsArray, KeyDetector.NOT_A_CODE);
94         for (int i = 0; i < mGridSize; ++i) {
95             final int proximityCharsLength = gridNeighborKeyIndexes[i].length;
96             for (int j = 0; j < proximityCharsLength; ++j) {
97                 proximityCharsArray[i * MAX_PROXIMITY_CHARS_SIZE + j] =
98                         keys.get(gridNeighborKeyIndexes[i][j]).mCode;
99             }
100         }
101         final int keyCount = keys.size();
102         final int[] keyXCoordinates = new int[keyCount];
103         final int[] keyYCoordinates = new int[keyCount];
104         final int[] keyWidths = new int[keyCount];
105         final int[] keyHeights = new int[keyCount];
106         final int[] keyCharCodes = new int[keyCount];
107         for (int i = 0; i < keyCount; ++i) {
108             final Key key = keys.get(i);
109             keyXCoordinates[i] = key.mX;
110             keyYCoordinates[i] = key.mY;
111             keyWidths[i] = key.mWidth;
112             keyHeights[i] = key.mHeight;
113             keyCharCodes[i] = key.mCode;
114         }
115 
116         float[] sweetSpotCenterXs = null;
117         float[] sweetSpotCenterYs = null;
118         float[] sweetSpotRadii = null;
119 
120         if (touchPositionCorrection != null && touchPositionCorrection.isValid()) {
121             sweetSpotCenterXs = new float[keyCount];
122             sweetSpotCenterYs = new float[keyCount];
123             sweetSpotRadii = new float[keyCount];
124             calculateSweetSpot(keys, touchPositionCorrection,
125                     sweetSpotCenterXs, sweetSpotCenterYs, sweetSpotRadii);
126         }
127 
128         mNativeProximityInfo = setProximityInfoNative(MAX_PROXIMITY_CHARS_SIZE,
129                 keyboardWidth, keyboardHeight, mGridWidth, mGridHeight, proximityCharsArray,
130                 keyCount, keyXCoordinates, keyYCoordinates, keyWidths, keyHeights, keyCharCodes,
131                 sweetSpotCenterXs, sweetSpotCenterYs, sweetSpotRadii);
132     }
133 
calculateSweetSpot(List<Key> keys, TouchPositionCorrection touchPositionCorrection, float[] sweetSpotCenterXs, float[] sweetSpotCenterYs, float[] sweetSpotRadii)134     private void calculateSweetSpot(List<Key> keys, TouchPositionCorrection touchPositionCorrection,
135             float[] sweetSpotCenterXs, float[] sweetSpotCenterYs, float[] sweetSpotRadii) {
136         final int keyCount = keys.size();
137         final float[] xs = touchPositionCorrection.mXs;
138         final float[] ys = touchPositionCorrection.mYs;
139         final float[] radii = touchPositionCorrection.mRadii;
140         for (int i = 0; i < keyCount; ++i) {
141             final Key key = keys.get(i);
142             final Rect hitBox = key.mHitBox;
143             final int row = hitBox.top / mKeyHeight;
144             if (row < radii.length) {
145                 final float hitBoxCenterX = (hitBox.left + hitBox.right) * 0.5f;
146                 final float hitBoxCenterY = (hitBox.top + hitBox.bottom) * 0.5f;
147                 final float hitBoxWidth = hitBox.right - hitBox.left;
148                 final float hitBoxHeight = hitBox.bottom - hitBox.top;
149                 final float x = xs[row];
150                 final float y = ys[row];
151                 final float radius = radii[row];
152                 sweetSpotCenterXs[i] = hitBoxCenterX + x * hitBoxWidth;
153                 sweetSpotCenterYs[i] = hitBoxCenterY + y * hitBoxHeight;
154                 sweetSpotRadii[i] = radius
155                         * (float)Math.sqrt(hitBoxWidth * hitBoxWidth + hitBoxHeight * hitBoxHeight);
156             }
157         }
158     }
159 
getNativeProximityInfo()160     public int getNativeProximityInfo() {
161         return mNativeProximityInfo;
162     }
163 
164     @Override
finalize()165     protected void finalize() throws Throwable {
166         try {
167             if (mNativeProximityInfo != 0) {
168                 releaseProximityInfoNative(mNativeProximityInfo);
169                 mNativeProximityInfo = 0;
170             }
171         } finally {
172             super.finalize();
173         }
174     }
175 
computeNearestNeighbors(int defaultWidth, List<Key> keys, TouchPositionCorrection touchPositionCorrection)176     private void computeNearestNeighbors(int defaultWidth, List<Key> keys,
177             TouchPositionCorrection touchPositionCorrection) {
178         final int thresholdBase = (int) (defaultWidth * SEARCH_DISTANCE);
179         final int threshold = thresholdBase * thresholdBase;
180         // Round-up so we don't have any pixels outside the grid
181         final int[] indices = new int[keys.size()];
182         final int gridWidth = mGridWidth * mCellWidth;
183         final int gridHeight = mGridHeight * mCellHeight;
184         for (int x = 0; x < gridWidth; x += mCellWidth) {
185             for (int y = 0; y < gridHeight; y += mCellHeight) {
186                 final int centerX = x + mCellWidth / 2;
187                 final int centerY = y + mCellHeight / 2;
188                 int count = 0;
189                 for (int i = 0; i < keys.size(); i++) {
190                     final Key key = keys.get(i);
191                     if (key.isSpacer()) continue;
192                     if (key.squaredDistanceToEdge(centerX, centerY) < threshold)
193                         indices[count++] = i;
194                 }
195                 final int[] cell = new int[count];
196                 System.arraycopy(indices, 0, cell, 0, count);
197                 mGridNeighbors[(y / mCellHeight) * mGridWidth + (x / mCellWidth)] = cell;
198             }
199         }
200         setProximityInfo(mGridNeighbors, mKeyboardMinWidth, mKeyboardHeight, keys,
201                 touchPositionCorrection);
202     }
203 
getNearestKeys(int x, int y)204     public int[] getNearestKeys(int x, int y) {
205         if (mGridNeighbors == null) {
206             return EMPTY_INT_ARRAY;
207         }
208         if (x >= 0 && x < mKeyboardMinWidth && y >= 0 && y < mKeyboardHeight) {
209             int index = (y /  mCellHeight) * mGridWidth + (x / mCellWidth);
210             if (index < mGridSize) {
211                 return mGridNeighbors[index];
212             }
213         }
214         return EMPTY_INT_ARRAY;
215     }
216 }
217