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 17 package com.android.inputmethod.keyboard; 18 19 import static com.android.inputmethod.keyboard.Keyboard.CODE_OUTPUT_TEXT; 20 import static com.android.inputmethod.keyboard.Keyboard.CODE_SHIFT; 21 import static com.android.inputmethod.keyboard.Keyboard.CODE_SWITCH_ALPHA_SYMBOL; 22 import static com.android.inputmethod.keyboard.Keyboard.CODE_UNSPECIFIED; 23 import static com.android.inputmethod.keyboard.internal.KeyboardIconsSet.ICON_UNDEFINED; 24 25 import android.content.res.Resources; 26 import android.content.res.TypedArray; 27 import android.graphics.Rect; 28 import android.graphics.Typeface; 29 import android.graphics.drawable.Drawable; 30 import android.text.TextUtils; 31 import android.util.Log; 32 import android.util.Xml; 33 34 import com.android.inputmethod.keyboard.internal.KeySpecParser; 35 import com.android.inputmethod.keyboard.internal.KeySpecParser.MoreKeySpec; 36 import com.android.inputmethod.keyboard.internal.KeyStyles.KeyStyle; 37 import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; 38 import com.android.inputmethod.latin.R; 39 import com.android.inputmethod.latin.StringUtils; 40 41 import org.xmlpull.v1.XmlPullParser; 42 import org.xmlpull.v1.XmlPullParserException; 43 44 import java.util.Arrays; 45 import java.util.Locale; 46 47 /** 48 * Class for describing the position and characteristics of a single key in the keyboard. 49 */ 50 public class Key { 51 private static final String TAG = Key.class.getSimpleName(); 52 53 /** 54 * The key code (unicode or custom code) that this key generates. 55 */ 56 public final int mCode; 57 public final int mAltCode; 58 59 /** Label to display */ 60 public final String mLabel; 61 /** Hint label to display on the key in conjunction with the label */ 62 public final String mHintLabel; 63 /** Flags of the label */ 64 private final int mLabelFlags; 65 private static final int LABEL_FLAGS_ALIGN_LEFT = 0x01; 66 private static final int LABEL_FLAGS_ALIGN_RIGHT = 0x02; 67 private static final int LABEL_FLAGS_ALIGN_LEFT_OF_CENTER = 0x08; 68 private static final int LABEL_FLAGS_FONT_NORMAL = 0x10; 69 private static final int LABEL_FLAGS_FONT_MONO_SPACE = 0x20; 70 // Start of key text ratio enum values 71 private static final int LABEL_FLAGS_FOLLOW_KEY_TEXT_RATIO_MASK = 0x1C0; 72 private static final int LABEL_FLAGS_FOLLOW_KEY_LARGE_LETTER_RATIO = 0x40; 73 private static final int LABEL_FLAGS_FOLLOW_KEY_LETTER_RATIO = 0x80; 74 private static final int LABEL_FLAGS_FOLLOW_KEY_LABEL_RATIO = 0xC0; 75 private static final int LABEL_FLAGS_FOLLOW_KEY_LARGE_LABEL_RATIO = 0x100; 76 private static final int LABEL_FLAGS_FOLLOW_KEY_HINT_LABEL_RATIO = 0x140; 77 // End of key text ratio mask enum values 78 private static final int LABEL_FLAGS_HAS_POPUP_HINT = 0x200; 79 private static final int LABEL_FLAGS_HAS_SHIFTED_LETTER_HINT = 0x400; 80 private static final int LABEL_FLAGS_HAS_HINT_LABEL = 0x800; 81 private static final int LABEL_FLAGS_WITH_ICON_LEFT = 0x1000; 82 private static final int LABEL_FLAGS_WITH_ICON_RIGHT = 0x2000; 83 private static final int LABEL_FLAGS_AUTO_X_SCALE = 0x4000; 84 private static final int LABEL_FLAGS_PRESERVE_CASE = 0x8000; 85 private static final int LABEL_FLAGS_SHIFTED_LETTER_ACTIVATED = 0x10000; 86 private static final int LABEL_FLAGS_FROM_CUSTOM_ACTION_LABEL = 0x20000; 87 private static final int LABEL_FLAGS_DISABLE_HINT_LABEL = 0x40000000; 88 private static final int LABEL_FLAGS_DISABLE_ADDITIONAL_MORE_KEYS = 0x80000000; 89 90 /** Icon to display instead of a label. Icon takes precedence over a label */ 91 private final int mIconId; 92 /** Icon for disabled state */ 93 private final int mDisabledIconId; 94 /** Preview version of the icon, for the preview popup */ 95 private final int mPreviewIconId; 96 97 /** Width of the key, not including the gap */ 98 public final int mWidth; 99 /** Height of the key, not including the gap */ 100 public final int mHeight; 101 /** The horizontal gap around this key */ 102 public final int mHorizontalGap; 103 /** The vertical gap below this key */ 104 public final int mVerticalGap; 105 /** The visual insets */ 106 public final int mVisualInsetsLeft; 107 public final int mVisualInsetsRight; 108 /** X coordinate of the key in the keyboard layout */ 109 public final int mX; 110 /** Y coordinate of the key in the keyboard layout */ 111 public final int mY; 112 /** Hit bounding box of the key */ 113 public final Rect mHitBox = new Rect(); 114 115 /** Text to output when pressed. This can be multiple characters, like ".com" */ 116 public final CharSequence mOutputText; 117 /** More keys */ 118 public final MoreKeySpec[] mMoreKeys; 119 /** More keys column number and flags */ 120 private final int mMoreKeysColumnAndFlags; 121 private static final int MORE_KEYS_COLUMN_MASK = 0x000000ff; 122 private static final int MORE_KEYS_FLAGS_FIXED_COLUMN_ORDER = 0x80000000; 123 private static final int MORE_KEYS_FLAGS_HAS_LABELS = 0x40000000; 124 private static final int MORE_KEYS_FLAGS_NEEDS_DIVIDERS = 0x20000000; 125 private static final int MORE_KEYS_FLAGS_EMBEDDED_MORE_KEY = 0x10000000; 126 private static final String MORE_KEYS_AUTO_COLUMN_ORDER = "!autoColumnOrder!"; 127 private static final String MORE_KEYS_FIXED_COLUMN_ORDER = "!fixedColumnOrder!"; 128 private static final String MORE_KEYS_HAS_LABELS = "!hasLabels!"; 129 private static final String MORE_KEYS_NEEDS_DIVIDERS = "!needsDividers!"; 130 private static final String MORE_KEYS_EMBEDDED_MORE_KEY = "!embeddedMoreKey!"; 131 132 /** Background type that represents different key background visual than normal one. */ 133 public final int mBackgroundType; 134 public static final int BACKGROUND_TYPE_NORMAL = 0; 135 public static final int BACKGROUND_TYPE_FUNCTIONAL = 1; 136 public static final int BACKGROUND_TYPE_ACTION = 2; 137 public static final int BACKGROUND_TYPE_STICKY_OFF = 3; 138 public static final int BACKGROUND_TYPE_STICKY_ON = 4; 139 140 private final int mActionFlags; 141 private static final int ACTION_FLAGS_IS_REPEATABLE = 0x01; 142 private static final int ACTION_FLAGS_NO_KEY_PREVIEW = 0x02; 143 private static final int ACTION_FLAGS_ALT_CODE_WHILE_TYPING = 0x04; 144 private static final int ACTION_FLAGS_ENABLE_LONG_PRESS = 0x08; 145 146 private final int mHashCode; 147 148 /** The current pressed state of this key */ 149 private boolean mPressed; 150 /** Key is enabled and responds on press */ 151 private boolean mEnabled = true; 152 153 /** 154 * This constructor is being used only for keys in more keys keyboard. 155 */ Key(Keyboard.Params params, MoreKeySpec moreKeySpec, int x, int y, int width, int height, int labelFlags)156 public Key(Keyboard.Params params, MoreKeySpec moreKeySpec, int x, int y, int width, int height, 157 int labelFlags) { 158 this(params, moreKeySpec.mLabel, null, moreKeySpec.mIconId, moreKeySpec.mCode, 159 moreKeySpec.mOutputText, x, y, width, height, labelFlags); 160 } 161 162 /** 163 * This constructor is being used only for key in popup suggestions pane. 164 */ Key(Keyboard.Params params, String label, String hintLabel, int iconId, int code, String outputText, int x, int y, int width, int height, int labelFlags)165 public Key(Keyboard.Params params, String label, String hintLabel, int iconId, 166 int code, String outputText, int x, int y, int width, int height, int labelFlags) { 167 mHeight = height - params.mVerticalGap; 168 mHorizontalGap = params.mHorizontalGap; 169 mVerticalGap = params.mVerticalGap; 170 mVisualInsetsLeft = mVisualInsetsRight = 0; 171 mWidth = width - mHorizontalGap; 172 mHintLabel = hintLabel; 173 mLabelFlags = labelFlags; 174 mBackgroundType = BACKGROUND_TYPE_NORMAL; 175 mActionFlags = 0; 176 mMoreKeys = null; 177 mMoreKeysColumnAndFlags = 0; 178 mLabel = label; 179 mOutputText = outputText; 180 mCode = code; 181 mEnabled = (code != CODE_UNSPECIFIED); 182 mAltCode = CODE_UNSPECIFIED; 183 mIconId = iconId; 184 mDisabledIconId = ICON_UNDEFINED; 185 mPreviewIconId = ICON_UNDEFINED; 186 // Horizontal gap is divided equally to both sides of the key. 187 mX = x + mHorizontalGap / 2; 188 mY = y; 189 mHitBox.set(x, y, x + width + 1, y + height); 190 191 mHashCode = computeHashCode(this); 192 } 193 194 /** 195 * Create a key with the given top-left coordinate and extract its attributes from the XML 196 * parser. 197 * @param res resources associated with the caller's context 198 * @param params the keyboard building parameters. 199 * @param row the row that this key belongs to. row's x-coordinate will be the right edge of 200 * this key. 201 * @param parser the XML parser containing the attributes for this key 202 * @throws XmlPullParserException 203 */ Key(Resources res, Keyboard.Params params, Keyboard.Builder.Row row, XmlPullParser parser)204 public Key(Resources res, Keyboard.Params params, Keyboard.Builder.Row row, 205 XmlPullParser parser) throws XmlPullParserException { 206 final float horizontalGap = isSpacer() ? 0 : params.mHorizontalGap; 207 final int keyHeight = row.mRowHeight; 208 mVerticalGap = params.mVerticalGap; 209 mHeight = keyHeight - mVerticalGap; 210 211 final TypedArray keyAttr = res.obtainAttributes(Xml.asAttributeSet(parser), 212 R.styleable.Keyboard_Key); 213 214 final KeyStyle style = params.mKeyStyles.getKeyStyle(keyAttr, parser); 215 final float keyXPos = row.getKeyX(keyAttr); 216 final float keyWidth = row.getKeyWidth(keyAttr, keyXPos); 217 final int keyYPos = row.getKeyY(); 218 219 // Horizontal gap is divided equally to both sides of the key. 220 mX = Math.round(keyXPos + horizontalGap / 2); 221 mY = keyYPos; 222 mWidth = Math.round(keyWidth - horizontalGap); 223 mHorizontalGap = Math.round(horizontalGap); 224 mHitBox.set(Math.round(keyXPos), keyYPos, Math.round(keyXPos + keyWidth) + 1, 225 keyYPos + keyHeight); 226 // Update row to have current x coordinate. 227 row.setXPos(keyXPos + keyWidth); 228 229 mBackgroundType = style.getInt(keyAttr, 230 R.styleable.Keyboard_Key_backgroundType, row.getDefaultBackgroundType()); 231 232 mVisualInsetsLeft = Math.round(Keyboard.Builder.getDimensionOrFraction(keyAttr, 233 R.styleable.Keyboard_Key_visualInsetsLeft, params.mBaseWidth, 0)); 234 mVisualInsetsRight = Math.round(Keyboard.Builder.getDimensionOrFraction(keyAttr, 235 R.styleable.Keyboard_Key_visualInsetsRight, params.mBaseWidth, 0)); 236 mIconId = KeySpecParser.getIconId(style.getString(keyAttr, 237 R.styleable.Keyboard_Key_keyIcon)); 238 mDisabledIconId = KeySpecParser.getIconId(style.getString(keyAttr, 239 R.styleable.Keyboard_Key_keyIconDisabled)); 240 mPreviewIconId = KeySpecParser.getIconId(style.getString(keyAttr, 241 R.styleable.Keyboard_Key_keyIconPreview)); 242 243 mLabelFlags = style.getFlag(keyAttr, R.styleable.Keyboard_Key_keyLabelFlags) 244 | row.getDefaultKeyLabelFlags(); 245 final boolean needsToUpperCase = needsToUpperCase(mLabelFlags, params.mId.mElementId); 246 final Locale locale = params.mId.mLocale; 247 int actionFlags = style.getFlag(keyAttr, R.styleable.Keyboard_Key_keyActionFlags); 248 String[] moreKeys = style.getStringArray(keyAttr, R.styleable.Keyboard_Key_moreKeys); 249 250 int moreKeysColumn = style.getInt(keyAttr, 251 R.styleable.Keyboard_Key_maxMoreKeysColumn, params.mMaxMoreKeysKeyboardColumn); 252 int value; 253 if ((value = KeySpecParser.getIntValue(moreKeys, MORE_KEYS_AUTO_COLUMN_ORDER, -1)) > 0) { 254 moreKeysColumn = value & MORE_KEYS_COLUMN_MASK; 255 } 256 if ((value = KeySpecParser.getIntValue(moreKeys, MORE_KEYS_FIXED_COLUMN_ORDER, -1)) > 0) { 257 moreKeysColumn = MORE_KEYS_FLAGS_FIXED_COLUMN_ORDER | (value & MORE_KEYS_COLUMN_MASK); 258 } 259 if (KeySpecParser.getBooleanValue(moreKeys, MORE_KEYS_HAS_LABELS)) { 260 moreKeysColumn |= MORE_KEYS_FLAGS_HAS_LABELS; 261 } 262 if (KeySpecParser.getBooleanValue(moreKeys, MORE_KEYS_NEEDS_DIVIDERS)) { 263 moreKeysColumn |= MORE_KEYS_FLAGS_NEEDS_DIVIDERS; 264 } 265 if (KeySpecParser.getBooleanValue(moreKeys, MORE_KEYS_EMBEDDED_MORE_KEY)) { 266 moreKeysColumn |= MORE_KEYS_FLAGS_EMBEDDED_MORE_KEY; 267 } 268 mMoreKeysColumnAndFlags = moreKeysColumn; 269 270 final String[] additionalMoreKeys; 271 if ((mLabelFlags & LABEL_FLAGS_DISABLE_ADDITIONAL_MORE_KEYS) != 0) { 272 additionalMoreKeys = null; 273 } else { 274 additionalMoreKeys = style.getStringArray(keyAttr, 275 R.styleable.Keyboard_Key_additionalMoreKeys); 276 } 277 moreKeys = KeySpecParser.insertAdditionalMoreKeys(moreKeys, additionalMoreKeys); 278 if (moreKeys != null) { 279 actionFlags |= ACTION_FLAGS_ENABLE_LONG_PRESS; 280 mMoreKeys = new MoreKeySpec[moreKeys.length]; 281 for (int i = 0; i < moreKeys.length; i++) { 282 mMoreKeys[i] = new MoreKeySpec( 283 moreKeys[i], needsToUpperCase, locale, params.mCodesSet); 284 } 285 } else { 286 mMoreKeys = null; 287 } 288 mActionFlags = actionFlags; 289 290 if ((mLabelFlags & LABEL_FLAGS_FROM_CUSTOM_ACTION_LABEL) != 0) { 291 mLabel = params.mId.mCustomActionLabel; 292 } else { 293 mLabel = KeySpecParser.toUpperCaseOfStringForLocale(style.getString(keyAttr, 294 R.styleable.Keyboard_Key_keyLabel), needsToUpperCase, locale); 295 } 296 if ((mLabelFlags & LABEL_FLAGS_DISABLE_HINT_LABEL) != 0) { 297 mHintLabel = null; 298 } else { 299 mHintLabel = KeySpecParser.toUpperCaseOfStringForLocale(style.getString(keyAttr, 300 R.styleable.Keyboard_Key_keyHintLabel), needsToUpperCase, locale); 301 } 302 String outputText = KeySpecParser.toUpperCaseOfStringForLocale(style.getString(keyAttr, 303 R.styleable.Keyboard_Key_keyOutputText), needsToUpperCase, locale); 304 final int code = KeySpecParser.parseCode(style.getString(keyAttr, 305 R.styleable.Keyboard_Key_code), params.mCodesSet, CODE_UNSPECIFIED); 306 // Choose the first letter of the label as primary code if not specified. 307 if (code == CODE_UNSPECIFIED && TextUtils.isEmpty(outputText) 308 && !TextUtils.isEmpty(mLabel)) { 309 if (StringUtils.codePointCount(mLabel) == 1) { 310 // Use the first letter of the hint label if shiftedLetterActivated flag is 311 // specified. 312 if (hasShiftedLetterHint() && isShiftedLetterActivated() 313 && !TextUtils.isEmpty(mHintLabel)) { 314 mCode = mHintLabel.codePointAt(0); 315 } else { 316 mCode = mLabel.codePointAt(0); 317 } 318 } else { 319 // In some locale and case, the character might be represented by multiple code 320 // points, such as upper case Eszett of German alphabet. 321 outputText = mLabel; 322 mCode = CODE_OUTPUT_TEXT; 323 } 324 } else if (code == CODE_UNSPECIFIED && outputText != null) { 325 if (StringUtils.codePointCount(outputText) == 1) { 326 mCode = outputText.codePointAt(0); 327 outputText = null; 328 } else { 329 mCode = CODE_OUTPUT_TEXT; 330 } 331 } else { 332 mCode = KeySpecParser.toUpperCaseOfCodeForLocale(code, needsToUpperCase, locale); 333 } 334 mOutputText = outputText; 335 mAltCode = KeySpecParser.toUpperCaseOfCodeForLocale( 336 KeySpecParser.parseCode(style.getString(keyAttr, 337 R.styleable.Keyboard_Key_altCode), params.mCodesSet, CODE_UNSPECIFIED), 338 needsToUpperCase, locale); 339 mHashCode = computeHashCode(this); 340 341 keyAttr.recycle(); 342 343 if (hasShiftedLetterHint() && TextUtils.isEmpty(mHintLabel)) { 344 Log.w(TAG, "hasShiftedLetterHint specified without keyHintLabel: " + this); 345 } 346 } 347 needsToUpperCase(int labelFlags, int keyboardElementId)348 private static boolean needsToUpperCase(int labelFlags, int keyboardElementId) { 349 if ((labelFlags & LABEL_FLAGS_PRESERVE_CASE) != 0) return false; 350 switch (keyboardElementId) { 351 case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED: 352 case KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED: 353 case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED: 354 case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED: 355 return true; 356 default: 357 return false; 358 } 359 } 360 computeHashCode(Key key)361 private static int computeHashCode(Key key) { 362 return Arrays.hashCode(new Object[] { 363 key.mX, 364 key.mY, 365 key.mWidth, 366 key.mHeight, 367 key.mCode, 368 key.mLabel, 369 key.mHintLabel, 370 key.mIconId, 371 key.mBackgroundType, 372 Arrays.hashCode(key.mMoreKeys), 373 key.mOutputText, 374 key.mActionFlags, 375 key.mLabelFlags, 376 // Key can be distinguishable without the following members. 377 // key.mAltCode, 378 // key.mDisabledIconId, 379 // key.mPreviewIconId, 380 // key.mHorizontalGap, 381 // key.mVerticalGap, 382 // key.mVisualInsetLeft, 383 // key.mVisualInsetRight, 384 // key.mMaxMoreKeysColumn, 385 }); 386 } 387 equals(Key o)388 private boolean equals(Key o) { 389 if (this == o) return true; 390 return o.mX == mX 391 && o.mY == mY 392 && o.mWidth == mWidth 393 && o.mHeight == mHeight 394 && o.mCode == mCode 395 && TextUtils.equals(o.mLabel, mLabel) 396 && TextUtils.equals(o.mHintLabel, mHintLabel) 397 && o.mIconId == mIconId 398 && o.mBackgroundType == mBackgroundType 399 && Arrays.equals(o.mMoreKeys, mMoreKeys) 400 && TextUtils.equals(o.mOutputText, mOutputText) 401 && o.mActionFlags == mActionFlags 402 && o.mLabelFlags == mLabelFlags; 403 } 404 405 @Override hashCode()406 public int hashCode() { 407 return mHashCode; 408 } 409 410 @Override equals(Object o)411 public boolean equals(Object o) { 412 return o instanceof Key && equals((Key)o); 413 } 414 415 @Override toString()416 public String toString() { 417 return String.format("%s/%s %d,%d %dx%d %s/%s/%s", 418 Keyboard.printableCode(mCode), mLabel, mX, mY, mWidth, mHeight, mHintLabel, 419 KeyboardIconsSet.getIconName(mIconId), backgroundName(mBackgroundType)); 420 } 421 backgroundName(int backgroundType)422 private static String backgroundName(int backgroundType) { 423 switch (backgroundType) { 424 case BACKGROUND_TYPE_NORMAL: return "normal"; 425 case BACKGROUND_TYPE_FUNCTIONAL: return "functional"; 426 case BACKGROUND_TYPE_ACTION: return "action"; 427 case BACKGROUND_TYPE_STICKY_OFF: return "stickyOff"; 428 case BACKGROUND_TYPE_STICKY_ON: return "stickyOn"; 429 default: return null; 430 } 431 } 432 markAsLeftEdge(Keyboard.Params params)433 public void markAsLeftEdge(Keyboard.Params params) { 434 mHitBox.left = params.mHorizontalEdgesPadding; 435 } 436 markAsRightEdge(Keyboard.Params params)437 public void markAsRightEdge(Keyboard.Params params) { 438 mHitBox.right = params.mOccupiedWidth - params.mHorizontalEdgesPadding; 439 } 440 markAsTopEdge(Keyboard.Params params)441 public void markAsTopEdge(Keyboard.Params params) { 442 mHitBox.top = params.mTopPadding; 443 } 444 markAsBottomEdge(Keyboard.Params params)445 public void markAsBottomEdge(Keyboard.Params params) { 446 mHitBox.bottom = params.mOccupiedHeight + params.mBottomPadding; 447 } 448 isSpacer()449 public final boolean isSpacer() { 450 return this instanceof Spacer; 451 } 452 isShift()453 public boolean isShift() { 454 return mCode == CODE_SHIFT; 455 } 456 isModifier()457 public boolean isModifier() { 458 return mCode == CODE_SHIFT || mCode == CODE_SWITCH_ALPHA_SYMBOL; 459 } 460 isRepeatable()461 public boolean isRepeatable() { 462 return (mActionFlags & ACTION_FLAGS_IS_REPEATABLE) != 0; 463 } 464 noKeyPreview()465 public boolean noKeyPreview() { 466 return (mActionFlags & ACTION_FLAGS_NO_KEY_PREVIEW) != 0; 467 } 468 altCodeWhileTyping()469 public boolean altCodeWhileTyping() { 470 return (mActionFlags & ACTION_FLAGS_ALT_CODE_WHILE_TYPING) != 0; 471 } 472 isLongPressEnabled()473 public boolean isLongPressEnabled() { 474 // We need not start long press timer on the key which has activated shifted letter. 475 return (mActionFlags & ACTION_FLAGS_ENABLE_LONG_PRESS) != 0 476 && (mLabelFlags & LABEL_FLAGS_SHIFTED_LETTER_ACTIVATED) == 0; 477 } 478 selectTypeface(Typeface defaultTypeface)479 public Typeface selectTypeface(Typeface defaultTypeface) { 480 // TODO: Handle "bold" here too? 481 if ((mLabelFlags & LABEL_FLAGS_FONT_NORMAL) != 0) { 482 return Typeface.DEFAULT; 483 } else if ((mLabelFlags & LABEL_FLAGS_FONT_MONO_SPACE) != 0) { 484 return Typeface.MONOSPACE; 485 } else { 486 return defaultTypeface; 487 } 488 } 489 selectTextSize(int letterSize, int largeLetterSize, int labelSize, int largeLabelSize, int hintLabelSize)490 public int selectTextSize(int letterSize, int largeLetterSize, int labelSize, 491 int largeLabelSize, int hintLabelSize) { 492 switch (mLabelFlags & LABEL_FLAGS_FOLLOW_KEY_TEXT_RATIO_MASK) { 493 case LABEL_FLAGS_FOLLOW_KEY_LETTER_RATIO: 494 return letterSize; 495 case LABEL_FLAGS_FOLLOW_KEY_LARGE_LETTER_RATIO: 496 return largeLetterSize; 497 case LABEL_FLAGS_FOLLOW_KEY_LABEL_RATIO: 498 return labelSize; 499 case LABEL_FLAGS_FOLLOW_KEY_LARGE_LABEL_RATIO: 500 return largeLabelSize; 501 case LABEL_FLAGS_FOLLOW_KEY_HINT_LABEL_RATIO: 502 return hintLabelSize; 503 default: // No follow key ratio flag specified. 504 return StringUtils.codePointCount(mLabel) == 1 ? letterSize : labelSize; 505 } 506 } 507 isAlignLeft()508 public boolean isAlignLeft() { 509 return (mLabelFlags & LABEL_FLAGS_ALIGN_LEFT) != 0; 510 } 511 isAlignRight()512 public boolean isAlignRight() { 513 return (mLabelFlags & LABEL_FLAGS_ALIGN_RIGHT) != 0; 514 } 515 isAlignLeftOfCenter()516 public boolean isAlignLeftOfCenter() { 517 return (mLabelFlags & LABEL_FLAGS_ALIGN_LEFT_OF_CENTER) != 0; 518 } 519 hasPopupHint()520 public boolean hasPopupHint() { 521 return (mLabelFlags & LABEL_FLAGS_HAS_POPUP_HINT) != 0; 522 } 523 hasShiftedLetterHint()524 public boolean hasShiftedLetterHint() { 525 return (mLabelFlags & LABEL_FLAGS_HAS_SHIFTED_LETTER_HINT) != 0; 526 } 527 hasHintLabel()528 public boolean hasHintLabel() { 529 return (mLabelFlags & LABEL_FLAGS_HAS_HINT_LABEL) != 0; 530 } 531 hasLabelWithIconLeft()532 public boolean hasLabelWithIconLeft() { 533 return (mLabelFlags & LABEL_FLAGS_WITH_ICON_LEFT) != 0; 534 } 535 hasLabelWithIconRight()536 public boolean hasLabelWithIconRight() { 537 return (mLabelFlags & LABEL_FLAGS_WITH_ICON_RIGHT) != 0; 538 } 539 needsXScale()540 public boolean needsXScale() { 541 return (mLabelFlags & LABEL_FLAGS_AUTO_X_SCALE) != 0; 542 } 543 isShiftedLetterActivated()544 public boolean isShiftedLetterActivated() { 545 return (mLabelFlags & LABEL_FLAGS_SHIFTED_LETTER_ACTIVATED) != 0; 546 } 547 getMoreKeysColumn()548 public int getMoreKeysColumn() { 549 return mMoreKeysColumnAndFlags & MORE_KEYS_COLUMN_MASK; 550 } 551 isFixedColumnOrderMoreKeys()552 public boolean isFixedColumnOrderMoreKeys() { 553 return (mMoreKeysColumnAndFlags & MORE_KEYS_FLAGS_FIXED_COLUMN_ORDER) != 0; 554 } 555 hasLabelsInMoreKeys()556 public boolean hasLabelsInMoreKeys() { 557 return (mMoreKeysColumnAndFlags & MORE_KEYS_FLAGS_HAS_LABELS) != 0; 558 } 559 getMoreKeyLabelFlags()560 public int getMoreKeyLabelFlags() { 561 return hasLabelsInMoreKeys() 562 ? LABEL_FLAGS_FOLLOW_KEY_LABEL_RATIO 563 : LABEL_FLAGS_FOLLOW_KEY_LETTER_RATIO; 564 } 565 needsDividersInMoreKeys()566 public boolean needsDividersInMoreKeys() { 567 return (mMoreKeysColumnAndFlags & MORE_KEYS_FLAGS_NEEDS_DIVIDERS) != 0; 568 } 569 hasEmbeddedMoreKey()570 public boolean hasEmbeddedMoreKey() { 571 return (mMoreKeysColumnAndFlags & MORE_KEYS_FLAGS_EMBEDDED_MORE_KEY) != 0; 572 } 573 getIcon(KeyboardIconsSet iconSet, int alpha)574 public Drawable getIcon(KeyboardIconsSet iconSet, int alpha) { 575 final int iconId = mEnabled ? mIconId : mDisabledIconId; 576 final Drawable icon = iconSet.getIconDrawable(iconId); 577 if (icon != null) { 578 icon.setAlpha(alpha); 579 } 580 return icon; 581 } 582 getPreviewIcon(KeyboardIconsSet iconSet)583 public Drawable getPreviewIcon(KeyboardIconsSet iconSet) { 584 return mPreviewIconId != ICON_UNDEFINED 585 ? iconSet.getIconDrawable(mPreviewIconId) 586 : iconSet.getIconDrawable(mIconId); 587 } 588 589 /** 590 * Informs the key that it has been pressed, in case it needs to change its appearance or 591 * state. 592 * @see #onReleased() 593 */ onPressed()594 public void onPressed() { 595 mPressed = true; 596 } 597 598 /** 599 * Informs the key that it has been released, in case it needs to change its appearance or 600 * state. 601 * @see #onPressed() 602 */ onReleased()603 public void onReleased() { 604 mPressed = false; 605 } 606 isEnabled()607 public boolean isEnabled() { 608 return mEnabled; 609 } 610 setEnabled(boolean enabled)611 public void setEnabled(boolean enabled) { 612 mEnabled = enabled; 613 } 614 615 /** 616 * Detects if a point falls on this key. 617 * @param x the x-coordinate of the point 618 * @param y the y-coordinate of the point 619 * @return whether or not the point falls on the key. If the key is attached to an edge, it 620 * will assume that all points between the key and the edge are considered to be on the key. 621 * @see #markAsLeftEdge(Keyboard.Params) etc. 622 */ isOnKey(int x, int y)623 public boolean isOnKey(int x, int y) { 624 return mHitBox.contains(x, y); 625 } 626 627 /** 628 * Returns the square of the distance to the nearest edge of the key and the given point. 629 * @param x the x-coordinate of the point 630 * @param y the y-coordinate of the point 631 * @return the square of the distance of the point from the nearest edge of the key 632 */ squaredDistanceToEdge(int x, int y)633 public int squaredDistanceToEdge(int x, int y) { 634 final int left = mX; 635 final int right = left + mWidth; 636 final int top = mY; 637 final int bottom = top + mHeight; 638 final int edgeX = x < left ? left : (x > right ? right : x); 639 final int edgeY = y < top ? top : (y > bottom ? bottom : y); 640 final int dx = x - edgeX; 641 final int dy = y - edgeY; 642 return dx * dx + dy * dy; 643 } 644 645 private final static int[] KEY_STATE_NORMAL_HIGHLIGHT_ON = { 646 android.R.attr.state_checkable, 647 android.R.attr.state_checked 648 }; 649 650 private final static int[] KEY_STATE_PRESSED_HIGHLIGHT_ON = { 651 android.R.attr.state_pressed, 652 android.R.attr.state_checkable, 653 android.R.attr.state_checked 654 }; 655 656 private final static int[] KEY_STATE_NORMAL_HIGHLIGHT_OFF = { 657 android.R.attr.state_checkable 658 }; 659 660 private final static int[] KEY_STATE_PRESSED_HIGHLIGHT_OFF = { 661 android.R.attr.state_pressed, 662 android.R.attr.state_checkable 663 }; 664 665 private final static int[] KEY_STATE_NORMAL = { 666 }; 667 668 private final static int[] KEY_STATE_PRESSED = { 669 android.R.attr.state_pressed 670 }; 671 672 // functional normal state (with properties) 673 private static final int[] KEY_STATE_FUNCTIONAL_NORMAL = { 674 android.R.attr.state_single 675 }; 676 677 // functional pressed state (with properties) 678 private static final int[] KEY_STATE_FUNCTIONAL_PRESSED = { 679 android.R.attr.state_single, 680 android.R.attr.state_pressed 681 }; 682 683 // action normal state (with properties) 684 private static final int[] KEY_STATE_ACTIVE_NORMAL = { 685 android.R.attr.state_active 686 }; 687 688 // action pressed state (with properties) 689 private static final int[] KEY_STATE_ACTIVE_PRESSED = { 690 android.R.attr.state_active, 691 android.R.attr.state_pressed 692 }; 693 694 /** 695 * Returns the drawable state for the key, based on the current state and type of the key. 696 * @return the drawable state of the key. 697 * @see android.graphics.drawable.StateListDrawable#setState(int[]) 698 */ getCurrentDrawableState()699 public int[] getCurrentDrawableState() { 700 switch (mBackgroundType) { 701 case BACKGROUND_TYPE_FUNCTIONAL: 702 return mPressed ? KEY_STATE_FUNCTIONAL_PRESSED : KEY_STATE_FUNCTIONAL_NORMAL; 703 case BACKGROUND_TYPE_ACTION: 704 return mPressed ? KEY_STATE_ACTIVE_PRESSED : KEY_STATE_ACTIVE_NORMAL; 705 case BACKGROUND_TYPE_STICKY_OFF: 706 return mPressed ? KEY_STATE_PRESSED_HIGHLIGHT_OFF : KEY_STATE_NORMAL_HIGHLIGHT_OFF; 707 case BACKGROUND_TYPE_STICKY_ON: 708 return mPressed ? KEY_STATE_PRESSED_HIGHLIGHT_ON : KEY_STATE_NORMAL_HIGHLIGHT_ON; 709 default: /* BACKGROUND_TYPE_NORMAL */ 710 return mPressed ? KEY_STATE_PRESSED : KEY_STATE_NORMAL; 711 } 712 } 713 714 public static class Spacer extends Key { Spacer(Resources res, Keyboard.Params params, Keyboard.Builder.Row row, XmlPullParser parser)715 public Spacer(Resources res, Keyboard.Params params, Keyboard.Builder.Row row, 716 XmlPullParser parser) throws XmlPullParserException { 717 super(res, params, row, parser); 718 } 719 720 /** 721 * This constructor is being used only for divider in more keys keyboard. 722 */ Spacer(Keyboard.Params params, int x, int y, int width, int height)723 protected Spacer(Keyboard.Params params, int x, int y, int width, int height) { 724 super(params, null, null, ICON_UNDEFINED, CODE_UNSPECIFIED, 725 null, x, y, width, height, 0); 726 } 727 } 728 } 729