Keyboard.java revision 35ff94547c16c84c5b6fafdae0b4a683be782b97
1/*
2 * Copyright (C) 2010 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.Typeface;
20import android.util.Log;
21import android.util.SparseArray;
22
23import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
24import com.android.inputmethod.keyboard.internal.KeyboardParams;
25import com.android.inputmethod.latin.CollectionUtils;
26
27
28
29/**
30 * Loads an XML description of a keyboard and stores the attributes of the keys. A keyboard
31 * consists of rows of keys.
32 * <p>The layout file for a keyboard contains XML that looks like the following snippet:</p>
33 * <pre>
34 * &lt;Keyboard
35 *         latin:keyWidth="%10p"
36 *         latin:keyHeight="50px"
37 *         latin:horizontalGap="2px"
38 *         latin:verticalGap="2px" &gt;
39 *     &lt;Row latin:keyWidth="32px" &gt;
40 *         &lt;Key latin:keyLabel="A" /&gt;
41 *         ...
42 *     &lt;/Row&gt;
43 *     ...
44 * &lt;/Keyboard&gt;
45 * </pre>
46 */
47public class Keyboard {
48    private static final String TAG = Keyboard.class.getSimpleName();
49
50    /** Some common keys code. Must be positive.
51     * These should be aligned with values/keycodes.xml
52     */
53    public static final int CODE_ENTER = '\n';
54    public static final int CODE_TAB = '\t';
55    public static final int CODE_SPACE = ' ';
56    public static final int CODE_PERIOD = '.';
57    public static final int CODE_DASH = '-';
58    public static final int CODE_SINGLE_QUOTE = '\'';
59    public static final int CODE_DOUBLE_QUOTE = '"';
60    // TODO: Check how this should work for right-to-left languages. It seems to stand
61    // that for rtl languages, a closing parenthesis is a left parenthesis. Is this
62    // managed by the font? Or is it a different char?
63    public static final int CODE_CLOSING_PARENTHESIS = ')';
64    public static final int CODE_CLOSING_SQUARE_BRACKET = ']';
65    public static final int CODE_CLOSING_CURLY_BRACKET = '}';
66    public static final int CODE_CLOSING_ANGLE_BRACKET = '>';
67
68    /** Special keys code. Must be negative.
69     * These should be aligned with KeyboardCodesSet.ID_TO_NAME[],
70     * KeyboardCodesSet.DEFAULT[] and KeyboardCodesSet.RTL[]
71     */
72    public static final int CODE_SHIFT = -1;
73    public static final int CODE_SWITCH_ALPHA_SYMBOL = -2;
74    public static final int CODE_OUTPUT_TEXT = -3;
75    public static final int CODE_DELETE = -4;
76    public static final int CODE_SETTINGS = -5;
77    public static final int CODE_SHORTCUT = -6;
78    public static final int CODE_ACTION_ENTER = -7;
79    public static final int CODE_ACTION_NEXT = -8;
80    public static final int CODE_ACTION_PREVIOUS = -9;
81    public static final int CODE_LANGUAGE_SWITCH = -10;
82    public static final int CODE_RESEARCH = -11;
83    // Code value representing the code is not specified.
84    public static final int CODE_UNSPECIFIED = -12;
85
86    public final KeyboardId mId;
87    public final int mThemeId;
88
89    /** Total height of the keyboard, including the padding and keys */
90    public final int mOccupiedHeight;
91    /** Total width of the keyboard, including the padding and keys */
92    public final int mOccupiedWidth;
93
94    /** The padding above the keyboard */
95    public final int mTopPadding;
96    /** Default gap between rows */
97    public final int mVerticalGap;
98
99    /** Per keyboard key visual parameters */
100    public final Typeface mKeyTypeface;
101    public final float mKeyLetterRatio;
102    public final int mKeyLetterSize;
103    public final float mKeyHintLetterRatio;
104    public final float mKeyShiftedLetterHintRatio;
105
106    public final int mMostCommonKeyHeight;
107    public final int mMostCommonKeyWidth;
108
109    /** More keys keyboard template */
110    public final int mMoreKeysTemplate;
111
112    /** Maximum column for more keys keyboard */
113    public final int mMaxMoreKeysKeyboardColumn;
114
115    /** Array of keys and icons in this keyboard */
116    public final Key[] mKeys;
117    public final Key[] mShiftKeys;
118    public final Key[] mAltCodeKeysWhileTyping;
119    public final KeyboardIconsSet mIconsSet;
120
121    private final SparseArray<Key> mKeyCache = CollectionUtils.newSparseArray();
122
123    private final ProximityInfo mProximityInfo;
124    private final boolean mProximityCharsCorrectionEnabled;
125
126    public Keyboard(final KeyboardParams params) {
127        mId = params.mId;
128        mThemeId = params.mThemeId;
129        mOccupiedHeight = params.mOccupiedHeight;
130        mOccupiedWidth = params.mOccupiedWidth;
131        mMostCommonKeyHeight = params.mMostCommonKeyHeight;
132        mMostCommonKeyWidth = params.mMostCommonKeyWidth;
133        mMoreKeysTemplate = params.mMoreKeysTemplate;
134        mMaxMoreKeysKeyboardColumn = params.mMaxMoreKeysKeyboardColumn;
135
136        mKeyTypeface = params.mKeyTypeface;
137        mKeyLetterRatio = params.mKeyLetterRatio;
138        mKeyLetterSize = params.mKeyLetterSize;
139        mKeyHintLetterRatio = params.mKeyHintLetterRatio;
140        mKeyShiftedLetterHintRatio = params.mKeyShiftedLetterHintRatio;
141
142        mTopPadding = params.mTopPadding;
143        mVerticalGap = params.mVerticalGap;
144
145        mKeys = params.mKeys.toArray(new Key[params.mKeys.size()]);
146        mShiftKeys = params.mShiftKeys.toArray(new Key[params.mShiftKeys.size()]);
147        mAltCodeKeysWhileTyping = params.mAltCodeKeysWhileTyping.toArray(
148                new Key[params.mAltCodeKeysWhileTyping.size()]);
149        mIconsSet = params.mIconsSet;
150
151        mProximityInfo = new ProximityInfo(params.mId.mLocale.toString(),
152                params.GRID_WIDTH, params.GRID_HEIGHT, mOccupiedWidth, mOccupiedHeight,
153                mMostCommonKeyWidth, mMostCommonKeyHeight, mKeys, params.mTouchPositionCorrection);
154        mProximityCharsCorrectionEnabled = params.mProximityCharsCorrectionEnabled;
155    }
156
157    public boolean hasProximityCharsCorrection(final int code) {
158        if (!mProximityCharsCorrectionEnabled) {
159            return false;
160        }
161        // Note: The native code has the main keyboard layout only at this moment.
162        // TODO: Figure out how to handle proximity characters information of all layouts.
163        final boolean canAssumeNativeHasProximityCharsInfoOfAllKeys = (
164                mId.mElementId == KeyboardId.ELEMENT_ALPHABET
165                || mId.mElementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED);
166        return canAssumeNativeHasProximityCharsInfoOfAllKeys || Character.isLetter(code);
167    }
168
169    public ProximityInfo getProximityInfo() {
170        return mProximityInfo;
171    }
172
173    public Key getKey(final int code) {
174        if (code == CODE_UNSPECIFIED) {
175            return null;
176        }
177        synchronized (mKeyCache) {
178            final int index = mKeyCache.indexOfKey(code);
179            if (index >= 0) {
180                return mKeyCache.valueAt(index);
181            }
182
183            for (final Key key : mKeys) {
184                if (key.mCode == code) {
185                    mKeyCache.put(code, key);
186                    return key;
187                }
188            }
189            mKeyCache.put(code, null);
190            return null;
191        }
192    }
193
194    public boolean hasKey(final Key aKey) {
195        if (mKeyCache.indexOfValue(aKey) >= 0) {
196            return true;
197        }
198
199        for (final Key key : mKeys) {
200            if (key == aKey) {
201                mKeyCache.put(key.mCode, key);
202                return true;
203            }
204        }
205        return false;
206    }
207
208    public static boolean isLetterCode(final int code) {
209        return code >= CODE_SPACE;
210    }
211
212    @Override
213    public String toString() {
214        return mId.toString();
215    }
216
217    /**
218     * Returns the array of the keys that are closest to the given point.
219     * @param x the x-coordinate of the point
220     * @param y the y-coordinate of the point
221     * @return the array of the nearest keys to the given point. If the given
222     * point is out of range, then an array of size zero is returned.
223     */
224    public Key[] getNearestKeys(final int x, final int y) {
225        // Avoid dead pixels at edges of the keyboard
226        final int adjustedX = Math.max(0, Math.min(x, mOccupiedWidth - 1));
227        final int adjustedY = Math.max(0, Math.min(y, mOccupiedHeight - 1));
228        return mProximityInfo.getNearestKeys(adjustedX, adjustedY);
229    }
230
231    public static String printableCode(final int code) {
232        switch (code) {
233        case CODE_SHIFT: return "shift";
234        case CODE_SWITCH_ALPHA_SYMBOL: return "symbol";
235        case CODE_OUTPUT_TEXT: return "text";
236        case CODE_DELETE: return "delete";
237        case CODE_SETTINGS: return "settings";
238        case CODE_SHORTCUT: return "shortcut";
239        case CODE_ACTION_ENTER: return "actionEnter";
240        case CODE_ACTION_NEXT: return "actionNext";
241        case CODE_ACTION_PREVIOUS: return "actionPrevious";
242        case CODE_LANGUAGE_SWITCH: return "languageSwitch";
243        case CODE_UNSPECIFIED: return "unspec";
244        case CODE_TAB: return "tab";
245        case CODE_ENTER: return "enter";
246        default:
247            if (code <= 0) Log.w(TAG, "Unknown non-positive key code=" + code);
248            if (code < CODE_SPACE) return String.format("'\\u%02x'", code);
249            if (code < 0x100) return String.format("'%c'", code);
250            return String.format("'\\u%04x'", code);
251        }
252    }
253}
254