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 android.content.res.Resources; 20 import android.content.res.TypedArray; 21 import android.graphics.Rect; 22 import android.graphics.Typeface; 23 import android.graphics.drawable.Drawable; 24 import android.text.TextUtils; 25 import android.util.Xml; 26 27 import com.android.inputmethod.keyboard.internal.KeyStyles; 28 import com.android.inputmethod.keyboard.internal.KeyStyles.KeyStyle; 29 import com.android.inputmethod.keyboard.internal.KeyboardBuilder; 30 import com.android.inputmethod.keyboard.internal.KeyboardBuilder.ParseException; 31 import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; 32 import com.android.inputmethod.keyboard.internal.KeyboardParams; 33 import com.android.inputmethod.keyboard.internal.MoreKeySpecParser; 34 import com.android.inputmethod.latin.R; 35 36 import org.xmlpull.v1.XmlPullParser; 37 38 import java.util.HashMap; 39 import java.util.Map; 40 41 /** 42 * Class for describing the position and characteristics of a single key in the keyboard. 43 */ 44 public class Key { 45 /** 46 * The key code (unicode or custom code) that this key generates. 47 */ 48 public final int mCode; 49 50 /** Label to display */ 51 public final CharSequence mLabel; 52 /** Hint label to display on the key in conjunction with the label */ 53 public final CharSequence mHintLabel; 54 /** Option of the label */ 55 private final int mLabelOption; 56 private static final int LABEL_OPTION_ALIGN_LEFT = 0x01; 57 private static final int LABEL_OPTION_ALIGN_RIGHT = 0x02; 58 private static final int LABEL_OPTION_ALIGN_LEFT_OF_CENTER = 0x08; 59 private static final int LABEL_OPTION_LARGE_LETTER = 0x10; 60 private static final int LABEL_OPTION_FONT_NORMAL = 0x20; 61 private static final int LABEL_OPTION_FONT_MONO_SPACE = 0x40; 62 private static final int LABEL_OPTION_FOLLOW_KEY_LETTER_RATIO = 0x80; 63 private static final int LABEL_OPTION_FOLLOW_KEY_HINT_LABEL_RATIO = 0x100; 64 private static final int LABEL_OPTION_HAS_POPUP_HINT = 0x200; 65 private static final int LABEL_OPTION_HAS_UPPERCASE_LETTER = 0x400; 66 private static final int LABEL_OPTION_HAS_HINT_LABEL = 0x800; 67 private static final int LABEL_OPTION_WITH_ICON_LEFT = 0x1000; 68 private static final int LABEL_OPTION_WITH_ICON_RIGHT = 0x2000; 69 private static final int LABEL_OPTION_AUTO_X_SCALE = 0x4000; 70 71 /** Icon to display instead of a label. Icon takes precedence over a label */ 72 private Drawable mIcon; 73 /** Preview version of the icon, for the preview popup */ 74 private Drawable mPreviewIcon; 75 76 /** Width of the key, not including the gap */ 77 public final int mWidth; 78 /** Height of the key, not including the gap */ 79 public final int mHeight; 80 /** The horizontal gap around this key */ 81 public final int mHorizontalGap; 82 /** The vertical gap below this key */ 83 public final int mVerticalGap; 84 /** The visual insets */ 85 public final int mVisualInsetsLeft; 86 public final int mVisualInsetsRight; 87 /** X coordinate of the key in the keyboard layout */ 88 public final int mX; 89 /** Y coordinate of the key in the keyboard layout */ 90 public final int mY; 91 /** Hit bounding box of the key */ 92 public final Rect mHitBox = new Rect(); 93 94 /** Text to output when pressed. This can be multiple characters, like ".com" */ 95 public final CharSequence mOutputText; 96 /** More keys */ 97 public final CharSequence[] mMoreKeys; 98 /** More keys maximum column number */ 99 public final int mMaxMoreKeysColumn; 100 101 /** Background type that represents different key background visual than normal one. */ 102 public final int mBackgroundType; 103 public static final int BACKGROUND_TYPE_NORMAL = 0; 104 public static final int BACKGROUND_TYPE_FUNCTIONAL = 1; 105 public static final int BACKGROUND_TYPE_ACTION = 2; 106 public static final int BACKGROUND_TYPE_STICKY = 3; 107 108 /** Whether this key repeats itself when held down */ 109 public final boolean mRepeatable; 110 111 /** The current pressed state of this key */ 112 private boolean mPressed; 113 /** If this is a sticky key, is its highlight on? */ 114 private boolean mHighlightOn; 115 /** Key is enabled and responds on press */ 116 private boolean mEnabled = true; 117 /** Whether this key needs to show the "..." popup hint for special purposes */ 118 private boolean mNeedsSpecialPopupHint; 119 120 // RTL parenthesis character swapping map. 121 private static final Map<Integer, Integer> sRtlParenthesisMap = new HashMap<Integer, Integer>(); 122 123 static { 124 // The all letters need to be mirrored are found at 125 // http://www.unicode.org/Public/6.0.0/ucd/extracted/DerivedBinaryProperties.txt 126 addRtlParenthesisPair('(', ')'); 127 addRtlParenthesisPair('[', ']'); 128 addRtlParenthesisPair('{', '}'); 129 addRtlParenthesisPair('<', '>'); 130 // \u00ab: LEFT-POINTING DOUBLE ANGLE QUOTATION MARK 131 // \u00bb: RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK 132 addRtlParenthesisPair('\u00ab', '\u00bb'); 133 // \u2039: SINGLE LEFT-POINTING ANGLE QUOTATION MARK 134 // \u203a: SINGLE RIGHT-POINTING ANGLE QUOTATION MARK 135 addRtlParenthesisPair('\u2039', '\u203a'); 136 // \u2264: LESS-THAN OR EQUAL TO 137 // \u2265: GREATER-THAN OR EQUAL TO 138 addRtlParenthesisPair('\u2264', '\u2265'); 139 } 140 addRtlParenthesisPair(int left, int right)141 private static void addRtlParenthesisPair(int left, int right) { 142 sRtlParenthesisMap.put(left, right); 143 sRtlParenthesisMap.put(right, left); 144 } 145 getRtlParenthesisCode(int code, boolean isRtl)146 public static int getRtlParenthesisCode(int code, boolean isRtl) { 147 if (isRtl && sRtlParenthesisMap.containsKey(code)) { 148 return sRtlParenthesisMap.get(code); 149 } else { 150 return code; 151 } 152 } 153 getCode(Resources res, KeyboardParams params, String moreKeySpec)154 private static int getCode(Resources res, KeyboardParams params, String moreKeySpec) { 155 return getRtlParenthesisCode( 156 MoreKeySpecParser.getCode(res, moreKeySpec), params.mIsRtlKeyboard); 157 } 158 getIcon(KeyboardParams params, String moreKeySpec)159 private static Drawable getIcon(KeyboardParams params, String moreKeySpec) { 160 return params.mIconsSet.getIcon(MoreKeySpecParser.getIconId(moreKeySpec)); 161 } 162 163 /** 164 * This constructor is being used only for key in more keys keyboard. 165 */ Key(Resources res, KeyboardParams params, String moreKeySpec, int x, int y, int width, int height)166 public Key(Resources res, KeyboardParams params, String moreKeySpec, 167 int x, int y, int width, int height) { 168 this(params, MoreKeySpecParser.getLabel(moreKeySpec), null, getIcon(params, moreKeySpec), 169 getCode(res, params, moreKeySpec), MoreKeySpecParser.getOutputText(moreKeySpec), 170 x, y, width, height); 171 } 172 173 /** 174 * This constructor is being used only for key in popup suggestions pane. 175 */ Key(KeyboardParams params, CharSequence label, CharSequence hintLabel, Drawable icon, int code, CharSequence outputText, int x, int y, int width, int height)176 public Key(KeyboardParams params, CharSequence label, CharSequence hintLabel, Drawable icon, 177 int code, CharSequence outputText, int x, int y, int width, int height) { 178 mHeight = height - params.mVerticalGap; 179 mHorizontalGap = params.mHorizontalGap; 180 mVerticalGap = params.mVerticalGap; 181 mVisualInsetsLeft = mVisualInsetsRight = 0; 182 mWidth = width - mHorizontalGap; 183 mHintLabel = hintLabel; 184 mLabelOption = 0; 185 mBackgroundType = BACKGROUND_TYPE_NORMAL; 186 mRepeatable = false; 187 mMoreKeys = null; 188 mMaxMoreKeysColumn = 0; 189 mLabel = label; 190 mOutputText = outputText; 191 mCode = code; 192 mIcon = icon; 193 // Horizontal gap is divided equally to both sides of the key. 194 mX = x + mHorizontalGap / 2; 195 mY = y; 196 mHitBox.set(x, y, x + width + 1, y + height); 197 } 198 199 /** 200 * Create a key with the given top-left coordinate and extract its attributes from the XML 201 * parser. 202 * @param res resources associated with the caller's context 203 * @param params the keyboard building parameters. 204 * @param row the row that this key belongs to. row's x-coordinate will be the right edge of 205 * this key. 206 * @param parser the XML parser containing the attributes for this key 207 * @param keyStyles active key styles set 208 */ Key(Resources res, KeyboardParams params, KeyboardBuilder.Row row, XmlPullParser parser, KeyStyles keyStyles)209 public Key(Resources res, KeyboardParams params, KeyboardBuilder.Row row, 210 XmlPullParser parser, KeyStyles keyStyles) { 211 final float horizontalGap = isSpacer() ? 0 : params.mHorizontalGap; 212 final int keyHeight = row.mRowHeight; 213 mVerticalGap = params.mVerticalGap; 214 mHeight = keyHeight - mVerticalGap; 215 216 final TypedArray keyAttr = res.obtainAttributes(Xml.asAttributeSet(parser), 217 R.styleable.Keyboard_Key); 218 219 final KeyStyle style; 220 if (keyAttr.hasValue(R.styleable.Keyboard_Key_keyStyle)) { 221 String styleName = keyAttr.getString(R.styleable.Keyboard_Key_keyStyle); 222 style = keyStyles.getKeyStyle(styleName); 223 if (style == null) 224 throw new ParseException("Unknown key style: " + styleName, parser); 225 } else { 226 style = keyStyles.getEmptyKeyStyle(); 227 } 228 229 final float keyXPos = row.getKeyX(keyAttr); 230 final float keyWidth = row.getKeyWidth(keyAttr, keyXPos); 231 final int keyYPos = row.getKeyY(); 232 233 // Horizontal gap is divided equally to both sides of the key. 234 mX = (int) (keyXPos + horizontalGap / 2); 235 mY = keyYPos; 236 mWidth = (int) (keyWidth - horizontalGap); 237 mHorizontalGap = (int) horizontalGap; 238 mHitBox.set((int)keyXPos, keyYPos, (int)(keyXPos + keyWidth) + 1, keyYPos + keyHeight); 239 // Update row to have current x coordinate. 240 row.setXPos(keyXPos + keyWidth); 241 242 final CharSequence[] moreKeys = style.getTextArray(keyAttr, 243 R.styleable.Keyboard_Key_moreKeys); 244 // In Arabic symbol layouts, we'd like to keep digits in more keys regardless of 245 // config_digit_more_keys_enabled. 246 if (params.mId.isAlphabetKeyboard() 247 && !res.getBoolean(R.bool.config_digit_more_keys_enabled)) { 248 mMoreKeys = MoreKeySpecParser.filterOut(res, moreKeys, MoreKeySpecParser.DIGIT_FILTER); 249 } else { 250 mMoreKeys = moreKeys; 251 } 252 mMaxMoreKeysColumn = style.getInt(keyAttr, 253 R.styleable.Keyboard_Key_maxMoreKeysColumn, params.mMaxMiniKeyboardColumn); 254 255 mBackgroundType = style.getInt(keyAttr, 256 R.styleable.Keyboard_Key_backgroundType, BACKGROUND_TYPE_NORMAL); 257 mRepeatable = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_isRepeatable, false); 258 mEnabled = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_enabled, true); 259 260 final KeyboardIconsSet iconsSet = params.mIconsSet; 261 mVisualInsetsLeft = (int) KeyboardBuilder.getDimensionOrFraction(keyAttr, 262 R.styleable.Keyboard_Key_visualInsetsLeft, params.mBaseWidth, 0); 263 mVisualInsetsRight = (int) KeyboardBuilder.getDimensionOrFraction(keyAttr, 264 R.styleable.Keyboard_Key_visualInsetsRight, params.mBaseWidth, 0); 265 mPreviewIcon = iconsSet.getIcon(style.getInt(keyAttr, 266 R.styleable.Keyboard_Key_keyIconPreview, KeyboardIconsSet.ICON_UNDEFINED)); 267 mIcon = iconsSet.getIcon(style.getInt(keyAttr, R.styleable.Keyboard_Key_keyIcon, 268 KeyboardIconsSet.ICON_UNDEFINED)); 269 final int shiftedIconId = style.getInt(keyAttr, R.styleable.Keyboard_Key_keyIconShifted, 270 KeyboardIconsSet.ICON_UNDEFINED); 271 if (shiftedIconId != KeyboardIconsSet.ICON_UNDEFINED) { 272 final Drawable shiftedIcon = iconsSet.getIcon(shiftedIconId); 273 params.addShiftedIcon(this, shiftedIcon); 274 } 275 mHintLabel = style.getText(keyAttr, R.styleable.Keyboard_Key_keyHintLabel); 276 277 mLabel = style.getText(keyAttr, R.styleable.Keyboard_Key_keyLabel); 278 mLabelOption = style.getFlag(keyAttr, R.styleable.Keyboard_Key_keyLabelOption, 0); 279 mOutputText = style.getText(keyAttr, R.styleable.Keyboard_Key_keyOutputText); 280 // Choose the first letter of the label as primary code if not 281 // specified. 282 final int code = style.getInt(keyAttr, R.styleable.Keyboard_Key_code, 283 Keyboard.CODE_UNSPECIFIED); 284 if (code == Keyboard.CODE_UNSPECIFIED && !TextUtils.isEmpty(mLabel)) { 285 final int firstChar = mLabel.charAt(0); 286 mCode = getRtlParenthesisCode(firstChar, params.mIsRtlKeyboard); 287 } else if (code != Keyboard.CODE_UNSPECIFIED) { 288 mCode = code; 289 } else { 290 mCode = Keyboard.CODE_DUMMY; 291 } 292 293 keyAttr.recycle(); 294 } 295 markAsLeftEdge(KeyboardParams params)296 public void markAsLeftEdge(KeyboardParams params) { 297 mHitBox.left = params.mHorizontalEdgesPadding; 298 } 299 markAsRightEdge(KeyboardParams params)300 public void markAsRightEdge(KeyboardParams params) { 301 mHitBox.right = params.mOccupiedWidth - params.mHorizontalEdgesPadding; 302 } 303 markAsTopEdge(KeyboardParams params)304 public void markAsTopEdge(KeyboardParams params) { 305 mHitBox.top = params.mTopPadding; 306 } 307 markAsBottomEdge(KeyboardParams params)308 public void markAsBottomEdge(KeyboardParams params) { 309 mHitBox.bottom = params.mOccupiedHeight + params.mBottomPadding; 310 } 311 isSticky()312 public boolean isSticky() { 313 return mBackgroundType == BACKGROUND_TYPE_STICKY; 314 } 315 isSpacer()316 public boolean isSpacer() { 317 return false; 318 } 319 selectTypeface(Typeface defaultTypeface)320 public Typeface selectTypeface(Typeface defaultTypeface) { 321 // TODO: Handle "bold" here too? 322 if ((mLabelOption & LABEL_OPTION_FONT_NORMAL) != 0) { 323 return Typeface.DEFAULT; 324 } else if ((mLabelOption & LABEL_OPTION_FONT_MONO_SPACE) != 0) { 325 return Typeface.MONOSPACE; 326 } else { 327 return defaultTypeface; 328 } 329 } 330 selectTextSize(int letter, int largeLetter, int label, int hintLabel)331 public int selectTextSize(int letter, int largeLetter, int label, int hintLabel) { 332 if (mLabel.length() > 1 333 && (mLabelOption & (LABEL_OPTION_FOLLOW_KEY_LETTER_RATIO 334 | LABEL_OPTION_FOLLOW_KEY_HINT_LABEL_RATIO)) == 0) { 335 return label; 336 } else if ((mLabelOption & LABEL_OPTION_FOLLOW_KEY_HINT_LABEL_RATIO) != 0) { 337 return hintLabel; 338 } else if ((mLabelOption & LABEL_OPTION_LARGE_LETTER) != 0) { 339 return largeLetter; 340 } else { 341 return letter; 342 } 343 } 344 isAlignLeft()345 public boolean isAlignLeft() { 346 return (mLabelOption & LABEL_OPTION_ALIGN_LEFT) != 0; 347 } 348 isAlignRight()349 public boolean isAlignRight() { 350 return (mLabelOption & LABEL_OPTION_ALIGN_RIGHT) != 0; 351 } 352 isAlignLeftOfCenter()353 public boolean isAlignLeftOfCenter() { 354 return (mLabelOption & LABEL_OPTION_ALIGN_LEFT_OF_CENTER) != 0; 355 } 356 hasPopupHint()357 public boolean hasPopupHint() { 358 return (mLabelOption & LABEL_OPTION_HAS_POPUP_HINT) != 0; 359 } 360 setNeedsSpecialPopupHint(boolean needsSpecialPopupHint)361 public void setNeedsSpecialPopupHint(boolean needsSpecialPopupHint) { 362 mNeedsSpecialPopupHint = needsSpecialPopupHint; 363 } 364 needsSpecialPopupHint()365 public boolean needsSpecialPopupHint() { 366 return mNeedsSpecialPopupHint; 367 } 368 hasUppercaseLetter()369 public boolean hasUppercaseLetter() { 370 return (mLabelOption & LABEL_OPTION_HAS_UPPERCASE_LETTER) != 0; 371 } 372 hasHintLabel()373 public boolean hasHintLabel() { 374 return (mLabelOption & LABEL_OPTION_HAS_HINT_LABEL) != 0; 375 } 376 hasLabelWithIconLeft()377 public boolean hasLabelWithIconLeft() { 378 return (mLabelOption & LABEL_OPTION_WITH_ICON_LEFT) != 0; 379 } 380 hasLabelWithIconRight()381 public boolean hasLabelWithIconRight() { 382 return (mLabelOption & LABEL_OPTION_WITH_ICON_RIGHT) != 0; 383 } 384 needsXScale()385 public boolean needsXScale() { 386 return (mLabelOption & LABEL_OPTION_AUTO_X_SCALE) != 0; 387 } 388 getIcon()389 public Drawable getIcon() { 390 return mIcon; 391 } 392 getPreviewIcon()393 public Drawable getPreviewIcon() { 394 return mPreviewIcon; 395 } 396 setIcon(Drawable icon)397 public void setIcon(Drawable icon) { 398 mIcon = icon; 399 } 400 setPreviewIcon(Drawable icon)401 public void setPreviewIcon(Drawable icon) { 402 mPreviewIcon = icon; 403 } 404 405 /** 406 * Informs the key that it has been pressed, in case it needs to change its appearance or 407 * state. 408 * @see #onReleased() 409 */ onPressed()410 public void onPressed() { 411 mPressed = true; 412 } 413 414 /** 415 * Informs the key that it has been released, in case it needs to change its appearance or 416 * state. 417 * @see #onPressed() 418 */ onReleased()419 public void onReleased() { 420 mPressed = false; 421 } 422 setHighlightOn(boolean highlightOn)423 public void setHighlightOn(boolean highlightOn) { 424 mHighlightOn = highlightOn; 425 } 426 isEnabled()427 public boolean isEnabled() { 428 return mEnabled; 429 } 430 setEnabled(boolean enabled)431 public void setEnabled(boolean enabled) { 432 mEnabled = enabled; 433 } 434 435 /** 436 * Detects if a point falls on this key. 437 * @param x the x-coordinate of the point 438 * @param y the y-coordinate of the point 439 * @return whether or not the point falls on the key. If the key is attached to an edge, it will 440 * assume that all points between the key and the edge are considered to be on the key. 441 * @see {@link #markAsLeftEdge(KeyboardParams)} etc. 442 */ isOnKey(int x, int y)443 public boolean isOnKey(int x, int y) { 444 return mHitBox.contains(x, y); 445 } 446 447 /** 448 * Returns the square of the distance to the nearest edge of the key and the given point. 449 * @param x the x-coordinate of the point 450 * @param y the y-coordinate of the point 451 * @return the square of the distance of the point from the nearest edge of the key 452 */ squaredDistanceToEdge(int x, int y)453 public int squaredDistanceToEdge(int x, int y) { 454 final int left = mX; 455 final int right = left + mWidth; 456 final int top = mY; 457 final int bottom = top + mHeight; 458 final int edgeX = x < left ? left : (x > right ? right : x); 459 final int edgeY = y < top ? top : (y > bottom ? bottom : y); 460 final int dx = x - edgeX; 461 final int dy = y - edgeY; 462 return dx * dx + dy * dy; 463 } 464 465 private final static int[] KEY_STATE_NORMAL_HIGHLIGHT_ON = { 466 android.R.attr.state_checkable, 467 android.R.attr.state_checked 468 }; 469 470 private final static int[] KEY_STATE_PRESSED_HIGHLIGHT_ON = { 471 android.R.attr.state_pressed, 472 android.R.attr.state_checkable, 473 android.R.attr.state_checked 474 }; 475 476 private final static int[] KEY_STATE_NORMAL_HIGHLIGHT_OFF = { 477 android.R.attr.state_checkable 478 }; 479 480 private final static int[] KEY_STATE_PRESSED_HIGHLIGHT_OFF = { 481 android.R.attr.state_pressed, 482 android.R.attr.state_checkable 483 }; 484 485 private final static int[] KEY_STATE_NORMAL = { 486 }; 487 488 private final static int[] KEY_STATE_PRESSED = { 489 android.R.attr.state_pressed 490 }; 491 492 // functional normal state (with properties) 493 private static final int[] KEY_STATE_FUNCTIONAL_NORMAL = { 494 android.R.attr.state_single 495 }; 496 497 // functional pressed state (with properties) 498 private static final int[] KEY_STATE_FUNCTIONAL_PRESSED = { 499 android.R.attr.state_single, 500 android.R.attr.state_pressed 501 }; 502 503 // action normal state (with properties) 504 private static final int[] KEY_STATE_ACTIVE_NORMAL = { 505 android.R.attr.state_active 506 }; 507 508 // action pressed state (with properties) 509 private static final int[] KEY_STATE_ACTIVE_PRESSED = { 510 android.R.attr.state_active, 511 android.R.attr.state_pressed 512 }; 513 514 /** 515 * Returns the drawable state for the key, based on the current state and type of the key. 516 * @return the drawable state of the key. 517 * @see android.graphics.drawable.StateListDrawable#setState(int[]) 518 */ getCurrentDrawableState()519 public int[] getCurrentDrawableState() { 520 final boolean pressed = mPressed; 521 522 switch (mBackgroundType) { 523 case BACKGROUND_TYPE_FUNCTIONAL: 524 return pressed ? KEY_STATE_FUNCTIONAL_PRESSED : KEY_STATE_FUNCTIONAL_NORMAL; 525 case BACKGROUND_TYPE_ACTION: 526 return pressed ? KEY_STATE_ACTIVE_PRESSED : KEY_STATE_ACTIVE_NORMAL; 527 case BACKGROUND_TYPE_STICKY: 528 if (mHighlightOn) { 529 return pressed ? KEY_STATE_PRESSED_HIGHLIGHT_ON : KEY_STATE_NORMAL_HIGHLIGHT_ON; 530 } else { 531 return pressed ? KEY_STATE_PRESSED_HIGHLIGHT_OFF : KEY_STATE_NORMAL_HIGHLIGHT_OFF; 532 } 533 default: /* BACKGROUND_TYPE_NORMAL */ 534 return pressed ? KEY_STATE_PRESSED : KEY_STATE_NORMAL; 535 } 536 } 537 538 public static class Spacer extends Key { Spacer(Resources res, KeyboardParams params, KeyboardBuilder.Row row, XmlPullParser parser, KeyStyles keyStyles)539 public Spacer(Resources res, KeyboardParams params, KeyboardBuilder.Row row, 540 XmlPullParser parser, KeyStyles keyStyles) { 541 super(res, params, row, parser, keyStyles); 542 } 543 544 /** 545 * This constructor is being used only for divider in more keys keyboard. 546 */ Spacer(KeyboardParams params, Drawable icon, int x, int y, int width, int height)547 public Spacer(KeyboardParams params, Drawable icon, int x, int y, int width, int height) { 548 super(params, null, null, icon, Keyboard.CODE_DUMMY, null, x, y, width, height); 549 } 550 551 @Override isSpacer()552 public boolean isSpacer() { 553 return true; 554 } 555 } 556 } 557