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 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