Keyboard.java revision 7dfd5a3e833e14d5bf90d728d5a50b40c8a927d2
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.drawable.Drawable;
20import android.text.TextUtils;
21import android.util.Log;
22
23import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
24import com.android.inputmethod.keyboard.internal.KeyboardParams;
25import com.android.inputmethod.keyboard.internal.KeyboardShiftState;
26
27import java.util.Collections;
28import java.util.HashMap;
29import java.util.List;
30import java.util.Map;
31import java.util.Set;
32
33/**
34 * Loads an XML description of a keyboard and stores the attributes of the keys. A keyboard
35 * consists of rows of keys.
36 * <p>The layout file for a keyboard contains XML that looks like the following snippet:</p>
37 * <pre>
38 * &lt;Keyboard
39 *         latin:keyWidth="%10p"
40 *         latin:keyHeight="50px"
41 *         latin:horizontalGap="2px"
42 *         latin:verticalGap="2px" &gt;
43 *     &lt;Row latin:keyWidth="32px" &gt;
44 *         &lt;Key latin:keyLabel="A" /&gt;
45 *         ...
46 *     &lt;/Row&gt;
47 *     ...
48 * &lt;/Keyboard&gt;
49 * </pre>
50 */
51public class Keyboard {
52    private static final String TAG = Keyboard.class.getSimpleName();
53
54    /** Some common keys code.  These should be aligned with values/keycodes.xml */
55    public static final int CODE_ENTER = '\n';
56    public static final int CODE_TAB = '\t';
57    public static final int CODE_SPACE = ' ';
58    public static final int CODE_PERIOD = '.';
59    public static final int CODE_DASH = '-';
60    public static final int CODE_SINGLE_QUOTE = '\'';
61    public static final int CODE_DOUBLE_QUOTE = '"';
62    // TODO: Check how this should work for right-to-left languages. It seems to stand
63    // that for rtl languages, a closing parenthesis is a left parenthesis. Is this
64    // managed by the font? Or is it a different char?
65    public static final int CODE_CLOSING_PARENTHESIS = ')';
66    public static final int CODE_CLOSING_SQUARE_BRACKET = ']';
67    public static final int CODE_CLOSING_CURLY_BRACKET = '}';
68    public static final int CODE_CLOSING_ANGLE_BRACKET = '>';
69    public static final int CODE_DIGIT0 = '0';
70    public static final int CODE_PLUS = '+';
71
72    /** Special keys code.  These should be aligned with values/keycodes.xml */
73    public static final int CODE_DUMMY = 0;
74    public static final int CODE_SHIFT = -1;
75    public static final int CODE_SWITCH_ALPHA_SYMBOL = -2;
76    public static final int CODE_CAPSLOCK = -3;
77    public static final int CODE_CANCEL = -4;
78    public static final int CODE_DELETE = -5;
79    public static final int CODE_SETTINGS = -6;
80    public static final int CODE_SHORTCUT = -7;
81    // Code value representing the code is not specified.
82    public static final int CODE_UNSPECIFIED = -99;
83
84    public final KeyboardId mId;
85    public final int mThemeId;
86
87    /** Total height of the keyboard, including the padding and keys */
88    public final int mOccupiedHeight;
89    /** Total width of the keyboard, including the padding and keys */
90    public final int mOccupiedWidth;
91
92    /** The padding above the keyboard */
93    public final int mTopPadding;
94    /** Default gap between rows */
95    public final int mVerticalGap;
96
97    public final int mMostCommonKeyHeight;
98    public final int mMostCommonKeyWidth;
99
100    /** More keys keyboard template */
101    public final int mMoreKeysTemplate;
102
103    /** Maximum column for mini keyboard */
104    public final int mMaxMiniKeyboardColumn;
105
106    /** True if Right-To-Left keyboard */
107    public final boolean mIsRtlKeyboard;
108
109    /** List of keys and icons in this keyboard */
110    public final List<Key> mKeys;
111    public final List<Key> mShiftKeys;
112    public final Set<Key> mShiftLockKeys;
113    public final Map<Key, Drawable> mShiftedIcons;
114    public final Map<Key, Drawable> mUnshiftedIcons;
115    public final KeyboardIconsSet mIconsSet;
116
117    private final Map<Integer, Key> mKeyCache = new HashMap<Integer, Key>();
118
119    private final ProximityInfo mProximityInfo;
120
121    // TODO: Remove this variable.
122    private final KeyboardShiftState mShiftState = new KeyboardShiftState();
123
124    public Keyboard(KeyboardParams params) {
125        mId = params.mId;
126        mThemeId = params.mThemeId;
127        mOccupiedHeight = params.mOccupiedHeight;
128        mOccupiedWidth = params.mOccupiedWidth;
129        mMostCommonKeyHeight = params.mMostCommonKeyHeight;
130        mMostCommonKeyWidth = params.mMostCommonKeyWidth;
131        mIsRtlKeyboard = params.mIsRtlKeyboard;
132        mMoreKeysTemplate = params.mMoreKeysTemplate;
133        mMaxMiniKeyboardColumn = params.mMaxMiniKeyboardColumn;
134
135        mTopPadding = params.mTopPadding;
136        mVerticalGap = params.mVerticalGap;
137
138        mKeys = Collections.unmodifiableList(params.mKeys);
139        mShiftKeys = Collections.unmodifiableList(params.mShiftKeys);
140        mShiftLockKeys = Collections.unmodifiableSet(params.mShiftLockKeys);
141        mShiftedIcons = Collections.unmodifiableMap(params.mShiftedIcons);
142        mUnshiftedIcons = Collections.unmodifiableMap(params.mUnshiftedIcons);
143        mIconsSet = params.mIconsSet;
144
145        mProximityInfo = new ProximityInfo(
146                params.GRID_WIDTH, params.GRID_HEIGHT, mOccupiedWidth, mOccupiedHeight,
147                mMostCommonKeyWidth, mMostCommonKeyHeight, mKeys, params.mTouchPositionCorrection);
148    }
149
150    public ProximityInfo getProximityInfo() {
151        return mProximityInfo;
152    }
153
154    public Key getKey(int code) {
155        final Integer keyCode = code;
156        if (mKeyCache.containsKey(keyCode)) {
157            return mKeyCache.get(keyCode);
158        }
159
160        for (final Key key : mKeys) {
161            if (key.mCode == code) {
162                mKeyCache.put(keyCode, key);
163                return key;
164            }
165        }
166        mKeyCache.put(keyCode, null);
167        return null;
168    }
169
170    // TODO: Remove this method.
171    public boolean hasShiftLockKey() {
172        return !mShiftLockKeys.isEmpty();
173    }
174
175    // TODO: Remove this method.
176    public void setShiftLocked(boolean newShiftLockState) {
177        for (final Key key : mShiftLockKeys) {
178            // To represent "shift locked" state. The highlight is handled by background image that
179            // might be a StateListDrawable.
180            key.setHighlightOn(newShiftLockState);
181            key.setIcon(newShiftLockState ? mShiftedIcons.get(key) : mUnshiftedIcons.get(key));
182        }
183        mShiftState.setShiftLocked(newShiftLockState);
184    }
185
186    // TODO: Move this method to KeyboardId.
187    public boolean isShiftLocked() {
188        return mShiftState.isShiftLocked();
189    }
190
191    // TODO: Remove this method.
192    public void setShifted(boolean newShiftState) {
193        if (!mShiftState.isShiftLocked()) {
194            for (final Key key : mShiftKeys) {
195                key.setIcon(newShiftState ? mShiftedIcons.get(key) : mUnshiftedIcons.get(key));
196            }
197        }
198        mShiftState.setShifted(newShiftState);
199    }
200
201    // TODO: Move this method to KeyboardId.
202    public boolean isShiftedOrShiftLocked() {
203        return mShiftState.isShiftedOrShiftLocked();
204    }
205
206    // TODO: Remove this method
207    public void setAutomaticTemporaryUpperCase() {
208        mShiftState.setAutomaticTemporaryUpperCase();
209    }
210
211    // TODO: Move this method to KeyboardId.
212    public boolean isManualTemporaryUpperCase() {
213        return mShiftState.isManualTemporaryUpperCase();
214    }
215
216    // TODO: Remove this method.
217    public CharSequence adjustLabelCase(CharSequence label) {
218        if (isShiftedOrShiftLocked() && !TextUtils.isEmpty(label) && label.length() < 3
219                && Character.isLowerCase(label.charAt(0))) {
220            return label.toString().toUpperCase(mId.mLocale);
221        }
222        return label;
223    }
224
225    /**
226     * Returns the indices of the keys that are closest to the given point.
227     * @param x the x-coordinate of the point
228     * @param y the y-coordinate of the point
229     * @return the array of integer indices for the nearest keys to the given point. If the given
230     * point is out of range, then an array of size zero is returned.
231     */
232    public int[] getNearestKeys(int x, int y) {
233        return mProximityInfo.getNearestKeys(x, y);
234    }
235
236    public static String themeName(int themeId) {
237        // This should be aligned with theme-*.xml resource files' themeId attribute.
238        switch (themeId) {
239        case 0: return "Basic";
240        case 1: return "BasicHighContrast";
241        case 5: return "IceCreamSandwich";
242        case 6: return "Stone";
243        case 7: return "StoneBold";
244        case 8: return "GingerBread";
245        default: return null;
246        }
247    }
248
249    public static String printableCode(int code) {
250        switch (code) {
251        case CODE_SHIFT: return "shift";
252        case CODE_SWITCH_ALPHA_SYMBOL: return "symbol";
253        case CODE_CAPSLOCK: return "capslock";
254        case CODE_DELETE: return "delete";
255        case CODE_SHORTCUT: return "shortcut";
256        case CODE_DUMMY: return "dummy";
257        case CODE_UNSPECIFIED: return "unspec";
258        default:
259            if (code < 0) Log.w(TAG, "Unknow negative key code=" + code);
260            if (code < 0x100) return String.format("\\u%02x", code);
261            return String.format("\\u04x", code);
262        }
263    }
264}
265