2010-12-02 09:46:21 +00:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2010 Google Inc.
|
|
|
|
*
|
|
|
|
* 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 com.android.inputmethod.keyboard.KeyStyles.KeyStyle;
|
2010-12-14 06:31:47 +00:00
|
|
|
import com.android.inputmethod.keyboard.KeyboardParser.ParseException;
|
2010-12-02 09:46:21 +00:00
|
|
|
import com.android.inputmethod.latin.R;
|
|
|
|
|
|
|
|
import android.content.res.Resources;
|
|
|
|
import android.content.res.TypedArray;
|
|
|
|
import android.content.res.XmlResourceParser;
|
|
|
|
import android.graphics.drawable.Drawable;
|
|
|
|
import android.text.TextUtils;
|
|
|
|
import android.util.Xml;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Class for describing the position and characteristics of a single key in the keyboard.
|
|
|
|
*/
|
|
|
|
public class Key {
|
|
|
|
/**
|
|
|
|
* All the key codes (unicode or custom code) that this key could generate, zero'th
|
|
|
|
* being the most important.
|
|
|
|
*/
|
2010-12-03 04:17:26 +00:00
|
|
|
public final int[] mCodes;
|
2010-12-02 09:46:21 +00:00
|
|
|
/** The unicode that this key generates in manual temporary upper case mode. */
|
2010-12-03 04:17:26 +00:00
|
|
|
public final int mManualTemporaryUpperCaseCode;
|
2010-12-02 09:46:21 +00:00
|
|
|
|
|
|
|
/** Label to display */
|
2010-12-03 04:17:26 +00:00
|
|
|
public final CharSequence mLabel;
|
2010-12-02 09:46:21 +00:00
|
|
|
/** Option of the label */
|
2010-12-03 04:17:26 +00:00
|
|
|
public final int mLabelOption;
|
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;
|
|
|
|
/** Hint icon to display on the key in conjunction with the label */
|
|
|
|
public final Drawable mHintIcon;
|
2010-12-02 09:46:21 +00:00
|
|
|
/**
|
|
|
|
* The hint icon to display on the key when keyboard is in manual temporary upper case
|
|
|
|
* mode.
|
|
|
|
*/
|
2010-12-03 04:17:26 +00:00
|
|
|
public final Drawable mManualTemporaryUpperCaseHintIcon;
|
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;
|
2010-12-02 09:46:21 +00:00
|
|
|
/** The horizontal gap before this key */
|
2010-12-03 04:17:26 +00:00
|
|
|
public final int mGap;
|
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;
|
2010-12-02 09:46:21 +00:00
|
|
|
/** Popup characters */
|
2010-12-14 06:31:47 +00:00
|
|
|
public final CharSequence[] mPopupCharacters;
|
|
|
|
/** Popup keyboard maximum column number */
|
|
|
|
public final int mMaxPopupColumn;
|
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}.
|
|
|
|
*/
|
2010-12-03 04:17:26 +00:00
|
|
|
public final int mEdgeFlags;
|
2010-12-02 09:46:21 +00:00
|
|
|
/** Whether this is a modifier key, such as Shift or Alt */
|
2010-12-03 04:17:26 +00:00
|
|
|
public final boolean mModifier;
|
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 Keyboard that this key belongs to */
|
|
|
|
private final Keyboard mKeyboard;
|
|
|
|
|
|
|
|
/** The current pressed state of this key */
|
|
|
|
public boolean mPressed;
|
|
|
|
/** If this is a sticky key, is it on? */
|
|
|
|
public boolean mOn;
|
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
|
|
|
|
};
|
|
|
|
|
2010-12-14 06:31:47 +00:00
|
|
|
private static final int[] DUMMY_CODES = { 0 };
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create an empty key with no attributes.
|
|
|
|
* This constructor is being used only for key in mini popup keyboard.
|
|
|
|
*/
|
|
|
|
public Key(Resources res, Row row, CharSequence popupCharacter, int x, int y) {
|
2010-12-03 04:17:26 +00:00
|
|
|
mKeyboard = row.getKeyboard();
|
2010-12-08 05:36:41 +00:00
|
|
|
mHeight = row.mDefaultHeight - row.mVerticalGap;
|
2010-12-03 04:17:26 +00:00
|
|
|
mGap = row.mDefaultHorizontalGap;
|
|
|
|
mWidth = row.mDefaultWidth - mGap;
|
|
|
|
mEdgeFlags = row.mRowEdgeFlags;
|
|
|
|
mHintIcon = null;
|
|
|
|
mManualTemporaryUpperCaseHintIcon = null;
|
|
|
|
mManualTemporaryUpperCaseCode = 0;
|
|
|
|
mLabelOption = 0;
|
|
|
|
mModifier = false;
|
|
|
|
mSticky = false;
|
|
|
|
mRepeatable = false;
|
|
|
|
mPopupCharacters = null;
|
2010-12-14 06:31:47 +00:00
|
|
|
mMaxPopupColumn = 0;
|
|
|
|
final String popupSpecification = popupCharacter.toString();
|
|
|
|
mLabel = PopupCharactersParser.getLabel(popupSpecification);
|
|
|
|
mOutputText = PopupCharactersParser.getOutputText(popupSpecification);
|
|
|
|
mCodes = PopupCharactersParser.getCodes(res, popupSpecification);
|
|
|
|
mIcon = PopupCharactersParser.getIcon(res, popupSpecification);
|
2010-12-03 04:17:26 +00:00
|
|
|
// Horizontal gap is divided equally to both sides of the key.
|
|
|
|
mX = x + mGap / 2;
|
|
|
|
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
|
2010-12-03 04:17:26 +00:00
|
|
|
* @param row the row that this key belongs to. The row must already be attached to
|
2010-12-02 09:46:21 +00:00
|
|
|
* a {@link Keyboard}.
|
|
|
|
* @param x the x coordinate of the top-left
|
|
|
|
* @param y the y coordinate of the top-left
|
|
|
|
* @param parser the XML parser containing the attributes for this key
|
|
|
|
*/
|
2010-12-03 04:17:26 +00:00
|
|
|
public Key(Resources res, Row row, int x, int y, XmlResourceParser parser,
|
2010-12-02 09:46:21 +00:00
|
|
|
KeyStyles keyStyles) {
|
2010-12-03 04:17:26 +00:00
|
|
|
mKeyboard = row.getKeyboard();
|
2010-12-02 09:46:21 +00:00
|
|
|
|
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);
|
2010-12-14 06:31:47 +00:00
|
|
|
try {
|
|
|
|
mHeight = KeyboardParser.getDimensionOrFraction(keyboardAttr,
|
|
|
|
R.styleable.Keyboard_rowHeight,
|
|
|
|
mKeyboard.getKeyboardHeight(), row.mDefaultHeight) - row.mVerticalGap;
|
|
|
|
mGap = KeyboardParser.getDimensionOrFraction(keyboardAttr,
|
|
|
|
R.styleable.Keyboard_horizontalGap,
|
|
|
|
mKeyboard.getDisplayWidth(), row.mDefaultHorizontalGap);
|
|
|
|
mWidth = KeyboardParser.getDimensionOrFraction(keyboardAttr,
|
|
|
|
R.styleable.Keyboard_keyWidth,
|
|
|
|
mKeyboard.getDisplayWidth(), row.mDefaultWidth) - mGap;
|
|
|
|
} finally {
|
|
|
|
keyboardAttr.recycle();
|
2010-12-02 09:46:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Horizontal gap is divided equally to both sides of the key.
|
2010-12-02 11:54:32 +00:00
|
|
|
this.mX = x + mGap / 2;
|
|
|
|
this.mY = y;
|
|
|
|
|
2010-12-14 06:31:47 +00:00
|
|
|
final TypedArray keyAttr = res.obtainAttributes(Xml.asAttributeSet(parser),
|
|
|
|
R.styleable.Keyboard_Key);
|
|
|
|
try {
|
|
|
|
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();
|
|
|
|
}
|
2010-12-08 08:07:25 +00:00
|
|
|
|
2010-12-14 06:31:47 +00:00
|
|
|
mPopupCharacters = style.getTextArray(keyAttr,
|
|
|
|
R.styleable.Keyboard_Key_popupCharacters);
|
|
|
|
mMaxPopupColumn = style.getInt(keyboardAttr,
|
|
|
|
R.styleable.Keyboard_Key_maxPopupKeyboardColumn,
|
|
|
|
mKeyboard.getMaxPopupKeyboardColumn());
|
2010-12-07 02:05:14 +00:00
|
|
|
|
2010-12-14 06:31:47 +00:00
|
|
|
mRepeatable = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_isRepeatable, false);
|
|
|
|
mModifier = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_isModifier, false);
|
|
|
|
mSticky = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_isSticky, false);
|
|
|
|
mEdgeFlags = style.getFlag(keyAttr, R.styleable.Keyboard_Key_keyEdgeFlags, 0)
|
|
|
|
| row.mRowEdgeFlags;
|
2010-12-02 09:46:21 +00:00
|
|
|
|
2010-12-14 06:31:47 +00:00
|
|
|
mPreviewIcon = style.getDrawable(keyAttr, R.styleable.Keyboard_Key_iconPreview);
|
|
|
|
Keyboard.setDefaultBounds(mPreviewIcon);
|
|
|
|
mIcon = style.getDrawable(keyAttr, R.styleable.Keyboard_Key_keyIcon);
|
|
|
|
Keyboard.setDefaultBounds(mIcon);
|
|
|
|
mHintIcon = style.getDrawable(keyAttr, R.styleable.Keyboard_Key_keyHintIcon);
|
|
|
|
Keyboard.setDefaultBounds(mHintIcon);
|
|
|
|
mManualTemporaryUpperCaseHintIcon = style.getDrawable(keyAttr,
|
|
|
|
R.styleable.Keyboard_Key_manualTemporaryUpperCaseHintIcon);
|
|
|
|
Keyboard.setDefaultBounds(mManualTemporaryUpperCaseHintIcon);
|
|
|
|
|
|
|
|
mLabel = style.getText(keyAttr, R.styleable.Keyboard_Key_keyLabel);
|
|
|
|
mLabelOption = style.getFlag(keyAttr, R.styleable.Keyboard_Key_keyLabelOption, 0);
|
|
|
|
mManualTemporaryUpperCaseCode = style.getInt(keyAttr,
|
|
|
|
R.styleable.Keyboard_Key_manualTemporaryUpperCaseCode, 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[] codes = style.getIntArray(keyAttr, R.styleable.Keyboard_Key_codes);
|
|
|
|
if (codes == null && !TextUtils.isEmpty(mLabel)) {
|
|
|
|
mCodes = new int[] { mLabel.charAt(0) };
|
|
|
|
} else if (codes != null) {
|
|
|
|
mCodes = codes;
|
|
|
|
} else {
|
|
|
|
mCodes = DUMMY_CODES;
|
|
|
|
}
|
|
|
|
|
|
|
|
final Drawable shiftedIcon = style.getDrawable(keyAttr,
|
|
|
|
R.styleable.Keyboard_Key_shiftedIcon);
|
|
|
|
if (shiftedIcon != null)
|
|
|
|
mKeyboard.getShiftedIcons().put(this, shiftedIcon);
|
|
|
|
} finally {
|
|
|
|
keyAttr.recycle();
|
|
|
|
}
|
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.
|
|
|
|
* @see #onReleased(boolean)
|
|
|
|
*/
|
|
|
|
public void onPressed() {
|
2010-12-02 11:54:32 +00:00
|
|
|
mPressed = !mPressed;
|
2010-12-02 09:46:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Changes the pressed state of the key. If it is a sticky key, it will also change the
|
|
|
|
* toggled state of the key if the finger was release inside.
|
|
|
|
* @param inside whether the finger was released inside the key
|
|
|
|
* @see #onPressed()
|
|
|
|
*/
|
|
|
|
public void onReleased(boolean inside) {
|
2010-12-02 11:54:32 +00:00
|
|
|
mPressed = !mPressed;
|
|
|
|
if (mSticky && !mKeyboard.isShiftLockEnabled(this))
|
|
|
|
mOn = !mOn;
|
2010-12-02 09:46:21 +00:00
|
|
|
}
|
|
|
|
|
2010-12-03 00:39:42 +00:00
|
|
|
public boolean isInside(int x, int y) {
|
|
|
|
return mKeyboard.isInside(this, x, y);
|
|
|
|
}
|
|
|
|
|
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) {
|
|
|
|
final int flags = mEdgeFlags;
|
|
|
|
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;
|
|
|
|
final int left = this.mX;
|
|
|
|
final int right = left + this.mWidth;
|
|
|
|
final int top = this.mY;
|
|
|
|
final int bottom = top + this.mHeight;
|
|
|
|
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) {
|
2010-12-02 11:54:32 +00:00
|
|
|
final int left = this.mX;
|
|
|
|
final int right = left + this.mWidth;
|
|
|
|
final int top = this.mY;
|
|
|
|
final int bottom = top + this.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;
|
|
|
|
}
|
|
|
|
|
2010-12-02 11:54:32 +00:00
|
|
|
// sticky is used for shift key. If a key is not sticky and is modifier,
|
|
|
|
// the key will be treated as functional.
|
|
|
|
private boolean isFunctionalKey() {
|
|
|
|
return !mSticky && mModifier;
|
|
|
|
}
|
|
|
|
|
2010-12-02 09:46:21 +00:00
|
|
|
/**
|
|
|
|
* 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() {
|
2010-12-02 11:54:32 +00:00
|
|
|
if (isFunctionalKey()) {
|
|
|
|
if (mPressed) {
|
|
|
|
return KEY_STATE_FUNCTIONAL_PRESSED;
|
|
|
|
} else {
|
|
|
|
return KEY_STATE_FUNCTIONAL_NORMAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-12-02 09:46:21 +00:00
|
|
|
int[] states = KEY_STATE_NORMAL;
|
|
|
|
|
2010-12-02 11:54:32 +00:00
|
|
|
if (mOn) {
|
|
|
|
if (mPressed) {
|
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) {
|
|
|
|
if (mPressed) {
|
2010-12-02 09:46:21 +00:00
|
|
|
states = KEY_STATE_PRESSED_OFF;
|
|
|
|
} else {
|
|
|
|
states = KEY_STATE_NORMAL_OFF;
|
|
|
|
}
|
|
|
|
} else {
|
2010-12-02 11:54:32 +00:00
|
|
|
if (mPressed) {
|
2010-12-02 09:46:21 +00:00
|
|
|
states = KEY_STATE_PRESSED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return states;
|
|
|
|
}
|
|
|
|
}
|