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 * <Keyboard 37 * latin:keyWidth="10%p" 38 * latin:rowHeight="50px" 39 * latin:horizontalGap="2%p" 40 * latin:verticalGap="2%p" > 41 * <Row latin:keyWidth="10%p" > 42 * <Key latin:keyLabel="A" /> 43 * ... 44 * </Row> 45 * ... 46 * </Keyboard> 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 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 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 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 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 */ 164 public List<Key> getSortedKeys() { 165 return mSortedKeys; 166 } 167 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 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 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 */ 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 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