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