| /* |
| * Copyright (C) 2010 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| * use this file except in compliance with the License. You may obtain a copy of |
| * the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| * License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| |
| package com.android.inputmethod.keyboard; |
| |
| import android.content.res.Resources; |
| import android.content.res.TypedArray; |
| import android.graphics.Rect; |
| import android.graphics.Typeface; |
| import android.graphics.drawable.Drawable; |
| import android.text.TextUtils; |
| import android.util.Log; |
| import android.util.Xml; |
| |
| import com.android.inputmethod.keyboard.internal.KeySpecParser; |
| import com.android.inputmethod.keyboard.internal.KeyStyles; |
| import com.android.inputmethod.keyboard.internal.KeyStyles.KeyStyle; |
| import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; |
| import com.android.inputmethod.latin.R; |
| import com.android.inputmethod.latin.Utils; |
| import com.android.inputmethod.latin.XmlParseUtils; |
| |
| import org.xmlpull.v1.XmlPullParser; |
| import org.xmlpull.v1.XmlPullParserException; |
| |
| import java.util.Arrays; |
| |
| /** |
| * Class for describing the position and characteristics of a single key in the keyboard. |
| */ |
| public class Key { |
| private static final String TAG = Key.class.getSimpleName(); |
| |
| /** |
| * The key code (unicode or custom code) that this key generates. |
| */ |
| public final int mCode; |
| public final int mAltCode; |
| |
| /** Label to display */ |
| public final String mLabel; |
| /** Hint label to display on the key in conjunction with the label */ |
| public final String mHintLabel; |
| /** Flags of the label */ |
| private final int mLabelFlags; |
| private static final int LABEL_FLAGS_ALIGN_LEFT = 0x01; |
| private static final int LABEL_FLAGS_ALIGN_RIGHT = 0x02; |
| private static final int LABEL_FLAGS_ALIGN_LEFT_OF_CENTER = 0x08; |
| private static final int LABEL_FLAGS_LARGE_LETTER = 0x10; |
| private static final int LABEL_FLAGS_FONT_NORMAL = 0x20; |
| private static final int LABEL_FLAGS_FONT_MONO_SPACE = 0x40; |
| private static final int LABEL_FLAGS_FOLLOW_KEY_LETTER_RATIO = 0x80; |
| private static final int LABEL_FLAGS_FOLLOW_KEY_HINT_LABEL_RATIO = 0x100; |
| private static final int LABEL_FLAGS_HAS_POPUP_HINT = 0x200; |
| private static final int LABEL_FLAGS_HAS_SHIFTED_LETTER_HINT = 0x400; |
| private static final int LABEL_FLAGS_HAS_HINT_LABEL = 0x800; |
| private static final int LABEL_FLAGS_WITH_ICON_LEFT = 0x1000; |
| private static final int LABEL_FLAGS_WITH_ICON_RIGHT = 0x2000; |
| private static final int LABEL_FLAGS_AUTO_X_SCALE = 0x4000; |
| private static final int LABEL_FLAGS_PRESERVE_CASE = 0x8000; |
| private static final int LABEL_FLAGS_SHIFTED_LETTER_ACTIVATED = 0x10000; |
| private static final int LABEL_FLAGS_FROM_CUSTOM_ACTION_LABEL = 0x20000; |
| |
| /** Icon to display instead of a label. Icon takes precedence over a label */ |
| private final int mIconId; |
| /** Icon for disabled state */ |
| private final int mDisabledIconId; |
| /** Preview version of the icon, for the preview popup */ |
| private final int mPreviewIconId; |
| |
| /** Width of the key, not including the gap */ |
| public final int mWidth; |
| /** Height of the key, not including the gap */ |
| public final int mHeight; |
| /** The horizontal gap around this key */ |
| public final int mHorizontalGap; |
| /** The vertical gap below this key */ |
| public final int mVerticalGap; |
| /** The visual insets */ |
| public final int mVisualInsetsLeft; |
| public final int mVisualInsetsRight; |
| /** X coordinate of the key in the keyboard layout */ |
| public final int mX; |
| /** Y coordinate of the key in the keyboard layout */ |
| public final int mY; |
| /** Hit bounding box of the key */ |
| public final Rect mHitBox = new Rect(); |
| |
| /** Text to output when pressed. This can be multiple characters, like ".com" */ |
| public final CharSequence mOutputText; |
| /** More keys */ |
| public final String[] mMoreKeys; |
| /** More keys column number and flags */ |
| private final int mMoreKeysColumnAndFlags; |
| private static final int MORE_KEYS_COLUMN_MASK = 0x000000ff; |
| private static final int MORE_KEYS_FLAGS_FIXED_COLUMN_ORDER = 0x80000000; |
| private static final String MORE_KEYS_AUTO_COLUMN_ORDER = "!autoColumnOrder!"; |
| private static final String MORE_KEYS_FIXED_COLUMN_ORDER = "!fixedColumnOrder!"; |
| |
| /** Background type that represents different key background visual than normal one. */ |
| public final int mBackgroundType; |
| public static final int BACKGROUND_TYPE_NORMAL = 0; |
| public static final int BACKGROUND_TYPE_FUNCTIONAL = 1; |
| public static final int BACKGROUND_TYPE_ACTION = 2; |
| public static final int BACKGROUND_TYPE_STICKY_OFF = 3; |
| public static final int BACKGROUND_TYPE_STICKY_ON = 4; |
| |
| private final int mActionFlags; |
| private static final int ACTION_FLAGS_IS_REPEATABLE = 0x01; |
| private static final int ACTION_FLAGS_NO_KEY_PREVIEW = 0x02; |
| private static final int ACTION_FLAGS_ALT_CODE_WHILE_TYPING = 0x04; |
| private static final int ACTION_FLAGS_ENABLE_LONG_PRESS = 0x08; |
| |
| private final int mHashCode; |
| |
| /** The current pressed state of this key */ |
| private boolean mPressed; |
| /** Key is enabled and responds on press */ |
| private boolean mEnabled = true; |
| |
| /** |
| * This constructor is being used only for key in more keys keyboard. |
| */ |
| public Key(Resources res, Keyboard.Params params, String moreKeySpec, |
| int x, int y, int width, int height) { |
| this(params, KeySpecParser.getLabel(moreKeySpec), null, |
| KeySpecParser.getIconId(moreKeySpec), |
| KeySpecParser.getCode(res, moreKeySpec), |
| KeySpecParser.getOutputText(moreKeySpec), |
| x, y, width, height); |
| } |
| |
| /** |
| * This constructor is being used only for key in popup suggestions pane. |
| */ |
| public Key(Keyboard.Params params, String label, String hintLabel, int iconId, |
| int code, String outputText, int x, int y, int width, int height) { |
| mHeight = height - params.mVerticalGap; |
| mHorizontalGap = params.mHorizontalGap; |
| mVerticalGap = params.mVerticalGap; |
| mVisualInsetsLeft = mVisualInsetsRight = 0; |
| mWidth = width - mHorizontalGap; |
| mHintLabel = hintLabel; |
| mLabelFlags = 0; |
| mBackgroundType = BACKGROUND_TYPE_NORMAL; |
| mActionFlags = 0; |
| mMoreKeys = null; |
| mMoreKeysColumnAndFlags = 0; |
| mLabel = label; |
| mOutputText = outputText; |
| mCode = code; |
| mAltCode = Keyboard.CODE_UNSPECIFIED; |
| mIconId = iconId; |
| mDisabledIconId = KeyboardIconsSet.ICON_UNDEFINED; |
| mPreviewIconId = KeyboardIconsSet.ICON_UNDEFINED; |
| // Horizontal gap is divided equally to both sides of the key. |
| mX = x + mHorizontalGap / 2; |
| mY = y; |
| mHitBox.set(x, y, x + width + 1, y + height); |
| |
| mHashCode = hashCode(this); |
| } |
| |
| /** |
| * Create a key with the given top-left coordinate and extract its attributes from the XML |
| * parser. |
| * @param res resources associated with the caller's context |
| * @param params the keyboard building parameters. |
| * @param row the row that this key belongs to. row's x-coordinate will be the right edge of |
| * this key. |
| * @param parser the XML parser containing the attributes for this key |
| * @param keyStyles active key styles set |
| * @throws XmlPullParserException |
| */ |
| public Key(Resources res, Keyboard.Params params, Keyboard.Builder.Row row, |
| XmlPullParser parser, KeyStyles keyStyles) throws XmlPullParserException { |
| final float horizontalGap = isSpacer() ? 0 : params.mHorizontalGap; |
| final int keyHeight = row.mRowHeight; |
| mVerticalGap = params.mVerticalGap; |
| mHeight = keyHeight - mVerticalGap; |
| |
| final TypedArray keyAttr = res.obtainAttributes(Xml.asAttributeSet(parser), |
| R.styleable.Keyboard_Key); |
| |
| final KeyStyle style; |
| if (keyAttr.hasValue(R.styleable.Keyboard_Key_keyStyle)) { |
| String styleName = keyAttr.getString(R.styleable.Keyboard_Key_keyStyle); |
| style = keyStyles.getKeyStyle(styleName); |
| if (style == null) |
| throw new XmlParseUtils.ParseException( |
| "Unknown key style: " + styleName, parser); |
| } else { |
| style = KeyStyles.getEmptyKeyStyle(); |
| } |
| |
| final float keyXPos = row.getKeyX(keyAttr); |
| final float keyWidth = row.getKeyWidth(keyAttr, keyXPos); |
| final int keyYPos = row.getKeyY(); |
| |
| // Horizontal gap is divided equally to both sides of the key. |
| mX = (int) (keyXPos + horizontalGap / 2); |
| mY = keyYPos; |
| mWidth = (int) (keyWidth - horizontalGap); |
| mHorizontalGap = (int) horizontalGap; |
| mHitBox.set((int)keyXPos, keyYPos, (int)(keyXPos + keyWidth) + 1, keyYPos + keyHeight); |
| // Update row to have current x coordinate. |
| row.setXPos(keyXPos + keyWidth); |
| |
| mBackgroundType = style.getInt(keyAttr, |
| R.styleable.Keyboard_Key_backgroundType, BACKGROUND_TYPE_NORMAL); |
| |
| mVisualInsetsLeft = (int) Keyboard.Builder.getDimensionOrFraction(keyAttr, |
| R.styleable.Keyboard_Key_visualInsetsLeft, params.mBaseWidth, 0); |
| mVisualInsetsRight = (int) Keyboard.Builder.getDimensionOrFraction(keyAttr, |
| R.styleable.Keyboard_Key_visualInsetsRight, params.mBaseWidth, 0); |
| mPreviewIconId = style.getInt(keyAttr, |
| R.styleable.Keyboard_Key_keyIconPreview, KeyboardIconsSet.ICON_UNDEFINED); |
| mIconId = style.getInt(keyAttr, |
| R.styleable.Keyboard_Key_keyIcon, KeyboardIconsSet.ICON_UNDEFINED); |
| mDisabledIconId = style.getInt(keyAttr, |
| R.styleable.Keyboard_Key_keyIconDisabled, KeyboardIconsSet.ICON_UNDEFINED); |
| |
| mLabelFlags = style.getFlag(keyAttr, R.styleable.Keyboard_Key_keyLabelFlags); |
| final boolean preserveCase = (mLabelFlags & LABEL_FLAGS_PRESERVE_CASE) != 0; |
| int actionFlags = style.getFlag(keyAttr, R.styleable.Keyboard_Key_keyActionFlags); |
| String[] moreKeys = style.getStringArray(keyAttr, R.styleable.Keyboard_Key_moreKeys); |
| |
| int moreKeysColumn = style.getInt(keyAttr, |
| R.styleable.Keyboard_Key_maxMoreKeysColumn, params.mMaxMoreKeysKeyboardColumn); |
| int value; |
| if ((value = KeySpecParser.getIntValue(moreKeys, MORE_KEYS_AUTO_COLUMN_ORDER, -1)) > 0) { |
| moreKeysColumn = value & MORE_KEYS_COLUMN_MASK; |
| } |
| if ((value = KeySpecParser.getIntValue(moreKeys, MORE_KEYS_FIXED_COLUMN_ORDER, -1)) > 0) { |
| moreKeysColumn = MORE_KEYS_FLAGS_FIXED_COLUMN_ORDER | (value & MORE_KEYS_COLUMN_MASK); |
| } |
| mMoreKeysColumnAndFlags = moreKeysColumn; |
| |
| final String[] additionalMoreKeys = style.getStringArray( |
| keyAttr, R.styleable.Keyboard_Key_additionalMoreKeys); |
| moreKeys = KeySpecParser.insertAddtionalMoreKeys(moreKeys, additionalMoreKeys); |
| if (moreKeys != null) { |
| actionFlags |= ACTION_FLAGS_ENABLE_LONG_PRESS; |
| for (int i = 0; i < moreKeys.length; i++) { |
| moreKeys[i] = adjustCaseOfStringForKeyboardId( |
| moreKeys[i], preserveCase, params.mId); |
| } |
| } |
| mActionFlags = actionFlags; |
| mMoreKeys = moreKeys; |
| |
| if ((mLabelFlags & LABEL_FLAGS_FROM_CUSTOM_ACTION_LABEL) != 0) { |
| mLabel = params.mId.mCustomActionLabel; |
| } else { |
| mLabel = adjustCaseOfStringForKeyboardId(style.getString( |
| keyAttr, R.styleable.Keyboard_Key_keyLabel), preserveCase, params.mId); |
| } |
| mHintLabel = adjustCaseOfStringForKeyboardId(style.getString( |
| keyAttr, R.styleable.Keyboard_Key_keyHintLabel), preserveCase, params.mId); |
| String outputText = adjustCaseOfStringForKeyboardId(style.getString( |
| keyAttr, R.styleable.Keyboard_Key_keyOutputText), preserveCase, params.mId); |
| final int code = style.getInt( |
| keyAttr, R.styleable.Keyboard_Key_code, Keyboard.CODE_UNSPECIFIED); |
| // Choose the first letter of the label as primary code if not specified. |
| if (code == Keyboard.CODE_UNSPECIFIED && TextUtils.isEmpty(outputText) |
| && !TextUtils.isEmpty(mLabel)) { |
| if (Utils.codePointCount(mLabel) == 1) { |
| // Use the first letter of the hint label if shiftedLetterActivated flag is |
| // specified. |
| if (hasShiftedLetterHint() && isShiftedLetterActivated() |
| && !TextUtils.isEmpty(mHintLabel)) { |
| mCode = mHintLabel.codePointAt(0); |
| } else { |
| mCode = mLabel.codePointAt(0); |
| } |
| } else { |
| // In some locale and case, the character might be represented by multiple code |
| // points, such as upper case Eszett of German alphabet. |
| outputText = mLabel; |
| mCode = Keyboard.CODE_OUTPUT_TEXT; |
| } |
| } else if (code == Keyboard.CODE_UNSPECIFIED && outputText != null) { |
| if (Utils.codePointCount(outputText) == 1) { |
| mCode = outputText.codePointAt(0); |
| outputText = null; |
| } else { |
| mCode = Keyboard.CODE_OUTPUT_TEXT; |
| } |
| } else { |
| mCode = adjustCaseOfCodeForKeyboardId(code, preserveCase, params.mId); |
| } |
| mOutputText = outputText; |
| mAltCode = adjustCaseOfCodeForKeyboardId(style.getInt(keyAttr, |
| R.styleable.Keyboard_Key_altCode, Keyboard.CODE_UNSPECIFIED), preserveCase, |
| params.mId); |
| mHashCode = hashCode(this); |
| |
| keyAttr.recycle(); |
| |
| if (hasShiftedLetterHint() && TextUtils.isEmpty(mHintLabel)) { |
| Log.w(TAG, "hasShiftedLetterHint specified without keyHintLabel: " + this); |
| } |
| } |
| |
| private static int adjustCaseOfCodeForKeyboardId(int code, boolean preserveCase, |
| KeyboardId id) { |
| if (!Keyboard.isLetterCode(code) || preserveCase) return code; |
| final String text = new String(new int[] { code } , 0, 1); |
| final String casedText = adjustCaseOfStringForKeyboardId(text, preserveCase, id); |
| return Utils.codePointCount(casedText) == 1 |
| ? casedText.codePointAt(0) : Keyboard.CODE_UNSPECIFIED; |
| } |
| |
| private static String adjustCaseOfStringForKeyboardId(String text, boolean preserveCase, |
| KeyboardId id) { |
| if (text == null || preserveCase) return text; |
| switch (id.mElementId) { |
| case KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED: |
| case KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED: |
| case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED: |
| case KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED: |
| return text.toUpperCase(id.mLocale); |
| default: |
| return text; |
| } |
| } |
| |
| private static int hashCode(Key key) { |
| return Arrays.hashCode(new Object[] { |
| key.mX, |
| key.mY, |
| key.mWidth, |
| key.mHeight, |
| key.mCode, |
| key.mLabel, |
| key.mHintLabel, |
| key.mIconId, |
| key.mBackgroundType, |
| Arrays.hashCode(key.mMoreKeys), |
| key.mOutputText, |
| key.mActionFlags, |
| key.mLabelFlags, |
| // Key can be distinguishable without the following members. |
| // key.mAltCode, |
| // key.mDisabledIconId, |
| // key.mPreviewIconId, |
| // key.mHorizontalGap, |
| // key.mVerticalGap, |
| // key.mVisualInsetLeft, |
| // key.mVisualInsetRight, |
| // key.mMaxMoreKeysColumn, |
| }); |
| } |
| |
| private boolean equals(Key o) { |
| if (this == o) return true; |
| return o.mX == mX |
| && o.mY == mY |
| && o.mWidth == mWidth |
| && o.mHeight == mHeight |
| && o.mCode == mCode |
| && TextUtils.equals(o.mLabel, mLabel) |
| && TextUtils.equals(o.mHintLabel, mHintLabel) |
| && o.mIconId == mIconId |
| && o.mBackgroundType == mBackgroundType |
| && Arrays.equals(o.mMoreKeys, mMoreKeys) |
| && TextUtils.equals(o.mOutputText, mOutputText) |
| && o.mActionFlags == mActionFlags |
| && o.mLabelFlags == mLabelFlags; |
| } |
| |
| @Override |
| public int hashCode() { |
| return mHashCode; |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| return o instanceof Key && equals((Key)o); |
| } |
| |
| @Override |
| public String toString() { |
| return String.format("%s/%s %d,%d %dx%d %s/%s/%s", |
| Keyboard.printableCode(mCode), mLabel, mX, mY, mWidth, mHeight, mHintLabel, |
| KeyboardIconsSet.getIconName(mIconId), backgroundName(mBackgroundType)); |
| } |
| |
| private static String backgroundName(int backgroundType) { |
| switch (backgroundType) { |
| case BACKGROUND_TYPE_NORMAL: return "normal"; |
| case BACKGROUND_TYPE_FUNCTIONAL: return "functional"; |
| case BACKGROUND_TYPE_ACTION: return "action"; |
| case BACKGROUND_TYPE_STICKY_OFF: return "stickyOff"; |
| case BACKGROUND_TYPE_STICKY_ON: return "stickyOn"; |
| default: return null; |
| } |
| } |
| |
| public void markAsLeftEdge(Keyboard.Params params) { |
| mHitBox.left = params.mHorizontalEdgesPadding; |
| } |
| |
| public void markAsRightEdge(Keyboard.Params params) { |
| mHitBox.right = params.mOccupiedWidth - params.mHorizontalEdgesPadding; |
| } |
| |
| public void markAsTopEdge(Keyboard.Params params) { |
| mHitBox.top = params.mTopPadding; |
| } |
| |
| public void markAsBottomEdge(Keyboard.Params params) { |
| mHitBox.bottom = params.mOccupiedHeight + params.mBottomPadding; |
| } |
| |
| public final boolean isSpacer() { |
| return this instanceof Spacer; |
| } |
| |
| public boolean isShift() { |
| return mCode == Keyboard.CODE_SHIFT; |
| } |
| |
| public boolean isModifier() { |
| return mCode == Keyboard.CODE_SHIFT || mCode == Keyboard.CODE_SWITCH_ALPHA_SYMBOL; |
| } |
| |
| public boolean isRepeatable() { |
| return (mActionFlags & ACTION_FLAGS_IS_REPEATABLE) != 0; |
| } |
| |
| public boolean noKeyPreview() { |
| return (mActionFlags & ACTION_FLAGS_NO_KEY_PREVIEW) != 0; |
| } |
| |
| public boolean altCodeWhileTyping() { |
| return (mActionFlags & ACTION_FLAGS_ALT_CODE_WHILE_TYPING) != 0; |
| } |
| |
| public boolean isLongPressEnabled() { |
| // We need not start long press timer on the key which has activated shifted letter. |
| return (mActionFlags & ACTION_FLAGS_ENABLE_LONG_PRESS) != 0 |
| && (mLabelFlags & LABEL_FLAGS_SHIFTED_LETTER_ACTIVATED) == 0; |
| } |
| |
| public Typeface selectTypeface(Typeface defaultTypeface) { |
| // TODO: Handle "bold" here too? |
| if ((mLabelFlags & LABEL_FLAGS_FONT_NORMAL) != 0) { |
| return Typeface.DEFAULT; |
| } else if ((mLabelFlags & LABEL_FLAGS_FONT_MONO_SPACE) != 0) { |
| return Typeface.MONOSPACE; |
| } else { |
| return defaultTypeface; |
| } |
| } |
| |
| public int selectTextSize(int letter, int largeLetter, int label, int hintLabel) { |
| if (mLabel.length() > 1 |
| && (mLabelFlags & (LABEL_FLAGS_FOLLOW_KEY_LETTER_RATIO |
| | LABEL_FLAGS_FOLLOW_KEY_HINT_LABEL_RATIO)) == 0) { |
| return label; |
| } else if ((mLabelFlags & LABEL_FLAGS_FOLLOW_KEY_HINT_LABEL_RATIO) != 0) { |
| return hintLabel; |
| } else if ((mLabelFlags & LABEL_FLAGS_LARGE_LETTER) != 0) { |
| return largeLetter; |
| } else { |
| return letter; |
| } |
| } |
| |
| public boolean isAlignLeft() { |
| return (mLabelFlags & LABEL_FLAGS_ALIGN_LEFT) != 0; |
| } |
| |
| public boolean isAlignRight() { |
| return (mLabelFlags & LABEL_FLAGS_ALIGN_RIGHT) != 0; |
| } |
| |
| public boolean isAlignLeftOfCenter() { |
| return (mLabelFlags & LABEL_FLAGS_ALIGN_LEFT_OF_CENTER) != 0; |
| } |
| |
| public boolean hasPopupHint() { |
| return (mLabelFlags & LABEL_FLAGS_HAS_POPUP_HINT) != 0; |
| } |
| |
| public boolean hasShiftedLetterHint() { |
| return (mLabelFlags & LABEL_FLAGS_HAS_SHIFTED_LETTER_HINT) != 0; |
| } |
| |
| public boolean hasHintLabel() { |
| return (mLabelFlags & LABEL_FLAGS_HAS_HINT_LABEL) != 0; |
| } |
| |
| public boolean hasLabelWithIconLeft() { |
| return (mLabelFlags & LABEL_FLAGS_WITH_ICON_LEFT) != 0; |
| } |
| |
| public boolean hasLabelWithIconRight() { |
| return (mLabelFlags & LABEL_FLAGS_WITH_ICON_RIGHT) != 0; |
| } |
| |
| public boolean needsXScale() { |
| return (mLabelFlags & LABEL_FLAGS_AUTO_X_SCALE) != 0; |
| } |
| |
| public boolean isShiftedLetterActivated() { |
| return (mLabelFlags & LABEL_FLAGS_SHIFTED_LETTER_ACTIVATED) != 0; |
| } |
| |
| public int getMoreKeysColumn() { |
| return mMoreKeysColumnAndFlags & MORE_KEYS_COLUMN_MASK; |
| } |
| |
| public boolean isFixedColumnOrderMoreKeys() { |
| return (mMoreKeysColumnAndFlags & MORE_KEYS_FLAGS_FIXED_COLUMN_ORDER) != 0; |
| } |
| |
| public Drawable getIcon(KeyboardIconsSet iconSet) { |
| return iconSet.getIconDrawable(mIconId); |
| } |
| |
| public Drawable getDisabledIcon(KeyboardIconsSet iconSet) { |
| return iconSet.getIconDrawable(mDisabledIconId); |
| } |
| |
| public Drawable getPreviewIcon(KeyboardIconsSet iconSet) { |
| return mPreviewIconId != KeyboardIconsSet.ICON_UNDEFINED |
| ? iconSet.getIconDrawable(mPreviewIconId) |
| : iconSet.getIconDrawable(mIconId); |
| } |
| |
| /** |
| * Informs the key that it has been pressed, in case it needs to change its appearance or |
| * state. |
| * @see #onReleased() |
| */ |
| public void onPressed() { |
| mPressed = true; |
| } |
| |
| /** |
| * Informs the key that it has been released, in case it needs to change its appearance or |
| * state. |
| * @see #onPressed() |
| */ |
| public void onReleased() { |
| mPressed = false; |
| } |
| |
| public boolean isEnabled() { |
| return mEnabled; |
| } |
| |
| public void setEnabled(boolean enabled) { |
| mEnabled = enabled; |
| } |
| |
| /** |
| * Detects if a point falls on this key. |
| * @param x the x-coordinate of the point |
| * @param y the y-coordinate of the point |
| * @return whether or not the point falls on the key. If the key is attached to an edge, it |
| * will assume that all points between the key and the edge are considered to be on the key. |
| * @see #markAsLeftEdge(Keyboard.Params) etc. |
| */ |
| public boolean isOnKey(int x, int y) { |
| return mHitBox.contains(x, y); |
| } |
| |
| /** |
| * Returns the square of the distance to the nearest edge of the key and the given point. |
| * @param x the x-coordinate of the point |
| * @param y the y-coordinate of the point |
| * @return the square of the distance of the point from the nearest edge of the key |
| */ |
| public int squaredDistanceToEdge(int x, int y) { |
| final int left = mX; |
| final int right = left + mWidth; |
| final int top = mY; |
| final int bottom = top + mHeight; |
| final int edgeX = x < left ? left : (x > right ? right : x); |
| final int edgeY = y < top ? top : (y > bottom ? bottom : y); |
| final int dx = x - edgeX; |
| final int dy = y - edgeY; |
| return dx * dx + dy * dy; |
| } |
| |
| private final static int[] KEY_STATE_NORMAL_HIGHLIGHT_ON = { |
| android.R.attr.state_checkable, |
| android.R.attr.state_checked |
| }; |
| |
| private final static int[] KEY_STATE_PRESSED_HIGHLIGHT_ON = { |
| android.R.attr.state_pressed, |
| android.R.attr.state_checkable, |
| android.R.attr.state_checked |
| }; |
| |
| private final static int[] KEY_STATE_NORMAL_HIGHLIGHT_OFF = { |
| android.R.attr.state_checkable |
| }; |
| |
| private final static int[] KEY_STATE_PRESSED_HIGHLIGHT_OFF = { |
| android.R.attr.state_pressed, |
| android.R.attr.state_checkable |
| }; |
| |
| private final static int[] KEY_STATE_NORMAL = { |
| }; |
| |
| private final static int[] KEY_STATE_PRESSED = { |
| android.R.attr.state_pressed |
| }; |
| |
| // functional normal state (with properties) |
| private static final int[] KEY_STATE_FUNCTIONAL_NORMAL = { |
| android.R.attr.state_single |
| }; |
| |
| // functional pressed state (with properties) |
| private static final int[] KEY_STATE_FUNCTIONAL_PRESSED = { |
| android.R.attr.state_single, |
| android.R.attr.state_pressed |
| }; |
| |
| // action normal state (with properties) |
| private static final int[] KEY_STATE_ACTIVE_NORMAL = { |
| android.R.attr.state_active |
| }; |
| |
| // action pressed state (with properties) |
| private static final int[] KEY_STATE_ACTIVE_PRESSED = { |
| android.R.attr.state_active, |
| android.R.attr.state_pressed |
| }; |
| |
| /** |
| * Returns the drawable state for the key, based on the current state and type of the key. |
| * @return the drawable state of the key. |
| * @see android.graphics.drawable.StateListDrawable#setState(int[]) |
| */ |
| public int[] getCurrentDrawableState() { |
| switch (mBackgroundType) { |
| case BACKGROUND_TYPE_FUNCTIONAL: |
| return mPressed ? KEY_STATE_FUNCTIONAL_PRESSED : KEY_STATE_FUNCTIONAL_NORMAL; |
| case BACKGROUND_TYPE_ACTION: |
| return mPressed ? KEY_STATE_ACTIVE_PRESSED : KEY_STATE_ACTIVE_NORMAL; |
| case BACKGROUND_TYPE_STICKY_OFF: |
| return mPressed ? KEY_STATE_PRESSED_HIGHLIGHT_OFF : KEY_STATE_NORMAL_HIGHLIGHT_OFF; |
| case BACKGROUND_TYPE_STICKY_ON: |
| return mPressed ? KEY_STATE_PRESSED_HIGHLIGHT_ON : KEY_STATE_NORMAL_HIGHLIGHT_ON; |
| default: /* BACKGROUND_TYPE_NORMAL */ |
| return mPressed ? KEY_STATE_PRESSED : KEY_STATE_NORMAL; |
| } |
| } |
| |
| public static class Spacer extends Key { |
| public Spacer(Resources res, Keyboard.Params params, Keyboard.Builder.Row row, |
| XmlPullParser parser, KeyStyles keyStyles) throws XmlPullParserException { |
| super(res, params, row, parser, keyStyles); |
| } |
| |
| /** |
| * This constructor is being used only for divider in more keys keyboard. |
| */ |
| protected Spacer(Keyboard.Params params, int x, int y, int width, int height) { |
| super(params, null, null, KeyboardIconsSet.ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED, |
| null, x, y, width, height); |
| } |
| } |
| } |