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.util.Log; 20import android.util.SparseArray; 21 22import com.android.inputmethod.keyboard.internal.KeyVisualAttributes; 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 * <Keyboard 35 * latin:keyWidth="%10p" 36 * latin:keyHeight="50px" 37 * latin:horizontalGap="2px" 38 * latin:verticalGap="2px" > 39 * <Row latin:keyWidth="32px" > 40 * <Key latin:keyLabel="A" /> 41 * ... 42 * </Row> 43 * ... 44 * </Keyboard> 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 public static final int CODE_QUESTION_MARK = '?'; 61 public static final int CODE_EXCLAMATION_MARK = '!'; 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 70 /** Special keys code. Must be negative. 71 * These should be aligned with KeyboardCodesSet.ID_TO_NAME[], 72 * KeyboardCodesSet.DEFAULT[] and KeyboardCodesSet.RTL[] 73 */ 74 public static final int CODE_SHIFT = -1; 75 public static final int CODE_SWITCH_ALPHA_SYMBOL = -2; 76 public static final int CODE_OUTPUT_TEXT = -3; 77 public static final int CODE_DELETE = -4; 78 public static final int CODE_SETTINGS = -5; 79 public static final int CODE_SHORTCUT = -6; 80 public static final int CODE_ACTION_ENTER = -7; 81 public static final int CODE_ACTION_NEXT = -8; 82 public static final int CODE_ACTION_PREVIOUS = -9; 83 public static final int CODE_LANGUAGE_SWITCH = -10; 84 public static final int CODE_RESEARCH = -11; 85 // Code value representing the code is not specified. 86 public static final int CODE_UNSPECIFIED = -12; 87 88 public final KeyboardId mId; 89 public final int mThemeId; 90 91 /** Total height of the keyboard, including the padding and keys */ 92 public final int mOccupiedHeight; 93 /** Total width of the keyboard, including the padding and keys */ 94 public final int mOccupiedWidth; 95 96 /** The padding above the keyboard */ 97 public final int mTopPadding; 98 /** Default gap between rows */ 99 public final int mVerticalGap; 100 101 /** Per keyboard key visual parameters */ 102 public final KeyVisualAttributes mKeyVisualAttributes; 103 104 public final int mMostCommonKeyHeight; 105 public final int mMostCommonKeyWidth; 106 107 /** More keys keyboard template */ 108 public final int mMoreKeysTemplate; 109 110 /** Maximum column for more keys keyboard */ 111 public final int mMaxMoreKeysKeyboardColumn; 112 113 /** Array of keys and icons in this keyboard */ 114 public final Key[] mKeys; 115 public final Key[] mShiftKeys; 116 public final Key[] mAltCodeKeysWhileTyping; 117 public final KeyboardIconsSet mIconsSet; 118 119 private final SparseArray<Key> mKeyCache = CollectionUtils.newSparseArray(); 120 121 private final ProximityInfo mProximityInfo; 122 private final boolean mProximityCharsCorrectionEnabled; 123 124 public Keyboard(final 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 mMoreKeysTemplate = params.mMoreKeysTemplate; 132 mMaxMoreKeysKeyboardColumn = params.mMaxMoreKeysKeyboardColumn; 133 mKeyVisualAttributes = params.mKeyVisualAttributes; 134 mTopPadding = params.mTopPadding; 135 mVerticalGap = params.mVerticalGap; 136 137 mKeys = params.mKeys.toArray(new Key[params.mKeys.size()]); 138 mShiftKeys = params.mShiftKeys.toArray(new Key[params.mShiftKeys.size()]); 139 mAltCodeKeysWhileTyping = params.mAltCodeKeysWhileTyping.toArray( 140 new Key[params.mAltCodeKeysWhileTyping.size()]); 141 mIconsSet = params.mIconsSet; 142 143 mProximityInfo = new ProximityInfo(params.mId.mLocale.toString(), 144 params.GRID_WIDTH, params.GRID_HEIGHT, mOccupiedWidth, mOccupiedHeight, 145 mMostCommonKeyWidth, mMostCommonKeyHeight, mKeys, params.mTouchPositionCorrection); 146 mProximityCharsCorrectionEnabled = params.mProximityCharsCorrectionEnabled; 147 } 148 149 public boolean hasProximityCharsCorrection(final int code) { 150 if (!mProximityCharsCorrectionEnabled) { 151 return false; 152 } 153 // Note: The native code has the main keyboard layout only at this moment. 154 // TODO: Figure out how to handle proximity characters information of all layouts. 155 final boolean canAssumeNativeHasProximityCharsInfoOfAllKeys = ( 156 mId.mElementId == KeyboardId.ELEMENT_ALPHABET 157 || mId.mElementId == KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED); 158 return canAssumeNativeHasProximityCharsInfoOfAllKeys || Character.isLetter(code); 159 } 160 161 public ProximityInfo getProximityInfo() { 162 return mProximityInfo; 163 } 164 165 public Key getKey(final int code) { 166 if (code == CODE_UNSPECIFIED) { 167 return null; 168 } 169 synchronized (mKeyCache) { 170 final int index = mKeyCache.indexOfKey(code); 171 if (index >= 0) { 172 return mKeyCache.valueAt(index); 173 } 174 175 for (final Key key : mKeys) { 176 if (key.mCode == code) { 177 mKeyCache.put(code, key); 178 return key; 179 } 180 } 181 mKeyCache.put(code, null); 182 return null; 183 } 184 } 185 186 public boolean hasKey(final Key aKey) { 187 if (mKeyCache.indexOfValue(aKey) >= 0) { 188 return true; 189 } 190 191 for (final Key key : mKeys) { 192 if (key == aKey) { 193 mKeyCache.put(key.mCode, key); 194 return true; 195 } 196 } 197 return false; 198 } 199 200 public static boolean isLetterCode(final int code) { 201 return code >= CODE_SPACE; 202 } 203 204 @Override 205 public String toString() { 206 return mId.toString(); 207 } 208 209 /** 210 * Returns the array of the keys that are closest to the given point. 211 * @param x the x-coordinate of the point 212 * @param y the y-coordinate of the point 213 * @return the array of the nearest keys to the given point. If the given 214 * point is out of range, then an array of size zero is returned. 215 */ 216 public Key[] getNearestKeys(final int x, final int y) { 217 // Avoid dead pixels at edges of the keyboard 218 final int adjustedX = Math.max(0, Math.min(x, mOccupiedWidth - 1)); 219 final int adjustedY = Math.max(0, Math.min(y, mOccupiedHeight - 1)); 220 return mProximityInfo.getNearestKeys(adjustedX, adjustedY); 221 } 222 223 public static String printableCode(final int code) { 224 switch (code) { 225 case CODE_SHIFT: return "shift"; 226 case CODE_SWITCH_ALPHA_SYMBOL: return "symbol"; 227 case CODE_OUTPUT_TEXT: return "text"; 228 case CODE_DELETE: return "delete"; 229 case CODE_SETTINGS: return "settings"; 230 case CODE_SHORTCUT: return "shortcut"; 231 case CODE_ACTION_ENTER: return "actionEnter"; 232 case CODE_ACTION_NEXT: return "actionNext"; 233 case CODE_ACTION_PREVIOUS: return "actionPrevious"; 234 case CODE_LANGUAGE_SWITCH: return "languageSwitch"; 235 case CODE_UNSPECIFIED: return "unspec"; 236 case CODE_TAB: return "tab"; 237 case CODE_ENTER: return "enter"; 238 default: 239 if (code <= 0) Log.w(TAG, "Unknown non-positive key code=" + code); 240 if (code < CODE_SPACE) return String.format("'\\u%02x'", code); 241 if (code < 0x100) return String.format("'%c'", code); 242 return String.format("'\\u%04x'", code); 243 } 244 } 245} 246