diff options
Diffstat (limited to 'java/src/com/android/inputmethod/latin')
24 files changed, 102 insertions, 5434 deletions
diff --git a/java/src/com/android/inputmethod/latin/BaseKeyboard.java b/java/src/com/android/inputmethod/latin/BaseKeyboard.java deleted file mode 100644 index e2331f334..000000000 --- a/java/src/com/android/inputmethod/latin/BaseKeyboard.java +++ /dev/null @@ -1,717 +0,0 @@ -/* - * 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.latin; - -import com.android.inputmethod.latin.BaseKeyboardParser.ParseException; -import com.android.inputmethod.latin.KeyStyles.KeyStyle; -import com.android.inputmethod.latin.KeyboardSwitcher.KeyboardId; - -import org.xmlpull.v1.XmlPullParserException; - -import android.content.Context; -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.Log; -import android.util.Xml; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * Loads an XML description of a keyboard and stores the attributes of the keys. A keyboard - * consists of rows of keys. - * <p>The layout file for a keyboard contains XML that looks like the following snippet:</p> - * <pre> - * <Keyboard - * latin:keyWidth="%10p" - * latin:keyHeight="50px" - * latin:horizontalGap="2px" - * latin:verticalGap="2px" > - * <Row latin:keyWidth="32px" > - * <Key latin:keyLabel="A" /> - * ... - * </Row> - * ... - * </Keyboard> - * </pre> - */ -public class BaseKeyboard { - - static final String TAG = "BaseKeyboard"; - - public static final int EDGE_LEFT = 0x01; - public static final int EDGE_RIGHT = 0x02; - public static final int EDGE_TOP = 0x04; - public static final int EDGE_BOTTOM = 0x08; - - public static final int KEYCODE_SHIFT = -1; - public static final int KEYCODE_MODE_CHANGE = -2; - public static final int KEYCODE_CANCEL = -3; - public static final int KEYCODE_DONE = -4; - public static final int KEYCODE_DELETE = -5; - public static final int KEYCODE_ALT = -6; - - /** Horizontal gap default for all rows */ - private int mDefaultHorizontalGap; - - /** Default key width */ - private int mDefaultWidth; - - /** Default key height */ - private int mDefaultHeight; - - /** Default gap between rows */ - private int mDefaultVerticalGap; - - /** Is the keyboard in the shifted state */ - private boolean mShifted; - - /** List of shift keys in this keyboard */ - private final List<Key> mShiftKeys = new ArrayList<Key>(); - - /** List of shift keys and its shifted state icon */ - private final HashMap<Key, Drawable> mShiftedIcons = new HashMap<Key, Drawable>(); - - /** Total height of the keyboard, including the padding and keys */ - private int mTotalHeight; - - /** - * Total width of the keyboard, including left side gaps and keys, but not any gaps on the - * right side. - */ - private int mTotalWidth; - - /** List of keys in this keyboard */ - private final List<Key> mKeys = new ArrayList<Key>(); - - /** Width of the screen available to fit the keyboard */ - private final int mDisplayWidth; - - /** Height of the screen */ - private final int mDisplayHeight; - - protected final KeyboardId mId; - - // Variables for pre-computing nearest keys. - - public final int GRID_WIDTH; - public final int GRID_HEIGHT; - private final int GRID_SIZE; - private int mCellWidth; - private int mCellHeight; - private int[][] mGridNeighbors; - private int mProximityThreshold; - private static int[] EMPTY_INT_ARRAY = new int[0]; - /** Number of key widths from current touch point to search for nearest keys. */ - private static float SEARCH_DISTANCE = 1.2f; - - /** - * Container for keys in the keyboard. All keys in a row are at the same Y-coordinate. - * Some of the key size defaults can be overridden per row from what the {@link BaseKeyboard} - * defines. - */ - public static class Row { - /** Default width of a key in this row. */ - public int defaultWidth; - /** Default height of a key in this row. */ - public int defaultHeight; - /** Default horizontal gap between keys in this row. */ - public int defaultHorizontalGap; - /** Vertical gap following this row. */ - public int verticalGap; - /** - * Edge flags for this row of keys. Possible values that can be assigned are - * {@link BaseKeyboard#EDGE_TOP EDGE_TOP} and {@link BaseKeyboard#EDGE_BOTTOM EDGE_BOTTOM} - */ - public int rowEdgeFlags; - - private final BaseKeyboard parent; - - private Row(BaseKeyboard parent) { - this.parent = parent; - } - - public Row(Resources res, BaseKeyboard parent, XmlResourceParser parser) { - this.parent = parent; - TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser), - R.styleable.BaseKeyboard); - defaultWidth = BaseKeyboardParser.getDimensionOrFraction(a, - R.styleable.BaseKeyboard_keyWidth, - parent.mDisplayWidth, parent.mDefaultWidth); - defaultHeight = BaseKeyboardParser.getDimensionOrFraction(a, - R.styleable.BaseKeyboard_keyHeight, - parent.mDisplayHeight, parent.mDefaultHeight); - defaultHorizontalGap = BaseKeyboardParser.getDimensionOrFraction(a, - R.styleable.BaseKeyboard_horizontalGap, - parent.mDisplayWidth, parent.mDefaultHorizontalGap); - verticalGap = BaseKeyboardParser.getDimensionOrFraction(a, - R.styleable.BaseKeyboard_verticalGap, - parent.mDisplayHeight, parent.mDefaultVerticalGap); - a.recycle(); - a = res.obtainAttributes(Xml.asAttributeSet(parser), - R.styleable.BaseKeyboard_Row); - rowEdgeFlags = a.getInt(R.styleable.BaseKeyboard_Row_rowEdgeFlags, 0); - } - } - - /** - * Class for describing the position and characteristics of a single key in the keyboard. - */ - public static class Key { - /** - * All the key codes (unicode or custom code) that this key could generate, zero'th - * being the most important. - */ - public int[] codes; - /** The unicode that this key generates in manual temporary upper case mode. */ - public int manualTemporaryUpperCaseCode; - - /** Label to display */ - public CharSequence label; - /** Option of the label */ - public int labelOption; - - /** Icon to display instead of a label. Icon takes precedence over a label */ - public Drawable icon; - /** Hint icon to display on the key in conjunction with the label */ - public Drawable hintIcon; - /** Preview version of the icon, for the preview popup */ - /** - * The hint icon to display on the key when keyboard is in manual temporary upper case - * mode. - */ - public Drawable manualTemporaryUpperCaseHintIcon; - - public Drawable iconPreview; - /** Width of the key, not including the gap */ - public int width; - /** Height of the key, not including the gap */ - public int height; - /** The horizontal gap before this key */ - public int gap; - /** Whether this key is sticky, i.e., a toggle key */ - public boolean sticky; - /** X coordinate of the key in the keyboard layout */ - public int x; - /** Y coordinate of the key in the keyboard layout */ - public int y; - /** The current pressed state of this key */ - public boolean pressed; - /** If this is a sticky key, is it on? */ - public boolean on; - /** Text to output when pressed. This can be multiple characters, like ".com" */ - public CharSequence text; - /** Popup characters */ - public CharSequence popupCharacters; - - /** - * 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 BaseKeyboard#EDGE_LEFT}, {@link BaseKeyboard#EDGE_RIGHT}, - * {@link BaseKeyboard#EDGE_TOP} and {@link BaseKeyboard#EDGE_BOTTOM}. - */ - public int edgeFlags; - /** Whether this is a modifier key, such as Shift or Alt */ - public boolean modifier; - /** The BaseKeyboard that this key belongs to */ - protected final BaseKeyboard keyboard; - /** - * If this key pops up a mini keyboard, this is the resource id for the XML layout for that - * keyboard. - */ - public int popupResId; - /** Whether this key repeats itself when held down */ - public boolean repeatable; - - - 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 - }; - - /** Create an empty key with no attributes. */ - public Key(Row parent) { - keyboard = parent.parent; - height = parent.defaultHeight; - gap = parent.defaultHorizontalGap; - width = parent.defaultWidth - gap; - edgeFlags = parent.rowEdgeFlags; - } - - /** 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 parent the row that this key belongs to. The row must already be attached to - * a {@link BaseKeyboard}. - * @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 - */ - public Key(Resources res, Row parent, int x, int y, XmlResourceParser parser, - KeyStyles keyStyles) { - this(parent); - - TypedArray a = res.obtainAttributes(Xml.asAttributeSet(parser), - R.styleable.BaseKeyboard); - height = BaseKeyboardParser.getDimensionOrFraction(a, - R.styleable.BaseKeyboard_keyHeight, - keyboard.mDisplayHeight, parent.defaultHeight); - gap = BaseKeyboardParser.getDimensionOrFraction(a, - R.styleable.BaseKeyboard_horizontalGap, - keyboard.mDisplayWidth, parent.defaultHorizontalGap); - width = BaseKeyboardParser.getDimensionOrFraction(a, - R.styleable.BaseKeyboard_keyWidth, - keyboard.mDisplayWidth, parent.defaultWidth) - gap; - a.recycle(); - - a = res.obtainAttributes(Xml.asAttributeSet(parser), R.styleable.BaseKeyboard_Key); - - final KeyStyle style; - if (a.hasValue(R.styleable.BaseKeyboard_Key_keyStyle)) { - String styleName = a.getString(R.styleable.BaseKeyboard_Key_keyStyle); - style = keyStyles.getKeyStyle(styleName); - if (style == null) - throw new ParseException("Unknown key style: " + styleName, parser); - } else { - style = keyStyles.getEmptyKeyStyle(); - } - - // Horizontal gap is divided equally to both sides of the key. - this.x = x + gap / 2; - this.y = y; - - codes = style.getIntArray(a, R.styleable.BaseKeyboard_Key_codes); - iconPreview = style.getDrawable(a, R.styleable.BaseKeyboard_Key_iconPreview); - setDefaultBounds(iconPreview); - popupCharacters = style.getText(a, R.styleable.BaseKeyboard_Key_popupCharacters); - popupResId = style.getResourceId(a, R.styleable.BaseKeyboard_Key_popupKeyboard, 0); - repeatable = style.getBoolean(a, R.styleable.BaseKeyboard_Key_isRepeatable, false); - modifier = style.getBoolean(a, R.styleable.BaseKeyboard_Key_isModifier, false); - sticky = style.getBoolean(a, R.styleable.BaseKeyboard_Key_isSticky, false); - edgeFlags = style.getFlag(a, R.styleable.BaseKeyboard_Key_keyEdgeFlags, 0); - edgeFlags |= parent.rowEdgeFlags; - - icon = style.getDrawable(a, R.styleable.BaseKeyboard_Key_keyIcon); - setDefaultBounds(icon); - hintIcon = style.getDrawable(a, R.styleable.BaseKeyboard_Key_keyHintIcon); - setDefaultBounds(hintIcon); - manualTemporaryUpperCaseHintIcon = style.getDrawable(a, - R.styleable.BaseKeyboard_Key_manualTemporaryUpperCaseHintIcon); - setDefaultBounds(manualTemporaryUpperCaseHintIcon); - - label = style.getText(a, R.styleable.BaseKeyboard_Key_keyLabel); - labelOption = style.getFlag(a, R.styleable.BaseKeyboard_Key_keyLabelOption, 0); - manualTemporaryUpperCaseCode = style.getInt(a, - R.styleable.BaseKeyboard_Key_manualTemporaryUpperCaseCode, 0); - text = style.getText(a, R.styleable.BaseKeyboard_Key_keyOutputText); - final Drawable shiftedIcon = style.getDrawable(a, - R.styleable.BaseKeyboard_Key_shiftedIcon); - if (shiftedIcon != null) - keyboard.getShiftedIcons().put(this, shiftedIcon); - - if (codes == null && !TextUtils.isEmpty(label)) { - codes = new int[] { label.charAt(0) }; - } - a.recycle(); - } - - /** - * Informs the key that it has been pressed, in case it needs to change its appearance or - * state. - * @see #onReleased(boolean) - */ - public void onPressed() { - pressed = !pressed; - } - - /** - * 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) { - pressed = !pressed; - if (sticky) { - on = !on; - } - } - - /** - * Detects if a point falls inside 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 inside 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 - * inside the key. - */ - public boolean isInside(int x, int y) { - boolean leftEdge = (edgeFlags & EDGE_LEFT) > 0; - boolean rightEdge = (edgeFlags & EDGE_RIGHT) > 0; - boolean topEdge = (edgeFlags & EDGE_TOP) > 0; - boolean bottomEdge = (edgeFlags & EDGE_BOTTOM) > 0; - if ((x >= this.x || (leftEdge && x <= this.x + this.width)) - && (x < this.x + this.width || (rightEdge && x >= this.x)) - && (y >= this.y || (topEdge && y <= this.y + this.height)) - && (y < this.y + this.height || (bottomEdge && y >= this.y))) { - return true; - } else { - return false; - } - } - - /** - * 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 = this.x; - final int right = left + this.width; - final int top = this.y; - final int bottom = top + this.height; - 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() { - int[] states = KEY_STATE_NORMAL; - - if (on) { - if (pressed) { - states = KEY_STATE_PRESSED_ON; - } else { - states = KEY_STATE_NORMAL_ON; - } - } else { - if (sticky) { - if (pressed) { - states = KEY_STATE_PRESSED_OFF; - } else { - states = KEY_STATE_NORMAL_OFF; - } - } else { - if (pressed) { - states = KEY_STATE_PRESSED; - } - } - } - return states; - } - } - - /** - * Creates a keyboard from the given xml key layout file. - * @param context the application or service context - * @param xmlLayoutResId the resource file that contains the keyboard layout and keys. - */ - public BaseKeyboard(Context context, int xmlLayoutResId) { - this(context, xmlLayoutResId, null); - } - - /** - * Creates a keyboard from the given keyboard identifier. - * @param context the application or service context - * @param id keyboard identifier - */ - public BaseKeyboard(Context context, KeyboardId id) { - this(context, id.getXmlId(), id); - } - - /** - * Creates a keyboard from the given xml key layout file. - * @param context the application or service context - * @param xmlLayoutResId the resource file that contains the keyboard layout and keys. - * @param id keyboard identifier - */ - private BaseKeyboard(Context context, int xmlLayoutResId, KeyboardId id) { - this(context, xmlLayoutResId, id, - context.getResources().getDisplayMetrics().widthPixels, - context.getResources().getDisplayMetrics().heightPixels); - } - - private BaseKeyboard(Context context, int xmlLayoutResId, KeyboardId id, int width, - int height) { - Resources res = context.getResources(); - GRID_WIDTH = res.getInteger(R.integer.config_keyboard_grid_width); - GRID_HEIGHT = res.getInteger(R.integer.config_keyboard_grid_height); - GRID_SIZE = GRID_WIDTH * GRID_HEIGHT; - - mDisplayWidth = width; - mDisplayHeight = height; - - mDefaultHorizontalGap = 0; - setKeyWidth(mDisplayWidth / 10); - mDefaultVerticalGap = 0; - mDefaultHeight = mDefaultWidth; - mId = id; - loadKeyboard(context, xmlLayoutResId); - } - - /** - * <p>Creates a blank keyboard from the given resource file and populates it with the specified - * characters in left-to-right, top-to-bottom fashion, using the specified number of columns. - * </p> - * <p>If the specified number of columns is -1, then the keyboard will fit as many keys as - * possible in each row.</p> - * @param context the application or service context - * @param layoutTemplateResId the layout template file, containing no keys. - * @param characters the list of characters to display on the keyboard. One key will be created - * for each character. - * @param columns the number of columns of keys to display. If this number is greater than the - * number of keys that can fit in a row, it will be ignored. If this number is -1, the - * keyboard will fit as many keys as possible in each row. - */ - public BaseKeyboard(Context context, int layoutTemplateResId, - CharSequence characters, int columns, int horizontalPadding) { - this(context, layoutTemplateResId); - int x = 0; - int y = 0; - int column = 0; - mTotalWidth = 0; - - Row row = new Row(this); - row.defaultHeight = mDefaultHeight; - row.defaultWidth = mDefaultWidth; - row.defaultHorizontalGap = mDefaultHorizontalGap; - row.verticalGap = mDefaultVerticalGap; - row.rowEdgeFlags = EDGE_TOP | EDGE_BOTTOM; - final int maxColumns = columns == -1 ? Integer.MAX_VALUE : columns; - for (int i = 0; i < characters.length(); i++) { - char c = characters.charAt(i); - if (column >= maxColumns - || x + mDefaultWidth + horizontalPadding > mDisplayWidth) { - x = 0; - y += mDefaultVerticalGap + mDefaultHeight; - column = 0; - } - final Key key = new Key(row); - // Horizontal gap is divided equally to both sides of the key. - key.x = x + key.gap / 2; - key.y = y; - key.label = String.valueOf(c); - key.codes = new int[] { c }; - column++; - x += key.width + key.gap; - mKeys.add(key); - if (x > mTotalWidth) { - mTotalWidth = x; - } - } - mTotalHeight = y + mDefaultHeight; - } - - public KeyboardId getKeyboardId() { - return mId; - } - - public List<Key> getKeys() { - return mKeys; - } - - protected int getHorizontalGap() { - return mDefaultHorizontalGap; - } - - protected void setHorizontalGap(int gap) { - mDefaultHorizontalGap = gap; - } - - protected int getVerticalGap() { - return mDefaultVerticalGap; - } - - protected void setVerticalGap(int gap) { - mDefaultVerticalGap = gap; - } - - protected int getKeyHeight() { - return mDefaultHeight; - } - - protected void setKeyHeight(int height) { - mDefaultHeight = height; - } - - protected int getKeyWidth() { - return mDefaultWidth; - } - - protected void setKeyWidth(int width) { - mDefaultWidth = width; - final int threshold = (int) (width * SEARCH_DISTANCE); - mProximityThreshold = threshold * threshold; - } - - /** - * Returns the total height of the keyboard - * @return the total height of the keyboard - */ - public int getHeight() { - return mTotalHeight; - } - - public int getMinWidth() { - return mTotalWidth; - } - - public int getKeyboardHeight() { - return mDisplayHeight; - } - - public int getKeyboardWidth() { - return mDisplayWidth; - } - - public boolean setShifted(boolean shiftState) { - for (final Key key : mShiftKeys) { - key.on = shiftState; - } - if (mShifted != shiftState) { - mShifted = shiftState; - return true; - } - return false; - } - - public boolean isShiftedOrShiftLocked() { - return mShifted; - } - - public List<Key> getShiftKeys() { - return mShiftKeys; - } - - public Map<Key, Drawable> getShiftedIcons() { - return mShiftedIcons; - } - - private void computeNearestNeighbors() { - // Round-up so we don't have any pixels outside the grid - mCellWidth = (getMinWidth() + GRID_WIDTH - 1) / GRID_WIDTH; - mCellHeight = (getHeight() + GRID_HEIGHT - 1) / GRID_HEIGHT; - mGridNeighbors = new int[GRID_SIZE][]; - final int[] indices = new int[mKeys.size()]; - final int gridWidth = GRID_WIDTH * mCellWidth; - final int gridHeight = GRID_HEIGHT * mCellHeight; - final int threshold = mProximityThreshold; - for (int x = 0; x < gridWidth; x += mCellWidth) { - for (int y = 0; y < gridHeight; y += mCellHeight) { - final int centerX = x + mCellWidth / 2; - final int centerY = y + mCellHeight / 2; - int count = 0; - for (int i = 0; i < mKeys.size(); i++) { - final Key key = mKeys.get(i); - if (key.squaredDistanceToEdge(centerX, centerY) < threshold) - indices[count++] = i; - } - final int[] cell = new int[count]; - System.arraycopy(indices, 0, cell, 0, count); - mGridNeighbors[(y / mCellHeight) * GRID_WIDTH + (x / mCellWidth)] = cell; - } - } - } - - /** - * Returns the indices of the keys that are closest to the given point. - * @param x the x-coordinate of the point - * @param y the y-coordinate of the point - * @return the array of integer indices for the nearest keys to the given point. If the given - * point is out of range, then an array of size zero is returned. - */ - public int[] getNearestKeys(int x, int y) { - if (mGridNeighbors == null) computeNearestNeighbors(); - if (x >= 0 && x < getMinWidth() && y >= 0 && y < getHeight()) { - int index = (y / mCellHeight) * GRID_WIDTH + (x / mCellWidth); - if (index < GRID_SIZE) { - return mGridNeighbors[index]; - } - } - return EMPTY_INT_ARRAY; - } - - // TODO should be private - protected BaseKeyboard.Row createRowFromXml(Resources res, XmlResourceParser parser) { - return new BaseKeyboard.Row(res, this, parser); - } - - // TODO should be private - protected BaseKeyboard.Key createKeyFromXml(Resources res, Row parent, int x, int y, - XmlResourceParser parser, KeyStyles keyStyles) { - return new BaseKeyboard.Key(res, parent, x, y, parser, keyStyles); - } - - private void loadKeyboard(Context context, int xmlLayoutResId) { - try { - final Resources res = context.getResources(); - BaseKeyboardParser parser = new BaseKeyboardParser(this, res); - parser.parseKeyboard(res.getXml(xmlLayoutResId)); - // mTotalWidth is the width of this keyboard which is maximum width of row. - mTotalWidth = parser.getMaxRowWidth(); - mTotalHeight = parser.getTotalHeight(); - } catch (XmlPullParserException e) { - Log.w(TAG, "keyboard XML parse error: " + e); - throw new IllegalArgumentException(e); - } catch (IOException e) { - Log.w(TAG, "keyboard XML parse error: " + e); - throw new RuntimeException(e); - } - } - - protected static void setDefaultBounds(Drawable drawable) { - if (drawable != null) - drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), - drawable.getIntrinsicHeight()); - } -} diff --git a/java/src/com/android/inputmethod/latin/BaseKeyboardParser.java b/java/src/com/android/inputmethod/latin/BaseKeyboardParser.java deleted file mode 100644 index 38b2a1b57..000000000 --- a/java/src/com/android/inputmethod/latin/BaseKeyboardParser.java +++ /dev/null @@ -1,571 +0,0 @@ -/* - * 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.latin; - -import com.android.inputmethod.latin.BaseKeyboard.Key; -import com.android.inputmethod.latin.BaseKeyboard.Row; -import com.android.inputmethod.latin.KeyboardSwitcher.KeyboardId; - -import org.xmlpull.v1.XmlPullParserException; - -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.content.res.XmlResourceParser; -import android.util.Log; -import android.util.TypedValue; -import android.util.Xml; -import android.view.InflateException; - -import java.io.IOException; -import java.util.List; - -/** - * Parser for BaseKeyboard. - * - * This class parses Keyboard XML file and fill out keys in BaseKeyboard. - * The Keyboard XML file looks like: - * <pre> - * >!-- xml/keyboard.xml --< - * >Keyboard keyboard_attributes*< - * >!-- Keyboard Content --< - * >Row row_attributes*< - * >!-- Row Content --< - * >Key key_attributes* /< - * >Spacer horizontalGap="0.2in" /< - * >include keyboardLayout="@xml/other_keys"< - * ... - * >/Row< - * >include keyboardLayout="@xml/other_rows"< - * ... - * >/Keyboard< - * </pre> - * The XML file which is included in other file must have >merge< as root element, such as: - * <pre> - * >!-- xml/other_keys.xml --< - * >merge< - * >Key key_attributes* /< - * ... - * >/merge< - * </pre> - * and - * <pre> - * >!-- xml/other_rows.xml --< - * >merge< - * >Row row_attributes*< - * >Key key_attributes* /< - * >/Row< - * ... - * >/merge< - * </pre> - * You can also use switch-case-default tags to select Rows and Keys. - * <pre> - * >switch< - * >case case_attribute*< - * >!-- Any valid tags at switch position --< - * >/case< - * ... - * >default< - * >!-- Any valid tags at switch position --< - * >/default< - * >/switch< - * </pre> - * You can declare Key style and specify styles within Key tags. - * <pre> - * >switch< - * >case colorScheme="white"< - * >key-style styleName="shift-key" parentStyle="modifier-key" - * keyIcon="@drawable/sym_keyboard_shift" - * /< - * >/case< - * >case colorScheme="black"< - * >key-style styleName="shift-key" parentStyle="modifier-key" - * keyIcon="@drawable/sym_bkeyboard_shift" - * /< - * >/case< - * >/switch< - * ... - * >Key keyStyle="shift-key" ... /< - * </pre> - */ - -public class BaseKeyboardParser { - private static final String TAG = "BaseKeyboardParser"; - private static final boolean DEBUG_TAG = false; - - // Keyboard XML Tags - private static final String TAG_KEYBOARD = "Keyboard"; - private static final String TAG_ROW = "Row"; - private static final String TAG_KEY = "Key"; - private static final String TAG_SPACER = "Spacer"; - private static final String TAG_INCLUDE = "include"; - private static final String TAG_MERGE = "merge"; - private static final String TAG_SWITCH = "switch"; - private static final String TAG_CASE = "case"; - private static final String TAG_DEFAULT = "default"; - private static final String TAG_KEY_STYLE = "key-style"; - - private final BaseKeyboard mKeyboard; - private final Resources mResources; - - private int mCurrentX = 0; - private int mCurrentY = 0; - private int mMaxRowWidth = 0; - private int mTotalHeight = 0; - private Row mCurrentRow = null; - private final KeyStyles mKeyStyles = new KeyStyles(); - - public BaseKeyboardParser(BaseKeyboard keyboard, Resources res) { - mKeyboard = keyboard; - mResources = res; - } - - public int getMaxRowWidth() { - return mMaxRowWidth; - } - - public int getTotalHeight() { - return mTotalHeight; - } - - public void parseKeyboard(XmlResourceParser parser) - throws XmlPullParserException, IOException { - int event; - while ((event = parser.next()) != XmlResourceParser.END_DOCUMENT) { - if (event == XmlResourceParser.START_TAG) { - final String tag = parser.getName(); - if (DEBUG_TAG) debugStartTag("parseKeyboard", tag, false); - if (TAG_KEYBOARD.equals(tag)) { - parseKeyboardAttributes(parser); - parseKeyboardContent(parser, mKeyboard.getKeys()); - break; - } else { - throw new IllegalStartTag(parser, TAG_KEYBOARD); - } - } - } - } - - private void parseKeyboardAttributes(XmlResourceParser parser) { - final BaseKeyboard keyboard = mKeyboard; - final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser), - R.styleable.BaseKeyboard); - final int width = keyboard.getKeyboardWidth(); - final int height = keyboard.getKeyboardHeight(); - keyboard.setKeyWidth(getDimensionOrFraction(a, - R.styleable.BaseKeyboard_keyWidth, width, width / 10)); - keyboard.setKeyHeight(getDimensionOrFraction(a, - R.styleable.BaseKeyboard_keyHeight, height, 50)); - keyboard.setHorizontalGap(getDimensionOrFraction(a, - R.styleable.BaseKeyboard_horizontalGap, width, 0)); - keyboard.setVerticalGap(getDimensionOrFraction(a, - R.styleable.BaseKeyboard_verticalGap, height, 0)); - a.recycle(); - if (DEBUG_TAG) Log.d(TAG, "id=" + keyboard.mId); - } - - private void parseKeyboardContent(XmlResourceParser parser, List<Key> keys) - throws XmlPullParserException, IOException { - int event; - while ((event = parser.next()) != XmlResourceParser.END_DOCUMENT) { - if (event == XmlResourceParser.START_TAG) { - final String tag = parser.getName(); - if (DEBUG_TAG) debugStartTag("parseKeyboardContent", tag, keys == null); - if (TAG_ROW.equals(tag)) { - Row row = mKeyboard.createRowFromXml(mResources, parser); - if (keys != null) - startRow(row); - parseRowContent(parser, row, keys); - } else if (TAG_INCLUDE.equals(tag)) { - parseIncludeKeyboardContent(parser, keys); - } else if (TAG_SWITCH.equals(tag)) { - parseSwitchKeyboardContent(parser, keys); - } else if (TAG_KEY_STYLE.equals(tag)) { - parseKeyStyle(parser, keys); - } else { - throw new IllegalStartTag(parser, TAG_ROW); - } - } else if (event == XmlResourceParser.END_TAG) { - final String tag = parser.getName(); - if (DEBUG_TAG) debugEndTag("parseKeyboardContent", tag, keys == null); - if (TAG_KEYBOARD.equals(tag)) { - endKeyboard(mKeyboard.getVerticalGap()); - break; - } else if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag)) { - break; - } else if (TAG_MERGE.equals(tag)) { - break; - } else if (TAG_KEY_STYLE.equals(tag)) { - continue; - } else { - throw new IllegalEndTag(parser, TAG_ROW); - } - } - } - } - - private void parseRowContent(XmlResourceParser parser, Row row, List<Key> keys) - throws XmlPullParserException, IOException { - int event; - while ((event = parser.next()) != XmlResourceParser.END_DOCUMENT) { - if (event == XmlResourceParser.START_TAG) { - final String tag = parser.getName(); - if (DEBUG_TAG) debugStartTag("parseRowContent", tag, keys == null); - if (TAG_KEY.equals(tag)) { - parseKey(parser, row, keys); - } else if (TAG_SPACER.equals(tag)) { - parseSpacer(parser, keys); - } else if (TAG_INCLUDE.equals(tag)) { - parseIncludeRowContent(parser, row, keys); - } else if (TAG_SWITCH.equals(tag)) { - parseSwitchRowContent(parser, row, keys); - } else if (TAG_KEY_STYLE.equals(tag)) { - parseKeyStyle(parser, keys); - } else { - throw new IllegalStartTag(parser, TAG_KEY); - } - } else if (event == XmlResourceParser.END_TAG) { - final String tag = parser.getName(); - if (DEBUG_TAG) debugEndTag("parseRowContent", tag, keys == null); - if (TAG_ROW.equals(tag)) { - if (keys != null) - endRow(); - break; - } else if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag)) { - break; - } else if (TAG_MERGE.equals(tag)) { - break; - } else if (TAG_KEY_STYLE.equals(tag)) { - continue; - } else { - throw new IllegalEndTag(parser, TAG_KEY); - } - } - } - } - - private void parseKey(XmlResourceParser parser, Row row, List<Key> keys) - throws XmlPullParserException, IOException { - if (keys == null) { - checkEndTag(TAG_KEY, parser); - } else { - Key key = mKeyboard.createKeyFromXml(mResources, row, mCurrentX, mCurrentY, parser, - mKeyStyles); - checkEndTag(TAG_KEY, parser); - keys.add(key); - if (key.codes[0] == BaseKeyboard.KEYCODE_SHIFT) - mKeyboard.getShiftKeys().add(key); - endKey(key); - } - } - - private void parseSpacer(XmlResourceParser parser, List<Key> keys) - throws XmlPullParserException, IOException { - if (keys == null) { - checkEndTag(TAG_SPACER, parser); - } else { - final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser), - R.styleable.BaseKeyboard); - final int gap = getDimensionOrFraction(a, R.styleable.BaseKeyboard_horizontalGap, - mKeyboard.getKeyboardWidth(), 0); - a.recycle(); - checkEndTag(TAG_SPACER, parser); - setSpacer(gap); - } - } - - private void parseIncludeKeyboardContent(XmlResourceParser parser, List<Key> keys) - throws XmlPullParserException, IOException { - parseIncludeInternal(parser, null, keys); - } - - private void parseIncludeRowContent(XmlResourceParser parser, Row row, List<Key> keys) - throws XmlPullParserException, IOException { - parseIncludeInternal(parser, row, keys); - } - - private void parseIncludeInternal(XmlResourceParser parser, Row row, List<Key> keys) - throws XmlPullParserException, IOException { - if (keys == null) { - checkEndTag(TAG_INCLUDE, parser); - } else { - final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser), - R.styleable.BaseKeyboard_Include); - final int keyboardLayout = a.getResourceId( - R.styleable.BaseKeyboard_Include_keyboardLayout, 0); - a.recycle(); - - checkEndTag(TAG_INCLUDE, parser); - if (keyboardLayout == 0) - throw new ParseException("No keyboardLayout attribute in <include/>", parser); - if (DEBUG_TAG) Log.d(TAG, String.format(" keyboardLayout=0x%08x", keyboardLayout)); - parseMerge(mResources.getLayout(keyboardLayout), row, keys); - } - } - - private void parseMerge(XmlResourceParser parser, Row row, List<Key> keys) - throws XmlPullParserException, IOException { - int event; - while ((event = parser.next()) != XmlResourceParser.END_DOCUMENT) { - if (event == XmlResourceParser.START_TAG) { - final String tag = parser.getName(); - if (DEBUG_TAG) debugStartTag("parseMerge", tag, keys == null); - if (TAG_MERGE.equals(tag)) { - if (row == null) { - parseKeyboardContent(parser, keys); - } else { - parseRowContent(parser, row, keys); - } - break; - } else { - throw new ParseException( - "Included keyboard layout must have <merge> root element", parser); - } - } - } - } - - private void parseSwitchKeyboardContent(XmlResourceParser parser, List<Key> keys) - throws XmlPullParserException, IOException { - parseSwitchInternal(parser, null, keys); - } - - private void parseSwitchRowContent(XmlResourceParser parser, Row row, List<Key> keys) - throws XmlPullParserException, IOException { - parseSwitchInternal(parser, row, keys); - } - - private void parseSwitchInternal(XmlResourceParser parser, Row row, List<Key> keys) - throws XmlPullParserException, IOException { - boolean selected = false; - int event; - if (DEBUG_TAG) Log.d(TAG, "parseSwitchInternal: id=" + mKeyboard.mId); - while ((event = parser.next()) != XmlResourceParser.END_DOCUMENT) { - if (event == XmlResourceParser.START_TAG) { - final String tag = parser.getName(); - if (DEBUG_TAG) debugStartTag("parseSwitchInternal", tag, keys == null); - if (TAG_CASE.equals(tag)) { - selected |= parseCase(parser, row, selected ? null : keys); - } else if (TAG_DEFAULT.equals(tag)) { - selected |= parseDefault(parser, row, selected ? null : keys); - } else { - throw new IllegalStartTag(parser, TAG_KEY); - } - } else if (event == XmlResourceParser.END_TAG) { - final String tag = parser.getName(); - if (DEBUG_TAG) debugEndTag("parseRowContent", tag, keys == null); - if (TAG_SWITCH.equals(tag)) { - break; - } else { - throw new IllegalEndTag(parser, TAG_KEY); - } - } - } - } - - private boolean parseCase(XmlResourceParser parser, Row row, List<Key> keys) - throws XmlPullParserException, IOException { - final boolean selected = parseCaseCondition(parser); - if (row == null) { - // Processing Rows. - parseKeyboardContent(parser, selected ? keys : null); - } else { - // Processing Keys. - parseRowContent(parser, row, selected ? keys : null); - } - return selected; - } - - private boolean parseCaseCondition(XmlResourceParser parser) { - final KeyboardId id = mKeyboard.mId; - if (id == null) - return true; - - final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser), - R.styleable.BaseKeyboard_Case); - final TypedArray viewAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser), - R.styleable.BaseKeyboardView); - try { - final boolean modeMatched = matchInteger(a, - R.styleable.BaseKeyboard_Case_mode, id.mMode); - final boolean settingsKeyMatched = matchBoolean(a, - R.styleable.BaseKeyboard_Case_hasSettingsKey, id.mHasSettingsKey); - final boolean voiceEnabledMatched = matchBoolean(a, - R.styleable.BaseKeyboard_Case_voiceKeyEnabled, id.mVoiceKeyEnabled); - final boolean voiceKeyMatched = matchBoolean(a, - R.styleable.BaseKeyboard_Case_hasVoiceKey, id.mHasVoiceKey); - final boolean colorSchemeMatched = matchInteger(viewAttr, - R.styleable.BaseKeyboardView_colorScheme, id.mColorScheme); - // As noted at KeyboardSwitcher.KeyboardId class, we are interested only in - // enum value masked by IME_MASK_ACTION and IME_FLAG_NO_ENTER_ACTION. So matching - // this attribute with id.mImeOptions as integer value is enough for our purpose. - final boolean imeOptionsMatched = matchInteger(a, - R.styleable.BaseKeyboard_Case_imeOptions, id.mImeOptions); - final boolean selected = modeMatched && settingsKeyMatched && voiceEnabledMatched - && voiceKeyMatched && colorSchemeMatched && imeOptionsMatched; - - if (DEBUG_TAG) { - Log.d(TAG, String.format( - "parseCaseCondition: %s%s%s%s%s%s%s", - Boolean.toString(selected).toUpperCase(), - debugInteger(a, R.styleable.BaseKeyboard_Case_mode, "mode"), - debugBoolean(a, R.styleable.BaseKeyboard_Case_hasSettingsKey, - "hasSettingsKey"), - debugBoolean(a, R.styleable.BaseKeyboard_Case_voiceKeyEnabled, - "voiceKeyEnabled"), - debugBoolean(a, R.styleable.BaseKeyboard_Case_hasVoiceKey, "hasVoiceKey"), - debugInteger(viewAttr, R.styleable.BaseKeyboardView_colorScheme, - "colorScheme"), - debugInteger(a, R.styleable.BaseKeyboard_Case_imeOptions, "imeOptions"))); - } - - return selected; - } finally { - a.recycle(); - viewAttr.recycle(); - } - } - - private static boolean matchInteger(TypedArray a, int index, int value) { - // If <case> does not have "index" attribute, that means this <case> is wild-card for the - // attribute. - return !a.hasValue(index) || a.getInt(index, 0) == value; - } - - private static boolean matchBoolean(TypedArray a, int index, boolean value) { - // If <case> does not have "index" attribute, that means this <case> is wild-card for the - // attribute. - return !a.hasValue(index) || a.getBoolean(index, false) == value; - } - - private boolean parseDefault(XmlResourceParser parser, Row row, List<Key> keys) - throws XmlPullParserException, IOException { - if (row == null) { - parseKeyboardContent(parser, keys); - } else { - parseRowContent(parser, row, keys); - } - return true; - } - - private void parseKeyStyle(XmlResourceParser parser, List<Key> keys) - throws XmlPullParserException, IOException { - TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser), - R.styleable.BaseKeyboard_KeyStyle); - TypedArray keyAttrs = mResources.obtainAttributes(Xml.asAttributeSet(parser), - R.styleable.BaseKeyboard_Key); - try { - if (!a.hasValue(R.styleable.BaseKeyboard_KeyStyle_styleName)) - throw new ParseException("<" + TAG_KEY_STYLE - + "/> needs styleName attribute", parser); - if (keys != null) - mKeyStyles.parseKeyStyleAttributes(a, keyAttrs, parser); - } finally { - a.recycle(); - keyAttrs.recycle(); - } - } - - private static void checkEndTag(String tag, XmlResourceParser parser) - throws XmlPullParserException, IOException { - if (parser.next() == XmlResourceParser.END_TAG && tag.equals(parser.getName())) - return; - throw new NonEmptyTag(tag, parser); - } - - private void startRow(Row row) { - mCurrentX = 0; - mCurrentRow = row; - } - - private void endRow() { - if (mCurrentRow == null) - throw new InflateException("orphant end row tag"); - mCurrentY += mCurrentRow.verticalGap + mCurrentRow.defaultHeight; - mCurrentRow = null; - } - - private void endKey(Key key) { - mCurrentX += key.gap + key.width; - if (mCurrentX > mMaxRowWidth) - mMaxRowWidth = mCurrentX; - } - - private void endKeyboard(int defaultVerticalGap) { - mTotalHeight = mCurrentY - defaultVerticalGap; - } - - private void setSpacer(int gap) { - mCurrentX += gap; - } - - public static int getDimensionOrFraction(TypedArray a, int index, int base, int defValue) { - final TypedValue value = a.peekValue(index); - if (value == null) - return defValue; - if (value.type == TypedValue.TYPE_DIMENSION) { - return a.getDimensionPixelOffset(index, defValue); - } else if (value.type == TypedValue.TYPE_FRACTION) { - // Round it to avoid values like 47.9999 from getting truncated - return Math.round(a.getFraction(index, base, base, defValue)); - } - return defValue; - } - - @SuppressWarnings("serial") - public static class ParseException extends InflateException { - public ParseException(String msg, XmlResourceParser parser) { - super(msg + " at line " + parser.getLineNumber()); - } - } - - @SuppressWarnings("serial") - private static class IllegalStartTag extends ParseException { - public IllegalStartTag(XmlResourceParser parser, String parent) { - super("Illegal start tag " + parser.getName() + " in " + parent, parser); - } - } - - @SuppressWarnings("serial") - private static class IllegalEndTag extends ParseException { - public IllegalEndTag(XmlResourceParser parser, String parent) { - super("Illegal end tag " + parser.getName() + " in " + parent, parser); - } - } - - @SuppressWarnings("serial") - private static class NonEmptyTag extends ParseException { - public NonEmptyTag(String tag, XmlResourceParser parser) { - super(tag + " must be empty tag", parser); - } - } - - private static void debugStartTag(String title, String tag, boolean skip) { - Log.d(TAG, title + ": <" + tag + ">" + (skip ? " skip" : "")); - } - - private static void debugEndTag(String title, String tag, boolean skip) { - Log.d(TAG, title + ": </" + tag + ">" + (skip ? " skip" : "")); - } - - private static String debugInteger(TypedArray a, int index, String name) { - return a.hasValue(index) ? " " + name + "=" + a.getInt(index, 0) : ""; - } - - private static String debugBoolean(TypedArray a, int index, String name) { - return a.hasValue(index) ? " " + name + "=" + a.getBoolean(index, false) : ""; - } -} diff --git a/java/src/com/android/inputmethod/latin/BaseKeyboardView.java b/java/src/com/android/inputmethod/latin/BaseKeyboardView.java deleted file mode 100644 index 3193cd46e..000000000 --- a/java/src/com/android/inputmethod/latin/BaseKeyboardView.java +++ /dev/null @@ -1,1585 +0,0 @@ -/* - * 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.latin; - -import com.android.inputmethod.latin.BaseKeyboard.Key; - -import android.content.Context; -import android.content.pm.PackageManager; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Paint.Align; -import android.graphics.PorterDuff; -import android.graphics.Rect; -import android.graphics.Region.Op; -import android.graphics.Typeface; -import android.graphics.drawable.Drawable; -import android.os.Handler; -import android.os.Message; -import android.os.SystemClock; -import android.util.AttributeSet; -import android.util.Log; -import android.util.TypedValue; -import android.view.GestureDetector; -import android.view.Gravity; -import android.view.LayoutInflater; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewGroup.LayoutParams; -import android.view.WindowManager; -import android.widget.PopupWindow; -import android.widget.TextView; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.WeakHashMap; - -/** - * A view that renders a virtual {@link LatinKeyboard}. It handles rendering of keys and - * detecting key presses and touch movements. - * - * TODO: References to LatinKeyboard in this class should be replaced with ones to its base class. - * - * @attr ref R.styleable#BaseKeyboardView_keyBackground - * @attr ref R.styleable#BaseKeyboardView_keyPreviewLayout - * @attr ref R.styleable#BaseKeyboardView_keyPreviewOffset - * @attr ref R.styleable#BaseKeyboardView_labelTextSize - * @attr ref R.styleable#BaseKeyboardView_keyTextSize - * @attr ref R.styleable#BaseKeyboardView_keyTextColor - * @attr ref R.styleable#BaseKeyboardView_verticalCorrection - * @attr ref R.styleable#BaseKeyboardView_popupLayout - */ -public class BaseKeyboardView extends View implements PointerTracker.UIProxy { - private static final String TAG = "BaseKeyboardView"; - private static final boolean DEBUG = false; - private static final boolean DEBUG_SHOW_ALIGN = false; - private static final boolean DEBUG_KEYBOARD_GRID = false; - - public static final int COLOR_SCHEME_WHITE = 0; - public static final int COLOR_SCHEME_BLACK = 1; - - public static final int NOT_A_TOUCH_COORDINATE = -1; - - public interface OnKeyboardActionListener { - - /** - * Called when the user presses a key. This is sent before the - * {@link #onKey} is called. For keys that repeat, this is only - * called once. - * - * @param primaryCode - * the unicode of the key being pressed. If the touch is - * not on a valid key, the value will be zero. - */ - void onPress(int primaryCode); - - /** - * Called when the user releases a key. This is sent after the - * {@link #onKey} is called. For keys that repeat, this is only - * called once. - * - * @param primaryCode - * the code of the key that was released - */ - void onRelease(int primaryCode); - - /** - * Send a key press to the listener. - * - * @param primaryCode - * this is the key that was pressed - * @param keyCodes - * the codes for all the possible alternative keys with - * the primary code being the first. If the primary key - * code is a single character such as an alphabet or - * number or symbol, the alternatives will include other - * characters that may be on the same key or adjacent - * keys. These codes are useful to correct for - * accidental presses of a key adjacent to the intended - * key. - * @param x - * x-coordinate pixel of touched event. If onKey is not called by onTouchEvent, - * the value should be NOT_A_TOUCH_COORDINATE. - * @param y - * y-coordinate pixel of touched event. If onKey is not called by onTouchEvent, - * the value should be NOT_A_TOUCH_COORDINATE. - */ - void onKey(int primaryCode, int[] keyCodes, int x, int y); - - /** - * Sends a sequence of characters to the listener. - * - * @param text - * the sequence of characters to be displayed. - */ - void onText(CharSequence text); - - /** - * Called when user released a finger outside any key. - */ - void onCancel(); - - /** - * Called when the user quickly moves the finger from right to - * left. - */ - void swipeLeft(); - - /** - * Called when the user quickly moves the finger from left to - * right. - */ - void swipeRight(); - - /** - * Called when the user quickly moves the finger from up to down. - */ - void swipeDown(); - - /** - * Called when the user quickly moves the finger from down to up. - */ - void swipeUp(); - } - - // Timing constants - private final int mKeyRepeatInterval; - - // Miscellaneous constants - /* package */ static final int NOT_A_KEY = -1; - private static final int[] LONG_PRESSABLE_STATE_SET = { android.R.attr.state_long_pressable }; - private static final int HINT_ICON_VERTICAL_ADJUSTMENT_PIXEL = -1; - - // XML attribute - private int mKeyLetterSize; - private int mKeyTextColor; - private int mKeyTextColorDisabled; - private Typeface mKeyLetterStyle = Typeface.DEFAULT; - private int mLabelTextSize; - private int mColorScheme = COLOR_SCHEME_WHITE; - private int mShadowColor; - private float mShadowRadius; - private Drawable mKeyBackground; - private float mBackgroundDimAmount; - private float mKeyHysteresisDistance; - private float mVerticalCorrection; - private int mPreviewOffset; - private int mPreviewHeight; - private int mPopupLayout; - - // Main keyboard - private BaseKeyboard mKeyboard; - private Key[] mKeys; - - // Key preview popup - private boolean mInForeground; - private TextView mPreviewText; - private PopupWindow mPreviewPopup; - private int mPreviewTextSizeLarge; - private int[] mOffsetInWindow; - private int mOldPreviewKeyIndex = NOT_A_KEY; - private boolean mShowPreview = true; - private boolean mShowTouchPoints = true; - private int mPopupPreviewOffsetX; - private int mPopupPreviewOffsetY; - private int mWindowY; - private int mPopupPreviewDisplayedY; - private final int mDelayBeforePreview; - private final int mDelayAfterPreview; - - // Popup mini keyboard - private PopupWindow mMiniKeyboardPopup; - private BaseKeyboardView mMiniKeyboard; - private View mMiniKeyboardParent; - private final WeakHashMap<Key, View> mMiniKeyboardCache = new WeakHashMap<Key, View>(); - private int mMiniKeyboardOriginX; - private int mMiniKeyboardOriginY; - private long mMiniKeyboardPopupTime; - private int[] mWindowOffset; - private final float mMiniKeyboardSlideAllowance; - private int mMiniKeyboardTrackerId; - - /** Listener for {@link OnKeyboardActionListener}. */ - private OnKeyboardActionListener mKeyboardActionListener; - - private final ArrayList<PointerTracker> mPointerTrackers = new ArrayList<PointerTracker>(); - - // TODO: Let the PointerTracker class manage this pointer queue - private final PointerQueue mPointerQueue = new PointerQueue(); - - private final boolean mHasDistinctMultitouch; - private int mOldPointerCount = 1; - - protected KeyDetector mKeyDetector = new ProximityKeyDetector(); - - // Swipe gesture detector - private GestureDetector mGestureDetector; - private final SwipeTracker mSwipeTracker = new SwipeTracker(); - private final int mSwipeThreshold; - private final boolean mDisambiguateSwipe; - - // Drawing - /** Whether the keyboard bitmap needs to be redrawn before it's blitted. **/ - private boolean mDrawPending; - /** The dirty region in the keyboard bitmap */ - private final Rect mDirtyRect = new Rect(); - /** The keyboard bitmap for faster updates */ - private Bitmap mBuffer; - /** Notes if the keyboard just changed, so that we could possibly reallocate the mBuffer. */ - private boolean mKeyboardChanged; - private Key mInvalidatedKey; - /** The canvas for the above mutable keyboard bitmap */ - private Canvas mCanvas; - private final Paint mPaint; - private final Rect mPadding; - private final Rect mClipRegion = new Rect(0, 0, 0, 0); - // This map caches key label text height in pixel as value and key label text size as map key. - private final HashMap<Integer, Integer> mTextHeightCache = new HashMap<Integer, Integer>(); - // Distance from horizontal center of the key, proportional to key label text height and width. - private final float KEY_LABEL_VERTICAL_ADJUSTMENT_FACTOR_CENTER = 0.55f; - private final float KEY_LABEL_VERTICAL_PADDING_FACTOR = 1.60f; - private final String KEY_LABEL_REFERENCE_CHAR = "H"; - private final int KEY_LABEL_OPTION_ALIGN_LEFT = 1; - private final int KEY_LABEL_OPTION_ALIGN_RIGHT = 2; - private final int KEY_LABEL_OPTION_ALIGN_BOTTOM = 8; - private final int KEY_LABEL_OPTION_FONT_NORMAL = 16; - private final int mKeyLabelHorizontalPadding; - - private final UIHandler mHandler = new UIHandler(); - - class UIHandler extends Handler { - private static final int MSG_POPUP_PREVIEW = 1; - private static final int MSG_DISMISS_PREVIEW = 2; - private static final int MSG_REPEAT_KEY = 3; - private static final int MSG_LONGPRESS_KEY = 4; - private static final int MSG_LONGPRESS_SHIFT_KEY = 5; - - private boolean mInKeyRepeat; - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_POPUP_PREVIEW: - showKey(msg.arg1, (PointerTracker)msg.obj); - break; - case MSG_DISMISS_PREVIEW: - mPreviewPopup.dismiss(); - break; - case MSG_REPEAT_KEY: { - final PointerTracker tracker = (PointerTracker)msg.obj; - tracker.repeatKey(msg.arg1); - startKeyRepeatTimer(mKeyRepeatInterval, msg.arg1, tracker); - break; - } - case MSG_LONGPRESS_KEY: { - final PointerTracker tracker = (PointerTracker)msg.obj; - openPopupIfRequired(msg.arg1, tracker); - break; - } - case MSG_LONGPRESS_SHIFT_KEY: { - final PointerTracker tracker = (PointerTracker)msg.obj; - onLongPressShiftKey(tracker); - break; - } - } - } - - public void popupPreview(long delay, int keyIndex, PointerTracker tracker) { - removeMessages(MSG_POPUP_PREVIEW); - if (mPreviewPopup.isShowing() && mPreviewText.getVisibility() == VISIBLE) { - // Show right away, if it's already visible and finger is moving around - showKey(keyIndex, tracker); - } else { - sendMessageDelayed(obtainMessage(MSG_POPUP_PREVIEW, keyIndex, 0, tracker), - delay); - } - } - - public void cancelPopupPreview() { - removeMessages(MSG_POPUP_PREVIEW); - } - - public void dismissPreview(long delay) { - if (mPreviewPopup.isShowing()) { - sendMessageDelayed(obtainMessage(MSG_DISMISS_PREVIEW), delay); - } - } - - public void cancelDismissPreview() { - removeMessages(MSG_DISMISS_PREVIEW); - } - - public void startKeyRepeatTimer(long delay, int keyIndex, PointerTracker tracker) { - mInKeyRepeat = true; - sendMessageDelayed(obtainMessage(MSG_REPEAT_KEY, keyIndex, 0, tracker), delay); - } - - public void cancelKeyRepeatTimer() { - mInKeyRepeat = false; - removeMessages(MSG_REPEAT_KEY); - } - - public boolean isInKeyRepeat() { - return mInKeyRepeat; - } - - public void startLongPressTimer(long delay, int keyIndex, PointerTracker tracker) { - cancelLongPressTimers(); - sendMessageDelayed(obtainMessage(MSG_LONGPRESS_KEY, keyIndex, 0, tracker), delay); - } - - public void startLongPressShiftTimer(long delay, int keyIndex, PointerTracker tracker) { - cancelLongPressTimers(); - sendMessageDelayed( - obtainMessage(MSG_LONGPRESS_SHIFT_KEY, keyIndex, 0, tracker), delay); - } - - public void cancelLongPressTimers() { - removeMessages(MSG_LONGPRESS_KEY); - removeMessages(MSG_LONGPRESS_SHIFT_KEY); - } - - public void cancelKeyTimers() { - cancelKeyRepeatTimer(); - cancelLongPressTimers(); - } - - public void cancelAllMessages() { - cancelKeyTimers(); - cancelPopupPreview(); - cancelDismissPreview(); - } - }; - - static class PointerQueue { - private LinkedList<PointerTracker> mQueue = new LinkedList<PointerTracker>(); - - public void add(PointerTracker tracker) { - mQueue.add(tracker); - } - - public int lastIndexOf(PointerTracker tracker) { - LinkedList<PointerTracker> queue = mQueue; - for (int index = queue.size() - 1; index >= 0; index--) { - PointerTracker t = queue.get(index); - if (t == tracker) - return index; - } - return -1; - } - - public void releaseAllPointersOlderThan(PointerTracker tracker, long eventTime) { - LinkedList<PointerTracker> queue = mQueue; - int oldestPos = 0; - for (PointerTracker t = queue.get(oldestPos); t != tracker; t = queue.get(oldestPos)) { - if (t.isModifier()) { - oldestPos++; - } else { - t.onUpEvent(t.getLastX(), t.getLastY(), eventTime); - t.setAlreadyProcessed(); - queue.remove(oldestPos); - } - } - } - - public void releaseAllPointersExcept(PointerTracker tracker, long eventTime) { - for (PointerTracker t : mQueue) { - if (t == tracker) - continue; - t.onUpEvent(t.getLastX(), t.getLastY(), eventTime); - t.setAlreadyProcessed(); - } - mQueue.clear(); - if (tracker != null) - mQueue.add(tracker); - } - - public void remove(PointerTracker tracker) { - mQueue.remove(tracker); - } - } - - public BaseKeyboardView(Context context, AttributeSet attrs) { - this(context, attrs, R.attr.keyboardViewStyle); - } - - public BaseKeyboardView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - - TypedArray a = context.obtainStyledAttributes( - attrs, R.styleable.BaseKeyboardView, defStyle, R.style.BaseKeyboardView); - LayoutInflater inflate = - (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); - int previewLayout = 0; - int keyTextSize = 0; - - int n = a.getIndexCount(); - - for (int i = 0; i < n; i++) { - int attr = a.getIndex(i); - - switch (attr) { - case R.styleable.BaseKeyboardView_keyBackground: - mKeyBackground = a.getDrawable(attr); - break; - case R.styleable.BaseKeyboardView_keyHysteresisDistance: - mKeyHysteresisDistance = a.getDimensionPixelOffset(attr, 0); - break; - case R.styleable.BaseKeyboardView_verticalCorrection: - mVerticalCorrection = a.getDimensionPixelOffset(attr, 0); - break; - case R.styleable.BaseKeyboardView_keyPreviewLayout: - previewLayout = a.getResourceId(attr, 0); - break; - case R.styleable.BaseKeyboardView_keyPreviewOffset: - mPreviewOffset = a.getDimensionPixelOffset(attr, 0); - break; - case R.styleable.BaseKeyboardView_keyPreviewHeight: - mPreviewHeight = a.getDimensionPixelSize(attr, 80); - break; - case R.styleable.BaseKeyboardView_keyLetterSize: - mKeyLetterSize = a.getDimensionPixelSize(attr, 18); - break; - case R.styleable.BaseKeyboardView_keyTextColor: - mKeyTextColor = a.getColor(attr, 0xFF000000); - break; - case R.styleable.BaseKeyboardView_keyTextColorDisabled: - mKeyTextColorDisabled = a.getColor(attr, 0xFF000000); - break; - case R.styleable.BaseKeyboardView_labelTextSize: - mLabelTextSize = a.getDimensionPixelSize(attr, 14); - break; - case R.styleable.BaseKeyboardView_popupLayout: - mPopupLayout = a.getResourceId(attr, 0); - break; - case R.styleable.BaseKeyboardView_shadowColor: - mShadowColor = a.getColor(attr, 0); - break; - case R.styleable.BaseKeyboardView_shadowRadius: - mShadowRadius = a.getFloat(attr, 0f); - break; - // TODO: Use Theme (android.R.styleable.Theme_backgroundDimAmount) - case R.styleable.BaseKeyboardView_backgroundDimAmount: - mBackgroundDimAmount = a.getFloat(attr, 0.5f); - break; - case R.styleable.BaseKeyboardView_keyLetterStyle: - mKeyLetterStyle = Typeface.defaultFromStyle(a.getInt(attr, Typeface.NORMAL)); - break; - case R.styleable.BaseKeyboardView_colorScheme: - mColorScheme = a.getInt(attr, COLOR_SCHEME_WHITE); - break; - } - } - - final Resources res = getResources(); - - mPreviewPopup = new PopupWindow(context); - if (previewLayout != 0) { - mPreviewText = (TextView) inflate.inflate(previewLayout, null); - mPreviewTextSizeLarge = (int) res.getDimension(R.dimen.key_preview_text_size_large); - mPreviewPopup.setContentView(mPreviewText); - mPreviewPopup.setBackgroundDrawable(null); - } else { - mShowPreview = false; - } - mPreviewPopup.setTouchable(false); - mPreviewPopup.setAnimationStyle(R.style.KeyPreviewAnimation); - mDelayBeforePreview = res.getInteger(R.integer.config_delay_before_preview); - mDelayAfterPreview = res.getInteger(R.integer.config_delay_after_preview); - mKeyLabelHorizontalPadding = (int)res.getDimension( - R.dimen.key_label_horizontal_alignment_padding); - - mMiniKeyboardParent = this; - mMiniKeyboardPopup = new PopupWindow(context); - mMiniKeyboardPopup.setBackgroundDrawable(null); - mMiniKeyboardPopup.setAnimationStyle(R.style.MiniKeyboardAnimation); - - mPaint = new Paint(); - mPaint.setAntiAlias(true); - mPaint.setTextSize(keyTextSize); - mPaint.setTextAlign(Align.CENTER); - mPaint.setAlpha(255); - - mPadding = new Rect(0, 0, 0, 0); - mKeyBackground.getPadding(mPadding); - - mSwipeThreshold = (int) (500 * res.getDisplayMetrics().density); - // TODO: Refer frameworks/base/core/res/res/values/config.xml - mDisambiguateSwipe = res.getBoolean(R.bool.config_swipeDisambiguation); - mMiniKeyboardSlideAllowance = res.getDimension(R.dimen.mini_keyboard_slide_allowance); - - GestureDetector.SimpleOnGestureListener listener = - new GestureDetector.SimpleOnGestureListener() { - @Override - public boolean onFling(MotionEvent me1, MotionEvent me2, float velocityX, - float velocityY) { - final float absX = Math.abs(velocityX); - final float absY = Math.abs(velocityY); - float deltaX = me2.getX() - me1.getX(); - float deltaY = me2.getY() - me1.getY(); - int travelX = getWidth() / 2; // Half the keyboard width - int travelY = getHeight() / 2; // Half the keyboard height - mSwipeTracker.computeCurrentVelocity(1000); - final float endingVelocityX = mSwipeTracker.getXVelocity(); - final float endingVelocityY = mSwipeTracker.getYVelocity(); - if (velocityX > mSwipeThreshold && absY < absX && deltaX > travelX) { - if (mDisambiguateSwipe && endingVelocityX >= velocityX / 4) { - swipeRight(); - return true; - } - } else if (velocityX < -mSwipeThreshold && absY < absX && deltaX < -travelX) { - if (mDisambiguateSwipe && endingVelocityX <= velocityX / 4) { - swipeLeft(); - return true; - } - } else if (velocityY < -mSwipeThreshold && absX < absY && deltaY < -travelY) { - if (mDisambiguateSwipe && endingVelocityY <= velocityY / 4) { - swipeUp(); - return true; - } - } else if (velocityY > mSwipeThreshold && absX < absY / 2 && deltaY > travelY) { - if (mDisambiguateSwipe && endingVelocityY >= velocityY / 4) { - swipeDown(); - return true; - } - } - return false; - } - }; - - final boolean ignoreMultitouch = true; - mGestureDetector = new GestureDetector(getContext(), listener, null, ignoreMultitouch); - mGestureDetector.setIsLongpressEnabled(false); - - mHasDistinctMultitouch = context.getPackageManager() - .hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT); - mKeyRepeatInterval = res.getInteger(R.integer.config_key_repeat_interval); - } - - public void setOnKeyboardActionListener(OnKeyboardActionListener listener) { - mKeyboardActionListener = listener; - for (PointerTracker tracker : mPointerTrackers) { - tracker.setOnKeyboardActionListener(listener); - } - } - - /** - * Returns the {@link OnKeyboardActionListener} object. - * @return the listener attached to this keyboard - */ - protected OnKeyboardActionListener getOnKeyboardActionListener() { - return mKeyboardActionListener; - } - - /** - * Attaches a keyboard to this view. The keyboard can be switched at any time and the - * view will re-layout itself to accommodate the keyboard. - * @see BaseKeyboard - * @see #getKeyboard() - * @param keyboard the keyboard to display in this view - */ - protected void setKeyboard(BaseKeyboard keyboard) { - if (mKeyboard != null) { - dismissKeyPreview(); - } - // Remove any pending messages, except dismissing preview - mHandler.cancelKeyTimers(); - mHandler.cancelPopupPreview(); - mKeyboard = keyboard; - LatinImeLogger.onSetKeyboard(keyboard); - mKeys = mKeyDetector.setKeyboard(keyboard, -getPaddingLeft(), - -getPaddingTop() + mVerticalCorrection); - for (PointerTracker tracker : mPointerTrackers) { - tracker.setKeyboard(keyboard, mKeys, mKeyHysteresisDistance); - } - requestLayout(); - // Hint to reallocate the buffer if the size changed - mKeyboardChanged = true; - invalidateAllKeys(); - computeProximityThreshold(keyboard, mKeys); - mMiniKeyboardCache.clear(); - } - - /** - * Returns the current keyboard being displayed by this view. - * @return the currently attached keyboard - * @see #setKeyboard(BaseKeyboard) - */ - protected BaseKeyboard getKeyboard() { - return mKeyboard; - } - - /** - * Return whether the device has distinct multi-touch panel. - * @return true if the device has distinct multi-touch panel. - */ - public boolean hasDistinctMultitouch() { - return mHasDistinctMultitouch; - } - - /** - * Enables or disables the key feedback popup. This is a popup that shows a magnified - * version of the depressed key. By default the preview is enabled. - * @param previewEnabled whether or not to enable the key feedback popup - * @see #isPreviewEnabled() - */ - public void setPreviewEnabled(boolean previewEnabled) { - mShowPreview = previewEnabled; - } - - /** - * Returns the enabled state of the key feedback popup. - * @return whether or not the key feedback popup is enabled - * @see #setPreviewEnabled(boolean) - */ - public boolean isPreviewEnabled() { - return mShowPreview; - } - - public int getColorScheme() { - return mColorScheme; - } - - public void setPopupParent(View v) { - mMiniKeyboardParent = v; - } - - public void setPopupOffset(int x, int y) { - mPopupPreviewOffsetX = x; - mPopupPreviewOffsetY = y; - mPreviewPopup.dismiss(); - } - - /** - * When enabled, calls to {@link OnKeyboardActionListener#onKey} will include key - * codes for adjacent keys. When disabled, only the primary key code will be - * reported. - * @param enabled whether or not the proximity correction is enabled - */ - public void setProximityCorrectionEnabled(boolean enabled) { - mKeyDetector.setProximityCorrectionEnabled(enabled); - } - - /** - * Returns true if proximity correction is enabled. - */ - public boolean isProximityCorrectionEnabled() { - return mKeyDetector.isProximityCorrectionEnabled(); - } - - protected CharSequence adjustCase(CharSequence label) { - if (mKeyboard.isShiftedOrShiftLocked() && label != null && label.length() < 3 - && Character.isLowerCase(label.charAt(0))) { - label = label.toString().toUpperCase(); - } - return label; - } - - @Override - public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - // Round up a little - if (mKeyboard == null) { - setMeasuredDimension( - getPaddingLeft() + getPaddingRight(), getPaddingTop() + getPaddingBottom()); - } else { - int width = mKeyboard.getMinWidth() + getPaddingLeft() + getPaddingRight(); - if (MeasureSpec.getSize(widthMeasureSpec) < width + 10) { - width = MeasureSpec.getSize(widthMeasureSpec); - } - setMeasuredDimension( - width, mKeyboard.getHeight() + getPaddingTop() + getPaddingBottom()); - } - } - - /** - * Compute the most common key width and use it as proximity key detection threshold. - * @param keyboard - * @param keys - */ - private void computeProximityThreshold(BaseKeyboard keyboard, Key[] keys) { - if (keyboard == null || keys == null || keys.length == 0) return; - final HashMap<Integer, Integer> histogram = new HashMap<Integer, Integer>(); - int maxCount = 0; - int mostCommonWidth = 0; - for (Key key : keys) { - final Integer width = key.width + key.gap; - Integer count = histogram.get(width); - if (count == null) - count = 0; - histogram.put(width, ++count); - if (count > maxCount) { - maxCount = count; - mostCommonWidth = width; - } - } - mKeyDetector.setProximityThreshold(mostCommonWidth); - } - - @Override - public void onSizeChanged(int w, int h, int oldw, int oldh) { - super.onSizeChanged(w, h, oldw, oldh); - // Release the buffer, if any and it will be reallocated on the next draw - mBuffer = null; - } - - @Override - public void onDraw(Canvas canvas) { - super.onDraw(canvas); - if (mDrawPending || mBuffer == null || mKeyboardChanged) { - onBufferDraw(); - } - canvas.drawBitmap(mBuffer, 0, 0, null); - } - - private void onBufferDraw() { - if (mBuffer == null || mKeyboardChanged) { - if (mBuffer == null || mKeyboardChanged && - (mBuffer.getWidth() != getWidth() || mBuffer.getHeight() != getHeight())) { - // Make sure our bitmap is at least 1x1 - final int width = Math.max(1, getWidth()); - final int height = Math.max(1, getHeight()); - mBuffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - mCanvas = new Canvas(mBuffer); - } - invalidateAllKeys(); - mKeyboardChanged = false; - } - final Canvas canvas = mCanvas; - canvas.clipRect(mDirtyRect, Op.REPLACE); - - if (mKeyboard == null) return; - - final Paint paint = mPaint; - final Drawable keyBackground = mKeyBackground; - final Rect clipRegion = mClipRegion; - final Rect padding = mPadding; - final int kbdPaddingLeft = getPaddingLeft(); - final int kbdPaddingTop = getPaddingTop(); - final Key[] keys = mKeys; - final Key invalidKey = mInvalidatedKey; - final boolean isManualTemporaryUpperCase = (mKeyboard instanceof LatinKeyboard - && ((LatinKeyboard)mKeyboard).isManualTemporaryUpperCase()); - - boolean drawSingleKey = false; - if (invalidKey != null && canvas.getClipBounds(clipRegion)) { - // TODO we should use Rect.inset and Rect.contains here. - // Is clipRegion completely contained within the invalidated key? - if (invalidKey.x + kbdPaddingLeft - 1 <= clipRegion.left && - invalidKey.y + kbdPaddingTop - 1 <= clipRegion.top && - invalidKey.x + invalidKey.width + kbdPaddingLeft + 1 >= clipRegion.right && - invalidKey.y + invalidKey.height + kbdPaddingTop + 1 >= clipRegion.bottom) { - drawSingleKey = true; - } - } - canvas.drawColor(0x00000000, PorterDuff.Mode.CLEAR); - final int keyCount = keys.length; - for (int i = 0; i < keyCount; i++) { - final Key key = keys[i]; - if (drawSingleKey && invalidKey != key) { - continue; - } - int[] drawableState = key.getCurrentDrawableState(); - keyBackground.setState(drawableState); - - // Switch the character to uppercase if shift is pressed - String label = key.label == null? null : adjustCase(key.label).toString(); - - final Rect bounds = keyBackground.getBounds(); - if (key.width != bounds.right || key.height != bounds.bottom) { - keyBackground.setBounds(0, 0, key.width, key.height); - } - canvas.translate(key.x + kbdPaddingLeft, key.y + kbdPaddingTop); - keyBackground.draw(canvas); - - final int rowHeight = padding.top + key.height; - // Draw key label - if (label != null) { - // For characters, use large font. For labels like "Done", use small font. - final int labelSize = getLabelSizeAndSetPaint(label, key, paint); - final int labelCharHeight = getLabelCharHeight(labelSize, paint); - - // Vertical label text alignment. - final float baseline; - if ((key.labelOption & KEY_LABEL_OPTION_ALIGN_BOTTOM) != 0) { - baseline = key.height - - + labelCharHeight * KEY_LABEL_VERTICAL_PADDING_FACTOR; - if (DEBUG_SHOW_ALIGN) - drawHorizontalLine(canvas, (int)baseline, key.width, 0xc0008000, - new Paint()); - } else { // Align center - final float centerY = (key.height + padding.top - padding.bottom) / 2; - baseline = centerY - + labelCharHeight * KEY_LABEL_VERTICAL_ADJUSTMENT_FACTOR_CENTER; - } - // Horizontal label text alignment - final int positionX; - if ((key.labelOption & KEY_LABEL_OPTION_ALIGN_LEFT) != 0) { - positionX = mKeyLabelHorizontalPadding + padding.left; - paint.setTextAlign(Align.LEFT); - if (DEBUG_SHOW_ALIGN) - drawVerticalLine(canvas, positionX, rowHeight, 0xc0800080, new Paint()); - } else if ((key.labelOption & KEY_LABEL_OPTION_ALIGN_RIGHT) != 0) { - positionX = key.width - mKeyLabelHorizontalPadding - padding.right; - paint.setTextAlign(Align.RIGHT); - if (DEBUG_SHOW_ALIGN) - drawVerticalLine(canvas, positionX, rowHeight, 0xc0808000, new Paint()); - } else { - positionX = (key.width + padding.left - padding.right) / 2; - paint.setTextAlign(Align.CENTER); - if (DEBUG_SHOW_ALIGN && label.length() > 1) - drawVerticalLine(canvas, positionX, rowHeight, 0xc0008080, new Paint()); - } - if (key.manualTemporaryUpperCaseHintIcon != null && isManualTemporaryUpperCase) { - paint.setColor(mKeyTextColorDisabled); - } else { - paint.setColor(mKeyTextColor); - } - // Set a drop shadow for the text - paint.setShadowLayer(mShadowRadius, 0, 0, mShadowColor); - canvas.drawText(label, positionX, baseline, paint); - // Turn off drop shadow - paint.setShadowLayer(0, 0, 0, 0); - } - // Draw key icon - if (key.label == null && key.icon != null) { - final int drawableWidth = key.icon.getIntrinsicWidth(); - final int drawableHeight = key.icon.getIntrinsicHeight(); - final int drawableX; - final int drawableY = ( - key.height + padding.top - padding.bottom - drawableHeight) / 2; - if ((key.labelOption & KEY_LABEL_OPTION_ALIGN_LEFT) != 0) { - drawableX = padding.left + mKeyLabelHorizontalPadding; - if (DEBUG_SHOW_ALIGN) - drawVerticalLine(canvas, drawableX, rowHeight, 0xc0800080, new Paint()); - } else if ((key.labelOption & KEY_LABEL_OPTION_ALIGN_RIGHT) != 0) { - drawableX = key.width - padding.right - mKeyLabelHorizontalPadding - - drawableWidth; - if (DEBUG_SHOW_ALIGN) - drawVerticalLine(canvas, drawableX + drawableWidth, rowHeight, - 0xc0808000, new Paint()); - } else { // Align center - drawableX = (key.width + padding.left - padding.right - drawableWidth) / 2; - if (DEBUG_SHOW_ALIGN) - drawVerticalLine(canvas, drawableX + drawableWidth / 2, rowHeight, - 0xc0008080, new Paint()); - } - drawIcon(canvas, key.icon, drawableX, drawableY, drawableWidth, drawableHeight); - if (DEBUG_SHOW_ALIGN) - drawRectangle(canvas, drawableX, drawableY, drawableWidth, drawableHeight, - 0x80c00000, new Paint()); - } - if (key.hintIcon != null) { - final int drawableWidth = key.width; - final int drawableHeight = key.height; - final int drawableX = 0; - final int drawableY = HINT_ICON_VERTICAL_ADJUSTMENT_PIXEL; - Drawable icon = (isManualTemporaryUpperCase - && key.manualTemporaryUpperCaseHintIcon != null) - ? key.manualTemporaryUpperCaseHintIcon : key.hintIcon; - drawIcon(canvas, icon, drawableX, drawableY, drawableWidth, drawableHeight); - if (DEBUG_SHOW_ALIGN) - drawRectangle(canvas, drawableX, drawableY, drawableWidth, drawableHeight, - 0x80c0c000, new Paint()); - } - canvas.translate(-key.x - kbdPaddingLeft, -key.y - kbdPaddingTop); - } - - if (DEBUG_KEYBOARD_GRID) { - Paint p = new Paint(); - p.setStyle(Paint.Style.STROKE); - p.setStrokeWidth(1.0f); - p.setColor(0x800000c0); - int cw = (mKeyboard.getMinWidth() + mKeyboard.GRID_WIDTH - 1) / mKeyboard.GRID_WIDTH; - int ch = (mKeyboard.getHeight() + mKeyboard.GRID_HEIGHT - 1) / mKeyboard.GRID_HEIGHT; - for (int i = 0; i <= mKeyboard.GRID_WIDTH; i++) - canvas.drawLine(i * cw, 0, i * cw, ch * mKeyboard.GRID_HEIGHT, p); - for (int i = 0; i <= mKeyboard.GRID_HEIGHT; i++) - canvas.drawLine(0, i * ch, cw * mKeyboard.GRID_WIDTH, i * ch, p); - } - - mInvalidatedKey = null; - // Overlay a dark rectangle to dim the keyboard - if (mMiniKeyboard != null) { - paint.setColor((int) (mBackgroundDimAmount * 0xFF) << 24); - canvas.drawRect(0, 0, getWidth(), getHeight(), paint); - } - - if (DEBUG) { - if (mShowTouchPoints) { - for (PointerTracker tracker : mPointerTrackers) { - int startX = tracker.getStartX(); - int startY = tracker.getStartY(); - int lastX = tracker.getLastX(); - int lastY = tracker.getLastY(); - paint.setAlpha(128); - paint.setColor(0xFFFF0000); - canvas.drawCircle(startX, startY, 3, paint); - canvas.drawLine(startX, startY, lastX, lastY, paint); - paint.setColor(0xFF0000FF); - canvas.drawCircle(lastX, lastY, 3, paint); - paint.setColor(0xFF00FF00); - canvas.drawCircle((startX + lastX) / 2, (startY + lastY) / 2, 2, paint); - } - } - } - - mDrawPending = false; - mDirtyRect.setEmpty(); - } - - private int getLabelSizeAndSetPaint(CharSequence label, Key key, Paint paint) { - // For characters, use large font. For labels like "Done", use small font. - final int labelSize; - final Typeface labelStyle; - if (label.length() > 1 && key.codes.length < 2) { - labelSize = mLabelTextSize; - if ((key.labelOption & KEY_LABEL_OPTION_FONT_NORMAL) != 0) { - labelStyle = Typeface.DEFAULT; - } else { - labelStyle = Typeface.DEFAULT_BOLD; - } - } else { - labelSize = mKeyLetterSize; - labelStyle = mKeyLetterStyle; - } - paint.setTextSize(labelSize); - paint.setTypeface(labelStyle); - return labelSize; - } - - private int getLabelCharHeight(int labelSize, Paint paint) { - Integer labelHeightValue = mTextHeightCache.get(labelSize); - final int labelCharHeight; - if (labelHeightValue != null) { - labelCharHeight = labelHeightValue; - } else { - Rect textBounds = new Rect(); - paint.getTextBounds(KEY_LABEL_REFERENCE_CHAR, 0, 1, textBounds); - labelCharHeight = textBounds.height(); - mTextHeightCache.put(labelSize, labelCharHeight); - } - return labelCharHeight; - } - - private static void drawIcon(Canvas canvas, Drawable icon, int x, int y, int width, - int height) { - canvas.translate(x, y); - icon.setBounds(0, 0, width, height); - icon.draw(canvas); - canvas.translate(-x, -y); - } - - private static void drawHorizontalLine(Canvas canvas, int y, int w, int color, Paint paint) { - paint.setStyle(Paint.Style.STROKE); - paint.setStrokeWidth(1.0f); - paint.setColor(color); - canvas.drawLine(0, y, w, y, paint); - } - - private static void drawVerticalLine(Canvas canvas, int x, int h, int color, Paint paint) { - paint.setStyle(Paint.Style.STROKE); - paint.setStrokeWidth(1.0f); - paint.setColor(color); - canvas.drawLine(x, 0, x, h, paint); - } - - private static void drawRectangle(Canvas canvas, int x, int y, int w, int h, int color, - Paint paint) { - paint.setStyle(Paint.Style.STROKE); - paint.setStrokeWidth(1.0f); - paint.setColor(color); - canvas.translate(x, y); - canvas.drawRect(0, 0, w, h, paint); - canvas.translate(-x, -y); - } - - public void setForeground(boolean foreground) { - mInForeground = foreground; - } - - // TODO: clean up this method. - private void dismissKeyPreview() { - for (PointerTracker tracker : mPointerTrackers) - tracker.releaseKey(); - showPreview(NOT_A_KEY, null); - } - - public void showPreview(int keyIndex, PointerTracker tracker) { - int oldKeyIndex = mOldPreviewKeyIndex; - mOldPreviewKeyIndex = keyIndex; - // We should re-draw popup preview when 1) we need to hide the preview, 2) we will show - // the space key preview and 3) pointer moves off the space key to other letter key, we - // should hide the preview of the previous key. - final boolean hidePreviewOrShowSpaceKeyPreview = (tracker == null) - || (SubtypeSwitcher.USE_SPACEBAR_LANGUAGE_SWITCHER - && SubtypeSwitcher.getInstance().needsToDisplayLanguage() - && (tracker.isSpaceKey(keyIndex) || tracker.isSpaceKey(oldKeyIndex))); - // If key changed and preview is on or the key is space (language switch is enabled) - if (oldKeyIndex != keyIndex && (mShowPreview || (hidePreviewOrShowSpaceKeyPreview))) { - if (keyIndex == NOT_A_KEY) { - mHandler.cancelPopupPreview(); - mHandler.dismissPreview(mDelayAfterPreview); - } else if (tracker != null) { - mHandler.popupPreview(mDelayBeforePreview, keyIndex, tracker); - } - } - } - - // TODO Must fix popup preview on xlarge layout - private void showKey(final int keyIndex, PointerTracker tracker) { - Key key = tracker.getKey(keyIndex); - // If keyIndex is invalid or IME is already closed, we must not show key preview. - // Trying to show preview PopupWindow while root window is closed causes - // WindowManager.BadTokenException. - if (key == null || !mInForeground) - return; - // What we show as preview should match what we show on key top in onBufferDraw(). - if (key.label != null) { - // TODO Should take care of temporaryShiftLabel here. - mPreviewText.setCompoundDrawables(null, null, null, null); - mPreviewText.setText(adjustCase(tracker.getPreviewText(key))); - if (key.label.length() > 1 && key.codes.length < 2) { - mPreviewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mKeyLetterSize); - mPreviewText.setTypeface(Typeface.DEFAULT_BOLD); - } else { - mPreviewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mPreviewTextSizeLarge); - mPreviewText.setTypeface(mKeyLetterStyle); - } - } else { - mPreviewText.setCompoundDrawables(null, null, null, - key.iconPreview != null ? key.iconPreview : key.icon); - mPreviewText.setText(null); - } - mPreviewText.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), - MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); - int popupWidth = Math.max(mPreviewText.getMeasuredWidth(), key.width - + mPreviewText.getPaddingLeft() + mPreviewText.getPaddingRight()); - final int popupHeight = mPreviewHeight; - LayoutParams lp = mPreviewText.getLayoutParams(); - if (lp != null) { - lp.width = popupWidth; - lp.height = popupHeight; - } - - int popupPreviewX = key.x - (popupWidth - key.width) / 2; - int popupPreviewY = key.y - popupHeight + mPreviewOffset; - - mHandler.cancelDismissPreview(); - if (mOffsetInWindow == null) { - mOffsetInWindow = new int[2]; - getLocationInWindow(mOffsetInWindow); - mOffsetInWindow[0] += mPopupPreviewOffsetX; // Offset may be zero - mOffsetInWindow[1] += mPopupPreviewOffsetY; // Offset may be zero - int[] windowLocation = new int[2]; - getLocationOnScreen(windowLocation); - mWindowY = windowLocation[1]; - } - // Set the preview background state - mPreviewText.getBackground().setState( - key.popupResId != 0 ? LONG_PRESSABLE_STATE_SET : EMPTY_STATE_SET); - popupPreviewX += mOffsetInWindow[0]; - popupPreviewY += mOffsetInWindow[1]; - - // If the popup cannot be shown above the key, put it on the side - if (popupPreviewY + mWindowY < 0) { - // If the key you're pressing is on the left side of the keyboard, show the popup on - // the right, offset by enough to see at least one key to the left/right. - if (key.x + key.width <= getWidth() / 2) { - popupPreviewX += (int) (key.width * 2.5); - } else { - popupPreviewX -= (int) (key.width * 2.5); - } - popupPreviewY += popupHeight; - } - - try { - if (mPreviewPopup.isShowing()) { - mPreviewPopup.update(popupPreviewX, popupPreviewY, popupWidth, popupHeight); - } else { - mPreviewPopup.setWidth(popupWidth); - mPreviewPopup.setHeight(popupHeight); - mPreviewPopup.showAtLocation(mMiniKeyboardParent, Gravity.NO_GRAVITY, - popupPreviewX, popupPreviewY); - } - } catch (WindowManager.BadTokenException e) { - // Swallow the exception which will be happened when IME is already closed. - Log.w(TAG, "LatinIME is already closed when tried showing key preview."); - } - // Record popup preview position to display mini-keyboard later at the same positon - mPopupPreviewDisplayedY = popupPreviewY; - mPreviewText.setVisibility(VISIBLE); - } - - /** - * Requests a redraw of the entire keyboard. Calling {@link #invalidate} is not sufficient - * because the keyboard renders the keys to an off-screen buffer and an invalidate() only - * draws the cached buffer. - * @see #invalidateKey(Key) - */ - public void invalidateAllKeys() { - mDirtyRect.union(0, 0, getWidth(), getHeight()); - mDrawPending = true; - invalidate(); - } - - /** - * Invalidates a key so that it will be redrawn on the next repaint. Use this method if only - * one key is changing it's content. Any changes that affect the position or size of the key - * may not be honored. - * @param key key in the attached {@link BaseKeyboard}. - * @see #invalidateAllKeys - */ - public void invalidateKey(Key key) { - if (key == null) - return; - mInvalidatedKey = key; - // TODO we should clean up this and record key's region to use in onBufferDraw. - mDirtyRect.union(key.x + getPaddingLeft(), key.y + getPaddingTop(), - key.x + key.width + getPaddingLeft(), key.y + key.height + getPaddingTop()); - onBufferDraw(); - invalidate(key.x + getPaddingLeft(), key.y + getPaddingTop(), - key.x + key.width + getPaddingLeft(), key.y + key.height + getPaddingTop()); - } - - private boolean openPopupIfRequired(int keyIndex, PointerTracker tracker) { - // Check if we have a popup layout specified first. - if (mPopupLayout == 0) { - return false; - } - - Key popupKey = tracker.getKey(keyIndex); - if (popupKey == null) - return false; - boolean result = onLongPress(popupKey); - if (result) { - dismissKeyPreview(); - mMiniKeyboardTrackerId = tracker.mPointerId; - // Mark this tracker "already processed" and remove it from the pointer queue - tracker.setAlreadyProcessed(); - mPointerQueue.remove(tracker); - } - return result; - } - - private void onLongPressShiftKey(PointerTracker tracker) { - tracker.setAlreadyProcessed(); - mPointerQueue.remove(tracker); - mKeyboardActionListener.onKey(LatinKeyboardView.KEYCODE_CAPSLOCK, null, 0, 0); - } - - private View inflateMiniKeyboardContainer(Key popupKey) { - int popupKeyboardId = popupKey.popupResId; - LayoutInflater inflater = (LayoutInflater)getContext().getSystemService( - Context.LAYOUT_INFLATER_SERVICE); - View container = inflater.inflate(mPopupLayout, null); - if (container == null) - throw new NullPointerException(); - - BaseKeyboardView miniKeyboard = - (BaseKeyboardView)container.findViewById(R.id.BaseKeyboardView); - miniKeyboard.setOnKeyboardActionListener(new OnKeyboardActionListener() { - public void onKey(int primaryCode, int[] keyCodes, int x, int y) { - mKeyboardActionListener.onKey(primaryCode, keyCodes, x, y); - dismissPopupKeyboard(); - } - - public void onText(CharSequence text) { - mKeyboardActionListener.onText(text); - dismissPopupKeyboard(); - } - - public void onCancel() { - dismissPopupKeyboard(); - } - - public void swipeLeft() { - } - public void swipeRight() { - } - public void swipeUp() { - } - public void swipeDown() { - } - public void onPress(int primaryCode) { - mKeyboardActionListener.onPress(primaryCode); - } - public void onRelease(int primaryCode) { - mKeyboardActionListener.onRelease(primaryCode); - } - }); - // Override default ProximityKeyDetector. - miniKeyboard.mKeyDetector = new MiniKeyboardKeyDetector(mMiniKeyboardSlideAllowance); - // Remove gesture detector on mini-keyboard - miniKeyboard.mGestureDetector = null; - - BaseKeyboard keyboard; - if (popupKey.popupCharacters != null) { - keyboard = new BaseKeyboard(getContext(), popupKeyboardId, popupKey.popupCharacters, - -1, getPaddingLeft() + getPaddingRight()); - } else { - keyboard = new BaseKeyboard(getContext(), popupKeyboardId); - } - miniKeyboard.setKeyboard(keyboard); - miniKeyboard.setPopupParent(this); - - container.measure(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.AT_MOST), - MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.AT_MOST)); - - return container; - } - - private static boolean isOneRowKeys(List<Key> keys) { - if (keys.size() == 0) return false; - final int edgeFlags = keys.get(0).edgeFlags; - // HACK: The first key of mini keyboard which was inflated from xml and has multiple rows, - // does not have both top and bottom edge flags on at the same time. On the other hand, - // the first key of mini keyboard that was created with popupCharacters must have both top - // and bottom edge flags on. - // When you want to use one row mini-keyboard from xml file, make sure that the row has - // both top and bottom edge flags set. - return (edgeFlags & BaseKeyboard.EDGE_TOP) != 0 - && (edgeFlags & BaseKeyboard.EDGE_BOTTOM) != 0; - } - - /** - * Called when a key is long pressed. By default this will open any popup keyboard associated - * with this key through the attributes popupLayout and popupCharacters. - * @param popupKey the key that was long pressed - * @return true if the long press is handled, false otherwise. Subclasses should call the - * method on the base class if the subclass doesn't wish to handle the call. - */ - protected boolean onLongPress(Key popupKey) { - // TODO if popupKey.popupCharacters has only one letter, send it as key without opening - // mini keyboard. - - if (popupKey.popupResId == 0) - return false; - - View container = mMiniKeyboardCache.get(popupKey); - if (container == null) { - container = inflateMiniKeyboardContainer(popupKey); - mMiniKeyboardCache.put(popupKey, container); - } - mMiniKeyboard = (BaseKeyboardView)container.findViewById(R.id.BaseKeyboardView); - if (mWindowOffset == null) { - mWindowOffset = new int[2]; - getLocationInWindow(mWindowOffset); - } - - // Get width of a key in the mini popup keyboard = "miniKeyWidth". - // On the other hand, "popupKey.width" is width of the pressed key on the main keyboard. - // We adjust the position of mini popup keyboard with the edge key in it: - // a) When we have the leftmost key in popup keyboard directly above the pressed key - // Right edges of both keys should be aligned for consistent default selection - // b) When we have the rightmost key in popup keyboard directly above the pressed key - // Left edges of both keys should be aligned for consistent default selection - final List<Key> miniKeys = mMiniKeyboard.getKeyboard().getKeys(); - final int miniKeyWidth = miniKeys.size() > 0 ? miniKeys.get(0).width : 0; - - // HACK: Have the leftmost number in the popup characters right above the key - boolean isNumberAtLeftmost = - hasMultiplePopupChars(popupKey) && isNumberAtLeftmostPopupChar(popupKey); - int popupX = popupKey.x + mWindowOffset[0]; - popupX += getPaddingLeft(); - if (isNumberAtLeftmost) { - popupX += popupKey.width - miniKeyWidth; // adjustment for a) described above - popupX -= container.getPaddingLeft(); - } else { - popupX += miniKeyWidth; // adjustment for b) described above - popupX -= container.getMeasuredWidth(); - popupX += container.getPaddingRight(); - } - int popupY = popupKey.y + mWindowOffset[1]; - popupY += getPaddingTop(); - popupY -= container.getMeasuredHeight(); - popupY += container.getPaddingBottom(); - final int x = popupX; - final int y = mShowPreview && isOneRowKeys(miniKeys) ? mPopupPreviewDisplayedY : popupY; - - int adjustedX = x; - if (x < 0) { - adjustedX = 0; - } else if (x > (getMeasuredWidth() - container.getMeasuredWidth())) { - adjustedX = getMeasuredWidth() - container.getMeasuredWidth(); - } - mMiniKeyboardOriginX = adjustedX + container.getPaddingLeft() - mWindowOffset[0]; - mMiniKeyboardOriginY = y + container.getPaddingTop() - mWindowOffset[1]; - mMiniKeyboard.setPopupOffset(adjustedX, y); - // TODO: change the below line to use getLatinKeyboard() instead of getKeyboard() - BaseKeyboard baseMiniKeyboard = mMiniKeyboard.getKeyboard(); - if (baseMiniKeyboard != null && baseMiniKeyboard.setShifted(mKeyboard == null - ? false : mKeyboard.isShiftedOrShiftLocked())) { - mMiniKeyboard.invalidateAllKeys(); - } - // Mini keyboard needs no pop-up key preview displayed. - mMiniKeyboard.setPreviewEnabled(false); - mMiniKeyboardPopup.setContentView(container); - mMiniKeyboardPopup.setWidth(container.getMeasuredWidth()); - mMiniKeyboardPopup.setHeight(container.getMeasuredHeight()); - mMiniKeyboardPopup.showAtLocation(this, Gravity.NO_GRAVITY, x, y); - - // Inject down event on the key to mini keyboard. - long eventTime = SystemClock.uptimeMillis(); - mMiniKeyboardPopupTime = eventTime; - MotionEvent downEvent = generateMiniKeyboardMotionEvent(MotionEvent.ACTION_DOWN, popupKey.x - + popupKey.width / 2, popupKey.y + popupKey.height / 2, eventTime); - mMiniKeyboard.onTouchEvent(downEvent); - downEvent.recycle(); - - invalidateAllKeys(); - return true; - } - - private static boolean hasMultiplePopupChars(Key key) { - if (key.popupCharacters != null && key.popupCharacters.length() > 1) { - return true; - } - return false; - } - - private static boolean isNumberAtLeftmostPopupChar(Key key) { - if (key.popupCharacters != null && key.popupCharacters.length() > 0 - && isAsciiDigit(key.popupCharacters.charAt(0))) { - return true; - } - return false; - } - - private static boolean isAsciiDigit(char c) { - return (c < 0x80) && Character.isDigit(c); - } - - private MotionEvent generateMiniKeyboardMotionEvent(int action, int x, int y, long eventTime) { - return MotionEvent.obtain(mMiniKeyboardPopupTime, eventTime, action, - x - mMiniKeyboardOriginX, y - mMiniKeyboardOriginY, 0); - } - - private PointerTracker getPointerTracker(final int id) { - final ArrayList<PointerTracker> pointers = mPointerTrackers; - final Key[] keys = mKeys; - final OnKeyboardActionListener listener = mKeyboardActionListener; - - // Create pointer trackers until we can get 'id+1'-th tracker, if needed. - for (int i = pointers.size(); i <= id; i++) { - final PointerTracker tracker = - new PointerTracker(i, mHandler, mKeyDetector, this, getResources()); - if (keys != null) - tracker.setKeyboard(mKeyboard, keys, mKeyHysteresisDistance); - if (listener != null) - tracker.setOnKeyboardActionListener(listener); - pointers.add(tracker); - } - - return pointers.get(id); - } - - @Override - public boolean onTouchEvent(MotionEvent me) { - final int pointerCount = me.getPointerCount(); - final int action = me.getActionMasked(); - - // TODO: cleanup this code into a multi-touch to single-touch event converter class? - // If the device does not have distinct multi-touch support panel, ignore all multi-touch - // events except a transition from/to single-touch. - if (!mHasDistinctMultitouch && pointerCount > 1 && mOldPointerCount > 1) { - return true; - } - - // Track the last few movements to look for spurious swipes. - mSwipeTracker.addMovement(me); - - // Gesture detector must be enabled only when mini-keyboard is not on the screen. - if (mMiniKeyboard == null - && mGestureDetector != null && mGestureDetector.onTouchEvent(me)) { - dismissKeyPreview(); - mHandler.cancelKeyTimers(); - return true; - } - - final long eventTime = me.getEventTime(); - final int index = me.getActionIndex(); - final int id = me.getPointerId(index); - final int x = (int)me.getX(index); - final int y = (int)me.getY(index); - - // Needs to be called after the gesture detector gets a turn, as it may have - // displayed the mini keyboard - if (mMiniKeyboard != null) { - final int miniKeyboardPointerIndex = me.findPointerIndex(mMiniKeyboardTrackerId); - if (miniKeyboardPointerIndex >= 0 && miniKeyboardPointerIndex < pointerCount) { - final int miniKeyboardX = (int)me.getX(miniKeyboardPointerIndex); - final int miniKeyboardY = (int)me.getY(miniKeyboardPointerIndex); - MotionEvent translated = generateMiniKeyboardMotionEvent(action, - miniKeyboardX, miniKeyboardY, eventTime); - mMiniKeyboard.onTouchEvent(translated); - translated.recycle(); - } - return true; - } - - if (mHandler.isInKeyRepeat()) { - // It will keep being in the key repeating mode while the key is being pressed. - if (action == MotionEvent.ACTION_MOVE) { - return true; - } - final PointerTracker tracker = getPointerTracker(id); - // Key repeating timer will be canceled if 2 or more keys are in action, and current - // event (UP or DOWN) is non-modifier key. - if (pointerCount > 1 && !tracker.isModifier()) { - mHandler.cancelKeyRepeatTimer(); - } - // Up event will pass through. - } - - // TODO: cleanup this code into a multi-touch to single-touch event converter class? - // Translate mutli-touch event to single-touch events on the device that has no distinct - // multi-touch panel. - if (!mHasDistinctMultitouch) { - // Use only main (id=0) pointer tracker. - PointerTracker tracker = getPointerTracker(0); - int oldPointerCount = mOldPointerCount; - if (pointerCount == 1 && oldPointerCount == 2) { - // Multi-touch to single touch transition. - // Send a down event for the latest pointer. - tracker.onDownEvent(x, y, eventTime); - } else if (pointerCount == 2 && oldPointerCount == 1) { - // Single-touch to multi-touch transition. - // Send an up event for the last pointer. - tracker.onUpEvent(tracker.getLastX(), tracker.getLastY(), eventTime); - } else if (pointerCount == 1 && oldPointerCount == 1) { - tracker.onTouchEvent(action, x, y, eventTime); - } else { - Log.w(TAG, "Unknown touch panel behavior: pointer count is " + pointerCount - + " (old " + oldPointerCount + ")"); - } - mOldPointerCount = pointerCount; - return true; - } - - if (action == MotionEvent.ACTION_MOVE) { - for (int i = 0; i < pointerCount; i++) { - PointerTracker tracker = getPointerTracker(me.getPointerId(i)); - tracker.onMoveEvent((int)me.getX(i), (int)me.getY(i), eventTime); - } - } else { - PointerTracker tracker = getPointerTracker(id); - switch (action) { - case MotionEvent.ACTION_DOWN: - case MotionEvent.ACTION_POINTER_DOWN: - onDownEvent(tracker, x, y, eventTime); - break; - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_POINTER_UP: - onUpEvent(tracker, x, y, eventTime); - break; - case MotionEvent.ACTION_CANCEL: - onCancelEvent(tracker, x, y, eventTime); - break; - } - } - - return true; - } - - private void onDownEvent(PointerTracker tracker, int x, int y, long eventTime) { - if (tracker.isOnModifierKey(x, y)) { - // Before processing a down event of modifier key, all pointers already being tracked - // should be released. - mPointerQueue.releaseAllPointersExcept(null, eventTime); - } - tracker.onDownEvent(x, y, eventTime); - mPointerQueue.add(tracker); - } - - private void onUpEvent(PointerTracker tracker, int x, int y, long eventTime) { - if (tracker.isModifier()) { - // Before processing an up event of modifier key, all pointers already being tracked - // should be released. - mPointerQueue.releaseAllPointersExcept(tracker, eventTime); - } else { - int index = mPointerQueue.lastIndexOf(tracker); - if (index >= 0) { - mPointerQueue.releaseAllPointersOlderThan(tracker, eventTime); - } else { - Log.w(TAG, "onUpEvent: corresponding down event not found for pointer " - + tracker.mPointerId); - } - } - tracker.onUpEvent(x, y, eventTime); - mPointerQueue.remove(tracker); - } - - private void onCancelEvent(PointerTracker tracker, int x, int y, long eventTime) { - tracker.onCancelEvent(x, y, eventTime); - mPointerQueue.remove(tracker); - } - - protected void swipeRight() { - mKeyboardActionListener.swipeRight(); - } - - protected void swipeLeft() { - mKeyboardActionListener.swipeLeft(); - } - - protected void swipeUp() { - mKeyboardActionListener.swipeUp(); - } - - protected void swipeDown() { - mKeyboardActionListener.swipeDown(); - } - - public void closing() { - mPreviewPopup.dismiss(); - mHandler.cancelAllMessages(); - - dismissPopupKeyboard(); - mBuffer = null; - mCanvas = null; - mMiniKeyboardCache.clear(); - } - - @Override - public void onDetachedFromWindow() { - super.onDetachedFromWindow(); - closing(); - } - - private void dismissPopupKeyboard() { - if (mMiniKeyboardPopup.isShowing()) { - mMiniKeyboardPopup.dismiss(); - mMiniKeyboard = null; - mMiniKeyboardOriginX = 0; - mMiniKeyboardOriginY = 0; - invalidateAllKeys(); - } - } - - public boolean handleBack() { - if (mMiniKeyboardPopup.isShowing()) { - dismissPopupKeyboard(); - return true; - } - return false; - } -} diff --git a/java/src/com/android/inputmethod/latin/InputLanguageSelection.java b/java/src/com/android/inputmethod/latin/InputLanguageSelection.java index fc517aeb7..ad3f1db9b 100644 --- a/java/src/com/android/inputmethod/latin/InputLanguageSelection.java +++ b/java/src/com/android/inputmethod/latin/InputLanguageSelection.java @@ -57,6 +57,7 @@ public class InputLanguageSelection extends PreferenceActivity { return this.label; } + @Override public int compareTo(Object o) { return sCollator.compare(this.label, ((Loc) o).label); } diff --git a/java/src/com/android/inputmethod/latin/KeyDetector.java b/java/src/com/android/inputmethod/latin/KeyDetector.java deleted file mode 100644 index 600a12fe5..000000000 --- a/java/src/com/android/inputmethod/latin/KeyDetector.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * 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.latin; - -import com.android.inputmethod.latin.BaseKeyboard.Key; - -import java.util.Arrays; -import java.util.List; - -abstract class KeyDetector { - protected BaseKeyboard mKeyboard; - - private Key[] mKeys; - - protected int mCorrectionX; - - protected int mCorrectionY; - - protected boolean mProximityCorrectOn; - - protected int mProximityThresholdSquare; - - public Key[] setKeyboard(BaseKeyboard keyboard, float correctionX, float correctionY) { - if (keyboard == null) - throw new NullPointerException(); - mCorrectionX = (int)correctionX; - mCorrectionY = (int)correctionY; - mKeyboard = keyboard; - List<Key> keys = mKeyboard.getKeys(); - Key[] array = keys.toArray(new Key[keys.size()]); - mKeys = array; - return array; - } - - protected int getTouchX(int x) { - return x + mCorrectionX; - } - - protected int getTouchY(int y) { - return y + mCorrectionY; - } - - protected Key[] getKeys() { - if (mKeys == null) - throw new IllegalStateException("keyboard isn't set"); - // mKeyboard is guaranteed not to be null at setKeybaord() method if mKeys is not null - return mKeys; - } - - public void setProximityCorrectionEnabled(boolean enabled) { - mProximityCorrectOn = enabled; - } - - public boolean isProximityCorrectionEnabled() { - return mProximityCorrectOn; - } - - public void setProximityThreshold(int threshold) { - mProximityThresholdSquare = threshold * threshold; - } - - /** - * Allocates array that can hold all key indices returned by {@link #getKeyIndexAndNearbyCodes} - * method. The maximum size of the array should be computed by {@link #getMaxNearbyKeys}. - * - * @return Allocates and returns an array that can hold all key indices returned by - * {@link #getKeyIndexAndNearbyCodes} method. All elements in the returned array are - * initialized by {@link com.android.inputmethod.latin.LatinKeyboardView.NOT_A_KEY} - * value. - */ - public int[] newCodeArray() { - int[] codes = new int[getMaxNearbyKeys()]; - Arrays.fill(codes, BaseKeyboardView.NOT_A_KEY); - return codes; - } - - /** - * Computes maximum size of the array that can contain all nearby key indices returned by - * {@link #getKeyIndexAndNearbyCodes}. - * - * @return Returns maximum size of the array that can contain all nearby key indices returned - * by {@link #getKeyIndexAndNearbyCodes}. - */ - abstract protected int getMaxNearbyKeys(); - - /** - * Finds all possible nearby key indices around a touch event point and returns the nearest key - * index. The algorithm to determine the nearby keys depends on the threshold set by - * {@link #setProximityThreshold(int)} and the mode set by - * {@link #setProximityCorrectionEnabled(boolean)}. - * - * @param x The x-coordinate of a touch point - * @param y The y-coordinate of a touch point - * @param allKeys All nearby key indices are returned in this array - * @return The nearest key index - */ - abstract public int getKeyIndexAndNearbyCodes(int x, int y, int[] allKeys); -} diff --git a/java/src/com/android/inputmethod/latin/KeyStyles.java b/java/src/com/android/inputmethod/latin/KeyStyles.java deleted file mode 100644 index fceede7c3..000000000 --- a/java/src/com/android/inputmethod/latin/KeyStyles.java +++ /dev/null @@ -1,238 +0,0 @@ -/* - * 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.latin; - -import com.android.inputmethod.latin.BaseKeyboardParser.ParseException; - -import android.content.res.TypedArray; -import android.content.res.XmlResourceParser; -import android.graphics.drawable.Drawable; -import android.util.Log; -import android.util.TypedValue; - -import java.util.HashMap; -import java.util.StringTokenizer; - -public class KeyStyles { - private static final String TAG = "KeyStyles"; - - private final HashMap<String, DeclaredKeyStyle> mStyles = - new HashMap<String, DeclaredKeyStyle>(); - private static final KeyStyle EMPTY_KEY_STYLE = new EmptyKeyStyle(); - - public interface KeyStyle { - public int[] getIntArray(TypedArray a, int index); - public Drawable getDrawable(TypedArray a, int index); - public CharSequence getText(TypedArray a, int index); - public int getResourceId(TypedArray a, int index, int defaultValue); - public int getInt(TypedArray a, int index, int defaultValue); - public int getFlag(TypedArray a, int index, int defaultValue); - public boolean getBoolean(TypedArray a, int index, boolean defaultValue); - } - - public static class EmptyKeyStyle implements KeyStyle { - private EmptyKeyStyle() { - } - - public int[] getIntArray(TypedArray a, int index) { - return parseIntArray(a, index); - } - - public Drawable getDrawable(TypedArray a, int index) { - return a.getDrawable(index); - } - - public CharSequence getText(TypedArray a, int index) { - return a.getText(index); - } - - public int getResourceId(TypedArray a, int index, int defaultValue) { - return a.getResourceId(index, defaultValue); - } - - public int getInt(TypedArray a, int index, int defaultValue) { - return a.getInt(index, defaultValue); - } - - public int getFlag(TypedArray a, int index, int defaultValue) { - return a.getInt(index, defaultValue); - } - - public boolean getBoolean(TypedArray a, int index, boolean defaultValue) { - return a.getBoolean(index, defaultValue); - } - - protected static int[] parseIntArray(TypedArray a, int index) { - TypedValue v = new TypedValue(); - a.getValue(index, v); - if (v.type == TypedValue.TYPE_INT_DEC || v.type == TypedValue.TYPE_INT_HEX) { - return new int[] { v.data }; - } else if (v.type == TypedValue.TYPE_STRING) { - return parseCSV(v.string.toString()); - } else { - return null; - } - } - - private static int[] parseCSV(String value) { - int count = 0; - int lastIndex = 0; - if (value.length() > 0) { - count++; - while ((lastIndex = value.indexOf(",", lastIndex + 1)) > 0) { - count++; - } - } - int[] values = new int[count]; - count = 0; - StringTokenizer st = new StringTokenizer(value, ","); - while (st.hasMoreTokens()) { - try { - values[count++] = Integer.parseInt(st.nextToken()); - } catch (NumberFormatException nfe) { - Log.w(TAG, "Error parsing integer CSV " + value); - } - } - return values; - } - } - - public static class DeclaredKeyStyle extends EmptyKeyStyle { - private final HashMap<Integer, Object> mAttributes = new HashMap<Integer, Object>(); - - @Override - public int[] getIntArray(TypedArray a, int index) { - return a.hasValue(index) - ? super.getIntArray(a, index) : (int[])mAttributes.get(index); - } - - @Override - public Drawable getDrawable(TypedArray a, int index) { - return a.hasValue(index) - ? super.getDrawable(a, index) : (Drawable)mAttributes.get(index); - } - - @Override - public CharSequence getText(TypedArray a, int index) { - return a.hasValue(index) - ? super.getText(a, index) : (CharSequence)mAttributes.get(index); - } - - @Override - public int getResourceId(TypedArray a, int index, int defaultValue) { - final Integer value = (Integer)mAttributes.get(index); - return super.getResourceId(a, index, (value != null) ? value : defaultValue); - } - - @Override - public int getFlag(TypedArray a, int index, int defaultValue) { - final Integer value = (Integer)mAttributes.get(index); - return super.getFlag(a, index, defaultValue) | (value != null ? value : 0); - } - - @Override - public boolean getBoolean(TypedArray a, int index, boolean defaultValue) { - final Boolean value = (Boolean)mAttributes.get(index); - return super.getBoolean(a, index, (value != null) ? value : defaultValue); - } - - private DeclaredKeyStyle() { - super(); - } - - private void parseKeyStyleAttributes(TypedArray a) { - // TODO: Currently not all Key attributes can be declared as style. - readIntArray(a, R.styleable.BaseKeyboard_Key_codes); - readText(a, R.styleable.BaseKeyboard_Key_keyLabel); - readFlag(a, R.styleable.BaseKeyboard_Key_keyLabelOption); - readText(a, R.styleable.BaseKeyboard_Key_keyOutputText); - readDrawable(a, R.styleable.BaseKeyboard_Key_keyIcon); - readDrawable(a, R.styleable.BaseKeyboard_Key_iconPreview); - readDrawable(a, R.styleable.BaseKeyboard_Key_keyHintIcon); - readDrawable(a, R.styleable.BaseKeyboard_Key_shiftedIcon); - readResourceId(a, R.styleable.BaseKeyboard_Key_popupKeyboard); - readBoolean(a, R.styleable.BaseKeyboard_Key_isModifier); - readBoolean(a, R.styleable.BaseKeyboard_Key_isSticky); - readBoolean(a, R.styleable.BaseKeyboard_Key_isRepeatable); - } - - private void readDrawable(TypedArray a, int index) { - if (a.hasValue(index)) - mAttributes.put(index, a.getDrawable(index)); - } - - private void readText(TypedArray a, int index) { - if (a.hasValue(index)) - mAttributes.put(index, a.getText(index)); - } - - private void readResourceId(TypedArray a, int index) { - if (a.hasValue(index)) - mAttributes.put(index, a.getResourceId(index, 0)); - } - - private void readFlag(TypedArray a, int index) { - final Integer value = (Integer)mAttributes.get(index); - if (a.hasValue(index)) - mAttributes.put(index, a.getInt(index, 0) | (value != null ? value : 0)); - } - - private void readBoolean(TypedArray a, int index) { - if (a.hasValue(index)) - mAttributes.put(index, a.getBoolean(index, false)); - } - - private void readIntArray(TypedArray a, int index) { - if (a.hasValue(index)) { - final int[] value = parseIntArray(a, index); - if (value != null) - mAttributes.put(index, value); - } - } - - private void addParent(DeclaredKeyStyle parentStyle) { - mAttributes.putAll(parentStyle.mAttributes); - } - } - - public void parseKeyStyleAttributes(TypedArray a, TypedArray keyAttrs, - XmlResourceParser parser) { - String styleName = a.getString(R.styleable.BaseKeyboard_KeyStyle_styleName); - if (mStyles.containsKey(styleName)) - throw new ParseException("duplicate key style declared: " + styleName, parser); - - final DeclaredKeyStyle style = new DeclaredKeyStyle(); - if (a.hasValue(R.styleable.BaseKeyboard_KeyStyle_parentStyle)) { - String parentStyle = a.getString( - R.styleable.BaseKeyboard_KeyStyle_parentStyle); - final DeclaredKeyStyle parent = mStyles.get(parentStyle); - if (parent == null) - throw new ParseException("Unknown parentStyle " + parent, parser); - style.addParent(parent); - } - style.parseKeyStyleAttributes(keyAttrs); - mStyles.put(styleName, style); - } - - public KeyStyle getKeyStyle(String styleName) { - return mStyles.get(styleName); - } - - public KeyStyle getEmptyKeyStyle() { - return EMPTY_KEY_STYLE; - } -} diff --git a/java/src/com/android/inputmethod/latin/KeyboardSwitcher.java b/java/src/com/android/inputmethod/latin/KeyboardSwitcher.java index 00b6f0a43..e8487e798 100644 --- a/java/src/com/android/inputmethod/latin/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/latin/KeyboardSwitcher.java @@ -16,16 +16,19 @@ package com.android.inputmethod.latin; +import com.android.inputmethod.keyboard.KeyboardView; +import com.android.inputmethod.keyboard.KeyboardId; +import com.android.inputmethod.keyboard.LatinKeyboard; +import com.android.inputmethod.keyboard.LatinKeyboardView; + import android.content.Context; import android.content.SharedPreferences; import android.content.res.Resources; import android.util.Log; import android.view.InflateException; -import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodManager; import java.lang.ref.SoftReference; -import java.util.Arrays; import java.util.HashMap; import java.util.Locale; @@ -34,14 +37,6 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha private static final boolean DEBUG = false; public static final boolean DEBUG_STATE = false; - public static final int MODE_TEXT = 0; - public static final int MODE_URL = 1; - public static final int MODE_EMAIL = 2; - public static final int MODE_IM = 3; - public static final int MODE_WEB = 4; - public static final int MODE_PHONE = 5; - public static final int MODE_NUMBER = 6; - // Changing DEFAULT_LAYOUT_ID also requires prefs_for_debug.xml to be matched with. public static final String DEFAULT_LAYOUT_ID = "5"; public static final String PREF_KEYBOARD_LAYOUT = "pref_keyboard_layout_20100902"; @@ -74,7 +69,7 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha private final HashMap<KeyboardId, SoftReference<LatinKeyboard>> mKeyboardCache = new HashMap<KeyboardId, SoftReference<LatinKeyboard>>(); - private int mMode = MODE_TEXT; /* default value */ + private int mMode = KeyboardId.MODE_TEXT; /* default value */ private int mImeOptions; private boolean mIsSymbols; /** mIsAutoCompletionActive indicates that auto completed word will be input instead of @@ -132,129 +127,13 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha // mSymbolsId and mSymbolsShiftedId to "phone keyboard" and "phone symbols keyboard" // respectively here for xlarge device's layout switching. mSymbolsId = new KeyboardId(locale, orientation, mode, - mode == MODE_PHONE ? R.xml.kbd_phone : R.xml.kbd_symbols, + mode == KeyboardId.MODE_PHONE ? R.xml.kbd_phone : R.xml.kbd_symbols, colorScheme, hasSettingsKey, voiceKeyEnabled, hasVoiceKey, imeOptions, true); mSymbolsShiftedId = new KeyboardId(locale, orientation, mode, - mode == MODE_PHONE ? R.xml.kbd_phone_symbols : R.xml.kbd_symbols_shift, + mode == KeyboardId.MODE_PHONE ? R.xml.kbd_phone_symbols : R.xml.kbd_symbols_shift, colorScheme, hasSettingsKey, voiceKeyEnabled, hasVoiceKey, imeOptions, true); } - /** - * Represents the parameters necessary to construct a new LatinKeyboard, - * which also serve as a unique identifier for each keyboard type. - */ - public static class KeyboardId { - public final Locale mLocale; - public final int mOrientation; - public final int mMode; - public final int mXmlId; - public final int mColorScheme; - public final boolean mHasSettingsKey; - public final boolean mVoiceKeyEnabled; - public final boolean mHasVoiceKey; - public final int mImeOptions; - public final boolean mEnableShiftLock; - - private final int mHashCode; - - public KeyboardId(Locale locale, int orientation, int mode, - int xmlId, int colorScheme, boolean hasSettingsKey, boolean voiceKeyEnabled, - boolean hasVoiceKey, int imeOptions, boolean enableShiftLock) { - this.mLocale = locale; - this.mOrientation = orientation; - this.mMode = mode; - this.mXmlId = xmlId; - this.mColorScheme = colorScheme; - this.mHasSettingsKey = hasSettingsKey; - this.mVoiceKeyEnabled = voiceKeyEnabled; - this.mHasVoiceKey = hasVoiceKey; - // We are interested only in IME_MASK_ACTION enum value and IME_FLAG_NO_ENTER_ACTION. - this.mImeOptions = imeOptions - & (EditorInfo.IME_MASK_ACTION | EditorInfo.IME_FLAG_NO_ENTER_ACTION); - this.mEnableShiftLock = enableShiftLock; - - this.mHashCode = Arrays.hashCode(new Object[] { - locale, - orientation, - mode, - xmlId, - colorScheme, - hasSettingsKey, - voiceKeyEnabled, - hasVoiceKey, - imeOptions, - enableShiftLock, - }); - } - - public int getXmlId() { - return mXmlId; - } - - public boolean isAlphabetMode() { - return mXmlId == R.xml.kbd_qwerty; - } - - @Override - public boolean equals(Object other) { - return other instanceof KeyboardId && equals((KeyboardId) other); - } - - private boolean equals(KeyboardId other) { - return other.mLocale.equals(this.mLocale) - && other.mOrientation == this.mOrientation - && other.mMode == this.mMode - && other.mXmlId == this.mXmlId - && other.mColorScheme == this.mColorScheme - && other.mHasSettingsKey == this.mHasSettingsKey - && other.mVoiceKeyEnabled == this.mVoiceKeyEnabled - && other.mHasVoiceKey == this.mHasVoiceKey - && other.mImeOptions == this.mImeOptions - && other.mEnableShiftLock == this.mEnableShiftLock; - } - - @Override - public int hashCode() { - return mHashCode; - } - - @Override - public String toString() { - return String.format("[%s %s %5s imeOptions=0x%08x xml=0x%08x %s%s%s%s%s]", - mLocale, - (mOrientation == 1 ? "port" : "land"), - modeName(mMode), - mImeOptions, - mXmlId, - colorSchemeName(mColorScheme), - (mHasSettingsKey ? " hasSettingsKey" : ""), - (mVoiceKeyEnabled ? " voiceKeyEnabled" : ""), - (mHasVoiceKey ? " hasVoiceKey" : ""), - (mEnableShiftLock ? " enableShiftLock" : "")); - } - - private static String modeName(int mode) { - switch (mode) { - case MODE_TEXT: return "text"; - case MODE_URL: return "url"; - case MODE_EMAIL: return "email"; - case MODE_IM: return "im"; - case MODE_WEB: return "web"; - case MODE_PHONE: return "phone"; - case MODE_NUMBER: return "number"; - } - return null; - } - - private static String colorSchemeName(int colorScheme) { - switch (colorScheme) { - case BaseKeyboardView.COLOR_SCHEME_WHITE: return "white"; - case BaseKeyboardView.COLOR_SCHEME_BLACK: return "black"; - } - return null; - } - } - private boolean hasVoiceKey(boolean isSymbols) { return mVoiceKeyEnabled && (isSymbols != mVoiceButtonOnPrimary); } @@ -327,9 +206,9 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha final boolean enableShiftLock; if (isSymbols) { - if (mode == MODE_PHONE) { + if (mode == KeyboardId.MODE_PHONE) { xmlId = R.xml.kbd_phone_symbols; - } else if (mode == MODE_NUMBER) { + } else if (mode == KeyboardId.MODE_NUMBER) { // Note: MODE_NUMBER keyboard layout has no "switch alpha symbol" key. xmlId = R.xml.kbd_number; } else { @@ -337,10 +216,10 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha } enableShiftLock = false; } else { - if (mode == MODE_PHONE) { + if (mode == KeyboardId.MODE_PHONE) { xmlId = R.xml.kbd_phone; enableShiftLock = false; - } else if (mode == MODE_NUMBER) { + } else if (mode == KeyboardId.MODE_NUMBER) { xmlId = R.xml.kbd_number; enableShiftLock = false; } else { @@ -731,7 +610,7 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha private int getColorScheme() { return (mInputView != null) - ? mInputView.getColorScheme() : BaseKeyboardView.COLOR_SCHEME_WHITE; + ? mInputView.getColorScheme() : KeyboardView.COLOR_SCHEME_WHITE; } public void onAutoCompletionStateChanged(boolean isAutoCompletion) { diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 36c77efaf..4f4112fa8 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -16,6 +16,12 @@ package com.android.inputmethod.latin; +import com.android.inputmethod.keyboard.Keyboard; +import com.android.inputmethod.keyboard.KeyboardActionListener; +import com.android.inputmethod.keyboard.KeyboardId; +import com.android.inputmethod.keyboard.KeyboardView; +import com.android.inputmethod.keyboard.LatinKeyboard; +import com.android.inputmethod.keyboard.LatinKeyboardView; import com.android.inputmethod.latin.LatinIMEUtil.RingCharBuffer; import com.android.inputmethod.voice.VoiceIMEConnector; @@ -73,7 +79,7 @@ import java.util.Locale; * Input method implementation for Qwerty'ish keyboard. */ public class LatinIME extends InputMethodService - implements BaseKeyboardView.OnKeyboardActionListener, + implements KeyboardActionListener, SharedPreferences.OnSharedPreferenceChangeListener, Tutorial.TutorialListener { private static final String TAG = "LatinIME"; @@ -548,10 +554,10 @@ public class LatinIME extends InputMethodService switch (attribute.inputType & EditorInfo.TYPE_MASK_CLASS) { case EditorInfo.TYPE_CLASS_NUMBER: case EditorInfo.TYPE_CLASS_DATETIME: - mode = KeyboardSwitcher.MODE_NUMBER; + mode = KeyboardId.MODE_NUMBER; break; case EditorInfo.TYPE_CLASS_PHONE: - mode = KeyboardSwitcher.MODE_PHONE; + mode = KeyboardId.MODE_PHONE; break; case EditorInfo.TYPE_CLASS_TEXT: //startPrediction(); @@ -568,24 +574,24 @@ public class LatinIME extends InputMethodService } if (isEmailVariation(variation)) { mPredictionOn = false; - mode = KeyboardSwitcher.MODE_EMAIL; + mode = KeyboardId.MODE_EMAIL; } else if (variation == EditorInfo.TYPE_TEXT_VARIATION_URI) { mPredictionOn = false; - mode = KeyboardSwitcher.MODE_URL; + mode = KeyboardId.MODE_URL; } else if (variation == EditorInfo.TYPE_TEXT_VARIATION_SHORT_MESSAGE) { - mode = KeyboardSwitcher.MODE_IM; + mode = KeyboardId.MODE_IM; } else if (variation == EditorInfo.TYPE_TEXT_VARIATION_FILTER) { mPredictionOn = false; - mode = KeyboardSwitcher.MODE_TEXT; + mode = KeyboardId.MODE_TEXT; } else if (variation == EditorInfo.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT) { - mode = KeyboardSwitcher.MODE_WEB; + mode = KeyboardId.MODE_WEB; // If it's a browser edit field and auto correct is not ON explicitly, then // disable auto correction, but keep suggestions on. if ((attribute.inputType & EditorInfo.TYPE_TEXT_FLAG_AUTO_CORRECT) == 0) { mInputTypeNoAutoCorrect = true; } } else { - mode = KeyboardSwitcher.MODE_TEXT; + mode = KeyboardId.MODE_TEXT; } // If NO_SUGGESTIONS is set, don't do prediction. @@ -604,7 +610,7 @@ public class LatinIME extends InputMethodService } break; default: - mode = KeyboardSwitcher.MODE_TEXT; + mode = KeyboardId.MODE_TEXT; break; } inputView.closing(); @@ -675,7 +681,7 @@ public class LatinIME extends InputMethodService mVoiceConnector.flushVoiceInputLogs(mConfigurationChanging); - BaseKeyboardView inputView = mKeyboardSwitcher.getInputView(); + KeyboardView inputView = mKeyboardSwitcher.getInputView(); if (inputView != null) inputView.closing(); if (mAutoDictionary != null) mAutoDictionary.flushPendingWrites(); @@ -685,7 +691,7 @@ public class LatinIME extends InputMethodService @Override public void onFinishInputView(boolean finishingInput) { super.onFinishInputView(finishingInput); - BaseKeyboardView inputView = mKeyboardSwitcher.getInputView(); + KeyboardView inputView = mKeyboardSwitcher.getInputView(); if (inputView != null) inputView.setForeground(false); // Remove pending messages related to update suggestions @@ -1073,51 +1079,52 @@ public class LatinIME extends InputMethodService // Implementation of KeyboardViewListener + @Override public void onKey(int primaryCode, int[] keyCodes, int x, int y) { long when = SystemClock.uptimeMillis(); - if (primaryCode != BaseKeyboard.KEYCODE_DELETE || when > mLastKeyTime + QUICK_PRESS) { + if (primaryCode != Keyboard.KEYCODE_DELETE || when > mLastKeyTime + QUICK_PRESS) { mDeleteCount = 0; } mLastKeyTime = when; KeyboardSwitcher switcher = mKeyboardSwitcher; final boolean distinctMultiTouch = switcher.hasDistinctMultitouch(); switch (primaryCode) { - case BaseKeyboard.KEYCODE_DELETE: + case Keyboard.KEYCODE_DELETE: handleBackspace(); mDeleteCount++; LatinImeLogger.logOnDelete(); break; - case BaseKeyboard.KEYCODE_SHIFT: + case Keyboard.KEYCODE_SHIFT: // Shift key is handled in onPress() when device has distinct multi-touch panel. if (!distinctMultiTouch) switcher.toggleShift(); break; - case BaseKeyboard.KEYCODE_MODE_CHANGE: + case Keyboard.KEYCODE_MODE_CHANGE: // Symbol key is handled in onPress() when device has distinct multi-touch panel. if (!distinctMultiTouch) switcher.changeKeyboardMode(); break; - case BaseKeyboard.KEYCODE_CANCEL: + case Keyboard.KEYCODE_CANCEL: if (!isShowingOptionDialog()) { handleClose(); } break; - case LatinKeyboardView.KEYCODE_OPTIONS: + case LatinKeyboard.KEYCODE_OPTIONS: onOptionKeyPressed(); break; - case LatinKeyboardView.KEYCODE_OPTIONS_LONGPRESS: + case LatinKeyboard.KEYCODE_OPTIONS_LONGPRESS: onOptionKeyLongPressed(); break; - case LatinKeyboardView.KEYCODE_NEXT_LANGUAGE: + case LatinKeyboard.KEYCODE_NEXT_LANGUAGE: toggleLanguage(false, true); break; - case LatinKeyboardView.KEYCODE_PREV_LANGUAGE: + case LatinKeyboard.KEYCODE_PREV_LANGUAGE: toggleLanguage(false, false); break; - case LatinKeyboardView.KEYCODE_CAPSLOCK: + case LatinKeyboard.KEYCODE_CAPSLOCK: switcher.toggleCapsLock(); break; - case LatinKeyboardView.KEYCODE_VOICE: /* was a button press, was not a swipe */ + case LatinKeyboard.KEYCODE_VOICE: /* was a button press, was not a swipe */ mVoiceConnector.startListening(false, mKeyboardSwitcher.getInputView().getWindowToken(), mConfigurationChanging); break; @@ -1143,6 +1150,7 @@ public class LatinIME extends InputMethodService mEnteredText = null; } + @Override public void onText(CharSequence text) { mVoiceConnector.commitVoiceInput(); InputConnection ic = getCurrentInputConnection(); @@ -1161,6 +1169,7 @@ public class LatinIME extends InputMethodService mEnteredText = text; } + @Override public void onCancel() { // User released a finger outside any key } @@ -1420,24 +1429,26 @@ public class LatinIME extends InputMethodService } public void switchToKeyboardView() { - mHandler.post(new Runnable() { - public void run() { - if (DEBUG) { - Log.d(TAG, "Switch to keyboard view."); - } - View v = mKeyboardSwitcher.getInputView(); - if (v != null) { - // Confirms that the keyboard view doesn't have parent view. - ViewParent p = v.getParent(); - if (p != null && p instanceof ViewGroup) { - ((ViewGroup)p).removeView(v); - } - setInputView(v); - } - setCandidatesViewShown(isCandidateStripVisible()); - updateInputViewShown(); - mHandler.postUpdateSuggestions(); - }}); + mHandler.post(new Runnable() { + @Override + public void run() { + if (DEBUG) { + Log.d(TAG, "Switch to keyboard view."); + } + View v = mKeyboardSwitcher.getInputView(); + if (v != null) { + // Confirms that the keyboard view doesn't have parent view. + ViewParent p = v.getParent(); + if (p != null && p instanceof ViewGroup) { + ((ViewGroup) p).removeView(v); + } + setInputView(v); + } + setCandidatesViewShown(isCandidateStripVisible()); + updateInputViewShown(); + mHandler.postUpdateSuggestions(); + } + }); } public void clearSuggestions() { @@ -1586,8 +1597,8 @@ public class LatinIME extends InputMethodService LatinImeLogger.logOnManualSuggestion( "", suggestion.toString(), index, suggestions); final char primaryCode = suggestion.charAt(0); - onKey(primaryCode, new int[]{primaryCode}, BaseKeyboardView.NOT_A_TOUCH_COORDINATE, - BaseKeyboardView.NOT_A_TOUCH_COORDINATE); + onKey(primaryCode, new int[]{primaryCode}, KeyboardView.NOT_A_TOUCH_COORDINATE, + KeyboardView.NOT_A_TOUCH_COORDINATE); if (ic != null) { ic.endBatchEdit(); } @@ -1877,6 +1888,7 @@ public class LatinIME extends InputMethodService switcher.updateShiftState(); } + @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { mSubtypeSwitcher.onSharedPreferenceChanged(sharedPreferences, key); @@ -1888,6 +1900,7 @@ public class LatinIME extends InputMethodService } } + @Override public void swipeRight() { if (LatinKeyboardView.DEBUG_AUTO_PLAY) { CharSequence text = ((android.text.ClipboardManager)getSystemService( @@ -1898,38 +1911,43 @@ public class LatinIME extends InputMethodService } } + @Override public void swipeLeft() { } + @Override public void swipeDown() { handleClose(); } + @Override public void swipeUp() { } + @Override public void onPress(int primaryCode) { vibrate(); playKeyClick(primaryCode); KeyboardSwitcher switcher = mKeyboardSwitcher; final boolean distinctMultiTouch = switcher.hasDistinctMultitouch(); - if (distinctMultiTouch && primaryCode == BaseKeyboard.KEYCODE_SHIFT) { + if (distinctMultiTouch && primaryCode == Keyboard.KEYCODE_SHIFT) { switcher.onPressShift(); - } else if (distinctMultiTouch && primaryCode == BaseKeyboard.KEYCODE_MODE_CHANGE) { + } else if (distinctMultiTouch && primaryCode == Keyboard.KEYCODE_MODE_CHANGE) { switcher.onPressSymbol(); } else { switcher.onOtherKeyPressed(); } } + @Override public void onRelease(int primaryCode) { KeyboardSwitcher switcher = mKeyboardSwitcher; // Reset any drag flags in the keyboard switcher.keyReleased(); final boolean distinctMultiTouch = switcher.hasDistinctMultitouch(); - if (distinctMultiTouch && primaryCode == BaseKeyboard.KEYCODE_SHIFT) { + if (distinctMultiTouch && primaryCode == Keyboard.KEYCODE_SHIFT) { switcher.onReleaseShift(); - } else if (distinctMultiTouch && primaryCode == BaseKeyboard.KEYCODE_MODE_CHANGE) { + } else if (distinctMultiTouch && primaryCode == Keyboard.KEYCODE_MODE_CHANGE) { switcher.onReleaseSymbol(); } } @@ -1966,7 +1984,7 @@ public class LatinIME extends InputMethodService // FIXME: These should be triggered after auto-repeat logic int sound = AudioManager.FX_KEYPRESS_STANDARD; switch (primaryCode) { - case BaseKeyboard.KEYCODE_DELETE: + case Keyboard.KEYCODE_DELETE: sound = AudioManager.FX_KEYPRESS_DELETE; break; case KEYCODE_ENTER: @@ -2006,6 +2024,7 @@ public class LatinIME extends InputMethodService } // Tutorial.TutorialListener + @Override public void onTutorialDone() { sendDownUpKeyEvents(-1); // Inform the setupwizard that tutorial is in last bubble mTutorial = null; @@ -2171,6 +2190,7 @@ public class LatinIME extends InputMethodService itemInputMethod, itemSettings}, new DialogInterface.OnClickListener() { + @Override public void onClick(DialogInterface di, int position) { di.dismiss(); switch (position) { diff --git a/java/src/com/android/inputmethod/latin/LatinIMEDebugSettings.java b/java/src/com/android/inputmethod/latin/LatinIMEDebugSettings.java index cba1a0af9..68738eca1 100644 --- a/java/src/com/android/inputmethod/latin/LatinIMEDebugSettings.java +++ b/java/src/com/android/inputmethod/latin/LatinIMEDebugSettings.java @@ -43,6 +43,7 @@ public class LatinIMEDebugSettings extends PreferenceActivity updateDebugMode(); } + @Override public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { if (key.equals(DEBUG_MODE_KEY)) { if (mDebugMode != null) { diff --git a/java/src/com/android/inputmethod/latin/LatinIMESettings.java b/java/src/com/android/inputmethod/latin/LatinIMESettings.java index ae6646113..187b6d394 100644 --- a/java/src/com/android/inputmethod/latin/LatinIMESettings.java +++ b/java/src/com/android/inputmethod/latin/LatinIMESettings.java @@ -137,6 +137,7 @@ public class LatinIMESettings extends PreferenceActivity super.onDestroy(); } + @Override public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { (new BackupManager(this)).dataChanged(); // If turning on voice input, show dialog @@ -181,6 +182,7 @@ public class LatinIMESettings extends PreferenceActivity switch (id) { case VOICE_INPUT_CONFIRM_DIALOG: DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() { + @Override public void onClick(DialogInterface dialog, int whichButton) { if (whichButton == DialogInterface.BUTTON_NEGATIVE) { mVoicePreference.setValue(mVoiceModeOff); @@ -226,6 +228,7 @@ public class LatinIMESettings extends PreferenceActivity } } + @Override public void onDismiss(DialogInterface dialog) { mLogger.settingsWarningDialogDismissed(); if (!mOkClicked) { diff --git a/java/src/com/android/inputmethod/latin/LatinIMEUtil.java b/java/src/com/android/inputmethod/latin/LatinIMEUtil.java index a58f630cd..f508b9ab8 100644 --- a/java/src/com/android/inputmethod/latin/LatinIMEUtil.java +++ b/java/src/com/android/inputmethod/latin/LatinIMEUtil.java @@ -310,6 +310,7 @@ public class LatinIMEUtil { public void write(final String log) { mLoggingHandler.post(new Runnable() { + @Override public void run() { createLogFileIfNotExist(); final long currentTime = System.currentTimeMillis(); @@ -327,6 +328,7 @@ public class LatinIMEUtil { public void printAll() { mLoggingHandler.post(new Runnable() { + @Override public void run() { mWriter.flush(); StringBuilder sb = new StringBuilder(); @@ -355,6 +357,7 @@ public class LatinIMEUtil { public void clearAll() { mLoggingHandler.post(new Runnable() { + @Override public void run() { if (mFile != null && mFile.exists()) { if (LatinImeLogger.sDBG) { diff --git a/java/src/com/android/inputmethod/latin/LatinImeLogger.java b/java/src/com/android/inputmethod/latin/LatinImeLogger.java index b64e3ddb2..de194d21b 100644 --- a/java/src/com/android/inputmethod/latin/LatinImeLogger.java +++ b/java/src/com/android/inputmethod/latin/LatinImeLogger.java @@ -16,6 +16,7 @@ package com.android.inputmethod.latin; +import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.latin.Dictionary.DataType; import android.content.Context; @@ -27,6 +28,7 @@ public class LatinImeLogger implements SharedPreferences.OnSharedPreferenceChang public static boolean sDBG = false; + @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { } @@ -67,7 +69,7 @@ public class LatinImeLogger implements SharedPreferences.OnSharedPreferenceChang public static void onAddSuggestedWord(String word, int typeId, DataType dataType) { } - public static void onSetKeyboard(BaseKeyboard kb) { + public static void onSetKeyboard(Keyboard kb) { } public static void onPrintAllUsabilityStudtyLogs() { diff --git a/java/src/com/android/inputmethod/latin/LatinKeyboard.java b/java/src/com/android/inputmethod/latin/LatinKeyboard.java deleted file mode 100644 index cae0b10b3..000000000 --- a/java/src/com/android/inputmethod/latin/LatinKeyboard.java +++ /dev/null @@ -1,720 +0,0 @@ -/* - * Copyright (C) 2008 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.latin; - -import android.content.Context; -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.content.res.XmlResourceParser; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.ColorFilter; -import android.graphics.Paint; -import android.graphics.Paint.Align; -import android.graphics.PixelFormat; -import android.graphics.PorterDuff; -import android.graphics.Rect; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.text.TextPaint; -import android.util.Log; -import android.view.ViewConfiguration; - -import java.util.HashMap; -import java.util.List; -import java.util.Locale; -import java.util.Map; - -public class LatinKeyboard extends BaseKeyboard { - - private static final boolean DEBUG_PREFERRED_LETTER = false; - private static final String TAG = "LatinKeyboard"; - private static final int OPACITY_FULLY_OPAQUE = 255; - private static final int SPACE_LED_LENGTH_PERCENT = 80; - - private Drawable mShiftLockPreviewIcon; - private final HashMap<Key, Drawable> mNormalShiftIcons = new HashMap<Key, Drawable>(); - private Drawable mSpaceIcon; - private Drawable mSpaceAutoCompletionIndicator; - private Drawable mSpacePreviewIcon; - private final Drawable mButtonArrowLeftIcon; - private final Drawable mButtonArrowRightIcon; - private final int mSpaceBarTextShadowColor; - private Key mSpaceKey; - private int mSpaceKeyIndex = -1; - private int mSpaceDragStartX; - private int mSpaceDragLastDiff; - private final Resources mRes; - private final Context mContext; - private boolean mCurrentlyInSpace; - private SlidingLocaleDrawable mSlidingLocaleIcon; - private int[] mPrefLetterFrequencies; - private int mPrefLetter; - private int mPrefLetterX; - private int mPrefLetterY; - private int mPrefDistance; - - private LatinKeyboardShiftState mShiftState = new LatinKeyboardShiftState(); - - private static final float SPACEBAR_DRAG_THRESHOLD = 0.8f; - private static final float OVERLAP_PERCENTAGE_LOW_PROB = 0.70f; - private static final float OVERLAP_PERCENTAGE_HIGH_PROB = 0.85f; - // Minimum width of space key preview (proportional to keyboard width) - private static final float SPACEBAR_POPUP_MIN_RATIO = 0.4f; - // Height in space key the language name will be drawn. (proportional to space key height) - private static final float SPACEBAR_LANGUAGE_BASELINE = 0.6f; - // If the full language name needs to be smaller than this value to be drawn on space key, - // its short language name will be used instead. - private static final float MINIMUM_SCALE_OF_LANGUAGE_NAME = 0.8f; - - private static int sSpacebarVerticalCorrection; - - public LatinKeyboard(Context context, KeyboardSwitcher.KeyboardId id) { - super(context, id); - final Resources res = context.getResources(); - mContext = context; - mRes = res; - if (id.mColorScheme == BaseKeyboardView.COLOR_SCHEME_BLACK) { - mSpaceBarTextShadowColor = res.getColor( - R.color.latinkeyboard_bar_language_shadow_black); - } else { // default color scheme is BaseKeyboardView.COLOR_SCHEME_WHITE - mSpaceBarTextShadowColor = res.getColor( - R.color.latinkeyboard_bar_language_shadow_white); - } - mShiftLockPreviewIcon = res.getDrawable(R.drawable.sym_keyboard_feedback_shift_locked); - setDefaultBounds(mShiftLockPreviewIcon); - mSpaceAutoCompletionIndicator = res.getDrawable(R.drawable.sym_keyboard_space_led); - mButtonArrowLeftIcon = res.getDrawable(R.drawable.sym_keyboard_language_arrows_left); - mButtonArrowRightIcon = res.getDrawable(R.drawable.sym_keyboard_language_arrows_right); - sSpacebarVerticalCorrection = res.getDimensionPixelOffset( - R.dimen.spacebar_vertical_correction); - mSpaceKeyIndex = indexOf(LatinIME.KEYCODE_SPACE); - } - - @Override - protected Key createKeyFromXml(Resources res, Row parent, int x, int y, - XmlResourceParser parser, KeyStyles keyStyles) { - Key key = new LatinKey(res, parent, x, y, parser, keyStyles); - switch (key.codes[0]) { - case LatinIME.KEYCODE_SPACE: - mSpaceKey = key; - mSpaceIcon = key.icon; - mSpacePreviewIcon = key.iconPreview; - break; - } - - return key; - } - - public void enableShiftLock() { - for (final Key key : getShiftKeys()) { - if (key instanceof LatinKey) { - ((LatinKey)key).enableShiftLock(); - } - mNormalShiftIcons.put(key, key.icon); - } - } - - public boolean setShiftLocked(boolean newShiftLockState) { - final Map<Key, Drawable> shiftedIcons = getShiftedIcons(); - for (final Key key : getShiftKeys()) { - key.on = newShiftLockState; - key.icon = newShiftLockState ? shiftedIcons.get(key) : mNormalShiftIcons.get(key); - } - mShiftState.setShiftLocked(newShiftLockState); - return true; - } - - public boolean isShiftLocked() { - return mShiftState.isShiftLocked(); - } - - @Override - public boolean setShifted(boolean newShiftState) { - if (getShiftKeys().size() == 0) - return super.setShifted(newShiftState); - - final Map<Key, Drawable> shiftedIcons = getShiftedIcons(); - for (final Key key : getShiftKeys()) { - if (!newShiftState && !mShiftState.isShiftLocked()) { - key.icon = mNormalShiftIcons.get(key); - } else if (newShiftState && !mShiftState.isShiftedOrShiftLocked()) { - key.icon = shiftedIcons.get(key); - } - } - return mShiftState.setShifted(newShiftState); - } - - @Override - public boolean isShiftedOrShiftLocked() { - if (getShiftKeys().size() > 0) { - return mShiftState.isShiftedOrShiftLocked(); - } else { - return super.isShiftedOrShiftLocked(); - } - } - - public void setAutomaticTemporaryUpperCase() { - setShifted(true); - mShiftState.setAutomaticTemporaryUpperCase(); - } - - public boolean isAutomaticTemporaryUpperCase() { - return isAlphaKeyboard() && mShiftState.isAutomaticTemporaryUpperCase(); - } - - public boolean isManualTemporaryUpperCase() { - return isAlphaKeyboard() && mShiftState.isManualTemporaryUpperCase(); - } - - /* package */ LatinKeyboardShiftState getKeyboardShiftState() { - return mShiftState; - } - - public boolean isAlphaKeyboard() { - return mId.getXmlId() == R.xml.kbd_qwerty; - } - - public boolean isPhoneKeyboard() { - return mId.mMode == KeyboardSwitcher.MODE_PHONE; - } - - public boolean isNumberKeyboard() { - return mId.mMode == KeyboardSwitcher.MODE_NUMBER; - } - - /** - * @return a key which should be invalidated. - */ - public Key onAutoCompletionStateChanged(boolean isAutoCompletion) { - updateSpaceBarForLocale(isAutoCompletion); - return mSpaceKey; - } - - private void updateSpaceBarForLocale(boolean isAutoCompletion) { - final Resources res = mRes; - // If application locales are explicitly selected. - if (SubtypeSwitcher.getInstance().needsToDisplayLanguage()) { - mSpaceKey.icon = new BitmapDrawable(res, - drawSpaceBar(OPACITY_FULLY_OPAQUE, isAutoCompletion)); - } else { - // sym_keyboard_space_led can be shared with Black and White symbol themes. - if (isAutoCompletion) { - mSpaceKey.icon = new BitmapDrawable(res, - drawSpaceBar(OPACITY_FULLY_OPAQUE, isAutoCompletion)); - } else { - mSpaceKey.icon = mSpaceIcon; - } - } - } - - // Compute width of text with specified text size using paint. - private static int getTextWidth(Paint paint, String text, float textSize, Rect bounds) { - paint.setTextSize(textSize); - paint.getTextBounds(text, 0, text.length(), bounds); - return bounds.width(); - } - - // Layout local language name and left and right arrow on space bar. - private static String layoutSpaceBar(Paint paint, Locale locale, Drawable lArrow, - Drawable rArrow, int width, int height, float origTextSize, - boolean allowVariableTextSize) { - final float arrowWidth = lArrow.getIntrinsicWidth(); - final float arrowHeight = lArrow.getIntrinsicHeight(); - final float maxTextWidth = width - (arrowWidth + arrowWidth); - final Rect bounds = new Rect(); - - // Estimate appropriate language name text size to fit in maxTextWidth. - String language = SubtypeSwitcher.getDisplayLanguage(locale); - int textWidth = getTextWidth(paint, language, origTextSize, bounds); - // Assuming text width and text size are proportional to each other. - float textSize = origTextSize * Math.min(maxTextWidth / textWidth, 1.0f); - - final boolean useShortName; - if (allowVariableTextSize) { - textWidth = getTextWidth(paint, language, textSize, bounds); - // If text size goes too small or text does not fit, use short name - useShortName = textSize / origTextSize < MINIMUM_SCALE_OF_LANGUAGE_NAME - || textWidth > maxTextWidth; - } else { - useShortName = textWidth > maxTextWidth; - textSize = origTextSize; - } - if (useShortName) { - language = SubtypeSwitcher.getShortDisplayLanguage(locale); - textWidth = getTextWidth(paint, language, origTextSize, bounds); - textSize = origTextSize * Math.min(maxTextWidth / textWidth, 1.0f); - } - paint.setTextSize(textSize); - - // Place left and right arrow just before and after language text. - final float baseline = height * SPACEBAR_LANGUAGE_BASELINE; - final int top = (int)(baseline - arrowHeight); - final float remains = (width - textWidth) / 2; - lArrow.setBounds((int)(remains - arrowWidth), top, (int)remains, (int)baseline); - rArrow.setBounds((int)(remains + textWidth), top, (int)(remains + textWidth + arrowWidth), - (int)baseline); - - return language; - } - - private Bitmap drawSpaceBar(int opacity, boolean isAutoCompletion) { - final int width = mSpaceKey.width; - final int height = mSpaceIcon.getIntrinsicHeight(); - final Bitmap buffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); - final Canvas canvas = new Canvas(buffer); - final Resources res = mRes; - canvas.drawColor(res.getColor(R.color.latinkeyboard_transparent), PorterDuff.Mode.CLEAR); - - SubtypeSwitcher subtypeSwitcher = SubtypeSwitcher.getInstance(); - // If application locales are explicitly selected. - if (subtypeSwitcher.needsToDisplayLanguage()) { - final Paint paint = new Paint(); - paint.setAlpha(opacity); - paint.setAntiAlias(true); - paint.setTextAlign(Align.CENTER); - - final boolean allowVariableTextSize = true; - final String language = layoutSpaceBar(paint, subtypeSwitcher.getInputLocale(), - mButtonArrowLeftIcon, mButtonArrowRightIcon, width, height, - getTextSizeFromTheme(android.R.style.TextAppearance_Small, 14), - allowVariableTextSize); - - // Draw language text with shadow - final float baseline = height * SPACEBAR_LANGUAGE_BASELINE; - final float descent = paint.descent(); - paint.setColor(mSpaceBarTextShadowColor); - canvas.drawText(language, width / 2, baseline - descent - 1, paint); - paint.setColor(res.getColor(R.color.latinkeyboard_bar_language_text)); - canvas.drawText(language, width / 2, baseline - descent, paint); - - // Put arrows that are already layed out on either side of the text - if (SubtypeSwitcher.USE_SPACEBAR_LANGUAGE_SWITCHER - && subtypeSwitcher.getEnabledKeyboardLocaleCount() > 1) { - mButtonArrowLeftIcon.draw(canvas); - mButtonArrowRightIcon.draw(canvas); - } - } - - // Draw the spacebar icon at the bottom - if (isAutoCompletion) { - final int iconWidth = width * SPACE_LED_LENGTH_PERCENT / 100; - final int iconHeight = mSpaceAutoCompletionIndicator.getIntrinsicHeight(); - int x = (width - iconWidth) / 2; - int y = height - iconHeight; - mSpaceAutoCompletionIndicator.setBounds(x, y, x + iconWidth, y + iconHeight); - mSpaceAutoCompletionIndicator.draw(canvas); - } else { - final int iconWidth = mSpaceIcon.getIntrinsicWidth(); - final int iconHeight = mSpaceIcon.getIntrinsicHeight(); - int x = (width - iconWidth) / 2; - int y = height - iconHeight; - mSpaceIcon.setBounds(x, y, x + iconWidth, y + iconHeight); - mSpaceIcon.draw(canvas); - } - return buffer; - } - - private void updateLocaleDrag(int diff) { - if (mSlidingLocaleIcon == null) { - final int width = Math.max(mSpaceKey.width, - (int)(getMinWidth() * SPACEBAR_POPUP_MIN_RATIO)); - final int height = mSpacePreviewIcon.getIntrinsicHeight(); - mSlidingLocaleIcon = new SlidingLocaleDrawable(mSpacePreviewIcon, width, height); - mSlidingLocaleIcon.setBounds(0, 0, width, height); - mSpaceKey.iconPreview = mSlidingLocaleIcon; - } - mSlidingLocaleIcon.setDiff(diff); - if (Math.abs(diff) == Integer.MAX_VALUE) { - mSpaceKey.iconPreview = mSpacePreviewIcon; - } else { - mSpaceKey.iconPreview = mSlidingLocaleIcon; - } - mSpaceKey.iconPreview.invalidateSelf(); - } - - public int getLanguageChangeDirection() { - if (mSpaceKey == null || SubtypeSwitcher.getInstance().getEnabledKeyboardLocaleCount() <= 1 - || Math.abs(mSpaceDragLastDiff) < mSpaceKey.width * SPACEBAR_DRAG_THRESHOLD) { - return 0; // No change - } - return mSpaceDragLastDiff > 0 ? 1 : -1; - } - - boolean isCurrentlyInSpace() { - return mCurrentlyInSpace; - } - - public void setPreferredLetters(int[] frequencies) { - mPrefLetterFrequencies = frequencies; - mPrefLetter = 0; - } - - public void keyReleased() { - mCurrentlyInSpace = false; - mSpaceDragLastDiff = 0; - mPrefLetter = 0; - mPrefLetterX = 0; - mPrefLetterY = 0; - mPrefDistance = Integer.MAX_VALUE; - if (mSpaceKey != null) { - updateLocaleDrag(Integer.MAX_VALUE); - } - } - - /** - * Does the magic of locking the touch gesture into the spacebar when - * switching input languages. - */ - public boolean isInside(LatinKey key, int x, int y) { - final int code = key.codes[0]; - if (code == KEYCODE_SHIFT || code == KEYCODE_DELETE) { - y -= key.height / 10; - if (code == KEYCODE_SHIFT) x += key.width / 6; - if (code == KEYCODE_DELETE) x -= key.width / 6; - } else if (code == LatinIME.KEYCODE_SPACE) { - y += LatinKeyboard.sSpacebarVerticalCorrection; - if (SubtypeSwitcher.USE_SPACEBAR_LANGUAGE_SWITCHER - && SubtypeSwitcher.getInstance().getEnabledKeyboardLocaleCount() > 1) { - if (mCurrentlyInSpace) { - int diff = x - mSpaceDragStartX; - if (Math.abs(diff - mSpaceDragLastDiff) > 0) { - updateLocaleDrag(diff); - } - mSpaceDragLastDiff = diff; - return true; - } else { - boolean insideSpace = key.isInsideSuper(x, y); - if (insideSpace) { - mCurrentlyInSpace = true; - mSpaceDragStartX = x; - updateLocaleDrag(0); - } - return insideSpace; - } - } - } else if (mPrefLetterFrequencies != null) { - // New coordinate? Reset - if (mPrefLetterX != x || mPrefLetterY != y) { - mPrefLetter = 0; - mPrefDistance = Integer.MAX_VALUE; - } - // Handle preferred next letter - final int[] pref = mPrefLetterFrequencies; - if (mPrefLetter > 0) { - if (DEBUG_PREFERRED_LETTER) { - if (mPrefLetter == code && !key.isInsideSuper(x, y)) { - Log.d(TAG, "CORRECTED !!!!!!"); - } - } - return mPrefLetter == code; - } else { - final boolean inside = key.isInsideSuper(x, y); - int[] nearby = getNearestKeys(x, y); - List<Key> nearbyKeys = getKeys(); - if (inside) { - // If it's a preferred letter - if (inPrefList(code, pref)) { - // Check if its frequency is much lower than a nearby key - mPrefLetter = code; - mPrefLetterX = x; - mPrefLetterY = y; - for (int i = 0; i < nearby.length; i++) { - Key k = nearbyKeys.get(nearby[i]); - if (k != key && inPrefList(k.codes[0], pref)) { - final int dist = distanceFrom(k, x, y); - if (dist < (int) (k.width * OVERLAP_PERCENTAGE_LOW_PROB) && - (pref[k.codes[0]] > pref[mPrefLetter] * 3)) { - mPrefLetter = k.codes[0]; - mPrefDistance = dist; - if (DEBUG_PREFERRED_LETTER) { - Log.d(TAG, "CORRECTED ALTHOUGH PREFERRED !!!!!!"); - } - break; - } - } - } - - return mPrefLetter == code; - } - } - - // Get the surrounding keys and intersect with the preferred list - // For all in the intersection - // if distance from touch point is within a reasonable distance - // make this the pref letter - // If no pref letter - // return inside; - // else return thiskey == prefletter; - - for (int i = 0; i < nearby.length; i++) { - Key k = nearbyKeys.get(nearby[i]); - if (inPrefList(k.codes[0], pref)) { - final int dist = distanceFrom(k, x, y); - if (dist < (int) (k.width * OVERLAP_PERCENTAGE_HIGH_PROB) - && dist < mPrefDistance) { - mPrefLetter = k.codes[0]; - mPrefLetterX = x; - mPrefLetterY = y; - mPrefDistance = dist; - } - } - } - // Didn't find any - if (mPrefLetter == 0) { - return inside; - } else { - return mPrefLetter == code; - } - } - } - - // Lock into the spacebar - if (mCurrentlyInSpace) return false; - - return key.isInsideSuper(x, y); - } - - private boolean inPrefList(int code, int[] pref) { - if (code < pref.length && code >= 0) return pref[code] > 0; - return false; - } - - private int distanceFrom(Key k, int x, int y) { - if (y > k.y && y < k.y + k.height) { - return Math.abs(k.x + k.width / 2 - x); - } else { - return Integer.MAX_VALUE; - } - } - - @Override - public int[] getNearestKeys(int x, int y) { - if (mCurrentlyInSpace) { - return new int[] { mSpaceKeyIndex }; - } else { - // Avoid dead pixels at edges of the keyboard - return super.getNearestKeys(Math.max(0, Math.min(x, getMinWidth() - 1)), - Math.max(0, Math.min(y, getHeight() - 1))); - } - } - - private int indexOf(int code) { - List<Key> keys = getKeys(); - int count = keys.size(); - for (int i = 0; i < count; i++) { - if (keys.get(i).codes[0] == code) return i; - } - return -1; - } - - private int getTextSizeFromTheme(int style, int defValue) { - TypedArray array = mContext.getTheme().obtainStyledAttributes( - style, new int[] { android.R.attr.textSize }); - int textSize = array.getDimensionPixelSize(array.getResourceId(0, 0), defValue); - return textSize; - } - - public static class LatinKey extends BaseKeyboard.Key { - - // functional normal state (with properties) - private final int[] KEY_STATE_FUNCTIONAL_NORMAL = { - android.R.attr.state_single - }; - - // functional pressed state (with properties) - private final int[] KEY_STATE_FUNCTIONAL_PRESSED = { - android.R.attr.state_single, - android.R.attr.state_pressed - }; - - private boolean mShiftLockEnabled; - - public LatinKey(Resources res, BaseKeyboard.Row parent, int x, int y, - XmlResourceParser parser, KeyStyles keyStyles) { - super(res, parent, x, y, parser, keyStyles); - if (popupCharacters != null && popupCharacters.length() == 0) { - // If there is a keyboard with no keys specified in popupCharacters - popupResId = 0; - } - } - - private void enableShiftLock() { - mShiftLockEnabled = true; - } - - // 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 !sticky && modifier; - } - - @Override - public void onReleased(boolean inside) { - if (!mShiftLockEnabled) { - super.onReleased(inside); - } else { - pressed = !pressed; - } - } - - /** - * Overriding this method so that we can reduce the target area for certain keys. - */ - @Override - public boolean isInside(int x, int y) { - boolean result = (keyboard instanceof LatinKeyboard) - && ((LatinKeyboard)keyboard).isInside(this, x, y); - return result; - } - - private boolean isInsideSuper(int x, int y) { - return super.isInside(x, y); - } - - @Override - public int[] getCurrentDrawableState() { - if (isFunctionalKey()) { - if (pressed) { - return KEY_STATE_FUNCTIONAL_PRESSED; - } else { - return KEY_STATE_FUNCTIONAL_NORMAL; - } - } - return super.getCurrentDrawableState(); - } - } - - /** - * Animation to be displayed on the spacebar preview popup when switching - * languages by swiping the spacebar. It draws the current, previous and - * next languages and moves them by the delta of touch movement on the spacebar. - */ - private class SlidingLocaleDrawable extends Drawable { - - private final int mWidth; - private final int mHeight; - private final Drawable mBackground; - private final TextPaint mTextPaint; - private final int mMiddleX; - private final Drawable mLeftDrawable; - private final Drawable mRightDrawable; - private final int mThreshold; - private int mDiff; - private boolean mHitThreshold; - private String mCurrentLanguage; - private String mNextLanguage; - private String mPrevLanguage; - - public SlidingLocaleDrawable(Drawable background, int width, int height) { - mBackground = background; - setDefaultBounds(mBackground); - mWidth = width; - mHeight = height; - final TextPaint textPaint = new TextPaint(); - textPaint.setTextSize(getTextSizeFromTheme(android.R.style.TextAppearance_Medium, 18)); - textPaint.setColor(R.color.latinkeyboard_transparent); - textPaint.setTextAlign(Align.CENTER); - textPaint.setAlpha(OPACITY_FULLY_OPAQUE); - textPaint.setAntiAlias(true); - mTextPaint = textPaint; - mMiddleX = (mWidth - mBackground.getIntrinsicWidth()) / 2; - final Resources res = mRes; - mLeftDrawable = res.getDrawable( - R.drawable.sym_keyboard_feedback_language_arrows_left); - mRightDrawable = res.getDrawable( - R.drawable.sym_keyboard_feedback_language_arrows_right); - mThreshold = ViewConfiguration.get(mContext).getScaledTouchSlop(); - } - - private void setDiff(int diff) { - if (diff == Integer.MAX_VALUE) { - mHitThreshold = false; - mCurrentLanguage = null; - return; - } - mDiff = diff; - if (mDiff > mWidth) mDiff = mWidth; - if (mDiff < -mWidth) mDiff = -mWidth; - if (Math.abs(mDiff) > mThreshold) mHitThreshold = true; - invalidateSelf(); - } - - - @Override - public void draw(Canvas canvas) { - canvas.save(); - if (mHitThreshold) { - Paint paint = mTextPaint; - final int width = mWidth; - final int height = mHeight; - final int diff = mDiff; - final Drawable lArrow = mLeftDrawable; - final Drawable rArrow = mRightDrawable; - canvas.clipRect(0, 0, width, height); - if (mCurrentLanguage == null) { - SubtypeSwitcher subtypeSwitcher = SubtypeSwitcher.getInstance(); - mCurrentLanguage = subtypeSwitcher.getInputLanguageName(); - mNextLanguage = subtypeSwitcher.getNextInputLanguageName(); - mPrevLanguage = subtypeSwitcher.getPreviousInputLanguageName(); - } - // Draw language text with shadow - final float baseline = mHeight * SPACEBAR_LANGUAGE_BASELINE - paint.descent(); - paint.setColor(mRes.getColor(R.color.latinkeyboard_feedback_language_text)); - canvas.drawText(mCurrentLanguage, width / 2 + diff, baseline, paint); - canvas.drawText(mNextLanguage, diff - width / 2, baseline, paint); - canvas.drawText(mPrevLanguage, diff + width + width / 2, baseline, paint); - - setDefaultBounds(lArrow); - rArrow.setBounds(width - rArrow.getIntrinsicWidth(), 0, width, - rArrow.getIntrinsicHeight()); - lArrow.draw(canvas); - rArrow.draw(canvas); - } - if (mBackground != null) { - canvas.translate(mMiddleX, 0); - mBackground.draw(canvas); - } - canvas.restore(); - } - - @Override - public int getOpacity() { - return PixelFormat.TRANSLUCENT; - } - - @Override - public void setAlpha(int alpha) { - // Ignore - } - - @Override - public void setColorFilter(ColorFilter cf) { - // Ignore - } - - @Override - public int getIntrinsicWidth() { - return mWidth; - } - - @Override - public int getIntrinsicHeight() { - return mHeight; - } - } -} diff --git a/java/src/com/android/inputmethod/latin/LatinKeyboardShiftState.java b/java/src/com/android/inputmethod/latin/LatinKeyboardShiftState.java deleted file mode 100644 index e916306c8..000000000 --- a/java/src/com/android/inputmethod/latin/LatinKeyboardShiftState.java +++ /dev/null @@ -1,89 +0,0 @@ -package com.android.inputmethod.latin; - -import android.util.Log; - -public class LatinKeyboardShiftState { - private static final String TAG = "LatinKeyboardShiftState"; - private static final boolean DEBUG = KeyboardSwitcher.DEBUG_STATE; - - private static final int NORMAL = 0; - private static final int MANUAL_SHIFTED = 1; - private static final int SHIFT_LOCKED = 2; - private static final int AUTO_SHIFTED = 3; - private static final int SHIFT_LOCK_SHIFTED = 4; - - private int mState = NORMAL; - - public boolean setShifted(boolean newShiftState) { - final int oldState = mState; - if (newShiftState) { - if (oldState == NORMAL || oldState == AUTO_SHIFTED) { - mState = MANUAL_SHIFTED; - } else if (oldState == SHIFT_LOCKED) { - mState = SHIFT_LOCK_SHIFTED; - } - } else { - if (oldState == MANUAL_SHIFTED || oldState == AUTO_SHIFTED) { - mState = NORMAL; - } else if (oldState == SHIFT_LOCK_SHIFTED) { - mState = SHIFT_LOCKED; - } - } - if (DEBUG) - Log.d(TAG, "setShifted(" + newShiftState + "): " + toString(oldState) + " > " + this); - return mState != oldState; - } - - public void setShiftLocked(boolean newShiftLockState) { - final int oldState = mState; - if (newShiftLockState) { - if (oldState == NORMAL || oldState == MANUAL_SHIFTED || oldState == AUTO_SHIFTED) - mState = SHIFT_LOCKED; - } else { - if (oldState == SHIFT_LOCKED || oldState == SHIFT_LOCK_SHIFTED) - mState = NORMAL; - } - if (DEBUG) - Log.d(TAG, "setShiftLocked(" + newShiftLockState + "): " + toString(oldState) - + " > " + this); - } - - public void setAutomaticTemporaryUpperCase() { - final int oldState = mState; - mState = AUTO_SHIFTED; - if (DEBUG) - Log.d(TAG, "setAutomaticTemporaryUpperCase: " + toString(oldState) + " > " + this); - } - - public boolean isShiftedOrShiftLocked() { - return mState != NORMAL; - } - - public boolean isShiftLocked() { - return mState == SHIFT_LOCKED || mState == SHIFT_LOCK_SHIFTED; - } - - public boolean isAutomaticTemporaryUpperCase() { - return mState == AUTO_SHIFTED; - } - - public boolean isManualTemporaryUpperCase() { - return mState == MANUAL_SHIFTED || mState == SHIFT_LOCK_SHIFTED; - } - - @Override - public String toString() { - return toString(mState); - } - - private static String toString(int state) { - switch (state) { - case NORMAL: return "NORMAL"; - case MANUAL_SHIFTED: return "MANUAL_SHIFTED"; - case SHIFT_LOCKED: return "SHIFT_LOCKED"; - case AUTO_SHIFTED: return "AUTO_SHIFTED"; - case SHIFT_LOCK_SHIFTED: return "SHIFT_LOCK_SHIFTED"; - default: return "UKNOWN"; - } - } -}
\ No newline at end of file diff --git a/java/src/com/android/inputmethod/latin/LatinKeyboardView.java b/java/src/com/android/inputmethod/latin/LatinKeyboardView.java deleted file mode 100644 index ac68e3c39..000000000 --- a/java/src/com/android/inputmethod/latin/LatinKeyboardView.java +++ /dev/null @@ -1,376 +0,0 @@ -/* - * Copyright (C) 2008 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.latin; - -import com.android.inputmethod.latin.BaseKeyboard.Key; -import com.android.inputmethod.voice.VoiceIMEConnector; - -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.os.Handler; -import android.os.Message; -import android.os.SystemClock; -import android.text.TextUtils; -import android.util.AttributeSet; -import android.view.MotionEvent; - -import java.util.List; - -public class LatinKeyboardView extends BaseKeyboardView { - - public static final int KEYCODE_OPTIONS = -100; - public static final int KEYCODE_OPTIONS_LONGPRESS = -101; - // TODO: remove this once LatinIME stops referring to this. - public static final int KEYCODE_VOICE = -102; - public static final int KEYCODE_NEXT_LANGUAGE = -104; - public static final int KEYCODE_PREV_LANGUAGE = -105; - public static final int KEYCODE_CAPSLOCK = -106; - - /** Whether we've started dropping move events because we found a big jump */ - private boolean mDroppingEvents; - /** - * Whether multi-touch disambiguation needs to be disabled if a real multi-touch event has - * occured - */ - private boolean mDisableDisambiguation; - /** The distance threshold at which we start treating the touch session as a multi-touch */ - private int mJumpThresholdSquare = Integer.MAX_VALUE; - /** The y coordinate of the last row */ - private int mLastRowY; - - public LatinKeyboardView(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public LatinKeyboardView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - @Override - public void setPreviewEnabled(boolean previewEnabled) { - LatinKeyboard latinKeyboard = getLatinKeyboard(); - if (latinKeyboard != null - && (latinKeyboard.isPhoneKeyboard() || latinKeyboard.isNumberKeyboard())) { - // Phone and number keyboard never shows popup preview (except language switch). - super.setPreviewEnabled(false); - } else { - super.setPreviewEnabled(previewEnabled); - } - } - - public void setLatinKeyboard(LatinKeyboard k) { - super.setKeyboard(k); - // One-seventh of the keyboard width seems like a reasonable threshold - mJumpThresholdSquare = k.getMinWidth() / 7; - mJumpThresholdSquare *= mJumpThresholdSquare; - // Assuming there are 4 rows, this is the coordinate of the last row - mLastRowY = (k.getHeight() * 3) / 4; - setKeyboardLocal(k); - } - - public LatinKeyboard getLatinKeyboard() { - BaseKeyboard keyboard = getKeyboard(); - if (keyboard instanceof LatinKeyboard) { - return (LatinKeyboard)keyboard; - } else { - return null; - } - } - - @Override - protected boolean onLongPress(Key key) { - int primaryCode = key.codes[0]; - if (primaryCode == KEYCODE_OPTIONS) { - return invokeOnKey(KEYCODE_OPTIONS_LONGPRESS); - } else if (primaryCode == '0' && getLatinKeyboard().isPhoneKeyboard()) { - // Long pressing on 0 in phone number keypad gives you a '+'. - return invokeOnKey('+'); - } else { - return super.onLongPress(key); - } - } - - private boolean invokeOnKey(int primaryCode) { - getOnKeyboardActionListener().onKey(primaryCode, null, - BaseKeyboardView.NOT_A_TOUCH_COORDINATE, - BaseKeyboardView.NOT_A_TOUCH_COORDINATE); - return true; - } - - @Override - protected CharSequence adjustCase(CharSequence label) { - LatinKeyboard keyboard = getLatinKeyboard(); - if (keyboard.isAlphaKeyboard() - && keyboard.isShiftedOrShiftLocked() - && !TextUtils.isEmpty(label) && label.length() < 3 - && Character.isLowerCase(label.charAt(0))) { - label = label.toString().toUpperCase(); - } - return label; - } - - /** - * This function checks to see if we need to handle any sudden jumps in the pointer location - * that could be due to a multi-touch being treated as a move by the firmware or hardware. - * Once a sudden jump is detected, all subsequent move events are discarded - * until an UP is received.<P> - * When a sudden jump is detected, an UP event is simulated at the last position and when - * the sudden moves subside, a DOWN event is simulated for the second key. - * @param me the motion event - * @return true if the event was consumed, so that it doesn't continue to be handled by - * KeyboardView. - */ - private boolean handleSuddenJump(MotionEvent me) { - final int action = me.getAction(); - final int x = (int) me.getX(); - final int y = (int) me.getY(); - boolean result = false; - - // Real multi-touch event? Stop looking for sudden jumps - if (me.getPointerCount() > 1) { - mDisableDisambiguation = true; - } - if (mDisableDisambiguation) { - // If UP, reset the multi-touch flag - if (action == MotionEvent.ACTION_UP) mDisableDisambiguation = false; - return false; - } - - switch (action) { - case MotionEvent.ACTION_DOWN: - // Reset the "session" - mDroppingEvents = false; - mDisableDisambiguation = false; - break; - case MotionEvent.ACTION_MOVE: - // Is this a big jump? - final int distanceSquare = (mLastX - x) * (mLastX - x) + (mLastY - y) * (mLastY - y); - // Check the distance and also if the move is not entirely within the bottom row - // If it's only in the bottom row, it might be an intentional slide gesture - // for language switching - if (distanceSquare > mJumpThresholdSquare - && (mLastY < mLastRowY || y < mLastRowY)) { - // If we're not yet dropping events, start dropping and send an UP event - if (!mDroppingEvents) { - mDroppingEvents = true; - // Send an up event - MotionEvent translated = MotionEvent.obtain(me.getEventTime(), me.getEventTime(), - MotionEvent.ACTION_UP, - mLastX, mLastY, me.getMetaState()); - super.onTouchEvent(translated); - translated.recycle(); - } - result = true; - } else if (mDroppingEvents) { - // If moves are small and we're already dropping events, continue dropping - result = true; - } - break; - case MotionEvent.ACTION_UP: - if (mDroppingEvents) { - // Send a down event first, as we dropped a bunch of sudden jumps and assume that - // the user is releasing the touch on the second key. - MotionEvent translated = MotionEvent.obtain(me.getEventTime(), me.getEventTime(), - MotionEvent.ACTION_DOWN, - x, y, me.getMetaState()); - super.onTouchEvent(translated); - translated.recycle(); - mDroppingEvents = false; - // Let the up event get processed as well, result = false - } - break; - } - // Track the previous coordinate - mLastX = x; - mLastY = y; - return result; - } - - @Override - public boolean onTouchEvent(MotionEvent me) { - LatinKeyboard keyboard = getLatinKeyboard(); - if (DEBUG_LINE) { - mLastX = (int) me.getX(); - mLastY = (int) me.getY(); - invalidate(); - } - - // If there was a sudden jump, return without processing the actual motion event. - if (handleSuddenJump(me)) - return true; - - // Reset any bounding box controls in the keyboard - if (me.getAction() == MotionEvent.ACTION_DOWN) { - keyboard.keyReleased(); - } - - if (me.getAction() == MotionEvent.ACTION_UP) { - int languageDirection = keyboard.getLanguageChangeDirection(); - if (languageDirection != 0) { - getOnKeyboardActionListener().onKey( - languageDirection == 1 ? KEYCODE_NEXT_LANGUAGE : KEYCODE_PREV_LANGUAGE, - null, mLastX, mLastY); - me.setAction(MotionEvent.ACTION_CANCEL); - keyboard.keyReleased(); - return super.onTouchEvent(me); - } - } - - return super.onTouchEvent(me); - } - - /**************************** INSTRUMENTATION *******************************/ - - static final boolean DEBUG_AUTO_PLAY = false; - static final boolean DEBUG_LINE = false; - private static final int MSG_TOUCH_DOWN = 1; - private static final int MSG_TOUCH_UP = 2; - - Handler mHandler2; - - private String mStringToPlay; - private int mStringIndex; - private boolean mDownDelivered; - private Key[] mAsciiKeys = new Key[256]; - private boolean mPlaying; - private int mLastX; - private int mLastY; - private Paint mPaint; - - private void setKeyboardLocal(LatinKeyboard k) { - if (DEBUG_AUTO_PLAY) { - findKeys(); - if (mHandler2 == null) { - mHandler2 = new Handler() { - @Override - public void handleMessage(Message msg) { - removeMessages(MSG_TOUCH_DOWN); - removeMessages(MSG_TOUCH_UP); - if (mPlaying == false) return; - - switch (msg.what) { - case MSG_TOUCH_DOWN: - if (mStringIndex >= mStringToPlay.length()) { - mPlaying = false; - return; - } - char c = mStringToPlay.charAt(mStringIndex); - while (c > 255 || mAsciiKeys[c] == null) { - mStringIndex++; - if (mStringIndex >= mStringToPlay.length()) { - mPlaying = false; - return; - } - c = mStringToPlay.charAt(mStringIndex); - } - int x = mAsciiKeys[c].x + 10; - int y = mAsciiKeys[c].y + 26; - MotionEvent me = MotionEvent.obtain(SystemClock.uptimeMillis(), - SystemClock.uptimeMillis(), - MotionEvent.ACTION_DOWN, x, y, 0); - LatinKeyboardView.this.dispatchTouchEvent(me); - me.recycle(); - sendEmptyMessageDelayed(MSG_TOUCH_UP, 500); // Deliver up in 500ms if nothing else - // happens - mDownDelivered = true; - break; - case MSG_TOUCH_UP: - char cUp = mStringToPlay.charAt(mStringIndex); - int x2 = mAsciiKeys[cUp].x + 10; - int y2 = mAsciiKeys[cUp].y + 26; - mStringIndex++; - - MotionEvent me2 = MotionEvent.obtain(SystemClock.uptimeMillis(), - SystemClock.uptimeMillis(), - MotionEvent.ACTION_UP, x2, y2, 0); - LatinKeyboardView.this.dispatchTouchEvent(me2); - me2.recycle(); - sendEmptyMessageDelayed(MSG_TOUCH_DOWN, 500); // Deliver up in 500ms if nothing else - // happens - mDownDelivered = false; - break; - } - } - }; - - } - } - } - - private void findKeys() { - List<Key> keys = getLatinKeyboard().getKeys(); - // Get the keys on this keyboard - for (int i = 0; i < keys.size(); i++) { - int code = keys.get(i).codes[0]; - if (code >= 0 && code <= 255) { - mAsciiKeys[code] = keys.get(i); - } - } - } - - public void startPlaying(String s) { - if (DEBUG_AUTO_PLAY) { - if (s == null) return; - mStringToPlay = s.toLowerCase(); - mPlaying = true; - mDownDelivered = false; - mStringIndex = 0; - mHandler2.sendEmptyMessageDelayed(MSG_TOUCH_DOWN, 10); - } - } - - @Override - public void draw(Canvas c) { - LatinIMEUtil.GCUtils.getInstance().reset(); - boolean tryGC = true; - for (int i = 0; i < LatinIMEUtil.GCUtils.GC_TRY_LOOP_MAX && tryGC; ++i) { - try { - super.draw(c); - tryGC = false; - } catch (OutOfMemoryError e) { - tryGC = LatinIMEUtil.GCUtils.getInstance().tryGCOrWait("LatinKeyboardView", e); - } - } - if (DEBUG_AUTO_PLAY) { - if (mPlaying) { - mHandler2.removeMessages(MSG_TOUCH_DOWN); - mHandler2.removeMessages(MSG_TOUCH_UP); - if (mDownDelivered) { - mHandler2.sendEmptyMessageDelayed(MSG_TOUCH_UP, 20); - } else { - mHandler2.sendEmptyMessageDelayed(MSG_TOUCH_DOWN, 20); - } - } - } - if (DEBUG_LINE) { - if (mPaint == null) { - mPaint = new Paint(); - mPaint.setColor(0x80FFFFFF); - mPaint.setAntiAlias(false); - } - c.drawLine(mLastX, 0, mLastX, getHeight(), mPaint); - c.drawLine(0, mLastY, getWidth(), mLastY, mPaint); - } - } - - @Override - protected void onAttachedToWindow() { - // Token is available from here. - VoiceIMEConnector.getInstance().onAttachedToWindow(); - } -} diff --git a/java/src/com/android/inputmethod/latin/MiniKeyboardKeyDetector.java b/java/src/com/android/inputmethod/latin/MiniKeyboardKeyDetector.java deleted file mode 100644 index 3cc43b99c..000000000 --- a/java/src/com/android/inputmethod/latin/MiniKeyboardKeyDetector.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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.latin; - -import com.android.inputmethod.latin.BaseKeyboard.Key; - -class MiniKeyboardKeyDetector extends KeyDetector { - private static final int MAX_NEARBY_KEYS = 1; - - private final int mSlideAllowanceSquare; - private final int mSlideAllowanceSquareTop; - - public MiniKeyboardKeyDetector(float slideAllowance) { - super(); - mSlideAllowanceSquare = (int)(slideAllowance * slideAllowance); - // Top slide allowance is slightly longer (sqrt(2) times) than other edges. - mSlideAllowanceSquareTop = mSlideAllowanceSquare * 2; - } - - @Override - protected int getMaxNearbyKeys() { - return MAX_NEARBY_KEYS; - } - - @Override - public int getKeyIndexAndNearbyCodes(int x, int y, int[] allKeys) { - final Key[] keys = getKeys(); - final int touchX = getTouchX(x); - final int touchY = getTouchY(y); - - int closestKeyIndex = BaseKeyboardView.NOT_A_KEY; - int closestKeyDist = (y < 0) ? mSlideAllowanceSquareTop : mSlideAllowanceSquare; - final int keyCount = keys.length; - for (int index = 0; index < keyCount; index++) { - final int dist = keys[index].squaredDistanceToEdge(touchX, touchY); - if (dist < closestKeyDist) { - closestKeyIndex = index; - closestKeyDist = dist; - } - } - - if (allKeys != null && closestKeyIndex != BaseKeyboardView.NOT_A_KEY) - allKeys[0] = keys[closestKeyIndex].codes[0]; - return closestKeyIndex; - } -} diff --git a/java/src/com/android/inputmethod/latin/PointerTracker.java b/java/src/com/android/inputmethod/latin/PointerTracker.java deleted file mode 100644 index 327fef107..000000000 --- a/java/src/com/android/inputmethod/latin/PointerTracker.java +++ /dev/null @@ -1,546 +0,0 @@ -/* - * 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.latin; - -import com.android.inputmethod.latin.BaseKeyboard.Key; -import com.android.inputmethod.latin.BaseKeyboardView.OnKeyboardActionListener; -import com.android.inputmethod.latin.BaseKeyboardView.UIHandler; - -import android.content.res.Resources; -import android.util.Log; -import android.view.MotionEvent; - -public class PointerTracker { - private static final String TAG = "PointerTracker"; - private static final boolean DEBUG = false; - private static final boolean DEBUG_MOVE = false; - - public interface UIProxy { - public void invalidateKey(Key key); - public void showPreview(int keyIndex, PointerTracker tracker); - public boolean hasDistinctMultitouch(); - } - - public final int mPointerId; - - // Timing constants - private final int mDelayBeforeKeyRepeatStart; - private final int mLongPressKeyTimeout; - private final int mLongPressShiftKeyTimeout; - private final int mMultiTapKeyTimeout; - - // Miscellaneous constants - private static final int NOT_A_KEY = BaseKeyboardView.NOT_A_KEY; - private static final int[] KEY_DELETE = { BaseKeyboard.KEYCODE_DELETE }; - - private final UIProxy mProxy; - private final UIHandler mHandler; - private final KeyDetector mKeyDetector; - private OnKeyboardActionListener mListener; - private final boolean mHasDistinctMultitouch; - - private BaseKeyboard mKeyboard; - private Key[] mKeys; - private int mKeyHysteresisDistanceSquared = -1; - - private final KeyState mKeyState; - - // true if event is already translated to a key action (long press or mini-keyboard) - private boolean mKeyAlreadyProcessed; - - // true if this pointer is repeatable key - private boolean mIsRepeatableKey; - - // For multi-tap - private int mLastSentIndex; - private int mTapCount; - private long mLastTapTime; - private boolean mInMultiTap; - private final StringBuilder mPreviewLabel = new StringBuilder(1); - - // pressed key - private int mPreviousKey = NOT_A_KEY; - - // This class keeps track of a key index and a position where this pointer is. - private static class KeyState { - private final KeyDetector mKeyDetector; - - // The position and time at which first down event occurred. - private int mStartX; - private int mStartY; - private long mDownTime; - - // The current key index where this pointer is. - private int mKeyIndex = NOT_A_KEY; - // The position where mKeyIndex was recognized for the first time. - private int mKeyX; - private int mKeyY; - - // Last pointer position. - private int mLastX; - private int mLastY; - - public KeyState(KeyDetector keyDetecor) { - mKeyDetector = keyDetecor; - } - - public int getKeyIndex() { - return mKeyIndex; - } - - public int getKeyX() { - return mKeyX; - } - - public int getKeyY() { - return mKeyY; - } - - public int getStartX() { - return mStartX; - } - - public int getStartY() { - return mStartY; - } - - public long getDownTime() { - return mDownTime; - } - - public int getLastX() { - return mLastX; - } - - public int getLastY() { - return mLastY; - } - - public int onDownKey(int x, int y, long eventTime) { - mStartX = x; - mStartY = y; - mDownTime = eventTime; - - return onMoveToNewKey(onMoveKeyInternal(x, y), x, y); - } - - private int onMoveKeyInternal(int x, int y) { - mLastX = x; - mLastY = y; - return mKeyDetector.getKeyIndexAndNearbyCodes(x, y, null); - } - - public int onMoveKey(int x, int y) { - return onMoveKeyInternal(x, y); - } - - public int onMoveToNewKey(int keyIndex, int x, int y) { - mKeyIndex = keyIndex; - mKeyX = x; - mKeyY = y; - return keyIndex; - } - - public int onUpKey(int x, int y) { - return onMoveKeyInternal(x, y); - } - - public void onSetKeyboard() { - mKeyIndex = mKeyDetector.getKeyIndexAndNearbyCodes(mKeyX, mKeyY, null); - } - } - - public PointerTracker(int id, UIHandler handler, KeyDetector keyDetector, UIProxy proxy, - Resources res) { - if (proxy == null || handler == null || keyDetector == null) - throw new NullPointerException(); - mPointerId = id; - mProxy = proxy; - mHandler = handler; - mKeyDetector = keyDetector; - mKeyState = new KeyState(keyDetector); - mHasDistinctMultitouch = proxy.hasDistinctMultitouch(); - mDelayBeforeKeyRepeatStart = res.getInteger(R.integer.config_delay_before_key_repeat_start); - mLongPressKeyTimeout = res.getInteger(R.integer.config_long_press_key_timeout); - mLongPressShiftKeyTimeout = res.getInteger(R.integer.config_long_press_shift_key_timeout); - mMultiTapKeyTimeout = res.getInteger(R.integer.config_multi_tap_key_timeout); - resetMultiTap(); - } - - public void setOnKeyboardActionListener(OnKeyboardActionListener listener) { - mListener = listener; - } - - public void setKeyboard(BaseKeyboard keyboard, Key[] keys, float keyHysteresisDistance) { - if (keyboard == null || keys == null || keyHysteresisDistance < 0) - throw new IllegalArgumentException(); - mKeyboard = keyboard; - mKeys = keys; - mKeyHysteresisDistanceSquared = (int)(keyHysteresisDistance * keyHysteresisDistance); - // Update current key index because keyboard layout has been changed. - mKeyState.onSetKeyboard(); - } - - private boolean isValidKeyIndex(int keyIndex) { - return keyIndex >= 0 && keyIndex < mKeys.length; - } - - public Key getKey(int keyIndex) { - return isValidKeyIndex(keyIndex) ? mKeys[keyIndex] : null; - } - - private boolean isModifierInternal(int keyIndex) { - Key key = getKey(keyIndex); - if (key == null) - return false; - int primaryCode = key.codes[0]; - return primaryCode == BaseKeyboard.KEYCODE_SHIFT - || primaryCode == BaseKeyboard.KEYCODE_MODE_CHANGE; - } - - public boolean isModifier() { - return isModifierInternal(mKeyState.getKeyIndex()); - } - - public boolean isOnModifierKey(int x, int y) { - return isModifierInternal(mKeyDetector.getKeyIndexAndNearbyCodes(x, y, null)); - } - - public boolean isSpaceKey(int keyIndex) { - Key key = getKey(keyIndex); - return key != null && key.codes[0] == LatinIME.KEYCODE_SPACE; - } - - public void releaseKey() { - updateKeyGraphics(NOT_A_KEY); - } - - private void updateKeyGraphics(int keyIndex) { - int oldKeyIndex = mPreviousKey; - mPreviousKey = keyIndex; - if (keyIndex != oldKeyIndex) { - if (isValidKeyIndex(oldKeyIndex)) { - // if new key index is not a key, old key was just released inside of the key. - final boolean inside = (keyIndex == NOT_A_KEY); - mKeys[oldKeyIndex].onReleased(inside); - mProxy.invalidateKey(mKeys[oldKeyIndex]); - } - if (isValidKeyIndex(keyIndex)) { - mKeys[keyIndex].onPressed(); - mProxy.invalidateKey(mKeys[keyIndex]); - } - } - } - - public void setAlreadyProcessed() { - mKeyAlreadyProcessed = true; - } - - public void onTouchEvent(int action, int x, int y, long eventTime) { - switch (action) { - case MotionEvent.ACTION_MOVE: - onMoveEvent(x, y, eventTime); - break; - case MotionEvent.ACTION_DOWN: - case MotionEvent.ACTION_POINTER_DOWN: - onDownEvent(x, y, eventTime); - break; - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_POINTER_UP: - onUpEvent(x, y, eventTime); - break; - case MotionEvent.ACTION_CANCEL: - onCancelEvent(x, y, eventTime); - break; - } - } - - public void onDownEvent(int x, int y, long eventTime) { - if (DEBUG) - debugLog("onDownEvent:", x, y); - int keyIndex = mKeyState.onDownKey(x, y, eventTime); - mKeyAlreadyProcessed = false; - mIsRepeatableKey = false; - checkMultiTap(eventTime, keyIndex); - if (mListener != null) { - if (isValidKeyIndex(keyIndex)) { - mListener.onPress(mKeys[keyIndex].codes[0]); - // This onPress call may have changed keyboard layout and have updated mKeyIndex. - // If that's the case, mKeyIndex has been updated in setKeyboard(). - keyIndex = mKeyState.getKeyIndex(); - } - } - if (isValidKeyIndex(keyIndex)) { - if (mKeys[keyIndex].repeatable) { - repeatKey(keyIndex); - mHandler.startKeyRepeatTimer(mDelayBeforeKeyRepeatStart, keyIndex, this); - mIsRepeatableKey = true; - } - startLongPressTimer(keyIndex); - } - showKeyPreviewAndUpdateKeyGraphics(keyIndex); - } - - public void onMoveEvent(int x, int y, long eventTime) { - if (DEBUG_MOVE) - debugLog("onMoveEvent:", x, y); - if (mKeyAlreadyProcessed) - return; - KeyState keyState = mKeyState; - final int keyIndex = keyState.onMoveKey(x, y); - final Key oldKey = getKey(keyState.getKeyIndex()); - if (isValidKeyIndex(keyIndex)) { - if (oldKey == null) { - keyState.onMoveToNewKey(keyIndex, x, y); - startLongPressTimer(keyIndex); - } else if (!isMinorMoveBounce(x, y, keyIndex)) { - if (mListener != null) - mListener.onRelease(oldKey.codes[0]); - resetMultiTap(); - keyState.onMoveToNewKey(keyIndex, x, y); - startLongPressTimer(keyIndex); - } - } else { - if (oldKey != null) { - if (mListener != null) - mListener.onRelease(oldKey.codes[0]); - keyState.onMoveToNewKey(keyIndex, x ,y); - mHandler.cancelLongPressTimers(); - } else if (!isMinorMoveBounce(x, y, keyIndex)) { - resetMultiTap(); - keyState.onMoveToNewKey(keyIndex, x ,y); - mHandler.cancelLongPressTimers(); - } - } - showKeyPreviewAndUpdateKeyGraphics(mKeyState.getKeyIndex()); - } - - public void onUpEvent(int x, int y, long eventTime) { - if (DEBUG) - debugLog("onUpEvent :", x, y); - showKeyPreviewAndUpdateKeyGraphics(NOT_A_KEY); - if (mKeyAlreadyProcessed) - return; - mHandler.cancelKeyTimers(); - mHandler.cancelPopupPreview(); - int keyIndex = mKeyState.onUpKey(x, y); - if (isMinorMoveBounce(x, y, keyIndex)) { - // Use previous fixed key index and coordinates. - keyIndex = mKeyState.getKeyIndex(); - x = mKeyState.getKeyX(); - y = mKeyState.getKeyY(); - } - if (!mIsRepeatableKey) { - detectAndSendKey(keyIndex, x, y, eventTime); - } - - if (isValidKeyIndex(keyIndex)) - mProxy.invalidateKey(mKeys[keyIndex]); - } - - public void onCancelEvent(int x, int y, long eventTime) { - if (DEBUG) - debugLog("onCancelEvt:", x, y); - mHandler.cancelKeyTimers(); - mHandler.cancelPopupPreview(); - showKeyPreviewAndUpdateKeyGraphics(NOT_A_KEY); - int keyIndex = mKeyState.getKeyIndex(); - if (isValidKeyIndex(keyIndex)) - mProxy.invalidateKey(mKeys[keyIndex]); - } - - public void repeatKey(int keyIndex) { - Key key = getKey(keyIndex); - if (key != null) { - // While key is repeating, because there is no need to handle multi-tap key, we can - // pass -1 as eventTime argument. - detectAndSendKey(keyIndex, key.x, key.y, -1); - } - } - - public int getLastX() { - return mKeyState.getLastX(); - } - - public int getLastY() { - return mKeyState.getLastY(); - } - - public long getDownTime() { - return mKeyState.getDownTime(); - } - - // These package scope methods are only for debugging purpose. - /* package */ int getStartX() { - return mKeyState.getStartX(); - } - - /* package */ int getStartY() { - return mKeyState.getStartY(); - } - - private boolean isMinorMoveBounce(int x, int y, int newKey) { - if (mKeys == null || mKeyHysteresisDistanceSquared < 0) - throw new IllegalStateException("keyboard and/or hysteresis not set"); - int curKey = mKeyState.getKeyIndex(); - if (newKey == curKey) { - return true; - } else if (isValidKeyIndex(curKey)) { - return mKeys[curKey].squaredDistanceToEdge(x, y) < mKeyHysteresisDistanceSquared; - } else { - return false; - } - } - - private void showKeyPreviewAndUpdateKeyGraphics(int keyIndex) { - updateKeyGraphics(keyIndex); - // The modifier key, such as shift key, should not be shown as preview when multi-touch is - // supported. On the other hand, if multi-touch is not supported, the modifier key should - // be shown as preview. - if (mHasDistinctMultitouch && isModifier()) { - mProxy.showPreview(NOT_A_KEY, this); - } else { - mProxy.showPreview(keyIndex, this); - } - } - - private void startLongPressTimer(int keyIndex) { - Key key = getKey(keyIndex); - if (key.codes[0] == BaseKeyboard.KEYCODE_SHIFT) { - mHandler.startLongPressShiftTimer(mLongPressShiftKeyTimeout, keyIndex, this); - } else { - mHandler.startLongPressTimer(mLongPressKeyTimeout, keyIndex, this); - } - } - - private boolean isManualTemporaryUpperCase() { - return mKeyboard instanceof LatinKeyboard - && ((LatinKeyboard)mKeyboard).isManualTemporaryUpperCase(); - } - - private void detectAndSendKey(int index, int x, int y, long eventTime) { - final OnKeyboardActionListener listener = mListener; - final Key key = getKey(index); - - if (key == null) { - if (listener != null) - listener.onCancel(); - } else { - if (key.text != null) { - if (listener != null) { - listener.onText(key.text); - listener.onRelease(NOT_A_KEY); - } - } else { - int code = key.codes[0]; - //TextEntryState.keyPressedAt(key, x, y); - int[] codes = mKeyDetector.newCodeArray(); - mKeyDetector.getKeyIndexAndNearbyCodes(x, y, codes); - // Multi-tap - if (mInMultiTap) { - if (mTapCount != -1) { - mListener.onKey(BaseKeyboard.KEYCODE_DELETE, KEY_DELETE, x, y); - } else { - mTapCount = 0; - } - code = key.codes[mTapCount]; - } - - // If keyboard is in manual temporary upper case state and key has manual temporary - // shift code, alternate character code should be sent. - if (isManualTemporaryUpperCase() && key.manualTemporaryUpperCaseCode != 0) { - code = key.manualTemporaryUpperCaseCode; - codes[0] = code; - } - - /* - * Swap the first and second values in the codes array if the primary code is not - * the first value but the second value in the array. This happens when key - * debouncing is in effect. - */ - if (codes.length >= 2 && codes[0] != code && codes[1] == code) { - codes[1] = codes[0]; - codes[0] = code; - } - if (listener != null) { - listener.onKey(code, codes, x, y); - listener.onRelease(code); - } - } - mLastSentIndex = index; - mLastTapTime = eventTime; - } - } - - /** - * Handle multi-tap keys by producing the key label for the current multi-tap state. - */ - public CharSequence getPreviewText(Key key) { - if (mInMultiTap) { - // Multi-tap - mPreviewLabel.setLength(0); - mPreviewLabel.append((char) key.codes[mTapCount < 0 ? 0 : mTapCount]); - return mPreviewLabel; - } else { - return key.label; - } - } - - private void resetMultiTap() { - mLastSentIndex = NOT_A_KEY; - mTapCount = 0; - mLastTapTime = -1; - mInMultiTap = false; - } - - private void checkMultiTap(long eventTime, int keyIndex) { - Key key = getKey(keyIndex); - if (key == null) - return; - - final boolean isMultiTap = - (eventTime < mLastTapTime + mMultiTapKeyTimeout && keyIndex == mLastSentIndex); - if (key.codes.length > 1) { - mInMultiTap = true; - if (isMultiTap) { - mTapCount = (mTapCount + 1) % key.codes.length; - return; - } else { - mTapCount = -1; - return; - } - } - if (!isMultiTap) { - resetMultiTap(); - } - } - - private void debugLog(String title, int x, int y) { - int keyIndex = mKeyDetector.getKeyIndexAndNearbyCodes(x, y, null); - Key key = getKey(keyIndex); - final String code; - if (key == null) { - code = "----"; - } else { - int primaryCode = key.codes[0]; - code = String.format((primaryCode < 0) ? "%4d" : "0x%02x", primaryCode); - } - Log.d(TAG, String.format("%s%s[%d] %3d,%3d %3d(%s) %s", title, - (mKeyAlreadyProcessed ? "-" : " "), mPointerId, x, y, keyIndex, code, - (isModifier() ? "modifier" : ""))); - } -} diff --git a/java/src/com/android/inputmethod/latin/ProximityKeyDetector.java b/java/src/com/android/inputmethod/latin/ProximityKeyDetector.java deleted file mode 100644 index 35bdc6728..000000000 --- a/java/src/com/android/inputmethod/latin/ProximityKeyDetector.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * 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.latin; - -import com.android.inputmethod.latin.BaseKeyboard.Key; - -import java.util.Arrays; - -class ProximityKeyDetector extends KeyDetector { - private static final int MAX_NEARBY_KEYS = 12; - - // working area - private int[] mDistances = new int[MAX_NEARBY_KEYS]; - - @Override - protected int getMaxNearbyKeys() { - return MAX_NEARBY_KEYS; - } - - @Override - public int getKeyIndexAndNearbyCodes(int x, int y, int[] allKeys) { - final Key[] keys = getKeys(); - final int touchX = getTouchX(x); - final int touchY = getTouchY(y); - - int primaryIndex = BaseKeyboardView.NOT_A_KEY; - int closestKeyIndex = BaseKeyboardView.NOT_A_KEY; - int closestKeyDist = mProximityThresholdSquare + 1; - final int[] distances = mDistances; - Arrays.fill(distances, Integer.MAX_VALUE); - for (final int index : mKeyboard.getNearestKeys(touchX, touchY)) { - final Key key = keys[index]; - final boolean isInside = key.isInside(touchX, touchY); - if (isInside) - primaryIndex = index; - final int dist = key.squaredDistanceToEdge(touchX, touchY); - if (isInside || (mProximityCorrectOn && dist < mProximityThresholdSquare)) { - if (dist < closestKeyDist) { - closestKeyDist = dist; - closestKeyIndex = index; - } - - if (allKeys == null) continue; - final int nCodes = key.codes.length; - // Find insertion point - for (int j = 0; j < distances.length; j++) { - if (distances[j] > dist) { - // Make space for nCodes codes - System.arraycopy(distances, j, distances, j + nCodes, - distances.length - (j + nCodes)); - System.arraycopy(allKeys, j, allKeys, j + nCodes, - allKeys.length - (j + nCodes)); - System.arraycopy(key.codes, 0, allKeys, j, nCodes); - Arrays.fill(distances, j, j + nCodes, dist); - break; - } - } - } - } - - return primaryIndex == BaseKeyboardView.NOT_A_KEY ? closestKeyIndex : primaryIndex; - } -} diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java index 103443e6d..a98917689 100644 --- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java +++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java @@ -16,6 +16,7 @@ package com.android.inputmethod.latin; +import com.android.inputmethod.keyboard.LatinKeyboard; import com.android.inputmethod.voice.SettingsUtil; import com.android.inputmethod.voice.VoiceIMEConnector; import com.android.inputmethod.voice.VoiceInput; @@ -197,7 +198,7 @@ public class SubtypeSwitcher { || VoiceIMEConnector.getInstance().needsToShowWarningDialog()) { if (mVoiceInput != null) { // TODO: Call proper function to trigger VoiceIME - mService.onKey(LatinKeyboardView.KEYCODE_VOICE, null, 0, 0); + mService.onKey(LatinKeyboard.KEYCODE_VOICE, null, 0, 0); } } } else { @@ -350,7 +351,7 @@ public class SubtypeSwitcher { if (DBG) { Log.d(TAG, "Set and call voice input."); } - mService.onKey(LatinKeyboardView.KEYCODE_VOICE, null, 0, 0); + mService.onKey(LatinKeyboard.KEYCODE_VOICE, null, 0, 0); return true; } } diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index ca2ffe13a..4c9b7509a 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -423,6 +423,7 @@ public class Suggest implements Dictionary.WordCallback { return false; } + @Override public boolean addWord(final char[] word, final int offset, final int length, int freq, final int dicTypeId, final Dictionary.DataType dataType) { Dictionary.DataType dataTypeForLog = dataType; diff --git a/java/src/com/android/inputmethod/latin/SwipeTracker.java b/java/src/com/android/inputmethod/latin/SwipeTracker.java deleted file mode 100644 index 970e91965..000000000 --- a/java/src/com/android/inputmethod/latin/SwipeTracker.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * 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.latin; - -import android.view.MotionEvent; - -class SwipeTracker { - private static final int NUM_PAST = 4; - private static final int LONGEST_PAST_TIME = 200; - - final EventRingBuffer mBuffer = new EventRingBuffer(NUM_PAST); - - private float mYVelocity; - private float mXVelocity; - - public void addMovement(MotionEvent ev) { - if (ev.getAction() == MotionEvent.ACTION_DOWN) { - mBuffer.clear(); - return; - } - long time = ev.getEventTime(); - final int count = ev.getHistorySize(); - for (int i = 0; i < count; i++) { - addPoint(ev.getHistoricalX(i), ev.getHistoricalY(i), ev.getHistoricalEventTime(i)); - } - addPoint(ev.getX(), ev.getY(), time); - } - - private void addPoint(float x, float y, long time) { - final EventRingBuffer buffer = mBuffer; - while (buffer.size() > 0) { - long lastT = buffer.getTime(0); - if (lastT >= time - LONGEST_PAST_TIME) - break; - buffer.dropOldest(); - } - buffer.add(x, y, time); - } - - public void computeCurrentVelocity(int units) { - computeCurrentVelocity(units, Float.MAX_VALUE); - } - - public void computeCurrentVelocity(int units, float maxVelocity) { - final EventRingBuffer buffer = mBuffer; - final float oldestX = buffer.getX(0); - final float oldestY = buffer.getY(0); - final long oldestTime = buffer.getTime(0); - - float accumX = 0; - float accumY = 0; - final int count = buffer.size(); - for (int pos = 1; pos < count; pos++) { - final int dur = (int)(buffer.getTime(pos) - oldestTime); - if (dur == 0) continue; - float dist = buffer.getX(pos) - oldestX; - float vel = (dist / dur) * units; // pixels/frame. - if (accumX == 0) accumX = vel; - else accumX = (accumX + vel) * .5f; - - dist = buffer.getY(pos) - oldestY; - vel = (dist / dur) * units; // pixels/frame. - if (accumY == 0) accumY = vel; - else accumY = (accumY + vel) * .5f; - } - mXVelocity = accumX < 0.0f ? Math.max(accumX, -maxVelocity) - : Math.min(accumX, maxVelocity); - mYVelocity = accumY < 0.0f ? Math.max(accumY, -maxVelocity) - : Math.min(accumY, maxVelocity); - } - - public float getXVelocity() { - return mXVelocity; - } - - public float getYVelocity() { - return mYVelocity; - } - - static class EventRingBuffer { - private final int bufSize; - private final float xBuf[]; - private final float yBuf[]; - private final long timeBuf[]; - private int top; // points new event - private int end; // points oldest event - private int count; // the number of valid data - - public EventRingBuffer(int max) { - this.bufSize = max; - xBuf = new float[max]; - yBuf = new float[max]; - timeBuf = new long[max]; - clear(); - } - - public void clear() { - top = end = count = 0; - } - - public int size() { - return count; - } - - // Position 0 points oldest event - private int index(int pos) { - return (end + pos) % bufSize; - } - - private int advance(int index) { - return (index + 1) % bufSize; - } - - public void add(float x, float y, long time) { - xBuf[top] = x; - yBuf[top] = y; - timeBuf[top] = time; - top = advance(top); - if (count < bufSize) { - count++; - } else { - end = advance(end); - } - } - - public float getX(int pos) { - return xBuf[index(pos)]; - } - - public float getY(int pos) { - return yBuf[index(pos)]; - } - - public long getTime(int pos) { - return timeBuf[index(pos)]; - } - - public void dropOldest() { - count--; - end = advance(end); - } - } -}
\ No newline at end of file diff --git a/java/src/com/android/inputmethod/latin/TextEntryState.java b/java/src/com/android/inputmethod/latin/TextEntryState.java index 56347af5d..31a192bfe 100644 --- a/java/src/com/android/inputmethod/latin/TextEntryState.java +++ b/java/src/com/android/inputmethod/latin/TextEntryState.java @@ -16,7 +16,7 @@ package com.android.inputmethod.latin; -import com.android.inputmethod.latin.BaseKeyboard.Key; +import com.android.inputmethod.keyboard.Key; import android.content.Context; import android.text.format.DateFormat; diff --git a/java/src/com/android/inputmethod/latin/Tutorial.java b/java/src/com/android/inputmethod/latin/Tutorial.java index cd7636f3c..bd069bd0d 100644 --- a/java/src/com/android/inputmethod/latin/Tutorial.java +++ b/java/src/com/android/inputmethod/latin/Tutorial.java @@ -16,6 +16,8 @@ package com.android.inputmethod.latin; +import com.android.inputmethod.keyboard.LatinKeyboardView; + import android.content.Context; import android.graphics.drawable.Drawable; import android.os.Handler; @@ -133,6 +135,7 @@ public class Tutorial implements OnTouchListener { if ((gravity & Gravity.BOTTOM) == Gravity.BOTTOM) offy -= window.getHeight(); if ((gravity & Gravity.RIGHT) == Gravity.RIGHT) offx -= window.getWidth(); textView.setOnTouchListener(new View.OnTouchListener() { + @Override public boolean onTouch(View view, MotionEvent me) { Tutorial.this.next(); return true; @@ -237,6 +240,7 @@ public class Tutorial implements OnTouchListener { return true; } + @Override public boolean onTouch(View v, MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { next(); diff --git a/java/src/com/android/inputmethod/latin/UserDictionary.java b/java/src/com/android/inputmethod/latin/UserDictionary.java index 49b95e9aa..a522303e6 100644 --- a/java/src/com/android/inputmethod/latin/UserDictionary.java +++ b/java/src/com/android/inputmethod/latin/UserDictionary.java @@ -97,6 +97,7 @@ public class UserDictionary extends ExpandableDictionary { final ContentResolver contentResolver = getContext().getContentResolver(); new Thread("addWord") { + @Override public void run() { contentResolver.insert(Words.CONTENT_URI, values); } |