Home | History | Annotate | Download | only in keyboard
      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 
    141     private static void addRtlParenthesisPair(int left, int right) {
    142         sRtlParenthesisMap.put(left, right);
    143         sRtlParenthesisMap.put(right, left);
    144     }
    145 
    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 
    154     private static int getCode(Resources res, KeyboardParams params, String moreKeySpec) {
    155         return getRtlParenthesisCode(
    156                 MoreKeySpecParser.getCode(res, moreKeySpec), params.mIsRtlKeyboard);
    157     }
    158 
    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      */
    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      */
    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      */
    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 
    296     public void markAsLeftEdge(KeyboardParams params) {
    297         mHitBox.left = params.mHorizontalEdgesPadding;
    298     }
    299 
    300     public void markAsRightEdge(KeyboardParams params) {
    301         mHitBox.right = params.mOccupiedWidth - params.mHorizontalEdgesPadding;
    302     }
    303 
    304     public void markAsTopEdge(KeyboardParams params) {
    305         mHitBox.top = params.mTopPadding;
    306     }
    307 
    308     public void markAsBottomEdge(KeyboardParams params) {
    309         mHitBox.bottom = params.mOccupiedHeight + params.mBottomPadding;
    310     }
    311 
    312     public boolean isSticky() {
    313         return mBackgroundType == BACKGROUND_TYPE_STICKY;
    314     }
    315 
    316     public boolean isSpacer() {
    317         return false;
    318     }
    319 
    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 
    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 
    345     public boolean isAlignLeft() {
    346         return (mLabelOption & LABEL_OPTION_ALIGN_LEFT) != 0;
    347     }
    348 
    349     public boolean isAlignRight() {
    350         return (mLabelOption & LABEL_OPTION_ALIGN_RIGHT) != 0;
    351     }
    352 
    353     public boolean isAlignLeftOfCenter() {
    354         return (mLabelOption & LABEL_OPTION_ALIGN_LEFT_OF_CENTER) != 0;
    355     }
    356 
    357     public boolean hasPopupHint() {
    358         return (mLabelOption & LABEL_OPTION_HAS_POPUP_HINT) != 0;
    359     }
    360 
    361     public void setNeedsSpecialPopupHint(boolean needsSpecialPopupHint) {
    362         mNeedsSpecialPopupHint = needsSpecialPopupHint;
    363     }
    364 
    365     public boolean needsSpecialPopupHint() {
    366         return mNeedsSpecialPopupHint;
    367     }
    368 
    369     public boolean hasUppercaseLetter() {
    370         return (mLabelOption & LABEL_OPTION_HAS_UPPERCASE_LETTER) != 0;
    371     }
    372 
    373     public boolean hasHintLabel() {
    374         return (mLabelOption & LABEL_OPTION_HAS_HINT_LABEL) != 0;
    375     }
    376 
    377     public boolean hasLabelWithIconLeft() {
    378         return (mLabelOption & LABEL_OPTION_WITH_ICON_LEFT) != 0;
    379     }
    380 
    381     public boolean hasLabelWithIconRight() {
    382         return (mLabelOption & LABEL_OPTION_WITH_ICON_RIGHT) != 0;
    383     }
    384 
    385     public boolean needsXScale() {
    386         return (mLabelOption & LABEL_OPTION_AUTO_X_SCALE) != 0;
    387     }
    388 
    389     public Drawable getIcon() {
    390         return mIcon;
    391     }
    392 
    393     public Drawable getPreviewIcon() {
    394         return mPreviewIcon;
    395     }
    396 
    397     public void setIcon(Drawable icon) {
    398         mIcon = icon;
    399     }
    400 
    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      */
    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      */
    419     public void onReleased() {
    420         mPressed = false;
    421     }
    422 
    423     public void setHighlightOn(boolean highlightOn) {
    424         mHighlightOn = highlightOn;
    425     }
    426 
    427     public boolean isEnabled() {
    428         return mEnabled;
    429     }
    430 
    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      */
    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      */
    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      */
    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 {
    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          */
    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
    552         public boolean isSpacer() {
    553             return true;
    554         }
    555     }
    556 }
    557