diff options
Diffstat (limited to 'java/src/com/android/inputmethod/latin')
13 files changed, 129 insertions, 320 deletions
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) { |