Key.java revision 7ab7f66c2d0f4a0b2e29be718b310ccaf368a4f4
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.StringUtils; 40 41import org.xmlpull.v1.XmlPullParser; 42import org.xmlpull.v1.XmlPullParserException; 43 44import java.util.Arrays; 45import java.util.Locale; 46 47/** 48 * Class for describing the position and characteristics of a single key in the keyboard. 49 */ 50public 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 */ 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 */ 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 */ 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 = (int) (keyXPos + horizontalGap / 2); 221 mY = keyYPos; 222 mWidth = (int) (keyWidth - horizontalGap); 223 mHorizontalGap = (int) horizontalGap; 224 mHitBox.set((int)keyXPos, keyYPos, (int)(keyXPos + keyWidth) + 1, keyYPos + keyHeight); 225 // Update row to have current x coordinate. 226 row.setXPos(keyXPos + keyWidth); 227 228 mBackgroundType = style.getInt(keyAttr, 229 R.styleable.Keyboard_Key_backgroundType, BACKGROUND_TYPE_NORMAL); 230 231 mVisualInsetsLeft = (int) Keyboard.Builder.getDimensionOrFraction(keyAttr, 232 R.styleable.Keyboard_Key_visualInsetsLeft, params.mBaseWidth, 0); 233 mVisualInsetsRight = (int) Keyboard.Builder.getDimensionOrFraction(keyAttr, 234 R.styleable.Keyboard_Key_visualInsetsRight, params.mBaseWidth, 0); 235 mIconId = KeySpecParser.getIconId(style.getString(keyAttr, 236 R.styleable.Keyboard_Key_keyIcon)); 237 mDisabledIconId = KeySpecParser.getIconId(style.getString(keyAttr, 238 R.styleable.Keyboard_Key_keyIconDisabled)); 239 mPreviewIconId = KeySpecParser.getIconId(style.getString(keyAttr, 240 R.styleable.Keyboard_Key_keyIconPreview)); 241 242 mLabelFlags = style.getFlag(keyAttr, R.styleable.Keyboard_Key_keyLabelFlags) 243 | row.getDefaultKeyLabelFlags(); 244 final boolean needsToUpperCase = needsToUpperCase(mLabelFlags, params.mId.mElementId); 245 final Locale locale = params.mId.mLocale; 246 int actionFlags = style.getFlag(keyAttr, R.styleable.Keyboard_Key_keyActionFlags); 247 String[] moreKeys = style.getStringArray(keyAttr, R.styleable.Keyboard_Key_moreKeys); 248 249 int moreKeysColumn = style.getInt(keyAttr, 250 R.styleable.Keyboard_Key_maxMoreKeysColumn, params.mMaxMoreKeysKeyboardColumn); 251 int value; 252 if ((value = KeySpecParser.getIntValue(moreKeys, MORE_KEYS_AUTO_COLUMN_ORDER, -1)) > 0) { 253 moreKeysColumn = value & MORE_KEYS_COLUMN_MASK; 254 } 255 if ((value = KeySpecParser.getIntValue(moreKeys, MORE_KEYS_FIXED_COLUMN_ORDER, -1)) > 0) { 256 moreKeysColumn = MORE_KEYS_FLAGS_FIXED_COLUMN_ORDER | (value & MORE_KEYS_COLUMN_MASK); 257 } 258 if (KeySpecParser.getBooleanValue(moreKeys, MORE_KEYS_HAS_LABELS)) { 259 moreKeysColumn |= MORE_KEYS_FLAGS_HAS_LABELS; 260 } 261 if (KeySpecParser.getBooleanValue(moreKeys, MORE_KEYS_NEEDS_DIVIDERS)) { 262 moreKeysColumn |= MORE_KEYS_FLAGS_NEEDS_DIVIDERS; 263 } 264 if (KeySpecParser.getBooleanValue(moreKeys, MORE_KEYS_EMBEDDED_MORE_KEY)) { 265 moreKeysColumn |= MORE_KEYS_FLAGS_EMBEDDED_MORE_KEY; 266 } 267 mMoreKeysColumnAndFlags = moreKeysColumn; 268 269 final String[] additionalMoreKeys; 270 if ((mLabelFlags & LABEL_FLAGS_DISABLE_ADDITIONAL_MORE_KEYS) != 0) { 271 additionalMoreKeys = null; 272 } else { 273 additionalMoreKeys = style.getStringArray(keyAttr, 274 R.styleable.Keyboard_Key_additionalMoreKeys); 275 } 276 moreKeys = KeySpecParser.insertAdditionalMoreKeys(moreKeys, additionalMoreKeys); 277 if (moreKeys != null) { 278 actionFlags |= ACTION_FLAGS_ENABLE_LONG_PRESS; 279 mMoreKeys = new MoreKeySpec[moreKeys.length]; 280 for (int i = 0; i < moreKeys.length; i++) { 281 mMoreKeys[i] = new MoreKeySpec( 282 moreKeys[i], needsToUpperCase, locale, params.mCodesSet); 283 } 284 } else { 285 mMoreKeys = null; 286 } 287 mActionFlags = actionFlags; 288 289 if ((mLabelFlags & LABEL_FLAGS_FROM_CUSTOM_ACTION_LABEL) != 0) { 290 mLabel = params.mId.mCustomActionLabel; 291 } else { 292 mLabel = KeySpecParser.toUpperCaseOfStringForLocale(style.getString(keyAttr, 293 R.styleable.Keyboard_Key_keyLabel), needsToUpperCase, locale); 294 } 295 if ((mLabelFlags & LABEL_FLAGS_DISABLE_HINT_LABEL) != 0) { 296 mHintLabel = null; 297 } else { 298 mHintLabel = KeySpecParser.toUpperCaseOfStringForLocale(style.getString(keyAttr, 299 R.styleable.Keyboard_Key_keyHintLabel), needsToUpperCase, locale); 300 } 301 String outputText = KeySpecParser.toUpperCaseOfStringForLocale(style.getString(keyAttr, 302 R.styleable.Keyboard_Key_keyOutputText), needsToUpperCase, locale); 303 final int code = KeySpecParser.parseCode(style.getString(keyAttr, 304 R.styleable.Keyboard_Key_code), params.mCodesSet, CODE_UNSPECIFIED); 305 // Choose the first letter of the label as primary code if not specified. 306 if (code == CODE_UNSPECIFIED && TextUtils.isEmpty(outputText) 307 && !TextUtils.isEmpty(mLabel)) { 308 if (StringUtils.codePointCount(mLabel) == 1) { 309 // Use the first letter of the hint label if shiftedLetterActivated flag is 310 // specified. 311 if (hasShiftedLetterHint() && isShiftedLetterActivated() 312 && !TextUtils.isEmpty(mHintLabel)) { 313 mCode = mHintLabel.codePointAt(0); 314 } else { 315 mCode = mLabel.codePointAt(0); 316 } 317 } else { 318 // In some locale and case, the character might be represented by multiple code 319 // points, such as upper case Eszett of German alphabet. 320 outputText = mLabel; 321 mCode = CODE_OUTPUT_TEXT; 322 } 323 } else if (code == CODE_UNSPECIFIED && outputText != null) { 324 if (StringUtils.codePointCount(outputText) == 1) { 325 mCode = outputText.codePointAt(0); 326 outputText = null; 327 } else { 328 mCode = CODE_OUTPUT_TEXT; 329 } 330 } else { 331 mCode = KeySpecParser.toUpperCaseOfCodeForLocale(code, needsToUpperCase, locale); 332 } 333 mOutputText = outputText; 334 mAltCode = KeySpecParser.toUpperCaseOfCodeForLocale( 335 KeySpecParser.parseCode(style.getString(keyAttr, 336 R.styleable.Keyboard_Key_altCode), params.mCodesSet, CODE_UNSPECIFIED), 337 needsToUpperCase, locale); 338 mHashCode = computeHashCode(this); 339 340 keyAttr.recycle(); 341 342 if (hasShiftedLetterHint() && TextUtils.isEmpty(mHintLabel)) { 343 Log.w(TAG, "hasShiftedLetterHint specified without keyHintLabel: " + this); 344 } 345 } 346 347 private static boolean needsToUpperCase(int labelFlags, int keyboardElementId) { 348 if ((labelFlags & LABEL_FLAGS_PRESERVE_CASE) != 0) return false; 349 switch (keyboardElementId) { 350 case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED: 351 case KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED: 352 case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED: 353 case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED: 354 return true; 355 default: 356 return false; 357 } 358 } 359 360 private static int computeHashCode(Key key) { 361 return Arrays.hashCode(new Object[] { 362 key.mX, 363 key.mY, 364 key.mWidth, 365 key.mHeight, 366 key.mCode, 367 key.mLabel, 368 key.mHintLabel, 369 key.mIconId, 370 key.mBackgroundType, 371 Arrays.hashCode(key.mMoreKeys), 372 key.mOutputText, 373 key.mActionFlags, 374 key.mLabelFlags, 375 // Key can be distinguishable without the following members. 376 // key.mAltCode, 377 // key.mDisabledIconId, 378 // key.mPreviewIconId, 379 // key.mHorizontalGap, 380 // key.mVerticalGap, 381 // key.mVisualInsetLeft, 382 // key.mVisualInsetRight, 383 // key.mMaxMoreKeysColumn, 384 }); 385 } 386 387 private boolean equals(Key o) { 388 if (this == o) return true; 389 return o.mX == mX 390 && o.mY == mY 391 && o.mWidth == mWidth 392 && o.mHeight == mHeight 393 && o.mCode == mCode 394 && TextUtils.equals(o.mLabel, mLabel) 395 && TextUtils.equals(o.mHintLabel, mHintLabel) 396 && o.mIconId == mIconId 397 && o.mBackgroundType == mBackgroundType 398 && Arrays.equals(o.mMoreKeys, mMoreKeys) 399 && TextUtils.equals(o.mOutputText, mOutputText) 400 && o.mActionFlags == mActionFlags 401 && o.mLabelFlags == mLabelFlags; 402 } 403 404 @Override 405 public int hashCode() { 406 return mHashCode; 407 } 408 409 @Override 410 public boolean equals(Object o) { 411 return o instanceof Key && equals((Key)o); 412 } 413 414 @Override 415 public String toString() { 416 return String.format("%s/%s %d,%d %dx%d %s/%s/%s", 417 Keyboard.printableCode(mCode), mLabel, mX, mY, mWidth, mHeight, mHintLabel, 418 KeyboardIconsSet.getIconName(mIconId), backgroundName(mBackgroundType)); 419 } 420 421 private static String backgroundName(int backgroundType) { 422 switch (backgroundType) { 423 case BACKGROUND_TYPE_NORMAL: return "normal"; 424 case BACKGROUND_TYPE_FUNCTIONAL: return "functional"; 425 case BACKGROUND_TYPE_ACTION: return "action"; 426 case BACKGROUND_TYPE_STICKY_OFF: return "stickyOff"; 427 case BACKGROUND_TYPE_STICKY_ON: return "stickyOn"; 428 default: return null; 429 } 430 } 431 432 public void markAsLeftEdge(Keyboard.Params params) { 433 mHitBox.left = params.mHorizontalEdgesPadding; 434 } 435 436 public void markAsRightEdge(Keyboard.Params params) { 437 mHitBox.right = params.mOccupiedWidth - params.mHorizontalEdgesPadding; 438 } 439 440 public void markAsTopEdge(Keyboard.Params params) { 441 mHitBox.top = params.mTopPadding; 442 } 443 444 public void markAsBottomEdge(Keyboard.Params params) { 445 mHitBox.bottom = params.mOccupiedHeight + params.mBottomPadding; 446 } 447 448 public final boolean isSpacer() { 449 return this instanceof Spacer; 450 } 451 452 public boolean isShift() { 453 return mCode == CODE_SHIFT; 454 } 455 456 public boolean isModifier() { 457 return mCode == CODE_SHIFT || mCode == CODE_SWITCH_ALPHA_SYMBOL; 458 } 459 460 public boolean isRepeatable() { 461 return (mActionFlags & ACTION_FLAGS_IS_REPEATABLE) != 0; 462 } 463 464 public boolean noKeyPreview() { 465 return (mActionFlags & ACTION_FLAGS_NO_KEY_PREVIEW) != 0; 466 } 467 468 public boolean altCodeWhileTyping() { 469 return (mActionFlags & ACTION_FLAGS_ALT_CODE_WHILE_TYPING) != 0; 470 } 471 472 public boolean isLongPressEnabled() { 473 // We need not start long press timer on the key which has activated shifted letter. 474 return (mActionFlags & ACTION_FLAGS_ENABLE_LONG_PRESS) != 0 475 && (mLabelFlags & LABEL_FLAGS_SHIFTED_LETTER_ACTIVATED) == 0; 476 } 477 478 public Typeface selectTypeface(Typeface defaultTypeface) { 479 // TODO: Handle "bold" here too? 480 if ((mLabelFlags & LABEL_FLAGS_FONT_NORMAL) != 0) { 481 return Typeface.DEFAULT; 482 } else if ((mLabelFlags & LABEL_FLAGS_FONT_MONO_SPACE) != 0) { 483 return Typeface.MONOSPACE; 484 } else { 485 return defaultTypeface; 486 } 487 } 488 489 public int selectTextSize(int letterSize, int largeLetterSize, int labelSize, 490 int largeLabelSize, int hintLabelSize) { 491 switch (mLabelFlags & LABEL_FLAGS_FOLLOW_KEY_TEXT_RATIO_MASK) { 492 case LABEL_FLAGS_FOLLOW_KEY_LETTER_RATIO: 493 return letterSize; 494 case LABEL_FLAGS_FOLLOW_KEY_LARGE_LETTER_RATIO: 495 return largeLetterSize; 496 case LABEL_FLAGS_FOLLOW_KEY_LABEL_RATIO: 497 return labelSize; 498 case LABEL_FLAGS_FOLLOW_KEY_LARGE_LABEL_RATIO: 499 return largeLabelSize; 500 case LABEL_FLAGS_FOLLOW_KEY_HINT_LABEL_RATIO: 501 return hintLabelSize; 502 default: // No follow key ratio flag specified. 503 return StringUtils.codePointCount(mLabel) == 1 ? letterSize : labelSize; 504 } 505 } 506 507 public boolean isAlignLeft() { 508 return (mLabelFlags & LABEL_FLAGS_ALIGN_LEFT) != 0; 509 } 510 511 public boolean isAlignRight() { 512 return (mLabelFlags & LABEL_FLAGS_ALIGN_RIGHT) != 0; 513 } 514 515 public boolean isAlignLeftOfCenter() { 516 return (mLabelFlags & LABEL_FLAGS_ALIGN_LEFT_OF_CENTER) != 0; 517 } 518 519 public boolean hasPopupHint() { 520 return (mLabelFlags & LABEL_FLAGS_HAS_POPUP_HINT) != 0; 521 } 522 523 public boolean hasShiftedLetterHint() { 524 return (mLabelFlags & LABEL_FLAGS_HAS_SHIFTED_LETTER_HINT) != 0; 525 } 526 527 public boolean hasHintLabel() { 528 return (mLabelFlags & LABEL_FLAGS_HAS_HINT_LABEL) != 0; 529 } 530 531 public boolean hasLabelWithIconLeft() { 532 return (mLabelFlags & LABEL_FLAGS_WITH_ICON_LEFT) != 0; 533 } 534 535 public boolean hasLabelWithIconRight() { 536 return (mLabelFlags & LABEL_FLAGS_WITH_ICON_RIGHT) != 0; 537 } 538 539 public boolean needsXScale() { 540 return (mLabelFlags & LABEL_FLAGS_AUTO_X_SCALE) != 0; 541 } 542 543 public boolean isShiftedLetterActivated() { 544 return (mLabelFlags & LABEL_FLAGS_SHIFTED_LETTER_ACTIVATED) != 0; 545 } 546 547 public int getMoreKeysColumn() { 548 return mMoreKeysColumnAndFlags & MORE_KEYS_COLUMN_MASK; 549 } 550 551 public boolean isFixedColumnOrderMoreKeys() { 552 return (mMoreKeysColumnAndFlags & MORE_KEYS_FLAGS_FIXED_COLUMN_ORDER) != 0; 553 } 554 555 public boolean hasLabelsInMoreKeys() { 556 return (mMoreKeysColumnAndFlags & MORE_KEYS_FLAGS_HAS_LABELS) != 0; 557 } 558 559 public int getMoreKeyLabelFlags() { 560 return hasLabelsInMoreKeys() 561 ? LABEL_FLAGS_FOLLOW_KEY_LABEL_RATIO 562 : LABEL_FLAGS_FOLLOW_KEY_LETTER_RATIO; 563 } 564 565 public boolean needsDividersInMoreKeys() { 566 return (mMoreKeysColumnAndFlags & MORE_KEYS_FLAGS_NEEDS_DIVIDERS) != 0; 567 } 568 569 public boolean hasEmbeddedMoreKey() { 570 return (mMoreKeysColumnAndFlags & MORE_KEYS_FLAGS_EMBEDDED_MORE_KEY) != 0; 571 } 572 573 public Drawable getIcon(KeyboardIconsSet iconSet, int alpha) { 574 final int iconId = mEnabled ? mIconId : mDisabledIconId; 575 final Drawable icon = iconSet.getIconDrawable(iconId); 576 if (icon != null) { 577 icon.setAlpha(alpha); 578 } 579 return icon; 580 } 581 582 public Drawable getPreviewIcon(KeyboardIconsSet iconSet) { 583 return mPreviewIconId != ICON_UNDEFINED 584 ? iconSet.getIconDrawable(mPreviewIconId) 585 : iconSet.getIconDrawable(mIconId); 586 } 587 588 /** 589 * Informs the key that it has been pressed, in case it needs to change its appearance or 590 * state. 591 * @see #onReleased() 592 */ 593 public void onPressed() { 594 mPressed = true; 595 } 596 597 /** 598 * Informs the key that it has been released, in case it needs to change its appearance or 599 * state. 600 * @see #onPressed() 601 */ 602 public void onReleased() { 603 mPressed = false; 604 } 605 606 public boolean isEnabled() { 607 return mEnabled; 608 } 609 610 public void setEnabled(boolean enabled) { 611 mEnabled = enabled; 612 } 613 614 /** 615 * Detects if a point falls on this key. 616 * @param x the x-coordinate of the point 617 * @param y the y-coordinate of the point 618 * @return whether or not the point falls on the key. If the key is attached to an edge, it 619 * will assume that all points between the key and the edge are considered to be on the key. 620 * @see #markAsLeftEdge(Keyboard.Params) etc. 621 */ 622 public boolean isOnKey(int x, int y) { 623 return mHitBox.contains(x, y); 624 } 625 626 /** 627 * Returns the square of the distance to the nearest edge of the key and the given point. 628 * @param x the x-coordinate of the point 629 * @param y the y-coordinate of the point 630 * @return the square of the distance of the point from the nearest edge of the key 631 */ 632 public int squaredDistanceToEdge(int x, int y) { 633 final int left = mX; 634 final int right = left + mWidth; 635 final int top = mY; 636 final int bottom = top + mHeight; 637 final int edgeX = x < left ? left : (x > right ? right : x); 638 final int edgeY = y < top ? top : (y > bottom ? bottom : y); 639 final int dx = x - edgeX; 640 final int dy = y - edgeY; 641 return dx * dx + dy * dy; 642 } 643 644 private final static int[] KEY_STATE_NORMAL_HIGHLIGHT_ON = { 645 android.R.attr.state_checkable, 646 android.R.attr.state_checked 647 }; 648 649 private final static int[] KEY_STATE_PRESSED_HIGHLIGHT_ON = { 650 android.R.attr.state_pressed, 651 android.R.attr.state_checkable, 652 android.R.attr.state_checked 653 }; 654 655 private final static int[] KEY_STATE_NORMAL_HIGHLIGHT_OFF = { 656 android.R.attr.state_checkable 657 }; 658 659 private final static int[] KEY_STATE_PRESSED_HIGHLIGHT_OFF = { 660 android.R.attr.state_pressed, 661 android.R.attr.state_checkable 662 }; 663 664 private final static int[] KEY_STATE_NORMAL = { 665 }; 666 667 private final static int[] KEY_STATE_PRESSED = { 668 android.R.attr.state_pressed 669 }; 670 671 // functional normal state (with properties) 672 private static final int[] KEY_STATE_FUNCTIONAL_NORMAL = { 673 android.R.attr.state_single 674 }; 675 676 // functional pressed state (with properties) 677 private static final int[] KEY_STATE_FUNCTIONAL_PRESSED = { 678 android.R.attr.state_single, 679 android.R.attr.state_pressed 680 }; 681 682 // action normal state (with properties) 683 private static final int[] KEY_STATE_ACTIVE_NORMAL = { 684 android.R.attr.state_active 685 }; 686 687 // action pressed state (with properties) 688 private static final int[] KEY_STATE_ACTIVE_PRESSED = { 689 android.R.attr.state_active, 690 android.R.attr.state_pressed 691 }; 692 693 /** 694 * Returns the drawable state for the key, based on the current state and type of the key. 695 * @return the drawable state of the key. 696 * @see android.graphics.drawable.StateListDrawable#setState(int[]) 697 */ 698 public int[] getCurrentDrawableState() { 699 switch (mBackgroundType) { 700 case BACKGROUND_TYPE_FUNCTIONAL: 701 return mPressed ? KEY_STATE_FUNCTIONAL_PRESSED : KEY_STATE_FUNCTIONAL_NORMAL; 702 case BACKGROUND_TYPE_ACTION: 703 return mPressed ? KEY_STATE_ACTIVE_PRESSED : KEY_STATE_ACTIVE_NORMAL; 704 case BACKGROUND_TYPE_STICKY_OFF: 705 return mPressed ? KEY_STATE_PRESSED_HIGHLIGHT_OFF : KEY_STATE_NORMAL_HIGHLIGHT_OFF; 706 case BACKGROUND_TYPE_STICKY_ON: 707 return mPressed ? KEY_STATE_PRESSED_HIGHLIGHT_ON : KEY_STATE_NORMAL_HIGHLIGHT_ON; 708 default: /* BACKGROUND_TYPE_NORMAL */ 709 return mPressed ? KEY_STATE_PRESSED : KEY_STATE_NORMAL; 710 } 711 } 712 713 public static class Spacer extends Key { 714 public Spacer(Resources res, Keyboard.Params params, Keyboard.Builder.Row row, 715 XmlPullParser parser) throws XmlPullParserException { 716 super(res, params, row, parser); 717 } 718 719 /** 720 * This constructor is being used only for divider in more keys keyboard. 721 */ 722 protected Spacer(Keyboard.Params params, int x, int y, int width, int height) { 723 super(params, null, null, ICON_UNDEFINED, CODE_UNSPECIFIED, 724 null, x, y, width, height, 0); 725 } 726 } 727} 728