diff options
Diffstat (limited to 'java/src/com/android')
17 files changed, 476 insertions, 286 deletions
diff --git a/java/src/com/android/inputmethod/deprecated/languageswitcher/InputLanguageSelection.java b/java/src/com/android/inputmethod/deprecated/languageswitcher/InputLanguageSelection.java index dbe7aec6a..e75e14861 100644 --- a/java/src/com/android/inputmethod/deprecated/languageswitcher/InputLanguageSelection.java +++ b/java/src/com/android/inputmethod/deprecated/languageswitcher/InputLanguageSelection.java @@ -17,7 +17,7 @@ package com.android.inputmethod.deprecated.languageswitcher; import com.android.inputmethod.compat.SharedPreferencesCompat; -import com.android.inputmethod.keyboard.internal.KeyboardBuilder; +import com.android.inputmethod.keyboard.KeyboardSet; import com.android.inputmethod.latin.DictionaryFactory; import com.android.inputmethod.latin.LocaleUtils; import com.android.inputmethod.latin.R; @@ -162,8 +162,8 @@ public class InputLanguageSelection extends PreferenceActivity { try { final String localeStr = locale.toString(); - final String[] layoutCountryCodes = KeyboardBuilder.parseKeyboardLocale( - this, R.xml.kbd_qwerty).split(",", -1); + final String[] layoutCountryCodes = KeyboardSet.parseKeyboardLocale( + getResources(), R.xml.keyboard_set).split(",", -1); if (!TextUtils.isEmpty(localeStr) && layoutCountryCodes.length > 0) { for (String s : layoutCountryCodes) { if (s.equals(localeStr)) { diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java index ced763841..4e4ccef18 100644 --- a/java/src/com/android/inputmethod/keyboard/Key.java +++ b/java/src/com/android/inputmethod/keyboard/Key.java @@ -28,13 +28,14 @@ import android.util.Xml; import com.android.inputmethod.keyboard.internal.KeyStyles; import com.android.inputmethod.keyboard.internal.KeyStyles.KeyStyle; import com.android.inputmethod.keyboard.internal.KeyboardBuilder; -import com.android.inputmethod.keyboard.internal.KeyboardBuilder.ParseException; import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; import com.android.inputmethod.keyboard.internal.KeyboardParams; import com.android.inputmethod.keyboard.internal.MoreKeySpecParser; +import com.android.inputmethod.keyboard.internal.XmlParseUtils; import com.android.inputmethod.latin.R; import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; import java.util.HashMap; import java.util.Map; @@ -212,9 +213,10 @@ public class Key { * this key. * @param parser the XML parser containing the attributes for this key * @param keyStyles active key styles set + * @throws XmlPullParserException */ public Key(Resources res, KeyboardParams params, KeyboardBuilder.Row row, - XmlPullParser parser, KeyStyles keyStyles) { + XmlPullParser parser, KeyStyles keyStyles) throws XmlPullParserException { final float horizontalGap = isSpacer() ? 0 : params.mHorizontalGap; final int keyHeight = row.mRowHeight; mVerticalGap = params.mVerticalGap; @@ -228,7 +230,8 @@ public class Key { String styleName = keyAttr.getString(R.styleable.Keyboard_Key_keyStyle); style = keyStyles.getKeyStyle(styleName); if (style == null) - throw new ParseException("Unknown key style: " + styleName, parser); + throw new XmlParseUtils.ParseException( + "Unknown key style: " + styleName, parser); } else { style = KeyStyles.getEmptyKeyStyle(); } @@ -468,9 +471,9 @@ public class Key { * Detects if a point falls on this key. * @param x the x-coordinate of the point * @param y the y-coordinate of the point - * @return whether or not the point falls on the key. If the key is attached to an edge, it will - * assume that all points between the key and the edge are considered to be on the key. - * @see {@link #markAsLeftEdge(KeyboardParams)} etc. + * @return whether or not the point falls on the key. If the key is attached to an edge, it + * will assume that all points between the key and the edge are considered to be on the key. + * @see #markAsLeftEdge(KeyboardParams) etc. */ public boolean isOnKey(int x, int y) { return mHitBox.contains(x, y); @@ -569,7 +572,7 @@ public class Key { public static class Spacer extends Key { public Spacer(Resources res, KeyboardParams params, KeyboardBuilder.Row row, - XmlPullParser parser, KeyStyles keyStyles) { + XmlPullParser parser, KeyStyles keyStyles) throws XmlPullParserException { super(res, params, row, parser, keyStyles); } diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java index e267aa65c..814b52398 100644 --- a/java/src/com/android/inputmethod/keyboard/Keyboard.java +++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java @@ -151,6 +151,9 @@ public class Keyboard { } public Key getKey(int code) { + if (code == CODE_DUMMY) { + return null; + } final Integer keyCode = code; if (mKeyCache.containsKey(keyCode)) { return mKeyCache.get(keyCode); @@ -232,19 +235,6 @@ public class Keyboard { return mProximityInfo.getNearestKeys(x, y); } - public static String themeName(int themeId) { - // This should be aligned with theme-*.xml resource files' themeId attribute. - switch (themeId) { - case 0: return "Basic"; - case 1: return "BasicHighContrast"; - case 5: return "IceCreamSandwich"; - case 6: return "Stone"; - case 7: return "StoneBold"; - case 8: return "GingerBread"; - default: return null; - } - } - public static String printableCode(int code) { switch (code) { case CODE_SHIFT: return "shift"; diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardId.java b/java/src/com/android/inputmethod/keyboard/KeyboardId.java index ecc821a4b..3b3ff0709 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardId.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardId.java @@ -21,11 +21,11 @@ import android.view.inputmethod.EditorInfo; import com.android.inputmethod.compat.EditorInfoCompatUtils; import com.android.inputmethod.compat.InputTypeCompatUtils; -import com.android.inputmethod.latin.R; 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. @@ -38,100 +38,167 @@ public class KeyboardId { public static final int MODE_PHONE = 4; public static final int MODE_NUMBER = 5; - public static final int F2KEY_MODE_NONE = 0; - public static final int F2KEY_MODE_SETTINGS = 1; - public static final int F2KEY_MODE_SHORTCUT_IME = 2; - public static final int F2KEY_MODE_SHORTCUT_IME_OR_SETTINGS = 3; + public static final int ELEMENT_ALPHABET = 0; + /* TODO: Implement alphabet variant shift keyboard. + public static final int ELEMENT_ALPHABET_MANUAL_TEMPORARY_SHIFT = 1; + public static final int ELEMENT_ALPHABET_AUTOMATIC_TEMPORARY_SHIFT = 2; + public static final int ELEMENT_ALPHABET_SHIFT_LOCK = 3; + public static final int ELEMENT_ALPHABET_SHIFT_LOCK_SHIFT = 4; + */ + public static final int ELEMENT_SYMBOLS = 5; + public static final int ELEMENT_SYMBOLS_SHIFT = 6; + public static final int ELEMENT_PHONE = 7; + public static final int ELEMENT_PHONE_SHIFT = 8; + public static final int ELEMENT_NUMBER = 9; + + private static final int F2KEY_MODE_NONE = 0; + private static final int F2KEY_MODE_SETTINGS = 1; + private static final int F2KEY_MODE_SHORTCUT_IME = 2; + private static final int F2KEY_MODE_SHORTCUT_IME_OR_SETTINGS = 3; public final Locale mLocale; public final int mOrientation; public final int mWidth; public final int mMode; - public final int mXmlId; - public final boolean mNavigateAction; - public final boolean mPasswordInput; - // TODO: Clean up these booleans and modes. - public final boolean mHasSettingsKey; - public final int mF2KeyMode; + // TODO: Remove this field. + private final int mXmlId; + public final int mElementState; + private final int mInputType; + private final int mImeOptions; + private final boolean mSettingsKeyEnabled; public final boolean mClobberSettingsKey; public final boolean mShortcutKeyEnabled; public final boolean mHasShortcutKey; - public final int mImeAction; - - public final String mXmlName; - public final EditorInfo mEditorInfo; private final int mHashCode; - public KeyboardId(String xmlName, int xmlId, Locale locale, int orientation, int width, - int mode, EditorInfo editorInfo, boolean hasSettingsKey, int f2KeyMode, + public KeyboardId(int xmlId, int elementState, Locale locale, int orientation, int width, + int mode, EditorInfo editorInfo, boolean settingsKeyEnabled, + boolean clobberSettingsKey, boolean shortcutKeyEnabled, boolean hasShortcutKey) { + this(xmlId, elementState, locale, orientation, width, mode, + (editorInfo != null ? editorInfo.inputType : 0), + (editorInfo != null ? editorInfo.imeOptions : 0), + settingsKeyEnabled, clobberSettingsKey, shortcutKeyEnabled, hasShortcutKey); + } + + private KeyboardId(int xmlId, int elementState, Locale locale, int orientation, int width, + int mode, int inputType, int imeOptions, boolean settingsKeyEnabled, boolean clobberSettingsKey, boolean shortcutKeyEnabled, boolean hasShortcutKey) { - final int inputType = (editorInfo != null) ? editorInfo.inputType : 0; - final int imeOptions = (editorInfo != null) ? editorInfo.imeOptions : 0; this.mLocale = locale; this.mOrientation = orientation; this.mWidth = width; this.mMode = mode; this.mXmlId = xmlId; - // Note: Turn off checking navigation flag to show TAB key for now. - this.mNavigateAction = InputTypeCompatUtils.isWebInputType(inputType); -// || EditorInfoCompatUtils.hasFlagNavigateNext(imeOptions) -// || EditorInfoCompatUtils.hasFlagNavigatePrevious(imeOptions); - this.mPasswordInput = InputTypeCompatUtils.isPasswordInputType(inputType) - || InputTypeCompatUtils.isVisiblePasswordInputType(inputType); - this.mHasSettingsKey = hasSettingsKey; - this.mF2KeyMode = f2KeyMode; + this.mElementState = elementState; + this.mInputType = inputType; + this.mImeOptions = imeOptions; + this.mSettingsKeyEnabled = settingsKeyEnabled; this.mClobberSettingsKey = clobberSettingsKey; this.mShortcutKeyEnabled = shortcutKeyEnabled; this.mHasShortcutKey = hasShortcutKey; - // We are interested only in {@link EditorInfo#IME_MASK_ACTION} enum value and - // {@link EditorInfo#IME_FLAG_NO_ENTER_ACTION}. - this.mImeAction = imeOptions & ( - EditorInfo.IME_MASK_ACTION | EditorInfo.IME_FLAG_NO_ENTER_ACTION); - this.mXmlName = xmlName; - this.mEditorInfo = editorInfo; - - this.mHashCode = Arrays.hashCode(new Object[] { - locale, - orientation, - width, - mode, - xmlId, - mNavigateAction, - mPasswordInput, - hasSettingsKey, - f2KeyMode, - clobberSettingsKey, - shortcutKeyEnabled, - hasShortcutKey, - mImeAction, + this.mHashCode = hashCode(this); + } + + private static int hashCode(KeyboardId id) { + return Arrays.hashCode(new Object[] { + id.mOrientation, + id.mElementState, + id.mMode, + id.mWidth, + id.mXmlId, + id.navigateAction(), + id.passwordInput(), + id.mSettingsKeyEnabled, + id.mClobberSettingsKey, + id.mShortcutKeyEnabled, + id.mHasShortcutKey, + id.imeAction(), + id.mLocale }); } - public KeyboardId cloneWithNewXml(String xmlName, int xmlId) { - return new KeyboardId(xmlName, xmlId, mLocale, mOrientation, mWidth, mMode, mEditorInfo, - false, F2KEY_MODE_NONE, false, false, false); + private boolean equals(KeyboardId other) { + if (other == this) + return true; + return other.mOrientation == this.mOrientation + && other.mElementState == this.mElementState + && other.mMode == this.mMode + && other.mWidth == this.mWidth + && other.mXmlId == this.mXmlId + && other.navigateAction() == this.navigateAction() + && other.passwordInput() == this.passwordInput() + && other.mSettingsKeyEnabled == this.mSettingsKeyEnabled + && other.mClobberSettingsKey == this.mClobberSettingsKey + && other.mShortcutKeyEnabled == this.mShortcutKeyEnabled + && other.mHasShortcutKey == this.mHasShortcutKey + && other.imeAction() == this.imeAction() + && other.mLocale.equals(this.mLocale); } + public KeyboardId cloneWithNewXml(int xmlId) { + return new KeyboardId(xmlId, mElementState, mLocale, mOrientation, mWidth, mMode, + mInputType, mImeOptions, false, false, false, false); + } + + // Remove this method. public int getXmlId() { return mXmlId; } public boolean isAlphabetKeyboard() { - return mXmlId == R.xml.kbd_qwerty; + return mElementState < ELEMENT_SYMBOLS; } public boolean isSymbolsKeyboard() { - return mXmlId == R.xml.kbd_symbols || mXmlId == R.xml.kbd_symbols_shift; + return mElementState == ELEMENT_SYMBOLS || mElementState == ELEMENT_SYMBOLS_SHIFT; } public boolean isPhoneKeyboard() { - return mMode == MODE_PHONE; + return mElementState == ELEMENT_PHONE || mElementState == ELEMENT_PHONE_SHIFT; } public boolean isPhoneShiftKeyboard() { - return mXmlId == R.xml.kbd_phone_shift; + return mElementState == ELEMENT_PHONE_SHIFT; + } + + public boolean navigateAction() { + // Note: Turn off checking navigation flag to show TAB key for now. + boolean navigateAction = InputTypeCompatUtils.isWebInputType(mInputType); +// || EditorInfoCompatUtils.hasFlagNavigateNext(mImeOptions) +// || EditorInfoCompatUtils.hasFlagNavigatePrevious(mImeOptions); + return navigateAction; + } + + public boolean passwordInput() { + return InputTypeCompatUtils.isPasswordInputType(mInputType) + || InputTypeCompatUtils.isVisiblePasswordInputType(mInputType); + } + + 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); + } + + public boolean hasSettingsKey() { + return mSettingsKeyEnabled && !mClobberSettingsKey; + } + + public int f2KeyMode() { + if (mClobberSettingsKey) { + // Never shows the Settings key + return KeyboardId.F2KEY_MODE_SHORTCUT_IME; + } + + if (mSettingsKeyEnabled) { + return KeyboardId.F2KEY_MODE_SETTINGS; + } else { + // It should be alright to fall back to the Settings key on 7-inch layouts + // even when the Settings key is not explicitly enabled. + return KeyboardId.F2KEY_MODE_SHORTCUT_IME_OR_SETTINGS; + } } @Override @@ -139,22 +206,6 @@ public class KeyboardId { return other instanceof KeyboardId && equals((KeyboardId) other); } - private boolean equals(KeyboardId other) { - return other.mLocale.equals(this.mLocale) - && other.mOrientation == this.mOrientation - && other.mWidth == this.mWidth - && other.mMode == this.mMode - && other.mXmlId == this.mXmlId - && other.mNavigateAction == this.mNavigateAction - && other.mPasswordInput == this.mPasswordInput - && other.mHasSettingsKey == this.mHasSettingsKey - && other.mF2KeyMode == this.mF2KeyMode - && other.mClobberSettingsKey == this.mClobberSettingsKey - && other.mShortcutKeyEnabled == this.mShortcutKeyEnabled - && other.mHasShortcutKey == this.mHasShortcutKey - && other.mImeAction == this.mImeAction; - } - @Override public int hashCode() { return mHashCode; @@ -162,17 +213,17 @@ public class KeyboardId { @Override public String toString() { - return String.format("[%s.xml %s %s%d %s %s %s%s%s%s%s%s%s]", - mXmlName, + return String.format("[%s %s %s%d %s %s %s%s%s%s%s%s%s]", + elementStateToString(mElementState), mLocale, (mOrientation == 1 ? "port" : "land"), mWidth, modeName(mMode), - EditorInfoCompatUtils.imeOptionsName(mImeAction), - f2KeyModeName(mF2KeyMode), + EditorInfoCompatUtils.imeOptionsName(imeAction()), + f2KeyModeName(f2KeyMode()), (mClobberSettingsKey ? " clobberSettingsKey" : ""), - (mNavigateAction ? " navigateAction" : ""), - (mPasswordInput ? " passwordInput" : ""), - (mHasSettingsKey ? " hasSettingsKey" : ""), + (navigateAction() ? " navigateAction" : ""), + (passwordInput() ? " passwordInput" : ""), + (hasSettingsKey() ? " hasSettingsKey" : ""), (mShortcutKeyEnabled ? " shortcutKeyEnabled" : ""), (mHasShortcutKey ? " hasShortcutKey" : "") ); @@ -186,6 +237,24 @@ public class KeyboardId { && TextUtils.equals(a.privateImeOptions, b.privateImeOptions); } + public static String elementStateToString(int elementState) { + switch (elementState) { + case ELEMENT_ALPHABET: return "alphabet"; + /* TODO: Implement alphabet variant shift keyboard. + case ELEMENT_ALPHABET_MANUAL_TEMPORARY_SHIFT: return "alphabetManualTemporaryShift"; + case ELEMENT_ALPHABET_AUTOMATIC_TEMPORARY_SHIFT: return "alphabetAutomaticTemporaryShift"; + case ELEMENT_ALPHABET_SHIFT_LOCK: return "alphabetShiftLock"; + case ELEMENT_ALPHABET_SHIFT_LOCK_SHIFT: return "alphabetShiftLockShift"; + */ + case ELEMENT_SYMBOLS: return "symbols"; + case ELEMENT_SYMBOLS_SHIFT: return "symbolsShift"; + case ELEMENT_PHONE: return "phone"; + case ELEMENT_PHONE_SHIFT: return "phoneShift"; + case ELEMENT_NUMBER: return "number"; + default: return null; + } + } + public static String modeName(int mode) { switch (mode) { case MODE_TEXT: return "text"; diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardSet.java index a803188da..a28cfa85d 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSet.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSet.java @@ -19,15 +19,25 @@ package com.android.inputmethod.keyboard; import android.content.Context; import android.content.res.Configuration; import android.content.res.Resources; +import android.content.res.TypedArray; +import android.content.res.XmlResourceParser; import android.util.DisplayMetrics; +import android.util.Xml; import android.view.inputmethod.EditorInfo; +import com.android.inputmethod.keyboard.internal.XmlParseUtils; import com.android.inputmethod.latin.LatinIME; +import com.android.inputmethod.latin.LocaleUtils; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SettingsValues; import com.android.inputmethod.latin.SubtypeSwitcher; import com.android.inputmethod.latin.Utils; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.util.HashMap; import java.util.Locale; /** @@ -37,6 +47,9 @@ import java.util.Locale; * A {@link KeyboardSet} needs to be created for each {@link android.view.inputmethod.EditorInfo}. */ public class KeyboardSet { + private static final String TAG_KEYBOARD_SET = "KeyboardSet"; + private static final String TAG_ELEMENT = "Element"; + // TODO: Make these KeyboardId private. public final KeyboardId mAlphabetId; public final KeyboardId mSymbolsId; @@ -52,12 +65,14 @@ public class KeyboardSet { private final Resources mResources; private final EditorInfo mEditorInfo; + private final HashMap<Integer, Integer> mElementKeyboards = + new HashMap<Integer, Integer>(); + private final int mMode; + private final boolean mSettingsKeyEnabled; private final boolean mVoiceKeyEnabled; - private final boolean mNoSettingsKey; - private final boolean mHasSettingsKey; - private final int mF2KeyMode; private final boolean mVoiceKeyOnMain; + private final boolean mNoSettingsKey; private final Locale mLocale; private final Configuration mConf; private final DisplayMetrics mMetrics; @@ -69,7 +84,7 @@ public class KeyboardSet { final String packageName = context.getPackageName(); mMode = Utils.getKeyboardMode(mEditorInfo); - final boolean settingsKeyEnabled = settingsValues.isSettingsKeyEnabled(); + mSettingsKeyEnabled = settingsValues.isSettingsKeyEnabled(); @SuppressWarnings("deprecation") final boolean noMicrophone = Utils.inPrivateImeOptions( packageName, LatinIME.IME_OPTION_NO_MICROPHONE, editorInfo) @@ -79,8 +94,6 @@ public class KeyboardSet { mVoiceKeyOnMain = settingsValues.isVoiceKeyOnMain(); mNoSettingsKey = Utils.inPrivateImeOptions( packageName, LatinIME.IME_OPTION_NO_SETTINGS_KEY, editorInfo); - mHasSettingsKey = settingsKeyEnabled && !mNoSettingsKey; - mF2KeyMode = getF2KeyMode(settingsKeyEnabled, mNoSettingsKey); final boolean forceAscii = Utils.inPrivateImeOptions( packageName, LatinIME.IME_OPTION_FORCE_ASCII, editorInfo); final boolean asciiCapable = subtypeSwitcher.currentSubtypeContainsExtraValueKey( @@ -91,44 +104,127 @@ public class KeyboardSet { } public KeyboardSet build() { + final Locale savedLocale = LocaleUtils.setSystemLocale(mResources, mLocale); + try { + parseKeyboardSet(mResources, R.xml.keyboard_set); + } catch (Exception e) { + // + } finally { + LocaleUtils.setSystemLocale(mResources, savedLocale); + } return new KeyboardSet(this); } KeyboardId getKeyboardId(boolean isSymbols, boolean isShift) { - final int xmlId = getXmlId(mMode, isSymbols, isShift); - final boolean hasShortCutKey = mVoiceKeyEnabled && (isSymbols != mVoiceKeyOnMain); - return new KeyboardId(mResources.getResourceEntryName(xmlId), xmlId, mLocale, - mConf.orientation, mMetrics.widthPixels, mMode, mEditorInfo, mHasSettingsKey, - mF2KeyMode, mNoSettingsKey, mVoiceKeyEnabled, hasShortCutKey); + final int elementState = getElementState(mMode, isSymbols, isShift); + final int xmlId = mElementKeyboards.get(elementState); + final boolean hasShortcutKey = mVoiceKeyEnabled && (isSymbols != mVoiceKeyOnMain); + return new KeyboardId(xmlId, elementState, mLocale, mConf.orientation, + mMetrics.widthPixels, mMode, mEditorInfo, mSettingsKeyEnabled, mNoSettingsKey, + mVoiceKeyEnabled, hasShortcutKey); } - private static int getXmlId(int mode, boolean isSymbols, boolean isShift) { + private static int getElementState(int mode, boolean isSymbols, boolean isShift) { switch (mode) { case KeyboardId.MODE_PHONE: - return (isSymbols && isShift) ? R.xml.kbd_phone_shift : R.xml.kbd_phone; + return (isSymbols && isShift) + ? KeyboardId.ELEMENT_PHONE_SHIFT : KeyboardId.ELEMENT_PHONE; case KeyboardId.MODE_NUMBER: - return R.xml.kbd_number; + return KeyboardId.ELEMENT_NUMBER; default: if (isSymbols) { - return isShift ? R.xml.kbd_symbols_shift : R.xml.kbd_symbols; + return isShift ? KeyboardId.ELEMENT_SYMBOLS_SHIFT : KeyboardId.ELEMENT_SYMBOLS; + } + return KeyboardId.ELEMENT_ALPHABET; + } + } + + private void parseKeyboardSet(Resources res, int resId) throws XmlPullParserException, + IOException { + final XmlResourceParser parser = res.getXml(resId); + try { + int event; + while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) { + if (event == XmlPullParser.START_TAG) { + final String tag = parser.getName(); + if (TAG_KEYBOARD_SET.equals(tag)) { + parseKeyboardSetContent(parser); + } else { + throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEYBOARD_SET); + } + } } - return R.xml.kbd_qwerty; + } finally { + parser.close(); } } - private static int getF2KeyMode(boolean settingsKeyEnabled, boolean noSettingsKey) { - if (noSettingsKey) { - // Never shows the Settings key - return KeyboardId.F2KEY_MODE_SHORTCUT_IME; + private void parseKeyboardSetContent(XmlPullParser parser) throws XmlPullParserException, + IOException { + int event; + while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) { + if (event == XmlPullParser.START_TAG) { + final String tag = parser.getName(); + if (TAG_ELEMENT.equals(tag)) { + parseKeyboardSetElement(parser); + } else { + throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEYBOARD_SET); + } + } else if (event == XmlPullParser.END_TAG) { + final String tag = parser.getName(); + if (TAG_KEYBOARD_SET.equals(tag)) { + break; + } else { + throw new XmlParseUtils.IllegalEndTag(parser, TAG_KEYBOARD_SET); + } + } } + } - if (settingsKeyEnabled) { - return KeyboardId.F2KEY_MODE_SETTINGS; - } else { - // It should be alright to fall back to the Settings key on 7-inch layouts - // even when the Settings key is not explicitly enabled. - return KeyboardId.F2KEY_MODE_SHORTCUT_IME_OR_SETTINGS; + private void parseKeyboardSetElement(XmlPullParser parser) throws XmlPullParserException, + IOException { + final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser), + R.styleable.KeyboardSet_Element); + try { + XmlParseUtils.checkAttributeExists(a, + R.styleable.KeyboardSet_Element_elementName, "elementName", + TAG_ELEMENT, parser); + XmlParseUtils.checkAttributeExists(a, + R.styleable.KeyboardSet_Element_elementKeyboard, "elementKeyboard", + TAG_ELEMENT, parser); + XmlParseUtils.checkEndTag(TAG_ELEMENT, parser); + + final int elementName = a.getInt( + R.styleable.KeyboardSet_Element_elementName, 0); + final int elementKeyboard = a.getResourceId( + R.styleable.KeyboardSet_Element_elementKeyboard, 0); + mElementKeyboards.put(elementName, elementKeyboard); + } finally { + a.recycle(); + } + } + } + + public static String parseKeyboardLocale(Resources res, int resId) + throws XmlPullParserException, IOException { + final XmlPullParser parser = res.getXml(resId); + if (parser == null) return ""; + int event; + while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) { + if (event == XmlPullParser.START_TAG) { + final String tag = parser.getName(); + if (TAG_KEYBOARD_SET.equals(tag)) { + final TypedArray keyboardSetAttr = res.obtainAttributes( + Xml.asAttributeSet(parser), R.styleable.KeyboardSet); + final String locale = keyboardSetAttr.getString( + R.styleable.KeyboardSet_keyboardLocale); + keyboardSetAttr.recycle(); + return locale; + } else { + throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEYBOARD_SET); + } } } + return ""; } } diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index 165e9aec7..1f3006d9f 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -191,11 +191,11 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions, if (DEBUG_CACHE) { Log.d(TAG, "keyboard cache size=" + mKeyboardCache.size() + ": " + ((ref == null) ? "LOAD" : "GCed") + " id=" + id - + " theme=" + Keyboard.themeName(keyboard.mThemeId)); + + " theme=" + themeName(keyboard.mThemeId)); } } else if (DEBUG_CACHE) { Log.d(TAG, "keyboard cache size=" + mKeyboardCache.size() + ": HIT id=" + id - + " theme=" + Keyboard.themeName(keyboard.mThemeId)); + + " theme=" + themeName(keyboard.mThemeId)); } keyboard.onAutoCorrectionStateChanged(mIsAutoCorrectionActive); @@ -463,4 +463,17 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions, } } } + + private static String themeName(int themeId) { + // This should be aligned with theme-*.xml resource files' themeId attribute. + switch (themeId) { + case 0: return "Basic"; + case 1: return "BasicHighContrast"; + case 5: return "IceCreamSandwich"; + case 6: return "Stone"; + case 7: return "StoneBold"; + case 8: return "GingerBread"; + default: return null; + } + } } diff --git a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java index af5f808fd..e56f2ea84 100644 --- a/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/LatinKeyboardView.java @@ -409,19 +409,16 @@ public class LatinKeyboardView extends KeyboardView implements PointerTracker.Ke invokeReleaseKey(primaryCode); return true; } - } - if (primaryCode == Keyboard.CODE_SETTINGS || primaryCode == Keyboard.CODE_SPACE) { - // Both long pressing settings key and space key invoke IME switcher dialog. - if (invokeCustomRequest(LatinIME.CODE_SHOW_INPUT_METHOD_PICKER)) { - tracker.onLongPressed(); - invokeReleaseKey(primaryCode); - return true; - } else { - return openMoreKeysPanel(parentKey, tracker); + if (primaryCode == Keyboard.CODE_SPACE) { + // Long pressing the space key invokes IME switcher dialog. + if (invokeCustomRequest(LatinIME.CODE_SHOW_INPUT_METHOD_PICKER)) { + tracker.onLongPressed(); + invokeReleaseKey(primaryCode); + return true; + } } - } else { - return openMoreKeysPanel(parentKey, tracker); } + return openMoreKeysPanel(parentKey, tracker); } private boolean invokeCustomRequest(int code) { diff --git a/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java b/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java index ac9290bfd..e0f21a247 100644 --- a/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java +++ b/java/src/com/android/inputmethod/keyboard/MiniKeyboard.java @@ -207,7 +207,7 @@ public class MiniKeyboard extends Keyboard { public Builder(KeyboardView view, int xmlId, Key parentKey, Keyboard parentKeyboard) { super(view.getContext(), new MiniKeyboardParams()); - load(parentKeyboard.mId.cloneWithNewXml(mResources.getResourceEntryName(xmlId), xmlId)); + load(parentKeyboard.mId.cloneWithNewXml(xmlId)); // TODO: Mini keyboard's vertical gap is currently calculated heuristically. // Should revise the algorithm. diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java index 218793500..3324fa6af 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java @@ -19,10 +19,10 @@ package com.android.inputmethod.keyboard.internal; import android.content.res.TypedArray; import android.util.Log; -import com.android.inputmethod.keyboard.internal.KeyboardBuilder.ParseException; import com.android.inputmethod.latin.R; import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; import java.util.ArrayList; import java.util.HashMap; @@ -43,7 +43,7 @@ public class KeyStyles { } /* package */ static class EmptyKeyStyle implements KeyStyle { - private EmptyKeyStyle() { + EmptyKeyStyle() { // Nothing to do. } @@ -118,7 +118,7 @@ public class KeyStyles { } } - private static class DeclaredKeyStyle extends EmptyKeyStyle { + /* package */ static class DeclaredKeyStyle extends EmptyKeyStyle { private final HashMap<Integer, Object> mAttributes = new HashMap<Integer, Object>(); @Override @@ -145,11 +145,11 @@ public class KeyStyles { return super.getFlag(a, index, defaultValue) | (value != null ? value : 0); } - private DeclaredKeyStyle() { + DeclaredKeyStyle() { super(); } - private void parseKeyStyleAttributes(TypedArray keyAttr) { + void parseKeyStyleAttributes(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); @@ -188,18 +188,19 @@ public class KeyStyles { mAttributes.put(index, value); } - private void addParent(DeclaredKeyStyle parentStyle) { + void addParent(DeclaredKeyStyle parentStyle) { mAttributes.putAll(parentStyle.mAttributes); } } public void parseKeyStyleAttributes(TypedArray keyStyleAttr, TypedArray keyAttrs, - XmlPullParser parser) { + XmlPullParser parser) throws XmlPullParserException { final String styleName = keyStyleAttr.getString(R.styleable.Keyboard_KeyStyle_styleName); if (DEBUG) Log.d(TAG, String.format("<%s styleName=%s />", KeyboardBuilder.TAG_KEY_STYLE, styleName)); if (mStyles.containsKey(styleName)) - throw new ParseException("duplicate key style declared: " + styleName, parser); + throw new XmlParseUtils.ParseException( + "duplicate key style declared: " + styleName, parser); final DeclaredKeyStyle style = new DeclaredKeyStyle(); if (keyStyleAttr.hasValue(R.styleable.Keyboard_KeyStyle_parentStyle)) { @@ -207,7 +208,8 @@ public class KeyStyles { R.styleable.Keyboard_KeyStyle_parentStyle); final DeclaredKeyStyle parent = mStyles.get(parentStyle); if (parent == null) - throw new ParseException("Unknown parentStyle " + parentStyle, parser); + throw new XmlParseUtils.ParseException( + "Unknown parentStyle " + parentStyle, parser); style.addParent(parent); } style.parseKeyStyleAttributes(keyAttrs); diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java index de64639b0..7382cfa7f 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java @@ -292,7 +292,7 @@ public class KeyboardBuilder<KP extends KeyboardParams> { return new Keyboard(mParams); } - private void parseKeyboard(XmlResourceParser parser) + private void parseKeyboard(XmlPullParser parser) throws XmlPullParserException, IOException { if (DEBUG) Log.d(TAG, String.format("<%s> %s", TAG_KEYBOARD, mParams.mId)); int event; @@ -305,36 +305,12 @@ public class KeyboardBuilder<KP extends KeyboardParams> { parseKeyboardContent(parser, false); break; } else { - throw new IllegalStartTag(parser, TAG_KEYBOARD); + throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEYBOARD); } } } } - public static String parseKeyboardLocale( - Context context, int resId) throws XmlPullParserException, IOException { - final Resources res = context.getResources(); - final XmlPullParser parser = res.getXml(resId); - if (parser == null) return ""; - int event; - while ((event = parser.next()) != XmlPullParser.END_DOCUMENT) { - if (event == XmlPullParser.START_TAG) { - final String tag = parser.getName(); - if (TAG_KEYBOARD.equals(tag)) { - final TypedArray keyboardAttr = res.obtainAttributes(Xml.asAttributeSet(parser), - R.styleable.Keyboard); - final String locale = keyboardAttr.getString( - R.styleable.Keyboard_keyboardLocale); - keyboardAttr.recycle(); - return locale; - } else { - throw new IllegalStartTag(parser, TAG_KEYBOARD); - } - } - } - return ""; - } - private void parseKeyboardAttributes(XmlPullParser parser) { final int displayWidth = mDisplayMetrics.widthPixels; final TypedArray keyboardAttr = mContext.obtainStyledAttributes( @@ -417,7 +393,7 @@ public class KeyboardBuilder<KP extends KeyboardParams> { } else if (TAG_KEY_STYLE.equals(tag)) { parseKeyStyle(parser, skip); } else { - throw new IllegalStartTag(parser, TAG_ROW); + throw new XmlParseUtils.IllegalStartTag(parser, TAG_ROW); } } else if (event == XmlPullParser.END_TAG) { final String tag = parser.getName(); @@ -431,20 +407,20 @@ public class KeyboardBuilder<KP extends KeyboardParams> { } else if (TAG_KEY_STYLE.equals(tag)) { continue; } else { - throw new IllegalEndTag(parser, TAG_ROW); + throw new XmlParseUtils.IllegalEndTag(parser, TAG_ROW); } } } } - private Row parseRowAttributes(XmlPullParser parser) { + private Row parseRowAttributes(XmlPullParser parser) throws XmlPullParserException { final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser), R.styleable.Keyboard); try { if (a.hasValue(R.styleable.Keyboard_horizontalGap)) - throw new IllegalAttribute(parser, "horizontalGap"); + throw new XmlParseUtils.IllegalAttribute(parser, "horizontalGap"); if (a.hasValue(R.styleable.Keyboard_verticalGap)) - throw new IllegalAttribute(parser, "verticalGap"); + throw new XmlParseUtils.IllegalAttribute(parser, "verticalGap"); return new Row(mResources, mParams, parser, mCurrentY); } finally { a.recycle(); @@ -468,7 +444,7 @@ public class KeyboardBuilder<KP extends KeyboardParams> { } else if (TAG_KEY_STYLE.equals(tag)) { parseKeyStyle(parser, skip); } else { - throw new IllegalStartTag(parser, TAG_KEY); + throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEY); } } else if (event == XmlPullParser.END_TAG) { final String tag = parser.getName(); @@ -484,7 +460,7 @@ public class KeyboardBuilder<KP extends KeyboardParams> { } else if (TAG_KEY_STYLE.equals(tag)) { continue; } else { - throw new IllegalEndTag(parser, TAG_KEY); + throw new XmlParseUtils.IllegalEndTag(parser, TAG_KEY); } } } @@ -493,13 +469,13 @@ public class KeyboardBuilder<KP extends KeyboardParams> { private void parseKey(XmlPullParser parser, Row row, boolean skip) throws XmlPullParserException, IOException { if (skip) { - checkEndTag(TAG_KEY, parser); + XmlParseUtils.checkEndTag(TAG_KEY, parser); } 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))); - checkEndTag(TAG_KEY, parser); + XmlParseUtils.checkEndTag(TAG_KEY, parser); endKey(key); } } @@ -507,11 +483,11 @@ public class KeyboardBuilder<KP extends KeyboardParams> { private void parseSpacer(XmlPullParser parser, Row row, boolean skip) throws XmlPullParserException, IOException { if (skip) { - checkEndTag(TAG_SPACER, parser); + XmlParseUtils.checkEndTag(TAG_SPACER, parser); } else { final Key.Spacer spacer = new Key.Spacer(mResources, mParams, row, parser, mKeyStyles); if (DEBUG) Log.d(TAG, String.format("<%s />", TAG_SPACER)); - checkEndTag(TAG_SPACER, parser); + XmlParseUtils.checkEndTag(TAG_SPACER, parser); endKey(spacer); } } @@ -529,17 +505,21 @@ public class KeyboardBuilder<KP extends KeyboardParams> { private void parseIncludeInternal(XmlPullParser parser, Row row, boolean skip) throws XmlPullParserException, IOException { if (skip) { - checkEndTag(TAG_INCLUDE, parser); + XmlParseUtils.checkEndTag(TAG_INCLUDE, parser); } else { final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser), R.styleable.Keyboard_Include); - final int keyboardLayout = a.getResourceId( - R.styleable.Keyboard_Include_keyboardLayout, 0); - a.recycle(); + int keyboardLayout = 0; + try { + XmlParseUtils.checkAttributeExists(a, + R.styleable.Keyboard_Include_keyboardLayout, "keyboardLayout", + TAG_INCLUDE, parser); + keyboardLayout = a.getResourceId(R.styleable.Keyboard_Include_keyboardLayout, 0); + } finally { + a.recycle(); + } - checkEndTag(TAG_INCLUDE, parser); - if (keyboardLayout == 0) - throw new ParseException("No keyboardLayout attribute in <include/>", parser); + XmlParseUtils.checkEndTag(TAG_INCLUDE, parser); if (DEBUG) Log.d(TAG, String.format("<%s keyboardLayout=%s />", TAG_INCLUDE, mResources.getResourceEntryName(keyboardLayout))); final XmlResourceParser parserForInclude = mResources.getXml(keyboardLayout); @@ -565,7 +545,7 @@ public class KeyboardBuilder<KP extends KeyboardParams> { } break; } else { - throw new ParseException( + throw new XmlParseUtils.ParseException( "Included keyboard layout must have <merge> root element", parser); } } @@ -595,7 +575,7 @@ public class KeyboardBuilder<KP extends KeyboardParams> { } else if (TAG_DEFAULT.equals(tag)) { selected |= parseDefault(parser, row, selected ? true : skip); } else { - throw new IllegalStartTag(parser, TAG_KEY); + throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEY); } } else if (event == XmlPullParser.END_TAG) { final String tag = parser.getName(); @@ -603,7 +583,7 @@ public class KeyboardBuilder<KP extends KeyboardParams> { if (DEBUG) Log.d(TAG, String.format("</%s>", TAG_SWITCH)); break; } else { - throw new IllegalEndTag(parser, TAG_KEY); + throw new XmlParseUtils.IllegalEndTag(parser, TAG_KEY); } } } @@ -633,13 +613,13 @@ public class KeyboardBuilder<KP extends KeyboardParams> { final boolean modeMatched = matchTypedValue(a, R.styleable.Keyboard_Case_mode, id.mMode, KeyboardId.modeName(id.mMode)); final boolean navigateActionMatched = matchBoolean(a, - R.styleable.Keyboard_Case_navigateAction, id.mNavigateAction); + R.styleable.Keyboard_Case_navigateAction, id.navigateAction()); final boolean passwordInputMatched = matchBoolean(a, - R.styleable.Keyboard_Case_passwordInput, id.mPasswordInput); + R.styleable.Keyboard_Case_passwordInput, id.passwordInput()); final boolean hasSettingsKeyMatched = matchBoolean(a, - R.styleable.Keyboard_Case_hasSettingsKey, id.mHasSettingsKey); + R.styleable.Keyboard_Case_hasSettingsKey, id.hasSettingsKey()); final boolean f2KeyModeMatched = matchInteger(a, - R.styleable.Keyboard_Case_f2KeyMode, id.mF2KeyMode); + R.styleable.Keyboard_Case_f2KeyMode, id.f2KeyMode()); final boolean clobberSettingsKeyMatched = matchBoolean(a, R.styleable.Keyboard_Case_clobberSettingsKey, id.mClobberSettingsKey); final boolean shortcutKeyEnabledMatched = matchBoolean(a, @@ -651,7 +631,7 @@ public class KeyboardBuilder<KP extends KeyboardParams> { // {@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 imeActionMatched = matchInteger(a, - R.styleable.Keyboard_Case_imeAction, id.mImeAction); + R.styleable.Keyboard_Case_imeAction, id.imeAction()); final boolean localeCodeMatched = matchString(a, R.styleable.Keyboard_Case_localeCode, id.mLocale.toString()); final boolean languageCodeMatched = matchString(a, @@ -740,14 +720,15 @@ public class KeyboardBuilder<KP extends KeyboardParams> { return true; } - private void parseKeyStyle(XmlPullParser parser, boolean skip) { + private void parseKeyStyle(XmlPullParser parser, boolean skip) + throws XmlPullParserException { TypedArray keyStyleAttr = mResources.obtainAttributes(Xml.asAttributeSet(parser), R.styleable.Keyboard_KeyStyle); TypedArray keyAttrs = mResources.obtainAttributes(Xml.asAttributeSet(parser), R.styleable.Keyboard_Key); try { if (!keyStyleAttr.hasValue(R.styleable.Keyboard_KeyStyle_styleName)) - throw new ParseException("<" + TAG_KEY_STYLE + throw new XmlParseUtils.ParseException("<" + TAG_KEY_STYLE + "/> needs styleName attribute", parser); if (!skip) mKeyStyles.parseKeyStyleAttributes(keyStyleAttr, keyAttrs, parser); @@ -757,13 +738,6 @@ public class KeyboardBuilder<KP extends KeyboardParams> { } } - private static void checkEndTag(String tag, XmlPullParser parser) - throws XmlPullParserException, IOException { - if (parser.next() == XmlPullParser.END_TAG && tag.equals(parser.getName())) - return; - throw new NonEmptyTag(tag, parser); - } - private void startKeyboard() { mCurrentY += mParams.mTopPadding; mTopEdge = true; @@ -802,6 +776,7 @@ public class KeyboardBuilder<KP extends KeyboardParams> { } private void endKeyboard() { + // nothing to do here. } private void addEdgeSpace(float width, Row row) { @@ -848,41 +823,6 @@ public class KeyboardBuilder<KP extends KeyboardParams> { return v.type == TypedValue.TYPE_STRING; } - @SuppressWarnings("serial") - public static class ParseException extends InflateException { - public ParseException(String msg, XmlPullParser parser) { - super(msg + " at line " + parser.getLineNumber()); - } - } - - @SuppressWarnings("serial") - private static class IllegalStartTag extends ParseException { - public IllegalStartTag(XmlPullParser parser, String parent) { - super("Illegal start tag " + parser.getName() + " in " + parent, parser); - } - } - - @SuppressWarnings("serial") - private static class IllegalEndTag extends ParseException { - public IllegalEndTag(XmlPullParser parser, String parent) { - super("Illegal end tag " + parser.getName() + " in " + parent, parser); - } - } - - @SuppressWarnings("serial") - private static class IllegalAttribute extends ParseException { - public IllegalAttribute(XmlPullParser parser, String attribute) { - super("Tag " + parser.getName() + " has illegal attribute " + attribute, parser); - } - } - - @SuppressWarnings("serial") - private static class NonEmptyTag extends ParseException { - public NonEmptyTag(String tag, XmlPullParser parser) { - super(tag + " must be empty tag", parser); - } - } - private static String textAttr(String value, String name) { return value != null ? String.format(" %s=%s", name, value) : ""; } diff --git a/java/src/com/android/inputmethod/keyboard/internal/XmlParseUtils.java b/java/src/com/android/inputmethod/keyboard/internal/XmlParseUtils.java new file mode 100644 index 000000000..170be347b --- /dev/null +++ b/java/src/com/android/inputmethod/keyboard/internal/XmlParseUtils.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.android.inputmethod.keyboard.internal; + +import android.content.res.TypedArray; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; + +public class XmlParseUtils { + @SuppressWarnings("serial") + public static class ParseException extends XmlPullParserException { + public ParseException(String msg, XmlPullParser parser) { + super(msg + " at line " + parser.getLineNumber() + + ", column " + parser.getColumnNumber()); + } + } + + @SuppressWarnings("serial") + public static class IllegalStartTag extends ParseException { + public IllegalStartTag(XmlPullParser parser, String parent) { + super("Illegal start tag " + parser.getName() + " in " + parent, parser); + } + } + + @SuppressWarnings("serial") + public static class IllegalEndTag extends ParseException { + public IllegalEndTag(XmlPullParser parser, String parent) { + super("Illegal end tag " + parser.getName() + " in " + parent, parser); + } + } + + @SuppressWarnings("serial") + static class IllegalAttribute extends ParseException { + public IllegalAttribute(XmlPullParser parser, String attribute) { + super("Tag " + parser.getName() + " has illegal attribute " + attribute, parser); + } + } + + @SuppressWarnings("serial") + static class NonEmptyTag extends ParseException{ + public NonEmptyTag(String tag, XmlPullParser parser) { + super(tag + " must be empty tag", parser); + } + } + + public static void checkEndTag(String tag, XmlPullParser parser) + throws XmlPullParserException, IOException { + if (parser.next() == XmlPullParser.END_TAG && tag.equals(parser.getName())) + return; + throw new NonEmptyTag(tag, parser); + } + + public static void checkAttributeExists(TypedArray attr, int attrId, String attrName, + String tag, XmlPullParser parser) throws XmlPullParserException { + if (attr.hasValue(attrId)) + return; + throw new ParseException( + "No " + attrName + " attribute found in <" + tag + "/>", parser); + } +} diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 2d7eed7ab..98fea1b5b 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -722,6 +722,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar : String.format("inputType=0x%08x imeOptions=0x%08x", editorInfo.inputType, editorInfo.imeOptions))); } + LatinImeLogger.onStartInputView(editorInfo); // In landscape mode, this method gets called without the input view being created. if (inputView == null) { return; @@ -1510,13 +1511,16 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar if (null != ic) removeTrailingSpaceWhileInBatchEdit(ic); } + boolean isComposingWord = mHasUncommittedTypedChars; int code = primaryCode; if ((isAlphabet(code) || mSettingsValues.isSymbolExcludedFromWordSeparators(code)) && isSuggestionsRequested() && !isCursorTouchingWord()) { - if (!mHasUncommittedTypedChars) { + if (!isComposingWord) { // Reset entirely the composing state anyway, then start composing a new word unless - // the character is a single quote. - mHasUncommittedTypedChars = (Keyboard.CODE_SINGLE_QUOTE != code); + // 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); mWordComposer.reset(); clearSuggestions(); mComposingStateManager.onFinishComposingText(); @@ -1543,7 +1547,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } } } - if (mHasUncommittedTypedChars) { + if (isComposingWord) { + mHasUncommittedTypedChars = true; mWordComposer.add(code, keyCodes, x, y); if (ic != null) { // If it's the first letter, make note of auto-caps state @@ -1581,7 +1586,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar mHandler.postUpdateSuggestions(); } - boolean pickedDefault = false; // Handle separator final InputConnection ic = getCurrentInputConnection(); if (ic != null) { @@ -1598,7 +1602,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar final boolean shouldAutoCorrect = mSettingsValues.mAutoCorrectEnabled && !mInputTypeNoAutoCorrect; if (shouldAutoCorrect && primaryCode != Keyboard.CODE_SINGLE_QUOTE) { - pickedDefault = pickDefaultSuggestion(primaryCode); + commitCurrentAutoCorrection(primaryCode, ic); } else { commitTyped(ic); } @@ -1651,21 +1655,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar Utils.Stats.onSeparator((char)primaryCode, x, y); - if (pickedDefault) { - final CharSequence autoCorrection = mWordComposer.getAutoCorrectionOrNull(); - final String typedWord = mWordComposer.getTypedWord(); - if (TextUtils.isEmpty(typedWord)) { - throw new RuntimeException("We have non-committed chars but the typed word " - + "is empty? Impossible! I must commit suicide."); - } - if (!typedWord.equals(autoCorrection)) { - // TODO: if the commitCorrection method is not supported by the platform - // this will do nothing and the correction will not be committed at all. What - // happens on Froyo/Gingerbread, where this API is not present? - InputConnectionCompatUtils.commitCorrection( - ic, mLastSelectionEnd - typedWord.length(), typedWord, autoCorrection); - } - } mKeyboardSwitcher.updateShiftState(); if (ic != null) { ic.endBatchEdit(); @@ -1785,7 +1774,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar return; } - final WordComposer wordComposer = mWordComposer; // TODO: May need a better way of retrieving previous word final InputConnection ic = getCurrentInputConnection(); final CharSequence prevWord; @@ -1795,18 +1783,18 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar prevWord = EditingUtils.getPreviousWord(ic, mSettingsValues.mWordSeparators); } // getSuggestedWordBuilder handles gracefully a null value of prevWord - final SuggestedWords.Builder builder = mSuggest.getSuggestedWordBuilder(wordComposer, + final SuggestedWords.Builder builder = mSuggest.getSuggestedWordBuilder(mWordComposer, prevWord, mKeyboardSwitcher.getLatinKeyboard().getProximityInfo(), mCorrectionMode); boolean autoCorrectionAvailable = !mInputTypeNoAutoCorrect && mSuggest.hasAutoCorrection(); - final CharSequence typedWord = wordComposer.getTypedWord(); + final CharSequence typedWord = mWordComposer.getTypedWord(); // Here, we want to promote a whitelisted word if exists. // TODO: Change this scheme - a boolean is not enough. A whitelisted word may be "valid" // but still autocorrected from - in the case the whitelist only capitalizes the word. // The whitelist should be case-insensitive, so it's not possible to be consistent with // a boolean flag. Right now this is handled with a slight hack in // WhitelistDictionary#shouldForciblyAutoCorrectFrom. - final int quotesCount = wordComposer.trailingSingleQuotesCount(); + final int quotesCount = mWordComposer.trailingSingleQuotesCount(); final boolean allowsToBeAutoCorrected = AutoCorrection.allowsToBeAutoCorrected( mSuggest.getUnigramDictionaries(), // If the typed string ends with a single quote, for dictionary lookup purposes @@ -1822,7 +1810,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar autoCorrectionAvailable |= (!allowsToBeAutoCorrected); } // Don't auto-correct words with multiple capital letter - autoCorrectionAvailable &= !wordComposer.isMostlyCaps(); + autoCorrectionAvailable &= !mWordComposer.isMostlyCaps(); // Basically, we update the suggestion strip only when suggestion count > 1. However, // there is an exception: We update the suggestion strip whenever typed word's length @@ -1871,7 +1859,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar setSuggestionStripShown(isSuggestionsStripVisible()); } - private boolean pickDefaultSuggestion(int separatorCode) { + private void commitCurrentAutoCorrection(final int separatorCodePoint, + final InputConnection ic) { // Complete any pending suggestions query first if (mHandler.hasPendingUpdateSuggestions()) { mHandler.cancelUpdateSuggestions(); @@ -1880,7 +1869,11 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar final CharSequence autoCorrection = mWordComposer.getAutoCorrectionOrNull(); if (autoCorrection != null) { final String typedWord = mWordComposer.getTypedWord(); - Utils.Stats.onAutoCorrection(typedWord, autoCorrection.toString(), separatorCode); + if (TextUtils.isEmpty(typedWord)) { + throw new RuntimeException("We have an auto-correction but the typed word " + + "is empty? Impossible! I must commit suicide."); + } + Utils.Stats.onAutoCorrection(typedWord, autoCorrection.toString(), separatorCodePoint); mExpectingUpdateSelection = true; commitBestWord(autoCorrection); if (!autoCorrection.equals(typedWord)) { @@ -1889,9 +1882,13 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar // Add the word to the user unigram dictionary if it's not a known word addToUserUnigramAndBigramDictionaries(autoCorrection, UserUnigramDictionary.FREQUENCY_FOR_TYPED); - return true; + if (!typedWord.equals(autoCorrection) && null != ic) { + // This will make the correction flash for a short while as a visual clue + // to the user that auto-correction happened. + InputConnectionCompatUtils.commitCorrection(ic, + mLastSelectionEnd - typedWord.length(), typedWord, autoCorrection); + } } - return false; } @Override @@ -1959,6 +1956,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } else { addToOnlyBigramDictionary(suggestion, 1); } + // TODO: the following is fishy, because if !mHasUncommittedTypedChars we are + // going to log an empty string LatinImeLogger.logOnManualSuggestion(mWordComposer.getTypedWord().toString(), suggestion.toString(), index, suggestions.mWords); // Follow it with a space @@ -2399,8 +2398,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } } - public WordComposer getCurrentWord() { - return mWordComposer; + public boolean isAutoCapitalized() { + return mWordComposer.isAutoCapitalized(); } boolean isSoundOn() { diff --git a/java/src/com/android/inputmethod/latin/LatinImeLogger.java b/java/src/com/android/inputmethod/latin/LatinImeLogger.java index cbac4d31c..da5058dd4 100644 --- a/java/src/com/android/inputmethod/latin/LatinImeLogger.java +++ b/java/src/com/android/inputmethod/latin/LatinImeLogger.java @@ -16,11 +16,12 @@ package com.android.inputmethod.latin; -import com.android.inputmethod.keyboard.Keyboard; -import com.android.inputmethod.latin.Dictionary.DataType; - import android.content.Context; import android.content.SharedPreferences; +import android.view.inputmethod.EditorInfo; + +import com.android.inputmethod.keyboard.Keyboard; +import com.android.inputmethod.latin.Dictionary.DataType; import java.util.List; @@ -44,7 +45,7 @@ public class LatinImeLogger implements SharedPreferences.OnSharedPreferenceChang public static void logOnManualSuggestion( String before, String after, int position, List<CharSequence> suggestions) { - } + } public static void logOnAutoCorrection(String before, String after, int separatorCode) { } @@ -67,6 +68,9 @@ public class LatinImeLogger implements SharedPreferences.OnSharedPreferenceChang public static void logOnWarning(String warning) { } + public static void onStartInputView(EditorInfo editorInfo) { + } + public static void onStartSuggestion(CharSequence previousWords) { } diff --git a/java/src/com/android/inputmethod/latin/MoreSuggestions.java b/java/src/com/android/inputmethod/latin/MoreSuggestions.java index 9a59ef2e0..86072b64b 100644 --- a/java/src/com/android/inputmethod/latin/MoreSuggestions.java +++ b/java/src/com/android/inputmethod/latin/MoreSuggestions.java @@ -178,7 +178,7 @@ public class MoreSuggestions extends Keyboard { int minWidth, int maxRow) { final Keyboard keyboard = KeyboardSwitcher.getInstance().getLatinKeyboard(); final int xmlId = R.xml.kbd_suggestions_pane_template; - load(keyboard.mId.cloneWithNewXml(mResources.getResourceEntryName(xmlId), xmlId)); + load(keyboard.mId.cloneWithNewXml(xmlId)); mParams.mVerticalGap = mParams.mTopPadding = keyboard.mVerticalGap / 2; final int count = mParams.layout(suggestions, fromPos, maxWidth, minWidth, maxRow, diff --git a/java/src/com/android/inputmethod/latin/UserBigramDictionary.java b/java/src/com/android/inputmethod/latin/UserBigramDictionary.java index 3a1af9311..f80534cb5 100644 --- a/java/src/com/android/inputmethod/latin/UserBigramDictionary.java +++ b/java/src/com/android/inputmethod/latin/UserBigramDictionary.java @@ -159,7 +159,7 @@ public class UserBigramDictionary extends ExpandableDictionary { */ public int addBigrams(String word1, String word2) { // remove caps if second word is autocapitalized - if (mIme != null && mIme.getCurrentWord().isAutoCapitalized()) { + if (mIme != null && mIme.isAutoCapitalized()) { word2 = Character.toLowerCase(word2.charAt(0)) + word2.substring(1); } // Do not insert a word as a bigram of itself diff --git a/java/src/com/android/inputmethod/latin/UserUnigramDictionary.java b/java/src/com/android/inputmethod/latin/UserUnigramDictionary.java index de7cb5716..6af20c754 100644 --- a/java/src/com/android/inputmethod/latin/UserUnigramDictionary.java +++ b/java/src/com/android/inputmethod/latin/UserUnigramDictionary.java @@ -149,7 +149,7 @@ public class UserUnigramDictionary extends ExpandableDictionary { final int length = word.length(); // Don't add very short or very long words. if (length < 2 || length > getMaxWordLength()) return; - if (mIme.getCurrentWord().isAutoCapitalized()) { + if (mIme.isAutoCapitalized()) { // Remove caps before adding word = Character.toLowerCase(word.charAt(0)) + word.substring(1); } diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java index fcaf81cd5..8bbcff758 100644 --- a/java/src/com/android/inputmethod/latin/WordComposer.java +++ b/java/src/com/android/inputmethod/latin/WordComposer.java @@ -301,7 +301,7 @@ public class WordComposer { } /** - * @return the auto-correction for this world, or null if none. + * @return the auto-correction for this word, or null if none. */ public CharSequence getAutoCorrectionOrNull() { return mAutoCorrection; |