Merge "Add shortcut support to UserDictionaryLookup."
commit
a0a6663894
|
@ -20,6 +20,7 @@ import com.android.inputmethod.annotations.UsedForTesting;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
@ -60,7 +61,17 @@ public final class CollectionUtils {
|
||||||
* @return Whether c contains no elements.
|
* @return Whether c contains no elements.
|
||||||
*/
|
*/
|
||||||
@UsedForTesting
|
@UsedForTesting
|
||||||
public static boolean isNullOrEmpty(@Nullable final Collection<?> c) {
|
public static boolean isNullOrEmpty(@Nullable final Collection c) {
|
||||||
return c == null || c.isEmpty();
|
return c == null || c.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests whether map contains no elements, true if map is null or map is empty.
|
||||||
|
* @param map Map to test.
|
||||||
|
* @return Whether map contains no elements.
|
||||||
|
*/
|
||||||
|
@UsedForTesting
|
||||||
|
public static boolean isNullOrEmpty(@Nullable final Map map) {
|
||||||
|
return map == null || map.isEmpty();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,10 @@ public abstract class Dictionary {
|
||||||
public static final String TYPE_USER_TYPED = "user_typed";
|
public static final String TYPE_USER_TYPED = "user_typed";
|
||||||
public static final PhonyDictionary DICTIONARY_USER_TYPED = new PhonyDictionary(TYPE_USER_TYPED);
|
public static final PhonyDictionary DICTIONARY_USER_TYPED = new PhonyDictionary(TYPE_USER_TYPED);
|
||||||
|
|
||||||
|
public static final String TYPE_USER_SHORTCUT = "user_shortcut";
|
||||||
|
public static final PhonyDictionary DICTIONARY_USER_SHORTCUT =
|
||||||
|
new PhonyDictionary(TYPE_USER_SHORTCUT);
|
||||||
|
|
||||||
public static final String TYPE_APPLICATION_DEFINED = "application_defined";
|
public static final String TYPE_APPLICATION_DEFINED = "application_defined";
|
||||||
public static final PhonyDictionary DICTIONARY_APPLICATION_DEFINED =
|
public static final PhonyDictionary DICTIONARY_APPLICATION_DEFINED =
|
||||||
new PhonyDictionary(TYPE_APPLICATION_DEFINED);
|
new PhonyDictionary(TYPE_APPLICATION_DEFINED);
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.android.inputmethod.latin.spellcheck;
|
package com.android.inputmethod.latin;
|
||||||
|
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
@ -22,9 +22,11 @@ import android.database.ContentObserver;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.provider.UserDictionary;
|
import android.provider.UserDictionary;
|
||||||
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.android.inputmethod.annotations.UsedForTesting;
|
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.common.LocaleUtils;
|
||||||
import com.android.inputmethod.latin.utils.ExecutorUtils;
|
import com.android.inputmethod.latin.utils.ExecutorUtils;
|
||||||
|
|
||||||
|
@ -36,6 +38,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.ScheduledFuture;
|
import java.util.concurrent.ScheduledFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* UserDictionaryLookup provides the ability to lookup into the system-wide "Personal dictionary".
|
* UserDictionaryLookup provides the ability to lookup into the system-wide "Personal dictionary".
|
||||||
*
|
*
|
||||||
|
@ -47,7 +52,6 @@ import java.util.concurrent.TimeUnit;
|
||||||
* onCreate and close() it in onDestroy.
|
* onCreate and close() it in onDestroy.
|
||||||
*/
|
*/
|
||||||
public class UserDictionaryLookup implements Closeable {
|
public class UserDictionaryLookup implements Closeable {
|
||||||
private static final String TAG = UserDictionaryLookup.class.getSimpleName();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This guards the execution of any Log.d() logging, so that if false, they are not even
|
* This guards the execution of any Log.d() logging, so that if false, they are not even
|
||||||
|
@ -79,7 +83,12 @@ public class UserDictionaryLookup implements Closeable {
|
||||||
@UsedForTesting
|
@UsedForTesting
|
||||||
static final int RELOAD_DELAY_MS = 200;
|
static final int RELOAD_DELAY_MS = 200;
|
||||||
|
|
||||||
|
@UsedForTesting
|
||||||
|
static final Locale ANY_LOCALE = new Locale("");
|
||||||
|
|
||||||
|
private final String mTag;
|
||||||
private final ContentResolver mResolver;
|
private final ContentResolver mResolver;
|
||||||
|
private final String mServiceName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runnable that calls loadUserDictionary().
|
* Runnable that calls loadUserDictionary().
|
||||||
|
@ -88,12 +97,11 @@ public class UserDictionaryLookup implements Closeable {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "Executing (re)load");
|
Log.d(mTag, "Executing (re)load");
|
||||||
}
|
}
|
||||||
loadUserDictionary();
|
loadUserDictionary();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private final UserDictionaryLoader mLoader = new UserDictionaryLoader();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Content observer for UserDictionary changes. It has the following properties:
|
* Content observer for UserDictionary changes. It has the following properties:
|
||||||
|
@ -122,7 +130,7 @@ public class UserDictionaryLookup implements Closeable {
|
||||||
@Override
|
@Override
|
||||||
public void onChange(boolean selfChange, Uri uri) {
|
public void onChange(boolean selfChange, Uri uri) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "Received content observer onChange notification for URI: " + uri);
|
Log.d(mTag, "Received content observer onChange notification for URI: " + uri);
|
||||||
}
|
}
|
||||||
// Cancel (but don't interrupt) any pending reloads (except the initial load).
|
// Cancel (but don't interrupt) any pending reloads (except the initial load).
|
||||||
if (mReloadFuture != null && !mReloadFuture.isCancelled() &&
|
if (mReloadFuture != null && !mReloadFuture.isCancelled() &&
|
||||||
|
@ -131,20 +139,20 @@ public class UserDictionaryLookup implements Closeable {
|
||||||
boolean isCancelled = mReloadFuture.cancel(false);
|
boolean isCancelled = mReloadFuture.cancel(false);
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
if (isCancelled) {
|
if (isCancelled) {
|
||||||
Log.d(TAG, "Successfully canceled previous reload request");
|
Log.d(mTag, "Successfully canceled previous reload request");
|
||||||
} else {
|
} else {
|
||||||
Log.d(TAG, "Unable to cancel previous reload request");
|
Log.d(mTag, "Unable to cancel previous reload request");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "Scheduling reload in " + RELOAD_DELAY_MS + " ms");
|
Log.d(mTag, "Scheduling reload in " + RELOAD_DELAY_MS + " ms");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Schedule a new reload after RELOAD_DELAY_MS.
|
// Schedule a new reload after RELOAD_DELAY_MS.
|
||||||
mReloadFuture = ExecutorUtils.getBackgroundExecutor(ExecutorUtils.SPELLING)
|
mReloadFuture = ExecutorUtils.getBackgroundExecutor(mServiceName)
|
||||||
.schedule(mLoader, RELOAD_DELAY_MS, TimeUnit.MILLISECONDS);
|
.schedule(new UserDictionaryLoader(), RELOAD_DELAY_MS, TimeUnit.MILLISECONDS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private final ContentObserver mObserver = new UserDictionaryContentObserver();
|
private final ContentObserver mObserver = new UserDictionaryContentObserver();
|
||||||
|
@ -166,6 +174,12 @@ public class UserDictionaryLookup implements Closeable {
|
||||||
*/
|
*/
|
||||||
private volatile HashMap<String, ArrayList<Locale>> mDictWords;
|
private volatile HashMap<String, ArrayList<Locale>> mDictWords;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We store a map from a shortcut to a word for each locale.
|
||||||
|
* Shortcuts that apply to any locale are keyed by {@link #ANY_LOCALE}.
|
||||||
|
*/
|
||||||
|
private volatile HashMap<Locale, HashMap<String, String>> mShortcutsPerLocale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The last-scheduled reload future. Saved in order to cancel a pending reload if a new one
|
* The last-scheduled reload future. Saved in order to cancel a pending reload if a new one
|
||||||
* is coming.
|
* is coming.
|
||||||
|
@ -175,18 +189,24 @@ public class UserDictionaryLookup implements Closeable {
|
||||||
/**
|
/**
|
||||||
* @param context the context from which to obtain content resolver
|
* @param context the context from which to obtain content resolver
|
||||||
*/
|
*/
|
||||||
public UserDictionaryLookup(Context context) {
|
public UserDictionaryLookup(@Nonnull final Context context, @Nonnull final String serviceName) {
|
||||||
if (DEBUG) {
|
mTag = serviceName + ".UserDictionaryLookup";
|
||||||
Log.d(TAG, "UserDictionaryLookup constructor with context: " + context);
|
|
||||||
}
|
Log.i(mTag, "create()");
|
||||||
|
|
||||||
|
mServiceName = serviceName;
|
||||||
|
|
||||||
// Obtain a content resolver.
|
// Obtain a content resolver.
|
||||||
mResolver = context.getContentResolver();
|
mResolver = context.getContentResolver();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void open() {
|
||||||
|
Log.i(mTag, "open()");
|
||||||
|
|
||||||
// Schedule the initial load to run immediately. It's possible that the first call to
|
// 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
|
// isValidWord occurs before the dictionary has actually loaded, so it should not
|
||||||
// assume that the dictionary has been loaded.
|
// assume that the dictionary has been loaded.
|
||||||
ExecutorUtils.getBackgroundExecutor(ExecutorUtils.SPELLING).execute(mLoader);
|
loadUserDictionary();
|
||||||
|
|
||||||
// Register the observer to be notified on changes to the UserDictionary and all individual
|
// Register the observer to be notified on changes to the UserDictionary and all individual
|
||||||
// items.
|
// items.
|
||||||
|
@ -210,7 +230,7 @@ public class UserDictionaryLookup implements Closeable {
|
||||||
public void finalize() throws Throwable {
|
public void finalize() throws Throwable {
|
||||||
try {
|
try {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "Finalize called, calling close()");
|
Log.d(mTag, "Finalize called, calling close()");
|
||||||
}
|
}
|
||||||
close();
|
close();
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -227,7 +247,7 @@ public class UserDictionaryLookup implements Closeable {
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "Close called (no pun intended), cleaning up executor and observer");
|
Log.d(mTag, "Close called (no pun intended), cleaning up executor and observer");
|
||||||
}
|
}
|
||||||
if (mIsClosed.compareAndSet(false, true)) {
|
if (mIsClosed.compareAndSet(false, true)) {
|
||||||
// Unregister the content observer.
|
// Unregister the content observer.
|
||||||
|
@ -240,9 +260,8 @@ public class UserDictionaryLookup implements Closeable {
|
||||||
*
|
*
|
||||||
* @return true if the initial load is successful
|
* @return true if the initial load is successful
|
||||||
*/
|
*/
|
||||||
@UsedForTesting
|
public boolean isLoaded() {
|
||||||
boolean isLoaded() {
|
return mDictWords != null && mShortcutsPerLocale != null;
|
||||||
return mDictWords != null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -255,14 +274,13 @@ public class UserDictionaryLookup implements Closeable {
|
||||||
* @param locale the locale in which to match the word
|
* @param locale the locale in which to match the word
|
||||||
* @return true iff the word has been matched for this locale in the UserDictionary.
|
* @return true iff the word has been matched for this locale in the UserDictionary.
|
||||||
*/
|
*/
|
||||||
public boolean isValidWord(
|
public boolean isValidWord(@Nonnull final String word, @Nonnull final Locale locale) {
|
||||||
final String word, final Locale locale) {
|
|
||||||
if (!isLoaded()) {
|
if (!isLoaded()) {
|
||||||
// This is a corner case in the event the initial load of UserDictionary has not
|
// 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
|
// been loaded. In that case, we assume the word is not a valid word in
|
||||||
// UserDictionary.
|
// UserDictionary.
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "isValidWord invoked, but initial load not complete");
|
Log.d(mTag, "isValidWord invoked, but initial load not complete");
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -271,7 +289,7 @@ public class UserDictionaryLookup implements Closeable {
|
||||||
final HashMap<String, ArrayList<Locale>> dictWords = mDictWords;
|
final HashMap<String, ArrayList<Locale>> dictWords = mDictWords;
|
||||||
|
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "isValidWord invoked for word [" + word +
|
Log.d(mTag, "isValidWord invoked for word [" + word +
|
||||||
"] in locale " + locale);
|
"] in locale " + locale);
|
||||||
}
|
}
|
||||||
// Lowercase the word using the given locale. Note, that dictionary
|
// Lowercase the word using the given locale. Note, that dictionary
|
||||||
|
@ -282,13 +300,13 @@ public class UserDictionaryLookup implements Closeable {
|
||||||
final ArrayList<Locale> dictLocales = dictWords.get(lowercased);
|
final ArrayList<Locale> dictLocales = dictWords.get(lowercased);
|
||||||
if (null == dictLocales) {
|
if (null == dictLocales) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "isValidWord=false, since there is no entry for " +
|
Log.d(mTag, "isValidWord=false, since there is no entry for " +
|
||||||
"lowercased word [" + lowercased + "]");
|
"lowercased word [" + lowercased + "]");
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "isValidWord found an entry for lowercased word [" + lowercased +
|
Log.d(mTag, "isValidWord found an entry for lowercased word [" + lowercased +
|
||||||
"]; examining locales");
|
"]; examining locales");
|
||||||
}
|
}
|
||||||
// Iterate over the locales this word is in.
|
// Iterate over the locales this word is in.
|
||||||
|
@ -296,27 +314,89 @@ public class UserDictionaryLookup implements Closeable {
|
||||||
final int matchLevel = LocaleUtils.getMatchLevel(dictLocale.toString(),
|
final int matchLevel = LocaleUtils.getMatchLevel(dictLocale.toString(),
|
||||||
locale.toString());
|
locale.toString());
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "matchLevel for dictLocale=" + dictLocale + ", locale=" +
|
Log.d(mTag, "matchLevel for dictLocale=" + dictLocale + ", locale=" +
|
||||||
locale + " is " + matchLevel);
|
locale + " is " + matchLevel);
|
||||||
}
|
}
|
||||||
if (LocaleUtils.isMatch(matchLevel)) {
|
if (LocaleUtils.isMatch(matchLevel)) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "isValidWord=true, since matchLevel " + matchLevel +
|
Log.d(mTag, "isValidWord=true, since matchLevel " + matchLevel +
|
||||||
" is a match");
|
" is a match");
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "matchLevel " + matchLevel + " is not a match");
|
Log.d(mTag, "matchLevel " + matchLevel + " is not a match");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "isValidWord=false, since none of the locales matched");
|
Log.d(mTag, "isValidWord=false, since none of the locales matched");
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expands the given shortcut for the given locale.
|
||||||
|
*
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
@Nullable public String expandShortcut(
|
||||||
|
@Nonnull final String shortcut, @Nonnull final Locale inputLocale) {
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d(mTag, "expandShortcut() : Shortcut [" + shortcut + "] for [" + inputLocale + "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Atomically obtain the current copy of mShortcuts;
|
||||||
|
final HashMap<Locale, HashMap<String, String>> shortcutsPerLocale = mShortcutsPerLocale;
|
||||||
|
|
||||||
|
// Exit as early as possible. Most users don't use shortcuts.
|
||||||
|
if (CollectionUtils.isNullOrEmpty(shortcutsPerLocale)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TextUtils.isEmpty(inputLocale.getCountry())) {
|
||||||
|
// First look for the country-specific shortcut: en_US, en_UK, fr_FR, etc.
|
||||||
|
final String expansionForCountry = expandShortcut(
|
||||||
|
shortcutsPerLocale, shortcut, inputLocale);
|
||||||
|
if (!TextUtils.isEmpty(expansionForCountry)) {
|
||||||
|
return expansionForCountry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next look for the language-specific shortcut: en, fr, etc.
|
||||||
|
final Locale languageOnlyLocale =
|
||||||
|
LocaleUtils.constructLocaleFromString(inputLocale.getLanguage());
|
||||||
|
final String expansionForLanguage = expandShortcut(
|
||||||
|
shortcutsPerLocale, shortcut, languageOnlyLocale);
|
||||||
|
if (!TextUtils.isEmpty(expansionForLanguage)) {
|
||||||
|
return expansionForLanguage;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If all else fails, loof for a global shortcut.
|
||||||
|
return expandShortcut(shortcutsPerLocale, shortcut, ANY_LOCALE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable private String expandShortcut(
|
||||||
|
@Nullable final HashMap<Locale, HashMap<String, String>> shortcutsPerLocale,
|
||||||
|
@Nonnull final String shortcut,
|
||||||
|
@Nonnull final Locale locale) {
|
||||||
|
if (CollectionUtils.isNullOrEmpty(shortcutsPerLocale)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final HashMap<String, String> localeShortcuts = shortcutsPerLocale.get(locale);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads the UserDictionary in the current thread.
|
* Loads the UserDictionary in the current thread.
|
||||||
*
|
*
|
||||||
|
@ -325,45 +405,36 @@ public class UserDictionaryLookup implements Closeable {
|
||||||
private void loadUserDictionary() {
|
private void loadUserDictionary() {
|
||||||
// Bail out if already in the process of loading.
|
// Bail out if already in the process of loading.
|
||||||
if (!mIsLoading.compareAndSet(false, true)) {
|
if (!mIsLoading.compareAndSet(false, true)) {
|
||||||
if (DEBUG) {
|
Log.i(mTag, "loadUserDictionary() : Already Loading (exit)");
|
||||||
Log.d(TAG, "Already in the process of loading UserDictionary, skipping");
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (DEBUG) {
|
Log.i(mTag, "loadUserDictionary() : Start Loading");
|
||||||
Log.d(TAG, "Loading UserDictionary");
|
|
||||||
}
|
|
||||||
HashMap<String, ArrayList<Locale>> dictWords = new HashMap<>();
|
HashMap<String, ArrayList<Locale>> dictWords = new HashMap<>();
|
||||||
|
HashMap<Locale, HashMap<String, String>> shortcutsPerLocale = new HashMap<>();
|
||||||
// Load the UserDictionary. Request that items be returned in the default sort order
|
// Load the UserDictionary. Request that items be returned in the default sort order
|
||||||
// for UserDictionary, which is by frequency.
|
// for UserDictionary, which is by frequency.
|
||||||
Cursor cursor = mResolver.query(UserDictionary.Words.CONTENT_URI,
|
Cursor cursor = mResolver.query(UserDictionary.Words.CONTENT_URI,
|
||||||
null, null, null, UserDictionary.Words.DEFAULT_SORT_ORDER);
|
null, null, null, UserDictionary.Words.DEFAULT_SORT_ORDER);
|
||||||
if (null == cursor || cursor.getCount() < 1) {
|
if (null == cursor || cursor.getCount() < 1) {
|
||||||
if (DEBUG) {
|
Log.i(mTag, "loadUserDictionary() : Empty");
|
||||||
Log.d(TAG, "No entries found in UserDictionary");
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Iterate over the entries in the UserDictionary. Note, that iteration is in
|
// Iterate over the entries in the UserDictionary. Note, that iteration is in
|
||||||
// descending frequency by default.
|
// descending frequency by default.
|
||||||
while (dictWords.size() < MAX_NUM_ENTRIES && cursor.moveToNext()) {
|
while (dictWords.size() < MAX_NUM_ENTRIES && cursor.moveToNext()) {
|
||||||
// If there is no column for locale, skip this entry. An empty
|
// If there is no column for locale, skip this entry. An empty
|
||||||
// locale on the other hand will not be skipped.
|
// locale on the other hand will not be skipped.
|
||||||
final int dictLocaleIndex = cursor.getColumnIndex(
|
final int dictLocaleIndex = cursor.getColumnIndex(UserDictionary.Words.LOCALE);
|
||||||
UserDictionary.Words.LOCALE);
|
|
||||||
if (dictLocaleIndex < 0) {
|
if (dictLocaleIndex < 0) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "Encountered UserDictionary entry " +
|
Log.d(mTag, "Encountered UserDictionary entry without LOCALE, skipping");
|
||||||
"without LOCALE, skipping");
|
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// If there is no column for word, skip this entry.
|
// If there is no column for word, skip this entry.
|
||||||
final int dictWordIndex = cursor.getColumnIndex(
|
final int dictWordIndex = cursor.getColumnIndex(UserDictionary.Words.WORD);
|
||||||
UserDictionary.Words.WORD);
|
|
||||||
if (dictWordIndex < 0) {
|
if (dictWordIndex < 0) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "Encountered UserDictionary entry without " +
|
Log.d(mTag, "Encountered UserDictionary entry without WORD, skipping");
|
||||||
"WORD, skipping");
|
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -371,7 +442,7 @@ public class UserDictionaryLookup implements Closeable {
|
||||||
final String rawDictWord = cursor.getString(dictWordIndex);
|
final String rawDictWord = cursor.getString(dictWordIndex);
|
||||||
if (null == rawDictWord) {
|
if (null == rawDictWord) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "Encountered null word");
|
Log.d(mTag, "Encountered null word");
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -380,19 +451,17 @@ public class UserDictionaryLookup implements Closeable {
|
||||||
String localeString = cursor.getString(dictLocaleIndex);
|
String localeString = cursor.getString(dictLocaleIndex);
|
||||||
if (null == localeString) {
|
if (null == localeString) {
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "Encountered null locale for word [" +
|
Log.d(mTag, "Encountered null locale for word [" +
|
||||||
rawDictWord + "], assuming all locales");
|
rawDictWord + "], assuming all locales");
|
||||||
}
|
}
|
||||||
// For purposes of LocaleUtils, an empty locale matches
|
// For purposes of LocaleUtils, an empty locale matches everything.
|
||||||
// everything.
|
|
||||||
localeString = "";
|
localeString = "";
|
||||||
}
|
}
|
||||||
final Locale dictLocale = LocaleUtils.constructLocaleFromString(
|
final Locale dictLocale = LocaleUtils.constructLocaleFromString(localeString);
|
||||||
localeString);
|
|
||||||
// Lowercase the word before storing it.
|
// Lowercase the word before storing it.
|
||||||
final String dictWord = rawDictWord.toLowerCase(dictLocale);
|
final String dictWord = rawDictWord.toLowerCase(dictLocale);
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "Incorporating UserDictionary word [" + dictWord +
|
Log.d(mTag, "Incorporating UserDictionary word [" + dictWord +
|
||||||
"] for locale " + dictLocale);
|
"] for locale " + dictLocale);
|
||||||
}
|
}
|
||||||
// Check if there is an existing entry for this word.
|
// Check if there is an existing entry for this word.
|
||||||
|
@ -400,7 +469,7 @@ public class UserDictionaryLookup implements Closeable {
|
||||||
if (null == dictLocales) {
|
if (null == dictLocales) {
|
||||||
// If there is no entry for this word, create one.
|
// If there is no entry for this word, create one.
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, "Word [" + dictWord +
|
Log.d(mTag, "Word [" + dictWord +
|
||||||
"] not seen for other locales, creating new entry");
|
"] not seen for other locales, creating new entry");
|
||||||
}
|
}
|
||||||
dictLocales = new ArrayList<>();
|
dictLocales = new ArrayList<>();
|
||||||
|
@ -408,13 +477,42 @@ public class UserDictionaryLookup implements Closeable {
|
||||||
}
|
}
|
||||||
// Append the locale to the list of locales this word is in.
|
// Append the locale to the list of locales this word is in.
|
||||||
dictLocales.add(dictLocale);
|
dictLocales.add(dictLocale);
|
||||||
|
|
||||||
|
// 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");
|
||||||
|
}
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Else, save the shortcut.
|
||||||
|
HashMap<String, String> localeShortcuts = shortcutsPerLocale.get(dictLocale);
|
||||||
|
if (localeShortcuts == null) {
|
||||||
|
localeShortcuts = new HashMap<>();
|
||||||
|
shortcutsPerLocale.put(dictLocale, localeShortcuts);
|
||||||
|
}
|
||||||
|
// Map to the raw input, which might be capitalized.
|
||||||
|
// This lets the user create a shortcut from "gm" to "General Motors".
|
||||||
|
localeShortcuts.put(shortcut, rawDictWord);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Atomically replace the copy of mDictWords.
|
// Atomically replace the copy of mDictWords and mShortcuts.
|
||||||
mDictWords = dictWords;
|
mDictWords = dictWords;
|
||||||
|
mShortcutsPerLocale = shortcutsPerLocale;
|
||||||
|
|
||||||
// Allow other calls to loadUserDictionary to execute now.
|
// Allow other calls to loadUserDictionary to execute now.
|
||||||
mIsLoading.set(false);
|
mIsLoading.set(false);
|
||||||
|
|
||||||
|
Log.i(mTag, "loadUserDictionary() : Loaded " + mDictWords.size() + " words");
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -24,7 +24,6 @@ import android.text.InputType;
|
||||||
import android.view.inputmethod.EditorInfo;
|
import android.view.inputmethod.EditorInfo;
|
||||||
import android.view.inputmethod.InputMethodSubtype;
|
import android.view.inputmethod.InputMethodSubtype;
|
||||||
import android.view.textservice.SuggestionsInfo;
|
import android.view.textservice.SuggestionsInfo;
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import com.android.inputmethod.keyboard.Keyboard;
|
import com.android.inputmethod.keyboard.Keyboard;
|
||||||
import com.android.inputmethod.keyboard.KeyboardId;
|
import com.android.inputmethod.keyboard.KeyboardId;
|
||||||
|
@ -83,7 +82,6 @@ public final class AndroidSpellCheckerService extends SpellCheckerService
|
||||||
|
|
||||||
public static final String SINGLE_QUOTE = "\u0027";
|
public static final String SINGLE_QUOTE = "\u0027";
|
||||||
public static final String APOSTROPHE = "\u2019";
|
public static final String APOSTROPHE = "\u2019";
|
||||||
private UserDictionaryLookup mUserDictionaryLookup;
|
|
||||||
|
|
||||||
public AndroidSpellCheckerService() {
|
public AndroidSpellCheckerService() {
|
||||||
super();
|
super();
|
||||||
|
@ -95,30 +93,11 @@ public final class AndroidSpellCheckerService extends SpellCheckerService
|
||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
mRecommendedThreshold =
|
mRecommendedThreshold = Float.parseFloat(
|
||||||
Float.parseFloat(getString(R.string.spellchecker_recommended_threshold_value));
|
getString(R.string.spellchecker_recommended_threshold_value));
|
||||||
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||||
prefs.registerOnSharedPreferenceChangeListener(this);
|
prefs.registerOnSharedPreferenceChangeListener(this);
|
||||||
onSharedPreferenceChanged(prefs, PREF_USE_CONTACTS_KEY);
|
onSharedPreferenceChanged(prefs, PREF_USE_CONTACTS_KEY);
|
||||||
// Create a UserDictionaryLookup. It needs to be close()d and set to null in onDestroy.
|
|
||||||
if (mUserDictionaryLookup == null) {
|
|
||||||
if (DEBUG) {
|
|
||||||
Log.d(TAG, "Creating mUserDictionaryLookup in onCreate");
|
|
||||||
}
|
|
||||||
mUserDictionaryLookup = new UserDictionaryLookup(this);
|
|
||||||
} else if (DEBUG) {
|
|
||||||
Log.d(TAG, "mUserDictionaryLookup already created before onCreate");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroy() {
|
|
||||||
if (DEBUG) {
|
|
||||||
Log.d(TAG, "Closing and dereferencing mUserDictionaryLookup in onDestroy");
|
|
||||||
}
|
|
||||||
mUserDictionaryLookup.close();
|
|
||||||
mUserDictionaryLookup = null;
|
|
||||||
super.onDestroy();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public float getRecommendedThreshold() {
|
public float getRecommendedThreshold() {
|
||||||
|
@ -181,16 +160,6 @@ public final class AndroidSpellCheckerService extends SpellCheckerService
|
||||||
public boolean isValidWord(final Locale locale, final String word) {
|
public boolean isValidWord(final Locale locale, final String word) {
|
||||||
mSemaphore.acquireUninterruptibly();
|
mSemaphore.acquireUninterruptibly();
|
||||||
try {
|
try {
|
||||||
if (mUserDictionaryLookup.isValidWord(word, locale)) {
|
|
||||||
if (DEBUG) {
|
|
||||||
Log.d(TAG, "mUserDictionaryLookup.isValidWord(" + word + ")=true");
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
if (DEBUG) {
|
|
||||||
Log.d(TAG, "mUserDictionaryLookup.isValidWord(" + word + ")=false");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DictionaryFacilitator dictionaryFacilitatorForLocale =
|
DictionaryFacilitator dictionaryFacilitatorForLocale =
|
||||||
mDictionaryFacilitatorCache.get(locale);
|
mDictionaryFacilitatorCache.get(locale);
|
||||||
return dictionaryFacilitatorForLocale.isValidSpellingWord(word);
|
return dictionaryFacilitatorForLocale.isValidSpellingWord(word);
|
||||||
|
|
|
@ -14,7 +14,9 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.android.inputmethod.latin.spellcheck;
|
package com.android.inputmethod.latin;
|
||||||
|
|
||||||
|
import static com.android.inputmethod.latin.UserDictionaryLookup.ANY_LOCALE;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
|
@ -25,11 +27,13 @@ import android.test.AndroidTestCase;
|
||||||
import android.test.suitebuilder.annotation.SmallTest;
|
import android.test.suitebuilder.annotation.SmallTest;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import com.android.inputmethod.latin.utils.ExecutorUtils;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unit tests for {@link UserDictionaryLookup}.
|
* Unit tests for {@link com.android.inputmethod.latin.UserDictionaryLookup}.
|
||||||
*
|
*
|
||||||
* Note, this test doesn't mock out the ContentResolver, in order to make sure UserDictionaryLookup
|
* Note, this test doesn't mock out the ContentResolver, in order to make sure UserDictionaryLookup
|
||||||
* works in a real setting.
|
* works in a real setting.
|
||||||
|
@ -68,9 +72,9 @@ public class UserDictionaryLookupTest extends AndroidTestCase {
|
||||||
* @return the Uri for the given word
|
* @return the Uri for the given word
|
||||||
*/
|
*/
|
||||||
@SuppressLint("NewApi")
|
@SuppressLint("NewApi")
|
||||||
private Uri addWord(final String word, final Locale locale, int frequency) {
|
private Uri addWord(final String word, final Locale locale, int frequency, String shortcut) {
|
||||||
// Add the given word for the given locale.
|
// Add the given word for the given locale.
|
||||||
UserDictionary.Words.addWord(mContext, word, frequency, null, locale);
|
UserDictionary.Words.addWord(mContext, word, frequency, shortcut, locale);
|
||||||
// Obtain an Uri for the given word.
|
// Obtain an Uri for the given word.
|
||||||
Cursor cursor = mContentResolver.query(UserDictionary.Words.CONTENT_URI, null,
|
Cursor cursor = mContentResolver.query(UserDictionary.Words.CONTENT_URI, null,
|
||||||
UserDictionary.Words.WORD + "='" + word + "'", null, null);
|
UserDictionary.Words.WORD + "='" + word + "'", null, null);
|
||||||
|
@ -94,14 +98,78 @@ public class UserDictionaryLookupTest extends AndroidTestCase {
|
||||||
mContentResolver.delete(uri, null, null);
|
mContentResolver.delete(uri, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private UserDictionaryLookup setUpShortcut(final Locale locale) {
|
||||||
|
// Insert "shortcut" => "Expansion" in the UserDictionary 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);
|
||||||
|
lookup.open();
|
||||||
|
while (!lookup.isLoaded()) {
|
||||||
|
}
|
||||||
|
return lookup;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testShortcutKeyMatching() {
|
||||||
|
Log.d(TAG, "testShortcutKeyMatching");
|
||||||
|
UserDictionaryLookup lookup = setUpShortcut(Locale.US);
|
||||||
|
|
||||||
|
assertEquals("Expansion", lookup.expandShortcut("shortcut", Locale.US));
|
||||||
|
assertNull(lookup.expandShortcut("Shortcut", Locale.US));
|
||||||
|
assertNull(lookup.expandShortcut("SHORTCUT", Locale.US));
|
||||||
|
assertNull(lookup.expandShortcut("shortcu", Locale.US));
|
||||||
|
assertNull(lookup.expandShortcut("shortcutt", Locale.US));
|
||||||
|
|
||||||
|
lookup.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testShortcutMatchesInputCountry() {
|
||||||
|
Log.d(TAG, "testShortcutMatchesInputCountry");
|
||||||
|
UserDictionaryLookup lookup = setUpShortcut(Locale.US);
|
||||||
|
|
||||||
|
assertEquals("Expansion", lookup.expandShortcut("shortcut", Locale.US));
|
||||||
|
assertNull(lookup.expandShortcut("shortcut", Locale.UK));
|
||||||
|
assertNull(lookup.expandShortcut("shortcut", Locale.ENGLISH));
|
||||||
|
assertNull(lookup.expandShortcut("shortcut", Locale.FRENCH));
|
||||||
|
assertNull(lookup.expandShortcut("shortcut", ANY_LOCALE));
|
||||||
|
|
||||||
|
lookup.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testShortcutMatchesInputLanguage() {
|
||||||
|
Log.d(TAG, "testShortcutMatchesInputLanguage");
|
||||||
|
UserDictionaryLookup lookup = setUpShortcut(Locale.ENGLISH);
|
||||||
|
|
||||||
|
assertEquals("Expansion", lookup.expandShortcut("shortcut", Locale.US));
|
||||||
|
assertEquals("Expansion", lookup.expandShortcut("shortcut", Locale.UK));
|
||||||
|
assertEquals("Expansion", lookup.expandShortcut("shortcut", Locale.ENGLISH));
|
||||||
|
assertNull(lookup.expandShortcut("shortcut", Locale.FRENCH));
|
||||||
|
assertNull(lookup.expandShortcut("shortcut", ANY_LOCALE));
|
||||||
|
|
||||||
|
lookup.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testShortcutMatchesAnyLocale() {
|
||||||
|
UserDictionaryLookup lookup = setUpShortcut(UserDictionaryLookup.ANY_LOCALE);
|
||||||
|
|
||||||
|
assertEquals("Expansion", lookup.expandShortcut("shortcut", Locale.US));
|
||||||
|
assertEquals("Expansion", lookup.expandShortcut("shortcut", Locale.UK));
|
||||||
|
assertEquals("Expansion", lookup.expandShortcut("shortcut", Locale.ENGLISH));
|
||||||
|
assertEquals("Expansion", lookup.expandShortcut("shortcut", Locale.FRENCH));
|
||||||
|
assertEquals("Expansion", lookup.expandShortcut("shortcut", ANY_LOCALE));
|
||||||
|
|
||||||
|
lookup.close();
|
||||||
|
}
|
||||||
|
|
||||||
public void testExactLocaleMatch() {
|
public void testExactLocaleMatch() {
|
||||||
Log.d(TAG, "testExactLocaleMatch");
|
Log.d(TAG, "testExactLocaleMatch");
|
||||||
|
|
||||||
// Insert "Foo" as capitalized in the UserDictionary under en_US locale.
|
// Insert "Foo" as capitalized in the UserDictionary under en_US locale.
|
||||||
addWord("Foo", Locale.US, 17);
|
addWord("Foo", Locale.US, 17, null);
|
||||||
|
|
||||||
// Create the UserDictionaryLookup and wait until it's loaded.
|
// Create the UserDictionaryLookup and wait until it's loaded.
|
||||||
UserDictionaryLookup lookup = new UserDictionaryLookup(mContext);
|
UserDictionaryLookup lookup = new UserDictionaryLookup(mContext, ExecutorUtils.SPELLING);
|
||||||
|
lookup.open();
|
||||||
while (!lookup.isLoaded()) {
|
while (!lookup.isLoaded()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,7 +185,7 @@ public class UserDictionaryLookupTest extends AndroidTestCase {
|
||||||
assertFalse(lookup.isValidWord("foo", Locale.ENGLISH));
|
assertFalse(lookup.isValidWord("foo", Locale.ENGLISH));
|
||||||
assertFalse(lookup.isValidWord("foo", Locale.UK));
|
assertFalse(lookup.isValidWord("foo", Locale.UK));
|
||||||
assertFalse(lookup.isValidWord("foo", Locale.FRENCH));
|
assertFalse(lookup.isValidWord("foo", Locale.FRENCH));
|
||||||
assertFalse(lookup.isValidWord("foo", new Locale("")));
|
assertFalse(lookup.isValidWord("foo", ANY_LOCALE));
|
||||||
|
|
||||||
lookup.close();
|
lookup.close();
|
||||||
}
|
}
|
||||||
|
@ -126,10 +194,11 @@ public class UserDictionaryLookupTest extends AndroidTestCase {
|
||||||
Log.d(TAG, "testSubLocaleMatch");
|
Log.d(TAG, "testSubLocaleMatch");
|
||||||
|
|
||||||
// Insert "Foo" as capitalized in the UserDictionary under the en locale.
|
// Insert "Foo" as capitalized in the UserDictionary under the en locale.
|
||||||
addWord("Foo", Locale.ENGLISH, 17);
|
addWord("Foo", Locale.ENGLISH, 17, null);
|
||||||
|
|
||||||
// Create the UserDictionaryLookup and wait until it's loaded.
|
// Create the UserDictionaryLookup and wait until it's loaded.
|
||||||
UserDictionaryLookup lookup = new UserDictionaryLookup(mContext);
|
UserDictionaryLookup lookup = new UserDictionaryLookup(mContext, ExecutorUtils.SPELLING);
|
||||||
|
lookup.open();
|
||||||
while (!lookup.isLoaded()) {
|
while (!lookup.isLoaded()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,15 +219,16 @@ public class UserDictionaryLookupTest extends AndroidTestCase {
|
||||||
Log.d(TAG, "testAllLocalesMatch");
|
Log.d(TAG, "testAllLocalesMatch");
|
||||||
|
|
||||||
// Insert "Foo" as capitalized in the UserDictionary under the all locales.
|
// Insert "Foo" as capitalized in the UserDictionary under the all locales.
|
||||||
addWord("Foo", null, 17);
|
addWord("Foo", null, 17, null);
|
||||||
|
|
||||||
// Create the UserDictionaryLookup and wait until it's loaded.
|
// Create the UserDictionaryLookup and wait until it's loaded.
|
||||||
UserDictionaryLookup lookup = new UserDictionaryLookup(mContext);
|
UserDictionaryLookup lookup = new UserDictionaryLookup(mContext, ExecutorUtils.SPELLING);
|
||||||
|
lookup.open();
|
||||||
while (!lookup.isLoaded()) {
|
while (!lookup.isLoaded()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Any capitalization variation should match for fr, en and en_US.
|
// Any capitalization variation should match for fr, en and en_US.
|
||||||
assertTrue(lookup.isValidWord("foo", new Locale("")));
|
assertTrue(lookup.isValidWord("foo", ANY_LOCALE));
|
||||||
assertTrue(lookup.isValidWord("foo", Locale.FRENCH));
|
assertTrue(lookup.isValidWord("foo", Locale.FRENCH));
|
||||||
assertTrue(lookup.isValidWord("foo", Locale.ENGLISH));
|
assertTrue(lookup.isValidWord("foo", Locale.ENGLISH));
|
||||||
assertTrue(lookup.isValidWord("foo", Locale.US));
|
assertTrue(lookup.isValidWord("foo", Locale.US));
|
||||||
|
@ -177,12 +247,13 @@ public class UserDictionaryLookupTest extends AndroidTestCase {
|
||||||
|
|
||||||
// Insert "Foo" as capitalized in the UserDictionary under the en_US and en_CA and fr
|
// Insert "Foo" as capitalized in the UserDictionary under the en_US and en_CA and fr
|
||||||
// locales.
|
// locales.
|
||||||
addWord("Foo", Locale.US, 17);
|
addWord("Foo", Locale.US, 17, null);
|
||||||
addWord("foO", Locale.CANADA, 17);
|
addWord("foO", Locale.CANADA, 17, null);
|
||||||
addWord("fOo", Locale.FRENCH, 17);
|
addWord("fOo", Locale.FRENCH, 17, null);
|
||||||
|
|
||||||
// Create the UserDictionaryLookup and wait until it's loaded.
|
// Create the UserDictionaryLookup and wait until it's loaded.
|
||||||
UserDictionaryLookup lookup = new UserDictionaryLookup(mContext);
|
UserDictionaryLookup lookup = new UserDictionaryLookup(mContext, ExecutorUtils.SPELLING);
|
||||||
|
lookup.open();
|
||||||
while (!lookup.isLoaded()) {
|
while (!lookup.isLoaded()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,7 +264,7 @@ public class UserDictionaryLookupTest extends AndroidTestCase {
|
||||||
// Other locales, including more general locales won't match.
|
// Other locales, including more general locales won't match.
|
||||||
assertFalse(lookup.isValidWord("foo", Locale.ENGLISH));
|
assertFalse(lookup.isValidWord("foo", Locale.ENGLISH));
|
||||||
assertFalse(lookup.isValidWord("foo", Locale.UK));
|
assertFalse(lookup.isValidWord("foo", Locale.UK));
|
||||||
assertFalse(lookup.isValidWord("foo", new Locale("")));
|
assertFalse(lookup.isValidWord("foo", ANY_LOCALE));
|
||||||
|
|
||||||
lookup.close();
|
lookup.close();
|
||||||
}
|
}
|
||||||
|
@ -202,10 +273,11 @@ public class UserDictionaryLookupTest extends AndroidTestCase {
|
||||||
Log.d(TAG, "testReload");
|
Log.d(TAG, "testReload");
|
||||||
|
|
||||||
// Insert "foo".
|
// Insert "foo".
|
||||||
Uri uri = addWord("foo", Locale.US, 17);
|
Uri uri = addWord("foo", Locale.US, 17, null);
|
||||||
|
|
||||||
// Create the UserDictionaryLookup and wait until it's loaded.
|
// Create the UserDictionaryLookup and wait until it's loaded.
|
||||||
UserDictionaryLookup lookup = new UserDictionaryLookup(mContext);
|
UserDictionaryLookup lookup = new UserDictionaryLookup(mContext, ExecutorUtils.SPELLING);
|
||||||
|
lookup.open();
|
||||||
while (!lookup.isLoaded()) {
|
while (!lookup.isLoaded()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,7 +289,7 @@ public class UserDictionaryLookupTest extends AndroidTestCase {
|
||||||
|
|
||||||
// Now delete "foo" and add "bar".
|
// Now delete "foo" and add "bar".
|
||||||
deleteWord(uri);
|
deleteWord(uri);
|
||||||
addWord("bar", Locale.US, 18);
|
addWord("bar", Locale.US, 18, null);
|
||||||
|
|
||||||
// Wait a little bit before expecting a change. The time we wait should be greater than
|
// Wait a little bit before expecting a change. The time we wait should be greater than
|
||||||
// UserDictionaryLookup.RELOAD_DELAY_MS.
|
// UserDictionaryLookup.RELOAD_DELAY_MS.
|
||||||
|
@ -241,10 +313,11 @@ public class UserDictionaryLookupTest extends AndroidTestCase {
|
||||||
Log.d(TAG, "testClose");
|
Log.d(TAG, "testClose");
|
||||||
|
|
||||||
// Insert "foo".
|
// Insert "foo".
|
||||||
Uri uri = addWord("foo", Locale.US, 17);
|
Uri uri = addWord("foo", Locale.US, 17, null);
|
||||||
|
|
||||||
// Create the UserDictionaryLookup and wait until it's loaded.
|
// Create the UserDictionaryLookup and wait until it's loaded.
|
||||||
UserDictionaryLookup lookup = new UserDictionaryLookup(mContext);
|
UserDictionaryLookup lookup = new UserDictionaryLookup(mContext, ExecutorUtils.SPELLING);
|
||||||
|
lookup.open();
|
||||||
while (!lookup.isLoaded()) {
|
while (!lookup.isLoaded()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -259,7 +332,7 @@ public class UserDictionaryLookupTest extends AndroidTestCase {
|
||||||
|
|
||||||
// Now delete "foo" and add "bar".
|
// Now delete "foo" and add "bar".
|
||||||
deleteWord(uri);
|
deleteWord(uri);
|
||||||
addWord("bar", Locale.US, 18);
|
addWord("bar", Locale.US, 18, null);
|
||||||
|
|
||||||
// Wait a little bit before expecting a change. The time we wait should be greater than
|
// Wait a little bit before expecting a change. The time we wait should be greater than
|
||||||
// UserDictionaryLookup.RELOAD_DELAY_MS.
|
// UserDictionaryLookup.RELOAD_DELAY_MS.
|
|
@ -24,6 +24,9 @@ import com.android.inputmethod.latin.common.CollectionUtils;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests for {@link CollectionUtils}.
|
* Tests for {@link CollectionUtils}.
|
||||||
|
@ -79,9 +82,13 @@ public class CollectionUtilsTests extends AndroidTestCase {
|
||||||
* results for a few cases.
|
* results for a few cases.
|
||||||
*/
|
*/
|
||||||
public void testIsNullOrEmpty() {
|
public void testIsNullOrEmpty() {
|
||||||
assertTrue(CollectionUtils.isNullOrEmpty(null));
|
assertTrue(CollectionUtils.isNullOrEmpty((List) null));
|
||||||
assertTrue(CollectionUtils.isNullOrEmpty(new ArrayList<>()));
|
assertTrue(CollectionUtils.isNullOrEmpty((Map) null));
|
||||||
assertTrue(CollectionUtils.isNullOrEmpty(Collections.EMPTY_SET));
|
assertTrue(CollectionUtils.isNullOrEmpty(new ArrayList()));
|
||||||
assertFalse(CollectionUtils.isNullOrEmpty(Collections.singleton("Not empty")));
|
assertTrue(CollectionUtils.isNullOrEmpty(new HashMap()));
|
||||||
|
assertTrue(CollectionUtils.isNullOrEmpty(Collections.EMPTY_LIST));
|
||||||
|
assertTrue(CollectionUtils.isNullOrEmpty(Collections.EMPTY_MAP));
|
||||||
|
assertFalse(CollectionUtils.isNullOrEmpty(Collections.singletonList("Not empty")));
|
||||||
|
assertFalse(CollectionUtils.isNullOrEmpty(Collections.singletonMap("Not", "empty")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue