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.CollectionUtils; 26 27 /** 28 * Loads an XML description of a keyboard and stores the attributes of the keys. A keyboard 29 * consists of rows of keys. 30 * <p>The layout file for a keyboard contains XML that looks like the following snippet:</p> 31 * <pre> 32 * <Keyboard 33 * latin:keyWidth="10%p" 34 * latin:rowHeight="50px" 35 * latin:horizontalGap="2%p" 36 * latin:verticalGap="2%p" > 37 * <Row latin:keyWidth="10%p" > 38 * <Key latin:keyLabel="A" /> 39 * ... 40 * </Row> 41 * ... 42 * </Keyboard> 43 * </pre> 44 */ 45 public class Keyboard { 46 public final KeyboardId mId; 47 public final int mThemeId; 48 49 /** Total height of the keyboard, including the padding and keys */ 50 public final int mOccupiedHeight; 51 /** Total width of the keyboard, including the padding and keys */ 52 public final int mOccupiedWidth; 53 54 /** Base height of the keyboard, used to calculate rows' height */ 55 public final int mBaseHeight; 56 /** Base width of the keyboard, used to calculate keys' width */ 57 public final int mBaseWidth; 58 59 /** The padding above the keyboard */ 60 public final int mTopPadding; 61 /** Default gap between rows */ 62 public final int mVerticalGap; 63 64 /** Per keyboard key visual parameters */ 65 public final KeyVisualAttributes mKeyVisualAttributes; 66 67 public final int mMostCommonKeyHeight; 68 public final int mMostCommonKeyWidth; 69 70 /** More keys keyboard template */ 71 public final int mMoreKeysTemplate; 72 73 /** Maximum column for more keys keyboard */ 74 public final int mMaxMoreKeysKeyboardColumn; 75 76 /** Array of keys and icons in this keyboard */ 77 private final Key[] mKeys; 78 public final Key[] mShiftKeys; 79 public final Key[] mAltCodeKeysWhileTyping; 80 public final KeyboardIconsSet mIconsSet; 81 82 private final SparseArray<Key> mKeyCache = CollectionUtils.newSparseArray(); 83 84 private final ProximityInfo mProximityInfo; 85 private final boolean mProximityCharsCorrectionEnabled; 86 87 public Keyboard(final KeyboardParams params) { 88 mId = params.mId; 89 mThemeId = params.mThemeId; 90 mOccupiedHeight = params.mOccupiedHeight; 91 mOccupiedWidth = params.mOccupiedWidth; 92 mBaseHeight = params.mBaseHeight; 93 mBaseWidth = params.mBaseWidth; 94 mMostCommonKeyHeight = params.mMostCommonKeyHeight; 95 mMostCommonKeyWidth = params.mMostCommonKeyWidth; 96 mMoreKeysTemplate = params.mMoreKeysTemplate; 97 mMaxMoreKeysKeyboardColumn = params.mMaxMoreKeysKeyboardColumn; 98 mKeyVisualAttributes = params.mKeyVisualAttributes; 99 mTopPadding = params.mTopPadding; 100 mVerticalGap = params.mVerticalGap; 101 102 mKeys = params.mKeys.toArray(new Key[params.mKeys.size()]); 103 mShiftKeys = params.mShiftKeys.toArray(new Key[params.mShiftKeys.size()]); 104 mAltCodeKeysWhileTyping = params.mAltCodeKeysWhileTyping.toArray( 105 new Key[params.mAltCodeKeysWhileTyping.size()]); 106 mIconsSet = params.mIconsSet; 107 108 mProximityInfo = new ProximityInfo(params.mId.mLocale.toString(), 109 params.GRID_WIDTH, params.GRID_HEIGHT, mOccupiedWidth, mOccupiedHeight, 110 mMostCommonKeyWidth, mMostCommonKeyHeight, mKeys, params.mTouchPositionCorrection); 111 mProximityCharsCorrectionEnabled = params.mProximityCharsCorrectionEnabled; 112 } 113 114 protected Keyboard(final Keyboard keyboard) { 115 mId = keyboard.mId; 116 mThemeId = keyboard.mThemeId; 117 mOccupiedHeight = keyboard.mOccupiedHeight; 118 mOccupiedWidth = keyboard.mOccupiedWidth; 119 mBaseHeight = keyboard.mBaseHeight; 120 mBaseWidth = keyboard.mBaseWidth; 121 mMostCommonKeyHeight = keyboard.mMostCommonKeyHeight; 122 mMostCommonKeyWidth = keyboard.mMostCommonKeyWidth; 123 mMoreKeysTemplate = keyboard.mMoreKeysTemplate; 124 mMaxMoreKeysKeyboardColumn = keyboard.mMaxMoreKeysKeyboardColumn; 125 mKeyVisualAttributes = keyboard.mKeyVisualAttributes; 126 mTopPadding = keyboard.mTopPadding; 127 mVerticalGap = keyboard.mVerticalGap; 128 129 mKeys = keyboard.mKeys; 130 mShiftKeys = keyboard.mShiftKeys; 131 mAltCodeKeysWhileTyping = keyboard.mAltCodeKeysWhileTyping; 132 mIconsSet = keyboard.mIconsSet; 133 134 mProximityInfo = keyboard.mProximityInfo; 135 mProximityCharsCorrectionEnabled = keyboard.mProximityCharsCorrectionEnabled; 136 } 137 138 public boolean hasProximityCharsCorrection(final int code) { 139 if (!mProximityCharsCorrectionEnabled) { 140 return false; 141 } 142 // Note: The native code has the main keyboard layout only at this moment. 143 // TODO: Figure out how to handle proximity characters information of all layouts. 144 final boolean canAssumeNativeHasProximityCharsInfoOfAllKeys = ( 145 mId.mElementId == KeyboardId.ELEMENT_ALPHABET 146 || mId.mElementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED); 147 return canAssumeNativeHasProximityCharsInfoOfAllKeys || Character.isLetter(code); 148 } 149 150 public ProximityInfo getProximityInfo() { 151 return mProximityInfo; 152 } 153 154 public Key[] getKeys() { 155 return mKeys; 156 } 157 158 public Key getKeyFromOutputText(final String outputText) { 159 for (final Key key : getKeys()) { 160 if (outputText.equals(key.getOutputText())) { 161 return key; 162 } 163 } 164 return null; 165 } 166 167 public Key getKey(final int code) { 168 if (code == Constants.CODE_UNSPECIFIED) { 169 return null; 170 } 171 synchronized (mKeyCache) { 172 final int index = mKeyCache.indexOfKey(code); 173 if (index >= 0) { 174 return mKeyCache.valueAt(index); 175 } 176 177 for (final Key key : getKeys()) { 178 if (key.getCode() == code) { 179 mKeyCache.put(code, key); 180 return key; 181 } 182 } 183 mKeyCache.put(code, null); 184 return null; 185 } 186 } 187 188 public boolean hasKey(final Key aKey) { 189 if (mKeyCache.indexOfValue(aKey) >= 0) { 190 return true; 191 } 192 193 for (final Key key : getKeys()) { 194 if (key == aKey) { 195 mKeyCache.put(key.getCode(), key); 196 return true; 197 } 198 } 199 return false; 200 } 201 202 @Override 203 public String toString() { 204 return mId.toString(); 205 } 206 207 /** 208 * Returns the array of the keys that are closest to the given point. 209 * @param x the x-coordinate of the point 210 * @param y the y-coordinate of the point 211 * @return the array of the nearest keys to the given point. If the given 212 * point is out of range, then an array of size zero is returned. 213 */ 214 public Key[] getNearestKeys(final int x, final int y) { 215 // Avoid dead pixels at edges of the keyboard 216 final int adjustedX = Math.max(0, Math.min(x, mOccupiedWidth - 1)); 217 final int adjustedY = Math.max(0, Math.min(y, mOccupiedHeight - 1)); 218 return mProximityInfo.getNearestKeys(adjustedX, adjustedY); 219 } 220 } 221