diff options
Diffstat (limited to 'java/src')
14 files changed, 451 insertions, 337 deletions
diff --git a/java/src/com/android/inputmethod/compat/EditorInfoCompatUtils.java b/java/src/com/android/inputmethod/compat/EditorInfoCompatUtils.java index e1db36088..3247997f6 100644 --- a/java/src/com/android/inputmethod/compat/EditorInfoCompatUtils.java +++ b/java/src/com/android/inputmethod/compat/EditorInfoCompatUtils.java @@ -67,42 +67,34 @@ public class EditorInfoCompatUtils { ic.performEditorAction(OBJ_IME_ACTION_PREVIOUS); } - public static String imeOptionsName(int imeOptions) { - if (imeOptions == -1) - return null; + public static String imeActionName(int imeOptions) { final int actionId = imeOptions & EditorInfo.IME_MASK_ACTION; - final String action; switch (actionId) { - case EditorInfo.IME_ACTION_UNSPECIFIED: - action = "actionUnspecified"; - break; - case EditorInfo.IME_ACTION_NONE: - action = "actionNone"; - break; - case EditorInfo.IME_ACTION_GO: - action = "actionGo"; - break; - case EditorInfo.IME_ACTION_SEARCH: - action = "actionSearch"; - break; - case EditorInfo.IME_ACTION_SEND: - action = "actionSend"; - break; - case EditorInfo.IME_ACTION_NEXT: - action = "actionNext"; - break; - case EditorInfo.IME_ACTION_DONE: - action = "actionDone"; - break; - default: { - if (OBJ_IME_ACTION_PREVIOUS != null && actionId == OBJ_IME_ACTION_PREVIOUS) { - action = "actionPrevious"; - } else { - action = "actionUnknown(" + actionId + ")"; - } - break; + case EditorInfo.IME_ACTION_UNSPECIFIED: + return "actionUnspecified"; + case EditorInfo.IME_ACTION_NONE: + return "actionNone"; + case EditorInfo.IME_ACTION_GO: + return "actionGo"; + case EditorInfo.IME_ACTION_SEARCH: + return "actionSearch"; + case EditorInfo.IME_ACTION_SEND: + return "actionSend"; + case EditorInfo.IME_ACTION_NEXT: + return "actionNext"; + case EditorInfo.IME_ACTION_DONE: + return "actionDone"; + default: + if (OBJ_IME_ACTION_PREVIOUS != null && actionId == OBJ_IME_ACTION_PREVIOUS) { + return "actionPrevious"; + } else { + return "actionUnknown(" + actionId + ")"; } } + } + + public static String imeOptionsName(int imeOptions) { + final String action = imeActionName(imeOptions); final StringBuilder flags = new StringBuilder(); if ((imeOptions & EditorInfo.IME_FLAG_NO_ENTER_ACTION) != 0) { flags.append("flagNoEnterAction|"); @@ -116,6 +108,6 @@ public class EditorInfoCompatUtils { if (hasFlagForceAscii(imeOptions)) { flags.append("flagForceAscii|"); } - return flags + action; + return (action != null) ? flags + action : flags.toString(); } } diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java index 686392da8..b90d45d3b 100644 --- a/java/src/com/android/inputmethod/keyboard/Key.java +++ b/java/src/com/android/inputmethod/keyboard/Key.java @@ -72,6 +72,7 @@ public class Key { private static final int LABEL_FLAGS_AUTO_X_SCALE = 0x4000; private static final int LABEL_FLAGS_PRESERVE_CASE = 0x8000; private static final int LABEL_FLAGS_SHIFTED_LETTER_ACTIVATED = 0x10000; + private static final int LABEL_FLAGS_FROM_CUSTOM_ACTION_LABEL = 0x20000; /** Icon to display instead of a label. Icon takes precedence over a label */ private final int mIconId; @@ -228,9 +229,9 @@ public class Key { mDisabledIconId = style.getInt(keyAttr, R.styleable.Keyboard_Key_keyIconDisabled, KeyboardIconsSet.ICON_UNDEFINED); - mLabelFlags = style.getFlag(keyAttr, R.styleable.Keyboard_Key_keyLabelFlags, 0); + mLabelFlags = style.getFlag(keyAttr, R.styleable.Keyboard_Key_keyLabelFlags); final boolean preserveCase = (mLabelFlags & LABEL_FLAGS_PRESERVE_CASE) != 0; - int actionFlags = style.getFlag(keyAttr, R.styleable.Keyboard_Key_keyActionFlags, 0); + int actionFlags = style.getFlag(keyAttr, R.styleable.Keyboard_Key_keyActionFlags); final String[] additionalMoreKeys = style.getStringArray( keyAttr, R.styleable.Keyboard_Key_additionalMoreKeys); final String[] moreKeys = KeySpecParser.insertAddtionalMoreKeys(style.getStringArray( @@ -245,10 +246,14 @@ public class Key { mActionFlags = actionFlags; mMoreKeys = moreKeys; mMaxMoreKeysColumn = style.getInt(keyAttr, - R.styleable.Keyboard_Key_maxMoreKeysColumn, params.mMaxMiniKeyboardColumn); + R.styleable.Keyboard_Key_maxMoreKeysColumn, params.mMaxMoreKeysKeyboardColumn); - mLabel = adjustCaseOfStringForKeyboardId(style.getString( - keyAttr, R.styleable.Keyboard_Key_keyLabel), preserveCase, params.mId); + if ((mLabelFlags & LABEL_FLAGS_FROM_CUSTOM_ACTION_LABEL) != 0) { + mLabel = params.mId.mCustomActionLabel; + } else { + mLabel = adjustCaseOfStringForKeyboardId(style.getString( + keyAttr, R.styleable.Keyboard_Key_keyLabel), preserveCase, params.mId); + } mHintLabel = adjustCaseOfStringForKeyboardId(style.getString( keyAttr, R.styleable.Keyboard_Key_keyHintLabel), preserveCase, params.mId); String outputText = adjustCaseOfStringForKeyboardId(style.getString( @@ -274,7 +279,12 @@ public class Key { mCode = Keyboard.CODE_OUTPUT_TEXT; } } else if (code == Keyboard.CODE_UNSPECIFIED && outputText != null) { - mCode = Keyboard.CODE_OUTPUT_TEXT; + if (Utils.codePointCount(outputText) == 1) { + mCode = outputText.codePointAt(0); + outputText = null; + } else { + mCode = Keyboard.CODE_OUTPUT_TEXT; + } } else { mCode = adjustCaseOfCodeForKeyboardId(code, preserveCase, params.mId); } diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java index 10e0a5b01..28f71f443 100644 --- a/java/src/com/android/inputmethod/keyboard/Keyboard.java +++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java @@ -28,7 +28,6 @@ import android.util.TypedValue; import android.util.Xml; import android.view.InflateException; -import com.android.inputmethod.compat.EditorInfoCompatUtils; import com.android.inputmethod.keyboard.internal.KeyStyles; import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; import com.android.inputmethod.latin.LatinImeLogger; @@ -95,10 +94,11 @@ public class Keyboard { */ public static final int CODE_SHIFT = -1; public static final int CODE_SWITCH_ALPHA_SYMBOL = -2; - public static final int CODE_OUTPUT_TEXT = -4; - public static final int CODE_DELETE = -5; - public static final int CODE_SETTINGS = -6; - public static final int CODE_SHORTCUT = -7; + public static final int CODE_OUTPUT_TEXT = -3; + public static final int CODE_DELETE = -4; + public static final int CODE_SETTINGS = -5; + public static final int CODE_SHORTCUT = -6; + public static final int CODE_ACTION_ENTER = -7; // Code value representing the code is not specified. public static final int CODE_UNSPECIFIED = -9; @@ -121,8 +121,8 @@ public class Keyboard { /** More keys keyboard template */ public final int mMoreKeysTemplate; - /** Maximum column for mini keyboard */ - public final int mMaxMiniKeyboardColumn; + /** Maximum column for more keys keyboard */ + public final int mMaxMoreKeysKeyboardColumn; /** List of keys and icons in this keyboard */ public final Set<Key> mKeys; @@ -143,7 +143,7 @@ public class Keyboard { mMostCommonKeyHeight = params.mMostCommonKeyHeight; mMostCommonKeyWidth = params.mMostCommonKeyWidth; mMoreKeysTemplate = params.mMoreKeysTemplate; - mMaxMiniKeyboardColumn = params.mMaxMiniKeyboardColumn; + mMaxMoreKeysKeyboardColumn = params.mMaxMoreKeysKeyboardColumn; mTopPadding = params.mTopPadding; mVerticalGap = params.mVerticalGap; @@ -226,7 +226,7 @@ public class Keyboard { public int mVerticalGap; public int mMoreKeysTemplate; - public int mMaxMiniKeyboardColumn; + public int mMaxMoreKeysKeyboardColumn; public int GRID_WIDTH; public int GRID_HEIGHT; @@ -380,6 +380,7 @@ public class Keyboard { case CODE_DELETE: return "delete"; case CODE_SETTINGS: return "settings"; case CODE_SHORTCUT: return "shortcut"; + case CODE_ACTION_ENTER: return "actionEnter"; case CODE_UNSPECIFIED: return "unspec"; case CODE_TAB: return "tab"; case CODE_ENTER: return "enter"; @@ -475,7 +476,7 @@ public class Keyboard { */ public static class Builder<KP extends Params> { - private static final String TAG = Builder.class.getSimpleName(); + private static final String BUILDER_TAG = "Keyboard.Builder"; private static final boolean DEBUG = false; // Keyboard XML Tags @@ -643,7 +644,7 @@ public class Keyboard { a.recycle(); if (resourceId == 0) { if (LatinImeLogger.sDBG) - Log.e(TAG, "touchPositionCorrectionData is not defined"); + Log.e(BUILDER_TAG, "touchPositionCorrectionData is not defined"); return; } @@ -680,10 +681,10 @@ public class Keyboard { try { parseKeyboard(parser); } catch (XmlPullParserException e) { - Log.w(TAG, "keyboard XML parse error: " + e); + Log.w(BUILDER_TAG, "keyboard XML parse error: " + e); throw new IllegalArgumentException(e); } catch (IOException e) { - Log.w(TAG, "keyboard XML parse error: " + e); + Log.w(BUILDER_TAG, "keyboard XML parse error: " + e); throw new RuntimeException(e); } finally { parser.close(); @@ -699,9 +700,29 @@ public class Keyboard { return new Keyboard(mParams); } + private int mIndent; + private static final String SPACES = " "; + + private static String spaces(int count) { + return (count < SPACES.length()) ? SPACES.substring(0, count) : SPACES; + } + + private void startTag(String format, Object ... args) { + Log.d(BUILDER_TAG, String.format(spaces(++mIndent * 2) + format, args)); + } + + private void endTag(String format, Object ... args) { + Log.d(BUILDER_TAG, String.format(spaces(mIndent-- * 2) + format, args)); + } + + private void startEndTag(String format, Object ... args) { + Log.d(BUILDER_TAG, String.format(spaces(++mIndent * 2) + format, args)); + mIndent--; + } + private void parseKeyboard(XmlPullParser parser) throws XmlPullParserException, IOException { - if (DEBUG) Log.d(TAG, String.format("<%s> %s", TAG_KEYBOARD, mParams.mId)); + if (DEBUG) startTag("<%s> %s", TAG_KEYBOARD, mParams.mId); int event; while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) { if (event == XmlPullParser.START_TAG) { @@ -770,7 +791,7 @@ public class Keyboard { params.mMoreKeysTemplate = keyboardAttr.getResourceId( R.styleable.Keyboard_moreKeysTemplate, 0); - params.mMaxMiniKeyboardColumn = keyAttr.getInt( + params.mMaxMoreKeysKeyboardColumn = keyAttr.getInt( R.styleable.Keyboard_Key_maxMoreKeysColumn, 5); params.mIconsSet.loadIcons(keyboardAttr); @@ -788,9 +809,10 @@ public class Keyboard { final String tag = parser.getName(); if (TAG_ROW.equals(tag)) { Row row = parseRowAttributes(parser); - if (DEBUG) Log.d(TAG, String.format("<%s>", TAG_ROW)); - if (!skip) + if (DEBUG) startTag("<%s>%s", TAG_ROW, skip ? " skipped" : ""); + if (!skip) { startRow(row); + } parseRowContent(parser, row, skip); } else if (TAG_INCLUDE.equals(tag)) { parseIncludeKeyboardContent(parser, skip); @@ -803,15 +825,13 @@ public class Keyboard { } } else if (event == XmlPullParser.END_TAG) { final String tag = parser.getName(); + if (DEBUG) endTag("</%s>", tag); if (TAG_KEYBOARD.equals(tag)) { endKeyboard(); break; } else if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag) || TAG_MERGE.equals(tag)) { - if (DEBUG) Log.d(TAG, String.format("</%s>", tag)); break; - } else if (TAG_KEY_STYLE.equals(tag)) { - continue; } else { throw new XmlParseUtils.IllegalEndTag(parser, TAG_ROW); } @@ -854,17 +874,15 @@ public class Keyboard { } } else if (event == XmlPullParser.END_TAG) { final String tag = parser.getName(); + if (DEBUG) endTag("</%s>", tag); if (TAG_ROW.equals(tag)) { - if (DEBUG) Log.d(TAG, String.format("</%s>", TAG_ROW)); - if (!skip) + if (!skip) { endRow(row); + } break; } else if (TAG_CASE.equals(tag) || TAG_DEFAULT.equals(tag) || TAG_MERGE.equals(tag)) { - if (DEBUG) Log.d(TAG, String.format("</%s>", tag)); break; - } else if (TAG_KEY_STYLE.equals(tag)) { - continue; } else { throw new XmlParseUtils.IllegalEndTag(parser, TAG_KEY); } @@ -876,11 +894,14 @@ public class Keyboard { throws XmlPullParserException, IOException { if (skip) { XmlParseUtils.checkEndTag(TAG_KEY, parser); + if (DEBUG) startEndTag("<%s /> skipped", TAG_KEY); } else { final Key key = new Key(mResources, mParams, row, parser, mKeyStyles); - if (DEBUG) Log.d(TAG, String.format("<%s%s keyLabel=%s code=%d moreKeys=%s />", - TAG_KEY, (key.isEnabled() ? "" : " disabled"), key.mLabel, key.mCode, - Arrays.toString(key.mMoreKeys))); + if (DEBUG) { + startEndTag("<%s%s %s moreKeys=%s />", TAG_KEY, + (key.isEnabled() ? "" : " disabled"), key, + Arrays.toString(key.mMoreKeys)); + } XmlParseUtils.checkEndTag(TAG_KEY, parser); endKey(key); } @@ -890,10 +911,11 @@ public class Keyboard { throws XmlPullParserException, IOException { if (skip) { XmlParseUtils.checkEndTag(TAG_SPACER, parser); + if (DEBUG) startEndTag("<%s /> skipped", TAG_SPACER); } else { final Key.Spacer spacer = new Key.Spacer( mResources, mParams, row, parser, mKeyStyles); - if (DEBUG) Log.d(TAG, String.format("<%s />", TAG_SPACER)); + if (DEBUG) startEndTag("<%s />", TAG_SPACER); XmlParseUtils.checkEndTag(TAG_SPACER, parser); endKey(spacer); } @@ -913,6 +935,7 @@ public class Keyboard { throws XmlPullParserException, IOException { if (skip) { XmlParseUtils.checkEndTag(TAG_INCLUDE, parser); + if (DEBUG) startEndTag("</%s> skipped", TAG_INCLUDE); } else { final AttributeSet attr = Xml.asAttributeSet(parser); final TypedArray keyboardAttr = mResources.obtainAttributes(attr, @@ -944,8 +967,10 @@ public class Keyboard { } XmlParseUtils.checkEndTag(TAG_INCLUDE, parser); - if (DEBUG) Log.d(TAG, String.format("<%s keyboardLayout=%s />", - TAG_INCLUDE, mResources.getResourceEntryName(keyboardLayout))); + if (DEBUG) { + startEndTag("<%s keyboardLayout=%s />",TAG_INCLUDE, + mResources.getResourceEntryName(keyboardLayout)); + } final XmlResourceParser parserForInclude = mResources.getXml(keyboardLayout); try { parseMerge(parserForInclude, row, skip); @@ -961,6 +986,7 @@ public class Keyboard { private void parseMerge(XmlPullParser parser, Row row, boolean skip) throws XmlPullParserException, IOException { + if (DEBUG) startTag("<%s>", TAG_MERGE); int event; while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) { if (event == XmlPullParser.START_TAG) { @@ -992,7 +1018,7 @@ public class Keyboard { private void parseSwitchInternal(XmlPullParser parser, Row row, boolean skip) throws XmlPullParserException, IOException { - if (DEBUG) Log.d(TAG, String.format("<%s> %s", TAG_SWITCH, mParams.mId)); + if (DEBUG) startTag("<%s> %s", TAG_SWITCH, mParams.mId); boolean selected = false; int event; while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) { @@ -1008,7 +1034,7 @@ public class Keyboard { } else if (event == XmlPullParser.END_TAG) { final String tag = parser.getName(); if (TAG_SWITCH.equals(tag)) { - if (DEBUG) Log.d(TAG, String.format("</%s>", TAG_SWITCH)); + if (DEBUG) endTag("</%s>", TAG_SWITCH); break; } else { throw new XmlParseUtils.IllegalEndTag(parser, TAG_KEY); @@ -1057,10 +1083,8 @@ public class Keyboard { R.styleable.Keyboard_Case_shortcutKeyEnabled, id.mShortcutKeyEnabled); final boolean hasShortcutKeyMatched = matchBoolean(a, R.styleable.Keyboard_Case_hasShortcutKey, id.mHasShortcutKey); - // As noted at {@link KeyboardId} class, we are interested only in enum value - // masked by {@link android.view.inputmethod.EditorInfo#IME_MASK_ACTION} and - // {@link android.view.inputmethod.EditorInfo#IME_FLAG_NO_ENTER_ACTION}. So matching - // this attribute with id.mImeOptions as integer value is enough for our purpose. + final boolean isMultiLineMatched = matchBoolean(a, + R.styleable.Keyboard_Case_isMultiLine, id.isMultiLine()); final boolean imeActionMatched = matchInteger(a, R.styleable.Keyboard_Case_imeAction, id.imeAction()); final boolean localeCodeMatched = matchString(a, @@ -1072,30 +1096,42 @@ public class Keyboard { final boolean selected = keyboardSetElementMatched && modeMatched && navigateActionMatched && passwordInputMatched && hasSettingsKeyMatched && f2KeyModeMatched && clobberSettingsKeyMatched - && shortcutKeyEnabledMatched && hasShortcutKeyMatched && imeActionMatched - && localeCodeMatched && languageCodeMatched && countryCodeMatched; - - if (DEBUG) Log.d(TAG, String.format("<%s%s%s%s%s%s%s%s%s%s%s%s%s%s> %s", TAG_CASE, - textAttr(a.getString(R.styleable.Keyboard_Case_keyboardSetElement), - "keyboardSetElement"), - textAttr(a.getString(R.styleable.Keyboard_Case_mode), "mode"), - booleanAttr(a, R.styleable.Keyboard_Case_navigateAction, "navigateAction"), - booleanAttr(a, R.styleable.Keyboard_Case_passwordInput, "passwordInput"), - booleanAttr(a, R.styleable.Keyboard_Case_hasSettingsKey, "hasSettingsKey"), - textAttr(KeyboardId.f2KeyModeName( - a.getInt(R.styleable.Keyboard_Case_f2KeyMode, -1)), "f2KeyMode"), - booleanAttr(a, R.styleable.Keyboard_Case_clobberSettingsKey, - "clobberSettingsKey"), - booleanAttr(a, R.styleable.Keyboard_Case_shortcutKeyEnabled, - "shortcutKeyEnabled"), - booleanAttr(a, R.styleable.Keyboard_Case_hasShortcutKey, "hasShortcutKey"), - textAttr(EditorInfoCompatUtils.imeOptionsName( - a.getInt(R.styleable.Keyboard_Case_imeAction, -1)), "imeAction"), - textAttr(a.getString(R.styleable.Keyboard_Case_localeCode), "localeCode"), - textAttr(a.getString(R.styleable.Keyboard_Case_languageCode), - "languageCode"), - textAttr(a.getString(R.styleable.Keyboard_Case_countryCode), "countryCode"), - Boolean.toString(selected))); + && shortcutKeyEnabledMatched && hasShortcutKeyMatched && isMultiLineMatched + && imeActionMatched && localeCodeMatched && languageCodeMatched + && countryCodeMatched; + + if (DEBUG) { + startTag("<%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s>%s", TAG_CASE, + textAttr(a.getString(R.styleable.Keyboard_Case_keyboardSetElement), + "keyboardSetElement"), + textAttr(a.getString(R.styleable.Keyboard_Case_mode), "mode"), + booleanAttr(a, R.styleable.Keyboard_Case_navigateAction, + "navigateAction"), + booleanAttr(a, R.styleable.Keyboard_Case_passwordInput, + "passwordInput"), + booleanAttr(a, R.styleable.Keyboard_Case_hasSettingsKey, + "hasSettingsKey"), + textAttr(KeyboardId.f2KeyModeName( + a.getInt(R.styleable.Keyboard_Case_f2KeyMode, -1)), + "f2KeyMode"), + booleanAttr(a, R.styleable.Keyboard_Case_clobberSettingsKey, + "clobberSettingsKey"), + booleanAttr(a, R.styleable.Keyboard_Case_shortcutKeyEnabled, + "shortcutKeyEnabled"), + booleanAttr(a, R.styleable.Keyboard_Case_hasShortcutKey, + "hasShortcutKey"), + booleanAttr(a, R.styleable.Keyboard_Case_isMultiLine, + "isMultiLine"), + textAttr(a.getString(R.styleable.Keyboard_Case_imeAction), + "imeAction"), + textAttr(a.getString(R.styleable.Keyboard_Case_localeCode), + "localeCode"), + textAttr(a.getString(R.styleable.Keyboard_Case_languageCode), + "languageCode"), + textAttr(a.getString(R.styleable.Keyboard_Case_countryCode), + "countryCode"), + selected ? "" : " skipped"); + } return selected; } finally { @@ -1148,7 +1184,7 @@ public class Keyboard { private boolean parseDefault(XmlPullParser parser, Row row, boolean skip) throws XmlPullParserException, IOException { - if (DEBUG) Log.d(TAG, String.format("<%s>", TAG_DEFAULT)); + if (DEBUG) startTag("<%s>", TAG_DEFAULT); if (row == null) { parseKeyboardContent(parser, skip); } else { @@ -1158,7 +1194,7 @@ public class Keyboard { } private void parseKeyStyle(XmlPullParser parser, boolean skip) - throws XmlPullParserException { + throws XmlPullParserException, IOException { TypedArray keyStyleAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser), R.styleable.Keyboard_KeyStyle); TypedArray keyAttrs = mResources.obtainAttributes(Xml.asAttributeSet(parser), @@ -1167,12 +1203,18 @@ public class Keyboard { if (!keyStyleAttr.hasValue(R.styleable.Keyboard_KeyStyle_styleName)) throw new XmlParseUtils.ParseException("<" + TAG_KEY_STYLE + "/> needs styleName attribute", parser); + if (DEBUG) { + startEndTag("<%s styleName=%s />%s", TAG_KEY_STYLE, + keyStyleAttr.getString(R.styleable.Keyboard_KeyStyle_styleName), + skip ? " skipped" : ""); + } if (!skip) mKeyStyles.parseKeyStyleAttributes(keyStyleAttr, keyAttrs, parser); } finally { keyStyleAttr.recycle(); keyAttrs.recycle(); } + XmlParseUtils.checkEndTag(TAG_KEY_STYLE, parser); } private void startKeyboard() { diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardId.java b/java/src/com/android/inputmethod/keyboard/KeyboardId.java index 0837e17da..3ab24933c 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardId.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardId.java @@ -16,6 +16,7 @@ package com.android.inputmethod.keyboard; +import android.text.InputType; import android.text.TextUtils; import android.view.inputmethod.EditorInfo; @@ -25,10 +26,8 @@ import com.android.inputmethod.compat.InputTypeCompatUtils; import java.util.Arrays; import java.util.Locale; -// TODO: Move to com.android.inputmethod.keyboard.internal package. /** - * Represents the parameters necessary to construct a new LatinKeyboard, - * which also serve as a unique identifier for each keyboard type. + * Unique identifier for each keyboard type. */ public class KeyboardId { public static final int MODE_TEXT = 0; @@ -54,34 +53,37 @@ public class KeyboardId { private static final int F2KEY_MODE_SHORTCUT_IME = 2; private static final int F2KEY_MODE_SHORTCUT_IME_OR_SETTINGS = 3; + private static final int IME_ACTION_CUSTOM_LABEL = EditorInfo.IME_MASK_ACTION + 1; + public final Locale mLocale; public final int mOrientation; public final int mWidth; public final int mMode; public final int mElementId; - private final int mInputType; - private final int mImeOptions; + private final EditorInfo mEditorInfo; private final boolean mSettingsKeyEnabled; public final boolean mClobberSettingsKey; public final boolean mShortcutKeyEnabled; public final boolean mHasShortcutKey; + public final String mCustomActionLabel; private final int mHashCode; public KeyboardId(int elementId, Locale locale, int orientation, int width, int mode, - int inputType, int imeOptions, boolean settingsKeyEnabled, boolean clobberSettingsKey, + EditorInfo editorInfo, boolean settingsKeyEnabled, boolean clobberSettingsKey, boolean shortcutKeyEnabled, boolean hasShortcutKey) { this.mLocale = locale; this.mOrientation = orientation; this.mWidth = width; this.mMode = mode; this.mElementId = elementId; - this.mInputType = inputType; - this.mImeOptions = imeOptions; + this.mEditorInfo = editorInfo; this.mSettingsKeyEnabled = settingsKeyEnabled; this.mClobberSettingsKey = clobberSettingsKey; this.mShortcutKeyEnabled = shortcutKeyEnabled; this.mHasShortcutKey = hasShortcutKey; + this.mCustomActionLabel = (editorInfo.actionLabel != null) + ? editorInfo.actionLabel.toString() : null; this.mHashCode = hashCode(this); } @@ -98,7 +100,9 @@ public class KeyboardId { id.mClobberSettingsKey, id.mShortcutKeyEnabled, id.mHasShortcutKey, + id.isMultiLine(), id.imeAction(), + id.mCustomActionLabel, id.mLocale }); } @@ -116,7 +120,9 @@ public class KeyboardId { && other.mClobberSettingsKey == this.mClobberSettingsKey && other.mShortcutKeyEnabled == this.mShortcutKeyEnabled && other.mHasShortcutKey == this.mHasShortcutKey + && other.isMultiLine() == this.isMultiLine() && other.imeAction() == this.imeAction() + && TextUtils.equals(other.mCustomActionLabel, this.mCustomActionLabel) && other.mLocale.equals(this.mLocale); } @@ -124,20 +130,17 @@ public class KeyboardId { return mElementId < ELEMENT_SYMBOLS; } - // This should be aligned with {@link KeyboardShiftState#isShiftLocked}. public boolean isAlphabetShiftLockedKeyboard() { return mElementId == ELEMENT_ALPHABET_SHIFT_LOCKED || mElementId == ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED; } - // This should be aligned with {@link KeyboardShiftState#isShiftedOrShiftLocked}. public boolean isAlphabetShiftedOrShiftLockedKeyboard() { return isAlphabetKeyboard() && mElementId != ELEMENT_ALPHABET; } - // This should be aligned with {@link KeyboardShiftState#isManualShifted}. public boolean isAlphabetManualShiftedKeyboard() { - return mElementId != ELEMENT_ALPHABET_MANUAL_SHIFTED; + return mElementId == ELEMENT_ALPHABET_MANUAL_SHIFTED; } public boolean isSymbolsKeyboard() { @@ -154,22 +157,30 @@ public class KeyboardId { public boolean navigateAction() { // Note: Turn off checking navigation flag to show TAB key for now. - boolean navigateAction = InputTypeCompatUtils.isWebInputType(mInputType); + boolean navigateAction = InputTypeCompatUtils.isWebInputType(mEditorInfo.inputType); // || EditorInfoCompatUtils.hasFlagNavigateNext(mImeOptions) // || EditorInfoCompatUtils.hasFlagNavigatePrevious(mImeOptions); return navigateAction; } public boolean passwordInput() { - return InputTypeCompatUtils.isPasswordInputType(mInputType) - || InputTypeCompatUtils.isVisiblePasswordInputType(mInputType); + final int inputType = mEditorInfo.inputType; + return InputTypeCompatUtils.isPasswordInputType(inputType) + || InputTypeCompatUtils.isVisiblePasswordInputType(inputType); + } + + public boolean isMultiLine() { + return (mEditorInfo.inputType & InputType.TYPE_TEXT_FLAG_MULTI_LINE) != 0; } public int imeAction() { - // We are interested only in {@link EditorInfo#IME_MASK_ACTION} enum value and - // {@link EditorInfo#IME_FLAG_NO_ENTER_ACTION}. - return mImeOptions & ( - EditorInfo.IME_MASK_ACTION | EditorInfo.IME_FLAG_NO_ENTER_ACTION); + if ((mEditorInfo.imeOptions & EditorInfo.IME_FLAG_NO_ENTER_ACTION) != 0) { + return EditorInfo.IME_ACTION_NONE; + } else if (mEditorInfo.actionLabel != null) { + return IME_ACTION_CUSTOM_LABEL; + } else { + return mEditorInfo.imeOptions & EditorInfo.IME_MASK_ACTION; + } } public boolean hasSettingsKey() { @@ -208,7 +219,7 @@ public class KeyboardId { mLocale, (mOrientation == 1 ? "port" : "land"), mWidth, modeName(mMode), - EditorInfoCompatUtils.imeOptionsName(imeAction()), + imeAction(), f2KeyModeName(f2KeyMode()), (mClobberSettingsKey ? " clobberSettingsKey" : ""), (navigateAction() ? " navigateAction" : ""), @@ -255,6 +266,11 @@ public class KeyboardId { } } + public static String actionName(int actionId) { + return (actionId == IME_ACTION_CUSTOM_LABEL) ? "actionCustomLabel" + : EditorInfoCompatUtils.imeActionName(actionId); + } + public static String f2KeyModeName(int f2KeyMode) { switch (f2KeyMode) { case F2KEY_MODE_NONE: return "none"; diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardSet.java index 82eaa1d17..f27170a89 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSet.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSet.java @@ -62,6 +62,8 @@ public class KeyboardSet { new HashMap<KeyboardId, SoftReference<Keyboard>>(); private static final KeysCache sKeysCache = new KeysCache(); + private static final EditorInfo EMPTY_EDITOR_INFO = new EditorInfo(); + public static class KeyboardSetException extends RuntimeException { public final KeyboardId mKeyboardId; public KeyboardSetException(Throwable cause, KeyboardId keyboardId) { @@ -94,8 +96,7 @@ public class KeyboardSet { static class Params { int mMode; - int mInputType; - int mImeOptions; + EditorInfo mEditorInfo; boolean mTouchPositionCorrectionEnabled; boolean mSettingsKeyEnabled; boolean mVoiceKeyEnabled; @@ -192,9 +193,8 @@ public class KeyboardSet { final boolean hasShortcutKey = params.mVoiceKeyEnabled && (isSymbols != params.mVoiceKeyOnMain); return new KeyboardId(keyboardSetElementId, params.mLocale, params.mOrientation, - params.mWidth, params.mMode, params.mInputType, params.mImeOptions, - params.mSettingsKeyEnabled, params.mNoSettingsKey, params.mVoiceKeyEnabled, - hasShortcutKey); + params.mWidth, params.mMode, params.mEditorInfo, params.mSettingsKeyEnabled, + params.mNoSettingsKey, params.mVoiceKeyEnabled, hasShortcutKey); } public static class Builder { @@ -213,10 +213,7 @@ public class KeyboardSet { final Params params = mParams; params.mMode = Utils.getKeyboardMode(editorInfo); - if (editorInfo != null) { - params.mInputType = editorInfo.inputType; - params.mImeOptions = editorInfo.imeOptions; - } + params.mEditorInfo = (editorInfo != null) ? editorInfo : EMPTY_EDITOR_INFO; params.mNoSettingsKey = Utils.inPrivateImeOptions( mPackageName, LatinIME.IME_OPTION_NO_SETTINGS_KEY, mEditorInfo); } @@ -232,7 +229,8 @@ public class KeyboardSet { boolean touchPositionCorrectionEnabled) { final boolean deprecatedForceAscii = Utils.inPrivateImeOptions( mPackageName, LatinIME.IME_OPTION_FORCE_ASCII, mEditorInfo); - final boolean forceAscii = EditorInfoCompatUtils.hasFlagForceAscii(mParams.mImeOptions) + final boolean forceAscii = EditorInfoCompatUtils.hasFlagForceAscii( + mParams.mEditorInfo.imeOptions) || deprecatedForceAscii; mParams.mLocale = (forceAscii && !asciiCapable) ? Locale.US : inputLocale; mParams.mTouchPositionCorrectionEnabled = touchPositionCorrectionEnabled; diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java index 870c7cb25..88a41579d 100644 --- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java @@ -92,13 +92,13 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke private final Drawable mAutoCorrectionSpacebarLedIcon; private static final int SPACE_LED_LENGTH_PERCENT = 80; - // Mini keyboard + // More keys keyboard private PopupWindow mMoreKeysWindow; private MoreKeysPanel mMoreKeysPanel; private int mMoreKeysPanelPointerTrackerId; private final WeakHashMap<Key, MoreKeysPanel> mMoreKeysPanelCache = new WeakHashMap<Key, MoreKeysPanel>(); - private final boolean mConfigShowMiniKeyboardAtTouchedPoint; + private final boolean mConfigShowMoreKeysKeyboardAtTouchedPoint; private final PointerTrackerParams mPointerTrackerParams; private final boolean mIsSpacebarTriggeringPopupByLongPress; @@ -137,7 +137,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke break; case MSG_LONGPRESS_KEY: if (tracker != null) { - keyboardView.openMiniKeyboardIfRequired(tracker.getKey(), tracker); + keyboardView.openMoreKeysKeyboardIfRequired(tracker.getKey(), tracker); } else { KeyboardSwitcher.getInstance().onLongPressTimeout(msg.arg1); } @@ -338,8 +338,8 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke R.styleable.LatinKeyboardView_keyHysteresisDistance, 0); mKeyDetector = new KeyDetector(keyHysteresisDistance); mKeyTimerHandler = new KeyTimerHandler(this, keyTimerParams); - mConfigShowMiniKeyboardAtTouchedPoint = a.getBoolean( - R.styleable.LatinKeyboardView_showMiniKeyboardAtTouchedPoint, false); + mConfigShowMoreKeysKeyboardAtTouchedPoint = a.getBoolean( + R.styleable.LatinKeyboardView_showMoreKeysKeyboardAtTouchedPoint, false); a.recycle(); PointerTracker.setParameters(mPointerTrackerParams); @@ -435,7 +435,7 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke super.cancelAllMessages(); } - private boolean openMiniKeyboardIfRequired(Key parentKey, PointerTracker tracker) { + private boolean openMoreKeysKeyboardIfRequired(Key parentKey, PointerTracker tracker) { // Check if we have a popup layout specified first. if (mMoreKeysLayout == 0) { return false; @@ -458,19 +458,19 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke if (container == null) throw new NullPointerException(); - final MiniKeyboardView miniKeyboardView = - (MiniKeyboardView)container.findViewById(R.id.mini_keyboard_view); + final MoreKeysKeyboardView moreKeysKeyboardView = + (MoreKeysKeyboardView)container.findViewById(R.id.more_keys_keyboard_view); final Keyboard parentKeyboard = getKeyboard(); - final Keyboard miniKeyboard = new MiniKeyboard.Builder( + final Keyboard moreKeysKeyboard = new MoreKeysKeyboard.Builder( this, parentKeyboard.mMoreKeysTemplate, parentKey, parentKeyboard).build(); - miniKeyboardView.setKeyboard(miniKeyboard); + moreKeysKeyboardView.setKeyboard(moreKeysKeyboard); container.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); - return miniKeyboardView; + return moreKeysKeyboardView; } /** - * Called when a key is long pressed. By default this will open mini keyboard associated + * Called when a key is long pressed. By default this will open more keys keyboard associated * with this key. * @param parentKey the key that was long pressed * @param tracker the pointer tracker which pressed the parent key @@ -524,13 +524,13 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke if (mMoreKeysWindow == null) { mMoreKeysWindow = new PopupWindow(getContext()); mMoreKeysWindow.setBackgroundDrawable(null); - mMoreKeysWindow.setAnimationStyle(R.style.MiniKeyboardAnimation); + mMoreKeysWindow.setAnimationStyle(R.style.MoreKeysKeyboardAnimation); } mMoreKeysPanel = moreKeysPanel; mMoreKeysPanelPointerTrackerId = tracker.mPointerId; final Keyboard keyboard = getKeyboard(); - final int pointX = (mConfigShowMiniKeyboardAtTouchedPoint) ? tracker.getLastX() + final int pointX = (mConfigShowMoreKeysKeyboardAtTouchedPoint) ? tracker.getLastX() : parentKey.mX + parentKey.mWidth / 2; final int pointY = parentKey.mY - keyboard.mVerticalGap; moreKeysPanel.showMoreKeysPanel( diff --git a/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java index 4648da1c1..83155f719 100644 --- a/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java +++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java @@ -21,10 +21,10 @@ import android.graphics.Paint; import com.android.inputmethod.keyboard.internal.KeySpecParser; import com.android.inputmethod.latin.R; -public class MiniKeyboard extends Keyboard { +public class MoreKeysKeyboard extends Keyboard { private final int mDefaultKeyCoordX; - private MiniKeyboard(Builder.MiniKeyboardParams params) { + private MoreKeysKeyboard(Builder.MoreKeysKeyboardParams params) { super(params); mDefaultKeyCoordX = params.getDefaultKeyCoordX() + params.mDefaultKeyWidth / 2; } @@ -33,21 +33,21 @@ public class MiniKeyboard extends Keyboard { return mDefaultKeyCoordX; } - public static class Builder extends Keyboard.Builder<Builder.MiniKeyboardParams> { + public static class Builder extends Keyboard.Builder<Builder.MoreKeysKeyboardParams> { private final String[] mMoreKeys; - public static class MiniKeyboardParams extends Keyboard.Params { + public static class MoreKeysKeyboardParams extends Keyboard.Params { /* package */int mTopRowAdjustment; public int mNumRows; public int mNumColumns; public int mLeftKeys; public int mRightKeys; // includes default key. - public MiniKeyboardParams() { + public MoreKeysKeyboardParams() { super(); } - /* package for test */MiniKeyboardParams(int numKeys, int maxColumns, int keyWidth, + /* package for test */MoreKeysKeyboardParams(int numKeys, int maxColumns, int keyWidth, int rowHeight, int coordXInParent, int parentKeyboardWidth) { super(); setParameters(numKeys, maxColumns, keyWidth, rowHeight, coordXInParent, @@ -55,21 +55,21 @@ public class MiniKeyboard extends Keyboard { } /** - * Set keyboard parameters of mini keyboard. + * Set keyboard parameters of more keys keyboard. * - * @param numKeys number of keys in this mini keyboard. - * @param maxColumns number of maximum columns of this mini keyboard. - * @param keyWidth mini keyboard key width in pixel, including horizontal gap. - * @param rowHeight mini keyboard row height in pixel, including vertical gap. - * @param coordXInParent coordinate x of the popup key in parent keyboard. + * @param numKeys number of keys in this more keys keyboard. + * @param maxColumns number of maximum columns of this more keys keyboard. + * @param keyWidth more keys keyboard key width in pixel, including horizontal gap. + * @param rowHeight more keys keyboard row height in pixel, including vertical gap. + * @param coordXInParent coordinate x of the key preview in parent keyboard. * @param parentKeyboardWidth parent keyboard width in pixel. */ public void setParameters(int numKeys, int maxColumns, int keyWidth, int rowHeight, int coordXInParent, int parentKeyboardWidth) { if (parentKeyboardWidth / keyWidth < maxColumns) { throw new IllegalArgumentException( - "Keyboard is too small to hold mini keyboard: " + parentKeyboardWidth - + " " + keyWidth + " " + maxColumns); + "Keyboard is too small to hold more keys keyboard: " + + parentKeyboardWidth + " " + keyWidth + " " + maxColumns); } mDefaultKeyWidth = keyWidth; mDefaultRowHeight = rowHeight; @@ -95,14 +95,14 @@ public class MiniKeyboard extends Keyboard { leftKeys = numLeftKeys; rightKeys = numRightKeys; } - // Shift right if the left edge of mini keyboard is on the edge of parent keyboard - // unless the parent key is on the left edge. + // Shift right if the left edge of more keys keyboard is on the edge of parent + // keyboard unless the parent key is on the left edge. if (leftKeys * keyWidth >= coordXInParent && leftKeys > 0) { leftKeys--; rightKeys++; } - // Shift left if the right edge of mini keyboard is on the edge of parent keyboard - // unless the parent key is on the right edge. + // Shift left if the right edge of more keys keyboard is on the edge of parent + // keyboard unless the parent key is on the right edge. if (rightKeys * keyWidth + coordXInParent >= parentKeyboardWidth && rightKeys > 1) { leftKeys++; rightKeys--; @@ -204,10 +204,10 @@ public class MiniKeyboard extends Keyboard { } public Builder(KeyboardView view, int xmlId, Key parentKey, Keyboard parentKeyboard) { - super(view.getContext(), new MiniKeyboardParams()); + super(view.getContext(), new MoreKeysKeyboardParams()); load(xmlId, parentKeyboard.mId); - // TODO: Mini keyboard's vertical gap is currently calculated heuristically. + // TODO: More keys keyboard's vertical gap is currently calculated heuristically. // Should revise the algorithm. mParams.mVerticalGap = parentKeyboard.mVerticalGap / 2; mMoreKeys = parentKey.mMoreKeys; @@ -215,8 +215,9 @@ public class MiniKeyboard extends Keyboard { final int previewWidth = view.mKeyPreviewDrawParams.mPreviewBackgroundWidth; final int previewHeight = view.mKeyPreviewDrawParams.mPreviewBackgroundHeight; final int width, height; - // Use pre-computed width and height if these values are available and mini keyboard - // has only one key to mitigate visual flicker between key preview and mini keyboard. + // Use pre-computed width and height if these values are available and more keys + // keyboard has only one key to mitigate visual flicker between key preview and more + // keys keyboard. if (view.isKeyPreviewPopupEnabled() && mMoreKeys.length == 1 && previewWidth > 0 && previewHeight > 0) { width = previewWidth; @@ -231,7 +232,7 @@ public class MiniKeyboard extends Keyboard { private static int getMaxKeyWidth(KeyboardView view, String[] moreKeys, int minKeyWidth) { final int padding = (int) view.getContext().getResources() - .getDimension(R.dimen.mini_keyboard_key_horizontal_padding); + .getDimension(R.dimen.more_keys_keyboard_key_horizontal_padding); Paint paint = null; int maxWidth = minKeyWidth; for (String moreKeySpec : moreKeys) { @@ -252,8 +253,8 @@ public class MiniKeyboard extends Keyboard { } @Override - public MiniKeyboard build() { - final MiniKeyboardParams params = mParams; + public MoreKeysKeyboard build() { + final MoreKeysKeyboardParams params = mParams; for (int n = 0; n < mMoreKeys.length; n++) { final String moreKeySpec = mMoreKeys[n]; final int row = n / params.mNumColumns; @@ -262,7 +263,7 @@ public class MiniKeyboard extends Keyboard { params.markAsEdgeKey(key, row); params.onAddKey(key); } - return new MiniKeyboard(params); + return new MoreKeysKeyboard(params); } } } diff --git a/java/src/com/android/inputmethod/keyboard/MiniKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java index 72d5b6889..b030dd95a 100644 --- a/java/src/com/android/inputmethod/keyboard/MiniKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java @@ -28,10 +28,10 @@ import com.android.inputmethod.keyboard.PointerTracker.TimerProxy; import com.android.inputmethod.latin.R; /** - * A view that renders a virtual {@link MiniKeyboard}. It handles rendering of keys and detecting - * key presses and touch movements. + * A view that renders a virtual {@link MoreKeysKeyboard}. It handles rendering of keys and + * detecting key presses and touch movements. */ -public class MiniKeyboardView extends KeyboardView implements MoreKeysPanel { +public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel { private final int[] mCoordinates = new int[2]; private final KeyDetector mKeyDetector; @@ -43,7 +43,7 @@ public class MiniKeyboardView extends KeyboardView implements MoreKeysPanel { private static final TimerProxy EMPTY_TIMER_PROXY = new TimerProxy.Adapter(); - private final KeyboardActionListener mMiniKeyboardListener = + private final KeyboardActionListener mMoreKeysKeyboardListener = new KeyboardActionListener.Adapter() { @Override public void onCodeInput(int primaryCode, int[] keyCodes, int x, int y) { @@ -71,16 +71,16 @@ public class MiniKeyboardView extends KeyboardView implements MoreKeysPanel { } }; - public MiniKeyboardView(Context context, AttributeSet attrs) { - this(context, attrs, R.attr.miniKeyboardViewStyle); + public MoreKeysKeyboardView(Context context, AttributeSet attrs) { + this(context, attrs, R.attr.moreKeysKeyboardViewStyle); } - public MiniKeyboardView(Context context, AttributeSet attrs, int defStyle) { + public MoreKeysKeyboardView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); final Resources res = context.getResources(); mKeyDetector = new MoreKeysDetector( - res.getDimension(R.dimen.mini_keyboard_slide_allowance)); + res.getDimension(R.dimen.more_keys_keyboard_slide_allowance)); setKeyPreviewPopupEnabled(false, 0); } @@ -110,7 +110,7 @@ public class MiniKeyboardView extends KeyboardView implements MoreKeysPanel { @Override public KeyboardActionListener getKeyboardActionListener() { - return mMiniKeyboardListener; + return mMoreKeysKeyboardListener; } @Override @@ -125,7 +125,7 @@ public class MiniKeyboardView extends KeyboardView implements MoreKeysPanel { @Override public void setKeyPreviewPopupEnabled(boolean previewEnabled, int delay) { - // Mini keyboard needs no pop-up key preview displayed, so we pass always false with a + // More keys keyboard needs no pop-up key preview displayed, so we pass always false with a // delay of 0. The delay does not matter actually since the popup is not shown anyway. super.setKeyPreviewPopupEnabled(false, 0); } @@ -136,13 +136,13 @@ public class MiniKeyboardView extends KeyboardView implements MoreKeysPanel { mController = controller; mListener = listener; final View container = (View)getParent(); - final MiniKeyboard miniKeyboard = (MiniKeyboard)getKeyboard(); + final MoreKeysKeyboard moreKeysKeyboard = (MoreKeysKeyboard)getKeyboard(); parentView.getLocationInWindow(mCoordinates); - final int miniKeyboardLeft = pointX - miniKeyboard.getDefaultCoordX() + final int moreKeysKeyboardLeft = pointX - moreKeysKeyboard.getDefaultCoordX() + parentView.getPaddingLeft(); - final int x = wrapUp(Math.max(0, Math.min(miniKeyboardLeft, - parentView.getWidth() - miniKeyboard.mOccupiedWidth)) + final int x = wrapUp(Math.max(0, Math.min(moreKeysKeyboardLeft, + parentView.getWidth() - moreKeysKeyboard.mOccupiedWidth)) - container.getPaddingLeft() + mCoordinates[0], container.getMeasuredWidth(), 0, parentView.getWidth()); final int y = pointY diff --git a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java index 2d1a0083d..41e7ef435 100644 --- a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java +++ b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java @@ -59,7 +59,7 @@ public class ProximityInfo { mKeyHeight = keyHeight; mGridNeighbors = new Key[mGridSize][]; if (minWidth == 0 || height == 0) { - // No proximity required. Keyboard might be mini keyboard. + // No proximity required. Keyboard might be more keys keyboard. return; } computeNearestNeighbors(keyWidth, keys, touchPositionCorrection, additionalProximityChars); diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java index e3c5da456..1626a140b 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java @@ -133,18 +133,28 @@ public class KeySpecParser { return label; } + private static String getOutputTextInternal(String moreKeySpec) { + final int end = indexOfLabelEnd(moreKeySpec, 0); + if (end <= 0) { + return null; + } + if (indexOfLabelEnd(moreKeySpec, end + 1) >= 0) { + throw new KeySpecParserError("Multiple " + LABEL_END + ": " + moreKeySpec); + } + return parseEscape(moreKeySpec.substring(end + /* LABEL_END */1)); + } + public static String getOutputText(String moreKeySpec) { if (hasCode(moreKeySpec)) { return null; } - final int end = indexOfLabelEnd(moreKeySpec, 0); - if (end > 0) { - if (indexOfLabelEnd(moreKeySpec, end + 1) >= 0) { - throw new KeySpecParserError("Multiple " + LABEL_END + ": " - + moreKeySpec); + final String outputText = getOutputTextInternal(moreKeySpec); + if (outputText != null) { + if (Utils.codePointCount(outputText) == 1) { + // If output text is one code point, it should be treated as a code. + // See {@link #getCode(Resources, String)}. + return null; } - final String outputText = parseEscape( - moreKeySpec.substring(end + /* LABEL_END */1)); if (!TextUtils.isEmpty(outputText)) { return outputText; } @@ -170,7 +180,13 @@ public class KeySpecParser { final int code = res.getInteger(resId); return code; } - if (indexOfLabelEnd(moreKeySpec, 0) > 0) { + final String outputText = getOutputTextInternal(moreKeySpec); + if (outputText != null) { + // If output text is one code point, it should be treated as a code. + // See {@link #getOutputText(String)}. + if (Utils.codePointCount(outputText) == 1) { + return outputText.codePointAt(0); + } return Keyboard.CODE_OUTPUT_TEXT; } final String label = getLabel(moreKeySpec); diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java index 6ec56ca9f..12a9c51f2 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java @@ -40,17 +40,13 @@ public class KeyStyles { public String[] getStringArray(TypedArray a, int index); public String getString(TypedArray a, int index); public int getInt(TypedArray a, int index, int defaultValue); - public int getFlag(TypedArray a, int index, int defaultValue); + public int getFlag(TypedArray a, int index); } - private static class EmptyKeyStyle implements KeyStyle { - EmptyKeyStyle() { - // Nothing to do. - } - + static class EmptyKeyStyle implements KeyStyle { @Override public String[] getStringArray(TypedArray a, int index) { - return parseStringArray(a, index); + return KeyStyles.parseStringArray(a, index); } @Override @@ -64,50 +60,47 @@ public class KeyStyles { } @Override - public int getFlag(TypedArray a, int index, int defaultValue) { - return a.getInt(index, defaultValue); - } - - protected static String[] parseStringArray(TypedArray a, int index) { - if (!a.hasValue(index)) - return null; - return KeySpecParser.parseCsvString( - a.getString(index), a.getResources(), R.string.english_ime_name); + public int getFlag(TypedArray a, int index) { + return a.getInt(index, 0); } } - private static class DeclaredKeyStyle extends EmptyKeyStyle { - private final HashMap<Integer, Object> mAttributes = new HashMap<Integer, Object>(); + static class DeclaredKeyStyle implements KeyStyle { + private final HashMap<Integer, Object> mStyleAttributes = new HashMap<Integer, Object>(); @Override public String[] getStringArray(TypedArray a, int index) { - return a.hasValue(index) - ? super.getStringArray(a, index) : (String[])mAttributes.get(index); + if (a.hasValue(index)) { + return parseStringArray(a, index); + } + return (String[])mStyleAttributes.get(index); } @Override public String getString(TypedArray a, int index) { - return a.hasValue(index) - ? super.getString(a, index) : (String)mAttributes.get(index); + if (a.hasValue(index)) { + return a.getString(index); + } + return (String)mStyleAttributes.get(index); } @Override public int getInt(TypedArray a, int index, int defaultValue) { - final Integer value = (Integer)mAttributes.get(index); - return super.getInt(a, index, (value != null) ? value : defaultValue); + if (a.hasValue(index)) { + return a.getInt(index, defaultValue); + } + final Integer styleValue = (Integer)mStyleAttributes.get(index); + return styleValue != null ? styleValue : defaultValue; } @Override - public int getFlag(TypedArray a, int index, int defaultValue) { - final Integer value = (Integer)mAttributes.get(index); - return super.getFlag(a, index, defaultValue) | (value != null ? value : 0); - } - - DeclaredKeyStyle() { - super(); + public int getFlag(TypedArray a, int index) { + final int value = a.getInt(index, 0); + final Integer styleValue = (Integer)mStyleAttributes.get(index); + return (styleValue != null ? styleValue : 0) | value; } - void parseKeyStyleAttributes(TypedArray keyAttr) { + void readKeyAttributes(TypedArray keyAttr) { // TODO: Currently not all Key attributes can be declared as style. readInt(keyAttr, R.styleable.Keyboard_Key_code); readInt(keyAttr, R.styleable.Keyboard_Key_altCode); @@ -126,52 +119,68 @@ public class KeyStyles { } private void readString(TypedArray a, int index) { - if (a.hasValue(index)) - mAttributes.put(index, a.getString(index)); + if (a.hasValue(index)) { + mStyleAttributes.put(index, a.getString(index)); + } } private void readInt(TypedArray a, int index) { - if (a.hasValue(index)) - mAttributes.put(index, a.getInt(index, 0)); + if (a.hasValue(index)) { + mStyleAttributes.put(index, a.getInt(index, 0)); + } } private void readFlag(TypedArray a, int index) { - final Integer value = (Integer)mAttributes.get(index); - if (a.hasValue(index)) - mAttributes.put(index, a.getInt(index, 0) | (value != null ? value : 0)); + final Integer value = (Integer)mStyleAttributes.get(index); + if (a.hasValue(index)) { + mStyleAttributes.put(index, a.getInt(index, 0) | (value != null ? value : 0)); + } } private void readStringArray(TypedArray a, int index) { final String[] value = parseStringArray(a, index); - if (value != null) - mAttributes.put(index, value); + if (value != null) { + mStyleAttributes.put(index, value); + } + } + + void addParentStyleAttributes(DeclaredKeyStyle parentStyle) { + mStyleAttributes.putAll(parentStyle.mStyleAttributes); } + } - void addParent(DeclaredKeyStyle parentStyle) { - mAttributes.putAll(parentStyle.mAttributes); + static String[] parseStringArray(TypedArray a, int index) { + if (a.hasValue(index)) { + return KeySpecParser.parseCsvString( + a.getString(index), a.getResources(), R.string.english_ime_name); } + return null; } public void parseKeyStyleAttributes(TypedArray keyStyleAttr, TypedArray keyAttrs, XmlPullParser parser) throws XmlPullParserException { final String styleName = keyStyleAttr.getString(R.styleable.Keyboard_KeyStyle_styleName); - if (DEBUG) Log.d(TAG, String.format("<%s styleName=%s />", - Keyboard.Builder.TAG_KEY_STYLE, styleName)); - if (mStyles.containsKey(styleName)) + if (DEBUG) { + Log.d(TAG, String.format("<%s styleName=%s />", + Keyboard.Builder.TAG_KEY_STYLE, styleName)); + } + if (mStyles.containsKey(styleName)) { throw new XmlParseUtils.ParseException( "duplicate key style declared: " + styleName, parser); + } final DeclaredKeyStyle style = new DeclaredKeyStyle(); if (keyStyleAttr.hasValue(R.styleable.Keyboard_KeyStyle_parentStyle)) { final String parentStyle = keyStyleAttr.getString( R.styleable.Keyboard_KeyStyle_parentStyle); final DeclaredKeyStyle parent = mStyles.get(parentStyle); - if (parent == null) + if (parent == null) { throw new XmlParseUtils.ParseException( "Unknown parentStyle " + parentStyle, parser); - style.addParent(parent); + } + style.addParentStyleAttributes(parent); } - style.parseKeyStyleAttributes(keyAttrs); + style.readKeyAttributes(keyAttrs); mStyles.put(styleName, style); } diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 8aaa0692f..1cb79e707 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -187,7 +187,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar private View mKeyPreviewBackingView; private View mSuggestionsContainer; private SuggestionsView mSuggestionsView; - private Suggest mSuggest; + /* package for tests */ Suggest mSuggest; private CompletionInfo[] mApplicationSpecifiedCompletions; private InputMethodManagerCompatWrapper mImm; @@ -1225,12 +1225,41 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar return mOptionsDialog != null && mOptionsDialog.isShowing(); } - private void insertPunctuationFromSuggestionStrip(final InputConnection ic, final int code) { + private void insertPunctuationFromSuggestionStrip(final int code) { onCodeInput(code, new int[] { code }, KeyboardActionListener.SUGGESTION_STRIP_COORDINATE, KeyboardActionListener.SUGGESTION_STRIP_COORDINATE); } + private static int getEditorActionId(EditorInfo editorInfo) { + if (editorInfo == null) return 0; + return (editorInfo.actionLabel != null) + ? editorInfo.actionId + : (editorInfo.imeOptions & EditorInfo.IME_MASK_ACTION); + } + + private void performeEditorAction(int actionId) { + final InputConnection ic = getCurrentInputConnection(); + if (ic != null) { + ic.performEditorAction(actionId); + } + } + + private void sendKeyCodePoint(int code) { + // TODO: Remove this special handling of digit letters. + // For backward compatibility. See {@link InputMethodService#sendKeyChar(char)}. + if (code >= '0' && code <= '9') { + super.sendKeyChar((char)code); + return; + } + + final InputConnection ic = getCurrentInputConnection(); + if (ic != null) { + final String text = new String(new int[] { code }, 0, 1); + ic.commitText(text, text.length()); + } + } + // Implementation of {@link KeyboardActionListener}. @Override public void onCodeInput(int primaryCode, int[] keyCodes, int x, int y) { @@ -1271,6 +1300,9 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar case Keyboard.CODE_SHORTCUT: mSubtypeSwitcher.switchToShortcutIME(); break; + case Keyboard.CODE_ACTION_ENTER: + performeEditorAction(getEditorActionId(getCurrentInputEditorInfo())); + break; case Keyboard.CODE_TAB: handleTab(); // There are two cases for tab. Either we send a "next" event, that may change the @@ -1308,7 +1340,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar commitTyped(ic); text = specificTldProcessingOnTextInput(ic, text); if (SPACE_STATE_PHANTOM == mSpaceState) { - sendKeyChar((char)Keyboard.CODE_SPACE); + sendKeyCodePoint(Keyboard.CODE_SPACE); } ic.commitText(text, 1); ic.endBatchEdit(); @@ -1327,10 +1359,12 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar // Not a tld: do nothing. return text; } + // We have a TLD (or something that looks like this): make sure we don't add + // a space even if currently in phantom mode. + mSpaceState = SPACE_STATE_NONE; 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; @@ -1399,7 +1433,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } if (SPACE_STATE_DOUBLE == spaceState) { - if (revertDoubleSpace(ic)) { + if (revertDoubleSpaceWhileInBatchEdit(ic)) { // No need to reset mSpaceState, it has already be done (that's why we // receive it as a parameter) return; @@ -1453,10 +1487,12 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } } + // TODO: Implement next and previous actions using other key code than tab's code. private void handleTab() { final int imeOptions = getCurrentInputEditorInfo().imeOptions; if (!EditorInfoCompatUtils.hasFlagNavigateNext(imeOptions) && !EditorInfoCompatUtils.hasFlagNavigatePrevious(imeOptions)) { + // TODO: This should be {@link #sendKeyCodePoint(int)}. sendDownUpKeyEvents(KeyEvent.KEYCODE_TAB); return; } @@ -1475,6 +1511,28 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } } + // ic may be null + private boolean maybeStripSpaceWhileInBatchEdit(final InputConnection ic, final int code, + final int spaceState, final boolean isFromSuggestionStrip) { + if (Keyboard.CODE_ENTER == code && SPACE_STATE_SWAP_PUNCTUATION == spaceState) { + removeTrailingSpaceWhileInBatchEdit(ic); + return false; + } else if ((SPACE_STATE_WEAK == spaceState + || SPACE_STATE_SWAP_PUNCTUATION == spaceState) + && isFromSuggestionStrip) { + if (mSettingsValues.isMagicSpaceSwapper(code)) { + return true; + } else { + if (mSettingsValues.isMagicSpaceStripper(code)) { + removeTrailingSpaceWhileInBatchEdit(ic); + } + return false; + } + } else { + return false; + } + } + private void handleCharacter(final int primaryCode, final int[] keyCodes, final int x, final int y, final int spaceState) { mVoiceProxy.handleCharacter(); @@ -1489,7 +1547,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar private void handleCharacterWhileInBatchEdit(final int primaryCode, final int[] keyCodes, final int x, final int y, final int spaceState, final InputConnection ic) { boolean isComposingWord = mWordComposer.isComposingWord(); - int code = primaryCode; if (SPACE_STATE_PHANTOM == spaceState && !mSettingsValues.isSymbolExcludedFromWordSeparators(primaryCode)) { @@ -1497,17 +1554,18 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar // Sanity check throw new RuntimeException("Should not be composing here"); } - sendKeyChar((char)Keyboard.CODE_SPACE); + sendKeyCodePoint(Keyboard.CODE_SPACE); } - if ((isAlphabet(code) || mSettingsValues.isSymbolExcludedFromWordSeparators(code)) + if ((isAlphabet(primaryCode) + || mSettingsValues.isSymbolExcludedFromWordSeparators(primaryCode)) && isSuggestionsRequested() && !isCursorTouchingWord()) { if (!isComposingWord) { // Reset entirely the composing state anyway, then start composing a new word unless // the character is a single quote. The idea here is, single quote is not a // separator and it should be treated as a normal character, except in the first // position where it should not start composing a word. - isComposingWord = (Keyboard.CODE_SINGLE_QUOTE != code); + isComposingWord = (Keyboard.CODE_SINGLE_QUOTE != primaryCode); // Here we don't need to reset the last composed word. It will be reset // when we commit this one, if we ever do; if on the other hand we backspace // it entirely and resume suggestions on the previous word, we'd like to still @@ -1518,7 +1576,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } } if (isComposingWord) { - mWordComposer.add(code, keyCodes, x, y); + mWordComposer.add(primaryCode, keyCodes, x, y); if (ic != null) { // If it's the first letter, make note of auto-caps state if (mWordComposer.size() == 1) { @@ -1529,14 +1587,17 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } mHandler.postUpdateSuggestions(); } else { - sendKeyChar((char)code); - } + final boolean swapWeakSpace = maybeStripSpaceWhileInBatchEdit(ic, primaryCode, + spaceState, KeyboardActionListener.SUGGESTION_STRIP_COORDINATE == x); - if (mSettingsValues.isWordSeparator(code)) { - Utils.Stats.onSeparator((char)code, x, y); - } else { - Utils.Stats.onNonSeparator((char)code, x, y); + sendKeyCodePoint(primaryCode); + + if (swapWeakSpace) { + swapSwapperAndSpaceWhileInBatchEdit(ic); + mSpaceState = SPACE_STATE_WEAK; + } } + Utils.Stats.onNonSeparator((char)primaryCode, x, y); } // Returns true if we did an autocorrection, false otherwise. @@ -1572,29 +1633,10 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } } - final boolean swapWeakSpace; - if (Keyboard.CODE_ENTER == primaryCode && SPACE_STATE_SWAP_PUNCTUATION == spaceState) { - removeTrailingSpaceWhileInBatchEdit(ic); - swapWeakSpace = false; - } else if ((SPACE_STATE_WEAK == spaceState || SPACE_STATE_SWAP_PUNCTUATION == spaceState) - && KeyboardActionListener.SUGGESTION_STRIP_COORDINATE == x) { - if (mSettingsValues.isMagicSpaceSwapper(primaryCode)) { - swapWeakSpace = true; - } else { - swapWeakSpace = false; - if (mSettingsValues.isMagicSpaceStripper(primaryCode)) { - removeTrailingSpaceWhileInBatchEdit(ic); - } - } - } else { - swapWeakSpace = false; - } + final boolean swapWeakSpace = maybeStripSpaceWhileInBatchEdit(ic, primaryCode, spaceState, + KeyboardActionListener.SUGGESTION_STRIP_COORDINATE == x); - // 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); + sendKeyCodePoint(primaryCode); if (Keyboard.CODE_SPACE == primaryCode) { if (isSuggestionsRequested()) { @@ -1613,7 +1655,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } else { if (swapWeakSpace) { swapSwapperAndSpaceWhileInBatchEdit(ic); - mSpaceState = SPACE_STATE_WEAK; + mSpaceState = SPACE_STATE_SWAP_PUNCTUATION; } 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 @@ -1869,52 +1911,40 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } @Override - public void pickSuggestionManually(int index, CharSequence suggestion) { + public void pickSuggestionManually(final int index, final CharSequence suggestion) { mComposingStateManager.onFinishComposingText(); - SuggestedWords suggestions = mSuggestionsView.getSuggestions(); + final SuggestedWords suggestions = mSuggestionsView.getSuggestions(); mVoiceProxy.flushAndLogAllTextModificationCounters(index, suggestion, mSettingsValues.mWordSeparators); - final InputConnection ic = getCurrentInputConnection(); - if (ic != null) { - ic.beginBatchEdit(); - } if (mInputAttributes.mApplicationSpecifiedCompletionOn && mApplicationSpecifiedCompletions != null && index >= 0 && index < mApplicationSpecifiedCompletions.length) { - if (ic != null) { - final CompletionInfo completionInfo = mApplicationSpecifiedCompletions[index]; - ic.commitCompletion(completionInfo); - } if (mSuggestionsView != null) { mSuggestionsView.clear(); } mKeyboardSwitcher.updateShiftState(); + final InputConnection ic = getCurrentInputConnection(); if (ic != null) { + ic.beginBatchEdit(); + final CompletionInfo completionInfo = mApplicationSpecifiedCompletions[index]; + ic.commitCompletion(completionInfo); ic.endBatchEdit(); } return; } - // If this is a punctuation, apply it through the normal key press - if (suggestion.length() == 1 && (mSettingsValues.isWordSeparator(suggestion.charAt(0)) - || mSettingsValues.isSuggestedPunctuation(suggestion.charAt(0)))) { + // If this is a punctuation picked from the suggestion strip, pass it to onCodeInput + if (suggestion.length() == 1 && isShowingPunctuationList()) { // Word separators are suggested before the user inputs something. // So, LatinImeLogger logs "" as a user's input. LatinImeLogger.logOnManualSuggestion( "", suggestion.toString(), index, suggestions.mWords); - final CharSequence outputText = mSettingsValues.mSuggestPuncOutputTextList - .getWord(index); - final int primaryCode = outputText.charAt(0); - // Find out whether the previous character is a space. If it is, as a special case - // for punctuation entered through the suggestion strip, it should be swapped - // if it was a magic or a weak space. This is meant to help in case the user - // pressed space on purpose of displaying the suggestion strip punctuation. - insertPunctuationFromSuggestionStrip(ic, primaryCode); - // TODO: the following endBatchEdit seems useless, check - if (ic != null) { - ic.endBatchEdit(); - } + // Rely on onCodeInput to do the complicated swapping/stripping logic consistently. + final int primaryCode = suggestion.charAt(0); + onCodeInput(primaryCode, new int[] { primaryCode }, + KeyboardActionListener.SUGGESTION_STRIP_COORDINATE, + KeyboardActionListener.SUGGESTION_STRIP_COORDINATE); return; } // We need to log before we commit, because the word composer will store away the user @@ -1967,9 +1997,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar mHandler.postUpdateSuggestions(); } } - if (ic != null) { - ic.endBatchEdit(); - } } /** @@ -2081,21 +2108,20 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar CharSequence toLeft = ic.getTextBeforeCursor(1, 0); CharSequence toRight = ic.getTextAfterCursor(1, 0); if (!TextUtils.isEmpty(toLeft) - && !mSettingsValues.isWordSeparator(toLeft.charAt(0)) - && !mSettingsValues.isSuggestedPunctuation(toLeft.charAt(0))) { + && !mSettingsValues.isWordSeparator(toLeft.charAt(0))) { return true; } if (!TextUtils.isEmpty(toRight) - && !mSettingsValues.isWordSeparator(toRight.charAt(0)) - && !mSettingsValues.isSuggestedPunctuation(toRight.charAt(0))) { + && !mSettingsValues.isWordSeparator(toRight.charAt(0))) { return true; } return false; } // "ic" must not be null - private static boolean sameAsTextBeforeCursor(final InputConnection ic, CharSequence text) { - CharSequence beforeText = ic.getTextBeforeCursor(text.length(), 0); + private static boolean sameAsTextBeforeCursor(final InputConnection ic, + final CharSequence text) { + final CharSequence beforeText = ic.getTextBeforeCursor(text.length(), 0); return TextUtils.equals(text, beforeText); } @@ -2212,13 +2238,14 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } } ic.deleteSurroundingText(restartLength, 0); + mComposingStateManager.onStartComposingText(); ic.setComposingText(mWordComposer.getTypedWord(), 1); mHandler.cancelUpdateBigramPredictions(); mHandler.postUpdateSuggestions(); } // "ic" must not be null - private boolean revertDoubleSpace(final InputConnection ic) { + private boolean revertDoubleSpaceWhileInBatchEdit(final InputConnection ic) { mHandler.cancelDoubleSpacesTimer(); // Here we test whether we indeed have a period and a space before us. This should not // be needed, but it's there just in case something went wrong. @@ -2231,10 +2258,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar + "\". \" just before the cursor."); return false; } - ic.beginBatchEdit(); ic.deleteSurroundingText(2, 0); ic.commitText(" ", 1); - ic.endBatchEdit(); return true; } diff --git a/java/src/com/android/inputmethod/latin/SettingsValues.java b/java/src/com/android/inputmethod/latin/SettingsValues.java index 589cb6f86..6b652313c 100644 --- a/java/src/com/android/inputmethod/latin/SettingsValues.java +++ b/java/src/com/android/inputmethod/latin/SettingsValues.java @@ -222,10 +222,6 @@ public class SettingsValues { res.getBoolean(R.bool.config_default_vibration_enabled)); } - public boolean isSuggestedPunctuation(int code) { - return mSuggestPuncs.contains(String.valueOf((char)code)); - } - public boolean isWordSeparator(int code) { return mWordSeparators.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 f418968b5..a1a329a8d 100644 --- a/java/src/com/android/inputmethod/latin/WordComposer.java +++ b/java/src/com/android/inputmethod/latin/WordComposer.java @@ -90,11 +90,11 @@ public class WordComposer { * @return the number of keystrokes */ public final int size() { - return mTypedWord.length(); + return mCodes.size(); } public final boolean isComposingWord() { - return size() > 0; + return mCodes.size() > 0; } /** @@ -125,8 +125,8 @@ public class WordComposer { * @param codes the array of unicode values */ public void add(int primaryCode, int[] codes, int x, int y) { - final int newIndex = size(); - mTypedWord.append((char) primaryCode); + final int newIndex = mCodes.size(); + mTypedWord.appendCodePoint(primaryCode); correctPrimaryJuxtapos(primaryCode, codes); mCodes.add(codes); if (newIndex < BinaryDictionary.MAX_WORD_LENGTH) { @@ -171,8 +171,8 @@ public class WordComposer { final KeyDetector keyDetector) { reset(); final int length = word.length(); - for (int i = 0; i < length; ++i) { - int codePoint = word.charAt(i); + for (int i = 0; i < length; i = Character.offsetByCodePoints(word, i, 1)) { + int codePoint = Character.codePointAt(word, i); addKeyInfo(codePoint, keyboard, keyDetector); } } @@ -207,16 +207,25 @@ public class WordComposer { * Delete the last keystroke as a result of hitting backspace. */ public void deleteLast() { - final int size = size(); + final int size = mCodes.size(); if (size > 0) { - 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); + mCodes.remove(size - 1); + // Note: mTypedWord.length() and mCodes.length differ when there are surrogate pairs + final int stringBuilderLength = mTypedWord.length(); + if (stringBuilderLength < size) { + throw new RuntimeException( + "In WordComposer: mCodes and mTypedWords have non-matching lengths"); + } + final int lastChar = mTypedWord.codePointBefore(stringBuilderLength); + if (Character.isSupplementaryCodePoint(lastChar)) { + mTypedWord.delete(stringBuilderLength - 2, stringBuilderLength); + } else { + mTypedWord.deleteCharAt(stringBuilderLength - 1); + } if (Character.isUpperCase(lastChar)) mCapsCount--; } - if (size() == 0) { + // We may have deleted the last one. + if (0 == mCodes.size()) { mIsFirstCharCapitalized = false; } if (mTrailingSingleQuotesCount > 0) { |