diff options
Diffstat (limited to 'java/src')
18 files changed, 254 insertions, 399 deletions
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java b/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java index dd43166af..bb5890ad2 100644 --- a/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java +++ b/java/src/com/android/inputmethod/accessibility/AccessibilityEntityProvider.java @@ -95,6 +95,7 @@ public class AccessibilityEntityProvider extends AccessibilityNodeProviderCompat event.setPackageName(mKeyboardView.getContext().getPackageName()); event.setClassName(key.getClass().getName()); event.getText().add(keyDescription); + event.setEnabled(true); final AccessibilityRecordCompat record = new AccessibilityRecordCompat(event); record.setSource(mKeyboardView, virtualViewId); @@ -171,6 +172,9 @@ public class AccessibilityEntityProvider extends AccessibilityNodeProviderCompat info.setSource(mKeyboardView, virtualViewId); info.setBoundsInScreen(boundsInScreen); info.setText(keyDescription); + info.setClickable(true); + info.setEnabled(true); + info.setLongClickable(true); } return info; diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java index 67e4e4a96..950b5e99b 100644 --- a/java/src/com/android/inputmethod/keyboard/Keyboard.java +++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java @@ -239,7 +239,7 @@ public class Keyboard { public final ArrayList<Key> mAltCodeKeysWhileTyping = new ArrayList<Key>(); public final KeyboardIconsSet mIconsSet = new KeyboardIconsSet(); - public KeyboardSet.KeysCache mKeysCache; + public KeyboardLayoutSet.KeysCache mKeysCache; public int mMostCommonKeyHeight = 0; public int mMostCommonKeyWidth = 0; @@ -637,7 +637,7 @@ public class Keyboard { params.GRID_HEIGHT = res.getInteger(R.integer.config_keyboard_grid_height); } - public void setAutoGenerate(KeyboardSet.KeysCache keysCache) { + public void setAutoGenerate(KeyboardLayoutSet.KeysCache keysCache) { mParams.mKeysCache = keysCache; } @@ -1061,8 +1061,8 @@ public class Keyboard { final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser), R.styleable.Keyboard_Case); try { - final boolean keyboardSetElementMatched = matchTypedValue(a, - R.styleable.Keyboard_Case_keyboardSetElement, id.mElementId, + final boolean keyboardLayoutSetElementMatched = matchTypedValue(a, + R.styleable.Keyboard_Case_keyboardLayoutSetElement, id.mElementId, KeyboardId.elementIdToName(id.mElementId)); final boolean modeMatched = matchTypedValue(a, R.styleable.Keyboard_Case_mode, id.mMode, KeyboardId.modeName(id.mMode)); @@ -1091,7 +1091,7 @@ public class Keyboard { R.styleable.Keyboard_Case_languageCode, id.mLocale.getLanguage()); final boolean countryCodeMatched = matchString(a, R.styleable.Keyboard_Case_countryCode, id.mLocale.getCountry()); - final boolean selected = keyboardSetElementMatched && modeMatched + final boolean selected = keyboardLayoutSetElementMatched && modeMatched && navigateNextMatched && navigatePreviousMatched && passwordInputMatched && clobberSettingsKeyMatched && shortcutKeyEnabledMatched && hasShortcutKeyMatched && languageSwitchKeyEnabledMatched @@ -1100,8 +1100,9 @@ public class Keyboard { 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_keyboardLayoutSetElement), + "keyboardLayoutSetElement"), textAttr(a.getString(R.styleable.Keyboard_Case_mode), "mode"), textAttr(a.getString(R.styleable.Keyboard_Case_imeAction), "imeAction"), @@ -1228,7 +1229,7 @@ public class Keyboard { private void endRow(Row row) { if (mCurrentRow == null) - throw new InflateException("orphant end row tag"); + throw new InflateException("orphan end row tag"); if (mRightEdgeKey != null) { mRightEdgeKey.markAsRightEdge(mParams); mRightEdgeKey = null; diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java index f9b6b72c7..51cd90549 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSet.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java @@ -28,14 +28,17 @@ import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputMethodSubtype; import com.android.inputmethod.compat.EditorInfoCompatUtils; -import com.android.inputmethod.keyboard.KeyboardSet.Params.ElementParams; +import com.android.inputmethod.keyboard.KeyboardLayoutSet.Params.ElementParams; +import com.android.inputmethod.keyboard.internal.KeySpecParser; import com.android.inputmethod.latin.InputTypeUtils; import com.android.inputmethod.latin.LatinIME; import com.android.inputmethod.latin.LatinImeLogger; +import com.android.inputmethod.latin.LocaleUtils; import com.android.inputmethod.latin.LocaleUtils.RunInLocale; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.StringUtils; import com.android.inputmethod.latin.SubtypeLocale; +import com.android.inputmethod.latin.SubtypeSwitcher; import com.android.inputmethod.latin.XmlParseUtils; import org.xmlpull.v1.XmlPullParser; @@ -47,18 +50,25 @@ import java.util.HashMap; import java.util.Locale; /** - * This class represents a set of keyboards. Each of them represents a different keyboard + * This class represents a set of keyboard layouts. Each of them represents a different keyboard * specific to a keyboard state, such as alphabet, symbols, and so on. Layouts in the same - * {@link KeyboardSet} are related to each other. - * A {@link KeyboardSet} needs to be created for each {@link android.view.inputmethod.EditorInfo}. + * {@link KeyboardLayoutSet} are related to each other. + * A {@link KeyboardLayoutSet} needs to be created for each + * {@link android.view.inputmethod.EditorInfo}. */ -public class KeyboardSet { - private static final String TAG = KeyboardSet.class.getSimpleName(); +public class KeyboardLayoutSet { + private static final String TAG = KeyboardLayoutSet.class.getSimpleName(); private static final boolean DEBUG_CACHE = LatinImeLogger.sDBG; - private static final String TAG_KEYBOARD_SET = "KeyboardSet"; + private static final String TAG_KEYBOARD_SET = "KeyboardLayoutSet"; private static final String TAG_ELEMENT = "Element"; + private static final String DEFAULT_KEYBOARD_LAYOUT_SET = "qwerty"; + private static final char KEYBOARD_LAYOUT_SET_LOCALE_DELIMITER = ':'; + private static final String KEYBOARD_LAYOUT_SET_RESOURCE_PREFIX = "xml/keyboard_layout_set_"; + private static final int DEFAULT_KEYBOARD_LAYOUT_SET_RESOURCE_ID = + R.xml.keyboard_layout_set_qwerty; + private final Context mContext; private final Params mParams; @@ -66,10 +76,10 @@ public class KeyboardSet { new HashMap<KeyboardId, SoftReference<Keyboard>>(); private static final KeysCache sKeysCache = new KeysCache(); - public static class KeyboardSetException extends RuntimeException { + public static class KeyboardLayoutSetException extends RuntimeException { public final KeyboardId mKeyboardId; - public KeyboardSetException(Throwable cause, KeyboardId keyboardId) { + public KeyboardLayoutSetException(Throwable cause, KeyboardId keyboardId) { super(cause); mKeyboardId = keyboardId; } @@ -98,6 +108,7 @@ public class KeyboardSet { } static class Params { + String mKeyboardLayoutSetName; int mMode; EditorInfo mEditorInfo; boolean mTouchPositionCorrectionEnabled; @@ -108,8 +119,8 @@ public class KeyboardSet { Locale mLocale; int mOrientation; int mWidth; - // KeyboardSet element id to element's parameters map. - final HashMap<Integer, ElementParams> mKeyboardSetElementIdToParamsMap = + // KeyboardLayoutSet element id to element's parameters map. + final HashMap<Integer, ElementParams> mKeyboardLayoutSetElementIdToParamsMap = new HashMap<Integer, ElementParams>(); static class ElementParams { @@ -123,43 +134,43 @@ public class KeyboardSet { sKeysCache.clear(); } - private KeyboardSet(Context context, Params params) { + private KeyboardLayoutSet(Context context, Params params) { mContext = context; mParams = params; } - public Keyboard getKeyboard(int baseKeyboardSetElementId) { - final int keyboardSetElementId; + public Keyboard getKeyboard(int baseKeyboardLayoutSetElementId) { + final int keyboardLayoutSetElementId; switch (mParams.mMode) { case KeyboardId.MODE_PHONE: - if (baseKeyboardSetElementId == KeyboardId.ELEMENT_SYMBOLS) { - keyboardSetElementId = KeyboardId.ELEMENT_PHONE_SYMBOLS; + if (baseKeyboardLayoutSetElementId == KeyboardId.ELEMENT_SYMBOLS) { + keyboardLayoutSetElementId = KeyboardId.ELEMENT_PHONE_SYMBOLS; } else { - keyboardSetElementId = KeyboardId.ELEMENT_PHONE; + keyboardLayoutSetElementId = KeyboardId.ELEMENT_PHONE; } break; case KeyboardId.MODE_NUMBER: case KeyboardId.MODE_DATE: case KeyboardId.MODE_TIME: case KeyboardId.MODE_DATETIME: - keyboardSetElementId = KeyboardId.ELEMENT_NUMBER; + keyboardLayoutSetElementId = KeyboardId.ELEMENT_NUMBER; break; default: - keyboardSetElementId = baseKeyboardSetElementId; + keyboardLayoutSetElementId = baseKeyboardLayoutSetElementId; break; } - ElementParams elementParams = mParams.mKeyboardSetElementIdToParamsMap.get( - keyboardSetElementId); + ElementParams elementParams = mParams.mKeyboardLayoutSetElementIdToParamsMap.get( + keyboardLayoutSetElementId); if (elementParams == null) { - elementParams = mParams.mKeyboardSetElementIdToParamsMap.get( + elementParams = mParams.mKeyboardLayoutSetElementIdToParamsMap.get( KeyboardId.ELEMENT_ALPHABET); } - final KeyboardId id = getKeyboardId(keyboardSetElementId); + final KeyboardId id = getKeyboardId(keyboardLayoutSetElementId); try { return getKeyboard(mContext, elementParams, id); } catch (RuntimeException e) { - throw new KeyboardSetException(e, id); + throw new KeyboardLayoutSetException(e, id); } } @@ -199,21 +210,46 @@ public class KeyboardSet { return keyboard; } - // Note: The keyboard for each locale, shift state, and mode are represented as KeyboardSet - // element id that is a key in keyboard_set.xml. Also that file specifies which XML layout - // should be used for each keyboard. The KeyboardId is an internal key for Keyboard object. - private KeyboardId getKeyboardId(int keyboardSetElementId) { + // Note: The keyboard for each locale, shift state, and mode are represented as + // KeyboardLayoutSet element id that is a key in keyboard_set.xml. Also that file specifies + // which XML layout should be used for each keyboard. The KeyboardId is an internal key for + // Keyboard object. + private KeyboardId getKeyboardId(int keyboardLayoutSetElementId) { final Params params = mParams; - final boolean isSymbols = (keyboardSetElementId == KeyboardId.ELEMENT_SYMBOLS - || keyboardSetElementId == KeyboardId.ELEMENT_SYMBOLS_SHIFTED); + final boolean isSymbols = (keyboardLayoutSetElementId == KeyboardId.ELEMENT_SYMBOLS + || keyboardLayoutSetElementId == KeyboardId.ELEMENT_SYMBOLS_SHIFTED); final boolean noLanguage = params.mLocale.getLanguage().equals(SubtypeLocale.NO_LANGUAGE); final boolean voiceKeyEnabled = params.mVoiceKeyEnabled && !noLanguage; final boolean hasShortcutKey = voiceKeyEnabled && (isSymbols != params.mVoiceKeyOnMain); - return new KeyboardId(keyboardSetElementId, params.mLocale, params.mOrientation, + return new KeyboardId(keyboardLayoutSetElementId, params.mLocale, params.mOrientation, params.mWidth, params.mMode, params.mEditorInfo, params.mNoSettingsKey, voiceKeyEnabled, hasShortcutKey, params.mLanguageSwitchKeyEnabled); } + private static String getKeyboardLayoutSetName(InputMethodSubtype subtype) { + final String keyboardLayoutSet = subtype.getExtraValueOf( + LatinIME.SUBTYPE_EXTRA_VALUE_KEYBOARD_LAYOUT_SET); + // TODO: Remove this null check when InputMethodManager.getCurrentInputMethodSubtype is + // fixed. + if (keyboardLayoutSet == null) return DEFAULT_KEYBOARD_LAYOUT_SET; + final int pos = keyboardLayoutSet.indexOf(KEYBOARD_LAYOUT_SET_LOCALE_DELIMITER); + return (pos > 0) ? keyboardLayoutSet.substring(0, pos) : keyboardLayoutSet; + } + + public static String getKeyboardLayoutSetLocaleString(InputMethodSubtype subtype) { + final String keyboardLayoutSet = subtype.getExtraValueOf( + LatinIME.SUBTYPE_EXTRA_VALUE_KEYBOARD_LAYOUT_SET); + // TODO: Remove this null check when InputMethodManager.getCurrentInputMethodSubtype is + // fixed. + if (keyboardLayoutSet == null) return subtype.getLocale(); + final int pos = keyboardLayoutSet.indexOf(KEYBOARD_LAYOUT_SET_LOCALE_DELIMITER); + return (pos > 0) ? keyboardLayoutSet.substring(pos + 1) : subtype.getLocale(); + } + + public static Locale getKeyboardLayoutSetLocale(InputMethodSubtype subtype) { + return LocaleUtils.constructLocaleFromString(getKeyboardLayoutSetLocaleString(subtype)); + } + public static class Builder { private final Context mContext; private final String mPackageName; @@ -244,7 +280,6 @@ public class KeyboardSet { } public Builder setSubtype(InputMethodSubtype subtype) { - final Locale inputLocale = SubtypeLocale.getSubtypeLocale(subtype); final boolean asciiCapable = subtype.containsExtraValueKey( LatinIME.SUBTYPE_EXTRA_VALUE_ASCII_CAPABLE); final boolean deprecatedForceAscii = StringUtils.inPrivateImeOptions( @@ -252,8 +287,12 @@ public class KeyboardSet { final boolean forceAscii = EditorInfoCompatUtils.hasFlagForceAscii( mParams.mEditorInfo.imeOptions) || deprecatedForceAscii; - mParams.mLocale = (forceAscii && !asciiCapable) - ? SubtypeLocale.LOCALE_NO_LANGUAGE_QWERTY : inputLocale; + final InputMethodSubtype keyboardSubtype = (forceAscii && !asciiCapable) + ? SubtypeSwitcher.getInstance().getNoLanguageSubtype() + : subtype; + mParams.mLocale = getKeyboardLayoutSetLocale(keyboardSubtype); + mParams.mKeyboardLayoutSetName = KEYBOARD_LAYOUT_SET_RESOURCE_PREFIX + + getKeyboardLayoutSetName(keyboardSubtype); return this; } @@ -275,31 +314,31 @@ public class KeyboardSet { mParams.mTouchPositionCorrectionEnabled = enabled; } - public KeyboardSet build() { + public KeyboardLayoutSet build() { if (mParams.mOrientation == Configuration.ORIENTATION_UNDEFINED) throw new RuntimeException("Screen geometry is not specified"); if (mParams.mLocale == null) - throw new RuntimeException("KeyboardSet subtype is not specified"); - + throw new RuntimeException("KeyboardLayoutSet subtype is not specified"); + final String keyboardLayoutSetName = mParams.mKeyboardLayoutSetName; + final int xmlId = KeySpecParser.getResourceId( + mResources, keyboardLayoutSetName, DEFAULT_KEYBOARD_LAYOUT_SET_RESOURCE_ID); final RunInLocale<Void> job = new RunInLocale<Void>() { @Override protected Void job(Resources res) { try { - parseKeyboardSet(res, R.xml.keyboard_set); + parseKeyboardLayoutSet(res, xmlId); } catch (Exception e) { - throw new RuntimeException(e.getMessage() + " in " - + res.getResourceName(R.xml.keyboard_set) - + " of locale " + mParams.mLocale); + throw new RuntimeException(e.getMessage() + " in " + keyboardLayoutSetName); } return null; } }; job.runInLocale(mResources, mParams.mLocale); - return new KeyboardSet(mContext, mParams); + return new KeyboardLayoutSet(mContext, mParams); } - private void parseKeyboardSet(Resources res, int resId) throws XmlPullParserException, - IOException { + private void parseKeyboardLayoutSet(Resources res, int resId) + throws XmlPullParserException, IOException { final XmlResourceParser parser = res.getXml(resId); try { int event; @@ -307,7 +346,7 @@ public class KeyboardSet { if (event == XmlPullParser.START_TAG) { final String tag = parser.getName(); if (TAG_KEYBOARD_SET.equals(tag)) { - parseKeyboardSetContent(parser); + parseKeyboardLayoutSetContent(parser); } else { throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEYBOARD_SET); } @@ -318,14 +357,14 @@ public class KeyboardSet { } } - private void parseKeyboardSetContent(XmlPullParser parser) throws XmlPullParserException, - IOException { + private void parseKeyboardLayoutSetContent(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); + parseKeyboardLayoutSetElement(parser); } else { throw new XmlParseUtils.IllegalStartTag(parser, TAG_KEYBOARD_SET); } @@ -340,27 +379,28 @@ public class KeyboardSet { } } - private void parseKeyboardSetElement(XmlPullParser parser) throws XmlPullParserException, - IOException { + private void parseKeyboardLayoutSetElement(XmlPullParser parser) + throws XmlPullParserException, IOException { final TypedArray a = mResources.obtainAttributes(Xml.asAttributeSet(parser), - R.styleable.KeyboardSet_Element); + R.styleable.KeyboardLayoutSet_Element); try { XmlParseUtils.checkAttributeExists(a, - R.styleable.KeyboardSet_Element_elementName, "elementName", + R.styleable.KeyboardLayoutSet_Element_elementName, "elementName", TAG_ELEMENT, parser); XmlParseUtils.checkAttributeExists(a, - R.styleable.KeyboardSet_Element_elementKeyboard, "elementKeyboard", + R.styleable.KeyboardLayoutSet_Element_elementKeyboard, "elementKeyboard", TAG_ELEMENT, parser); XmlParseUtils.checkEndTag(TAG_ELEMENT, parser); final ElementParams elementParams = new ElementParams(); final int elementName = a.getInt( - R.styleable.KeyboardSet_Element_elementName, 0); + R.styleable.KeyboardLayoutSet_Element_elementName, 0); elementParams.mKeyboardXmlId = a.getResourceId( - R.styleable.KeyboardSet_Element_elementKeyboard, 0); + R.styleable.KeyboardLayoutSet_Element_elementKeyboard, 0); elementParams.mProximityCharsCorrectionEnabled = a.getBoolean( - R.styleable.KeyboardSet_Element_enableProximityCharsCorrection, false); - mParams.mKeyboardSetElementIdToParamsMap.put(elementName, elementParams); + R.styleable.KeyboardLayoutSet_Element_enableProximityCharsCorrection, + false); + mParams.mKeyboardLayoutSetElementIdToParamsMap.put(elementName, elementParams); } finally { a.recycle(); } diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index 3bc63f3dd..e50d922ea 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -27,7 +27,7 @@ import android.view.View; import android.view.inputmethod.EditorInfo; import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy; -import com.android.inputmethod.keyboard.KeyboardSet.KeyboardSetException; +import com.android.inputmethod.keyboard.KeyboardLayoutSet.KeyboardLayoutSetException; import com.android.inputmethod.keyboard.PointerTracker.TimerProxy; import com.android.inputmethod.keyboard.internal.KeyboardState; import com.android.inputmethod.latin.DebugSettings; @@ -76,7 +76,7 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions { private KeyboardState mState; - private KeyboardSet mKeyboardSet; + private KeyboardLayoutSet mKeyboardLayoutSet; /** mIsAutoCorrectionActive indicates that auto corrected word will be input instead of * what user actually typed. */ @@ -129,12 +129,13 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions { if (mKeyboardTheme.mThemeId != keyboardTheme.mThemeId) { mKeyboardTheme = keyboardTheme; mThemeContext = new ContextThemeWrapper(context, keyboardTheme.mStyleId); - KeyboardSet.clearKeyboardCache(); + KeyboardLayoutSet.clearKeyboardCache(); } } public void loadKeyboard(EditorInfo editorInfo, SettingsValues settingsValues) { - final KeyboardSet.Builder builder = new KeyboardSet.Builder(mThemeContext, editorInfo); + final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder( + mThemeContext, editorInfo); builder.setScreenGeometry(mThemeContext.getResources().getConfiguration().orientation, mThemeContext.getResources().getDisplayMetrics().widthPixels); builder.setSubtype(mSubtypeSwitcher.getCurrentSubtype()); @@ -142,10 +143,10 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions { settingsValues.isVoiceKeyEnabled(editorInfo), settingsValues.isVoiceKeyOnMain(), settingsValues.isLanguageSwitchKeyEnabled(mThemeContext)); - mKeyboardSet = builder.build(); + mKeyboardLayoutSet = builder.build(); try { mState.onLoadKeyboard(mResources.getString(R.string.layout_switch_back_symbols)); - } catch (KeyboardSetException e) { + } catch (KeyboardLayoutSetException e) { Log.w(TAG, "loading keyboard failed: " + e.mKeyboardId, e.getCause()); LatinImeLogger.logOnException(e.mKeyboardId.toString(), e.getCause()); return; @@ -214,43 +215,43 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions { // Implements {@link KeyboardState.SwitchActions}. @Override public void setAlphabetKeyboard() { - setKeyboard(mKeyboardSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET)); + setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET)); } // Implements {@link KeyboardState.SwitchActions}. @Override public void setAlphabetManualShiftedKeyboard() { - setKeyboard(mKeyboardSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED)); + setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED)); } // Implements {@link KeyboardState.SwitchActions}. @Override public void setAlphabetAutomaticShiftedKeyboard() { - setKeyboard(mKeyboardSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED)); + setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED)); } // Implements {@link KeyboardState.SwitchActions}. @Override public void setAlphabetShiftLockedKeyboard() { - setKeyboard(mKeyboardSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED)); + setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED)); } // Implements {@link KeyboardState.SwitchActions}. @Override public void setAlphabetShiftLockShiftedKeyboard() { - setKeyboard(mKeyboardSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED)); + setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED)); } // Implements {@link KeyboardState.SwitchActions}. @Override public void setSymbolsKeyboard() { - setKeyboard(mKeyboardSet.getKeyboard(KeyboardId.ELEMENT_SYMBOLS)); + setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_SYMBOLS)); } // Implements {@link KeyboardState.SwitchActions}. @Override public void setSymbolsShiftedKeyboard() { - setKeyboard(mKeyboardSet.getKeyboard(KeyboardId.ELEMENT_SYMBOLS_SHIFTED)); + setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_SYMBOLS_SHIFTED)); } // Implements {@link KeyboardState.SwitchActions}. diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java index 0aba813b2..4abd887f0 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java @@ -318,7 +318,7 @@ public class KeySpecParser { } } - private static int getResourceId(Resources res, String name, int packageNameResId) { + public static int getResourceId(Resources res, String name, int packageNameResId) { String packageName = res.getResourcePackageName(packageNameResId); int resId = res.getIdentifier(name, null, packageName); if (resId == 0) { diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index 92019c0ed..cc7540e4e 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -56,36 +56,7 @@ public class BinaryDictionary extends Dictionary { private final int[] mScores = new int[MAX_WORDS]; private final int[] mBigramScores = new int[MAX_BIGRAMS]; - public static final Flag FLAG_REQUIRES_GERMAN_UMLAUT_PROCESSING = - new Flag(R.bool.config_require_umlaut_processing, 0x1); - public static final Flag FLAG_REQUIRES_FRENCH_LIGATURES_PROCESSING = - new Flag(R.bool.config_require_ligatures_processing, 0x4); - - // FULL_EDIT_DISTANCE is a flag that forces the dictionary to use full words - // when computing edit distance, instead of the default behavior of stopping - // the evaluation at the size the user typed. - public static final Flag FLAG_USE_FULL_EDIT_DISTANCE = new Flag(0x2); - - // Can create a new flag from extravalue : - // public static final Flag FLAG_MYFLAG = - // new Flag("my_flag", 0x02); - - // ALL_CONFIG_FLAGS is a collection of flags that enable reading all flags from configuration. - // This is but a mask - it does not mean the flags will be on, only that the configuration - // will be read for this particular flag. - public static final Flag[] ALL_CONFIG_FLAGS = { - // Here should reside all flags that trigger some special processing - // These *must* match the definition in UnigramDictionary enum in - // unigram_dictionary.h so please update both at the same time. - // Please note that flags created with a resource are of type CONFIG while flags - // created with a string are of type EXTRAVALUE. These behave like masks, and the - // actual value will be read from the configuration/extra value at run time for - // the configuration at dictionary creation time. - FLAG_REQUIRES_GERMAN_UMLAUT_PROCESSING, - FLAG_REQUIRES_FRENCH_LIGATURES_PROCESSING, - }; - - private int mFlags = 0; + private final boolean mUseFullEditDistance; /** * Constructor for the binary dictionary. This is supposed to be called from the @@ -95,26 +66,16 @@ public class BinaryDictionary extends Dictionary { * @param filename the name of the file to read through native code. * @param offset the offset of the dictionary data within the file. * @param length the length of the binary data. - * @param flagArray the flags to limit the dictionary to, or null for default. + * @param useFullEditDistance whether to use the full edit distance in suggestions */ public BinaryDictionary(final Context context, - final String filename, final long offset, final long length, final Flag[] flagArray, - Locale locale) { + final String filename, final long offset, final long length, + final boolean useFullEditDistance, final Locale locale) { // Note: at the moment a binary dictionary is always of the "main" type. // Initializing this here will help transitioning out of the scheme where // the Suggest class knows everything about every single dictionary. mDicTypeId = Suggest.DIC_MAIN; - // TODO: Stop relying on the state of SubtypeSwitcher, get it as a parameter - final RunInLocale<Void> job = new RunInLocale<Void>() { - @Override - protected Void job(Resources res) { - // TODO: remove this when all flags are moved to the native code - mFlags = Flag.initFlags(null == flagArray ? ALL_CONFIG_FLAGS : flagArray, context, - SubtypeSwitcher.getInstance()); - return null; - } - }; - job.runInLocale(context.getResources(), locale); + mUseFullEditDistance = useFullEditDistance; loadDictionary(filename, offset, length); } @@ -127,8 +88,8 @@ public class BinaryDictionary extends Dictionary { private native void closeNative(long dict); private native boolean isValidWordNative(long dict, char[] word, int wordLength); private native int getSuggestionsNative(long dict, long proximityInfo, int[] xCoordinates, - int[] yCoordinates, int[] inputCodes, int codesSize, int flags, char[] outputChars, - int[] scores); + int[] yCoordinates, int[] inputCodes, int codesSize, boolean useFullEditDistance, + char[] outputChars, int[] scores); private native int getBigramsNative(long dict, char[] prevWord, int prevWordLength, int[] inputCodes, int inputCodesLength, char[] outputChars, int[] scores, int maxWordLength, int maxBigrams); @@ -220,7 +181,7 @@ public class BinaryDictionary extends Dictionary { return getSuggestionsNative( mNativeDict, proximityInfo.getNativeProximityInfo(), codes.getXCoordinates(), codes.getYCoordinates(), mInputCodes, codesSize, - mFlags, outputChars, scores); + mUseFullEditDistance, outputChars, scores); } public static double calcNormalizedScore(String before, String after, int score) { diff --git a/java/src/com/android/inputmethod/latin/DictionaryFactory.java b/java/src/com/android/inputmethod/latin/DictionaryFactory.java index 7a59d80f1..fedb45407 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFactory.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFactory.java @@ -43,11 +43,11 @@ public class DictionaryFactory { * @param context application context for reading resources * @param locale the locale for which to create the dictionary * @param fallbackResId the id of the resource to use as a fallback if no pack is found - * @param flagArray an array of flags to use + * @param useFullEditDistance whether to use the full edit distance in suggestions * @return an initialized instance of DictionaryCollection */ public static DictionaryCollection createDictionaryFromManager(final Context context, - final Locale locale, final int fallbackResId, final Flag[] flagArray) { + final Locale locale, final int fallbackResId, final boolean useFullEditDistance) { if (null == locale) { Log.e(TAG, "No locale defined for dictionary"); return new DictionaryCollection(createBinaryDictionary(context, fallbackResId, locale)); @@ -59,8 +59,8 @@ public class DictionaryFactory { if (null != assetFileList) { for (final AssetFileAddress f : assetFileList) { final BinaryDictionary binaryDictionary = - new BinaryDictionary(context, f.mFilename, f.mOffset, f.mLength, flagArray, - locale); + new BinaryDictionary(context, f.mFilename, f.mOffset, f.mLength, + useFullEditDistance, locale); if (binaryDictionary.isValidDictionary()) { dictList.add(binaryDictionary); } @@ -86,7 +86,8 @@ public class DictionaryFactory { */ public static DictionaryCollection createDictionaryFromManager(final Context context, final Locale locale, final int fallbackResId) { - return createDictionaryFromManager(context, locale, fallbackResId, null); + return createDictionaryFromManager(context, locale, fallbackResId, + false /* useFullEditDistance */); } /** @@ -119,8 +120,8 @@ public class DictionaryFactory { Log.e(TAG, "sourceDir is not a file: " + sourceDir); return null; } - return new BinaryDictionary(context, - sourceDir, afd.getStartOffset(), afd.getLength(), null, locale); + return new BinaryDictionary(context, sourceDir, afd.getStartOffset(), afd.getLength(), + false /* useFullEditDistance */, locale); } catch (android.content.res.Resources.NotFoundException e) { Log.e(TAG, "Could not find the resource. resId=" + resId); return null; @@ -141,14 +142,14 @@ public class DictionaryFactory { * @param dictionary the file to read * @param startOffset the offset in the file where the data starts * @param length the length of the data - * @param flagArray the flags to use with this data for testing + * @param useFullEditDistance whether to use the full edit distance in suggestions * @return the created dictionary, or null. */ public static Dictionary createDictionaryForTest(Context context, File dictionary, - long startOffset, long length, Flag[] flagArray) { + long startOffset, long length, final boolean useFullEditDistance, Locale locale) { if (dictionary.isFile()) { return new BinaryDictionary(context, dictionary.getAbsolutePath(), startOffset, length, - flagArray, null); + useFullEditDistance, locale); } else { Log.e(TAG, "Could not find the file. path=" + dictionary.getAbsolutePath()); return null; diff --git a/java/src/com/android/inputmethod/latin/Flag.java b/java/src/com/android/inputmethod/latin/Flag.java deleted file mode 100644 index 4ba6c80f5..000000000 --- a/java/src/com/android/inputmethod/latin/Flag.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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.latin; - -import android.content.Context; -import android.content.res.Resources; - -public class Flag { - public final String mName; - public final int mResource; - public final int mMask; - public final int mSource; - - private static final int SOURCE_CONFIG = 1; - private static final int SOURCE_EXTRAVALUE = 2; - private static final int SOURCE_PARAM = 3; - - public Flag(int resourceId, int mask) { - mName = null; - mResource = resourceId; - mSource = SOURCE_CONFIG; - mMask = mask; - } - - public Flag(String name, int mask) { - mName = name; - mResource = 0; - mSource = SOURCE_EXTRAVALUE; - mMask = mask; - } - - public Flag(int mask) { - mName = null; - mResource = 0; - mSource = SOURCE_PARAM; - mMask = mask; - } - - // If context/switcher are null, set all related flags in flagArray to on. - public static int initFlags(Flag[] flagArray, Context context, SubtypeSwitcher switcher) { - int flags = 0; - final Resources res = null == context ? null : context.getResources(); - for (Flag entry : flagArray) { - switch (entry.mSource) { - case Flag.SOURCE_CONFIG: - if (res == null || res.getBoolean(entry.mResource)) - flags |= entry.mMask; - break; - case Flag.SOURCE_EXTRAVALUE: - if (switcher == null || - switcher.currentSubtypeContainsExtraValueKey(entry.mName)) - flags |= entry.mMask; - break; - case Flag.SOURCE_PARAM: - flags |= entry.mMask; - break; - } - } - return flags; - } -} diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 8a26d2b9e..f5c09974e 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -115,16 +115,15 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen public static final String IME_OPTION_FORCE_ASCII = "forceAscii"; /** - * The subtype extra value used to indicate that the subtype keyboard layout is capable for - * typing ASCII characters. + * The subtype extra value used to indicate that the subtype keyboard layout set name. */ - public static final String SUBTYPE_EXTRA_VALUE_ASCII_CAPABLE = "AsciiCapable"; + public static final String SUBTYPE_EXTRA_VALUE_KEYBOARD_LAYOUT_SET = "KeyboardLayoutSet"; /** - * The subtype extra value used to indicate that the subtype keyboard layout should be loaded - * from the specified locale. + * The subtype extra value used to indicate that the subtype keyboard layout is capable for + * typing ASCII characters. */ - public static final String SUBTYPE_EXTRA_VALUE_KEYBOARD_LOCALE = "KeyboardLocale"; + public static final String SUBTYPE_EXTRA_VALUE_ASCII_CAPABLE = "AsciiCapable"; private static final int EXTENDED_TOUCHABLE_REGION_HEIGHT = 100; diff --git a/java/src/com/android/inputmethod/latin/StringUtils.java b/java/src/com/android/inputmethod/latin/StringUtils.java index 7000e4633..649cd650a 100644 --- a/java/src/com/android/inputmethod/latin/StringUtils.java +++ b/java/src/com/android/inputmethod/latin/StringUtils.java @@ -22,6 +22,7 @@ import android.view.inputmethod.EditorInfo; import com.android.inputmethod.keyboard.Keyboard; import java.util.ArrayList; +import java.util.Locale; public class StringUtils { private StringUtils() { @@ -45,7 +46,7 @@ public class StringUtils { return text.codePointCount(0, text.length()); } - public static boolean containsInCsv(String key, String csv) { + private static boolean containsInCsv(String key, String csv) { if (csv == null) return false; for (String option : csv.split(",")) { @@ -128,7 +129,7 @@ public class StringUtils { /** * Remove duplicates from an array of strings. * - * This method will always keep the first occurence of all strings at their position + * This method will always keep the first occurrence of all strings at their position * in the array, removing the subsequent ones. */ public static void removeDupes(final ArrayList<CharSequence> suggestions) { @@ -149,4 +150,20 @@ public class StringUtils { i++; } } + + public static String toTitleCase(String s, Locale locale) { + if (s.length() <= 1) { + // TODO: is this really correct? Shouldn't this be s.toUpperCase()? + return s; + } + // TODO: fix the bugs below + // - This does not work for Greek, because it returns upper case instead of title case. + // - It does not work for Serbian, because it fails to account for the "lj" character, + // which should be "Lj" in title case and "LJ" in upper case. + // - It does not work for Dutch, because it fails to account for the "ij" digraph, which + // are two different characters but both should be capitalized as "IJ" as if they were + // a single letter. + // - It also does not work with unicode surrogate code points. + return s.toUpperCase(locale).charAt(0) + s.substring(1); + } } diff --git a/java/src/com/android/inputmethod/latin/SubtypeLocale.java b/java/src/com/android/inputmethod/latin/SubtypeLocale.java index fac74f0b5..2bc22a6f9 100644 --- a/java/src/com/android/inputmethod/latin/SubtypeLocale.java +++ b/java/src/com/android/inputmethod/latin/SubtypeLocale.java @@ -18,7 +18,7 @@ package com.android.inputmethod.latin; import android.content.Context; import android.content.res.Resources; -import android.view.inputmethod.InputMethodSubtype; + import java.util.Locale; @@ -69,10 +69,10 @@ public class SubtypeLocale { } final String value = lookupExceptionalLocale(key); if (value == null) { - return toTitleCase(locale.getDisplayName(locale), locale); + return StringUtils.toTitleCase(locale.getDisplayName(locale), locale); } if (value.indexOf("%s") >= 0) { - final String languageName = toTitleCase(locale.getDisplayLanguage(locale), locale); + final String languageName = StringUtils.toTitleCase(locale.getDisplayLanguage(locale), locale); return String.format(value, languageName); } return value; @@ -88,7 +88,7 @@ public class SubtypeLocale { if (NO_LANGUAGE.equals(locale.getLanguage())) { return lookupExceptionalLocale(locale.getCountry()); } else { - return toTitleCase(locale.getDisplayLanguage(locale), locale); + return StringUtils.toTitleCase(locale.getDisplayLanguage(locale), locale); } } @@ -102,33 +102,7 @@ public class SubtypeLocale { if (NO_LANGUAGE.equals(locale.getLanguage())) { return locale.getCountry(); } else { - return toTitleCase(locale.getLanguage(), locale); - } - } - - public static String toTitleCase(String s, Locale locale) { - if (s.length() <= 1) { - // TODO: is this really correct? Shouldn't this be s.toUpperCase()? - return s; + return StringUtils.toTitleCase(locale.getLanguage(), locale); } - // TODO: fix the bugs below - // - This does not work for Greek, because it returns upper case instead of title case. - // - It does not work for Serbian, because it fails to account for the "lj" character, - // which should be "Lj" in title case and "LJ" in upper case. - // - It does not work for Dutch, because it fails to account for the "ij" digraph, which - // are two different characters but both should be capitalized as "IJ" as if they were - // a single letter. - // - It also does not work with unicode surrogate code points. - return s.toUpperCase(locale).charAt(0) + s.substring(1); - } - - public static String getSubtypeLocaleString(InputMethodSubtype subtype) { - final String keyboardLocale = subtype.getExtraValueOf( - LatinIME.SUBTYPE_EXTRA_VALUE_KEYBOARD_LOCALE); - return keyboardLocale != null ? keyboardLocale : subtype.getLocale(); - } - - public static Locale getSubtypeLocale(InputMethodSubtype subtype) { - return LocaleUtils.constructLocaleFromString(getSubtypeLocaleString(subtype)); } } diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java index 3ed7f8700..6612c24cd 100644 --- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java +++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java @@ -18,11 +18,9 @@ package com.android.inputmethod.latin; import android.content.Context; import android.content.Intent; -import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Configuration; import android.content.res.Resources; -import android.graphics.drawable.Drawable; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.AsyncTask; @@ -33,6 +31,7 @@ import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodSubtype; import com.android.inputmethod.compat.InputMethodManagerCompatWrapper; +import com.android.inputmethod.keyboard.KeyboardLayoutSet; import com.android.inputmethod.keyboard.KeyboardSwitcher; import java.util.ArrayList; @@ -68,6 +67,7 @@ public class SubtypeSwitcher { private InputMethodInfo mShortcutInputMethodInfo; private InputMethodSubtype mShortcutSubtype; private List<InputMethodSubtype> mAllEnabledSubtypesOfCurrentInputMethod; + private InputMethodSubtype mNoLanguageSubtype; // Note: This variable is always non-null after {@link #initialize(LatinIME)}. private InputMethodSubtype mCurrentSubtype; private Locale mSystemLocale; @@ -104,6 +104,8 @@ public class SubtypeSwitcher { mInputLocaleStr = null; mCurrentSubtype = mImm.getCurrentInputMethodSubtype(); mAllEnabledSubtypesOfCurrentInputMethod = null; + mNoLanguageSubtype = SubtypeUtils.findSubtypeByKeyboardLayoutSetLocale( + service, SubtypeLocale.LOCALE_NO_LANGUAGE_QWERTY); final NetworkInfo info = mConnectivityManager.getActiveNetworkInfo(); mIsNetworkConnected = (info != null && info.isConnected()); @@ -133,7 +135,7 @@ public class SubtypeSwitcher { mEnabledLanguagesOfCurrentInputMethod.clear(); mEnabledKeyboardSubtypesOfCurrentInputMethod.clear(); for (InputMethodSubtype ims : mAllEnabledSubtypesOfCurrentInputMethod) { - final String locale = getSubtypeLocale(ims); + final String locale = KeyboardLayoutSet.getKeyboardLayoutSetLocaleString(ims); final String mode = ims.getMode(); mLocaleSplitter.setString(locale); if (mLocaleSplitter.hasNext()) { @@ -162,7 +164,8 @@ public class SubtypeSwitcher { Log.d(TAG, "Update shortcut IME from : " + (mShortcutInputMethodInfo == null ? "<null>" : mShortcutInputMethodInfo.getId()) + ", " - + (mShortcutSubtype == null ? "<null>" : (getSubtypeLocale(mShortcutSubtype) + + (mShortcutSubtype == null ? "<null>" : ( + KeyboardLayoutSet.getKeyboardLayoutSetLocaleString(mShortcutSubtype) + ", " + mShortcutSubtype.getMode()))); } // TODO: Update an icon for shortcut IME @@ -184,20 +187,15 @@ public class SubtypeSwitcher { Log.d(TAG, "Update shortcut IME to : " + (mShortcutInputMethodInfo == null ? "<null>" : mShortcutInputMethodInfo.getId()) + ", " - + (mShortcutSubtype == null ? "<null>" : (getSubtypeLocale(mShortcutSubtype) + + (mShortcutSubtype == null ? "<null>" : ( + KeyboardLayoutSet.getKeyboardLayoutSetLocaleString(mShortcutSubtype) + ", " + mShortcutSubtype.getMode()))); } } - private static String getSubtypeLocale(InputMethodSubtype subtype) { - final String keyboardLocale = subtype.getExtraValueOf( - LatinIME.SUBTYPE_EXTRA_VALUE_KEYBOARD_LOCALE); - return keyboardLocale != null ? keyboardLocale : subtype.getLocale(); - } - // Update the current subtype. LatinIME.onCurrentInputMethodSubtypeChanged calls this function. public void updateSubtype(InputMethodSubtype newSubtype) { - final String newLocale = getSubtypeLocale(newSubtype); + final String newLocale = KeyboardLayoutSet.getKeyboardLayoutSetLocaleString(newSubtype); final String newMode = newSubtype.getMode(); final String oldMode = getCurrentSubtypeMode(); if (DBG) { @@ -291,44 +289,6 @@ public class SubtypeSwitcher { }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } - public Drawable getShortcutIcon() { - return getSubtypeIcon(mShortcutInputMethodInfo, mShortcutSubtype); - } - - private Drawable getSubtypeIcon(InputMethodInfo imi, InputMethodSubtype subtype) { - final PackageManager pm = mService.getPackageManager(); - if (imi != null) { - final String imiPackageName = imi.getPackageName(); - if (DBG) { - Log.d(TAG, "Update icons of IME: " + imiPackageName + "," - + getSubtypeLocale(subtype) + "," + subtype.getMode()); - } - if (subtype != null) { - return pm.getDrawable(imiPackageName, subtype.getIconResId(), - imi.getServiceInfo().applicationInfo); - } else if (imi.getSubtypeCount() > 0 && imi.getSubtypeAt(0) != null) { - return pm.getDrawable(imiPackageName, - imi.getSubtypeAt(0).getIconResId(), - imi.getServiceInfo().applicationInfo); - } else { - try { - return pm.getApplicationInfo(imiPackageName, 0).loadIcon(pm); - } catch (PackageManager.NameNotFoundException e) { - Log.w(TAG, "IME can't be found: " + imiPackageName); - } - } - } - return null; - } - - private static boolean contains(String[] hay, String needle) { - for (String element : hay) { - if (element.equals(needle)) - return true; - } - return false; - } - public boolean isShortcutImeEnabled() { if (mShortcutInputMethodInfo == null) { return false; @@ -351,7 +311,7 @@ public class SubtypeSwitcher { return false; if (mShortcutSubtype == null) return true; - if (contains(mShortcutSubtype.getExtraValue().split(","), + if (mShortcutSubtype.containsExtraValueKey( SUBTYPE_EXTRAVALUE_REQUIRE_NETWORK_CONNECTIVITY)) { return mIsNetworkConnected; } @@ -438,4 +398,8 @@ public class SubtypeSwitcher { public InputMethodSubtype getCurrentSubtype() { return mCurrentSubtype; } + + public InputMethodSubtype getNoLanguageSubtype() { + return mNoLanguageSubtype; + } } diff --git a/java/src/com/android/inputmethod/latin/SubtypeUtils.java b/java/src/com/android/inputmethod/latin/SubtypeUtils.java index 2c5d58200..a747c9ad7 100644 --- a/java/src/com/android/inputmethod/latin/SubtypeUtils.java +++ b/java/src/com/android/inputmethod/latin/SubtypeUtils.java @@ -21,9 +21,11 @@ import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodSubtype; import com.android.inputmethod.compat.InputMethodManagerCompatWrapper; +import com.android.inputmethod.keyboard.KeyboardLayoutSet; import java.util.Collections; import java.util.List; +import java.util.Locale; public class SubtypeUtils { private SubtypeUtils() { @@ -129,4 +131,18 @@ public class SubtypeUtils { } throw new RuntimeException("Can not find input method id for " + packageName); } + + public static InputMethodSubtype findSubtypeByKeyboardLayoutSetLocale( + Context context, Locale locale) { + final String localeString = locale.toString(); + final InputMethodInfo imi = SubtypeUtils.getInputMethodInfo(context.getPackageName()); + final int count = imi.getSubtypeCount(); + for (int i = 0; i < count; i++) { + final InputMethodSubtype subtype = imi.getSubtypeAt(i); + if (localeString.equals(KeyboardLayoutSet.getKeyboardLayoutSetLocaleString(subtype))) { + return subtype; + } + } + throw new RuntimeException("Can not find subtype of locale " + localeString); + } } diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index 0fcac58a6..fa6664b1a 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -109,10 +109,9 @@ public class Suggest implements Dictionary.WordCallback { } /* package for test */ Suggest(final Context context, final File dictionary, - final long startOffset, final long length, final Flag[] flagArray, - final Locale locale) { + final long startOffset, final long length, final Locale locale) { initSynchronously(context, DictionaryFactory.createDictionaryForTest(context, dictionary, - startOffset, length, flagArray), locale); + startOffset, length /* useFullEditDistance */, false, locale), locale); } private void initWhitelistAndAutocorrectAndPool(final Context context, final Locale locale) { diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java index 010ea6813..d22332116 100644 --- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java @@ -17,6 +17,7 @@ package com.android.inputmethod.latin.makedict; import com.android.inputmethod.latin.makedict.FusionDictionary.CharGroup; +import com.android.inputmethod.latin.makedict.FusionDictionary.DictionaryOptions; import com.android.inputmethod.latin.makedict.FusionDictionary.Node; import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString; @@ -126,8 +127,9 @@ public class BinaryDictInputOutput { private static final int NOT_A_VERSION_NUMBER = -1; private static final int FIRST_VERSION_WITH_HEADER_SIZE = 2; - // No options yet, reserved for future use. - private static final int OPTIONS = 0; + // These options need to be the same numeric values as the one in the native reading code. + private static final int GERMAN_UMLAUT_PROCESSING_FLAG = 0x1; + private static final int FRENCH_LIGATURE_PROCESSING_FLAG = 0x4; // TODO: Make this value adaptative to content data, store it in the header, and // use it in the reading code. @@ -704,6 +706,14 @@ public class BinaryDictInputOutput { } /** + * Makes the 2-byte value for options flags. + */ + private static final int makeOptionsValue(final DictionaryOptions options) { + return (options.mFrenchLigatureProcessing ? FRENCH_LIGATURE_PROCESSING_FLAG : 0) + + (options.mGermanUmlautProcessing ? GERMAN_UMLAUT_PROCESSING_FLAG : 0); + } + + /** * Makes the flag value for a shortcut. * * @param more whether there are more attributes after this one. @@ -918,8 +928,9 @@ public class BinaryDictInputOutput { buffer[index++] = (byte) (0xFF & version); } // Options flags - buffer[index++] = (byte) (0xFF & (OPTIONS >> 8)); - buffer[index++] = (byte) (0xFF & OPTIONS); + final int options = makeOptionsValue(dict.mOptions); + buffer[index++] = (byte) (0xFF & (options >> 8)); + buffer[index++] = (byte) (0xFF & options); if (version >= FIRST_VERSION_WITH_HEADER_SIZE) { final int headerSizeOffset = index; index += 4; // Size of the header size @@ -1172,11 +1183,10 @@ public class BinaryDictInputOutput { } nodeContents.add( new CharGroup(info.mCharacters, shortcutTargets, bigrams, info.mFrequency, - children, false)); + children)); } else { nodeContents.add( - new CharGroup(info.mCharacters, shortcutTargets, bigrams, info.mFrequency, - false)); + new CharGroup(info.mCharacters, shortcutTargets, bigrams, info.mFrequency)); } groupOffset = info.mEndAddress; } @@ -1219,7 +1229,7 @@ public class BinaryDictInputOutput { } // Read options - source.readUnsignedShort(); + final int optionsFlags = source.readUnsignedShort(); final long headerSize; final HashMap<String, String> options = new HashMap<String, String>(); @@ -1241,7 +1251,9 @@ public class BinaryDictInputOutput { final Node root = readNode(source, headerSize, reverseNodeMapping, reverseGroupMapping); FusionDictionary newDict = new FusionDictionary(root, - new FusionDictionary.DictionaryOptions(options)); + new FusionDictionary.DictionaryOptions(options, + 0 != (optionsFlags & GERMAN_UMLAUT_PROCESSING_FLAG), + 0 != (optionsFlags & FRENCH_LIGATURE_PROCESSING_FLAG))); if (null != dict) { for (Word w : dict) { newDict.add(w.mWord, w.mFrequency, w.mShortcutTargets, w.mBigrams); diff --git a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java index 99b17048d..40bcfc3aa 100644 --- a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java +++ b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java @@ -98,35 +98,24 @@ public class FusionDictionary implements Iterable<Word> { ArrayList<WeightedString> mShortcutTargets; ArrayList<WeightedString> mBigrams; int mFrequency; // NOT_A_TERMINAL == mFrequency indicates this is not a terminal. - boolean mIsShortcutOnly; // Only valid if this is a terminal. Node mChildren; // The two following members to help with binary generation int mCachedSize; int mCachedAddress; public CharGroup(final int[] chars, final ArrayList<WeightedString> shortcutTargets, - final ArrayList<WeightedString> bigrams, final int frequency, - final boolean isShortcutOnly) { + final ArrayList<WeightedString> bigrams, final int frequency) { mChars = chars; mFrequency = frequency; - mIsShortcutOnly = isShortcutOnly; - if (mIsShortcutOnly && NOT_A_TERMINAL == mFrequency) { - throw new RuntimeException("A node must be a terminal to be a shortcut only"); - } mShortcutTargets = shortcutTargets; mBigrams = bigrams; mChildren = null; } public CharGroup(final int[] chars, final ArrayList<WeightedString> shortcutTargets, - final ArrayList<WeightedString> bigrams, final int frequency, final Node children, - final boolean isShortcutOnly) { + final ArrayList<WeightedString> bigrams, final int frequency, final Node children) { mChars = chars; mFrequency = frequency; - mIsShortcutOnly = isShortcutOnly; - if (mIsShortcutOnly && NOT_A_TERMINAL == mFrequency) { - throw new RuntimeException("A node must be a terminal to be a shortcut only"); - } mShortcutTargets = shortcutTargets; mBigrams = bigrams; mChildren = children; @@ -205,7 +194,7 @@ public class FusionDictionary implements Iterable<Word> { * updated if they are higher than the existing ones. */ public void update(int frequency, ArrayList<WeightedString> shortcutTargets, - ArrayList<WeightedString> bigrams, boolean isShortcutOnly) { + ArrayList<WeightedString> bigrams) { if (frequency > mFrequency) { mFrequency = frequency; } @@ -241,7 +230,6 @@ public class FusionDictionary implements Iterable<Word> { } } } - mIsShortcutOnly = isShortcutOnly; } } @@ -251,26 +239,20 @@ public class FusionDictionary implements Iterable<Word> { * There are no options at the moment, so this class is empty. */ public static class DictionaryOptions { - final HashMap<String, String> mAttributes; - public DictionaryOptions(final HashMap<String, String> attributes) { + public final boolean mGermanUmlautProcessing; + public final boolean mFrenchLigatureProcessing; + public final HashMap<String, String> mAttributes; + public DictionaryOptions(final HashMap<String, String> attributes, + final boolean germanUmlautProcessing, final boolean frenchLigatureProcessing) { mAttributes = attributes; + mGermanUmlautProcessing = germanUmlautProcessing; + mFrenchLigatureProcessing = frenchLigatureProcessing; } } - public final DictionaryOptions mOptions; public final Node mRoot; - public FusionDictionary() { - mRoot = new Node(); - mOptions = new DictionaryOptions(new HashMap<String, String>()); - } - - public FusionDictionary(final HashMap<String, String> attributes) { - mRoot = new Node(); - mOptions = new DictionaryOptions(attributes); - } - public FusionDictionary(final Node root, final DictionaryOptions options) { mRoot = root; mOptions = options; @@ -304,7 +286,7 @@ public class FusionDictionary implements Iterable<Word> { for (WeightedString word : words) { final CharGroup t = findWordInTree(mRoot, word.mWord); if (null == t) { - add(getCodePoints(word.mWord), 0, null, null, false /* isShortcutOnly */); + add(getCodePoints(word.mWord), 0, null, null); } } } @@ -328,7 +310,7 @@ public class FusionDictionary implements Iterable<Word> { if (null != bigrams) { addNeutralWords(bigrams); } - add(getCodePoints(word), frequency, shortcutTargets, bigrams, false /* isShortcutOnly */); + add(getCodePoints(word), frequency, shortcutTargets, bigrams); } /** @@ -350,21 +332,6 @@ public class FusionDictionary implements Iterable<Word> { } /** - * Helper method to add a shortcut that should not be a dictionary word. - * - * @param word the word to add. - * @param frequency the frequency of the word, in the range [0..255]. - * @param shortcutTargets a list of shortcut targets. May not be null. - */ - public void addShortcutOnly(final String word, final int frequency, - final ArrayList<WeightedString> shortcutTargets) { - if (null == shortcutTargets) { - throw new RuntimeException("Can't add a shortcut without targets"); - } - add(getCodePoints(word), frequency, shortcutTargets, null, true /* isShortcutOnly */); - } - - /** * Helper method to add a new bigram to the dictionary. * * @param word1 the previous word of the context @@ -377,7 +344,7 @@ public class FusionDictionary implements Iterable<Word> { final CharGroup charGroup2 = findWordInTree(mRoot, word2); if (charGroup2 == null) { // TODO: refactor with the identical code in addNeutralWords - add(getCodePoints(word2), 0, null, null, false /* isShortcutOnly */); + add(getCodePoints(word2), 0, null, null); } charGroup.addBigram(word2, frequency); } else { @@ -395,12 +362,10 @@ public class FusionDictionary implements Iterable<Word> { * @param frequency the frequency of the word, in the range [0..255]. * @param shortcutTargets an optional list of shortcut targets for this word (null if none). * @param bigrams an optional list of bigrams for this word (null if none). - * @param isShortcutOnly whether this should be a shortcut only. */ private void add(final int[] word, final int frequency, final ArrayList<WeightedString> shortcutTargets, - final ArrayList<WeightedString> bigrams, - final boolean isShortcutOnly) { + final ArrayList<WeightedString> bigrams) { assert(frequency >= 0 && frequency <= 255); Node currentNode = mRoot; int charIndex = 0; @@ -425,7 +390,7 @@ public class FusionDictionary implements Iterable<Word> { final int insertionIndex = findInsertionIndex(currentNode, word[charIndex]); final CharGroup newGroup = new CharGroup( Arrays.copyOfRange(word, charIndex, word.length), - shortcutTargets, bigrams, frequency, isShortcutOnly); + shortcutTargets, bigrams, frequency); currentNode.mData.add(insertionIndex, newGroup); checkStack(currentNode); } else { @@ -435,13 +400,13 @@ public class FusionDictionary implements Iterable<Word> { // The new word is a prefix of an existing word, but the node on which it // should end already exists as is. Since the old CharNode was not a terminal, // make it one by filling in its frequency and other attributes - currentGroup.update(frequency, shortcutTargets, bigrams, isShortcutOnly); + currentGroup.update(frequency, shortcutTargets, bigrams); } else { // The new word matches the full old word and extends past it. // We only have to create a new node and add it to the end of this. final CharGroup newNode = new CharGroup( Arrays.copyOfRange(word, charIndex + differentCharIndex, word.length), - shortcutTargets, bigrams, frequency, isShortcutOnly); + shortcutTargets, bigrams, frequency); currentGroup.mChildren = new Node(); currentGroup.mChildren.mData.add(newNode); } @@ -449,7 +414,7 @@ public class FusionDictionary implements Iterable<Word> { if (0 == differentCharIndex) { // Exact same word. Update the frequency if higher. This will also add the // new bigrams to the existing bigram list if it already exists. - currentGroup.update(frequency, shortcutTargets, bigrams, isShortcutOnly); + currentGroup.update(frequency, shortcutTargets, bigrams); } else { // Partial prefix match only. We have to replace the current node with a node // containing the current prefix and create two new ones for the tails. @@ -457,26 +422,21 @@ public class FusionDictionary implements Iterable<Word> { final CharGroup newOldWord = new CharGroup( Arrays.copyOfRange(currentGroup.mChars, differentCharIndex, currentGroup.mChars.length), currentGroup.mShortcutTargets, - currentGroup.mBigrams, currentGroup.mFrequency, currentGroup.mChildren, - currentGroup.mIsShortcutOnly); + currentGroup.mBigrams, currentGroup.mFrequency, currentGroup.mChildren); newChildren.mData.add(newOldWord); final CharGroup newParent; if (charIndex + differentCharIndex >= word.length) { newParent = new CharGroup( Arrays.copyOfRange(currentGroup.mChars, 0, differentCharIndex), - shortcutTargets, bigrams, frequency, newChildren, isShortcutOnly); + shortcutTargets, bigrams, frequency, newChildren); } else { - // isShortcutOnly makes no sense for non-terminal nodes. The following node - // is non-terminal (frequency 0 in FusionDictionary representation) so we - // pass false for isShortcutOnly newParent = new CharGroup( Arrays.copyOfRange(currentGroup.mChars, 0, differentCharIndex), - null, null, -1, newChildren, false /* isShortcutOnly */); + null, null, -1, newChildren); final CharGroup newWord = new CharGroup( Arrays.copyOfRange(word, charIndex + differentCharIndex, - word.length), shortcutTargets, bigrams, frequency, - isShortcutOnly); + word.length), shortcutTargets, bigrams, frequency); final int addIndex = word[charIndex + differentCharIndex] > currentGroup.mChars[differentCharIndex] ? 1 : 0; newChildren.mData.add(addIndex, newWord); @@ -534,8 +494,7 @@ public class FusionDictionary implements Iterable<Word> { */ private static int findInsertionIndex(final Node node, int character) { final ArrayList<CharGroup> data = node.mData; - final CharGroup reference = new CharGroup(new int[] { character }, null, null, 0, - false /* isShortcutOnly */); + final CharGroup reference = new CharGroup(new int[] { character }, null, null, 0); int result = Collections.binarySearch(data, reference, CHARGROUP_COMPARATOR); return result >= 0 ? result : -result - 1; } @@ -763,8 +722,7 @@ public class FusionDictionary implements Iterable<Word> { } if (currentGroup.mFrequency >= 0) return new Word(mCurrentString.toString(), currentGroup.mFrequency, - currentGroup.mShortcutTargets, currentGroup.mBigrams, - currentGroup.mIsShortcutOnly); + currentGroup.mShortcutTargets, currentGroup.mBigrams); } else { mPositions.removeLast(); currentPos = mPositions.getLast(); diff --git a/java/src/com/android/inputmethod/latin/makedict/Word.java b/java/src/com/android/inputmethod/latin/makedict/Word.java index 4e0ab1049..d07826757 100644 --- a/java/src/com/android/inputmethod/latin/makedict/Word.java +++ b/java/src/com/android/inputmethod/latin/makedict/Word.java @@ -29,7 +29,6 @@ import java.util.Arrays; public class Word implements Comparable<Word> { final String mWord; final int mFrequency; - final boolean mIsShortcutOnly; final ArrayList<WeightedString> mShortcutTargets; final ArrayList<WeightedString> mBigrams; @@ -37,19 +36,17 @@ public class Word implements Comparable<Word> { public Word(final String word, final int frequency, final ArrayList<WeightedString> shortcutTargets, - final ArrayList<WeightedString> bigrams, final boolean isShortcutOnly) { + final ArrayList<WeightedString> bigrams) { mWord = word; mFrequency = frequency; mShortcutTargets = shortcutTargets; mBigrams = bigrams; - mIsShortcutOnly = isShortcutOnly; } private static int computeHashCode(Word word) { return Arrays.hashCode(new Object[] { word.mWord, word.mFrequency, - word.mIsShortcutOnly, word.mShortcutTargets.hashCode(), word.mBigrams.hashCode() }); @@ -80,7 +77,6 @@ public class Word implements Comparable<Word> { if (!(o instanceof Word)) return false; Word w = (Word)o; return mFrequency == w.mFrequency && mWord.equals(w.mWord) - && mIsShortcutOnly == w.mIsShortcutOnly && mShortcutTargets.equals(w.mShortcutTargets) && mBigrams.equals(w.mBigrams); } diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java index cd01bb146..97296147f 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java @@ -33,11 +33,9 @@ import com.android.inputmethod.latin.Dictionary; import com.android.inputmethod.latin.Dictionary.WordCallback; import com.android.inputmethod.latin.DictionaryCollection; import com.android.inputmethod.latin.DictionaryFactory; -import com.android.inputmethod.latin.Flag; import com.android.inputmethod.latin.LocaleUtils; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.StringUtils; -import com.android.inputmethod.latin.SubtypeLocale; import com.android.inputmethod.latin.SynchronouslyLoadedContactsDictionary; import com.android.inputmethod.latin.SynchronouslyLoadedUserDictionary; import com.android.inputmethod.latin.WhitelistDictionary; @@ -69,17 +67,6 @@ public class AndroidSpellCheckerService extends SpellCheckerService private static final int CAPITALIZE_ALL = 2; // All caps private final static String[] EMPTY_STRING_ARRAY = new String[0]; - private final static Flag[] USE_FULL_EDIT_DISTANCE_FLAG_ARRAY; - static { - // See BinaryDictionary.java for an explanation of these flags - // Specifially, ALL_CONFIG_FLAGS means that we want to consider all flags with the - // current dictionary configuration - for example, consider the UMLAUT flag - // so that it will be turned on for German dictionaries and off for others. - USE_FULL_EDIT_DISTANCE_FLAG_ARRAY = Arrays.copyOf(BinaryDictionary.ALL_CONFIG_FLAGS, - BinaryDictionary.ALL_CONFIG_FLAGS.length + 1); - USE_FULL_EDIT_DISTANCE_FLAG_ARRAY[BinaryDictionary.ALL_CONFIG_FLAGS.length] = - BinaryDictionary.FLAG_USE_FULL_EDIT_DISTANCE; - } private Map<String, DictionaryPool> mDictionaryPools = Collections.synchronizedMap(new TreeMap<String, DictionaryPool>()); private Map<String, Dictionary> mUserDictionaries = @@ -326,7 +313,7 @@ public class AndroidSpellCheckerService extends SpellCheckerService } else if (CAPITALIZE_FIRST == capitalizeType) { for (int i = 0; i < mSuggestions.size(); ++i) { // Likewise - mSuggestions.set(i, SubtypeLocale.toTitleCase( + mSuggestions.set(i, StringUtils.toTitleCase( mSuggestions.get(i).toString(), locale)); } } @@ -403,7 +390,7 @@ public class AndroidSpellCheckerService extends SpellCheckerService final int fallbackResourceId = DictionaryFactory.getMainDictionaryResourceId(resources); final DictionaryCollection dictionaryCollection = DictionaryFactory.createDictionaryFromManager(this, locale, fallbackResourceId, - USE_FULL_EDIT_DISTANCE_FLAG_ARRAY); + true /* useFullEditDistance */); final String localeStr = locale.toString(); Dictionary userDictionary = mUserDictionaries.get(localeStr); if (null == userDictionary) { |