diff --git a/java/src/com/android/inputmethod/latin/ContactsContentObserver.java b/java/src/com/android/inputmethod/latin/ContactsContentObserver.java index 5eb9b16d1..561bac36e 100644 --- a/java/src/com/android/inputmethod/latin/ContactsContentObserver.java +++ b/java/src/com/android/inputmethod/latin/ContactsContentObserver.java @@ -24,6 +24,7 @@ import android.provider.ContactsContract.Contacts; import android.util.Log; import com.android.inputmethod.latin.ContactsManager.ContactsChangedListener; +import com.android.inputmethod.latin.define.DebugFlags; import com.android.inputmethod.latin.utils.ExecutorUtils; import java.util.ArrayList; @@ -33,8 +34,7 @@ import java.util.concurrent.atomic.AtomicBoolean; * A content observer that listens to updates to content provider {@link Contacts#CONTENT_URI}. */ public class ContactsContentObserver implements Runnable { - private static final String TAG = ContactsContentObserver.class.getSimpleName(); - private static final boolean DEBUG = false; + private static final String TAG = "ContactsContentObserver"; private static AtomicBoolean sRunning = new AtomicBoolean(false); private final Context mContext; @@ -49,8 +49,8 @@ public class ContactsContentObserver implements Runnable { } public void registerObserver(final ContactsChangedListener listener) { - if (DEBUG) { - Log.d(TAG, "Registered Contacts Content Observer"); + if (DebugFlags.DEBUG_ENABLED) { + Log.d(TAG, "registerObserver()"); } mContactsChangedListener = listener; mContentObserver = new ContentObserver(null /* handler */) { @@ -67,13 +67,13 @@ public class ContactsContentObserver implements Runnable { @Override public void run() { if (!sRunning.compareAndSet(false /* expect */, true /* update */)) { - if (DEBUG) { + if (DebugFlags.DEBUG_ENABLED) { Log.d(TAG, "run() : Already running. Don't waste time checking again."); } return; } if (haveContentsChanged()) { - if (DEBUG) { + if (DebugFlags.DEBUG_ENABLED) { Log.d(TAG, "run() : Contacts have changed. Notifying listeners."); } mContactsChangedListener.onContactsChange(); @@ -91,9 +91,9 @@ public class ContactsContentObserver implements Runnable { return false; } if (contactCount != mManager.getContactCountAtLastRebuild()) { - if (DEBUG) { - Log.d(TAG, "Contact count changed: " + mManager.getContactCountAtLastRebuild() - + " to " + contactCount); + if (DebugFlags.DEBUG_ENABLED) { + Log.d(TAG, "haveContentsChanged() : Count changed from " + + mManager.getContactCountAtLastRebuild() + " to " + contactCount); } return true; } @@ -101,9 +101,9 @@ public class ContactsContentObserver implements Runnable { if (names.hashCode() != mManager.getHashCodeAtLastRebuild()) { return true; } - if (DEBUG) { - Log.d(TAG, "No contacts changed. (runtime = " + (SystemClock.uptimeMillis() - startTime) - + " ms)"); + if (DebugFlags.DEBUG_ENABLED) { + Log.d(TAG, "haveContentsChanged() : No change detected in " + + (SystemClock.uptimeMillis() - startTime) + " ms)"); } return false; } diff --git a/java/src/com/android/inputmethod/latin/ContactsManager.java b/java/src/com/android/inputmethod/latin/ContactsManager.java index 1fadc6f6f..7a971cf75 100644 --- a/java/src/com/android/inputmethod/latin/ContactsManager.java +++ b/java/src/com/android/inputmethod/latin/ContactsManager.java @@ -35,8 +35,7 @@ import java.util.concurrent.atomic.AtomicInteger; * measure of the current state of the content provider. */ public class ContactsManager { - private static final String TAG = ContactsManager.class.getSimpleName(); - private static final boolean DEBUG = false; + private static final String TAG = "ContactsManager"; /** * Interface to implement for classes interested in getting notified for updates diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java index d5dff10db..ff798abd6 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java @@ -45,31 +45,14 @@ public interface DictionaryFacilitator { public static final String[] ALL_DICTIONARY_TYPES = new String[] { Dictionary.TYPE_MAIN, + Dictionary.TYPE_CONTACTS, Dictionary.TYPE_USER_HISTORY, - Dictionary.TYPE_USER, - Dictionary.TYPE_CONTACTS}; + Dictionary.TYPE_USER}; public static final String[] DYNAMIC_DICTIONARY_TYPES = new String[] { + Dictionary.TYPE_CONTACTS, Dictionary.TYPE_USER_HISTORY, - Dictionary.TYPE_USER, - Dictionary.TYPE_CONTACTS}; - - /** - * {@link Dictionary#TYPE_USER} is deprecated, except for the spelling service. - */ - public static final String[] DICTIONARY_TYPES_FOR_SPELLING = new String[] { - Dictionary.TYPE_MAIN, - Dictionary.TYPE_USER_HISTORY, - Dictionary.TYPE_USER, - Dictionary.TYPE_CONTACTS}; - - /** - * {@link Dictionary#TYPE_USER} is deprecated, except for the spelling service. - */ - public static final String[] DICTIONARY_TYPES_FOR_SUGGESTIONS = new String[] { - Dictionary.TYPE_MAIN, - Dictionary.TYPE_USER_HISTORY, - Dictionary.TYPE_CONTACTS}; + Dictionary.TYPE_USER}; /** * Returns whether this facilitator is exactly for this locale. diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorImpl.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitatorImpl.java index 9ce92da9e..7233d27ab 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorImpl.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitatorImpl.java @@ -557,7 +557,7 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator { false /* firstSuggestionExceedsConfidenceThreshold */); final float[] weightOfLangModelVsSpatialModel = new float[] { Dictionary.NOT_A_WEIGHT_OF_LANG_MODEL_VS_SPATIAL_MODEL }; - for (final String dictType : DICTIONARY_TYPES_FOR_SUGGESTIONS) { + for (final String dictType : ALL_DICTIONARY_TYPES) { final Dictionary dictionary = mDictionaryGroup.getDict(dictType); if (null == dictionary) continue; final float weightForLocale = composedData.mIsBatchMode @@ -577,11 +577,11 @@ public class DictionaryFacilitatorImpl implements DictionaryFacilitator { } public boolean isValidSpellingWord(final String word) { - return isValidWord(word, DICTIONARY_TYPES_FOR_SPELLING); + return isValidWord(word, ALL_DICTIONARY_TYPES); } public boolean isValidSuggestionWord(final String word) { - return isValidWord(word, DICTIONARY_TYPES_FOR_SUGGESTIONS); + return isValidWord(word, ALL_DICTIONARY_TYPES); } private boolean isValidWord(final String word, final String[] dictionariesToCheck) { diff --git a/java/src/com/android/inputmethod/latin/UserDictionaryLookup.java b/java/src/com/android/inputmethod/latin/PersonalDictionaryLookup.java similarity index 59% rename from java/src/com/android/inputmethod/latin/UserDictionaryLookup.java rename to java/src/com/android/inputmethod/latin/PersonalDictionaryLookup.java index 1eed3d79c..1ba075c54 100644 --- a/java/src/com/android/inputmethod/latin/UserDictionaryLookup.java +++ b/java/src/com/android/inputmethod/latin/PersonalDictionaryLookup.java @@ -28,13 +28,18 @@ import android.util.Log; import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.common.CollectionUtils; import com.android.inputmethod.latin.common.LocaleUtils; +import com.android.inputmethod.latin.define.DebugFlags; import com.android.inputmethod.latin.utils.ExecutorUtils; import java.io.Closeable; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Locale; +import java.util.Map; +import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -43,21 +48,22 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; /** - * UserDictionaryLookup provides the ability to lookup into the system-wide "Personal dictionary". + * This class provides the ability to look into the system-wide "Personal dictionary". It loads the + * data once when created and reloads it when notified of changes to {@link UserDictionary} + * + * It can be used directly to validate words or expand shortcuts, and it can be used by instances + * of {@link PersonalLanguageModelHelper} that create language model files for a specific input + * locale. * * Note, that the initial dictionary loading happens asynchronously so it is possible (hopefully - * rarely) that isValidWord is called before the initial load has started. + * rarely) that {@link #isValidWord} or {@link #expandShortcut} is called before the initial load + * has started. * - * The caller should explicitly call close() when the object is no longer needed, in order to - * release any resources and references to this object. A service should create this object in - * onCreate and close() it in onDestroy. + * The caller should explicitly call {@link #close} when the object is no longer needed, in order + * to release any resources and references to this object. A service should create this object in + * {@link android.app.Service#onCreate} and close it in {@link android.app.Service#onDestroy}. */ -public class UserDictionaryLookup implements Closeable { - - /** - * This guards the execution of any Log.d() logging, so that if false, they are not even - */ - private static final boolean DEBUG = false; +public class PersonalDictionaryLookup implements Closeable { /** * To avoid loading too many dictionary entries in memory, we cap them at this number. If @@ -92,28 +98,42 @@ public class UserDictionaryLookup implements Closeable { private final String mServiceName; /** - * Runnable that calls loadUserDictionary(). + * Interface to implement for classes interested in getting notified of updates. */ - private class UserDictionaryLoader implements Runnable { - @Override - public void run() { - if (DEBUG) { - Log.d(mTag, "Executing (re)load"); - } - loadUserDictionary(); + public static interface PersonalDictionaryListener { + public void onUpdate(); + } + + private final Set mListeners = new HashSet<>(); + + public void addListener(@Nonnull final PersonalDictionaryListener listener) { + mListeners.add(listener); + } + + public void removeListener(@Nonnull final PersonalDictionaryListener listener) { + mListeners.remove(listener); + } + + /** + * Broadcast the update to all the Locale-specific language models. + */ + @UsedForTesting + void notifyListeners() { + for (PersonalDictionaryListener listener : mListeners) { + listener.onUpdate(); } } /** - * Content observer for UserDictionary changes. It has the following properties: - * 1. It spawns off a UserDictionary reload in another thread, after some delay. + * Content observer for changes to the personal dictionary. It has the following properties: + * 1. It spawns off a reload in another thread, after some delay. * 2. It cancels previously scheduled reloads, and only executes the latest. * 3. It may be called multiple times quickly in succession (and is in fact called so - * when UserDictionary is edited through its settings UI, when sometimes multiple - * notifications are sent for the edited entry, but also for the entire UserDictionary). + * when the dictionary is edited through its settings UI, when sometimes multiple + * notifications are sent for the edited entry, but also for the entire dictionary). */ - private class UserDictionaryContentObserver extends ContentObserver { - public UserDictionaryContentObserver() { + private class PersonalDictionaryContentObserver extends ContentObserver implements Runnable { + public PersonalDictionaryContentObserver() { super(null); } @@ -130,33 +150,40 @@ public class UserDictionaryLookup implements Closeable { @Override public void onChange(boolean selfChange, Uri uri) { - if (DEBUG) { - Log.d(mTag, "Received content observer onChange notification for URI: " + uri); + if (DebugFlags.DEBUG_ENABLED) { + Log.d(mTag, "onChange() : URI = " + uri); } // Cancel (but don't interrupt) any pending reloads (except the initial load). if (mReloadFuture != null && !mReloadFuture.isCancelled() && !mReloadFuture.isDone()) { // Note, that if already cancelled or done, this will do nothing. boolean isCancelled = mReloadFuture.cancel(false); - if (DEBUG) { + if (DebugFlags.DEBUG_ENABLED) { if (isCancelled) { - Log.d(mTag, "Successfully canceled previous reload request"); + Log.d(mTag, "onChange() : Canceled previous reload request"); } else { - Log.d(mTag, "Unable to cancel previous reload request"); + Log.d(mTag, "onChange() : Failed to cancel previous reload request"); } } } - if (DEBUG) { - Log.d(mTag, "Scheduling reload in " + RELOAD_DELAY_MS + " ms"); + if (DebugFlags.DEBUG_ENABLED) { + Log.d(mTag, "onChange() : Scheduling reload in " + RELOAD_DELAY_MS + " ms"); } // Schedule a new reload after RELOAD_DELAY_MS. mReloadFuture = ExecutorUtils.getBackgroundExecutor(mServiceName) - .schedule(new UserDictionaryLoader(), RELOAD_DELAY_MS, TimeUnit.MILLISECONDS); + .schedule(this, RELOAD_DELAY_MS, TimeUnit.MILLISECONDS); + } + + @Override + public void run() { + loadPersonalDictionary(); } } - private final ContentObserver mObserver = new UserDictionaryContentObserver(); + + private final PersonalDictionaryContentObserver mPersonalDictionaryContentObserver = + new PersonalDictionaryContentObserver(); /** * Indicates that a load is in progress, so no need for another. @@ -192,8 +219,10 @@ public class UserDictionaryLookup implements Closeable { /** * @param context the context from which to obtain content resolver */ - public UserDictionaryLookup(@Nonnull final Context context, @Nonnull final String serviceName) { - mTag = serviceName + ".UserDictionaryLookup"; + public PersonalDictionaryLookup( + @Nonnull final Context context, + @Nonnull final String serviceName) { + mTag = serviceName + ".Personal"; Log.i(mTag, "create()"); @@ -216,12 +245,12 @@ public class UserDictionaryLookup implements Closeable { // Schedule the initial load to run immediately. It's possible that the first call to // isValidWord occurs before the dictionary has actually loaded, so it should not // assume that the dictionary has been loaded. - loadUserDictionary(); + loadPersonalDictionary(); - // Register the observer to be notified on changes to the UserDictionary and all individual - // items. + // Register the observer to be notified on changes to the personal dictionary and all + // individual items. // - // If the user is interacting with the UserDictionary settings UI, or with the + // If the user is interacting with the Personal Dictionary settings UI, or with the // "Add to dictionary" drop-down option, duplicate notifications will be sent for the same // edit: if a new entry is added, there is a notification for the entry itself, and // separately for the entire dictionary. However, when used programmatically, @@ -229,7 +258,9 @@ public class UserDictionaryLookup implements Closeable { // receive every possible notification, and instead has throttling logic to avoid doing too // many reloads. mResolver.registerContentObserver( - UserDictionary.Words.CONTENT_URI, true /* notifyForDescendents */, mObserver); + UserDictionary.Words.CONTENT_URI, + true /* notifyForDescendents */, + mPersonalDictionaryContentObserver); } /** @@ -239,8 +270,8 @@ public class UserDictionaryLookup implements Closeable { @Override public void finalize() throws Throwable { try { - if (DEBUG) { - Log.d(mTag, "Finalize called, calling close()"); + if (DebugFlags.DEBUG_ENABLED) { + Log.d(mTag, "finalize()"); } close(); } finally { @@ -249,19 +280,19 @@ public class UserDictionaryLookup implements Closeable { } /** - * Cleans up UserDictionaryLookup: shuts down any extra threads and unregisters the observer. + * Cleans up PersonalDictionaryLookup: shuts down any extra threads and unregisters the observer. * * It is safe, but not advised to call this multiple times, and isValidWord would continue to * work, but no data will be reloaded any longer. */ @Override public void close() { - if (DEBUG) { - Log.d(mTag, "Close called (no pun intended), cleaning up executor and observer"); + if (DebugFlags.DEBUG_ENABLED) { + Log.d(mTag, "close() : Unregistering content observer"); } if (mIsClosed.compareAndSet(false, true)) { // Unregister the content observer. - mResolver.unregisterContentObserver(mObserver); + mResolver.unregisterContentObserver(mPersonalDictionaryContentObserver); } } @@ -275,22 +306,95 @@ public class UserDictionaryLookup implements Closeable { } /** - * Determines if the given word is a valid word in the given locale based on the UserDictionary. + * Returns the set of words defined for the given locale and more general locales. + * + * For example, input locale en_US uses data for en_US, en, and the global dictionary. + * + * Note that this method returns expanded words, not shortcuts. Shortcuts are handled + * by {@link #getShortcutsForLocale}. + * + * @param inputLocale the locale to restrict for + * @return set of words that apply to the given locale. + */ + public Set getWordsForLocale(@Nonnull final Locale inputLocale) { + final HashMap> dictWords = mDictWords; + if (CollectionUtils.isNullOrEmpty(dictWords)) { + return Collections.emptySet(); + } + + final Set words = new HashSet<>(); + final String inputLocaleString = inputLocale.toString(); + for (String word : dictWords.keySet()) { + for (Locale wordLocale : dictWords.get(word)) { + final String wordLocaleString = wordLocale.toString(); + final int match = LocaleUtils.getMatchLevel(wordLocaleString, inputLocaleString); + if (LocaleUtils.isMatch(match)) { + words.add(word); + } + } + } + return words; + } + + /** + * Returns the set of shortcuts defined for the given locale and more general locales. + * + * For example, input locale en_US uses data for en_US, en, and the global dictionary. + * + * Note that this method returns shortcut keys, not expanded words. Words are handled + * by {@link #getWordsForLocale}. + * + * @param inputLocale the locale to restrict for + * @return set of shortcuts that apply to the given locale. + */ + public Set getShortcutsForLocale(@Nonnull final Locale inputLocale) { + final Map> shortcutsPerLocale = mShortcutsPerLocale; + if (CollectionUtils.isNullOrEmpty(shortcutsPerLocale)) { + return Collections.emptySet(); + } + + final Set shortcuts = new HashSet<>(); + if (!TextUtils.isEmpty(inputLocale.getCountry())) { + // First look for the country-specific shortcut: en_US, en_UK, fr_FR, etc. + final Map countryShortcuts = shortcutsPerLocale.get(inputLocale); + if (!CollectionUtils.isNullOrEmpty(countryShortcuts)) { + shortcuts.addAll(countryShortcuts.keySet()); + } + } + + // Next look for the language-specific shortcut: en, fr, etc. + final Locale languageOnlyLocale = + LocaleUtils.constructLocaleFromString(inputLocale.getLanguage()); + final Map languageShortcuts = shortcutsPerLocale.get(languageOnlyLocale); + if (!CollectionUtils.isNullOrEmpty(languageShortcuts)) { + shortcuts.addAll(languageShortcuts.keySet()); + } + + // If all else fails, look for a global shortcut. + final Map globalShortcuts = shortcutsPerLocale.get(ANY_LOCALE); + if (!CollectionUtils.isNullOrEmpty(globalShortcuts)) { + shortcuts.addAll(globalShortcuts.keySet()); + } + + return shortcuts; + } + + /** + * Determines if the given word is a valid word in the given locale based on the dictionary. * It tries hard to find a match: for example, casing is ignored and if the word is present in a * more general locale (e.g. en or all locales), and isValidWord is asking for a more specific * locale (e.g. en_US), it will be considered a match. * * @param word the word to match - * @param locale the locale in which to match the word - * @return true iff the word has been matched for this locale in the UserDictionary. + * @param inputLocale the locale in which to match the word + * @return true iff the word has been matched for this locale in the dictionary. */ - public boolean isValidWord(@Nonnull final String word, @Nonnull final Locale locale) { + public boolean isValidWord(@Nonnull final String word, @Nonnull final Locale inputLocale) { if (!isLoaded()) { - // This is a corner case in the event the initial load of UserDictionary has not - // been loaded. In that case, we assume the word is not a valid word in - // UserDictionary. - if (DEBUG) { - Log.d(mTag, "isValidWord invoked, but initial load not complete"); + // This is a corner case in the event the initial load of the dictionary has not + // completed. In that case, we assume the word is not a valid word in the dictionary. + if (DebugFlags.DEBUG_ENABLED) { + Log.d(mTag, "isValidWord() : Initial load not complete"); } return false; } @@ -298,48 +402,44 @@ public class UserDictionaryLookup implements Closeable { // Atomically obtain the current copy of mDictWords; final HashMap> dictWords = mDictWords; - if (DEBUG) { - Log.d(mTag, "isValidWord invoked for word [" + word + - "] in locale " + locale); + if (DebugFlags.DEBUG_ENABLED) { + Log.d(mTag, "isValidWord() : Word [" + word + "] in Locale [" + inputLocale + "]"); } // Lowercase the word using the given locale. Note, that dictionary // words are lowercased using their locale, and theoretically the // lowercasing between two matching locales may differ. For simplicity // we ignore that possibility. - final String lowercased = word.toLowerCase(locale); + final String lowercased = word.toLowerCase(inputLocale); final ArrayList dictLocales = dictWords.get(lowercased); if (null == dictLocales) { - if (DEBUG) { - Log.d(mTag, "isValidWord=false, since there is no entry for " + - "lowercased word [" + lowercased + "]"); + if (DebugFlags.DEBUG_ENABLED) { + Log.d(mTag, "isValidWord() : No entry for lowercased word [" + lowercased + "]"); } return false; } else { - if (DEBUG) { - Log.d(mTag, "isValidWord found an entry for lowercased word [" + lowercased + - "]; examining locales"); + if (DebugFlags.DEBUG_ENABLED) { + Log.d(mTag, "isValidWord() : Found entry for lowercased word [" + lowercased + "]"); } // Iterate over the locales this word is in. for (final Locale dictLocale : dictLocales) { final int matchLevel = LocaleUtils.getMatchLevel(dictLocale.toString(), - locale.toString()); - if (DEBUG) { - Log.d(mTag, "matchLevel for dictLocale=" + dictLocale + ", locale=" + - locale + " is " + matchLevel); + inputLocale.toString()); + if (DebugFlags.DEBUG_ENABLED) { + Log.d(mTag, "isValidWord() : MatchLevel for DictLocale [" + dictLocale + + "] and InputLocale [" + inputLocale + "] is " + matchLevel); } if (LocaleUtils.isMatch(matchLevel)) { - if (DEBUG) { - Log.d(mTag, "isValidWord=true, since matchLevel " + matchLevel + - " is a match"); + if (DebugFlags.DEBUG_ENABLED) { + Log.d(mTag, "isValidWord() : MatchLevel " + matchLevel + " IS a match"); } return true; } - if (DEBUG) { - Log.d(mTag, "matchLevel " + matchLevel + " is not a match"); + if (DebugFlags.DEBUG_ENABLED) { + Log.d(mTag, "isValidWord() : MatchLevel " + matchLevel + " is NOT a match"); } } - if (DEBUG) { - Log.d(mTag, "isValidWord=false, since none of the locales matched"); + if (DebugFlags.DEBUG_ENABLED) { + Log.d(mTag, "isValidWord() : False, since none of the locales matched"); } return false; } @@ -350,11 +450,11 @@ public class UserDictionaryLookup implements Closeable { * * @param shortcut the shortcut to expand * @param inputLocale the locale in which to expand the shortcut - * @return expanded shortcut iff the word is a shortcut in the UserDictionary. + * @return expanded shortcut iff the word is a shortcut in the dictionary. */ @Nullable public String expandShortcut( @Nonnull final String shortcut, @Nonnull final Locale inputLocale) { - if (DEBUG) { + if (DebugFlags.DEBUG_ENABLED) { Log.d(mTag, "expandShortcut() : Shortcut [" + shortcut + "] for [" + inputLocale + "]"); } @@ -363,6 +463,9 @@ public class UserDictionaryLookup implements Closeable { // Exit as early as possible. Most users don't use shortcuts. if (CollectionUtils.isNullOrEmpty(shortcutsPerLocale)) { + if (DebugFlags.DEBUG_ENABLED) { + Log.d(mTag, "expandShortcut() : User has no shortcuts"); + } return null; } @@ -371,6 +474,10 @@ public class UserDictionaryLookup implements Closeable { final String expansionForCountry = expandShortcut( shortcutsPerLocale, shortcut, inputLocale); if (!TextUtils.isEmpty(expansionForCountry)) { + if (DebugFlags.DEBUG_ENABLED) { + Log.d(mTag, "expandShortcut() : Country expansion is [" + + expansionForCountry + "]"); + } return expansionForCountry; } } @@ -381,11 +488,19 @@ public class UserDictionaryLookup implements Closeable { final String expansionForLanguage = expandShortcut( shortcutsPerLocale, shortcut, languageOnlyLocale); if (!TextUtils.isEmpty(expansionForLanguage)) { + if (DebugFlags.DEBUG_ENABLED) { + Log.d(mTag, "expandShortcut() : Language expansion is [" + + expansionForLanguage + "]"); + } return expansionForLanguage; } - // If all else fails, loof for a global shortcut. - return expandShortcut(shortcutsPerLocale, shortcut, ANY_LOCALE); + // If all else fails, look for a global shortcut. + final String expansionForGlobal = expandShortcut(shortcutsPerLocale, shortcut, ANY_LOCALE); + if (!TextUtils.isEmpty(expansionForGlobal) && DebugFlags.DEBUG_ENABLED) { + Log.d(mTag, "expandShortcut() : Global expansion is [" + expansionForGlobal + "]"); + } + return expansionForGlobal; } @Nullable private String expandShortcut( @@ -399,60 +514,54 @@ public class UserDictionaryLookup implements Closeable { if (CollectionUtils.isNullOrEmpty(localeShortcuts)) { return null; } - final String word = localeShortcuts.get(shortcut); - if (DEBUG && word != null) { - Log.d(mTag, "expandShortcut() : Shortcut [" + shortcut + "] for [" + locale - + "] expands to [" + word + "]"); - } - return word; + return localeShortcuts.get(shortcut); } /** - * Loads the UserDictionary in the current thread. + * Loads the personal dictionary in the current thread. * * Only one reload can happen at a time. If already running, will exit quickly. */ - private void loadUserDictionary() { + private void loadPersonalDictionary() { // Bail out if already in the process of loading. if (!mIsLoading.compareAndSet(false, true)) { - Log.i(mTag, "loadUserDictionary() : Already Loading (exit)"); + Log.i(mTag, "loadPersonalDictionary() : Already Loading (exit)"); return; } - Log.i(mTag, "loadUserDictionary() : Start Loading"); + Log.i(mTag, "loadPersonalDictionary() : Start Loading"); HashMap> dictWords = new HashMap<>(); HashMap> shortcutsPerLocale = new HashMap<>(); - // Load the UserDictionary. Request that items be returned in the default sort order - // for UserDictionary, which is by frequency. + // Load the dictionary. Items are returned in the default sort order (by frequency). Cursor cursor = mResolver.query(UserDictionary.Words.CONTENT_URI, null, null, null, UserDictionary.Words.DEFAULT_SORT_ORDER); if (null == cursor || cursor.getCount() < 1) { - Log.i(mTag, "loadUserDictionary() : Empty"); + Log.i(mTag, "loadPersonalDictionary() : Empty"); } else { - // Iterate over the entries in the UserDictionary. Note, that iteration is in + // Iterate over the entries in the personal dictionary. Note, that iteration is in // descending frequency by default. while (dictWords.size() < MAX_NUM_ENTRIES && cursor.moveToNext()) { // If there is no column for locale, skip this entry. An empty // locale on the other hand will not be skipped. final int dictLocaleIndex = cursor.getColumnIndex(UserDictionary.Words.LOCALE); if (dictLocaleIndex < 0) { - if (DEBUG) { - Log.d(mTag, "Encountered UserDictionary entry without LOCALE, skipping"); + if (DebugFlags.DEBUG_ENABLED) { + Log.d(mTag, "loadPersonalDictionary() : Entry without LOCALE, skipping"); } continue; } // If there is no column for word, skip this entry. final int dictWordIndex = cursor.getColumnIndex(UserDictionary.Words.WORD); if (dictWordIndex < 0) { - if (DEBUG) { - Log.d(mTag, "Encountered UserDictionary entry without WORD, skipping"); + if (DebugFlags.DEBUG_ENABLED) { + Log.d(mTag, "loadPersonalDictionary() : Entry without WORD, skipping"); } continue; } // If the word is null, skip this entry. final String rawDictWord = cursor.getString(dictWordIndex); if (null == rawDictWord) { - if (DEBUG) { - Log.d(mTag, "Encountered null word"); + if (DebugFlags.DEBUG_ENABLED) { + Log.d(mTag, "loadPersonalDictionary() : Null word"); } continue; } @@ -460,8 +569,8 @@ public class UserDictionaryLookup implements Closeable { // zz locale for an Alphabet (QWERTY) layout will not match any actual language. String localeString = cursor.getString(dictLocaleIndex); if (null == localeString) { - if (DEBUG) { - Log.d(mTag, "Encountered null locale for word [" + + if (DebugFlags.DEBUG_ENABLED) { + Log.d(mTag, "loadPersonalDictionary() : Null locale for word [" + rawDictWord + "], assuming all locales"); } // For purposes of LocaleUtils, an empty locale matches everything. @@ -470,16 +579,16 @@ public class UserDictionaryLookup implements Closeable { final Locale dictLocale = LocaleUtils.constructLocaleFromString(localeString); // Lowercase the word before storing it. final String dictWord = rawDictWord.toLowerCase(dictLocale); - if (DEBUG) { - Log.d(mTag, "Incorporating UserDictionary word [" + dictWord + - "] for locale " + dictLocale); + if (DebugFlags.DEBUG_ENABLED) { + Log.d(mTag, "loadPersonalDictionary() : Adding word [" + dictWord + + "] for locale " + dictLocale); } // Check if there is an existing entry for this word. ArrayList dictLocales = dictWords.get(dictWord); if (null == dictLocales) { // If there is no entry for this word, create one. - if (DEBUG) { - Log.d(mTag, "Word [" + dictWord + + if (DebugFlags.DEBUG_ENABLED) { + Log.d(mTag, "loadPersonalDictionary() : Word [" + dictWord + "] not seen for other locales, creating new entry"); } dictLocales = new ArrayList<>(); @@ -491,16 +600,16 @@ public class UserDictionaryLookup implements Closeable { // If there is no column for a shortcut, we're done. final int shortcutIndex = cursor.getColumnIndex(UserDictionary.Words.SHORTCUT); if (shortcutIndex < 0) { - if (DEBUG) { - Log.d(mTag, "Encountered UserDictionary entry without SHORTCUT, done"); + if (DebugFlags.DEBUG_ENABLED) { + Log.d(mTag, "loadPersonalDictionary() : Entry without SHORTCUT, done"); } continue; } // If the shortcut is null, we're done. final String shortcut = cursor.getString(shortcutIndex); if (shortcut == null) { - if (DEBUG) { - Log.d(mTag, "Encountered null shortcut"); + if (DebugFlags.DEBUG_ENABLED) { + Log.d(mTag, "loadPersonalDictionary() : Null shortcut"); } continue; } @@ -529,10 +638,12 @@ public class UserDictionaryLookup implements Closeable { mDictWords = dictWords; mShortcutsPerLocale = shortcutsPerLocale; - // Allow other calls to loadUserDictionary to execute now. + // Allow other calls to loadPersonalDictionary to execute now. mIsLoading.set(false); - Log.i(mTag, "loadUserDictionary() : Loaded " + mDictWords.size() + Log.i(mTag, "loadPersonalDictionary() : Loaded " + mDictWords.size() + " words and " + numShortcuts + " shortcuts"); + + notifyListeners(); } } diff --git a/tests/src/com/android/inputmethod/latin/UserDictionaryLookupTest.java b/tests/src/com/android/inputmethod/latin/PersonalDictionaryLookupTest.java similarity index 63% rename from tests/src/com/android/inputmethod/latin/UserDictionaryLookupTest.java rename to tests/src/com/android/inputmethod/latin/PersonalDictionaryLookupTest.java index 917140ab5..983957fd4 100644 --- a/tests/src/com/android/inputmethod/latin/UserDictionaryLookupTest.java +++ b/tests/src/com/android/inputmethod/latin/PersonalDictionaryLookupTest.java @@ -16,7 +16,12 @@ package com.android.inputmethod.latin; -import static com.android.inputmethod.latin.UserDictionaryLookup.ANY_LOCALE; +import static com.android.inputmethod.latin.PersonalDictionaryLookup.ANY_LOCALE; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; import android.annotation.SuppressLint; import android.content.ContentResolver; @@ -27,20 +32,22 @@ import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.SmallTest; import android.util.Log; +import com.android.inputmethod.latin.PersonalDictionaryLookup.PersonalDictionaryListener; import com.android.inputmethod.latin.utils.ExecutorUtils; import java.util.HashSet; import java.util.Locale; +import java.util.Set; /** - * Unit tests for {@link com.android.inputmethod.latin.UserDictionaryLookup}. + * Unit tests for {@link PersonalDictionaryLookup}. * - * Note, this test doesn't mock out the ContentResolver, in order to make sure UserDictionaryLookup - * works in a real setting. + * Note, this test doesn't mock out the ContentResolver, in order to make sure + * {@link PersonalDictionaryLookup} works in a real setting. */ @SmallTest -public class UserDictionaryLookupTest extends AndroidTestCase { - private static final String TAG = UserDictionaryLookupTest.class.getSimpleName(); +public class PersonalDictionaryLookupTest extends AndroidTestCase { + private static final String TAG = PersonalDictionaryLookupTest.class.getSimpleName(); private ContentResolver mContentResolver; private HashSet mAddedBackup; @@ -64,7 +71,7 @@ public class UserDictionaryLookupTest extends AndroidTestCase { } /** - * Adds the given word to UserDictionary. + * Adds the given word to the personal dictionary. * * @param word the word to add * @param locale the locale of the word to add @@ -94,25 +101,43 @@ public class UserDictionaryLookupTest extends AndroidTestCase { private void deleteWord(Uri uri) { // Remove the word from the backup so that it's not cleared again later. mAddedBackup.remove(uri); - // Remove the word from UserDictionary. + // Remove the word from the personal dictionary. mContentResolver.delete(uri, null, null); } - private UserDictionaryLookup setUpShortcut(final Locale locale) { - // Insert "shortcut" => "Expansion" in the UserDictionary for the given locale. + private PersonalDictionaryLookup setUpWord(final Locale locale) { + // Insert "foo" in the personal dictionary for the given locale. + addWord("foo", locale, 17, null); + + // Create the PersonalDictionaryLookup and wait until it's loaded. + PersonalDictionaryLookup lookup = + new PersonalDictionaryLookup(mContext, ExecutorUtils.SPELLING); + lookup.open(); + return lookup; + } + + private PersonalDictionaryLookup setUpShortcut(final Locale locale) { + // Insert "shortcut" => "Expansion" in the personal dictionary for the given locale. addWord("Expansion", locale, 17, "shortcut"); - // Create the UserDictionaryLookup and wait until it's loaded. - UserDictionaryLookup lookup = new UserDictionaryLookup(mContext, ExecutorUtils.SPELLING); + // Create the PersonalDictionaryLookup and wait until it's loaded. + PersonalDictionaryLookup lookup = + new PersonalDictionaryLookup(mContext, ExecutorUtils.SPELLING); lookup.open(); - while (!lookup.isLoaded()) { - } return lookup; } + private void verifyWordExists(final Set set, final String word) { + assertTrue(set.contains(word)); + } + + private void verifyWordDoesNotExist(final Set set, final String word) { + assertFalse(set.contains(word)); + } + public void testShortcutKeyMatching() { Log.d(TAG, "testShortcutKeyMatching"); - UserDictionaryLookup lookup = setUpShortcut(Locale.US); + PersonalDictionaryLookup lookup = setUpShortcut(Locale.US); assertEquals("Expansion", lookup.expandShortcut("shortcut", Locale.US)); assertNull(lookup.expandShortcut("Shortcut", Locale.US)); @@ -125,7 +150,13 @@ public class UserDictionaryLookupTest extends AndroidTestCase { public void testShortcutMatchesInputCountry() { Log.d(TAG, "testShortcutMatchesInputCountry"); - UserDictionaryLookup lookup = setUpShortcut(Locale.US); + PersonalDictionaryLookup lookup = setUpShortcut(Locale.US); + + verifyWordExists(lookup.getShortcutsForLocale(Locale.US), "shortcut"); + assertTrue(lookup.getShortcutsForLocale(Locale.UK).isEmpty()); + assertTrue(lookup.getShortcutsForLocale(Locale.ENGLISH).isEmpty()); + assertTrue(lookup.getShortcutsForLocale(Locale.FRENCH).isEmpty()); + assertTrue(lookup.getShortcutsForLocale(ANY_LOCALE).isEmpty()); assertEquals("Expansion", lookup.expandShortcut("shortcut", Locale.US)); assertNull(lookup.expandShortcut("shortcut", Locale.UK)); @@ -138,7 +169,13 @@ public class UserDictionaryLookupTest extends AndroidTestCase { public void testShortcutMatchesInputLanguage() { Log.d(TAG, "testShortcutMatchesInputLanguage"); - UserDictionaryLookup lookup = setUpShortcut(Locale.ENGLISH); + PersonalDictionaryLookup lookup = setUpShortcut(Locale.ENGLISH); + + verifyWordExists(lookup.getShortcutsForLocale(Locale.US), "shortcut"); + verifyWordExists(lookup.getShortcutsForLocale(Locale.UK), "shortcut"); + verifyWordExists(lookup.getShortcutsForLocale(Locale.ENGLISH), "shortcut"); + assertTrue(lookup.getShortcutsForLocale(Locale.FRENCH).isEmpty()); + assertTrue(lookup.getShortcutsForLocale(ANY_LOCALE).isEmpty()); assertEquals("Expansion", lookup.expandShortcut("shortcut", Locale.US)); assertEquals("Expansion", lookup.expandShortcut("shortcut", Locale.UK)); @@ -150,7 +187,13 @@ public class UserDictionaryLookupTest extends AndroidTestCase { } public void testShortcutMatchesAnyLocale() { - UserDictionaryLookup lookup = setUpShortcut(UserDictionaryLookup.ANY_LOCALE); + PersonalDictionaryLookup lookup = setUpShortcut(PersonalDictionaryLookup.ANY_LOCALE); + + verifyWordExists(lookup.getShortcutsForLocale(Locale.US), "shortcut"); + verifyWordExists(lookup.getShortcutsForLocale(Locale.UK), "shortcut"); + verifyWordExists(lookup.getShortcutsForLocale(Locale.ENGLISH), "shortcut"); + verifyWordExists(lookup.getShortcutsForLocale(Locale.FRENCH), "shortcut"); + verifyWordExists(lookup.getShortcutsForLocale(ANY_LOCALE), "shortcut"); assertEquals("Expansion", lookup.expandShortcut("shortcut", Locale.US)); assertEquals("Expansion", lookup.expandShortcut("shortcut", Locale.UK)); @@ -163,15 +206,13 @@ public class UserDictionaryLookupTest extends AndroidTestCase { public void testExactLocaleMatch() { Log.d(TAG, "testExactLocaleMatch"); + PersonalDictionaryLookup lookup = setUpWord(Locale.US); - // Insert "Foo" as capitalized in the UserDictionary under en_US locale. - addWord("Foo", Locale.US, 17, null); - - // Create the UserDictionaryLookup and wait until it's loaded. - UserDictionaryLookup lookup = new UserDictionaryLookup(mContext, ExecutorUtils.SPELLING); - lookup.open(); - while (!lookup.isLoaded()) { - } + verifyWordExists(lookup.getWordsForLocale(Locale.US), "foo"); + verifyWordDoesNotExist(lookup.getWordsForLocale(Locale.UK), "foo"); + verifyWordDoesNotExist(lookup.getWordsForLocale(Locale.ENGLISH), "foo"); + verifyWordDoesNotExist(lookup.getWordsForLocale(Locale.FRENCH), "foo"); + verifyWordDoesNotExist(lookup.getWordsForLocale(ANY_LOCALE), "foo"); // Any capitalization variation should match. assertTrue(lookup.isValidWord("foo", Locale.US)); @@ -192,15 +233,13 @@ public class UserDictionaryLookupTest extends AndroidTestCase { public void testSubLocaleMatch() { Log.d(TAG, "testSubLocaleMatch"); + PersonalDictionaryLookup lookup = setUpWord(Locale.ENGLISH); - // Insert "Foo" as capitalized in the UserDictionary under the en locale. - addWord("Foo", Locale.ENGLISH, 17, null); - - // Create the UserDictionaryLookup and wait until it's loaded. - UserDictionaryLookup lookup = new UserDictionaryLookup(mContext, ExecutorUtils.SPELLING); - lookup.open(); - while (!lookup.isLoaded()) { - } + verifyWordExists(lookup.getWordsForLocale(Locale.US), "foo"); + verifyWordExists(lookup.getWordsForLocale(Locale.UK), "foo"); + verifyWordExists(lookup.getWordsForLocale(Locale.ENGLISH), "foo"); + verifyWordDoesNotExist(lookup.getWordsForLocale(Locale.FRENCH), "foo"); + verifyWordDoesNotExist(lookup.getWordsForLocale(ANY_LOCALE), "foo"); // Any capitalization variation should match for both en and en_US. assertTrue(lookup.isValidWord("foo", Locale.ENGLISH)); @@ -217,15 +256,13 @@ public class UserDictionaryLookupTest extends AndroidTestCase { public void testAllLocalesMatch() { Log.d(TAG, "testAllLocalesMatch"); + PersonalDictionaryLookup lookup = setUpWord(null); - // Insert "Foo" as capitalized in the UserDictionary under the all locales. - addWord("Foo", null, 17, null); - - // Create the UserDictionaryLookup and wait until it's loaded. - UserDictionaryLookup lookup = new UserDictionaryLookup(mContext, ExecutorUtils.SPELLING); - lookup.open(); - while (!lookup.isLoaded()) { - } + verifyWordExists(lookup.getWordsForLocale(Locale.US), "foo"); + verifyWordExists(lookup.getWordsForLocale(Locale.UK), "foo"); + verifyWordExists(lookup.getWordsForLocale(Locale.ENGLISH), "foo"); + verifyWordExists(lookup.getWordsForLocale(Locale.FRENCH), "foo"); + verifyWordExists(lookup.getWordsForLocale(ANY_LOCALE), "foo"); // Any capitalization variation should match for fr, en and en_US. assertTrue(lookup.isValidWord("foo", ANY_LOCALE)); @@ -245,17 +282,15 @@ public class UserDictionaryLookupTest extends AndroidTestCase { public void testMultipleLocalesMatch() { Log.d(TAG, "testMultipleLocalesMatch"); - // Insert "Foo" as capitalized in the UserDictionary under the en_US and en_CA and fr + // Insert "Foo" as capitalized in the personal dictionary under the en_US and en_CA and fr // locales. addWord("Foo", Locale.US, 17, null); addWord("foO", Locale.CANADA, 17, null); addWord("fOo", Locale.FRENCH, 17, null); - // Create the UserDictionaryLookup and wait until it's loaded. - UserDictionaryLookup lookup = new UserDictionaryLookup(mContext, ExecutorUtils.SPELLING); + // Create the PersonalDictionaryLookup and wait until it's loaded. + PersonalDictionaryLookup lookup = new PersonalDictionaryLookup(mContext, ExecutorUtils.SPELLING); lookup.open(); - while (!lookup.isLoaded()) { - } // Both en_CA and en_US match. assertTrue(lookup.isValidWord("foo", Locale.CANADA)); @@ -269,17 +304,40 @@ public class UserDictionaryLookupTest extends AndroidTestCase { lookup.close(); } + public void testManageListeners() { + Log.d(TAG, "testManageListeners"); + + PersonalDictionaryLookup lookup = + new PersonalDictionaryLookup(mContext, ExecutorUtils.SPELLING); + + PersonalDictionaryListener listener = mock(PersonalDictionaryListener.class); + // Add the same listener a bunch of times. It doesn't make a difference. + lookup.addListener(listener); + lookup.addListener(listener); + lookup.addListener(listener); + lookup.notifyListeners(); + + verify(listener, times(1)).onUpdate(); + + // Remove the same listener a bunch of times. It doesn't make a difference. + lookup.removeListener(listener); + lookup.removeListener(listener); + lookup.removeListener(listener); + lookup.notifyListeners(); + + verifyNoMoreInteractions(listener); + } + public void testReload() { Log.d(TAG, "testReload"); // Insert "foo". Uri uri = addWord("foo", Locale.US, 17, null); - // Create the UserDictionaryLookup and wait until it's loaded. - UserDictionaryLookup lookup = new UserDictionaryLookup(mContext, ExecutorUtils.SPELLING); + // Create the PersonalDictionaryLookup and wait until it's loaded. + PersonalDictionaryLookup lookup = + new PersonalDictionaryLookup(mContext, ExecutorUtils.SPELLING); lookup.open(); - while (!lookup.isLoaded()) { - } // "foo" should match. assertTrue(lookup.isValidWord("foo", Locale.US)); @@ -292,9 +350,9 @@ public class UserDictionaryLookupTest extends AndroidTestCase { addWord("bar", Locale.US, 18, null); // Wait a little bit before expecting a change. The time we wait should be greater than - // UserDictionaryLookup.RELOAD_DELAY_MS. + // PersonalDictionaryLookup.RELOAD_DELAY_MS. try { - Thread.sleep(UserDictionaryLookup.RELOAD_DELAY_MS + 1000); + Thread.sleep(PersonalDictionaryLookup.RELOAD_DELAY_MS + 1000); } catch (InterruptedException e) { } @@ -316,11 +374,10 @@ public class UserDictionaryLookupTest extends AndroidTestCase { Uri uri = addWord("foo", Locale.GERMANY, 17, "f"); addWord("bar", Locale.GERMANY, 17, null); - // Create the UserDictionaryLookup and wait until it's loaded. - UserDictionaryLookup lookup = new UserDictionaryLookup(mContext, ExecutorUtils.SPELLING); + // Create the PersonalDictionaryLookup and wait until it's loaded. + PersonalDictionaryLookup lookup = + new PersonalDictionaryLookup(mContext, ExecutorUtils.SPELLING); lookup.open(); - while (!lookup.isLoaded()) { - } // "foo" should match. assertTrue(lookup.isValidWord("foo", Locale.GERMANY)); @@ -335,9 +392,9 @@ public class UserDictionaryLookupTest extends AndroidTestCase { deleteWord(uri); // Wait a little bit before expecting a change. The time we wait should be greater than - // UserDictionaryLookup.RELOAD_DELAY_MS. + // PersonalDictionaryLookup.RELOAD_DELAY_MS. try { - Thread.sleep(UserDictionaryLookup.RELOAD_DELAY_MS + 1000); + Thread.sleep(PersonalDictionaryLookup.RELOAD_DELAY_MS + 1000); } catch (InterruptedException e) { } @@ -361,11 +418,10 @@ public class UserDictionaryLookupTest extends AndroidTestCase { // Insert "foo". Uri uri = addWord("foo", Locale.US, 17, null); - // Create the UserDictionaryLookup and wait until it's loaded. - UserDictionaryLookup lookup = new UserDictionaryLookup(mContext, ExecutorUtils.SPELLING); + // Create the PersonalDictionaryLookup and wait until it's loaded. + PersonalDictionaryLookup lookup = + new PersonalDictionaryLookup(mContext, ExecutorUtils.SPELLING); lookup.open(); - while (!lookup.isLoaded()) { - } // "foo" should match. assertTrue(lookup.isValidWord("foo", Locale.US)); @@ -381,9 +437,9 @@ public class UserDictionaryLookupTest extends AndroidTestCase { addWord("bar", Locale.US, 18, null); // Wait a little bit before expecting a change. The time we wait should be greater than - // UserDictionaryLookup.RELOAD_DELAY_MS. + // PersonalDictionaryLookup.RELOAD_DELAY_MS. try { - Thread.sleep(UserDictionaryLookup.RELOAD_DELAY_MS + 1000); + Thread.sleep(PersonalDictionaryLookup.RELOAD_DELAY_MS + 1000); } catch (InterruptedException e) { } diff --git a/tests/src/com/android/inputmethod/latin/settings/AccountsSettingsFragmentTests.java b/tests/src/com/android/inputmethod/latin/settings/AccountsSettingsFragmentTests.java index 7a019c364..f2d8973f7 100644 --- a/tests/src/com/android/inputmethod/latin/settings/AccountsSettingsFragmentTests.java +++ b/tests/src/com/android/inputmethod/latin/settings/AccountsSettingsFragmentTests.java @@ -17,7 +17,6 @@ package com.android.inputmethod.latin.settings; import static org.mockito.Matchers.any; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import android.app.AlertDialog; diff --git a/tests/src/com/android/inputmethod/latin/utils/CollectionUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/CollectionUtilsTests.java index 6871a41d5..0cbb02c4f 100644 --- a/tests/src/com/android/inputmethod/latin/utils/CollectionUtilsTests.java +++ b/tests/src/com/android/inputmethod/latin/utils/CollectionUtilsTests.java @@ -68,7 +68,7 @@ public class CollectionUtilsTests extends AndroidTestCase { */ public void testArrayAsList() { final ArrayList empty = new ArrayList<>(); - assertEquals(empty, CollectionUtils.arrayAsList(new String[] { }, 0, 0)); + assertEquals(empty, CollectionUtils.arrayAsList(new String[] {}, 0, 0)); final String[] array = { "0", "1", "2", "3", "4" }; assertEquals(empty, CollectionUtils.arrayAsList(array, 0, 0)); assertEquals(empty, CollectionUtils.arrayAsList(array, 1, 1)); @@ -82,12 +82,10 @@ public class CollectionUtilsTests extends AndroidTestCase { * results for a few cases. */ public void testIsNullOrEmpty() { - assertTrue(CollectionUtils.isNullOrEmpty((List) null)); - assertTrue(CollectionUtils.isNullOrEmpty((Map) null)); - assertTrue(CollectionUtils.isNullOrEmpty(new ArrayList())); - assertTrue(CollectionUtils.isNullOrEmpty(new HashMap())); - assertTrue(CollectionUtils.isNullOrEmpty(Collections.EMPTY_LIST)); - assertTrue(CollectionUtils.isNullOrEmpty(Collections.EMPTY_MAP)); + assertTrue(CollectionUtils.isNullOrEmpty((List) null)); + assertTrue(CollectionUtils.isNullOrEmpty((Map) null)); + assertTrue(CollectionUtils.isNullOrEmpty(new ArrayList())); + assertTrue(CollectionUtils.isNullOrEmpty(new HashMap())); assertFalse(CollectionUtils.isNullOrEmpty(Collections.singletonList("Not empty"))); assertFalse(CollectionUtils.isNullOrEmpty(Collections.singletonMap("Not", "empty"))); }