ProximityInfo.java revision f098fbbef324df034cc04de04d9b5fe6657238c7
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 com.android.inputmethod.latin.Utils;
20import com.android.inputmethod.latin.spellcheck.SpellCheckerProximityInfo;
21
22import java.util.Arrays;
23import java.util.Collections;
24import java.util.List;
25
26public class ProximityInfo {
27    public static final int MAX_PROXIMITY_CHARS_SIZE = 16;
28    /** Number of key widths from current touch point to search for nearest keys. */
29    private static float SEARCH_DISTANCE = 1.2f;
30    private static final int[] EMPTY_INT_ARRAY = new int[0];
31
32    private final int mGridWidth;
33    private final int mGridHeight;
34    private final int mGridSize;
35    private final int mCellWidth;
36    private final int mCellHeight;
37    // TODO: Find a proper name for mKeyboardMinWidth
38    private final int mKeyboardMinWidth;
39    private final int mKeyboardHeight;
40    private final int[][] mGridNeighbors;
41
42    ProximityInfo(
43            int gridWidth, int gridHeight, int minWidth, int height, int keyWidth, List<Key> keys) {
44        mGridWidth = gridWidth;
45        mGridHeight = gridHeight;
46        mGridSize = mGridWidth * mGridHeight;
47        mCellWidth = (minWidth + mGridWidth - 1) / mGridWidth;
48        mCellHeight = (height + mGridHeight - 1) / mGridHeight;
49        mKeyboardMinWidth = minWidth;
50        mKeyboardHeight = height;
51        mGridNeighbors = new int[mGridSize][];
52        if (minWidth == 0 || height == 0) {
53            // No proximity required. Keyboard might be mini keyboard.
54            return;
55        }
56        computeNearestNeighbors(keyWidth, keys);
57    }
58
59    public static ProximityInfo getDummyProximityInfo() {
60        return new ProximityInfo(1, 1, 1, 1, 1, Collections.<Key>emptyList());
61    }
62
63    public static ProximityInfo getSpellCheckerProximityInfo() {
64        final ProximityInfo spellCheckerProximityInfo = getDummyProximityInfo();
65        spellCheckerProximityInfo.mNativeProximityInfo =
66                spellCheckerProximityInfo.setProximityInfoNative(
67                        SpellCheckerProximityInfo.ROW_SIZE,
68                        480, 300, 10, 3, SpellCheckerProximityInfo.PROXIMITY);
69        return spellCheckerProximityInfo;
70    }
71
72    private int mNativeProximityInfo;
73    static {
74        Utils.loadNativeLibrary();
75    }
76    private native int setProximityInfoNative(int maxProximityCharsSize, int displayWidth,
77            int displayHeight, int gridWidth, int gridHeight, int[] proximityCharsArray);
78    private native void releaseProximityInfoNative(int nativeProximityInfo);
79
80    private final void setProximityInfo(int[][] gridNeighborKeyIndexes, int keyboardWidth,
81            int keyboardHeight, List<Key> keys) {
82        int[] proximityCharsArray = new int[mGridSize * MAX_PROXIMITY_CHARS_SIZE];
83        Arrays.fill(proximityCharsArray, KeyDetector.NOT_A_CODE);
84        for (int i = 0; i < mGridSize; ++i) {
85            final int proximityCharsLength = gridNeighborKeyIndexes[i].length;
86            for (int j = 0; j < proximityCharsLength; ++j) {
87                proximityCharsArray[i * MAX_PROXIMITY_CHARS_SIZE + j] =
88                        keys.get(gridNeighborKeyIndexes[i][j]).mCode;
89            }
90        }
91        mNativeProximityInfo = setProximityInfoNative(MAX_PROXIMITY_CHARS_SIZE,
92                keyboardWidth, keyboardHeight, mGridWidth, mGridHeight, proximityCharsArray);
93    }
94
95    public int getNativeProximityInfo() {
96        return mNativeProximityInfo;
97    }
98
99    @Override
100    protected void finalize() throws Throwable {
101        try {
102            if (mNativeProximityInfo != 0) {
103                releaseProximityInfoNative(mNativeProximityInfo);
104                mNativeProximityInfo = 0;
105            }
106        } finally {
107            super.finalize();
108        }
109    }
110
111    private void computeNearestNeighbors(int defaultWidth, List<Key> keys) {
112        final int thresholdBase = (int) (defaultWidth * SEARCH_DISTANCE);
113        final int threshold = thresholdBase * thresholdBase;
114        // Round-up so we don't have any pixels outside the grid
115        final int[] indices = new int[keys.size()];
116        final int gridWidth = mGridWidth * mCellWidth;
117        final int gridHeight = mGridHeight * mCellHeight;
118        for (int x = 0; x < gridWidth; x += mCellWidth) {
119            for (int y = 0; y < gridHeight; y += mCellHeight) {
120                final int centerX = x + mCellWidth / 2;
121                final int centerY = y + mCellHeight / 2;
122                int count = 0;
123                for (int i = 0; i < keys.size(); i++) {
124                    final Key key = keys.get(i);
125                    if (key.squaredDistanceToEdge(centerX, centerY) < threshold)
126                        indices[count++] = i;
127                }
128                final int[] cell = new int[count];
129                System.arraycopy(indices, 0, cell, 0, count);
130                mGridNeighbors[(y / mCellHeight) * mGridWidth + (x / mCellWidth)] = cell;
131            }
132        }
133        setProximityInfo(mGridNeighbors, mKeyboardMinWidth, mKeyboardHeight, keys);
134    }
135
136    public int[] getNearestKeys(int x, int y) {
137        if (mGridNeighbors == null) {
138            return EMPTY_INT_ARRAY;
139        }
140        if (x >= 0 && x < mKeyboardMinWidth && y >= 0 && y < mKeyboardHeight) {
141            int index = (y /  mCellHeight) * mGridWidth + (x / mCellWidth);
142            if (index < mGridSize) {
143                return mGridNeighbors[index];
144            }
145        }
146        return EMPTY_INT_ARRAY;
147    }
148}
149