diff options
Diffstat (limited to 'java/src/com/android/inputmethod/latin')
7 files changed, 100 insertions, 62 deletions
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java index e428b1d54..72757e086 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java @@ -28,6 +28,7 @@ import android.text.TextUtils; import android.util.Log; import com.android.inputmethod.dictionarypack.DictionaryPackConstants; +import com.android.inputmethod.dictionarypack.MD5Calculator; import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.DictionaryInfoUtils; import com.android.inputmethod.latin.utils.DictionaryInfoUtils.DictionaryInfo; @@ -38,6 +39,7 @@ import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.Closeable; import java.io.File; +import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; @@ -167,8 +169,9 @@ public final class BinaryDictionaryFileDumper { do { final String wordListId = cursor.getString(0); final String wordListLocale = cursor.getString(1); + final String wordListRawChecksum = cursor.getString(2); if (TextUtils.isEmpty(wordListId)) continue; - list.add(new WordListInfo(wordListId, wordListLocale)); + list.add(new WordListInfo(wordListId, wordListLocale, wordListRawChecksum)); } while (cursor.moveToNext()); return list; } catch (RemoteException e) { @@ -217,7 +220,8 @@ public final class BinaryDictionaryFileDumper { * and creating it (and its containing directory) if necessary. */ private static void cacheWordList(final String wordlistId, final String locale, - final ContentProviderClient providerClient, final Context context) { + final String rawChecksum, final ContentProviderClient providerClient, + final Context context) { final int COMPRESSED_CRYPTED_COMPRESSED = 0; final int CRYPTED_COMPRESSED = 1; final int COMPRESSED_CRYPTED = 2; @@ -299,6 +303,13 @@ public final class BinaryDictionaryFileDumper { checkMagicAndCopyFileTo(bufferedInputStream, bufferedOutputStream); bufferedOutputStream.flush(); bufferedOutputStream.close(); + final String actualRawChecksum = MD5Calculator.checksum( + new BufferedInputStream(new FileInputStream(outputFile))); + Log.i(TAG, "Computed checksum for downloaded dictionary. Expected = " + rawChecksum + + " ; actual = " + actualRawChecksum); + if (!TextUtils.isEmpty(rawChecksum) && !rawChecksum.equals(actualRawChecksum)) { + throw new IOException("Could not decode the file correctly : checksum differs"); + } final File finalFile = new File(finalFileName); finalFile.delete(); if (!outputFile.renameTo(finalFile)) { @@ -408,7 +419,7 @@ public final class BinaryDictionaryFileDumper { final List<WordListInfo> idList = getWordListWordListInfos(locale, context, hasDefaultWordList); for (WordListInfo id : idList) { - cacheWordList(id.mId, id.mLocale, providerClient, context); + cacheWordList(id.mId, id.mLocale, id.mRawChecksum, providerClient, context); } } finally { providerClient.release(); diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 19c777a3e..ab7e66a09 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -84,7 +84,6 @@ import com.android.inputmethod.latin.utils.CapsModeUtils; import com.android.inputmethod.latin.utils.CoordinateUtils; import com.android.inputmethod.latin.utils.DialogUtils; import com.android.inputmethod.latin.utils.DistracterFilter; -import com.android.inputmethod.latin.utils.DistracterFilterUtils; import com.android.inputmethod.latin.utils.ImportantNoticeUtils; import com.android.inputmethod.latin.utils.IntentUtils; import com.android.inputmethod.latin.utils.JniUtils; @@ -1748,7 +1747,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen @UsedForTesting /* package for test */ DistracterFilter createDistracterFilter() { - return DistracterFilterUtils.createDistracterFilter(this /* Context */, mKeyboardSwitcher); + return new DistracterFilter(this /* Context */, + mRichImm.getMyEnabledInputMethodSubtypeList( + true /* allowsImplicitlySelectedSubtypes */)); } public void dumpDictionaryForDebug(final String dictName) { diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index e3759a586..43daee4d2 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -18,7 +18,6 @@ package com.android.inputmethod.latin; import android.text.TextUtils; -import com.android.inputmethod.event.Event; import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.define.ProductionFlag; @@ -112,7 +111,10 @@ public final class Suggest { additionalFeaturesOptions, SESSION_TYPING, rawSuggestions); final boolean isFirstCharCapitalized = wordComposer.isFirstCharCapitalized(); - final boolean isAllUpperCase = wordComposer.isAllUpperCase(); + // If resumed, then we don't want to upcase everything: resuming on a fully-capitalized + // words is rarely done to switch to another fully-capitalized word, but usually to a + // normal, non-capitalized suggestion. + final boolean isAllUpperCase = wordComposer.isAllUpperCase() && !wordComposer.isResumed(); final String firstSuggestion; final String whitelistedWord; if (suggestionResults.isEmpty()) { diff --git a/java/src/com/android/inputmethod/latin/WordListInfo.java b/java/src/com/android/inputmethod/latin/WordListInfo.java index 5ac806a0c..268fe9818 100644 --- a/java/src/com/android/inputmethod/latin/WordListInfo.java +++ b/java/src/com/android/inputmethod/latin/WordListInfo.java @@ -22,8 +22,10 @@ package com.android.inputmethod.latin; public final class WordListInfo { public final String mId; public final String mLocale; - public WordListInfo(final String id, final String locale) { + public final String mRawChecksum; + public WordListInfo(final String id, final String locale, final String rawChecksum) { mId = id; mLocale = locale; + mRawChecksum = rawChecksum; } } diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java index faab76944..7536ff94c 100644 --- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java +++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java @@ -805,10 +805,11 @@ public final class InputLogic { final int codePoint = inputTransaction.mEvent.mCodePoint; final SettingsValues settingsValues = inputTransaction.mSettingsValues; boolean didAutoCorrect = false; + final boolean wasComposingWord = mWordComposer.isComposingWord(); // We avoid sending spaces in languages without spaces if we were composing. final boolean shouldAvoidSendingCode = Constants.CODE_SPACE == codePoint && !settingsValues.mSpacingAndPunctuations.mCurrentLanguageHasSpaces - && mWordComposer.isComposingWord(); + && wasComposingWord; if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) { // If we are in the middle of a recorrection, we need to commit the recorrection // first so that we can insert the separator at the current cursor position. @@ -852,7 +853,7 @@ public final class InputLogic { promotePhantomSpace(settingsValues); } if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.latinIME_handleSeparator(codePoint, mWordComposer.isComposingWord()); + ResearchLogger.latinIME_handleSeparator(codePoint, wasComposingWord); } if (!shouldAvoidSendingCode) { @@ -868,7 +869,9 @@ public final class InputLogic { } startDoubleSpacePeriodCountdown(inputTransaction); - inputTransaction.setRequiresUpdateSuggestions(); + if (wasComposingWord) { + inputTransaction.setRequiresUpdateSuggestions(); + } } else { if (swapWeakSpace) { swapSwapperAndSpace(inputTransaction); diff --git a/java/src/com/android/inputmethod/latin/utils/DistracterFilter.java b/java/src/com/android/inputmethod/latin/utils/DistracterFilter.java index 9ea7e217e..f1057da0b 100644 --- a/java/src/com/android/inputmethod/latin/utils/DistracterFilter.java +++ b/java/src/com/android/inputmethod/latin/utils/DistracterFilter.java @@ -16,13 +16,23 @@ package com.android.inputmethod.latin.utils; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; import java.util.Locale; +import java.util.Map; import java.util.concurrent.TimeUnit; import android.content.Context; +import android.content.res.Resources; +import android.text.InputType; import android.util.Log; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodSubtype; import com.android.inputmethod.keyboard.Keyboard; +import com.android.inputmethod.keyboard.KeyboardId; +import com.android.inputmethod.keyboard.KeyboardLayoutSet; import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.PrevWordsInfo; import com.android.inputmethod.latin.Suggest; @@ -41,8 +51,10 @@ public class DistracterFilter { private static final long TIMEOUT_TO_WAIT_LOADING_DICTIONARIES_IN_SECONDS = 120; private final Context mContext; + private final Map<Locale, InputMethodSubtype> mLocaleToSubtypeMap; + private final Map<Locale, Keyboard> mLocaleToKeyboardMap; private final Suggest mSuggest; - private final Keyboard mKeyboard; + private Keyboard mKeyboard; // If the score of the top suggestion exceeds this value, the tested word (e.g., // an OOV, a misspelling, or an in-vocabulary word) would be considered as a distracter to @@ -51,17 +63,34 @@ public class DistracterFilter { // the dictionary. private static final float DISTRACTER_WORD_SCORE_THRESHOLD = 2.0f; + // Create empty distracter filter. + public DistracterFilter() { + this(null, new ArrayList<InputMethodSubtype>()); + } + /** * Create a DistracterFilter instance. * * @param context the context. - * @param keyboard the keyboard that is currently being used. This information is needed - * when calling mSuggest.getSuggestedWords(...) to obtain a list of suggestions. + * @param enabledSubtypes the enabled subtypes. */ - public DistracterFilter(final Context context, final Keyboard keyboard) { + public DistracterFilter(final Context context, final List<InputMethodSubtype> enabledSubtypes) { mContext = context; + mLocaleToSubtypeMap = new HashMap<>(); + if (enabledSubtypes != null) { + for (final InputMethodSubtype subtype : enabledSubtypes) { + final Locale locale = SubtypeLocaleUtils.getSubtypeLocale(subtype); + if (mLocaleToSubtypeMap.containsKey(locale)) { + // Multiple subtypes are enabled for one locale. + // TODO: Investigate what we should do for this case. + continue; + } + mLocaleToSubtypeMap.put(locale, subtype); + } + } + mLocaleToKeyboardMap = new HashMap<>(); mSuggest = new Suggest(); - mKeyboard = keyboard; + mKeyboard = null; } private static boolean suggestionExceedsDistracterThreshold( @@ -78,6 +107,30 @@ public class DistracterFilter { return false; } + private void loadKeyboardForLocale(final Locale newLocale) { + final Keyboard cachedKeyboard = mLocaleToKeyboardMap.get(newLocale); + if (cachedKeyboard != null) { + mKeyboard = cachedKeyboard; + return; + } + final InputMethodSubtype subtype = mLocaleToSubtypeMap.get(newLocale); + if (subtype == null) { + return; + } + final EditorInfo editorInfo = new EditorInfo(); + editorInfo.inputType = InputType.TYPE_CLASS_TEXT; + final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder( + mContext, editorInfo); + final Resources res = mContext.getResources(); + final int keyboardWidth = ResourceUtils.getDefaultKeyboardWidth(res); + final int keyboardHeight = ResourceUtils.getDefaultKeyboardHeight(res); + builder.setKeyboardGeometry(keyboardWidth, keyboardHeight); + builder.setSubtype(subtype); + builder.setIsSpellChecker(false /* isSpellChecker */); + final KeyboardLayoutSet layoutSet = builder.build(); + mKeyboard = layoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET); + } + private void loadDictionariesForLocale(final Locale newlocale) throws InterruptedException { mSuggest.mDictionaryFacilitator.resetDictionaries(mContext, newlocale, false /* useContactsDict */, false /* usePersonalizedDicts */, @@ -92,15 +145,21 @@ public class DistracterFilter { * @param prevWordsInfo the information of previous words. * @param testedWord the word that will be tested to see whether it is a distracter to words * in dictionaries. - * @param locale the locale of words. + * @param locale the locale of word. * @return true if testedWord is a distracter, otherwise false. */ public boolean isDistracterToWordsInDictionaries(final PrevWordsInfo prevWordsInfo, final String testedWord, final Locale locale) { - if (mKeyboard == null || locale == null) { + if (locale == null) { return false; } if (!locale.equals(mSuggest.mDictionaryFacilitator.getLocale())) { + if (!mLocaleToSubtypeMap.containsKey(locale)) { + Log.e(TAG, "Locale " + locale + " is not enabled."); + // TODO: Investigate what we should do for disabled locales. + return false; + } + loadKeyboardForLocale(locale); // Reset dictionaries for the locale. try { loadDictionariesForLocale(locale); @@ -109,11 +168,12 @@ public class DistracterFilter { return false; } } - + if (mKeyboard == null) { + return false; + } final WordComposer composer = new WordComposer(); final int[] codePoints = StringUtils.toCodePointArray(testedWord); - final int[] coordinates; - coordinates = mKeyboard.getCoordinates(codePoints); + final int[] coordinates = mKeyboard.getCoordinates(codePoints); composer.setComposingWord(codePoints, coordinates, prevWordsInfo); final int trailingSingleQuotesCount = StringUtils.getTrailingSingleQuotesCount(testedWord); diff --git a/java/src/com/android/inputmethod/latin/utils/DistracterFilterUtils.java b/java/src/com/android/inputmethod/latin/utils/DistracterFilterUtils.java deleted file mode 100644 index 8a711a24e..000000000 --- a/java/src/com/android/inputmethod/latin/utils/DistracterFilterUtils.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2014 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.utils; - -import android.content.Context; - -import com.android.inputmethod.keyboard.Keyboard; -import com.android.inputmethod.keyboard.KeyboardSwitcher; -import com.android.inputmethod.keyboard.MainKeyboardView; - -public class DistracterFilterUtils { - private DistracterFilterUtils() { - // This utility class is not publicly instantiable. - } - - public static final DistracterFilter createDistracterFilter(final Context context, - final KeyboardSwitcher keyboardSwitcher) { - final MainKeyboardView mainKeyboardView = keyboardSwitcher.getMainKeyboardView(); - // TODO: Create Keyboard when mainKeyboardView is null. - // TODO: Figure out the most reasonable keyboard for the filter. Refer to the - // spellchecker's logic. - final Keyboard keyboard = (mainKeyboardView != null) ? - mainKeyboardView.getKeyboard() : null; - final DistracterFilter distracterFilter = new DistracterFilter(context, keyboard); - return distracterFilter; - } -} |