2010-12-02 09:46:21 +00:00
|
|
|
/*
|
2011-05-20 03:09:57 +00:00
|
|
|
* Copyright (C) 2010 The Android Open Source Project
|
2010-12-02 09:46:21 +00:00
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
2011-06-23 12:23:44 +00:00
|
|
|
package com.android.inputmethod.keyboard;
|
2010-12-02 09:46:21 +00:00
|
|
|
|
|
|
|
import android.content.res.Resources;
|
|
|
|
import android.content.res.TypedArray;
|
|
|
|
import android.content.res.XmlResourceParser;
|
2011-06-25 10:38:55 +00:00
|
|
|
import android.graphics.Typeface;
|
2010-12-02 09:46:21 +00:00
|
|
|
import android.graphics.drawable.Drawable;
|
|
|
|
import android.text.TextUtils;
|
|
|
|
import android.util.Xml;
|
|
|
|
|
2011-06-23 12:23:44 +00:00
|
|
|
import com.android.inputmethod.keyboard.internal.KeyStyles;
|
2011-06-25 10:38:55 +00:00
|
|
|
import com.android.inputmethod.keyboard.internal.KeyStyles.KeyStyle;
|
2011-08-02 21:35:18 +00:00
|
|
|
import com.android.inputmethod.keyboard.internal.KeyboardBuilder;
|
|
|
|
import com.android.inputmethod.keyboard.internal.KeyboardBuilder.ParseException;
|
2011-08-30 09:35:56 +00:00
|
|
|
import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
|
|
|
|
import com.android.inputmethod.keyboard.internal.KeyboardParams;
|
2011-08-31 06:26:32 +00:00
|
|
|
import com.android.inputmethod.keyboard.internal.MoreKeySpecParser;
|
2011-06-21 14:38:42 +00:00
|
|
|
import com.android.inputmethod.latin.R;
|
|
|
|
|
2011-07-24 23:35:54 +00:00
|
|
|
import java.util.HashMap;
|
|
|
|
import java.util.Map;
|
2011-01-06 13:37:39 +00:00
|
|
|
|
2010-12-02 09:46:21 +00:00
|
|
|
/**
|
|
|
|
* Class for describing the position and characteristics of a single key in the keyboard.
|
|
|
|
*/
|
|
|
|
public class Key {
|
|
|
|
/**
|
2010-12-20 11:30:26 +00:00
|
|
|
* The key code (unicode or custom code) that this key generates.
|
2010-12-02 09:46:21 +00:00
|
|
|
*/
|
2010-12-20 11:30:26 +00:00
|
|
|
public final int mCode;
|
2010-12-02 09:46:21 +00:00
|
|
|
|
|
|
|
/** Label to display */
|
2010-12-03 04:17:26 +00:00
|
|
|
public final CharSequence mLabel;
|
2011-06-25 10:38:55 +00:00
|
|
|
/** Hint label to display on the key in conjunction with the label */
|
|
|
|
public final CharSequence mHintLabel;
|
2010-12-02 09:46:21 +00:00
|
|
|
/** Option of the label */
|
2011-08-03 04:29:24 +00:00
|
|
|
private final int mLabelOption;
|
|
|
|
private static final int LABEL_OPTION_ALIGN_LEFT = 0x01;
|
|
|
|
private static final int LABEL_OPTION_ALIGN_RIGHT = 0x02;
|
|
|
|
private static final int LABEL_OPTION_ALIGN_LEFT_OF_CENTER = 0x08;
|
2011-06-25 10:38:55 +00:00
|
|
|
private static final int LABEL_OPTION_LARGE_LETTER = 0x10;
|
|
|
|
private static final int LABEL_OPTION_FONT_NORMAL = 0x20;
|
|
|
|
private static final int LABEL_OPTION_FONT_MONO_SPACE = 0x40;
|
|
|
|
private static final int LABEL_OPTION_FOLLOW_KEY_LETTER_RATIO = 0x80;
|
|
|
|
private static final int LABEL_OPTION_FOLLOW_KEY_HINT_LABEL_RATIO = 0x100;
|
|
|
|
private static final int LABEL_OPTION_HAS_POPUP_HINT = 0x200;
|
|
|
|
private static final int LABEL_OPTION_HAS_UPPERCASE_LETTER = 0x400;
|
|
|
|
private static final int LABEL_OPTION_HAS_HINT_LABEL = 0x800;
|
2011-08-03 04:29:24 +00:00
|
|
|
private static final int LABEL_OPTION_WITH_ICON_LEFT = 0x1000;
|
|
|
|
private static final int LABEL_OPTION_WITH_ICON_RIGHT = 0x2000;
|
2010-12-02 09:46:21 +00:00
|
|
|
|
|
|
|
/** Icon to display instead of a label. Icon takes precedence over a label */
|
2010-12-03 04:17:26 +00:00
|
|
|
private Drawable mIcon;
|
2010-12-02 09:46:21 +00:00
|
|
|
/** Preview version of the icon, for the preview popup */
|
2010-12-03 04:17:26 +00:00
|
|
|
private Drawable mPreviewIcon;
|
2010-12-02 09:46:21 +00:00
|
|
|
|
|
|
|
/** Width of the key, not including the gap */
|
2010-12-03 04:17:26 +00:00
|
|
|
public final int mWidth;
|
2010-12-02 09:46:21 +00:00
|
|
|
/** Height of the key, not including the gap */
|
2010-12-03 04:17:26 +00:00
|
|
|
public final int mHeight;
|
2011-04-11 02:30:15 +00:00
|
|
|
/** The horizontal gap around this key */
|
2011-08-01 23:50:08 +00:00
|
|
|
public final int mHorizontalGap;
|
|
|
|
/** The vertical gap below this key */
|
|
|
|
public final int mVerticalGap;
|
2011-04-11 02:30:15 +00:00
|
|
|
/** The visual insets */
|
|
|
|
public final int mVisualInsetsLeft;
|
|
|
|
public final int mVisualInsetsRight;
|
2010-12-02 09:46:21 +00:00
|
|
|
/** Whether this key is sticky, i.e., a toggle key */
|
2010-12-03 04:17:26 +00:00
|
|
|
public final boolean mSticky;
|
2010-12-02 09:46:21 +00:00
|
|
|
/** X coordinate of the key in the keyboard layout */
|
2010-12-03 04:17:26 +00:00
|
|
|
public final int mX;
|
2010-12-02 09:46:21 +00:00
|
|
|
/** Y coordinate of the key in the keyboard layout */
|
2010-12-03 04:17:26 +00:00
|
|
|
public final int mY;
|
2010-12-02 09:46:21 +00:00
|
|
|
/** Text to output when pressed. This can be multiple characters, like ".com" */
|
2010-12-03 04:17:26 +00:00
|
|
|
public final CharSequence mOutputText;
|
2011-08-31 06:26:32 +00:00
|
|
|
/** More keys */
|
|
|
|
public final CharSequence[] mMoreKeys;
|
|
|
|
/** More keys maximum column number */
|
|
|
|
public final int mMaxMoreKeysColumn;
|
2010-12-02 09:46:21 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Flags that specify the anchoring to edges of the keyboard for detecting touch events
|
|
|
|
* that are just out of the boundary of the key. This is a bit mask of
|
|
|
|
* {@link Keyboard#EDGE_LEFT}, {@link Keyboard#EDGE_RIGHT},
|
|
|
|
* {@link Keyboard#EDGE_TOP} and {@link Keyboard#EDGE_BOTTOM}.
|
|
|
|
*/
|
2011-07-19 00:14:50 +00:00
|
|
|
private int mEdgeFlags;
|
2011-04-07 07:12:00 +00:00
|
|
|
/** Whether this is a functional key which has different key top than normal key */
|
|
|
|
public final boolean mFunctional;
|
2010-12-02 09:46:21 +00:00
|
|
|
/** Whether this key repeats itself when held down */
|
2010-12-03 04:17:26 +00:00
|
|
|
public final boolean mRepeatable;
|
2010-12-02 09:46:21 +00:00
|
|
|
|
2010-12-03 04:17:26 +00:00
|
|
|
/** The current pressed state of this key */
|
2011-06-23 12:23:44 +00:00
|
|
|
private boolean mPressed;
|
2011-04-07 07:12:00 +00:00
|
|
|
/** If this is a sticky key, is its highlight on? */
|
2011-06-23 12:23:44 +00:00
|
|
|
private boolean mHighlightOn;
|
2011-02-18 02:28:17 +00:00
|
|
|
/** Key is enabled and responds on press */
|
2011-06-23 12:23:44 +00:00
|
|
|
private boolean mEnabled = true;
|
2011-08-09 10:50:21 +00:00
|
|
|
/** Whether this key needs to show the "..." popup hint for special purposes */
|
|
|
|
private boolean mNeedsSpecialPopupHint;
|
2010-12-02 09:46:21 +00:00
|
|
|
|
2011-09-08 08:19:23 +00:00
|
|
|
// keyWidth enum constants
|
|
|
|
private static final int KEYWIDTH_NOT_ENUM = 0;
|
|
|
|
private static final int KEYWIDTH_FILL_RIGHT = -1;
|
|
|
|
private static final int KEYWIDTH_FILL_BOTH = -2;
|
2011-05-30 11:05:50 +00:00
|
|
|
|
2010-12-02 09:46:21 +00:00
|
|
|
private final static int[] KEY_STATE_NORMAL_ON = {
|
|
|
|
android.R.attr.state_checkable,
|
|
|
|
android.R.attr.state_checked
|
|
|
|
};
|
|
|
|
|
|
|
|
private final static int[] KEY_STATE_PRESSED_ON = {
|
|
|
|
android.R.attr.state_pressed,
|
|
|
|
android.R.attr.state_checkable,
|
|
|
|
android.R.attr.state_checked
|
|
|
|
};
|
|
|
|
|
|
|
|
private final static int[] KEY_STATE_NORMAL_OFF = {
|
|
|
|
android.R.attr.state_checkable
|
|
|
|
};
|
|
|
|
|
|
|
|
private final static int[] KEY_STATE_PRESSED_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
|
|
|
|
};
|
|
|
|
|
2010-12-02 11:54:32 +00:00
|
|
|
// 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
|
|
|
|
};
|
|
|
|
|
2011-07-24 23:35:54 +00:00
|
|
|
// RTL parenthesis character swapping map.
|
|
|
|
private static final Map<Integer, Integer> sRtlParenthesisMap = new HashMap<Integer, Integer>();
|
|
|
|
|
|
|
|
static {
|
2011-07-25 18:39:27 +00:00
|
|
|
// The all letters need to be mirrored are found at
|
|
|
|
// http://www.unicode.org/Public/6.0.0/ucd/extracted/DerivedBinaryProperties.txt
|
2011-07-24 23:35:54 +00:00
|
|
|
addRtlParenthesisPair('(', ')');
|
|
|
|
addRtlParenthesisPair('[', ']');
|
|
|
|
addRtlParenthesisPair('{', '}');
|
|
|
|
addRtlParenthesisPair('<', '>');
|
|
|
|
// \u00ab: LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
|
|
|
|
// \u00bb: RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
|
|
|
|
addRtlParenthesisPair('\u00ab', '\u00bb');
|
|
|
|
// \u2039: SINGLE LEFT-POINTING ANGLE QUOTATION MARK
|
|
|
|
// \u203a: SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
|
|
|
|
addRtlParenthesisPair('\u2039', '\u203a');
|
|
|
|
// \u2264: LESS-THAN OR EQUAL TO
|
|
|
|
// \u2265: GREATER-THAN OR EQUAL TO
|
|
|
|
addRtlParenthesisPair('\u2264', '\u2265');
|
|
|
|
}
|
|
|
|
|
|
|
|
private static void addRtlParenthesisPair(int left, int right) {
|
|
|
|
sRtlParenthesisMap.put(left, right);
|
|
|
|
sRtlParenthesisMap.put(right, left);
|
|
|
|
}
|
|
|
|
|
2011-08-30 09:35:56 +00:00
|
|
|
public static int getRtlParenthesisCode(int code, boolean isRtl) {
|
|
|
|
if (isRtl && sRtlParenthesisMap.containsKey(code)) {
|
2011-07-24 23:35:54 +00:00
|
|
|
return sRtlParenthesisMap.get(code);
|
|
|
|
} else {
|
|
|
|
return code;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-31 06:26:32 +00:00
|
|
|
private static int getCode(Resources res, KeyboardParams params, String moreKeySpec) {
|
2011-08-30 12:41:52 +00:00
|
|
|
return getRtlParenthesisCode(
|
2011-08-31 06:26:32 +00:00
|
|
|
MoreKeySpecParser.getCode(res, moreKeySpec), params.mIsRtlKeyboard);
|
2011-08-30 12:41:52 +00:00
|
|
|
}
|
|
|
|
|
2011-08-31 06:26:32 +00:00
|
|
|
private static Drawable getIcon(KeyboardParams params, String moreKeySpec) {
|
|
|
|
return params.mIconsSet.getIcon(MoreKeySpecParser.getIconId(moreKeySpec));
|
2011-08-30 12:41:52 +00:00
|
|
|
}
|
|
|
|
|
2010-12-14 06:31:47 +00:00
|
|
|
/**
|
2011-08-31 06:26:32 +00:00
|
|
|
* This constructor is being used only for key in more keys keyboard.
|
2010-12-14 06:31:47 +00:00
|
|
|
*/
|
2011-08-31 06:26:32 +00:00
|
|
|
public Key(Resources res, KeyboardParams params, String moreKeySpec,
|
2011-08-30 09:35:56 +00:00
|
|
|
int x, int y, int width, int height, int edgeFlags) {
|
2011-08-31 06:26:32 +00:00
|
|
|
this(params, MoreKeySpecParser.getLabel(moreKeySpec), null, getIcon(params, moreKeySpec),
|
|
|
|
getCode(res, params, moreKeySpec), MoreKeySpecParser.getOutputText(moreKeySpec),
|
2011-08-30 12:41:52 +00:00
|
|
|
x, y, width, height, edgeFlags);
|
2011-08-30 09:35:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This constructor is being used only for key in popup suggestions pane.
|
|
|
|
*/
|
2011-08-30 12:41:52 +00:00
|
|
|
public Key(KeyboardParams params, CharSequence label, CharSequence hintLabel, Drawable icon,
|
|
|
|
int code, CharSequence outputText, int x, int y, int width, int height, int edgeFlags) {
|
2011-07-29 00:05:40 +00:00
|
|
|
mHeight = height - params.mVerticalGap;
|
|
|
|
mHorizontalGap = params.mHorizontalGap;
|
|
|
|
mVerticalGap = params.mVerticalGap;
|
2011-04-11 02:30:15 +00:00
|
|
|
mVisualInsetsLeft = mVisualInsetsRight = 0;
|
2011-08-01 23:50:08 +00:00
|
|
|
mWidth = width - mHorizontalGap;
|
2011-01-01 04:50:47 +00:00
|
|
|
mEdgeFlags = edgeFlags;
|
2011-08-30 09:35:56 +00:00
|
|
|
mHintLabel = hintLabel;
|
2010-12-03 04:17:26 +00:00
|
|
|
mLabelOption = 0;
|
2011-04-07 07:12:00 +00:00
|
|
|
mFunctional = false;
|
2010-12-03 04:17:26 +00:00
|
|
|
mSticky = false;
|
|
|
|
mRepeatable = false;
|
2011-08-31 06:26:32 +00:00
|
|
|
mMoreKeys = null;
|
|
|
|
mMaxMoreKeysColumn = 0;
|
2011-08-30 12:41:52 +00:00
|
|
|
mLabel = label;
|
|
|
|
mOutputText = outputText;
|
2011-08-30 09:35:56 +00:00
|
|
|
mCode = code;
|
2011-08-30 12:41:52 +00:00
|
|
|
mIcon = icon;
|
2010-12-03 04:17:26 +00:00
|
|
|
// Horizontal gap is divided equally to both sides of the key.
|
2011-08-01 23:50:08 +00:00
|
|
|
mX = x + mHorizontalGap / 2;
|
2010-12-03 04:17:26 +00:00
|
|
|
mY = y;
|
2010-12-02 09:46:21 +00:00
|
|
|
}
|
|
|
|
|
2010-12-14 06:31:47 +00:00
|
|
|
/**
|
|
|
|
* Create a key with the given top-left coordinate and extract its attributes from the XML
|
|
|
|
* parser.
|
2010-12-02 09:46:21 +00:00
|
|
|
* @param res resources associated with the caller's context
|
2011-07-29 00:05:40 +00:00
|
|
|
* @param params the keyboard building parameters.
|
2011-09-02 04:48:27 +00:00
|
|
|
* @param row the row that this key belongs to. row's x-coordinate will be the right edge of
|
|
|
|
* this key.
|
2010-12-02 09:46:21 +00:00
|
|
|
* @param parser the XML parser containing the attributes for this key
|
2011-05-30 11:05:50 +00:00
|
|
|
* @param keyStyles active key styles set
|
2010-12-02 09:46:21 +00:00
|
|
|
*/
|
2011-09-02 08:38:59 +00:00
|
|
|
public Key(Resources res, KeyboardParams params, KeyboardBuilder.Row row,
|
|
|
|
XmlResourceParser parser, KeyStyles keyStyles) {
|
2010-12-14 06:31:47 +00:00
|
|
|
final TypedArray keyboardAttr = res.obtainAttributes(Xml.asAttributeSet(parser),
|
2010-12-02 09:46:21 +00:00
|
|
|
R.styleable.Keyboard);
|
2011-09-02 08:05:24 +00:00
|
|
|
mHeight = (int)KeyboardBuilder.getDimensionOrFraction(keyboardAttr,
|
|
|
|
R.styleable.Keyboard_rowHeight, params.mHeight, row.mRowHeight)
|
|
|
|
- params.mVerticalGap;
|
2011-09-08 08:19:23 +00:00
|
|
|
final float horizontalGap = isSpacer() ? 0
|
|
|
|
: KeyboardBuilder.getDimensionOrFraction(keyboardAttr,
|
|
|
|
R.styleable.Keyboard_horizontalGap, params.mWidth, params.mHorizontalGap);
|
2011-09-02 08:05:24 +00:00
|
|
|
mVerticalGap = params.mVerticalGap;
|
2011-09-08 08:19:23 +00:00
|
|
|
final int widthType = KeyboardBuilder.getEnumValue(keyboardAttr,
|
|
|
|
R.styleable.Keyboard_keyWidth, KEYWIDTH_NOT_ENUM);
|
2011-09-02 08:05:24 +00:00
|
|
|
float keyWidth = KeyboardBuilder.getDimensionOrFraction(keyboardAttr,
|
|
|
|
R.styleable.Keyboard_keyWidth, params.mWidth, row.mDefaultKeyWidth);
|
|
|
|
keyboardAttr.recycle();
|
2010-12-02 09:46:21 +00:00
|
|
|
|
2010-12-14 06:31:47 +00:00
|
|
|
final TypedArray keyAttr = res.obtainAttributes(Xml.asAttributeSet(parser),
|
|
|
|
R.styleable.Keyboard_Key);
|
2010-12-08 08:07:25 +00:00
|
|
|
|
2011-09-02 08:05:24 +00:00
|
|
|
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 ParseException("Unknown key style: " + styleName, parser);
|
|
|
|
} else {
|
|
|
|
style = keyStyles.getEmptyKeyStyle();
|
|
|
|
}
|
|
|
|
|
|
|
|
final int keyboardWidth = params.mOccupiedWidth;
|
|
|
|
final float x = row.mCurrentX;
|
|
|
|
float keyXPos = KeyboardBuilder.getDimensionOrFraction(keyAttr,
|
|
|
|
R.styleable.Keyboard_Key_keyXPos, keyboardWidth, x);
|
|
|
|
if (keyXPos < 0) {
|
|
|
|
// If keyXPos is negative, the actual x-coordinate will be keyboardWidth + keyXPos.
|
|
|
|
keyXPos += keyboardWidth;
|
|
|
|
if (keyXPos < x) {
|
|
|
|
// keyXPos shouldn't be less than x because drawable area for this key starts
|
|
|
|
// at x. Or, this key will overlaps the adjacent key on its left hand side.
|
2011-05-30 11:05:50 +00:00
|
|
|
keyXPos = x;
|
|
|
|
}
|
2011-09-02 08:05:24 +00:00
|
|
|
}
|
2011-09-08 08:19:23 +00:00
|
|
|
if (widthType == KEYWIDTH_FILL_RIGHT) {
|
2011-09-02 08:05:24 +00:00
|
|
|
// If keyWidth is zero, the actual key width will be determined to fill out the
|
|
|
|
// area up to the right edge of the keyboard.
|
|
|
|
keyWidth = keyboardWidth - keyXPos;
|
2011-09-08 08:19:23 +00:00
|
|
|
} else if (widthType == KEYWIDTH_FILL_BOTH) {
|
2011-09-02 08:05:24 +00:00
|
|
|
// If keyWidth is negative, the actual key width will be determined to fill out the
|
|
|
|
// area between the nearest key on the left hand side and the right edge of the
|
|
|
|
// keyboard.
|
|
|
|
keyXPos = x;
|
|
|
|
keyWidth = keyboardWidth - keyXPos;
|
|
|
|
}
|
2011-05-30 11:05:50 +00:00
|
|
|
|
2011-09-02 08:05:24 +00:00
|
|
|
// Horizontal gap is divided equally to both sides of the key.
|
|
|
|
mX = (int) (keyXPos + horizontalGap / 2);
|
|
|
|
mY = row.mCurrentY;
|
|
|
|
mWidth = (int) (keyWidth - horizontalGap);
|
|
|
|
mHorizontalGap = (int) horizontalGap;
|
|
|
|
// Update row to have current x coordinate.
|
|
|
|
row.mCurrentX = keyXPos + keyWidth;
|
|
|
|
|
|
|
|
final CharSequence[] moreKeys = style.getTextArray(keyAttr,
|
|
|
|
R.styleable.Keyboard_Key_moreKeys);
|
|
|
|
// In Arabic symbol layouts, we'd like to keep digits in more keys regardless of
|
|
|
|
// config_digit_more_keys_enabled.
|
|
|
|
if (params.mId.isAlphabetKeyboard()
|
|
|
|
&& !res.getBoolean(R.bool.config_digit_more_keys_enabled)) {
|
|
|
|
mMoreKeys = MoreKeySpecParser.filterOut(res, moreKeys, MoreKeySpecParser.DIGIT_FILTER);
|
|
|
|
} else {
|
|
|
|
mMoreKeys = moreKeys;
|
2010-12-14 06:31:47 +00:00
|
|
|
}
|
2011-09-02 08:05:24 +00:00
|
|
|
mMaxMoreKeysColumn = style.getInt(keyboardAttr, R.styleable.Keyboard_Key_maxMoreKeysColumn,
|
|
|
|
params.mMaxMiniKeyboardColumn);
|
|
|
|
|
|
|
|
mRepeatable = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_isRepeatable, false);
|
|
|
|
mFunctional = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_isFunctional, false);
|
|
|
|
mSticky = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_isSticky, false);
|
|
|
|
mEnabled = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_enabled, true);
|
|
|
|
mEdgeFlags = 0;
|
|
|
|
|
|
|
|
final KeyboardIconsSet iconsSet = params.mIconsSet;
|
|
|
|
mVisualInsetsLeft = (int) KeyboardBuilder.getDimensionOrFraction(keyAttr,
|
|
|
|
R.styleable.Keyboard_Key_visualInsetsLeft, keyboardWidth, 0);
|
|
|
|
mVisualInsetsRight = (int) KeyboardBuilder.getDimensionOrFraction(keyAttr,
|
|
|
|
R.styleable.Keyboard_Key_visualInsetsRight, keyboardWidth, 0);
|
|
|
|
mPreviewIcon = iconsSet.getIcon(style.getInt(keyAttr,
|
|
|
|
R.styleable.Keyboard_Key_keyIconPreview, KeyboardIconsSet.ICON_UNDEFINED));
|
|
|
|
mIcon = iconsSet.getIcon(style.getInt(keyAttr, R.styleable.Keyboard_Key_keyIcon,
|
|
|
|
KeyboardIconsSet.ICON_UNDEFINED));
|
|
|
|
final int shiftedIconId = style.getInt(keyAttr, R.styleable.Keyboard_Key_keyIconShifted,
|
|
|
|
KeyboardIconsSet.ICON_UNDEFINED);
|
|
|
|
if (shiftedIconId != KeyboardIconsSet.ICON_UNDEFINED) {
|
|
|
|
final Drawable shiftedIcon = iconsSet.getIcon(shiftedIconId);
|
|
|
|
params.addShiftedIcon(this, shiftedIcon);
|
|
|
|
}
|
|
|
|
mHintLabel = style.getText(keyAttr, R.styleable.Keyboard_Key_keyHintLabel);
|
|
|
|
|
|
|
|
mLabel = style.getText(keyAttr, R.styleable.Keyboard_Key_keyLabel);
|
|
|
|
mLabelOption = style.getFlag(keyAttr, R.styleable.Keyboard_Key_keyLabelOption, 0);
|
|
|
|
mOutputText = style.getText(keyAttr, R.styleable.Keyboard_Key_keyOutputText);
|
|
|
|
// Choose the first letter of the label as primary code if not
|
|
|
|
// specified.
|
|
|
|
final int code = style.getInt(keyAttr, R.styleable.Keyboard_Key_code,
|
|
|
|
Keyboard.CODE_UNSPECIFIED);
|
|
|
|
if (code == Keyboard.CODE_UNSPECIFIED && !TextUtils.isEmpty(mLabel)) {
|
|
|
|
final int firstChar = mLabel.charAt(0);
|
|
|
|
mCode = getRtlParenthesisCode(firstChar, params.mIsRtlKeyboard);
|
|
|
|
} else if (code != Keyboard.CODE_UNSPECIFIED) {
|
|
|
|
mCode = code;
|
|
|
|
} else {
|
|
|
|
mCode = Keyboard.CODE_DUMMY;
|
|
|
|
}
|
|
|
|
|
|
|
|
keyAttr.recycle();
|
2010-12-03 04:17:26 +00:00
|
|
|
}
|
|
|
|
|
2011-07-19 00:14:50 +00:00
|
|
|
public void addEdgeFlags(int flags) {
|
|
|
|
mEdgeFlags |= flags;
|
|
|
|
}
|
|
|
|
|
2011-09-08 08:19:23 +00:00
|
|
|
public boolean isSpacer() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-06-25 10:38:55 +00:00
|
|
|
public Typeface selectTypeface(Typeface defaultTypeface) {
|
|
|
|
// TODO: Handle "bold" here too?
|
|
|
|
if ((mLabelOption & LABEL_OPTION_FONT_NORMAL) != 0) {
|
|
|
|
return Typeface.DEFAULT;
|
|
|
|
} else if ((mLabelOption & LABEL_OPTION_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
|
|
|
|
&& (mLabelOption & (LABEL_OPTION_FOLLOW_KEY_LETTER_RATIO
|
|
|
|
| LABEL_OPTION_FOLLOW_KEY_HINT_LABEL_RATIO)) == 0) {
|
|
|
|
return label;
|
|
|
|
} else if ((mLabelOption & LABEL_OPTION_FOLLOW_KEY_HINT_LABEL_RATIO) != 0) {
|
|
|
|
return hintLabel;
|
|
|
|
} else if ((mLabelOption & LABEL_OPTION_LARGE_LETTER) != 0) {
|
|
|
|
return largeLetter;
|
|
|
|
} else {
|
|
|
|
return letter;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-03 04:29:24 +00:00
|
|
|
public boolean isAlignLeft() {
|
|
|
|
return (mLabelOption & LABEL_OPTION_ALIGN_LEFT) != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean isAlignRight() {
|
|
|
|
return (mLabelOption & LABEL_OPTION_ALIGN_RIGHT) != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean isAlignLeftOfCenter() {
|
|
|
|
return (mLabelOption & LABEL_OPTION_ALIGN_LEFT_OF_CENTER) != 0;
|
|
|
|
}
|
|
|
|
|
2011-06-15 03:36:53 +00:00
|
|
|
public boolean hasPopupHint() {
|
2011-06-25 10:38:55 +00:00
|
|
|
return (mLabelOption & LABEL_OPTION_HAS_POPUP_HINT) != 0;
|
2011-06-15 03:36:53 +00:00
|
|
|
}
|
|
|
|
|
2011-08-09 10:50:21 +00:00
|
|
|
public void setNeedsSpecialPopupHint(boolean needsSpecialPopupHint) {
|
|
|
|
mNeedsSpecialPopupHint = needsSpecialPopupHint;
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean needsSpecialPopupHint() {
|
|
|
|
return mNeedsSpecialPopupHint;
|
|
|
|
}
|
|
|
|
|
2011-06-15 03:36:53 +00:00
|
|
|
public boolean hasUppercaseLetter() {
|
|
|
|
return (mLabelOption & LABEL_OPTION_HAS_UPPERCASE_LETTER) != 0;
|
|
|
|
}
|
|
|
|
|
2011-06-25 10:38:55 +00:00
|
|
|
public boolean hasHintLabel() {
|
|
|
|
return (mLabelOption & LABEL_OPTION_HAS_HINT_LABEL) != 0;
|
|
|
|
}
|
|
|
|
|
2011-08-03 04:29:24 +00:00
|
|
|
public boolean hasLabelWithIconLeft() {
|
|
|
|
return (mLabelOption & LABEL_OPTION_WITH_ICON_LEFT) != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean hasLabelWithIconRight() {
|
|
|
|
return (mLabelOption & LABEL_OPTION_WITH_ICON_RIGHT) != 0;
|
|
|
|
}
|
|
|
|
|
2010-12-03 04:17:26 +00:00
|
|
|
public Drawable getIcon() {
|
|
|
|
return mIcon;
|
|
|
|
}
|
|
|
|
|
|
|
|
public Drawable getPreviewIcon() {
|
|
|
|
return mPreviewIcon;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setIcon(Drawable icon) {
|
|
|
|
mIcon = icon;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setPreviewIcon(Drawable icon) {
|
|
|
|
mPreviewIcon = icon;
|
2010-12-02 09:46:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Informs the key that it has been pressed, in case it needs to change its appearance or
|
|
|
|
* state.
|
2011-04-07 07:12:00 +00:00
|
|
|
* @see #onReleased()
|
2010-12-02 09:46:21 +00:00
|
|
|
*/
|
|
|
|
public void onPressed() {
|
2011-04-07 07:12:00 +00:00
|
|
|
mPressed = true;
|
2010-12-02 09:46:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2011-04-07 07:12:00 +00:00
|
|
|
* Informs the key that it has been released, in case it needs to change its appearance or
|
|
|
|
* state.
|
2010-12-02 09:46:21 +00:00
|
|
|
* @see #onPressed()
|
|
|
|
*/
|
2011-04-07 07:12:00 +00:00
|
|
|
public void onReleased() {
|
|
|
|
mPressed = false;
|
2010-12-02 09:46:21 +00:00
|
|
|
}
|
|
|
|
|
2011-06-23 12:23:44 +00:00
|
|
|
public void setHighlightOn(boolean highlightOn) {
|
|
|
|
mHighlightOn = highlightOn;
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean isEnabled() {
|
|
|
|
return mEnabled;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void setEnabled(boolean enabled) {
|
|
|
|
mEnabled = enabled;
|
|
|
|
}
|
|
|
|
|
2010-12-02 09:46:21 +00:00
|
|
|
/**
|
2010-12-03 00:39:42 +00:00
|
|
|
* Detects if a point falls on this key.
|
2010-12-02 09:46:21 +00:00
|
|
|
* @param x the x-coordinate of the point
|
|
|
|
* @param y the y-coordinate of the point
|
2010-12-03 00:39:42 +00:00
|
|
|
* @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.
|
2010-12-02 09:46:21 +00:00
|
|
|
*/
|
2010-12-03 00:39:42 +00:00
|
|
|
public boolean isOnKey(int x, int y) {
|
2011-08-01 23:50:08 +00:00
|
|
|
final int left = mX - mHorizontalGap / 2;
|
|
|
|
final int right = left + mWidth + mHorizontalGap;
|
2011-07-19 00:14:50 +00:00
|
|
|
final int top = mY;
|
2011-08-01 23:50:08 +00:00
|
|
|
final int bottom = top + mHeight + mVerticalGap;
|
2010-12-03 00:39:42 +00:00
|
|
|
final int flags = mEdgeFlags;
|
2011-07-19 00:14:50 +00:00
|
|
|
if (flags == 0) {
|
|
|
|
return x >= left && x <= right && y >= top && y <= bottom;
|
|
|
|
}
|
2010-12-03 00:39:42 +00:00
|
|
|
final boolean leftEdge = (flags & Keyboard.EDGE_LEFT) != 0;
|
|
|
|
final boolean rightEdge = (flags & Keyboard.EDGE_RIGHT) != 0;
|
|
|
|
final boolean topEdge = (flags & Keyboard.EDGE_TOP) != 0;
|
|
|
|
final boolean bottomEdge = (flags & Keyboard.EDGE_BOTTOM) != 0;
|
2011-04-28 14:39:49 +00:00
|
|
|
// In order to mitigate rounding errors, we use (left <= x <= right) here.
|
|
|
|
return (x >= left || leftEdge) && (x <= right || rightEdge)
|
|
|
|
&& (y >= top || topEdge) && (y <= bottom || bottomEdge);
|
2010-12-02 09:46:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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) {
|
2011-04-28 14:39:49 +00:00
|
|
|
final int left = mX;
|
|
|
|
final int right = left + mWidth;
|
|
|
|
final int top = mY;
|
|
|
|
final int bottom = top + mHeight;
|
2010-12-02 09:46:21 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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() {
|
2011-04-08 08:14:12 +00:00
|
|
|
final boolean pressed = mPressed;
|
2011-04-07 07:12:00 +00:00
|
|
|
if (!mSticky && mFunctional) {
|
2011-01-25 03:13:35 +00:00
|
|
|
if (pressed) {
|
2010-12-02 11:54:32 +00:00
|
|
|
return KEY_STATE_FUNCTIONAL_PRESSED;
|
|
|
|
} else {
|
|
|
|
return KEY_STATE_FUNCTIONAL_NORMAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-12-02 09:46:21 +00:00
|
|
|
int[] states = KEY_STATE_NORMAL;
|
|
|
|
|
2011-04-07 07:12:00 +00:00
|
|
|
if (mHighlightOn) {
|
2011-01-25 03:13:35 +00:00
|
|
|
if (pressed) {
|
2010-12-02 09:46:21 +00:00
|
|
|
states = KEY_STATE_PRESSED_ON;
|
|
|
|
} else {
|
|
|
|
states = KEY_STATE_NORMAL_ON;
|
|
|
|
}
|
|
|
|
} else {
|
2010-12-02 11:54:32 +00:00
|
|
|
if (mSticky) {
|
2011-01-25 03:13:35 +00:00
|
|
|
if (pressed) {
|
2010-12-02 09:46:21 +00:00
|
|
|
states = KEY_STATE_PRESSED_OFF;
|
|
|
|
} else {
|
|
|
|
states = KEY_STATE_NORMAL_OFF;
|
|
|
|
}
|
|
|
|
} else {
|
2011-01-25 03:13:35 +00:00
|
|
|
if (pressed) {
|
2010-12-02 09:46:21 +00:00
|
|
|
states = KEY_STATE_PRESSED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return states;
|
|
|
|
}
|
2011-09-08 08:19:23 +00:00
|
|
|
|
|
|
|
public static class Spacer extends Key {
|
|
|
|
public Spacer(Resources res, KeyboardParams params, KeyboardBuilder.Row row,
|
|
|
|
XmlResourceParser parser, KeyStyles keyStyles) {
|
|
|
|
super(res, params, row, parser, keyStyles);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean isSpacer() {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2010-12-02 09:46:21 +00:00
|
|
|
}
|