1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of 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,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.inputmethod.keyboard;
18 
19 import android.util.SparseArray;
20 
21 import com.android.inputmethod.keyboard.internal.KeyVisualAttributes;
22 import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
23 import com.android.inputmethod.keyboard.internal.KeyboardParams;
24 import com.android.inputmethod.latin.Constants;
25 import com.android.inputmethod.latin.utils.CoordinateUtils;
26 
27 import java.util.ArrayList;
28 import java.util.Collections;
29 import java.util.List;
30 
31 /**
32  * Loads an XML description of a keyboard and stores the attributes of the keys. A keyboard
33  * consists of rows of keys.
34  * <p>The layout file for a keyboard contains XML that looks like the following snippet:</p>
35  * <pre>
36  * &lt;Keyboard
37  *         latin:keyWidth="10%p"
38  *         latin:rowHeight="50px"
39  *         latin:horizontalGap="2%p"
40  *         latin:verticalGap="2%p" &gt;
41  *     &lt;Row latin:keyWidth="10%p" &gt;
42  *         &lt;Key latin:keyLabel="A" /&gt;
43  *         ...
44  *     &lt;/Row&gt;
45  *     ...
46  * &lt;/Keyboard&gt;
47  * </pre>
48  */
49 public class Keyboard {
50     public final KeyboardId mId;
51     public final int mThemeId;
52 
53     /** Total height of the keyboard, including the padding and keys */
54     public final int mOccupiedHeight;
55     /** Total width of the keyboard, including the padding and keys */
56     public final int mOccupiedWidth;
57 
58     /** Base height of the keyboard, used to calculate rows' height */
59     public final int mBaseHeight;
60     /** Base width of the keyboard, used to calculate keys' width */
61     public final int mBaseWidth;
62 
63     /** The padding above the keyboard */
64     public final int mTopPadding;
65     /** Default gap between rows */
66     public final int mVerticalGap;
67 
68     /** Per keyboard key visual parameters */
69     public final KeyVisualAttributes mKeyVisualAttributes;
70 
71     public final int mMostCommonKeyHeight;
72     public final int mMostCommonKeyWidth;
73 
74     /** More keys keyboard template */
75     public final int mMoreKeysTemplate;
76 
77     /** Maximum column for more keys keyboard */
78     public final int mMaxMoreKeysKeyboardColumn;
79 
80     /** List of keys in this keyboard */
81     private final List<Key> mSortedKeys;
82     public final List<Key> mShiftKeys;
83     public final List<Key> mAltCodeKeysWhileTyping;
84     public final KeyboardIconsSet mIconsSet;
85 
86     private final SparseArray<Key> mKeyCache = new SparseArray<>();
87 
88     private final ProximityInfo mProximityInfo;
89     private final boolean mProximityCharsCorrectionEnabled;
90 
Keyboard(final KeyboardParams params)91     public Keyboard(final KeyboardParams params) {
92         mId = params.mId;
93         mThemeId = params.mThemeId;
94         mOccupiedHeight = params.mOccupiedHeight;
95         mOccupiedWidth = params.mOccupiedWidth;
96         mBaseHeight = params.mBaseHeight;
97         mBaseWidth = params.mBaseWidth;
98         mMostCommonKeyHeight = params.mMostCommonKeyHeight;
99         mMostCommonKeyWidth = params.mMostCommonKeyWidth;
100         mMoreKeysTemplate = params.mMoreKeysTemplate;
101         mMaxMoreKeysKeyboardColumn = params.mMaxMoreKeysKeyboardColumn;
102         mKeyVisualAttributes = params.mKeyVisualAttributes;
103         mTopPadding = params.mTopPadding;
104         mVerticalGap = params.mVerticalGap;
105 
106         mSortedKeys = Collections.unmodifiableList(new ArrayList<>(params.mSortedKeys));
107         mShiftKeys = Collections.unmodifiableList(params.mShiftKeys);
108         mAltCodeKeysWhileTyping = Collections.unmodifiableList(params.mAltCodeKeysWhileTyping);
109         mIconsSet = params.mIconsSet;
110 
111         mProximityInfo = new ProximityInfo(params.mId.mLocale.toString(),
112                 params.GRID_WIDTH, params.GRID_HEIGHT, mOccupiedWidth, mOccupiedHeight,
113                 mMostCommonKeyWidth, mMostCommonKeyHeight, mSortedKeys,
114                 params.mTouchPositionCorrection);
115         mProximityCharsCorrectionEnabled = params.mProximityCharsCorrectionEnabled;
116     }
117 
Keyboard(final Keyboard keyboard)118     protected Keyboard(final Keyboard keyboard) {
119         mId = keyboard.mId;
120         mThemeId = keyboard.mThemeId;
121         mOccupiedHeight = keyboard.mOccupiedHeight;
122         mOccupiedWidth = keyboard.mOccupiedWidth;
123         mBaseHeight = keyboard.mBaseHeight;
124         mBaseWidth = keyboard.mBaseWidth;
125         mMostCommonKeyHeight = keyboard.mMostCommonKeyHeight;
126         mMostCommonKeyWidth = keyboard.mMostCommonKeyWidth;
127         mMoreKeysTemplate = keyboard.mMoreKeysTemplate;
128         mMaxMoreKeysKeyboardColumn = keyboard.mMaxMoreKeysKeyboardColumn;
129         mKeyVisualAttributes = keyboard.mKeyVisualAttributes;
130         mTopPadding = keyboard.mTopPadding;
131         mVerticalGap = keyboard.mVerticalGap;
132 
133         mSortedKeys = keyboard.mSortedKeys;
134         mShiftKeys = keyboard.mShiftKeys;
135         mAltCodeKeysWhileTyping = keyboard.mAltCodeKeysWhileTyping;
136         mIconsSet = keyboard.mIconsSet;
137 
138         mProximityInfo = keyboard.mProximityInfo;
139         mProximityCharsCorrectionEnabled = keyboard.mProximityCharsCorrectionEnabled;
140     }
141 
hasProximityCharsCorrection(final int code)142     public boolean hasProximityCharsCorrection(final int code) {
143         if (!mProximityCharsCorrectionEnabled) {
144             return false;
145         }
146         // Note: The native code has the main keyboard layout only at this moment.
147         // TODO: Figure out how to handle proximity characters information of all layouts.
148         final boolean canAssumeNativeHasProximityCharsInfoOfAllKeys = (
149                 mId.mElementId == KeyboardId.ELEMENT_ALPHABET
150                 || mId.mElementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED);
151         return canAssumeNativeHasProximityCharsInfoOfAllKeys || Character.isLetter(code);
152     }
153 
getProximityInfo()154     public ProximityInfo getProximityInfo() {
155         return mProximityInfo;
156     }
157 
158     /**
159      * Return the sorted list of keys of this keyboard.
160      * The keys are sorted from top-left to bottom-right order.
161      * The list may contain {@link Key.Spacer} object as well.
162      * @return the sorted unmodifiable list of {@link Key}s of this keyboard.
163      */
getSortedKeys()164     public List<Key> getSortedKeys() {
165         return mSortedKeys;
166     }
167 
getKey(final int code)168     public Key getKey(final int code) {
169         if (code == Constants.CODE_UNSPECIFIED) {
170             return null;
171         }
172         synchronized (mKeyCache) {
173             final int index = mKeyCache.indexOfKey(code);
174             if (index >= 0) {
175                 return mKeyCache.valueAt(index);
176             }
177 
178             for (final Key key : getSortedKeys()) {
179                 if (key.getCode() == code) {
180                     mKeyCache.put(code, key);
181                     return key;
182                 }
183             }
184             mKeyCache.put(code, null);
185             return null;
186         }
187     }
188 
hasKey(final Key aKey)189     public boolean hasKey(final Key aKey) {
190         if (mKeyCache.indexOfValue(aKey) >= 0) {
191             return true;
192         }
193 
194         for (final Key key : getSortedKeys()) {
195             if (key == aKey) {
196                 mKeyCache.put(key.getCode(), key);
197                 return true;
198             }
199         }
200         return false;
201     }
202 
203     @Override
toString()204     public String toString() {
205         return mId.toString();
206     }
207 
208     /**
209      * Returns the array of the keys that are closest to the given point.
210      * @param x the x-coordinate of the point
211      * @param y the y-coordinate of the point
212      * @return the list of the nearest keys to the given point. If the given
213      * point is out of range, then an array of size zero is returned.
214      */
getNearestKeys(final int x, final int y)215     public List<Key> getNearestKeys(final int x, final int y) {
216         // Avoid dead pixels at edges of the keyboard
217         final int adjustedX = Math.max(0, Math.min(x, mOccupiedWidth - 1));
218         final int adjustedY = Math.max(0, Math.min(y, mOccupiedHeight - 1));
219         return mProximityInfo.getNearestKeys(adjustedX, adjustedY);
220     }
221 
getCoordinates(final int[] codePoints)222     public int[] getCoordinates(final int[] codePoints) {
223         final int length = codePoints.length;
224         final int[] coordinates = CoordinateUtils.newCoordinateArray(length);
225         for (int i = 0; i < length; ++i) {
226             final Key key = getKey(codePoints[i]);
227             if (null != key) {
228                 CoordinateUtils.setXYInArray(coordinates, i,
229                         key.getX() + key.getWidth() / 2, key.getY() + key.getHeight() / 2);
230             } else {
231                 CoordinateUtils.setXYInArray(coordinates, i,
232                         Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
233             }
234         }
235         return coordinates;
236     }
237 }
238