ProximityInfo.java revision 370674422a47f20979b1fd30aaaed4f2423b28a3
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 17package com.android.inputmethod.keyboard; 18 19import android.graphics.Rect; 20 21import com.android.inputmethod.keyboard.Keyboard.Params.TouchPositionCorrection; 22import com.android.inputmethod.latin.Utils; 23import com.android.inputmethod.latin.spellcheck.SpellCheckerProximityInfo; 24 25import java.util.Arrays; 26import java.util.Collections; 27import java.util.HashMap; 28import java.util.List; 29import java.util.Map; 30import java.util.Set; 31 32public class ProximityInfo { 33 public static final int MAX_PROXIMITY_CHARS_SIZE = 16; 34 /** Number of key widths from current touch point to search for nearest keys. */ 35 private static float SEARCH_DISTANCE = 1.2f; 36 private static final Key[] EMPTY_KEY_ARRAY = new Key[0]; 37 38 private final int mKeyHeight; 39 private final int mGridWidth; 40 private final int mGridHeight; 41 private final int mGridSize; 42 private final int mCellWidth; 43 private final int mCellHeight; 44 // TODO: Find a proper name for mKeyboardMinWidth 45 private final int mKeyboardMinWidth; 46 private final int mKeyboardHeight; 47 private final Key[][] mGridNeighbors; 48 49 ProximityInfo(int gridWidth, int gridHeight, int minWidth, int height, int keyWidth, 50 int keyHeight, Set<Key> keys, TouchPositionCorrection touchPositionCorrection, 51 Map<Integer, List<Integer>> additionalProximityChars) { 52 mGridWidth = gridWidth; 53 mGridHeight = gridHeight; 54 mGridSize = mGridWidth * mGridHeight; 55 mCellWidth = (minWidth + mGridWidth - 1) / mGridWidth; 56 mCellHeight = (height + mGridHeight - 1) / mGridHeight; 57 mKeyboardMinWidth = minWidth; 58 mKeyboardHeight = height; 59 mKeyHeight = keyHeight; 60 mGridNeighbors = new Key[mGridSize][]; 61 if (minWidth == 0 || height == 0) { 62 // No proximity required. Keyboard might be more keys keyboard. 63 return; 64 } 65 computeNearestNeighbors(keyWidth, keys, touchPositionCorrection, additionalProximityChars); 66 } 67 68 public static ProximityInfo createDummyProximityInfo() { 69 return new ProximityInfo(1, 1, 1, 1, 1, 1, Collections.<Key> emptySet(), 70 null, Collections.<Integer, List<Integer>> emptyMap()); 71 } 72 73 public static ProximityInfo createSpellCheckerProximityInfo(final int[] proximity) { 74 final ProximityInfo spellCheckerProximityInfo = createDummyProximityInfo(); 75 spellCheckerProximityInfo.mNativeProximityInfo = 76 spellCheckerProximityInfo.setProximityInfoNative( 77 SpellCheckerProximityInfo.ROW_SIZE, 480, 300, 11, 3, proximity, 0, 78 null, null, null, null, null, null, null, null); 79 return spellCheckerProximityInfo; 80 } 81 82 private long mNativeProximityInfo; 83 static { 84 Utils.loadNativeLibrary(); 85 } 86 87 private native long setProximityInfoNative(int maxProximityCharsSize, int displayWidth, 88 int displayHeight, int gridWidth, int gridHeight, int[] proximityCharsArray, 89 int keyCount, int[] keyXCoordinates, int[] keyYCoordinates, 90 int[] keyWidths, int[] keyHeights, int[] keyCharCodes, 91 float[] sweetSpotCenterX, float[] sweetSpotCenterY, float[] sweetSpotRadii); 92 93 private native void releaseProximityInfoNative(long nativeProximityInfo); 94 95 private final void setProximityInfo(Key[][] gridNeighborKeys, int keyboardWidth, 96 int keyboardHeight, Set<Key> keys, 97 TouchPositionCorrection touchPositionCorrection) { 98 final int[] proximityCharsArray = new int[mGridSize * MAX_PROXIMITY_CHARS_SIZE]; 99 Arrays.fill(proximityCharsArray, KeyDetector.NOT_A_CODE); 100 for (int i = 0; i < mGridSize; ++i) { 101 final int proximityCharsLength = gridNeighborKeys[i].length; 102 for (int j = 0; j < proximityCharsLength; ++j) { 103 proximityCharsArray[i * MAX_PROXIMITY_CHARS_SIZE + j] = 104 gridNeighborKeys[i][j].mCode; 105 } 106 } 107 final int keyCount = keys.size(); 108 final int[] keyXCoordinates = new int[keyCount]; 109 final int[] keyYCoordinates = new int[keyCount]; 110 final int[] keyWidths = new int[keyCount]; 111 final int[] keyHeights = new int[keyCount]; 112 final int[] keyCharCodes = new int[keyCount]; 113 final float[] sweetSpotCenterXs; 114 final float[] sweetSpotCenterYs; 115 final float[] sweetSpotRadii; 116 final boolean calculateSweetSpotParams; 117 if (touchPositionCorrection != null && touchPositionCorrection.isValid()) { 118 sweetSpotCenterXs = new float[keyCount]; 119 sweetSpotCenterYs = new float[keyCount]; 120 sweetSpotRadii = new float[keyCount]; 121 calculateSweetSpotParams = true; 122 int i = 0; 123 for (final Key key : keys) { 124 keyXCoordinates[i] = key.mX; 125 keyYCoordinates[i] = key.mY; 126 keyWidths[i] = key.mWidth; 127 keyHeights[i] = key.mHeight; 128 keyCharCodes[i] = key.mCode; 129 if (calculateSweetSpotParams) { 130 final Rect hitBox = key.mHitBox; 131 final int row = hitBox.top / mKeyHeight; 132 if (row < touchPositionCorrection.mRadii.length) { 133 final float hitBoxCenterX = (hitBox.left + hitBox.right) * 0.5f; 134 final float hitBoxCenterY = (hitBox.top + hitBox.bottom) * 0.5f; 135 final float hitBoxWidth = hitBox.right - hitBox.left; 136 final float hitBoxHeight = hitBox.bottom - hitBox.top; 137 final float x = touchPositionCorrection.mXs[row]; 138 final float y = touchPositionCorrection.mYs[row]; 139 final float radius = touchPositionCorrection.mRadii[row]; 140 sweetSpotCenterXs[i] = hitBoxCenterX + x * hitBoxWidth; 141 sweetSpotCenterYs[i] = hitBoxCenterY + y * hitBoxHeight; 142 sweetSpotRadii[i] = radius * (float) Math.sqrt( 143 hitBoxWidth * hitBoxWidth + hitBoxHeight * hitBoxHeight); 144 } 145 } 146 i++; 147 } 148 } else { 149 sweetSpotCenterXs = sweetSpotCenterYs = sweetSpotRadii = null; 150 calculateSweetSpotParams = false; 151 } 152 153 mNativeProximityInfo = setProximityInfoNative(MAX_PROXIMITY_CHARS_SIZE, 154 keyboardWidth, keyboardHeight, mGridWidth, mGridHeight, proximityCharsArray, 155 keyCount, keyXCoordinates, keyYCoordinates, keyWidths, keyHeights, keyCharCodes, 156 sweetSpotCenterXs, sweetSpotCenterYs, sweetSpotRadii); 157 } 158 159 public long getNativeProximityInfo() { 160 return mNativeProximityInfo; 161 } 162 163 @Override 164 protected void finalize() throws Throwable { 165 try { 166 if (mNativeProximityInfo != 0) { 167 releaseProximityInfoNative(mNativeProximityInfo); 168 mNativeProximityInfo = 0; 169 } 170 } finally { 171 super.finalize(); 172 } 173 } 174 175 private void computeNearestNeighbors(int defaultWidth, Set<Key> keys, 176 TouchPositionCorrection touchPositionCorrection, 177 Map<Integer, List<Integer>> additionalProximityChars) { 178 final Map<Integer, Key> keyCodeMap = new HashMap<Integer, Key>(); 179 for (final Key key : keys) { 180 keyCodeMap.put(key.mCode, key); 181 } 182 final int thresholdBase = (int) (defaultWidth * SEARCH_DISTANCE); 183 final int threshold = thresholdBase * thresholdBase; 184 // Round-up so we don't have any pixels outside the grid 185 final Key[] neighborKeys = new Key[keys.size()]; 186 final int gridWidth = mGridWidth * mCellWidth; 187 final int gridHeight = mGridHeight * mCellHeight; 188 for (int x = 0; x < gridWidth; x += mCellWidth) { 189 for (int y = 0; y < gridHeight; y += mCellHeight) { 190 final int centerX = x + mCellWidth / 2; 191 final int centerY = y + mCellHeight / 2; 192 int count = 0; 193 for (final Key key : keys) { 194 if (key.isSpacer()) continue; 195 if (key.squaredDistanceToEdge(centerX, centerY) < threshold) { 196 neighborKeys[count++] = key; 197 } 198 } 199 int currentCodesSize = count; 200 for (int i = 0; i < currentCodesSize; ++i) { 201 final int c = neighborKeys[i].mCode; 202 final List<Integer> additionalChars = additionalProximityChars.get(c); 203 if (additionalChars == null || additionalChars.size() == 0) { 204 continue; 205 } 206 for (int j = 0; j < additionalChars.size(); ++j) { 207 final int additionalChar = additionalChars.get(j); 208 boolean contains = false; 209 for (int k = 0; k < count; ++k) { 210 if(additionalChar == neighborKeys[k].mCode) { 211 contains = true; 212 break; 213 } 214 } 215 if (!contains) { 216 neighborKeys[count++] = keyCodeMap.get(additionalChar); 217 } 218 } 219 } 220 mGridNeighbors[(y / mCellHeight) * mGridWidth + (x / mCellWidth)] = 221 Arrays.copyOfRange(neighborKeys, 0, count); 222 } 223 } 224 setProximityInfo(mGridNeighbors, mKeyboardMinWidth, mKeyboardHeight, keys, 225 touchPositionCorrection); 226 } 227 228 public Key[] getNearestKeys(int x, int y) { 229 if (mGridNeighbors == null) { 230 return EMPTY_KEY_ARRAY; 231 } 232 if (x >= 0 && x < mKeyboardMinWidth && y >= 0 && y < mKeyboardHeight) { 233 int index = (y / mCellHeight) * mGridWidth + (x / mCellWidth); 234 if (index < mGridSize) { 235 return mGridNeighbors[index]; 236 } 237 } 238 return EMPTY_KEY_ARRAY; 239 } 240} 241