From 5ac4638f999db4fea8a9e24171dbceb640a10858 Mon Sep 17 00:00:00 2001 From: Alan Viverette Date: Tue, 17 May 2011 17:03:25 -0700 Subject: Added support for touch exploration to Latin IME. Bug: 4379983 Change-Id: I97f22e54827c6229054b514801401ffa5b4ed3b8 --- .../accessibility/KeyCodeDescriptionMapper.java | 226 +++++++++++++++++++++ 1 file changed, 226 insertions(+) create mode 100644 java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java (limited to 'java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java') diff --git a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java new file mode 100644 index 000000000..154f4af91 --- /dev/null +++ b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java @@ -0,0 +1,226 @@ +/* + * Copyright (C) 2011 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.accessibility; + +import android.content.Context; +import android.content.SharedPreferences; +import android.text.TextUtils; + +import com.android.inputmethod.keyboard.Key; +import com.android.inputmethod.keyboard.Keyboard; +import com.android.inputmethod.keyboard.KeyboardId; +import com.android.inputmethod.latin.R; + +import java.util.HashMap; + +public class KeyCodeDescriptionMapper { + private static KeyCodeDescriptionMapper sInstance = new KeyCodeDescriptionMapper(); + + // Map of key labels to spoken description resource IDs + private final HashMap mKeyLabelMap; + + // Map of key codes to spoken description resource IDs + private final HashMap mKeyCodeMap; + + // Map of shifted key codes to spoken description resource IDs + private final HashMap mShiftedKeyCodeMap; + + // Map of shift-locked key codes to spoken description resource IDs + private final HashMap mShiftLockedKeyCodeMap; + + public static void init(Context context, SharedPreferences prefs) { + sInstance.initInternal(context, prefs); + } + + public static KeyCodeDescriptionMapper getInstance() { + return sInstance; + } + + private KeyCodeDescriptionMapper() { + mKeyLabelMap = new HashMap(); + mKeyCodeMap = new HashMap(); + mShiftedKeyCodeMap = new HashMap(); + mShiftLockedKeyCodeMap = new HashMap(); + } + + private void initInternal(Context context, SharedPreferences prefs) { + // Manual label substitutions for key labels with no string resource + mKeyLabelMap.put(":-)", R.string.spoken_description_smiley); + + // Symbols that most TTS engines can't speak + mKeyCodeMap.put((int) '.', R.string.spoken_description_period); + mKeyCodeMap.put((int) ',', R.string.spoken_description_comma); + mKeyCodeMap.put((int) '(', R.string.spoken_description_left_parenthesis); + mKeyCodeMap.put((int) ')', R.string.spoken_description_right_parenthesis); + mKeyCodeMap.put((int) ':', R.string.spoken_description_colon); + mKeyCodeMap.put((int) ';', R.string.spoken_description_semicolon); + mKeyCodeMap.put((int) '!', R.string.spoken_description_exclamation_mark); + mKeyCodeMap.put((int) '?', R.string.spoken_description_question_mark); + mKeyCodeMap.put((int) '\"', R.string.spoken_description_double_quote); + mKeyCodeMap.put((int) '\'', R.string.spoken_description_single_quote); + mKeyCodeMap.put((int) '*', R.string.spoken_description_star); + mKeyCodeMap.put((int) '#', R.string.spoken_description_pound); + mKeyCodeMap.put((int) ' ', R.string.spoken_description_space); + + // Non-ASCII symbols (must use escape codes!) + mKeyCodeMap.put((int) '\u2022', R.string.spoken_description_dot); + mKeyCodeMap.put((int) '\u221A', R.string.spoken_description_square_root); + mKeyCodeMap.put((int) '\u03C0', R.string.spoken_description_pi); + mKeyCodeMap.put((int) '\u0394', R.string.spoken_description_delta); + mKeyCodeMap.put((int) '\u2122', R.string.spoken_description_trademark); + mKeyCodeMap.put((int) '\u2105', R.string.spoken_description_care_of); + mKeyCodeMap.put((int) '\u2026', R.string.spoken_description_ellipsis); + mKeyCodeMap.put((int) '\u201E', R.string.spoken_description_low_double_quote); + + // Special non-character codes defined in Keyboard + mKeyCodeMap.put(Keyboard.CODE_DELETE, R.string.spoken_description_delete); + mKeyCodeMap.put(Keyboard.CODE_ENTER, R.string.spoken_description_return); + mKeyCodeMap.put(Keyboard.CODE_SETTINGS, R.string.spoken_description_settings); + mKeyCodeMap.put(Keyboard.CODE_SHIFT, R.string.spoken_description_shift); + mKeyCodeMap.put(Keyboard.CODE_SHORTCUT, R.string.spoken_description_mic); + mKeyCodeMap.put(Keyboard.CODE_SWITCH_ALPHA_SYMBOL, R.string.spoken_description_to_symbol); + mKeyCodeMap.put(Keyboard.CODE_TAB, R.string.spoken_description_tab); + + // Shifted versions of non-character codes defined in Keyboard + mShiftedKeyCodeMap.put(Keyboard.CODE_SHIFT, R.string.spoken_description_shift_shifted); + + // Shift-locked versions of non-character codes defined in Keyboard + mShiftLockedKeyCodeMap.put(Keyboard.CODE_SHIFT, R.string.spoken_description_caps_lock); + } + + /** + * Returns the localized description of the action performed by a specified + * key based on the current keyboard state. + *

+ * The order of precedence for key descriptions is: + *

    + *
  1. Manually-defined based on the key label
  2. + *
  3. Automatic or manually-defined based on the key code
  4. + *
  5. Automatically based on the key label
  6. + *
  7. {code null} for keys with no label or key code defined
  8. + *

    + * + * @param context The package's context. + * @param keyboard The keyboard on which the key resides. + * @param key The key from which to obtain a description. + * @return a character sequence describing the action performed by pressing + * the key + */ + public CharSequence getDescriptionForKey(Context context, Keyboard keyboard, Key key) { + if (key.mCode == Keyboard.CODE_SWITCH_ALPHA_SYMBOL) { + final CharSequence description = getDescriptionForSwitchAlphaSymbol(context, keyboard); + if (description != null) + return description; + } + + if (!TextUtils.isEmpty(key.mLabel)) { + final String label = key.mLabel.toString().trim(); + + if (mKeyLabelMap.containsKey(label)) { + return context.getString(mKeyLabelMap.get(label)); + } else if (label.length() == 1 + || (keyboard.isManualTemporaryUpperCase() && !TextUtils + .isEmpty(key.mHintLetter))) { + return getDescriptionForKeyCode(context, keyboard, key); + } else { + return label; + } + } else if (key.mCode != Keyboard.CODE_DUMMY) { + return getDescriptionForKeyCode(context, keyboard, key); + } + + return null; + } + + /** + * Returns a context-specific description for the CODE_SWITCH_ALPHA_SYMBOL + * key or {@code null} if there is not a description provided for the + * current keyboard context. + * + * @param context The package's context. + * @param keyboard The keyboard on which the key resides. + * @return a character sequence describing the action performed by pressing + * the key + */ + private CharSequence getDescriptionForSwitchAlphaSymbol(Context context, Keyboard keyboard) { + final KeyboardId id = keyboard.mId; + + if (id.isAlphabetKeyboard()) { + return context.getString(R.string.spoken_description_to_symbol); + } else if (id.isSymbolsKeyboard()) { + return context.getString(R.string.spoken_description_to_alpha); + } else if (id.isPhoneSymbolsKeyboard()) { + return context.getString(R.string.spoken_description_to_numeric); + } else if (id.isPhoneKeyboard()) { + return context.getString(R.string.spoken_description_to_symbol); + } else { + return null; + } + } + + /** + * Returns the keycode for the specified key given the current keyboard + * state. + * + * @param keyboard The keyboard on which the key resides. + * @param key The key from which to obtain a key code. + * @return the key code for the specified key + */ + private int getCorrectKeyCode(Keyboard keyboard, Key key) { + if (keyboard.isManualTemporaryUpperCase() && !TextUtils.isEmpty(key.mHintLetter)) { + return key.mHintLetter.charAt(0); + } else { + return key.mCode; + } + } + + /** + * Returns a localized character sequence describing what will happen when + * the specified key is pressed based on its key code. + *

    + * The order of precedence for key code descriptions is: + *

      + *
    1. Manually-defined shift-locked description
    2. + *
    3. Manually-defined shifted description
    4. + *
    5. Manually-defined normal description
    6. + *
    7. Automatic based on the character represented by the key code
    8. + *
    9. Fall-back for undefined or control characters
    10. + *
    + *

    + * + * @param context The package's context. + * @param keyboard The keyboard on which the key resides. + * @param key The key from which to obtain a description. + * @return a character sequence describing the action performed by pressing + * the key + */ + private CharSequence getDescriptionForKeyCode(Context context, Keyboard keyboard, Key key) { + final int code = getCorrectKeyCode(keyboard, key); + + if (keyboard.isShiftLocked() && mShiftLockedKeyCodeMap.containsKey(code)) { + return context.getString(mShiftLockedKeyCodeMap.get(code)); + } else if (keyboard.isShiftedOrShiftLocked() && mShiftedKeyCodeMap.containsKey(code)) { + return context.getString(mShiftedKeyCodeMap.get(code)); + } else if (mKeyCodeMap.containsKey(code)) { + return context.getString(mKeyCodeMap.get(code)); + } else if (Character.isDefined(code) && !Character.isISOControl(code)) { + return Character.toString((char) code); + } else { + return context.getString(R.string.spoken_description_unknown, code); + } + } +} -- cgit v1.2.3-83-g751a From 44f8dc3132ebfd5886749a888b6388963293d298 Mon Sep 17 00:00:00 2001 From: Alan Viverette Date: Wed, 22 Jun 2011 12:08:10 -0700 Subject: Fixing build error due to refactored import. Change-Id: I7ed51fa4373fd5d1ebc6ebe52c6e93b27b43cff5 --- .../android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java | 2 +- .../com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java') diff --git a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java index 96f7fc9f2..1adef9057 100644 --- a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java +++ b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java @@ -27,10 +27,10 @@ import android.view.accessibility.AccessibilityEvent; import com.android.inputmethod.compat.AccessibilityEventCompatUtils; import com.android.inputmethod.compat.MotionEventCompatUtils; -import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.KeyDetector; import com.android.inputmethod.keyboard.KeyboardView; import com.android.inputmethod.keyboard.PointerTracker; +import com.android.inputmethod.keyboard.internal.Key; public class AccessibleKeyboardViewProxy { private static final String TAG = AccessibleKeyboardViewProxy.class.getSimpleName(); diff --git a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java index 154f4af91..5e6f10b04 100644 --- a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java +++ b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java @@ -20,9 +20,9 @@ import android.content.Context; import android.content.SharedPreferences; import android.text.TextUtils; -import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardId; +import com.android.inputmethod.keyboard.internal.Key; import com.android.inputmethod.latin.R; import java.util.HashMap; -- cgit v1.2.3-83-g751a From e7759091ddb5ec18268945d70d9212195bf6497b Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Thu, 23 Jun 2011 21:23:44 +0900 Subject: Move Key class out of internal package Bug: 4903326 Change-Id: I2493d6dae613d2e37dfa8ce96b4cddc9a038160c --- .../accessibility/AccessibleKeyboardViewProxy.java | 2 +- .../accessibility/KeyCodeDescriptionMapper.java | 2 +- java/src/com/android/inputmethod/keyboard/Key.java | 474 +++++++++++++++++++++ .../android/inputmethod/keyboard/KeyDetector.java | 2 - .../com/android/inputmethod/keyboard/Keyboard.java | 3 +- .../inputmethod/keyboard/KeyboardSwitcher.java | 1 - .../android/inputmethod/keyboard/KeyboardView.java | 3 +- .../inputmethod/keyboard/LatinKeyboard.java | 3 +- .../inputmethod/keyboard/LatinKeyboardView.java | 1 - .../android/inputmethod/keyboard/MiniKeyboard.java | 2 - .../keyboard/MiniKeyboardKeyDetector.java | 2 - .../inputmethod/keyboard/PointerTracker.java | 13 +- .../keyboard/PopupMiniKeyboardView.java | 1 - .../android/inputmethod/keyboard/PopupPanel.java | 2 - .../inputmethod/keyboard/ProximityInfo.java | 1 - .../android/inputmethod/keyboard/internal/Key.java | 458 -------------------- .../keyboard/internal/KeyboardParser.java | 3 +- .../keyboard/internal/MiniKeyboardBuilder.java | 1 + .../android/inputmethod/latin/SuggestHelper.java | 2 +- 19 files changed, 489 insertions(+), 487 deletions(-) create mode 100644 java/src/com/android/inputmethod/keyboard/Key.java delete mode 100644 java/src/com/android/inputmethod/keyboard/internal/Key.java (limited to 'java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java') diff --git a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java index 1adef9057..96f7fc9f2 100644 --- a/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java +++ b/java/src/com/android/inputmethod/accessibility/AccessibleKeyboardViewProxy.java @@ -27,10 +27,10 @@ import android.view.accessibility.AccessibilityEvent; import com.android.inputmethod.compat.AccessibilityEventCompatUtils; import com.android.inputmethod.compat.MotionEventCompatUtils; +import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.KeyDetector; import com.android.inputmethod.keyboard.KeyboardView; import com.android.inputmethod.keyboard.PointerTracker; -import com.android.inputmethod.keyboard.internal.Key; public class AccessibleKeyboardViewProxy { private static final String TAG = AccessibleKeyboardViewProxy.class.getSimpleName(); diff --git a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java index 5e6f10b04..154f4af91 100644 --- a/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java +++ b/java/src/com/android/inputmethod/accessibility/KeyCodeDescriptionMapper.java @@ -20,9 +20,9 @@ import android.content.Context; import android.content.SharedPreferences; import android.text.TextUtils; +import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardId; -import com.android.inputmethod.keyboard.internal.Key; import com.android.inputmethod.latin.R; import java.util.HashMap; diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java new file mode 100644 index 000000000..2850c95df --- /dev/null +++ b/java/src/com/android/inputmethod/keyboard/Key.java @@ -0,0 +1,474 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.android.inputmethod.keyboard; + +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.content.res.XmlResourceParser; +import android.graphics.drawable.Drawable; +import android.text.TextUtils; +import android.util.Xml; + +import com.android.inputmethod.keyboard.internal.KeyStyles; +import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; +import com.android.inputmethod.keyboard.internal.KeyboardParser; +import com.android.inputmethod.keyboard.internal.PopupCharactersParser; +import com.android.inputmethod.keyboard.internal.Row; +import com.android.inputmethod.keyboard.internal.KeyStyles.KeyStyle; +import com.android.inputmethod.keyboard.internal.KeyboardParser.ParseException; +import com.android.inputmethod.latin.R; + +import java.util.ArrayList; + +/** + * Class for describing the position and characteristics of a single key in the keyboard. + */ +public class Key { + /** + * The key code (unicode or custom code) that this key generates. + */ + public final int mCode; + + /** Label to display */ + public final CharSequence mLabel; + /** Hint letter to display on the key in conjunction with the label */ + public final CharSequence mHintLetter; + /** Option of the label */ + public final int mLabelOption; + public static final int LABEL_OPTION_ALIGN_LEFT = 0x01; + public static final int LABEL_OPTION_ALIGN_RIGHT = 0x02; + public static final int LABEL_OPTION_ALIGN_BOTTOM = 0x08; + public static final int LABEL_OPTION_FONT_NORMAL = 0x10; + public static final int LABEL_OPTION_FONT_FIXED_WIDTH = 0x20; + public static final int LABEL_OPTION_FOLLOW_KEY_LETTER_RATIO = 0x40; + private static final int LABEL_OPTION_POPUP_HINT = 0x80; + private static final int LABEL_OPTION_HAS_UPPERCASE_LETTER = 0x100; + + /** Icon to display instead of a label. Icon takes precedence over a label */ + private Drawable mIcon; + /** Preview version of the icon, for the preview popup */ + private Drawable mPreviewIcon; + + /** Width of the key, not including the gap */ + public final int mWidth; + /** Height of the key, not including the gap */ + public final int mHeight; + /** The horizontal gap around this key */ + public final int mGap; + /** The visual insets */ + public final int mVisualInsetsLeft; + public final int mVisualInsetsRight; + /** Whether this key is sticky, i.e., a toggle key */ + public final boolean mSticky; + /** X coordinate of the key in the keyboard layout */ + public final int mX; + /** Y coordinate of the key in the keyboard layout */ + public final int mY; + /** Text to output when pressed. This can be multiple characters, like ".com" */ + public final CharSequence mOutputText; + /** Popup characters */ + public final CharSequence[] mPopupCharacters; + /** Popup keyboard maximum column number */ + public final int mMaxPopupColumn; + + /** + * Flags that specify the anchoring to edges of the keyboard for detecting touch events + * that are just out of the boundary of the key. This is a bit mask of + * {@link Keyboard#EDGE_LEFT}, {@link Keyboard#EDGE_RIGHT}, + * {@link Keyboard#EDGE_TOP} and {@link Keyboard#EDGE_BOTTOM}. + */ + public final int mEdgeFlags; + /** Whether this is a functional key which has different key top than normal key */ + public final boolean mFunctional; + /** Whether this key repeats itself when held down */ + public final boolean mRepeatable; + + /** The Keyboard that this key belongs to */ + private final Keyboard mKeyboard; + + /** The current pressed state of this key */ + private boolean mPressed; + /** If this is a sticky key, is its highlight on? */ + private boolean mHighlightOn; + /** Key is enabled and responds on press */ + private boolean mEnabled = true; + + // keyWidth constants + private static final int KEYWIDTH_FILL_RIGHT = 0; + private static final int KEYWIDTH_FILL_BOTH = -1; + + 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 + }; + + // functional normal state (with properties) + private static final int[] KEY_STATE_FUNCTIONAL_NORMAL = { + android.R.attr.state_single + }; + + // functional pressed state (with properties) + private static final int[] KEY_STATE_FUNCTIONAL_PRESSED = { + android.R.attr.state_single, + android.R.attr.state_pressed + }; + + /** + * This constructor is being used only for key in popup mini keyboard. + */ + public Key(Resources res, Keyboard keyboard, CharSequence popupCharacter, int x, int y, + int width, int height, int edgeFlags) { + mKeyboard = keyboard; + mHeight = height - keyboard.getVerticalGap(); + mGap = keyboard.getHorizontalGap(); + mVisualInsetsLeft = mVisualInsetsRight = 0; + mWidth = width - mGap; + mEdgeFlags = edgeFlags; + mHintLetter = null; + mLabelOption = 0; + mFunctional = false; + mSticky = false; + mRepeatable = false; + mPopupCharacters = null; + mMaxPopupColumn = 0; + final String popupSpecification = popupCharacter.toString(); + mLabel = PopupCharactersParser.getLabel(popupSpecification); + mOutputText = PopupCharactersParser.getOutputText(popupSpecification); + mCode = PopupCharactersParser.getCode(res, popupSpecification); + mIcon = keyboard.mIconsSet.getIcon(PopupCharactersParser.getIconId(popupSpecification)); + // Horizontal gap is divided equally to both sides of the key. + mX = x + mGap / 2; + mY = y; + } + + /** + * 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 row the row that this key belongs to. The row must already be attached to + * a {@link Keyboard}. + * @param x the x coordinate of the top-left + * @param y the y coordinate of the top-left + * @param parser the XML parser containing the attributes for this key + * @param keyStyles active key styles set + */ + public Key(Resources res, Row row, int x, int y, XmlResourceParser parser, + KeyStyles keyStyles) { + mKeyboard = row.getKeyboard(); + + final TypedArray keyboardAttr = res.obtainAttributes(Xml.asAttributeSet(parser), + R.styleable.Keyboard); + int keyWidth; + try { + mHeight = KeyboardParser.getDimensionOrFraction(keyboardAttr, + R.styleable.Keyboard_rowHeight, + mKeyboard.getKeyboardHeight(), row.mDefaultHeight) - row.mVerticalGap; + mGap = KeyboardParser.getDimensionOrFraction(keyboardAttr, + R.styleable.Keyboard_horizontalGap, + mKeyboard.getDisplayWidth(), row.mDefaultHorizontalGap); + keyWidth = KeyboardParser.getDimensionOrFraction(keyboardAttr, + R.styleable.Keyboard_keyWidth, + mKeyboard.getDisplayWidth(), row.mDefaultWidth); + } finally { + keyboardAttr.recycle(); + } + + final TypedArray keyAttr = res.obtainAttributes(Xml.asAttributeSet(parser), + R.styleable.Keyboard_Key); + try { + final KeyStyle style; + if (keyAttr.hasValue(R.styleable.Keyboard_Key_keyStyle)) { + String styleName = keyAttr.getString(R.styleable.Keyboard_Key_keyStyle); + style = keyStyles.getKeyStyle(styleName); + if (style == null) + throw new ParseException("Unknown key style: " + styleName, parser); + } else { + style = keyStyles.getEmptyKeyStyle(); + } + + final int keyboardWidth = mKeyboard.getDisplayWidth(); + int keyXPos = KeyboardParser.getDimensionOrFraction(keyAttr, + R.styleable.Keyboard_Key_keyXPos, keyboardWidth, x); + if (keyXPos < 0) { + // If keyXPos is negative, the actual x-coordinate will be k + keyXPos. + keyXPos += keyboardWidth; + if (keyXPos < x) { + // keyXPos shouldn't be less than x because drawable area for this key starts + // at x. Or, this key will overlaps the adjacent key on its left hand side. + keyXPos = x; + } + } + if (keyWidth == KEYWIDTH_FILL_RIGHT) { + // If keyWidth is zero, the actual key width will be determined to fill out the + // area up to the right edge of the keyboard. + keyWidth = keyboardWidth - keyXPos; + } else if (keyWidth <= KEYWIDTH_FILL_BOTH) { + // If keyWidth is negative, the actual key width will be determined to fill out the + // area between the nearest key on the left hand side and the right edge of the + // keyboard. + keyXPos = x; + keyWidth = keyboardWidth - keyXPos; + } + + // Horizontal gap is divided equally to both sides of the key. + mX = keyXPos + mGap / 2; + mY = y; + mWidth = keyWidth - mGap; + + final CharSequence[] popupCharacters = style.getTextArray(keyAttr, + R.styleable.Keyboard_Key_popupCharacters); + if (res.getBoolean(R.bool.config_digit_popup_characters_enabled)) { + mPopupCharacters = popupCharacters; + } else { + mPopupCharacters = filterOutDigitPopupCharacters(popupCharacters); + } + mMaxPopupColumn = style.getInt(keyboardAttr, + R.styleable.Keyboard_Key_maxPopupKeyboardColumn, + mKeyboard.getMaxPopupKeyboardColumn()); + + mRepeatable = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_isRepeatable, false); + mFunctional = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_isFunctional, false); + mSticky = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_isSticky, false); + mEnabled = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_enabled, true); + mEdgeFlags = style.getFlag(keyAttr, R.styleable.Keyboard_Key_keyEdgeFlags, 0) + | row.mRowEdgeFlags; + + final KeyboardIconsSet iconsSet = mKeyboard.mIconsSet; + mVisualInsetsLeft = KeyboardParser.getDimensionOrFraction(keyAttr, + R.styleable.Keyboard_Key_visualInsetsLeft, mKeyboard.getDisplayHeight(), 0); + mVisualInsetsRight = KeyboardParser.getDimensionOrFraction(keyAttr, + R.styleable.Keyboard_Key_visualInsetsRight, mKeyboard.getDisplayHeight(), 0); + mPreviewIcon = iconsSet.getIcon(style.getInt( + keyAttr, R.styleable.Keyboard_Key_keyIconPreview, + KeyboardIconsSet.ICON_UNDEFINED)); + Keyboard.setDefaultBounds(mPreviewIcon); + mIcon = iconsSet.getIcon(style.getInt( + keyAttr, R.styleable.Keyboard_Key_keyIcon, + KeyboardIconsSet.ICON_UNDEFINED)); + Keyboard.setDefaultBounds(mIcon); + mHintLetter = style.getText(keyAttr, R.styleable.Keyboard_Key_keyHintLetter); + + mLabel = style.getText(keyAttr, R.styleable.Keyboard_Key_keyLabel); + mLabelOption = style.getFlag(keyAttr, R.styleable.Keyboard_Key_keyLabelOption, 0); + mOutputText = style.getText(keyAttr, R.styleable.Keyboard_Key_keyOutputText); + // Choose the first letter of the label as primary code if not + // specified. + final int code = style.getInt(keyAttr, R.styleable.Keyboard_Key_code, + Keyboard.CODE_UNSPECIFIED); + if (code == Keyboard.CODE_UNSPECIFIED && !TextUtils.isEmpty(mLabel)) { + mCode = mLabel.charAt(0); + } else if (code != Keyboard.CODE_UNSPECIFIED) { + mCode = code; + } else { + mCode = Keyboard.CODE_DUMMY; + } + + final Drawable shiftedIcon = iconsSet.getIcon(style.getInt( + keyAttr, R.styleable.Keyboard_Key_keyIconShifted, + KeyboardIconsSet.ICON_UNDEFINED)); + if (shiftedIcon != null) + mKeyboard.getShiftedIcons().put(this, shiftedIcon); + } finally { + keyAttr.recycle(); + } + } + + public boolean hasPopupHint() { + return (mLabelOption & LABEL_OPTION_POPUP_HINT) != 0; + } + + public boolean hasUppercaseLetter() { + return (mLabelOption & LABEL_OPTION_HAS_UPPERCASE_LETTER) != 0; + } + + private static boolean isDigitPopupCharacter(CharSequence label) { + return label != null && label.length() == 1 && Character.isDigit(label.charAt(0)); + } + + private static CharSequence[] filterOutDigitPopupCharacters(CharSequence[] popupCharacters) { + if (popupCharacters == null || popupCharacters.length < 1) + return null; + if (popupCharacters.length == 1 && isDigitPopupCharacter( + PopupCharactersParser.getLabel(popupCharacters[0].toString()))) + return null; + ArrayList filtered = null; + for (int i = 0; i < popupCharacters.length; i++) { + final CharSequence popupSpec = popupCharacters[i]; + if (isDigitPopupCharacter(PopupCharactersParser.getLabel(popupSpec.toString()))) { + if (filtered == null) { + filtered = new ArrayList(); + for (int j = 0; j < i; j++) + filtered.add(popupCharacters[j]); + } + } else if (filtered != null) { + filtered.add(popupSpec); + } + } + if (filtered == null) + return popupCharacters; + if (filtered.size() == 0) + return null; + return filtered.toArray(new CharSequence[filtered.size()]); + } + + public Drawable getIcon() { + return mIcon; + } + + public Drawable getPreviewIcon() { + return mPreviewIcon; + } + + public void setIcon(Drawable icon) { + mIcon = icon; + } + + public void setPreviewIcon(Drawable icon) { + mPreviewIcon = icon; + } + + /** + * Informs the key that it has been pressed, in case it needs to change its appearance or + * state. + * @see #onReleased() + */ + public void onPressed() { + mPressed = true; + } + + /** + * Informs the key that it has been released, in case it needs to change its appearance or + * state. + * @see #onPressed() + */ + public void onReleased() { + mPressed = false; + } + + public void setHighlightOn(boolean highlightOn) { + mHighlightOn = highlightOn; + } + + public boolean isEnabled() { + return mEnabled; + } + + public void setEnabled(boolean enabled) { + mEnabled = enabled; + } + + /** + * Detects if a point falls on this key. + * @param x the x-coordinate of the point + * @param y the y-coordinate of the point + * @return whether or not the point falls on the key. If the key is attached to an edge, it will + * assume that all points between the key and the edge are considered to be on the key. + */ + public boolean isOnKey(int x, int y) { + final int flags = mEdgeFlags; + final boolean leftEdge = (flags & Keyboard.EDGE_LEFT) != 0; + final boolean rightEdge = (flags & Keyboard.EDGE_RIGHT) != 0; + final boolean topEdge = (flags & Keyboard.EDGE_TOP) != 0; + final boolean bottomEdge = (flags & Keyboard.EDGE_BOTTOM) != 0; + final int left = mX - mGap / 2; + final int right = left + mWidth + mGap; + final int top = mY; + final int bottom = top + mHeight + mKeyboard.getVerticalGap(); + // In order to mitigate rounding errors, we use (left <= x <= right) here. + return (x >= left || leftEdge) && (x <= right || rightEdge) + && (y >= top || topEdge) && (y <= bottom || bottomEdge); + } + + /** + * Returns the square of the distance to the nearest edge of the key and the given point. + * @param x the x-coordinate of the point + * @param y the y-coordinate of the point + * @return the square of the distance of the point from the nearest edge of the key + */ + public int squaredDistanceToEdge(int x, int y) { + final int left = mX; + final int right = left + mWidth; + final int top = mY; + final int bottom = top + mHeight; + final int edgeX = x < left ? left : (x > right ? right : x); + final int edgeY = y < top ? top : (y > bottom ? bottom : y); + final int dx = x - edgeX; + final int dy = y - edgeY; + return dx * dx + dy * dy; + } + + /** + * 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() { + final boolean pressed = mPressed; + if (!mSticky && mFunctional) { + if (pressed) { + return KEY_STATE_FUNCTIONAL_PRESSED; + } else { + return KEY_STATE_FUNCTIONAL_NORMAL; + } + } + + int[] states = KEY_STATE_NORMAL; + + if (mHighlightOn) { + if (pressed) { + states = KEY_STATE_PRESSED_ON; + } else { + states = KEY_STATE_NORMAL_ON; + } + } else { + if (mSticky) { + if (pressed) { + states = KEY_STATE_PRESSED_OFF; + } else { + states = KEY_STATE_NORMAL_OFF; + } + } else { + if (pressed) { + states = KEY_STATE_PRESSED; + } + } + } + return states; + } +} diff --git a/java/src/com/android/inputmethod/keyboard/KeyDetector.java b/java/src/com/android/inputmethod/keyboard/KeyDetector.java index 818f3f925..7add43a6d 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyDetector.java +++ b/java/src/com/android/inputmethod/keyboard/KeyDetector.java @@ -18,8 +18,6 @@ package com.android.inputmethod.keyboard; import android.util.Log; -import com.android.inputmethod.keyboard.internal.Key; - import java.util.Arrays; import java.util.List; diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java index 889d54bf3..20327c5b2 100644 --- a/java/src/com/android/inputmethod/keyboard/Keyboard.java +++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java @@ -21,7 +21,6 @@ import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.util.Log; -import com.android.inputmethod.keyboard.internal.Key; import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; import com.android.inputmethod.keyboard.internal.KeyboardParser; import com.android.inputmethod.keyboard.internal.KeyboardShiftState; @@ -297,7 +296,7 @@ public class Keyboard { public boolean setShiftLocked(boolean newShiftLockState) { final Map shiftedIcons = getShiftedIcons(); for (final Key key : getShiftKeys()) { - key.mHighlightOn = newShiftLockState; + key.setHighlightOn(newShiftLockState); key.setIcon(newShiftLockState ? shiftedIcons.get(key) : mNormalShiftIcons.get(key)); } mShiftState.setShiftLocked(newShiftLockState); diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index 1ad5b08eb..eef01a67c 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -28,7 +28,6 @@ import android.view.inputmethod.EditorInfo; import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy; import com.android.inputmethod.compat.InputMethodManagerCompatWrapper; -import com.android.inputmethod.keyboard.internal.Key; import com.android.inputmethod.keyboard.internal.ModifierKeyState; import com.android.inputmethod.keyboard.internal.ShiftKeyState; import com.android.inputmethod.latin.LatinIME; diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java index a6aef27e0..6c6d2df19 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java @@ -49,7 +49,6 @@ import android.widget.TextView; import com.android.inputmethod.accessibility.AccessibilityUtils; import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy; import com.android.inputmethod.compat.FrameLayoutCompatUtils; -import com.android.inputmethod.keyboard.internal.Key; import com.android.inputmethod.keyboard.internal.MiniKeyboardBuilder; import com.android.inputmethod.keyboard.internal.PointerTrackerQueue; import com.android.inputmethod.keyboard.internal.SwipeTracker; @@ -745,7 +744,7 @@ public class KeyboardView extends View implements PointerTracker.UIProxy { } else { paint.setColor(mKeyTextColor); } - if (key.mEnabled) { + if (key.isEnabled()) { // Set a drop shadow for the text paint.setShadowLayer(mShadowRadius, 0, 0, mShadowColor); } else { diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java index 0329ee2b3..76eac1ac8 100644 --- a/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java +++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboard.java @@ -33,7 +33,6 @@ import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; -import com.android.inputmethod.keyboard.internal.Key; import com.android.inputmethod.keyboard.internal.SlidingLocaleDrawable; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SubtypeSwitcher; @@ -175,7 +174,7 @@ public class LatinKeyboard extends Keyboard { public void updateShortcutKey(boolean available, LatinKeyboardView view) { if (mShortcutKey == null) return; - mShortcutKey.mEnabled = available; + mShortcutKey.setEnabled(available); mShortcutKey.setIcon(available ? mEnabledShortcutIcon : mDisabledShortcutIcon); if (view != null) view.invalidateKey(mShortcutKey); diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java index d25d1f098..901df6ab7 100644 --- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java @@ -24,7 +24,6 @@ import android.util.Log; import android.view.MotionEvent; import com.android.inputmethod.deprecated.VoiceProxy; -import com.android.inputmethod.keyboard.internal.Key; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.Utils; diff --git a/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java b/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java index d3d3fa59f..2d6766f2d 100644 --- a/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java +++ b/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java @@ -18,8 +18,6 @@ package com.android.inputmethod.keyboard; import android.content.Context; -import com.android.inputmethod.keyboard.internal.Key; - import java.util.List; public class MiniKeyboard extends Keyboard { diff --git a/java/src/com/android/inputmethod/keyboard/MiniKeyboardKeyDetector.java b/java/src/com/android/inputmethod/keyboard/MiniKeyboardKeyDetector.java index 9170c3bd1..cc5c3bbfe 100644 --- a/java/src/com/android/inputmethod/keyboard/MiniKeyboardKeyDetector.java +++ b/java/src/com/android/inputmethod/keyboard/MiniKeyboardKeyDetector.java @@ -16,8 +16,6 @@ package com.android.inputmethod.keyboard; -import com.android.inputmethod.keyboard.internal.Key; - import java.util.List; public class MiniKeyboardKeyDetector extends KeyDetector { diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index 1d70481f4..c7620f946 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -22,7 +22,6 @@ import android.util.Log; import android.view.MotionEvent; import com.android.inputmethod.keyboard.KeyboardView.UIHandler; -import com.android.inputmethod.keyboard.internal.Key; import com.android.inputmethod.keyboard.internal.PointerTrackerKeyState; import com.android.inputmethod.keyboard.internal.PointerTrackerQueue; import com.android.inputmethod.latin.LatinImeLogger; @@ -150,7 +149,7 @@ public class PointerTracker { + " ignoreModifier=" + ignoreModifierKey); if (ignoreModifierKey) return false; - if (key.mEnabled) { + if (key.isEnabled()) { mListener.onPress(key.mCode, withSliding); final boolean keyboardLayoutHasBeenChanged = mKeyboardLayoutHasBeenChanged; mKeyboardLayoutHasBeenChanged = false; @@ -169,14 +168,14 @@ public class PointerTracker { + " ignoreModifier=" + ignoreModifierKey); if (ignoreModifierKey) return; - if (key.mEnabled) + if (key.isEnabled()) mListener.onCodeInput(primaryCode, keyCodes, x, y); } private void callListenerOnTextInput(Key key) { if (DEBUG_LISTENER) Log.d(TAG, "onTextInput: text=" + key.mOutputText); - if (key.mEnabled) + if (key.isEnabled()) mListener.onTextInput(key.mOutputText); } @@ -189,7 +188,7 @@ public class PointerTracker { + withSliding + " ignoreModifier=" + ignoreModifierKey); if (ignoreModifierKey) return; - if (key.mEnabled) + if (key.isEnabled()) mListener.onRelease(primaryCode, withSliding); } @@ -269,7 +268,7 @@ public class PointerTracker { private void setPressedKeyGraphics(int keyIndex) { final Key key = getKey(keyIndex); - if (key != null && key.mEnabled) { + if (key != null && key.isEnabled()) { key.onPressed(); mProxy.invalidateKey(key); } @@ -618,7 +617,7 @@ public class PointerTracker { // The modifier key, such as shift key, should not show its key preview. private boolean isKeyPreviewNotRequired(int keyIndex) { final Key key = getKey(keyIndex); - if (key == null || !key.mEnabled) + if (key == null || !key.isEnabled()) return true; // Such as spacebar sliding language switch. if (mKeyboard.needSpacebarPreview(keyIndex)) diff --git a/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java b/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java index 6180f09c1..3642df0ec 100644 --- a/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/PopupMiniKeyboardView.java @@ -25,7 +25,6 @@ import android.view.MotionEvent; import android.view.View; import android.widget.PopupWindow; -import com.android.inputmethod.keyboard.internal.Key; import com.android.inputmethod.latin.R; /** diff --git a/java/src/com/android/inputmethod/keyboard/PopupPanel.java b/java/src/com/android/inputmethod/keyboard/PopupPanel.java index 48454679e..6ecb1c93f 100644 --- a/java/src/com/android/inputmethod/keyboard/PopupPanel.java +++ b/java/src/com/android/inputmethod/keyboard/PopupPanel.java @@ -19,8 +19,6 @@ package com.android.inputmethod.keyboard; import android.view.MotionEvent; import android.widget.PopupWindow; -import com.android.inputmethod.keyboard.internal.Key; - public interface PopupPanel { /** * Show popup panel. diff --git a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java index a6a07e518..33acc6907 100644 --- a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java +++ b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java @@ -16,7 +16,6 @@ package com.android.inputmethod.keyboard; -import com.android.inputmethod.keyboard.internal.Key; import com.android.inputmethod.latin.Utils; import java.util.Arrays; diff --git a/java/src/com/android/inputmethod/keyboard/internal/Key.java b/java/src/com/android/inputmethod/keyboard/internal/Key.java deleted file mode 100644 index ebd80be5e..000000000 --- a/java/src/com/android/inputmethod/keyboard/internal/Key.java +++ /dev/null @@ -1,458 +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.keyboard.internal; - -import android.content.res.Resources; -import android.content.res.TypedArray; -import android.content.res.XmlResourceParser; -import android.graphics.drawable.Drawable; -import android.text.TextUtils; -import android.util.Xml; - -import com.android.inputmethod.keyboard.Keyboard; -import com.android.inputmethod.keyboard.internal.KeyStyles.KeyStyle; -import com.android.inputmethod.keyboard.internal.KeyboardParser.ParseException; -import com.android.inputmethod.latin.R; - -import java.util.ArrayList; - -/** - * Class for describing the position and characteristics of a single key in the keyboard. - */ -public class Key { - /** - * The key code (unicode or custom code) that this key generates. - */ - public final int mCode; - - /** Label to display */ - public final CharSequence mLabel; - /** Hint letter to display on the key in conjunction with the label */ - public final CharSequence mHintLetter; - /** Option of the label */ - public final int mLabelOption; - public static final int LABEL_OPTION_ALIGN_LEFT = 0x01; - public static final int LABEL_OPTION_ALIGN_RIGHT = 0x02; - public static final int LABEL_OPTION_ALIGN_BOTTOM = 0x08; - public static final int LABEL_OPTION_FONT_NORMAL = 0x10; - public static final int LABEL_OPTION_FONT_FIXED_WIDTH = 0x20; - public static final int LABEL_OPTION_FOLLOW_KEY_LETTER_RATIO = 0x40; - private static final int LABEL_OPTION_POPUP_HINT = 0x80; - private static final int LABEL_OPTION_HAS_UPPERCASE_LETTER = 0x100; - - /** Icon to display instead of a label. Icon takes precedence over a label */ - private Drawable mIcon; - /** Preview version of the icon, for the preview popup */ - private Drawable mPreviewIcon; - - /** Width of the key, not including the gap */ - public final int mWidth; - /** Height of the key, not including the gap */ - public final int mHeight; - /** The horizontal gap around this key */ - public final int mGap; - /** The visual insets */ - public final int mVisualInsetsLeft; - public final int mVisualInsetsRight; - /** Whether this key is sticky, i.e., a toggle key */ - public final boolean mSticky; - /** X coordinate of the key in the keyboard layout */ - public final int mX; - /** Y coordinate of the key in the keyboard layout */ - public final int mY; - /** Text to output when pressed. This can be multiple characters, like ".com" */ - public final CharSequence mOutputText; - /** Popup characters */ - public final CharSequence[] mPopupCharacters; - /** Popup keyboard maximum column number */ - public final int mMaxPopupColumn; - - /** - * Flags that specify the anchoring to edges of the keyboard for detecting touch events - * that are just out of the boundary of the key. This is a bit mask of - * {@link Keyboard#EDGE_LEFT}, {@link Keyboard#EDGE_RIGHT}, - * {@link Keyboard#EDGE_TOP} and {@link Keyboard#EDGE_BOTTOM}. - */ - public final int mEdgeFlags; - /** Whether this is a functional key which has different key top than normal key */ - public final boolean mFunctional; - /** Whether this key repeats itself when held down */ - public final boolean mRepeatable; - - /** The Keyboard that this key belongs to */ - private final Keyboard mKeyboard; - - /** The current pressed state of this key */ - public boolean mPressed; - /** If this is a sticky key, is its highlight on? */ - public boolean mHighlightOn; - /** Key is enabled and responds on press */ - public boolean mEnabled = true; - - // keyWidth constants - private static final int KEYWIDTH_FILL_RIGHT = 0; - private static final int KEYWIDTH_FILL_BOTH = -1; - - 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 - }; - - // functional normal state (with properties) - private static final int[] KEY_STATE_FUNCTIONAL_NORMAL = { - android.R.attr.state_single - }; - - // functional pressed state (with properties) - private static final int[] KEY_STATE_FUNCTIONAL_PRESSED = { - android.R.attr.state_single, - android.R.attr.state_pressed - }; - - /** - * This constructor is being used only for key in popup mini keyboard. - */ - public Key(Resources res, Keyboard keyboard, CharSequence popupCharacter, int x, int y, - int width, int height, int edgeFlags) { - mKeyboard = keyboard; - mHeight = height - keyboard.getVerticalGap(); - mGap = keyboard.getHorizontalGap(); - mVisualInsetsLeft = mVisualInsetsRight = 0; - mWidth = width - mGap; - mEdgeFlags = edgeFlags; - mHintLetter = null; - mLabelOption = 0; - mFunctional = false; - mSticky = false; - mRepeatable = false; - mPopupCharacters = null; - mMaxPopupColumn = 0; - final String popupSpecification = popupCharacter.toString(); - mLabel = PopupCharactersParser.getLabel(popupSpecification); - mOutputText = PopupCharactersParser.getOutputText(popupSpecification); - mCode = PopupCharactersParser.getCode(res, popupSpecification); - mIcon = keyboard.mIconsSet.getIcon(PopupCharactersParser.getIconId(popupSpecification)); - // Horizontal gap is divided equally to both sides of the key. - mX = x + mGap / 2; - mY = y; - } - - /** - * 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 row the row that this key belongs to. The row must already be attached to - * a {@link Keyboard}. - * @param x the x coordinate of the top-left - * @param y the y coordinate of the top-left - * @param parser the XML parser containing the attributes for this key - * @param keyStyles active key styles set - */ - public Key(Resources res, Row row, int x, int y, XmlResourceParser parser, - KeyStyles keyStyles) { - mKeyboard = row.getKeyboard(); - - final TypedArray keyboardAttr = res.obtainAttributes(Xml.asAttributeSet(parser), - R.styleable.Keyboard); - int keyWidth; - try { - mHeight = KeyboardParser.getDimensionOrFraction(keyboardAttr, - R.styleable.Keyboard_rowHeight, - mKeyboard.getKeyboardHeight(), row.mDefaultHeight) - row.mVerticalGap; - mGap = KeyboardParser.getDimensionOrFraction(keyboardAttr, - R.styleable.Keyboard_horizontalGap, - mKeyboard.getDisplayWidth(), row.mDefaultHorizontalGap); - keyWidth = KeyboardParser.getDimensionOrFraction(keyboardAttr, - R.styleable.Keyboard_keyWidth, - mKeyboard.getDisplayWidth(), row.mDefaultWidth); - } finally { - keyboardAttr.recycle(); - } - - final TypedArray keyAttr = res.obtainAttributes(Xml.asAttributeSet(parser), - R.styleable.Keyboard_Key); - try { - final KeyStyle style; - if (keyAttr.hasValue(R.styleable.Keyboard_Key_keyStyle)) { - String styleName = keyAttr.getString(R.styleable.Keyboard_Key_keyStyle); - style = keyStyles.getKeyStyle(styleName); - if (style == null) - throw new ParseException("Unknown key style: " + styleName, parser); - } else { - style = keyStyles.getEmptyKeyStyle(); - } - - final int keyboardWidth = mKeyboard.getDisplayWidth(); - int keyXPos = KeyboardParser.getDimensionOrFraction(keyAttr, - R.styleable.Keyboard_Key_keyXPos, keyboardWidth, x); - if (keyXPos < 0) { - // If keyXPos is negative, the actual x-coordinate will be k + keyXPos. - keyXPos += keyboardWidth; - if (keyXPos < x) { - // keyXPos shouldn't be less than x because drawable area for this key starts - // at x. Or, this key will overlaps the adjacent key on its left hand side. - keyXPos = x; - } - } - if (keyWidth == KEYWIDTH_FILL_RIGHT) { - // If keyWidth is zero, the actual key width will be determined to fill out the - // area up to the right edge of the keyboard. - keyWidth = keyboardWidth - keyXPos; - } else if (keyWidth <= KEYWIDTH_FILL_BOTH) { - // If keyWidth is negative, the actual key width will be determined to fill out the - // area between the nearest key on the left hand side and the right edge of the - // keyboard. - keyXPos = x; - keyWidth = keyboardWidth - keyXPos; - } - - // Horizontal gap is divided equally to both sides of the key. - mX = keyXPos + mGap / 2; - mY = y; - mWidth = keyWidth - mGap; - - final CharSequence[] popupCharacters = style.getTextArray(keyAttr, - R.styleable.Keyboard_Key_popupCharacters); - if (res.getBoolean(R.bool.config_digit_popup_characters_enabled)) { - mPopupCharacters = popupCharacters; - } else { - mPopupCharacters = filterOutDigitPopupCharacters(popupCharacters); - } - mMaxPopupColumn = style.getInt(keyboardAttr, - R.styleable.Keyboard_Key_maxPopupKeyboardColumn, - mKeyboard.getMaxPopupKeyboardColumn()); - - mRepeatable = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_isRepeatable, false); - mFunctional = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_isFunctional, false); - mSticky = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_isSticky, false); - mEnabled = style.getBoolean(keyAttr, R.styleable.Keyboard_Key_enabled, true); - mEdgeFlags = style.getFlag(keyAttr, R.styleable.Keyboard_Key_keyEdgeFlags, 0) - | row.mRowEdgeFlags; - - final KeyboardIconsSet iconsSet = mKeyboard.mIconsSet; - mVisualInsetsLeft = KeyboardParser.getDimensionOrFraction(keyAttr, - R.styleable.Keyboard_Key_visualInsetsLeft, mKeyboard.getDisplayHeight(), 0); - mVisualInsetsRight = KeyboardParser.getDimensionOrFraction(keyAttr, - R.styleable.Keyboard_Key_visualInsetsRight, mKeyboard.getDisplayHeight(), 0); - mPreviewIcon = iconsSet.getIcon(style.getInt( - keyAttr, R.styleable.Keyboard_Key_keyIconPreview, - KeyboardIconsSet.ICON_UNDEFINED)); - Keyboard.setDefaultBounds(mPreviewIcon); - mIcon = iconsSet.getIcon(style.getInt( - keyAttr, R.styleable.Keyboard_Key_keyIcon, - KeyboardIconsSet.ICON_UNDEFINED)); - Keyboard.setDefaultBounds(mIcon); - mHintLetter = style.getText(keyAttr, R.styleable.Keyboard_Key_keyHintLetter); - - mLabel = style.getText(keyAttr, R.styleable.Keyboard_Key_keyLabel); - mLabelOption = style.getFlag(keyAttr, R.styleable.Keyboard_Key_keyLabelOption, 0); - mOutputText = style.getText(keyAttr, R.styleable.Keyboard_Key_keyOutputText); - // Choose the first letter of the label as primary code if not - // specified. - final int code = style.getInt(keyAttr, R.styleable.Keyboard_Key_code, - Keyboard.CODE_UNSPECIFIED); - if (code == Keyboard.CODE_UNSPECIFIED && !TextUtils.isEmpty(mLabel)) { - mCode = mLabel.charAt(0); - } else if (code != Keyboard.CODE_UNSPECIFIED) { - mCode = code; - } else { - mCode = Keyboard.CODE_DUMMY; - } - - final Drawable shiftedIcon = iconsSet.getIcon(style.getInt( - keyAttr, R.styleable.Keyboard_Key_keyIconShifted, - KeyboardIconsSet.ICON_UNDEFINED)); - if (shiftedIcon != null) - mKeyboard.getShiftedIcons().put(this, shiftedIcon); - } finally { - keyAttr.recycle(); - } - } - - public boolean hasPopupHint() { - return (mLabelOption & LABEL_OPTION_POPUP_HINT) != 0; - } - - public boolean hasUppercaseLetter() { - return (mLabelOption & LABEL_OPTION_HAS_UPPERCASE_LETTER) != 0; - } - - private static boolean isDigitPopupCharacter(CharSequence label) { - return label != null && label.length() == 1 && Character.isDigit(label.charAt(0)); - } - - private static CharSequence[] filterOutDigitPopupCharacters(CharSequence[] popupCharacters) { - if (popupCharacters == null || popupCharacters.length < 1) - return null; - if (popupCharacters.length == 1 && isDigitPopupCharacter( - PopupCharactersParser.getLabel(popupCharacters[0].toString()))) - return null; - ArrayList filtered = null; - for (int i = 0; i < popupCharacters.length; i++) { - final CharSequence popupSpec = popupCharacters[i]; - if (isDigitPopupCharacter(PopupCharactersParser.getLabel(popupSpec.toString()))) { - if (filtered == null) { - filtered = new ArrayList(); - for (int j = 0; j < i; j++) - filtered.add(popupCharacters[j]); - } - } else if (filtered != null) { - filtered.add(popupSpec); - } - } - if (filtered == null) - return popupCharacters; - if (filtered.size() == 0) - return null; - return filtered.toArray(new CharSequence[filtered.size()]); - } - - public Drawable getIcon() { - return mIcon; - } - - public Drawable getPreviewIcon() { - return mPreviewIcon; - } - - public void setIcon(Drawable icon) { - mIcon = icon; - } - - public void setPreviewIcon(Drawable icon) { - mPreviewIcon = icon; - } - - /** - * Informs the key that it has been pressed, in case it needs to change its appearance or - * state. - * @see #onReleased() - */ - public void onPressed() { - mPressed = true; - } - - /** - * Informs the key that it has been released, in case it needs to change its appearance or - * state. - * @see #onPressed() - */ - public void onReleased() { - mPressed = false; - } - - /** - * Detects if a point falls on this key. - * @param x the x-coordinate of the point - * @param y the y-coordinate of the point - * @return whether or not the point falls on the key. If the key is attached to an edge, it will - * assume that all points between the key and the edge are considered to be on the key. - */ - public boolean isOnKey(int x, int y) { - final int flags = mEdgeFlags; - final boolean leftEdge = (flags & Keyboard.EDGE_LEFT) != 0; - final boolean rightEdge = (flags & Keyboard.EDGE_RIGHT) != 0; - final boolean topEdge = (flags & Keyboard.EDGE_TOP) != 0; - final boolean bottomEdge = (flags & Keyboard.EDGE_BOTTOM) != 0; - final int left = mX - mGap / 2; - final int right = left + mWidth + mGap; - final int top = mY; - final int bottom = top + mHeight + mKeyboard.getVerticalGap(); - // In order to mitigate rounding errors, we use (left <= x <= right) here. - return (x >= left || leftEdge) && (x <= right || rightEdge) - && (y >= top || topEdge) && (y <= bottom || bottomEdge); - } - - /** - * Returns the square of the distance to the nearest edge of the key and the given point. - * @param x the x-coordinate of the point - * @param y the y-coordinate of the point - * @return the square of the distance of the point from the nearest edge of the key - */ - public int squaredDistanceToEdge(int x, int y) { - final int left = mX; - final int right = left + mWidth; - final int top = mY; - final int bottom = top + mHeight; - final int edgeX = x < left ? left : (x > right ? right : x); - final int edgeY = y < top ? top : (y > bottom ? bottom : y); - final int dx = x - edgeX; - final int dy = y - edgeY; - return dx * dx + dy * dy; - } - - /** - * 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() { - final boolean pressed = mPressed; - if (!mSticky && mFunctional) { - if (pressed) { - return KEY_STATE_FUNCTIONAL_PRESSED; - } else { - return KEY_STATE_FUNCTIONAL_NORMAL; - } - } - - int[] states = KEY_STATE_NORMAL; - - if (mHighlightOn) { - if (pressed) { - states = KEY_STATE_PRESSED_ON; - } else { - states = KEY_STATE_NORMAL_ON; - } - } else { - if (mSticky) { - if (pressed) { - states = KEY_STATE_PRESSED_OFF; - } else { - states = KEY_STATE_NORMAL_OFF; - } - } else { - if (pressed) { - states = KEY_STATE_PRESSED; - } - } - } - return states; - } -} diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParser.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParser.java index d5b364818..8954eec59 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardParser.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardParser.java @@ -26,6 +26,7 @@ import android.util.Xml; import android.view.InflateException; import com.android.inputmethod.compat.EditorInfoCompatUtils; +import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardId; import com.android.inputmethod.latin.R; @@ -331,7 +332,7 @@ public class KeyboardParser { } else { Key key = new Key(mResources, row, mCurrentX, mCurrentY, parser, mKeyStyles); if (DEBUG) Log.d(TAG, String.format("<%s%s keyLabel=%s code=%d popupCharacters=%s />", - TAG_KEY, (key.mEnabled ? "" : " disabled"), key.mLabel, key.mCode, + TAG_KEY, (key.isEnabled() ? "" : " disabled"), key.mLabel, key.mCode, Arrays.toString(key.mPopupCharacters))); checkEndTag(TAG_KEY, parser); keys.add(key); diff --git a/java/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilder.java index 01faae61d..040c16ded 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilder.java +++ b/java/src/com/android/inputmethod/keyboard/internal/MiniKeyboardBuilder.java @@ -21,6 +21,7 @@ import android.content.res.Resources; import android.graphics.Paint; import android.graphics.Rect; +import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardView; import com.android.inputmethod.keyboard.MiniKeyboard; diff --git a/tests/src/com/android/inputmethod/latin/SuggestHelper.java b/tests/src/com/android/inputmethod/latin/SuggestHelper.java index f1224a25a..87ea011fa 100644 --- a/tests/src/com/android/inputmethod/latin/SuggestHelper.java +++ b/tests/src/com/android/inputmethod/latin/SuggestHelper.java @@ -16,10 +16,10 @@ package com.android.inputmethod.latin; +import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.KeyDetector; import com.android.inputmethod.keyboard.KeyboardId; import com.android.inputmethod.keyboard.LatinKeyboard; -import com.android.inputmethod.keyboard.internal.Key; import android.content.Context; import android.text.TextUtils; -- cgit v1.2.3-83-g751a