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