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