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