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 */ 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 = 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 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 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 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 406 public int hashCode() { 407 return mHashCode; 408 } 409 410 @Override 411 public boolean equals(Object o) { 412 return o instanceof Key && equals((Key)o); 413 } 414 415 @Override 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 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 433 public void markAsLeftEdge(Keyboard.Params params) { 434 mHitBox.left = params.mHorizontalEdgesPadding; 435 } 436 437 public void markAsRightEdge(Keyboard.Params params) { 438 mHitBox.right = params.mOccupiedWidth - params.mHorizontalEdgesPadding; 439 } 440 441 public void markAsTopEdge(Keyboard.Params params) { 442 mHitBox.top = params.mTopPadding; 443 } 444 445 public void markAsBottomEdge(Keyboard.Params params) { 446 mHitBox.bottom = params.mOccupiedHeight + params.mBottomPadding; 447 } 448 449 public final boolean isSpacer() { 450 return this instanceof Spacer; 451 } 452 453 public boolean isShift() { 454 return mCode == CODE_SHIFT; 455 } 456 457 public boolean isModifier() { 458 return mCode == CODE_SHIFT || mCode == CODE_SWITCH_ALPHA_SYMBOL; 459 } 460 461 public boolean isRepeatable() { 462 return (mActionFlags & ACTION_FLAGS_IS_REPEATABLE) != 0; 463 } 464 465 public boolean noKeyPreview() { 466 return (mActionFlags & ACTION_FLAGS_NO_KEY_PREVIEW) != 0; 467 } 468 469 public boolean altCodeWhileTyping() { 470 return (mActionFlags & ACTION_FLAGS_ALT_CODE_WHILE_TYPING) != 0; 471 } 472 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 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 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 508 public boolean isAlignLeft() { 509 return (mLabelFlags & LABEL_FLAGS_ALIGN_LEFT) != 0; 510 } 511 512 public boolean isAlignRight() { 513 return (mLabelFlags & LABEL_FLAGS_ALIGN_RIGHT) != 0; 514 } 515 516 public boolean isAlignLeftOfCenter() { 517 return (mLabelFlags & LABEL_FLAGS_ALIGN_LEFT_OF_CENTER) != 0; 518 } 519 520 public boolean hasPopupHint() { 521 return (mLabelFlags & LABEL_FLAGS_HAS_POPUP_HINT) != 0; 522 } 523 524 public boolean hasShiftedLetterHint() { 525 return (mLabelFlags & LABEL_FLAGS_HAS_SHIFTED_LETTER_HINT) != 0; 526 } 527 528 public boolean hasHintLabel() { 529 return (mLabelFlags & LABEL_FLAGS_HAS_HINT_LABEL) != 0; 530 } 531 532 public boolean hasLabelWithIconLeft() { 533 return (mLabelFlags & LABEL_FLAGS_WITH_ICON_LEFT) != 0; 534 } 535 536 public boolean hasLabelWithIconRight() { 537 return (mLabelFlags & LABEL_FLAGS_WITH_ICON_RIGHT) != 0; 538 } 539 540 public boolean needsXScale() { 541 return (mLabelFlags & LABEL_FLAGS_AUTO_X_SCALE) != 0; 542 } 543 544 public boolean isShiftedLetterActivated() { 545 return (mLabelFlags & LABEL_FLAGS_SHIFTED_LETTER_ACTIVATED) != 0; 546 } 547 548 public int getMoreKeysColumn() { 549 return mMoreKeysColumnAndFlags & MORE_KEYS_COLUMN_MASK; 550 } 551 552 public boolean isFixedColumnOrderMoreKeys() { 553 return (mMoreKeysColumnAndFlags & MORE_KEYS_FLAGS_FIXED_COLUMN_ORDER) != 0; 554 } 555 556 public boolean hasLabelsInMoreKeys() { 557 return (mMoreKeysColumnAndFlags & MORE_KEYS_FLAGS_HAS_LABELS) != 0; 558 } 559 560 public int getMoreKeyLabelFlags() { 561 return hasLabelsInMoreKeys() 562 ? LABEL_FLAGS_FOLLOW_KEY_LABEL_RATIO 563 : LABEL_FLAGS_FOLLOW_KEY_LETTER_RATIO; 564 } 565 566 public boolean needsDividersInMoreKeys() { 567 return (mMoreKeysColumnAndFlags & MORE_KEYS_FLAGS_NEEDS_DIVIDERS) != 0; 568 } 569 570 public boolean hasEmbeddedMoreKey() { 571 return (mMoreKeysColumnAndFlags & MORE_KEYS_FLAGS_EMBEDDED_MORE_KEY) != 0; 572 } 573 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 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 */ 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 */ 603 public void onReleased() { 604 mPressed = false; 605 } 606 607 public boolean isEnabled() { 608 return mEnabled; 609 } 610 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 */ 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 */ 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 */ 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 { 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 */ 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