diff options
Diffstat (limited to 'java/src/com/android/inputmethod/latin/utils')
23 files changed, 375 insertions, 935 deletions
diff --git a/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java b/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java index 2bb30a2ba..3ca7c7e1c 100644 --- a/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java @@ -95,8 +95,7 @@ public final class AdditionalSubtypeUtils { return EMPTY_SUBTYPE_ARRAY; } final String[] prefSubtypeArray = prefSubtypes.split(PREF_SUBTYPE_SEPARATOR); - final ArrayList<InputMethodSubtype> subtypesList = - CollectionUtils.newArrayList(prefSubtypeArray.length); + final ArrayList<InputMethodSubtype> subtypesList = new ArrayList<>(prefSubtypeArray.length); for (final String prefSubtype : prefSubtypeArray) { final String elems[] = prefSubtype.split(LOCALE_AND_LAYOUT_SEPARATOR); if (elems.length != LENGTH_WITHOUT_EXTRA_VALUE diff --git a/java/src/com/android/inputmethod/latin/utils/AutoCorrectionUtils.java b/java/src/com/android/inputmethod/latin/utils/AutoCorrectionUtils.java index 22b9b77d2..34ee2152a 100644 --- a/java/src/com/android/inputmethod/latin/utils/AutoCorrectionUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/AutoCorrectionUtils.java @@ -16,12 +16,11 @@ package com.android.inputmethod.latin.utils; -import com.android.inputmethod.latin.BinaryDictionary; +import android.util.Log; + import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; -import android.util.Log; - public final class AutoCorrectionUtils { private static final boolean DBG = LatinImeLogger.sDBG; private static final String TAG = AutoCorrectionUtils.class.getSimpleName(); @@ -36,7 +35,9 @@ public final class AutoCorrectionUtils { final float autoCorrectionThreshold) { if (null != suggestion) { // Shortlist a whitelisted word - if (suggestion.mKind == SuggestedWordInfo.KIND_WHITELIST) return true; + if (suggestion.isKindOf(SuggestedWordInfo.KIND_WHITELIST)) { + return true; + } final int autoCorrectionSuggestionScore = suggestion.mScore; // TODO: when the normalized score of the first suggestion is nearly equals to // the normalized score of the second suggestion, behave less aggressive. diff --git a/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java b/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java index 702688f93..936219332 100644 --- a/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java @@ -62,6 +62,22 @@ public final class CapsModeUtils { } /** + * Helper method to find out if a code point is starting punctuation. + * + * This include the Unicode START_PUNCTUATION category, but also some other symbols that are + * starting, like the inverted question mark or the double quote. + * + * @param codePoint the code point + * @return true if it's starting punctuation, false otherwise. + */ + private static boolean isStartPunctuation(final int codePoint) { + return (codePoint == Constants.CODE_DOUBLE_QUOTE || codePoint == Constants.CODE_SINGLE_QUOTE + || codePoint == Constants.CODE_INVERTED_QUESTION_MARK + || codePoint == Constants.CODE_INVERTED_EXCLAMATION_MARK + || Character.getType(codePoint) == Character.START_PUNCTUATION); + } + + /** * Determine what caps mode should be in effect at the current offset in * the text. Only the mode bits set in <var>reqModes</var> will be * checked. Note that the caps mode flags here are explicitly defined @@ -115,8 +131,7 @@ public final class CapsModeUtils { } else { for (i = cs.length(); i > 0; i--) { final char c = cs.charAt(i - 1); - if (c != Constants.CODE_DOUBLE_QUOTE && c != Constants.CODE_SINGLE_QUOTE - && Character.getType(c) != Character.START_PUNCTUATION) { + if (!isStartPunctuation(c)) { break; } } @@ -210,11 +225,14 @@ public final class CapsModeUtils { // We found out that we have a period. We need to determine if this is a full stop or // otherwise sentence-ending period, or an abbreviation like "e.g.". An abbreviation - // looks like (\w\.){2,} + // looks like (\w\.){2,}. Moreover, in German, you put periods after digits for dates + // and some other things, and in German specifically we need to not go into autocaps after + // a whitespace-digits-period sequence. // To find out, we will have a simple state machine with the following states : - // START, WORD, PERIOD, ABBREVIATION + // START, WORD, PERIOD, ABBREVIATION, NUMBER // On START : (just before the first period) // letter => WORD + // digit => NUMBER if German; end with caps otherwise // whitespace => end with no caps (it was a stand-alone period) // otherwise => end with caps (several periods/symbols in a row) // On WORD : (within the word just before the first period) @@ -228,6 +246,11 @@ public final class CapsModeUtils { // letter => LETTER // period => PERIOD // otherwise => end with no caps (it was an abbreviation) + // On NUMBER : (period immediately preceded by one or more digits) + // digit => NUMBER + // letter => LETTER (promote to word) + // otherwise => end with no caps (it was a whitespace-digits-period sequence, + // or a punctuation-digits-period sequence like "11.11.") // "Not an abbreviation" in the above chart essentially covers cases like "...yes.". This // should capitalize. @@ -235,6 +258,7 @@ public final class CapsModeUtils { final int WORD = 1; final int PERIOD = 2; final int LETTER = 3; + final int NUMBER = 4; final int caps = (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS | TextUtils.CAP_MODE_SENTENCES) & reqModes; final int noCaps = (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS) & reqModes; @@ -247,6 +271,8 @@ public final class CapsModeUtils { state = WORD; } else if (Character.isWhitespace(c)) { return noCaps; + } else if (Character.isDigit(c) && spacingAndPunctuations.mUsesGermanRules) { + state = NUMBER; } else { return caps; } @@ -275,6 +301,15 @@ public final class CapsModeUtils { } else { return noCaps; } + break; + case NUMBER: + if (Character.isLetter(c)) { + state = WORD; + } else if (Character.isDigit(c)) { + state = NUMBER; + } else { + return noCaps; + } } } // Here we arrived at the start of the line. This should behave exactly like whitespace. diff --git a/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java b/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java index bbfa0f091..e3aef29ba 100644 --- a/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java @@ -16,93 +16,21 @@ package com.android.inputmethod.latin.utils; -import android.util.SparseArray; - -import java.util.ArrayDeque; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; import java.util.Map; import java.util.TreeMap; -import java.util.TreeSet; -import java.util.WeakHashMap; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.CopyOnWriteArrayList; public final class CollectionUtils { private CollectionUtils() { // This utility class is not publicly instantiable. } - public static <K,V> HashMap<K,V> newHashMap() { - return new HashMap<K,V>(); - } - - public static <K, V> WeakHashMap<K, V> newWeakHashMap() { - return new WeakHashMap<K, V>(); - } - - public static <K,V> TreeMap<K,V> newTreeMap() { - return new TreeMap<K,V>(); - } - public static <K, V> Map<K,V> newSynchronizedTreeMap() { - final TreeMap<K,V> treeMap = newTreeMap(); + final TreeMap<K,V> treeMap = new TreeMap<>(); return Collections.synchronizedMap(treeMap); } - public static <K,V> ConcurrentHashMap<K,V> newConcurrentHashMap() { - return new ConcurrentHashMap<K,V>(); - } - - public static <E> HashSet<E> newHashSet() { - return new HashSet<E>(); - } - - public static <E> TreeSet<E> newTreeSet() { - return new TreeSet<E>(); - } - - public static <E> ArrayList<E> newArrayList() { - return new ArrayList<E>(); - } - - public static <E> ArrayList<E> newArrayList(final int initialCapacity) { - return new ArrayList<E>(initialCapacity); - } - - public static <E> ArrayList<E> newArrayList(final Collection<E> collection) { - return new ArrayList<E>(collection); - } - - public static <E> LinkedList<E> newLinkedList() { - return new LinkedList<E>(); - } - - public static <E> CopyOnWriteArrayList<E> newCopyOnWriteArrayList() { - return new CopyOnWriteArrayList<E>(); - } - - public static <E> CopyOnWriteArrayList<E> newCopyOnWriteArrayList( - final Collection<E> collection) { - return new CopyOnWriteArrayList<E>(collection); - } - - public static <E> CopyOnWriteArrayList<E> newCopyOnWriteArrayList(final E[] array) { - return new CopyOnWriteArrayList<E>(array); - } - - public static <E> ArrayDeque<E> newArrayDeque() { - return new ArrayDeque<E>(); - } - - public static <E> SparseArray<E> newSparseArray() { - return new SparseArray<E>(); - } - public static <E> ArrayList<E> arrayAsList(final E[] array, final int start, final int end) { if (array == null) { throw new NullPointerException(); @@ -111,7 +39,7 @@ public final class CollectionUtils { throw new IllegalArgumentException(); } - final ArrayList<E> list = newArrayList(end - start); + final ArrayList<E> list = new ArrayList<>(end - start); for (int i = start; i < end; i++) { list.add(array[i]); } diff --git a/java/src/com/android/inputmethod/latin/utils/CsvUtils.java b/java/src/com/android/inputmethod/latin/utils/CsvUtils.java index b18a1d83b..a21a1373b 100644 --- a/java/src/com/android/inputmethod/latin/utils/CsvUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/CsvUtils.java @@ -209,7 +209,7 @@ public final class CsvUtils { @UsedForTesting public static String[] split(final int splitFlags, final String line) throws CsvParseException { final boolean trimSpaces = (splitFlags & SPLIT_FLAGS_TRIM_SPACES) != 0; - final ArrayList<String> fields = CollectionUtils.newArrayList(); + final ArrayList<String> fields = new ArrayList<>(); final int length = line.length(); int start = 0; do { diff --git a/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java b/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java index 315913e2f..d76ea10c0 100644 --- a/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java @@ -336,7 +336,7 @@ public class DictionaryInfoUtils { public static ArrayList<DictionaryInfo> getCurrentDictionaryFileNameAndVersionInfo( final Context context) { - final ArrayList<DictionaryInfo> dictList = CollectionUtils.newArrayList(); + final ArrayList<DictionaryInfo> dictList = new ArrayList<>(); // Retrieve downloaded dictionaries final File[] directoryList = getCachedDirectoryList(context); diff --git a/java/src/com/android/inputmethod/latin/utils/DistracterFilter.java b/java/src/com/android/inputmethod/latin/utils/DistracterFilter.java index f1057da0b..787e4a59d 100644 --- a/java/src/com/android/inputmethod/latin/utils/DistracterFilter.java +++ b/java/src/com/android/inputmethod/latin/utils/DistracterFilter.java @@ -16,129 +16,14 @@ 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; -import com.android.inputmethod.latin.Suggest.OnGetSuggestedWordsCallback; -import com.android.inputmethod.latin.SuggestedWords; -import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; -import com.android.inputmethod.latin.WordComposer; - -/** - * This class is used to prevent distracters being added to personalization - * or user history dictionaries - */ -public class DistracterFilter { - private static final String TAG = DistracterFilter.class.getSimpleName(); - - 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 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 - // words in dictionary. The greater the threshold is, the less likely the tested word would - // become a distracter, which means the tested word will be more likely to be added to - // 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 enabledSubtypes the enabled subtypes. - */ - 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 = null; - } - - private static boolean suggestionExceedsDistracterThreshold( - final SuggestedWordInfo suggestion, final String consideredWord, - final float distracterThreshold) { - if (null != suggestion) { - final int suggestionScore = suggestion.mScore; - final float normalizedScore = BinaryDictionaryUtils.calcNormalizedScore( - consideredWord, suggestion.mWord, suggestionScore); - if (normalizedScore > distracterThreshold) { - return true; - } - } - 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 */, - false /* forceReloadMainDictionary */, null /* listener */); - mSuggest.mDictionaryFacilitator.waitForLoadingMainDictionary( - TIMEOUT_TO_WAIT_LOADING_DICTIONARIES_IN_SECONDS, TimeUnit.SECONDS); - } +public interface DistracterFilter { /** * Determine whether a word is a distracter to words in dictionaries. * @@ -149,56 +34,25 @@ public class DistracterFilter { * @return true if testedWord is a distracter, otherwise false. */ public boolean isDistracterToWordsInDictionaries(final PrevWordsInfo prevWordsInfo, - final String testedWord, final Locale locale) { - 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); - } catch (final InterruptedException e) { - Log.e(TAG, "Interrupted while waiting for loading dicts in DistracterFilter", e); - return false; - } - } - if (mKeyboard == null) { + final String testedWord, final Locale locale); + + public void updateEnabledSubtypes(final List<InputMethodSubtype> enabledSubtypes); + + public void close(); + + public static final DistracterFilter EMPTY_DISTRACTER_FILTER = new DistracterFilter() { + @Override + public boolean isDistracterToWordsInDictionaries(PrevWordsInfo prevWordsInfo, + String testedWord, Locale locale) { return false; } - final WordComposer composer = new WordComposer(); - final int[] codePoints = StringUtils.toCodePointArray(testedWord); - final int[] coordinates = mKeyboard.getCoordinates(codePoints); - composer.setComposingWord(codePoints, coordinates, prevWordsInfo); - final int trailingSingleQuotesCount = StringUtils.getTrailingSingleQuotesCount(testedWord); - final String consideredWord = trailingSingleQuotesCount > 0 ? - testedWord.substring(0, testedWord.length() - trailingSingleQuotesCount) : - testedWord; - final AsyncResultHolder<Boolean> holder = new AsyncResultHolder<Boolean>(); - final OnGetSuggestedWordsCallback callback = new OnGetSuggestedWordsCallback() { - @Override - public void onGetSuggestedWords(final SuggestedWords suggestedWords) { - if (suggestedWords != null && suggestedWords.size() > 1) { - // The suggestedWordInfo at 0 is the typed word. The 1st suggestion from - // the decoder is at index 1. - final SuggestedWordInfo firstSuggestion = suggestedWords.getInfo(1); - final boolean hasStrongDistractor = suggestionExceedsDistracterThreshold( - firstSuggestion, consideredWord, DISTRACTER_WORD_SCORE_THRESHOLD); - holder.set(hasStrongDistractor); - } - } - }; - mSuggest.getSuggestedWords(composer, prevWordsInfo, mKeyboard.getProximityInfo(), - true /* blockOffensiveWords */, true /* isCorrectionEnbaled */, - null /* additionalFeaturesOptions */, 0 /* sessionId */, - SuggestedWords.NOT_A_SEQUENCE_NUMBER, callback); + @Override + public void close() { + } - return holder.get(false /* defaultValue */, Constants.GET_SUGGESTED_WORDS_TIMEOUT); - } + @Override + public void updateEnabledSubtypes(List<InputMethodSubtype> enabledSubtypes) { + } + }; } diff --git a/java/src/com/android/inputmethod/latin/utils/DistracterFilterUsingSuggestion.java b/java/src/com/android/inputmethod/latin/utils/DistracterFilterUsingSuggestion.java new file mode 100644 index 000000000..b9c7f5671 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/utils/DistracterFilterUsingSuggestion.java @@ -0,0 +1,243 @@ +/* + * 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 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.Dictionary; +import com.android.inputmethod.latin.DictionaryFacilitator; +import com.android.inputmethod.latin.PrevWordsInfo; +import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; +import com.android.inputmethod.latin.WordComposer; + +/** + * This class is used to prevent distracters being added to personalization + * or user history dictionaries + */ +public class DistracterFilterUsingSuggestion implements DistracterFilter { + private static final String TAG = DistracterFilterUsingSuggestion.class.getSimpleName(); + private static final boolean DEBUG = false; + + 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 DictionaryFacilitator mDictionaryFacilitator; + private Keyboard mKeyboard; + private final Object mLock = new Object(); + + /** + * Create a DistracterFilter instance. + * + * @param context the context. + */ + public DistracterFilterUsingSuggestion(final Context context) { + mContext = context; + mLocaleToSubtypeMap = new HashMap<>(); + mLocaleToKeyboardMap = new HashMap<>(); + mDictionaryFacilitator = new DictionaryFacilitator(); + mKeyboard = null; + } + + @Override + public void close() { + mDictionaryFacilitator.closeDictionaries(); + } + + @Override + public void updateEnabledSubtypes(final List<InputMethodSubtype> enabledSubtypes) { + final Map<Locale, InputMethodSubtype> newLocaleToSubtypeMap = new HashMap<>(); + if (enabledSubtypes != null) { + for (final InputMethodSubtype subtype : enabledSubtypes) { + final Locale locale = SubtypeLocaleUtils.getSubtypeLocale(subtype); + if (newLocaleToSubtypeMap.containsKey(locale)) { + // Multiple subtypes are enabled for one locale. + // TODO: Investigate what we should do for this case. + continue; + } + newLocaleToSubtypeMap.put(locale, subtype); + } + } + if (mLocaleToSubtypeMap.equals(newLocaleToSubtypeMap)) { + // Enabled subtypes have not been changed. + return; + } + synchronized (mLock) { + mLocaleToSubtypeMap.clear(); + mLocaleToSubtypeMap.putAll(newLocaleToSubtypeMap); + mLocaleToKeyboardMap.clear(); + } + } + + private boolean isDistracter( + final SuggestionResults suggestionResults, final String consideredWord) { + int perfectMatchProbability = Dictionary.NOT_A_PROBABILITY; + for (final SuggestedWordInfo suggestedWordInfo : suggestionResults) { + if (suggestedWordInfo.mWord.equals(consideredWord)) { + perfectMatchProbability = mDictionaryFacilitator.getFrequency(consideredWord); + continue; + } + // Exact match can include case errors, accent errors, digraph conversions. + final boolean isExactMatch = suggestedWordInfo.isExactMatch(); + final boolean isExactMatchWithIntentionalOmission = + suggestedWordInfo.isExactMatchWithIntentionalOmission(); + + if (DEBUG) { + final float normalizedScore = BinaryDictionaryUtils.calcNormalizedScore( + consideredWord, suggestedWordInfo.mWord, suggestedWordInfo.mScore); + Log.d(TAG, "consideredWord: " + consideredWord); + Log.d(TAG, "top suggestion: " + suggestedWordInfo.mWord); + Log.d(TAG, "suggestionScore: " + suggestedWordInfo.mScore); + Log.d(TAG, "normalizedScore: " + normalizedScore); + Log.d(TAG, "isExactMatch: " + isExactMatch); + Log.d(TAG, "isExactMatchWithIntentionalOmission: " + + isExactMatchWithIntentionalOmission); + } + if (perfectMatchProbability != Dictionary.NOT_A_PROBABILITY) { + final int topNonPerfectProbability = mDictionaryFacilitator.getFrequency( + suggestedWordInfo.mWord); + if (DEBUG) { + Log.d(TAG, "perfectMatchProbability: " + perfectMatchProbability); + Log.d(TAG, "topNonPerfectProbability: " + topNonPerfectProbability); + } + if (perfectMatchProbability > topNonPerfectProbability) { + return false; + } + } + return isExactMatch || isExactMatchWithIntentionalOmission; + } + return false; + } + + private void loadKeyboardForLocale(final Locale newLocale) { + final Keyboard cachedKeyboard = mLocaleToKeyboardMap.get(newLocale); + if (cachedKeyboard != null) { + mKeyboard = cachedKeyboard; + return; + } + final InputMethodSubtype subtype; + synchronized (mLock) { + 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 { + mDictionaryFacilitator.resetDictionaries(mContext, newlocale, + false /* useContactsDict */, false /* usePersonalizedDicts */, + false /* forceReloadMainDictionary */, null /* listener */); + mDictionaryFacilitator.waitForLoadingMainDictionary( + TIMEOUT_TO_WAIT_LOADING_DICTIONARIES_IN_SECONDS, TimeUnit.SECONDS); + } + + /** + * Determine whether a word is a distracter to words in dictionaries. + * + * @param prevWordsInfo the information of previous words. Not used for now. + * @param testedWord the word that will be tested to see whether it is a distracter to words + * in dictionaries. + * @param locale the locale of word. + * @return true if testedWord is a distracter, otherwise false. + */ + @Override + public boolean isDistracterToWordsInDictionaries(final PrevWordsInfo prevWordsInfo, + final String testedWord, final Locale locale) { + if (locale == null) { + return false; + } + if (!locale.equals(mDictionaryFacilitator.getLocale())) { + synchronized (mLock) { + 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); + } catch (final InterruptedException e) { + Log.e(TAG, "Interrupted while waiting for loading dicts in DistracterFilter", + e); + return false; + } + } + } + if (mKeyboard == null) { + return false; + } + final WordComposer composer = new WordComposer(); + final int[] codePoints = StringUtils.toCodePointArray(testedWord); + final int[] coordinates = mKeyboard.getCoordinates(codePoints); + composer.setComposingWord(codePoints, coordinates, PrevWordsInfo.EMPTY_PREV_WORDS_INFO); + + final int trailingSingleQuotesCount = StringUtils.getTrailingSingleQuotesCount(testedWord); + final String consideredWord = trailingSingleQuotesCount > 0 ? + testedWord.substring(0, testedWord.length() - trailingSingleQuotesCount) : + testedWord; + final AsyncResultHolder<Boolean> holder = new AsyncResultHolder<>(); + ExecutorUtils.getExecutor("check distracters").execute(new Runnable() { + @Override + public void run() { + final SuggestionResults suggestionResults = + mDictionaryFacilitator.getSuggestionResults( + composer, PrevWordsInfo.EMPTY_PREV_WORDS_INFO, + mKeyboard.getProximityInfo(), true /* blockOffensiveWords */, + null /* additionalFeaturesOptions */, 0 /* sessionId */, + null /* rawSuggestions */); + if (suggestionResults.isEmpty()) { + holder.set(false); + return; + } + holder.set(isDistracter(suggestionResults, consideredWord)); + } + }); + // It's OK to block the distracter filtering, but the dictionary lookup should be done + // sequentially using ExecutorUtils. + return holder.get(false /* defaultValue */, Constants.GET_SUGGESTED_WORDS_TIMEOUT); + } +} diff --git a/java/src/com/android/inputmethod/latin/utils/ExecutorUtils.java b/java/src/com/android/inputmethod/latin/utils/ExecutorUtils.java index ed502ed3d..61da1b789 100644 --- a/java/src/com/android/inputmethod/latin/utils/ExecutorUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/ExecutorUtils.java @@ -19,22 +19,42 @@ package com.android.inputmethod.latin.utils; import com.android.inputmethod.annotations.UsedForTesting; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; /** * Utilities to manage executors. */ public class ExecutorUtils { - private static final ConcurrentHashMap<String, PrioritizedSerialExecutor> - sExecutorMap = CollectionUtils.newConcurrentHashMap(); + private static final ConcurrentHashMap<String, ExecutorService> sExecutorMap = + new ConcurrentHashMap<>(); + + private static class ThreadFactoryWithId implements ThreadFactory { + private final String mId; + + public ThreadFactoryWithId(final String id) { + mId = id; + } + + @Override + public Thread newThread(final Runnable r) { + return new Thread(r, "Executor - " + mId); + } + } + /** - * Gets the executor for the given dictionary name. + * Gets the executor for the given id. */ - public static PrioritizedSerialExecutor getExecutor(final String dictName) { - PrioritizedSerialExecutor executor = sExecutorMap.get(dictName); + public static ExecutorService getExecutor(final String id) { + ExecutorService executor = sExecutorMap.get(id); if (executor == null) { synchronized(sExecutorMap) { - executor = new PrioritizedSerialExecutor(); - sExecutorMap.put(dictName, executor); + executor = sExecutorMap.get(id); + if (executor == null) { + executor = Executors.newSingleThreadExecutor(new ThreadFactoryWithId(id)); + sExecutorMap.put(id, executor); + } } } return executor; @@ -46,7 +66,7 @@ public class ExecutorUtils { @UsedForTesting public static void shutdownAllExecutors() { synchronized(sExecutorMap) { - for (final PrioritizedSerialExecutor executor : sExecutorMap.values()) { + for (final ExecutorService executor : sExecutorMap.values()) { executor.execute(new Runnable() { @Override public void run() { diff --git a/java/src/com/android/inputmethod/latin/utils/FragmentUtils.java b/java/src/com/android/inputmethod/latin/utils/FragmentUtils.java index ee2b97b2a..e300bd1d3 100644 --- a/java/src/com/android/inputmethod/latin/utils/FragmentUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/FragmentUtils.java @@ -26,12 +26,11 @@ import com.android.inputmethod.latin.userdictionary.UserDictionaryAddWordFragmen import com.android.inputmethod.latin.userdictionary.UserDictionaryList; import com.android.inputmethod.latin.userdictionary.UserDictionaryLocalePicker; import com.android.inputmethod.latin.userdictionary.UserDictionarySettings; -import com.android.inputmethod.research.FeedbackFragment; import java.util.HashSet; public class FragmentUtils { - private static final HashSet<String> sLatinImeFragments = new HashSet<String>(); + private static final HashSet<String> sLatinImeFragments = new HashSet<>(); static { sLatinImeFragments.add(DictionarySettingsFragment.class.getName()); sLatinImeFragments.add(AboutPreferences.class.getName()); @@ -43,7 +42,6 @@ public class FragmentUtils { sLatinImeFragments.add(UserDictionaryList.class.getName()); sLatinImeFragments.add(UserDictionaryLocalePicker.class.getName()); sLatinImeFragments.add(UserDictionarySettings.class.getName()); - sLatinImeFragments.add(FeedbackFragment.class.getName()); } public static boolean isValidFragment(String fragmentName) { diff --git a/java/src/com/android/inputmethod/latin/utils/JsonUtils.java b/java/src/com/android/inputmethod/latin/utils/JsonUtils.java index 764ef72ce..6dd8d9711 100644 --- a/java/src/com/android/inputmethod/latin/utils/JsonUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/JsonUtils.java @@ -37,7 +37,7 @@ public final class JsonUtils { private static final String EMPTY_STRING = ""; public static List<Object> jsonStrToList(final String s) { - final ArrayList<Object> list = CollectionUtils.newArrayList(); + final ArrayList<Object> list = new ArrayList<>(); final JsonReader reader = new JsonReader(new StringReader(s)); try { reader.beginArray(); diff --git a/java/src/com/android/inputmethod/latin/utils/LanguageModelParam.java b/java/src/com/android/inputmethod/latin/utils/LanguageModelParam.java index aaf4a4064..4248bebf6 100644 --- a/java/src/com/android/inputmethod/latin/utils/LanguageModelParam.java +++ b/java/src/com/android/inputmethod/latin/utils/LanguageModelParam.java @@ -19,11 +19,12 @@ package com.android.inputmethod.latin.utils; import android.util.Log; import com.android.inputmethod.latin.Dictionary; -import com.android.inputmethod.latin.DictionaryFacilitatorForSuggest; +import com.android.inputmethod.latin.DictionaryFacilitator; import com.android.inputmethod.latin.PrevWordsInfo; import com.android.inputmethod.latin.settings.SpacingAndPunctuations; import java.util.ArrayList; +import java.util.List; import java.util.Locale; // Note: this class is used as a parameter type of a native method. You should be careful when you @@ -79,14 +80,13 @@ public final class LanguageModelParam { // Process a list of words and return a list of {@link LanguageModelParam} objects. public static ArrayList<LanguageModelParam> createLanguageModelParamsFrom( - final ArrayList<String> tokens, final int timestamp, - final DictionaryFacilitatorForSuggest dictionaryFacilitator, + final List<String> tokens, final int timestamp, + final DictionaryFacilitator dictionaryFacilitator, final SpacingAndPunctuations spacingAndPunctuations, final DistracterFilter distracterFilter) { - final ArrayList<LanguageModelParam> languageModelParams = - CollectionUtils.newArrayList(); + final ArrayList<LanguageModelParam> languageModelParams = new ArrayList<>(); final int N = tokens.size(); - PrevWordsInfo prevWordsInfo = new PrevWordsInfo(null); + PrevWordsInfo prevWordsInfo = PrevWordsInfo.EMPTY_PREV_WORDS_INFO; for (int i = 0; i < N; ++i) { final String tempWord = tokens.get(i); if (StringUtils.isEmptyStringOrWhiteSpaces(tempWord)) { @@ -103,7 +103,7 @@ public final class LanguageModelParam { + tempWord + "\""); } // Sentence terminator found. Split. - prevWordsInfo = new PrevWordsInfo(null); + prevWordsInfo = PrevWordsInfo.EMPTY_PREV_WORDS_INFO; continue; } if (DEBUG_TOKEN) { @@ -124,43 +124,33 @@ public final class LanguageModelParam { private static LanguageModelParam detectWhetherVaildWordOrNotAndGetLanguageModelParam( final PrevWordsInfo prevWordsInfo, final String targetWord, final int timestamp, - final DictionaryFacilitatorForSuggest dictionaryFacilitator, + final DictionaryFacilitator dictionaryFacilitator, final DistracterFilter distracterFilter) { final Locale locale = dictionaryFacilitator.getLocale(); if (locale == null) { return null; } - // TODO: Though targetWord is an IV (in-vocabulary) word, we should still apply - // distracterFilter in the following code. If targetWord is a distracter, - // it should be filtered out. if (dictionaryFacilitator.isValidWord(targetWord, false /* ignoreCase */)) { return createAndGetLanguageModelParamOfWord(prevWordsInfo, targetWord, timestamp, - true /* isValidWord */, locale); + true /* isValidWord */, locale, distracterFilter); } final String lowerCaseTargetWord = targetWord.toLowerCase(locale); if (dictionaryFacilitator.isValidWord(lowerCaseTargetWord, false /* ignoreCase */)) { // Add the lower-cased word. return createAndGetLanguageModelParamOfWord(prevWordsInfo, lowerCaseTargetWord, - timestamp, true /* isValidWord */, locale); + timestamp, true /* isValidWord */, locale, distracterFilter); } - // Treat the word as an OOV word. The following statement checks whether this OOV - // is a distracter to words in dictionaries. Being a distracter means the OOV word is - // too close to a common word in dictionaries (e.g., the OOV "mot" is very close to "not"). - // Adding such a word to dictonaries would interfere with entering in-dictionary words. For - // example, adding "mot" to dictionaries might interfere with entering "not". - // This kind of OOV should be filtered out. - if (distracterFilter.isDistracterToWordsInDictionaries(prevWordsInfo, targetWord, locale)) { - return null; - } + // Treat the word as an OOV word. return createAndGetLanguageModelParamOfWord(prevWordsInfo, targetWord, timestamp, - false /* isValidWord */, locale); + false /* isValidWord */, locale, distracterFilter); } private static LanguageModelParam createAndGetLanguageModelParamOfWord( final PrevWordsInfo prevWordsInfo, final String targetWord, final int timestamp, - final boolean isValidWord, final Locale locale) { + final boolean isValidWord, final Locale locale, + final DistracterFilter distracterFilter) { final String word; if (StringUtils.getCapitalizationType(targetWord) == StringUtils.CAPITALIZE_FIRST && prevWordsInfo.mPrevWord == null && !isValidWord) { @@ -168,6 +158,13 @@ public final class LanguageModelParam { } else { word = targetWord; } + // Check whether the word is a distracter to words in the dictionaries. + if (distracterFilter.isDistracterToWordsInDictionaries(prevWordsInfo, word, locale)) { + if (DEBUG) { + Log.d(TAG, "The word (" + word + ") is a distracter. Skip this word."); + } + return null; + } final int unigramProbability = isValidWord ? UNIGRAM_PROBABILITY_FOR_VALID_WORD : UNIGRAM_PROBABILITY_FOR_OOV_WORD; if (prevWordsInfo.mPrevWord == null) { diff --git a/java/src/com/android/inputmethod/latin/utils/LatinImeLoggerUtils.java b/java/src/com/android/inputmethod/latin/utils/LatinImeLoggerUtils.java deleted file mode 100644 index d14ba508b..000000000 --- a/java/src/com/android/inputmethod/latin/utils/LatinImeLoggerUtils.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2013 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.text.TextUtils; - -import com.android.inputmethod.latin.Constants; -import com.android.inputmethod.latin.LatinImeLogger; -import com.android.inputmethod.latin.WordComposer; - -public final class LatinImeLoggerUtils { - private LatinImeLoggerUtils() { - // This utility class is not publicly instantiable. - } - - public static void onNonSeparator(final char code, final int x, final int y) { - UserLogRingCharBuffer.getInstance().push(code, x, y); - LatinImeLogger.logOnInputChar(); - } - - public static void onSeparator(final int code, final int x, final int y) { - // Helper method to log a single code point separator - // TODO: cache this mapping of a code point to a string in a sparse array in StringUtils - onSeparator(StringUtils.newSingleCodePointString(code), x, y); - } - - public static void onSeparator(final String separator, final int x, final int y) { - final int length = separator.length(); - for (int i = 0; i < length; i = Character.offsetByCodePoints(separator, i, 1)) { - int codePoint = Character.codePointAt(separator, i); - // TODO: accept code points - UserLogRingCharBuffer.getInstance().push((char)codePoint, x, y); - } - LatinImeLogger.logOnInputSeparator(); - } - - public static void onAutoCorrection(final String typedWord, final String correctedWord, - final String separatorString, final WordComposer wordComposer) { - final boolean isBatchMode = wordComposer.isBatchMode(); - if (!isBatchMode && TextUtils.isEmpty(typedWord)) { - return; - } - // TODO: this fails when the separator is more than 1 code point long, but - // the backend can't handle it yet. The only case when this happens is with - // smileys and other multi-character keys. - final int codePoint = TextUtils.isEmpty(separatorString) ? Constants.NOT_A_CODE - : separatorString.codePointAt(0); - if (!isBatchMode) { - LatinImeLogger.logOnAutoCorrectionForTyping(typedWord, correctedWord, codePoint); - } else { - if (!TextUtils.isEmpty(correctedWord)) { - // We must make sure that InputPointer contains only the relative timestamps, - // not actual timestamps. - LatinImeLogger.logOnAutoCorrectionForGeometric( - "", correctedWord, codePoint, wordComposer.getInputPointers()); - } - } - } - - public static void onAutoCorrectionCancellation() { - LatinImeLogger.logOnAutoCorrectionCancelled(); - } -} diff --git a/java/src/com/android/inputmethod/latin/utils/LeakGuardHandlerWrapper.java b/java/src/com/android/inputmethod/latin/utils/LeakGuardHandlerWrapper.java index 8469c87b0..dd6fac671 100644 --- a/java/src/com/android/inputmethod/latin/utils/LeakGuardHandlerWrapper.java +++ b/java/src/com/android/inputmethod/latin/utils/LeakGuardHandlerWrapper.java @@ -33,7 +33,7 @@ public class LeakGuardHandlerWrapper<T> extends Handler { if (ownerInstance == null) { throw new NullPointerException("ownerInstance is null"); } - mOwnerInstanceRef = new WeakReference<T>(ownerInstance); + mOwnerInstanceRef = new WeakReference<>(ownerInstance); } public T getOwnerInstance() { diff --git a/java/src/com/android/inputmethod/latin/utils/LocaleUtils.java b/java/src/com/android/inputmethod/latin/utils/LocaleUtils.java index 0c55484b4..c519a0de6 100644 --- a/java/src/com/android/inputmethod/latin/utils/LocaleUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/LocaleUtils.java @@ -159,7 +159,7 @@ public final class LocaleUtils { return LOCALE_MATCH <= level; } - private static final HashMap<String, Locale> sLocaleCache = CollectionUtils.newHashMap(); + private static final HashMap<String, Locale> sLocaleCache = new HashMap<>(); /** * Creates a locale from a string specification. diff --git a/java/src/com/android/inputmethod/latin/utils/PrioritizedSerialExecutor.java b/java/src/com/android/inputmethod/latin/utils/PrioritizedSerialExecutor.java deleted file mode 100644 index bf38abc95..000000000 --- a/java/src/com/android/inputmethod/latin/utils/PrioritizedSerialExecutor.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2013 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 com.android.inputmethod.annotations.UsedForTesting; - -import java.util.Queue; -import java.util.concurrent.ArrayBlockingQueue; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -/** - * An object that executes submitted tasks using a thread. - */ -public class PrioritizedSerialExecutor { - public static final String TAG = PrioritizedSerialExecutor.class.getSimpleName(); - - private final Object mLock = new Object(); - - private final Queue<Runnable> mTasks; - private final Queue<Runnable> mPrioritizedTasks; - private boolean mIsShutdown; - private final ThreadPoolExecutor mThreadPoolExecutor; - - // The task which is running now. - private Runnable mActive; - - public PrioritizedSerialExecutor() { - mTasks = new ConcurrentLinkedQueue<Runnable>(); - mPrioritizedTasks = new ConcurrentLinkedQueue<Runnable>(); - mIsShutdown = false; - mThreadPoolExecutor = new ThreadPoolExecutor(1 /* corePoolSize */, 1 /* maximumPoolSize */, - 0 /* keepAliveTime */, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(1)); - } - - /** - * Enqueues the given task into the task queue. - * @param r the enqueued task - */ - public void execute(final Runnable r) { - synchronized(mLock) { - if (!mIsShutdown) { - mTasks.offer(new Runnable() { - @Override - public void run() { - try { - r.run(); - } finally { - scheduleNext(); - } - } - }); - if (mActive == null) { - scheduleNext(); - } - } - } - } - - /** - * Enqueues the given task into the prioritized task queue. - * @param r the enqueued task - */ - @UsedForTesting - public void executePrioritized(final Runnable r) { - synchronized(mLock) { - if (!mIsShutdown) { - mPrioritizedTasks.offer(new Runnable() { - @Override - public void run() { - try { - r.run(); - } finally { - scheduleNext(); - } - } - }); - if (mActive == null) { - scheduleNext(); - } - } - } - } - - private boolean fetchNextTasksLocked() { - mActive = mPrioritizedTasks.poll(); - if (mActive == null) { - mActive = mTasks.poll(); - } - return mActive != null; - } - - private void scheduleNext() { - synchronized(mLock) { - if (fetchNextTasksLocked()) { - mThreadPoolExecutor.execute(mActive); - } - } - } - - public void shutdown() { - synchronized(mLock) { - mIsShutdown = true; - mThreadPoolExecutor.shutdown(); - } - } -} diff --git a/java/src/com/android/inputmethod/latin/utils/ResourceUtils.java b/java/src/com/android/inputmethod/latin/utils/ResourceUtils.java index 49f4929b4..093c5a6c1 100644 --- a/java/src/com/android/inputmethod/latin/utils/ResourceUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/ResourceUtils.java @@ -41,8 +41,7 @@ public final class ResourceUtils { // This utility class is not publicly instantiable. } - private static final HashMap<String, String> sDeviceOverrideValueMap = - CollectionUtils.newHashMap(); + private static final HashMap<String, String> sDeviceOverrideValueMap = new HashMap<>(); private static final String[] BUILD_KEYS_AND_VALUES = { "HARDWARE", Build.HARDWARE, @@ -54,8 +53,8 @@ public final class ResourceUtils { private static final String sBuildKeyValuesDebugString; static { - sBuildKeyValues = CollectionUtils.newHashMap(); - final ArrayList<String> keyValuePairs = CollectionUtils.newArrayList(); + sBuildKeyValues = new HashMap<>(); + final ArrayList<String> keyValuePairs = new ArrayList<>(); final int keyCount = BUILD_KEYS_AND_VALUES.length / 2; for (int i = 0; i < keyCount; i++) { final int index = i * 2; diff --git a/java/src/com/android/inputmethod/latin/utils/StringUtils.java b/java/src/com/android/inputmethod/latin/utils/StringUtils.java index 73ac9a573..4ed0f0a94 100644 --- a/java/src/com/android/inputmethod/latin/utils/StringUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/StringUtils.java @@ -28,7 +28,6 @@ import java.util.Arrays; import java.util.Locale; public final class StringUtils { - private static final String TAG = StringUtils.class.getSimpleName(); public static final int CAPITALIZE_NONE = 0; // No caps, or mixed case public static final int CAPITALIZE_FIRST = 1; // First only public static final int CAPITALIZE_ALL = 2; // All caps @@ -110,7 +109,7 @@ public final class StringUtils { if (!containsInArray(text, elements)) { return extraValues; } - final ArrayList<String> result = CollectionUtils.newArrayList(elements.length - 1); + final ArrayList<String> result = new ArrayList<>(elements.length - 1); for (final String element : elements) { if (!text.equals(element)) { result.add(element); diff --git a/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java b/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java index 938d27122..351d01400 100644 --- a/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java @@ -49,17 +49,14 @@ public final class SubtypeLocaleUtils { private static Resources sResources; private static String[] sPredefinedKeyboardLayoutSet; // Keyboard layout to its display name map. - private static final HashMap<String, String> sKeyboardLayoutToDisplayNameMap = - CollectionUtils.newHashMap(); + private static final HashMap<String, String> sKeyboardLayoutToDisplayNameMap = new HashMap<>(); // Keyboard layout to subtype name resource id map. - private static final HashMap<String, Integer> sKeyboardLayoutToNameIdsMap = - CollectionUtils.newHashMap(); + private static final HashMap<String, Integer> sKeyboardLayoutToNameIdsMap = new HashMap<>(); // Exceptional locale to subtype name resource id map. - private static final HashMap<String, Integer> sExceptionalLocaleToNameIdsMap = - CollectionUtils.newHashMap(); + private static final HashMap<String, Integer> sExceptionalLocaleToNameIdsMap = new HashMap<>(); // Exceptional locale to subtype name with layout resource id map. private static final HashMap<String, Integer> sExceptionalLocaleToWithLayoutNameIdsMap = - CollectionUtils.newHashMap(); + new HashMap<>(); private static final String SUBTYPE_NAME_RESOURCE_PREFIX = "string/subtype_"; private static final String SUBTYPE_NAME_RESOURCE_GENERIC_PREFIX = @@ -71,7 +68,7 @@ public final class SubtypeLocaleUtils { // Keyboard layout set name for the subtypes that don't have a keyboardLayoutSet extra value. // This is for compatibility to keep the same subtype ids as pre-JellyBean. private static final HashMap<String, String> sLocaleAndExtraValueToKeyboardLayoutSetMap = - CollectionUtils.newHashMap(); + new HashMap<>(); private SubtypeLocaleUtils() { // Intentional empty constructor for utility class. diff --git a/java/src/com/android/inputmethod/latin/utils/TargetPackageInfoGetterTask.java b/java/src/com/android/inputmethod/latin/utils/TargetPackageInfoGetterTask.java index 42ea3c959..ab2b00e36 100644 --- a/java/src/com/android/inputmethod/latin/utils/TargetPackageInfoGetterTask.java +++ b/java/src/com/android/inputmethod/latin/utils/TargetPackageInfoGetterTask.java @@ -27,8 +27,7 @@ import com.android.inputmethod.compat.AppWorkaroundsUtils; public final class TargetPackageInfoGetterTask extends AsyncTask<String, Void, PackageInfo> { private static final int MAX_CACHE_ENTRIES = 64; // arbitrary - private static final LruCache<String, PackageInfo> sCache = - new LruCache<String, PackageInfo>(MAX_CACHE_ENTRIES); + private static final LruCache<String, PackageInfo> sCache = new LruCache<>(MAX_CACHE_ENTRIES); public static PackageInfo getCachedPackageInfo(final String packageName) { if (null == packageName) return null; diff --git a/java/src/com/android/inputmethod/latin/utils/TypefaceUtils.java b/java/src/com/android/inputmethod/latin/utils/TypefaceUtils.java index 087a7f255..fafba79c2 100644 --- a/java/src/com/android/inputmethod/latin/utils/TypefaceUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/TypefaceUtils.java @@ -30,7 +30,7 @@ public final class TypefaceUtils { } // This sparse array caches key label text height in pixel indexed by key label text size. - private static final SparseArray<Float> sTextHeightCache = CollectionUtils.newSparseArray(); + private static final SparseArray<Float> sTextHeightCache = new SparseArray<>(); // Working variable for the following method. private static final Rect sTextHeightBounds = new Rect(); @@ -50,7 +50,7 @@ public final class TypefaceUtils { } // This sparse array caches key label text width in pixel indexed by key label text size. - private static final SparseArray<Float> sTextWidthCache = CollectionUtils.newSparseArray(); + private static final SparseArray<Float> sTextWidthCache = new SparseArray<>(); // Working variable for the following method. private static final Rect sTextWidthBounds = new Rect(); diff --git a/java/src/com/android/inputmethod/latin/utils/UsabilityStudyLogUtils.java b/java/src/com/android/inputmethod/latin/utils/UsabilityStudyLogUtils.java deleted file mode 100644 index 06826dac0..000000000 --- a/java/src/com/android/inputmethod/latin/utils/UsabilityStudyLogUtils.java +++ /dev/null @@ -1,293 +0,0 @@ -/* - * Copyright (C) 2016 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.Intent; -import android.content.pm.PackageManager; -import android.inputmethodservice.InputMethodService; -import android.net.Uri; -import android.os.Environment; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.Process; -import android.util.Log; -import android.view.MotionEvent; - -import com.android.inputmethod.latin.LatinImeLogger; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.FileReader; -import java.io.IOException; -import java.io.PrintWriter; -import java.nio.channels.FileChannel; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Locale; - -public final class UsabilityStudyLogUtils { - // TODO: remove code duplication with ResearchLog class - private static final String USABILITY_TAG = UsabilityStudyLogUtils.class.getSimpleName(); - private static final String FILENAME = "log.txt"; - private final Handler mLoggingHandler; - private File mFile; - private File mDirectory; - private InputMethodService mIms; - private PrintWriter mWriter; - private final Date mDate; - private final SimpleDateFormat mDateFormat; - - private UsabilityStudyLogUtils() { - mDate = new Date(); - mDateFormat = new SimpleDateFormat("yyyyMMdd-HHmmss.SSSZ", Locale.US); - - HandlerThread handlerThread = new HandlerThread("UsabilityStudyLogUtils logging task", - Process.THREAD_PRIORITY_BACKGROUND); - handlerThread.start(); - mLoggingHandler = new Handler(handlerThread.getLooper()); - } - - // Initialization-on-demand holder - private static final class OnDemandInitializationHolder { - public static final UsabilityStudyLogUtils sInstance = new UsabilityStudyLogUtils(); - } - - public static UsabilityStudyLogUtils getInstance() { - return OnDemandInitializationHolder.sInstance; - } - - public void init(final InputMethodService ims) { - mIms = ims; - mDirectory = ims.getFilesDir(); - } - - private void createLogFileIfNotExist() { - if ((mFile == null || !mFile.exists()) - && (mDirectory != null && mDirectory.exists())) { - try { - mWriter = getPrintWriter(mDirectory, FILENAME, false); - } catch (final IOException e) { - Log.e(USABILITY_TAG, "Can't create log file."); - } - } - } - - public static void writeBackSpace(final int x, final int y) { - UsabilityStudyLogUtils.getInstance().write("<backspace>\t" + x + "\t" + y); - } - - public static void writeChar(final char c, final int x, final int y) { - String inputChar = String.valueOf(c); - switch (c) { - case '\n': - inputChar = "<enter>"; - break; - case '\t': - inputChar = "<tab>"; - break; - case ' ': - inputChar = "<space>"; - break; - } - UsabilityStudyLogUtils.getInstance().write(inputChar + "\t" + x + "\t" + y); - LatinImeLogger.onPrintAllUsabilityStudyLogs(); - } - - public static void writeMotionEvent(final MotionEvent me) { - final int action = me.getActionMasked(); - final long eventTime = me.getEventTime(); - final int pointerCount = me.getPointerCount(); - for (int index = 0; index < pointerCount; index++) { - final int id = me.getPointerId(index); - final int x = (int)me.getX(index); - final int y = (int)me.getY(index); - final float size = me.getSize(index); - final float pressure = me.getPressure(index); - - final String eventTag; - switch (action) { - case MotionEvent.ACTION_UP: - eventTag = "[Up]"; - break; - case MotionEvent.ACTION_DOWN: - eventTag = "[Down]"; - break; - case MotionEvent.ACTION_POINTER_UP: - eventTag = "[PointerUp]"; - break; - case MotionEvent.ACTION_POINTER_DOWN: - eventTag = "[PointerDown]"; - break; - case MotionEvent.ACTION_MOVE: - eventTag = "[Move]"; - break; - default: - eventTag = "[Action" + action + "]"; - break; - } - getInstance().write(eventTag + eventTime + "," + id + "," + x + "," + y + "," + size - + "," + pressure); - } - } - - public void write(final String log) { - mLoggingHandler.post(new Runnable() { - @Override - public void run() { - createLogFileIfNotExist(); - final long currentTime = System.currentTimeMillis(); - mDate.setTime(currentTime); - - final String printString = String.format(Locale.US, "%s\t%d\t%s\n", - mDateFormat.format(mDate), currentTime, log); - if (LatinImeLogger.sDBG) { - Log.d(USABILITY_TAG, "Write: " + log); - } - mWriter.print(printString); - } - }); - } - - private synchronized String getBufferedLogs() { - mWriter.flush(); - final StringBuilder sb = new StringBuilder(); - final BufferedReader br = getBufferedReader(); - String line; - try { - while ((line = br.readLine()) != null) { - sb.append('\n'); - sb.append(line); - } - } catch (final IOException e) { - Log.e(USABILITY_TAG, "Can't read log file."); - } finally { - if (LatinImeLogger.sDBG) { - Log.d(USABILITY_TAG, "Got all buffered logs\n" + sb.toString()); - } - try { - br.close(); - } catch (final IOException e) { - // ignore. - } - } - return sb.toString(); - } - - public void emailResearcherLogsAll() { - mLoggingHandler.post(new Runnable() { - @Override - public void run() { - final Date date = new Date(); - date.setTime(System.currentTimeMillis()); - final String currentDateTimeString = - new SimpleDateFormat("yyyyMMdd-HHmmssZ", Locale.US).format(date); - if (mFile == null) { - Log.w(USABILITY_TAG, "No internal log file found."); - return; - } - if (mIms.checkCallingOrSelfPermission( - android.Manifest.permission.WRITE_EXTERNAL_STORAGE) - != PackageManager.PERMISSION_GRANTED) { - Log.w(USABILITY_TAG, "Doesn't have the permission WRITE_EXTERNAL_STORAGE"); - return; - } - mWriter.flush(); - final String destPath = Environment.getExternalStorageDirectory() - + "/research-" + currentDateTimeString + ".log"; - final File destFile = new File(destPath); - try { - final FileInputStream srcStream = new FileInputStream(mFile); - final FileOutputStream destStream = new FileOutputStream(destFile); - final FileChannel src = srcStream.getChannel(); - final FileChannel dest = destStream.getChannel(); - src.transferTo(0, src.size(), dest); - src.close(); - srcStream.close(); - dest.close(); - destStream.close(); - } catch (final FileNotFoundException e1) { - Log.w(USABILITY_TAG, e1); - return; - } catch (final IOException e2) { - Log.w(USABILITY_TAG, e2); - return; - } - if (!destFile.exists()) { - Log.w(USABILITY_TAG, "Dest file doesn't exist."); - return; - } - final Intent intent = new Intent(Intent.ACTION_SEND); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - if (LatinImeLogger.sDBG) { - Log.d(USABILITY_TAG, "Destination file URI is " + destFile.toURI()); - } - intent.setType("text/plain"); - intent.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://" + destPath)); - intent.putExtra(Intent.EXTRA_SUBJECT, - "[Research Logs] " + currentDateTimeString); - mIms.startActivity(intent); - } - }); - } - - public void printAll() { - mLoggingHandler.post(new Runnable() { - @Override - public void run() { - mIms.getCurrentInputConnection().commitText(getBufferedLogs(), 0); - } - }); - } - - public void clearAll() { - mLoggingHandler.post(new Runnable() { - @Override - public void run() { - if (mFile != null && mFile.exists()) { - if (LatinImeLogger.sDBG) { - Log.d(USABILITY_TAG, "Delete log file."); - } - mFile.delete(); - mWriter.close(); - } - } - }); - } - - private BufferedReader getBufferedReader() { - createLogFileIfNotExist(); - try { - return new BufferedReader(new FileReader(mFile)); - } catch (final FileNotFoundException e) { - return null; - } - } - - private PrintWriter getPrintWriter(final File dir, final String filename, - final boolean renew) throws IOException { - mFile = new File(dir, filename); - if (mFile.exists()) { - if (renew) { - mFile.delete(); - } - } - return new PrintWriter(new FileOutputStream(mFile), true /* autoFlush */); - } -} diff --git a/java/src/com/android/inputmethod/latin/utils/UserLogRingCharBuffer.java b/java/src/com/android/inputmethod/latin/utils/UserLogRingCharBuffer.java deleted file mode 100644 index a75d353c9..000000000 --- a/java/src/com/android/inputmethod/latin/utils/UserLogRingCharBuffer.java +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2013 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.inputmethodservice.InputMethodService; - -import com.android.inputmethod.annotations.UsedForTesting; -import com.android.inputmethod.latin.LatinImeLogger; -import com.android.inputmethod.latin.settings.Settings; - -public final class UserLogRingCharBuffer { - public /* for test */ static final int BUFSIZE = 20; - public /* for test */ int mLength = 0; - - private static UserLogRingCharBuffer sUserLogRingCharBuffer = new UserLogRingCharBuffer(); - private static final char PLACEHOLDER_DELIMITER_CHAR = '\uFFFC'; - private static final int INVALID_COORDINATE = -2; - private boolean mEnabled = false; - private int mEnd = 0; - private char[] mCharBuf = new char[BUFSIZE]; - private int[] mXBuf = new int[BUFSIZE]; - private int[] mYBuf = new int[BUFSIZE]; - - private UserLogRingCharBuffer() { - // Intentional empty constructor for singleton. - } - - @UsedForTesting - public static UserLogRingCharBuffer getInstance() { - return sUserLogRingCharBuffer; - } - - public static UserLogRingCharBuffer init(final InputMethodService context, - final boolean enabled, final boolean usabilityStudy) { - if (!(enabled || usabilityStudy)) { - return null; - } - sUserLogRingCharBuffer.mEnabled = true; - UsabilityStudyLogUtils.getInstance().init(context); - return sUserLogRingCharBuffer; - } - - private static int normalize(final int in) { - int ret = in % BUFSIZE; - return ret < 0 ? ret + BUFSIZE : ret; - } - - // TODO: accept code points - @UsedForTesting - public void push(final char c, final int x, final int y) { - if (!mEnabled) { - return; - } - if (LatinImeLogger.sUsabilityStudy) { - UsabilityStudyLogUtils.getInstance().writeChar(c, x, y); - } - mCharBuf[mEnd] = c; - mXBuf[mEnd] = x; - mYBuf[mEnd] = y; - mEnd = normalize(mEnd + 1); - if (mLength < BUFSIZE) { - ++mLength; - } - } - - public char pop() { - if (mLength < 1) { - return PLACEHOLDER_DELIMITER_CHAR; - } - mEnd = normalize(mEnd - 1); - --mLength; - return mCharBuf[mEnd]; - } - - public char getBackwardNthChar(final int n) { - if (mLength <= n || n < 0) { - return PLACEHOLDER_DELIMITER_CHAR; - } - return mCharBuf[normalize(mEnd - n - 1)]; - } - - public int getPreviousX(final char c, final int back) { - final int index = normalize(mEnd - 2 - back); - if (mLength <= back - || Character.toLowerCase(c) != Character.toLowerCase(mCharBuf[index])) { - return INVALID_COORDINATE; - } - return mXBuf[index]; - } - - public int getPreviousY(final char c, final int back) { - int index = normalize(mEnd - 2 - back); - if (mLength <= back - || Character.toLowerCase(c) != Character.toLowerCase(mCharBuf[index])) { - return INVALID_COORDINATE; - } - return mYBuf[index]; - } - - public String getLastWord(final int ignoreCharCount) { - final StringBuilder sb = new StringBuilder(); - int i = ignoreCharCount; - for (; i < mLength; ++i) { - final char c = mCharBuf[normalize(mEnd - 1 - i)]; - if (!Settings.getInstance().isWordSeparator(c)) { - break; - } - } - for (; i < mLength; ++i) { - char c = mCharBuf[normalize(mEnd - 1 - i)]; - if (!Settings.getInstance().isWordSeparator(c)) { - sb.append(c); - } else { - break; - } - } - return sb.reverse().toString(); - } - - public void reset() { - mLength = 0; - } -} |