diff options
Diffstat (limited to 'java/src')
14 files changed, 239 insertions, 249 deletions
diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java index 8f2efab29..686392da8 100644 --- a/java/src/com/android/inputmethod/keyboard/Key.java +++ b/java/src/com/android/inputmethod/keyboard/Key.java @@ -25,10 +25,10 @@ import android.text.TextUtils; import android.util.Log; import android.util.Xml; +import com.android.inputmethod.keyboard.internal.KeySpecParser; import com.android.inputmethod.keyboard.internal.KeyStyles; import com.android.inputmethod.keyboard.internal.KeyStyles.KeyStyle; import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; -import com.android.inputmethod.keyboard.internal.KeySpecParser; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.Utils; import com.android.inputmethod.latin.XmlParseUtils; @@ -74,13 +74,11 @@ public class Key { private static final int LABEL_FLAGS_SHIFTED_LETTER_ACTIVATED = 0x10000; /** Icon to display instead of a label. Icon takes precedence over a label */ - private final int mIconAttrId; - // TODO: Remove this variable. - private Drawable mIcon; + private final int mIconId; /** Icon for disabled state */ - private final int mDisabledIconAttrId; + private final int mDisabledIconId; /** Preview version of the icon, for the preview popup */ - public final int mPreviewIconAttrId; + private final int mPreviewIconId; /** Width of the key, not including the gap */ public final int mWidth; @@ -128,21 +126,13 @@ public class Key { /** Key is enabled and responds on press */ private boolean mEnabled = true; - private static Drawable getIcon(Keyboard.Params params, String moreKeySpec) { - final int iconAttrId = KeySpecParser.getIconAttrId(moreKeySpec); - if (iconAttrId == KeyboardIconsSet.ICON_UNDEFINED) { - return null; - } else { - return params.mIconsSet.getIconByAttrId(iconAttrId); - } - } - /** * This constructor is being used only for key in more keys keyboard. */ public Key(Resources res, Keyboard.Params params, String moreKeySpec, int x, int y, int width, int height) { - this(params, KeySpecParser.getLabel(moreKeySpec), null, getIcon(params, moreKeySpec), + this(params, KeySpecParser.getLabel(moreKeySpec), null, + KeySpecParser.getIconId(moreKeySpec), KeySpecParser.getCode(res, moreKeySpec), KeySpecParser.getOutputText(moreKeySpec), x, y, width, height); @@ -151,7 +141,7 @@ public class Key { /** * This constructor is being used only for key in popup suggestions pane. */ - public Key(Keyboard.Params params, String label, String hintLabel, Drawable icon, + public Key(Keyboard.Params params, String label, String hintLabel, int iconId, int code, CharSequence outputText, int x, int y, int width, int height) { mHeight = height - params.mVerticalGap; mHorizontalGap = params.mHorizontalGap; @@ -168,10 +158,9 @@ public class Key { mOutputText = outputText; mCode = code; mAltCode = Keyboard.CODE_UNSPECIFIED; - mIconAttrId = KeyboardIconsSet.ATTR_UNDEFINED; - mIcon = icon; - mDisabledIconAttrId = KeyboardIconsSet.ATTR_UNDEFINED; - mPreviewIconAttrId = KeyboardIconsSet.ATTR_UNDEFINED; + mIconId = iconId; + mDisabledIconId = KeyboardIconsSet.ICON_UNDEFINED; + mPreviewIconId = KeyboardIconsSet.ICON_UNDEFINED; // Horizontal gap is divided equally to both sides of the key. mX = x + mHorizontalGap / 2; mY = y; @@ -228,18 +217,16 @@ public class Key { mBackgroundType = style.getInt(keyAttr, R.styleable.Keyboard_Key_backgroundType, BACKGROUND_TYPE_NORMAL); - final KeyboardIconsSet iconsSet = params.mIconsSet; mVisualInsetsLeft = (int) Keyboard.Builder.getDimensionOrFraction(keyAttr, R.styleable.Keyboard_Key_visualInsetsLeft, params.mBaseWidth, 0); mVisualInsetsRight = (int) Keyboard.Builder.getDimensionOrFraction(keyAttr, R.styleable.Keyboard_Key_visualInsetsRight, params.mBaseWidth, 0); - mPreviewIconAttrId = KeyboardIconsSet.getIconAttrId(style.getInt(keyAttr, - R.styleable.Keyboard_Key_keyIconPreview, KeyboardIconsSet.ICON_UNDEFINED)); - mIconAttrId = KeyboardIconsSet.getIconAttrId(style.getInt(keyAttr, - R.styleable.Keyboard_Key_keyIcon, KeyboardIconsSet.ICON_UNDEFINED)); - mIcon = iconsSet.getIconByAttrId(mIconAttrId); - mDisabledIconAttrId = KeyboardIconsSet.getIconAttrId(style.getInt(keyAttr, - R.styleable.Keyboard_Key_keyIconDisabled, KeyboardIconsSet.ICON_UNDEFINED)); + mPreviewIconId = style.getInt(keyAttr, + R.styleable.Keyboard_Key_keyIconPreview, KeyboardIconsSet.ICON_UNDEFINED); + mIconId = style.getInt(keyAttr, + R.styleable.Keyboard_Key_keyIcon, KeyboardIconsSet.ICON_UNDEFINED); + mDisabledIconId = style.getInt(keyAttr, + R.styleable.Keyboard_Key_keyIconDisabled, KeyboardIconsSet.ICON_UNDEFINED); mLabelFlags = style.getFlag(keyAttr, R.styleable.Keyboard_Key_keyLabelFlags, 0); final boolean preserveCase = (mLabelFlags & LABEL_FLAGS_PRESERVE_CASE) != 0; @@ -336,16 +323,15 @@ public class Key { key.mCode, key.mLabel, key.mHintLabel, - key.mIconAttrId, + key.mIconId, key.mBackgroundType, // Key can be distinguishable without the following members. // key.mAltCode, // key.mOutputText, // key.mActionFlags, // key.mLabelFlags, - // key.mIcon, - // key.mDisabledIconAttrId, - // key.mPreviewIconAttrId, + // key.mDisabledIconId, + // key.mPreviewIconId, // key.mHorizontalGap, // key.mVerticalGap, // key.mVisualInsetLeft, @@ -364,8 +350,8 @@ public class Key { && o.mCode == mCode && TextUtils.equals(o.mLabel, mLabel) && TextUtils.equals(o.mHintLabel, mHintLabel) - && o.mIconAttrId != mIconAttrId - && o.mBackgroundType != mBackgroundType; + && o.mIconId == mIconId + && o.mBackgroundType == mBackgroundType; } @Override @@ -380,11 +366,20 @@ public class Key { @Override public String toString() { - String top = Keyboard.printableCode(mCode); - if (Utils.codePointCount(mLabel) != 1) { - top += "/\"" + mLabel + '"'; + return String.format("%s/%s %d,%d %dx%d %s/%s/%s", + Keyboard.printableCode(mCode), mLabel, mX, mY, mWidth, mHeight, mHintLabel, + KeyboardIconsSet.getIconName(mIconId), backgroundName(mBackgroundType)); + } + + private static String backgroundName(int backgroundType) { + switch (backgroundType) { + case BACKGROUND_TYPE_NORMAL: return "normal"; + case BACKGROUND_TYPE_FUNCTIONAL: return "functional"; + case BACKGROUND_TYPE_ACTION: return "action"; + case BACKGROUND_TYPE_STICKY_OFF: return "stickyOff"; + case BACKGROUND_TYPE_STICKY_ON: return "stickyOn"; + default: return null; } - return String.format("%s %d,%d", top, mX, mY); } public void markAsLeftEdge(Keyboard.Params params) { @@ -403,8 +398,8 @@ public class Key { mHitBox.bottom = params.mOccupiedHeight + params.mBottomPadding; } - public boolean isSpacer() { - return false; + public final boolean isSpacer() { + return this instanceof Spacer; } public boolean isShift() { @@ -498,14 +493,18 @@ public class Key { return (mLabelFlags & LABEL_FLAGS_SHIFTED_LETTER_ACTIVATED) != 0; } - // TODO: Get rid of this method. public Drawable getIcon(KeyboardIconsSet iconSet) { - return mEnabled ? mIcon : iconSet.getIconByAttrId(mDisabledIconAttrId); + return iconSet.getIconDrawable(mIconId); + } + + public Drawable getDisabledIcon(KeyboardIconsSet iconSet) { + return iconSet.getIconDrawable(mDisabledIconId); } - // TODO: Get rid of this method. - public void setIcon(Drawable icon) { - mIcon = icon; + public Drawable getPreviewIcon(KeyboardIconsSet iconSet) { + return mPreviewIconId != KeyboardIconsSet.ICON_UNDEFINED + ? iconSet.getIconDrawable(mPreviewIconId) + : iconSet.getIconDrawable(mIconId); } /** @@ -642,13 +641,9 @@ public class Key { /** * This constructor is being used only for divider in more keys keyboard. */ - public Spacer(Keyboard.Params params, Drawable icon, int x, int y, int width, int height) { - super(params, null, null, icon, Keyboard.CODE_UNSPECIFIED, null, x, y, width, height); - } - - @Override - public boolean isSpacer() { - return true; + protected Spacer(Keyboard.Params params, int x, int y, int width, int height) { + super(params, null, null, KeyboardIconsSet.ICON_UNDEFINED, Keyboard.CODE_UNSPECIFIED, + null, x, y, width, height); } } } diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java index dce2c37f2..6e13b95b5 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java @@ -48,14 +48,17 @@ public interface KeyboardActionListener { * presses of a key adjacent to the intended key. * @param x x-coordinate pixel of touched event. If {@link #onCodeInput} is not called by * {@link PointerTracker#onTouchEvent} or so, the value should be - * {@link #NOT_A_TOUCH_COORDINATE}. + * {@link #NOT_A_TOUCH_COORDINATE}. If it's called on insertion from the suggestion + * strip, it should be {@link #SUGGESTION_STRIP_COORDINATE}. * @param y y-coordinate pixel of touched event. If {@link #onCodeInput} is not called by * {@link PointerTracker#onTouchEvent} or so, the value should be - * {@link #NOT_A_TOUCH_COORDINATE}. + * {@link #NOT_A_TOUCH_COORDINATE}. If it's called on insertion from the suggestion + * strip, it should be {@link #SUGGESTION_STRIP_COORDINATE}. */ public void onCodeInput(int primaryCode, int[] keyCodes, int x, int y); public static final int NOT_A_TOUCH_COORDINATE = -1; + public static final int SUGGESTION_STRIP_COORDINATE = -2; /** * Sends a sequence of characters to the listener. diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardSet.java index 664e7656e..82eaa1d17 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSet.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSet.java @@ -57,7 +57,10 @@ public class KeyboardSet { private final Context mContext; private final Params mParams; - private final KeysCache mKeysCache = new KeysCache(); + + private static final HashMap<KeyboardId, SoftReference<Keyboard>> sKeyboardCache = + new HashMap<KeyboardId, SoftReference<Keyboard>>(); + private static final KeysCache sKeysCache = new KeysCache(); public static class KeyboardSetException extends RuntimeException { public final KeyboardId mKeyboardId; @@ -74,6 +77,10 @@ public class KeyboardSet { mMap = new HashMap<Key, Key>(); } + public void clear() { + mMap.clear(); + } + public Key get(Key key) { final Key existingKey = mMap.get(key); if (existingKey != null) { @@ -103,11 +110,9 @@ public class KeyboardSet { Params() {} } - private static final HashMap<KeyboardId, SoftReference<Keyboard>> sKeyboardCache = - new HashMap<KeyboardId, SoftReference<Keyboard>>(); - public static void clearKeyboardCache() { sKeyboardCache.clear(); + sKeysCache.clear(); } private KeyboardSet(Context context, Params params) { @@ -156,7 +161,7 @@ public class KeyboardSet { final Keyboard.Builder<Keyboard.Params> builder = new Keyboard.Builder<Keyboard.Params>(context, new Keyboard.Params()); if (id.isAlphabetKeyboard()) { - builder.setAutoGenerate(mKeysCache); + builder.setAutoGenerate(sKeysCache); } builder.load(keyboardXmlId, id); builder.setTouchPositionCorrectionEnabled(mParams.mTouchPositionCorrectionEnabled); diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java index c6fb75489..d65253ede 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java @@ -185,7 +185,7 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { public final int mKeyShiftedLetterHintInactivatedColor; public final int mKeyShiftedLetterHintActivatedColor; - private final float mKeyLetterRatio; + /* package */ final float mKeyLetterRatio; private final float mKeyLargeLetterRatio; private final float mKeyLabelRatio; private final float mKeyHintLetterRatio; @@ -486,8 +486,9 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { } private void onDrawKey(Key key, Canvas canvas, Paint paint, KeyDrawParams params) { - if (key.isSpacer()) return; - onDrawKeyBackground(key, canvas, params); + if (!key.isSpacer()) { + onDrawKeyBackground(key, canvas, params); + } onDrawKeyTopVisuals(key, canvas, paint, params); } @@ -861,10 +862,8 @@ public class KeyboardView extends View implements PointerTracker.DrawingProxy { } previewText.setText(key.mLabel); } else { - final Drawable previewIcon = mKeyboard.mIconsSet.getIconByAttrId( - key.mPreviewIconAttrId); previewText.setCompoundDrawables(null, null, null, - previewIcon != null ? previewIcon : key.getIcon(mKeyboard.mIconsSet)); + key.getPreviewIcon(mKeyboard.mIconsSet)); previewText.setText(null); } previewText.setBackgroundDrawable(params.mPreviewBackground); diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java index f3583fefc..870c7cb25 100644 --- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java @@ -42,6 +42,7 @@ import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy; import com.android.inputmethod.deprecated.VoiceProxy; import com.android.inputmethod.keyboard.PointerTracker.DrawingProxy; import com.android.inputmethod.keyboard.PointerTracker.TimerProxy; +import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; import com.android.inputmethod.latin.LatinIME; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; @@ -132,7 +133,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke switch (msg.what) { case MSG_REPEAT_KEY: tracker.onRepeatKey(tracker.getKey()); - startKeyRepeatTimer(tracker); + startKeyRepeatTimer(tracker, mParams.mKeyRepeatInterval); break; case MSG_LONGPRESS_KEY: if (tracker != null) { @@ -144,11 +145,14 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke } } + private void startKeyRepeatTimer(PointerTracker tracker, long delay) { + sendMessageDelayed(obtainMessage(MSG_REPEAT_KEY, tracker), delay); + } + @Override public void startKeyRepeatTimer(PointerTracker tracker) { mInKeyRepeat = true; - sendMessageDelayed(obtainMessage(MSG_REPEAT_KEY, tracker), - mParams.mKeyRepeatStartTimeout); + startKeyRepeatTimer(tracker, mParams.mKeyRepeatStartTimeout); } public void cancelKeyRepeatTimer() { @@ -390,7 +394,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke mMoreKeysPanelCache.clear(); mSpaceKey = keyboard.getKey(Keyboard.CODE_SPACE); - mSpaceIcon = keyboard.mIconsSet.getIconByAttrId(R.styleable.Keyboard_iconSpaceKey); + mSpaceIcon = keyboard.mIconsSet.getIconDrawable(KeyboardIconsSet.ICON_SPACE); final int keyHeight = keyboard.mMostCommonKeyHeight - keyboard.mVerticalGap; mSpacebarTextSize = keyHeight * mSpacebarTextRatio; mSpacebarLocale = keyboard.mId.mLocale; diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java index ba12676ad..adb5f4759 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java @@ -48,7 +48,7 @@ public class KeySpecParser { private static final char ESCAPE_CHAR = '\\'; private static final char PREFIX_AT = '@'; private static final char SUFFIX_SLASH = '/'; - private static final String PREFIX_STRING = PREFIX_AT + "string"; + private static final String PREFIX_STRING = PREFIX_AT + "string" + SUFFIX_SLASH; private static final char LABEL_END = '|'; private static final String PREFIX_ICON = PREFIX_AT + "icon" + SUFFIX_SLASH; private static final String PREFIX_CODE = PREFIX_AT + "integer" + SUFFIX_SLASH; @@ -179,11 +179,11 @@ public class KeySpecParser { return Keyboard.CODE_OUTPUT_TEXT; } - public static int getIconAttrId(String moreKeySpec) { + public static int getIconId(String moreKeySpec) { if (hasIcon(moreKeySpec)) { final int end = moreKeySpec.indexOf(LABEL_END, PREFIX_ICON.length()); final String name = moreKeySpec.substring(PREFIX_ICON.length(), end); - return KeyboardIconsSet.getIconAttrId(name); + return KeyboardIconsSet.getIconId(name); } return KeyboardIconsSet.ICON_UNDEFINED; } @@ -293,13 +293,11 @@ public class KeySpecParser { sb.append(res.getString(resId)); pos = end - 1; } else if (c == ESCAPE_CHAR) { - pos++; if (sb != null) { - sb.append(c); - if (pos < size) { - sb.append(text.charAt(pos)); - } + // Append both escape character and escaped character. + sb.append(text.substring(pos, Math.min(pos + 2, size))); } + pos++; } else if (sb != null) { sb.append(c); } @@ -309,10 +307,7 @@ public class KeySpecParser { private static int searchResourceNameEnd(String text, int start) { final int size = text.length(); - if (start >= size || text.charAt(start) != SUFFIX_SLASH) { - throw new RuntimeException("Resource name not specified"); - } - for (int pos = start + 1; pos < size; pos++) { + for (int pos = start; pos < size; pos++) { final char c = text.charAt(pos); // String resource name should be consisted of [a-z_0-9]. if ((c >= 'a' && c <= 'z') || c == '_' || (c >= '0' && c <= '9')) { @@ -330,56 +325,35 @@ public class KeySpecParser { return null; } if (Utils.codePointCount(text) == 1) { - return new String[] { text }; + return text.codePointAt(0) == COMMA ? null : new String[] { text }; } - final StringBuilder sb = new StringBuilder(); ArrayList<String> list = null; int start = 0; for (int pos = 0; pos < size; pos++) { final char c = text.charAt(pos); if (c == COMMA) { - if (list == null) { - list = new ArrayList<String>(); - } - if (sb.length() == 0) { + // Skip empty entry. + if (pos - start > 0) { + if (list == null) { + list = new ArrayList<String>(); + } list.add(text.substring(start, pos)); - } else { - list.add(sb.toString()); - sb.setLength(0); } // Skip comma start = pos + 1; - continue; - } - // TODO: Only parse escaped comma. Other escaped character should be passed through - // with escaped character prefixed. - // Skip escaped sequence. - if (c == ESCAPE_CHAR) { - if (start == pos) { - // Skip escaping comma at the beginning of the text. - start++; - pos++; - } else { - if (start < pos && sb.length() == 0) { - sb.append(text.substring(start, pos)); - } - // Skip comma - pos++; - if (pos < size) { - sb.append(text.charAt(pos)); - } - } - } else if (sb.length() > 0) { - sb.append(c); + } else if (c == ESCAPE_CHAR) { + // Skip escape character and escaped character. + pos++; } } + final String remain = (size - start > 0) ? text.substring(start) : null; if (list == null) { - return new String[] { - sb.length() > 0 ? sb.toString() : text.substring(start) - }; + return remain != null ? new String[] { remain } : null; } else { - list.add(sb.length() > 0 ? sb.toString() : text.substring(start)); + if (remain != null) { + list.add(remain); + } return list.toArray(new String[list.size()]); } } diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java index bec6ae1cc..162e96d06 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java @@ -23,7 +23,6 @@ import android.util.Log; import com.android.inputmethod.latin.R; -import java.util.Collection; import java.util.HashMap; import java.util.Map; @@ -31,16 +30,20 @@ public class KeyboardIconsSet { private static final String TAG = KeyboardIconsSet.class.getSimpleName(); public static final int ICON_UNDEFINED = 0; - public static final int ATTR_UNDEFINED = 0; + // The value should be aligned with the enum value of Key.keyIcon. + public static final int ICON_SPACE = 4; + private static final int NUM_ICONS = 13; - private final Map<Integer, Drawable> mIcons = new HashMap<Integer, Drawable>(); + private final Drawable[] mIcons = new Drawable[NUM_ICONS + 1]; - // The key value should be aligned with the enum value of Keyboard.icon*. - private static final Map<Integer, Integer> ICONS_TO_ATTRS_MAP = new HashMap<Integer, Integer>(); - private static final Map<String, Integer> NAME_TO_ATTRS_MAP = new HashMap<String, Integer>(); - private static final Collection<Integer> VALID_ATTRS; + private static final Map<Integer, Integer> ATTR_ID_TO_ICON_ID = new HashMap<Integer, Integer>(); + private static final Map<String, Integer> NAME_TO_ICON_ID = new HashMap<String, Integer>(); + private static final String[] ICON_NAMES = new String[NUM_ICONS + 1]; + private static final int ATTR_UNDEFINED = 0; static { + // The key value should be aligned with the enum value of Key.keyIcon. + addIconIdMap(0, "undefined", ATTR_UNDEFINED); addIconIdMap(1, "shiftKey", R.styleable.Keyboard_iconShiftKey); addIconIdMap(2, "deleteKey", R.styleable.Keyboard_iconDeleteKey); addIconIdMap(3, "settingsKey", R.styleable.Keyboard_iconSettingsKey); @@ -55,21 +58,23 @@ public class KeyboardIconsSet { addIconIdMap(11, "shiftKeyShifted", R.styleable.Keyboard_iconShiftKeyShifted); addIconIdMap(12, "disabledShortcurKey", R.styleable.Keyboard_iconDisabledShortcutKey); addIconIdMap(13, "previewTabKey", R.styleable.Keyboard_iconPreviewTabKey); - VALID_ATTRS = ICONS_TO_ATTRS_MAP.values(); } - private static void addIconIdMap(int iconId, String name, Integer attrId) { - ICONS_TO_ATTRS_MAP.put(iconId, attrId); - NAME_TO_ATTRS_MAP.put(name, attrId); + private static void addIconIdMap(int iconId, String name, int attrId) { + if (attrId != ATTR_UNDEFINED) { + ATTR_ID_TO_ICON_ID.put(attrId, iconId); + } + NAME_TO_ICON_ID.put(name, iconId); + ICON_NAMES[iconId] = name; } public void loadIcons(final TypedArray keyboardAttrs) { - for (final Integer attrId : VALID_ATTRS) { + for (final Integer attrId : ATTR_ID_TO_ICON_ID.keySet()) { try { final Drawable icon = keyboardAttrs.getDrawable(attrId); - if (icon == null) continue; setDefaultBounds(icon); - mIcons.put(attrId, icon); + final Integer iconId = ATTR_ID_TO_ICON_ID.get(attrId); + mIcons[iconId] = icon; } catch (Resources.NotFoundException e) { Log.w(TAG, "Drawable resource for icon #" + keyboardAttrs.getResources().getResourceEntryName(attrId) @@ -78,39 +83,32 @@ public class KeyboardIconsSet { } } - public static int getIconAttrId(final Integer iconId) { - if (iconId == ICON_UNDEFINED) { - return ATTR_UNDEFINED; - } - final Integer attrId = ICONS_TO_ATTRS_MAP.get(iconId); - if (attrId == null) { - throw new IllegalArgumentException("icon id is out of range: " + iconId); - } - return attrId; + private static boolean isValidIconId(final int iconId) { + return iconId >= 0 && iconId < ICON_NAMES.length; } - public static int getIconAttrId(final String iconName) { - final Integer attrId = NAME_TO_ATTRS_MAP.get(iconName); - if (attrId == null) { - throw new IllegalArgumentException("unknown icon name: " + iconName); - } - return attrId; + public static String getIconName(final int iconId) { + return isValidIconId(iconId) ? ICON_NAMES[iconId] : "unknown<" + iconId + ">"; } - public Drawable getIconByAttrId(final Integer attrId) { - if (attrId == ATTR_UNDEFINED) { - return null; + public static int getIconId(final String name) { + final Integer iconId = NAME_TO_ICON_ID.get(name); + if (iconId != null) { + return iconId; } - if (!VALID_ATTRS.contains(attrId)) { - throw new IllegalArgumentException("unknown icon attribute id: " + attrId); + throw new RuntimeException("unknown icon name: " + name); + } + + public Drawable getIconDrawable(final int iconId) { + if (isValidIconId(iconId)) { + return mIcons[iconId]; } - return mIcons.get(attrId); + throw new RuntimeException("unknown icon id: " + getIconName(iconId)); } - private static Drawable setDefaultBounds(final Drawable icon) { + private static void setDefaultBounds(final Drawable icon) { if (icon != null) { icon.setBounds(0, 0, icon.getIntrinsicWidth(), icon.getIntrinsicHeight()); } - return icon; } } diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java index b333e4873..79441c557 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java @@ -75,7 +75,8 @@ class BinaryDictionaryGetter { // This assumes '%' is fully available as a non-separator, normal // character in a file name. This is probably true for all file systems. final StringBuilder sb = new StringBuilder(); - for (int i = 0; i < name.length(); ++i) { + final int nameLength = name.length(); + for (int i = 0; i < nameLength; i = name.offsetByCodePoints(i, 1)) { final int codePoint = name.codePointAt(i); if (isFileNameCharacter(codePoint)) { sb.appendCodePoint(codePoint); @@ -92,7 +93,8 @@ class BinaryDictionaryGetter { */ private static String getWordListIdFromFileName(final String fname) { final StringBuilder sb = new StringBuilder(); - for (int i = 0; i < fname.length(); ++i) { + final int fnameLength = fname.length(); + for (int i = 0; i < fnameLength; i = fname.offsetByCodePoints(i, 1)) { final int codePoint = fname.codePointAt(i); if ('%' != codePoint) { sb.appendCodePoint(codePoint); diff --git a/java/src/com/android/inputmethod/latin/InputAttributes.java b/java/src/com/android/inputmethod/latin/InputAttributes.java index f5cf953c4..3de5c1d48 100644 --- a/java/src/com/android/inputmethod/latin/InputAttributes.java +++ b/java/src/com/android/inputmethod/latin/InputAttributes.java @@ -28,7 +28,6 @@ import com.android.inputmethod.compat.InputTypeCompatUtils; public class InputAttributes { private final String TAG = InputAttributes.class.getSimpleName(); - final public boolean mInsertSpaceOnPickSuggestionManually; final public boolean mInputTypeNoAutoCorrect; final public boolean mIsSettingsSuggestionStripOn; final public boolean mApplicationSpecifiedCompletionOn; @@ -52,7 +51,6 @@ public class InputAttributes { + " imeOptions=0x%08x", inputType, editorInfo.imeOptions)); } - mInsertSpaceOnPickSuggestionManually = false; mIsSettingsSuggestionStripOn = false; mInputTypeNoAutoCorrect = false; mApplicationSpecifiedCompletionOn = false; @@ -80,15 +78,6 @@ public class InputAttributes { mIsSettingsSuggestionStripOn = true; } - if (InputTypeCompatUtils.isEmailVariation(variation) - || variation == InputType.TYPE_TEXT_VARIATION_PERSON_NAME) { - // The point in turning this off is that we don't want to insert a space after - // a name when filling a form: we can't delete trailing spaces when changing fields - mInsertSpaceOnPickSuggestionManually = false; - } else { - mInsertSpaceOnPickSuggestionManually = true; - } - // If it's a browser edit field and auto correct is not ON explicitly, then // disable auto correction, but keep suggestions on. // If NO_SUGGESTIONS is set, don't do prediction. @@ -109,8 +98,7 @@ public class InputAttributes { // Pretty print @Override public String toString() { - return "\n mInsertSpaceOnPickSuggestionManually = " + mInsertSpaceOnPickSuggestionManually - + "\n mInputTypeNoAutoCorrect = " + mInputTypeNoAutoCorrect + return "\n mInputTypeNoAutoCorrect = " + mInputTypeNoAutoCorrect + "\n mIsSettingsSuggestionStripOn = " + mIsSettingsSuggestionStripOn + "\n mApplicationSpecifiedCompletionOn = " + mApplicationSpecifiedCompletionOn; } diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 1bc55a583..24007f95a 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -160,18 +160,21 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar SUGGESTION_VISIBILILTY_HIDE_VALUE }; - // Magic space: a space that should disappear on space/apostrophe insertion, move after the - // punctuation on punctuation insertion, and become a real space on alpha char insertion. - // Weak space: a space that should be swapped only by suggestion strip punctuation. + private static final int SPACE_STATE_NONE = 0; // Double space: the state where the user pressed space twice quickly, which LatinIME // resolved as period-space. Undoing this converts the period to a space. + private static final int SPACE_STATE_DOUBLE = 1; // Swap punctuation: the state where a (weak or magic) space and a punctuation from the // suggestion strip have just been swapped. Undoing this swaps them back. - private static final int SPACE_STATE_NONE = 0; - private static final int SPACE_STATE_DOUBLE = 1; private static final int SPACE_STATE_SWAP_PUNCTUATION = 2; - private static final int SPACE_STATE_MAGIC = 3; - private static final int SPACE_STATE_WEAK = 4; + // Weak space: a space that should be swapped only by suggestion strip punctuation. Weak + // spaces happen when the user presses space, accepting the current suggestion (whether + // it's an auto-correction or not). + private static final int SPACE_STATE_WEAK = 3; + // Phantom space: a not-yet-inserted space that should get inserted on the next input, + // character provided it's not a separator. If it's a separator, the phantom space is dropped. + // Phantom spaces happen when a user chooses a word from the suggestion strip. + private static final int SPACE_STATE_PHANTOM = 4; // Current space state of the input method. This can be any of the above constants. private int mSpaceState; @@ -1162,18 +1165,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar return false; } - // "ic" must not be null - private static void maybeRemovePreviousPeriod(final InputConnection ic, CharSequence text) { - // When the text's first character is '.', remove the previous period - // if there is one. - final CharSequence lastOne = ic.getTextBeforeCursor(1, 0); - if (lastOne != null && lastOne.length() == 1 - && lastOne.charAt(0) == Keyboard.CODE_PERIOD - && text.charAt(0) == Keyboard.CODE_PERIOD) { - ic.deleteSurroundingText(1, 0); - } - } - // "ic" may be null private static void removeTrailingSpaceWhileInBatchEdit(final InputConnection ic) { if (ic == null) return; @@ -1234,26 +1225,9 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } private void insertPunctuationFromSuggestionStrip(final InputConnection ic, final int code) { - final CharSequence beforeText = ic != null ? ic.getTextBeforeCursor(1, 0) : null; - final int toLeft = TextUtils.isEmpty(beforeText) ? 0 : beforeText.charAt(0); - final boolean shouldRegisterSwapPunctuation; - // If we have a space left of the cursor and it's a weak or a magic space, then we should - // swap it, and override the space state with SPACESTATE_SWAP_PUNCTUATION. - // To swap it, we fool handleSeparator to think the previous space state was a - // magic space. - if (Keyboard.CODE_SPACE == toLeft && mSpaceState == SPACE_STATE_WEAK - && mSettingsValues.isMagicSpaceSwapper(code)) { - mSpaceState = SPACE_STATE_MAGIC; - shouldRegisterSwapPunctuation = true; - } else { - shouldRegisterSwapPunctuation = false; - } onCodeInput(code, new int[] { code }, - KeyboardActionListener.NOT_A_TOUCH_COORDINATE, - KeyboardActionListener.NOT_A_TOUCH_COORDINATE); - if (shouldRegisterSwapPunctuation) { - mSpaceState = SPACE_STATE_SWAP_PUNCTUATION; - } + KeyboardActionListener.SUGGESTION_STRIP_COORDINATE, + KeyboardActionListener.SUGGESTION_STRIP_COORDINATE); } // Implementation of {@link KeyboardActionListener}. @@ -1331,7 +1305,10 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar if (ic == null) return; ic.beginBatchEdit(); commitTyped(ic); - maybeRemovePreviousPeriod(ic, text); + text = specificTldProcessingOnTextInput(ic, text); + if (SPACE_STATE_PHANTOM == mSpaceState) { + sendKeyChar((char)Keyboard.CODE_SPACE); + } ic.commitText(text, 1); ic.endBatchEdit(); mKeyboardSwitcher.updateShiftState(); @@ -1341,6 +1318,24 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar resetComposingState(true /* alsoResetLastComposedWord */); } + // ic may not be null + private CharSequence specificTldProcessingOnTextInput(final InputConnection ic, + final CharSequence text) { + if (text.length() <= 1 || text.charAt(0) != Keyboard.CODE_PERIOD + || !Character.isLetter(text.charAt(1))) { + // Not a tld: do nothing. + return text; + } + final CharSequence lastOne = ic.getTextBeforeCursor(1, 0); + if (lastOne != null && lastOne.length() == 1 + && lastOne.charAt(0) == Keyboard.CODE_PERIOD) { + mSpaceState = SPACE_STATE_NONE; + return text.subSequence(1, text.length()); + } else { + return text; + } + } + @Override public void onCancelInput() { // User released a finger outside any key @@ -1492,13 +1487,18 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar // "ic" may be null without this crashing, but the behavior will be really strange private void handleCharacterWhileInBatchEdit(final int primaryCode, final int[] keyCodes, final int x, final int y, final int spaceState, final InputConnection ic) { - if (SPACE_STATE_MAGIC == spaceState - && mSettingsValues.isMagicSpaceStripper(primaryCode)) { - if (null != ic) removeTrailingSpaceWhileInBatchEdit(ic); - } - boolean isComposingWord = mWordComposer.isComposingWord(); int code = primaryCode; + + if (SPACE_STATE_PHANTOM == spaceState && + !mSettingsValues.isSymbolExcludedFromWordSeparators(primaryCode)) { + if (isComposingWord) { + // Sanity check + throw new RuntimeException("Should not be composing here"); + } + sendKeyChar((char)Keyboard.CODE_SPACE); + } + if ((isAlphabet(code) || mSettingsValues.isSymbolExcludedFromWordSeparators(code)) && isSuggestionsRequested() && !isCursorTouchingWord()) { if (!isComposingWord) { @@ -1530,10 +1530,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } else { sendKeyChar((char)code); } - if (SPACE_STATE_MAGIC == spaceState - && mSettingsValues.isMagicSpaceSwapper(primaryCode)) { - if (null != ic) swapSwapperAndSpaceWhileInBatchEdit(ic); - } if (mSettingsValues.isWordSeparator(code)) { Utils.Stats.onSeparator((char)code, x, y); @@ -1575,24 +1571,28 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } } - final boolean swapMagicSpace; - if (Keyboard.CODE_ENTER == primaryCode && (SPACE_STATE_MAGIC == spaceState - || SPACE_STATE_SWAP_PUNCTUATION == spaceState)) { + final boolean swapWeakSpace; + if (Keyboard.CODE_ENTER == primaryCode && SPACE_STATE_SWAP_PUNCTUATION == spaceState) { removeTrailingSpaceWhileInBatchEdit(ic); - swapMagicSpace = false; - } else if (SPACE_STATE_MAGIC == spaceState) { + swapWeakSpace = false; + } else if ((SPACE_STATE_WEAK == spaceState || SPACE_STATE_SWAP_PUNCTUATION == spaceState) + && KeyboardActionListener.SUGGESTION_STRIP_COORDINATE == x) { if (mSettingsValues.isMagicSpaceSwapper(primaryCode)) { - swapMagicSpace = true; + swapWeakSpace = true; } else { - swapMagicSpace = false; + swapWeakSpace = false; if (mSettingsValues.isMagicSpaceStripper(primaryCode)) { removeTrailingSpaceWhileInBatchEdit(ic); } } } else { - swapMagicSpace = false; + swapWeakSpace = false; } + // TODO: rethink interactions of sendKeyChar, commitText("\n") and actions. sendKeyChar + // with a CODE_ENTER parameter will have the default InputMethodService implementation + // possibly redirect on the keyboard action. That may be the right thing to do, but + // on Shift+Enter, it's generally not, so we may want to do the redirection right here. sendKeyChar((char)primaryCode); if (Keyboard.CODE_SPACE == primaryCode) { @@ -1610,9 +1610,17 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar mHandler.postUpdateBigramPredictions(); } } else { - if (swapMagicSpace) { + if (swapWeakSpace) { swapSwapperAndSpaceWhileInBatchEdit(ic); - mSpaceState = SPACE_STATE_MAGIC; + mSpaceState = SPACE_STATE_WEAK; + } else if (SPACE_STATE_PHANTOM == spaceState) { + // If we are in phantom space state, and the user presses a separator, we want to + // stay in phantom space state so that the next keypress has a chance to add the + // space. For example, if I type "Good dat", pick "day" from the suggestion strip + // then insert a comma and go on to typing the next word, I want the space to be + // inserted automatically before the next word, the same way it is when I don't + // input the comma. + mSpaceState = SPACE_STATE_PHANTOM; } // Set punctuation right away. onUpdateSelection will fire but tests whether it is @@ -1921,10 +1929,9 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } else { addToOnlyBigramDictionary(suggestion, 1); } - // Follow it with a space - if (mInputAttributes.mInsertSpaceOnPickSuggestionManually) { - sendMagicSpace(); - } + mSpaceState = SPACE_STATE_PHANTOM; + // TODO: is this necessary? + mKeyboardSwitcher.updateShiftState(); // We should show the "Touch again to save" hint if the user pressed the first entry // AND either: @@ -2259,12 +2266,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar return mSettingsValues.isWordSeparator(code); } - private void sendMagicSpace() { - sendKeyChar((char)Keyboard.CODE_SPACE); - mSpaceState = SPACE_STATE_MAGIC; - mKeyboardSwitcher.updateShiftState(); - } - public boolean preferCapitalization() { return mWordComposer.isFirstCharCapitalized(); } diff --git a/java/src/com/android/inputmethod/latin/SettingsValues.java b/java/src/com/android/inputmethod/latin/SettingsValues.java index 8e2f605c4..589cb6f86 100644 --- a/java/src/com/android/inputmethod/latin/SettingsValues.java +++ b/java/src/com/android/inputmethod/latin/SettingsValues.java @@ -93,7 +93,8 @@ public class SettingsValues { mMagicSpaceStrippers = res.getString(R.string.magic_space_stripping_symbols); mMagicSpaceSwappers = res.getString(R.string.magic_space_swapping_symbols); if (LatinImeLogger.sDBG) { - for (int i = 0; i < mMagicSpaceStrippers.length(); ++i) { + final int length = mMagicSpaceStrippers.length(); + for (int i = 0; i < length; i = mMagicSpaceStrippers.offsetByCodePoints(i, 1)) { if (isMagicSpaceSwapper(mMagicSpaceStrippers.codePointAt(i))) { throw new RuntimeException("Char code " + mMagicSpaceStrippers.codePointAt(i) + " is both a magic space swapper and stripper."); @@ -234,10 +235,12 @@ public class SettingsValues { } public boolean isMagicSpaceStripper(int code) { + // TODO: this does not work if the code does not fit in a char return mMagicSpaceStrippers.contains(String.valueOf((char)code)); } public boolean isMagicSpaceSwapper(int code) { + // TODO: this does not work if the code does not fit in a char return mMagicSpaceSwappers.contains(String.valueOf((char)code)); } diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java index bd244b913..dd24432f7 100644 --- a/java/src/com/android/inputmethod/latin/WordComposer.java +++ b/java/src/com/android/inputmethod/latin/WordComposer.java @@ -212,6 +212,7 @@ public class WordComposer { final int lastPos = size - 1; char lastChar = mTypedWord.charAt(lastPos); mCodes.remove(lastPos); + // TODO: This crashes and catches fire if the code point doesn't fit a char mTypedWord.deleteCharAt(lastPos); if (Character.isUpperCase(lastChar)) mCapsCount--; } @@ -221,7 +222,9 @@ public class WordComposer { if (mTrailingSingleQuotesCount > 0) { --mTrailingSingleQuotesCount; } else { - for (int i = mTypedWord.length() - 1; i >= 0; --i) { + int i = mTypedWord.length(); + while (i > 0) { + i = mTypedWord.offsetByCodePoints(i, -1); if (Keyboard.CODE_SINGLE_QUOTE != mTypedWord.codePointAt(i)) break; ++mTrailingSingleQuotesCount; } diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java index 88ac043ed..8ac82ee5b 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java @@ -431,9 +431,9 @@ public class AndroidSpellCheckerService extends SpellCheckerService // If the first char is not uppercase, then the word is either all lower case, // and in either case we return CAPITALIZE_NONE. if (!Character.isUpperCase(text.codePointAt(0))) return CAPITALIZE_NONE; - final int len = text.codePointCount(0, text.length()); + final int len = text.length(); int capsCount = 1; - for (int i = 1; i < len; ++i) { + for (int i = 1; i < len; i = text.offsetByCodePoints(i, 1)) { if (1 != capsCount && i != capsCount) break; if (Character.isUpperCase(text.codePointAt(i))) ++capsCount; } @@ -522,13 +522,12 @@ public class AndroidSpellCheckerService extends SpellCheckerService // Filter contents final int length = text.length(); int letterCount = 0; - for (int i = 0; i < length; ++i) { + for (int i = 0; i < length; i = text.offsetByCodePoints(i, 1)) { final int codePoint = text.codePointAt(i); // Any word containing a '@' is probably an e-mail address // Any word containing a '/' is probably either an ad-hoc combination of two // words or a URI - in either case we don't want to spell check that - if ('@' == codePoint - || '/' == codePoint) return true; + if ('@' == codePoint || '/' == codePoint) return true; if (isLetterCheckableByLanguage(codePoint, script)) ++letterCount; } // Guestimate heuristic: perform spell checking if at least 3/4 of the characters @@ -570,7 +569,7 @@ public class AndroidSpellCheckerService extends SpellCheckerService suggestionsLimit); final WordComposer composer = new WordComposer(); final int length = text.length(); - for (int i = 0; i < length; ++i) { + for (int i = 0; i < length; i = text.offsetByCodePoints(i, 1)) { final int character = text.codePointAt(i); final int proximityIndex = SpellCheckerProximityInfo.getIndexOfCodeForScript(character, mScript); diff --git a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java index f42b8e681..4ef5bd386 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java +++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java @@ -25,6 +25,7 @@ import com.android.inputmethod.keyboard.Key; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.KeyboardSwitcher; import com.android.inputmethod.keyboard.KeyboardView; +import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SuggestedWords; @@ -199,6 +200,21 @@ public class MoreSuggestions extends Keyboard { return info; } + private static class Divider extends Key.Spacer { + private final Drawable mIcon; + + public Divider(Keyboard.Params params, Drawable icon, int x, int y, int width, + int height) { + super(params, x, y, width, height); + mIcon = icon; + } + + @Override + public Drawable getIcon(KeyboardIconsSet iconSet) { + return mIcon; + } + } + @Override public MoreSuggestions build() { final MoreSuggestionsParam params = mParams; @@ -210,16 +226,16 @@ public class MoreSuggestions extends Keyboard { final String info = getDebugInfo(mSuggestions, pos); final int index = pos + SUGGESTION_CODE_BASE; final Key key = new Key( - params, word, info, null, index, null, x, y, width, - params.mDefaultRowHeight); + params, word, info, KeyboardIconsSet.ICON_UNDEFINED, index, null, x, y, + width, params.mDefaultRowHeight); params.markAsEdgeKey(key, pos); params.onAddKey(key); final int columnNumber = params.getColumnNumber(pos); final int numColumnInRow = params.getNumColumnInRow(pos); if (columnNumber < numColumnInRow - 1) { - final Key.Spacer spacer = new Key.Spacer(params, params.mDivider, x + width, y, + final Divider divider = new Divider(params, params.mDivider, x + width, y, params.mDividerWidth, params.mDefaultRowHeight); - params.onAddKey(spacer); + params.onAddKey(divider); } } return new MoreSuggestions(params); |