aboutsummaryrefslogtreecommitdiffstats
path: root/java/src
diff options
context:
space:
mode:
Diffstat (limited to 'java/src')
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java5
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java29
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java133
-rw-r--r--java/src/com/android/inputmethod/latin/StringBuilderPool.java56
-rw-r--r--java/src/com/android/inputmethod/latin/Suggest.java65
5 files changed, 181 insertions, 107 deletions
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index 21477a992..9937937ff 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -545,9 +545,8 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
private void setAutomaticTemporaryUpperCase() {
if (mKeyboardView == null) return;
final Keyboard keyboard = mKeyboardView.getKeyboard();
- if (keyboard != null) {
- keyboard.setAutomaticTemporaryUpperCase();
- }
+ if (keyboard == null) return;
+ keyboard.setAutomaticTemporaryUpperCase();
mKeyboardView.invalidateAllKeys();
}
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
index 3da670e2e..ed5f83b3b 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
@@ -98,23 +98,32 @@ public class BinaryDictionaryFileDumper {
* @throw IOException if the provider-returned data could not be read.
*/
public static List<AssetFileAddress> cacheDictionariesFromContentProvider(final Locale locale,
- final Context context) throws FileNotFoundException, IOException {
+ final Context context) {
final ContentResolver resolver = context.getContentResolver();
final List<String> idList = getDictIdList(locale, context);
final List<AssetFileAddress> fileAddressList = new ArrayList<AssetFileAddress>();
for (String id : idList) {
final Uri wordListUri = getProviderUri(id);
- final AssetFileDescriptor afd =
- resolver.openAssetFileDescriptor(wordListUri, "r");
+ AssetFileDescriptor afd = null;
+ try {
+ afd = resolver.openAssetFileDescriptor(wordListUri, "r");
+ } catch (FileNotFoundException e) {
+ // leave null inside afd and continue
+ }
if (null == afd) continue;
- final String fileName = copyFileTo(afd.createInputStream(),
- BinaryDictionaryGetter.getCacheFileName(id, locale, context));
- afd.close();
- if (0 >= resolver.delete(wordListUri, null, null)) {
- // I'd rather not print the word list ID to the log here out of security concerns
- Log.e(TAG, "Could not have the dictionary pack delete a word list");
+ try {
+ final String fileName = copyFileTo(afd.createInputStream(),
+ BinaryDictionaryGetter.getCacheFileName(id, locale, context));
+ afd.close();
+ if (0 >= resolver.delete(wordListUri, null, null)) {
+ // I'd rather not print the word list ID to the log out of security concerns
+ Log.e(TAG, "Could not have the dictionary pack delete a word list");
+ }
+ fileAddressList.add(AssetFileAddress.makeFromFileName(fileName));
+ } catch (IOException e) {
+ // Can't read the file for some reason. Continue onto the next file.
+ Log.e(TAG, "Cannot read a word list from the dictionary pack : " + e);
}
- fileAddressList.add(AssetFileAddress.makeFromFileName(fileName));
}
return fileAddressList;
}
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
index 360cf21ca..5d2dab0a9 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
@@ -42,6 +42,11 @@ class BinaryDictionaryGetter {
private static final String TAG = BinaryDictionaryGetter.class.getSimpleName();
/**
+ * Used to return empty lists
+ */
+ private static final File[] EMPTY_FILE_ARRAY = new File[0];
+
+ /**
* Name of the common preferences name to know which word list are on and which are off.
*/
private static final String COMMON_PREFERENCES_NAME = "LatinImeDictPrefs";
@@ -158,46 +163,61 @@ class BinaryDictionaryGetter {
context.getApplicationInfo().sourceDir, afd.getStartOffset(), afd.getLength());
}
+ static private class DictPackSettings {
+ final SharedPreferences mDictPreferences;
+ public DictPackSettings(final Context context) {
+ Context dictPackContext = null;
+ try {
+ final String dictPackName =
+ context.getString(R.string.dictionary_pack_package_name);
+ dictPackContext = context.createPackageContext(dictPackName, 0);
+ } catch (NameNotFoundException e) {
+ // The dictionary pack is not installed...
+ // TODO: fallback on the built-in dict, see the TODO above
+ Log.e(TAG, "Could not find a dictionary pack");
+ }
+ mDictPreferences = null == dictPackContext ? null
+ : dictPackContext.getSharedPreferences(COMMON_PREFERENCES_NAME,
+ Context.MODE_WORLD_READABLE | Context.MODE_MULTI_PROCESS);
+ }
+ public boolean isWordListActive(final String dictId) {
+ if (null == mDictPreferences) {
+ // If we don't have preferences it basically means we can't find the dictionary
+ // pack - either it's not installed, or it's disabled, or there is some strange
+ // bug. Either way, a word list with no settings should be on by default: default
+ // dictionaries in LatinIME are on if there is no settings at all, and if for some
+ // reason some dictionaries have been installed BUT the dictionary pack can't be
+ // found anymore it's safer to actually supply installed dictionaries.
+ return true;
+ } else {
+ // The default is true here for the same reasons as above. We got the dictionary
+ // pack but if we don't have any settings for it it means the user has never been
+ // to the settings yet. So by default, the main dictionaries should be on.
+ return mDictPreferences.getBoolean(dictId, true);
+ }
+ }
+ }
+
/**
* Returns the list of cached files for a specific locale.
*
* @param locale the locale to find the dictionary files for.
* @param context the context on which to open the files upon.
- * @return a list of binary dictionary files, which may be null but may not be empty.
+ * @return an array of binary dictionary files, which may be empty but may not be null.
*/
- private static List<AssetFileAddress> getCachedDictionaryList(final Locale locale,
+ private static File[] getCachedDictionaryList(final Locale locale,
final Context context) {
final String directoryName = getCacheDirectoryForLocale(locale, context);
final File[] cacheFiles = new File(directoryName).listFiles();
- // TODO: Never return null. Fallback on the built-in dictionary, and if that's
- // not present or disabled, then return an empty list.
- if (null == cacheFiles) return null;
-
- final SharedPreferences dictPackSettings;
- try {
- final String dictPackName = context.getString(R.string.dictionary_pack_package_name);
- final Context dictPackContext = context.createPackageContext(dictPackName, 0);
- dictPackSettings = dictPackContext.getSharedPreferences(COMMON_PREFERENCES_NAME,
- Context.MODE_WORLD_READABLE | Context.MODE_MULTI_PROCESS);
- } catch (NameNotFoundException e) {
- // The dictionary pack is not installed...
- // TODO: fallback on the built-in dict, see the TODO above
- Log.e(TAG, "Could not find a dictionary pack");
- return null;
- }
+ if (null == cacheFiles) return EMPTY_FILE_ARRAY;
+ return cacheFiles;
+ }
- final ArrayList<AssetFileAddress> fileList = new ArrayList<AssetFileAddress>();
- for (File f : cacheFiles) {
- final String wordListId = getWordListIdFromFileName(f.getName());
- final boolean isActive = dictPackSettings.getBoolean(wordListId, true);
- if (!isActive) continue;
- if (f.canRead()) {
- fileList.add(AssetFileAddress.makeFromFileName(f.getPath()));
- } else {
- Log.e(TAG, "Found a cached dictionary file but cannot read it");
- }
- }
- return fileList.size() > 0 ? fileList : null;
+ /**
+ * Returns the id of the main dict for a specified locale.
+ */
+ private static String getMainDictId(final Locale locale) {
+ return locale.toString();
}
/**
@@ -214,26 +234,41 @@ class BinaryDictionaryGetter {
*/
public static List<AssetFileAddress> getDictionaryFiles(final Locale locale,
final Context context, final int fallbackResId) {
- try {
- // cacheDictionariesFromContentProvider returns the list of files it copied to local
- // storage, but we don't really care about what was copied NOW: what we want is the
- // list of everything we ever cached, so we ignore the return value.
- BinaryDictionaryFileDumper.cacheDictionariesFromContentProvider(locale, context);
- List<AssetFileAddress> cachedDictionaryList = getCachedDictionaryList(locale, context);
- if (null != cachedDictionaryList) {
- return cachedDictionaryList;
+
+ // cacheDictionariesFromContentProvider returns the list of files it copied to local
+ // storage, but we don't really care about what was copied NOW: what we want is the
+ // list of everything we ever cached, so we ignore the return value.
+ BinaryDictionaryFileDumper.cacheDictionariesFromContentProvider(locale, context);
+ final File[] cachedDictionaryList = getCachedDictionaryList(locale, context);
+
+ final String mainDictId = getMainDictId(locale);
+
+ final DictPackSettings dictPackSettings = new DictPackSettings(context);
+
+ boolean foundMainDict = false;
+ final ArrayList<AssetFileAddress> fileList = new ArrayList<AssetFileAddress>();
+ // cachedDictionaryList may not be null, see doc for getCachedDictionaryList
+ for (final File f : cachedDictionaryList) {
+ final String wordListId = getWordListIdFromFileName(f.getName());
+ if (wordListId.equals(mainDictId)) {
+ foundMainDict = true;
+ }
+ if (!dictPackSettings.isWordListActive(wordListId)) continue;
+ if (f.canRead()) {
+ fileList.add(AssetFileAddress.makeFromFileName(f.getPath()));
+ } else {
+ Log.e(TAG, "Found a cached dictionary file but cannot read it");
}
- // If the list is null, fall through and return the fallback
- } catch (FileNotFoundException e) {
- Log.e(TAG, "Unable to create dictionary file from provider for locale "
- + locale.toString() + ": falling back to internal dictionary");
- } catch (IOException e) {
- Log.e(TAG, "Unable to read source data for locale "
- + locale.toString() + ": falling back to internal dictionary");
}
- final AssetFileAddress fallbackAsset = loadFallbackResource(context, fallbackResId,
- locale);
- if (null == fallbackAsset) return null;
- return Arrays.asList(fallbackAsset);
+
+ if (!foundMainDict && dictPackSettings.isWordListActive(mainDictId)) {
+ final AssetFileAddress fallbackAsset = loadFallbackResource(context, fallbackResId,
+ locale);
+ if (null != fallbackAsset) {
+ fileList.add(fallbackAsset);
+ }
+ }
+
+ return fileList;
}
}
diff --git a/java/src/com/android/inputmethod/latin/StringBuilderPool.java b/java/src/com/android/inputmethod/latin/StringBuilderPool.java
new file mode 100644
index 000000000..66f123731
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/StringBuilderPool.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.inputmethod.latin;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A pool of string builders to be used from anywhere.
+ */
+public class StringBuilderPool {
+ // Singleton
+ private static final StringBuilderPool sInstance = new StringBuilderPool();
+ private StringBuilderPool() {}
+ // TODO: Make this a normal array with a size of 20
+ private final List<StringBuilder> mPool =
+ Collections.synchronizedList(new ArrayList<StringBuilder>());
+
+ public static StringBuilder getStringBuilder(final int initialSize) {
+ final int poolSize = sInstance.mPool.size();
+ final StringBuilder sb = poolSize > 0 ? (StringBuilder) sInstance.mPool.remove(poolSize - 1)
+ : new StringBuilder(initialSize);
+ sb.setLength(0);
+ return sb;
+ }
+
+ public static void recycle(final StringBuilder garbage) {
+ sInstance.mPool.add(garbage);
+ }
+
+ public static void ensureCapacity(final int capacity, final int initialSize) {
+ for (int i = sInstance.mPool.size(); i < capacity; ++i) {
+ final StringBuilder sb = new StringBuilder(initialSize);
+ sInstance.mPool.add(sb);
+ }
+ }
+
+ public static int getSize() {
+ return sInstance.mPool.size();
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index a2d66f398..c3caae448 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -105,9 +105,6 @@ public class Suggest implements Dictionary.WordCallback {
private ArrayList<CharSequence> mSuggestions = new ArrayList<CharSequence>();
ArrayList<CharSequence> mBigramSuggestions = new ArrayList<CharSequence>();
- // TODO: maybe this should be synchronized, it's quite scary as it is.
- // TODO: if it becomes synchronized, also move initPool in the thread in initAsynchronously
- private ArrayList<CharSequence> mStringPool = new ArrayList<CharSequence>();
private CharSequence mTypedWord;
// TODO: Remove these member variables by passing more context to addWord() callback method
@@ -130,7 +127,7 @@ public class Suggest implements Dictionary.WordCallback {
mWhiteListDictionary = WhitelistDictionary.init(context);
addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_WHITELIST, mWhiteListDictionary);
mAutoCorrection = new AutoCorrection();
- initPool();
+ StringBuilderPool.ensureCapacity(mPrefMaxSuggestions, getApproxMaxWordLength());
}
private void initAsynchronously(final Context context, final int dictionaryResId,
@@ -138,7 +135,7 @@ public class Suggest implements Dictionary.WordCallback {
resetMainDict(context, dictionaryResId, locale);
// TODO: read the whitelist and init the pool asynchronously too.
- // initPool should be done asynchronously but the pool is not thread-safe at the moment.
+ // initPool should be done asynchronously now that the pool is thread-safe.
initWhitelistAndAutocorrectAndPool(context);
}
@@ -173,12 +170,6 @@ public class Suggest implements Dictionary.WordCallback {
}.start();
}
- private void initPool() {
- for (int i = 0; i < mPrefMaxSuggestions; i++) {
- StringBuilder sb = new StringBuilder(getApproxMaxWordLength());
- mStringPool.add(sb);
- }
- }
public void setQuickFixesEnabled(boolean enabled) {
mQuickFixesEnabled = enabled;
@@ -259,10 +250,7 @@ public class Suggest implements Dictionary.WordCallback {
mScores = new int[mPrefMaxSuggestions];
mBigramScores = new int[PREF_MAX_BIGRAMS];
collectGarbage(mSuggestions, mPrefMaxSuggestions);
- while (mStringPool.size() < mPrefMaxSuggestions) {
- StringBuilder sb = new StringBuilder(getApproxMaxWordLength());
- mStringPool.add(sb);
- }
+ StringBuilderPool.ensureCapacity(mPrefMaxSuggestions, getApproxMaxWordLength());
}
/**
@@ -282,11 +270,7 @@ public class Suggest implements Dictionary.WordCallback {
private CharSequence capitalizeWord(boolean all, boolean first, CharSequence word) {
if (TextUtils.isEmpty(word) || !(all || first)) return word;
final int wordLength = word.length();
- final int poolSize = mStringPool.size();
- final StringBuilder sb =
- poolSize > 0 ? (StringBuilder) mStringPool.remove(poolSize - 1)
- : new StringBuilder(getApproxMaxWordLength());
- sb.setLength(0);
+ final StringBuilder sb = StringBuilderPool.getStringBuilder(getApproxMaxWordLength());
// TODO: Must pay attention to locale when changing case.
if (all) {
sb.append(word.toString().toUpperCase());
@@ -300,13 +284,7 @@ public class Suggest implements Dictionary.WordCallback {
}
protected void addBigramToSuggestions(CharSequence bigram) {
- final int poolSize = mStringPool.size();
- final StringBuilder sb = poolSize > 0 ?
- (StringBuilder) mStringPool.remove(poolSize - 1)
- : new StringBuilder(getApproxMaxWordLength());
- sb.setLength(0);
- sb.append(bigram);
- mSuggestions.add(sb);
+ mSuggestions.add(bigram);
}
// TODO: cleanup dictionaries looking up and suggestions building with SuggestedWords.Builder
@@ -426,7 +404,7 @@ public class Suggest implements Dictionary.WordCallback {
if (typedWord != null) {
mSuggestions.add(0, typedWordString);
}
- removeDupes();
+ removeDupes(mSuggestions);
if (DBG) {
double normalizedScore = mAutoCorrection.getNormalizedScore();
@@ -453,8 +431,7 @@ public class Suggest implements Dictionary.WordCallback {
return new SuggestedWords.Builder().addWords(mSuggestions, null);
}
- private void removeDupes() {
- final ArrayList<CharSequence> suggestions = mSuggestions;
+ private static void removeDupes(final ArrayList<CharSequence> suggestions) {
if (suggestions.size() < 2) return;
int i = 1;
// Don't cache suggestions.size(), since we may be removing items
@@ -464,7 +441,7 @@ public class Suggest implements Dictionary.WordCallback {
for (int j = 0; j < i; j++) {
CharSequence previous = suggestions.get(j);
if (TextUtils.equals(cur, previous)) {
- removeFromSuggestions(i);
+ removeFromSuggestions(suggestions, i);
i--;
break;
}
@@ -473,10 +450,11 @@ public class Suggest implements Dictionary.WordCallback {
}
}
- private void removeFromSuggestions(int index) {
- CharSequence garbage = mSuggestions.remove(index);
- if (garbage != null && garbage instanceof StringBuilder) {
- mStringPool.add(garbage);
+ private static void removeFromSuggestions(final ArrayList<CharSequence> suggestions,
+ final int index) {
+ final CharSequence garbage = suggestions.remove(index);
+ if (garbage instanceof StringBuilder) {
+ StringBuilderPool.recycle((StringBuilder)garbage);
}
}
@@ -554,10 +532,7 @@ public class Suggest implements Dictionary.WordCallback {
System.arraycopy(sortedScores, pos, sortedScores, pos + 1, prefMaxSuggestions - pos - 1);
sortedScores[pos] = score;
- int poolSize = mStringPool.size();
- StringBuilder sb = poolSize > 0 ? (StringBuilder) mStringPool.remove(poolSize - 1)
- : new StringBuilder(getApproxMaxWordLength());
- sb.setLength(0);
+ final StringBuilder sb = StringBuilderPool.getStringBuilder(getApproxMaxWordLength());
// TODO: Must pay attention to locale when changing case.
if (mIsAllUpperCase) {
sb.append(new String(word, offset, length).toUpperCase());
@@ -571,9 +546,9 @@ public class Suggest implements Dictionary.WordCallback {
}
suggestions.add(pos, sb);
if (suggestions.size() > prefMaxSuggestions) {
- CharSequence garbage = suggestions.remove(prefMaxSuggestions);
+ final CharSequence garbage = suggestions.remove(prefMaxSuggestions);
if (garbage instanceof StringBuilder) {
- mStringPool.add(garbage);
+ StringBuilderPool.recycle((StringBuilder)garbage);
}
} else {
LatinImeLogger.onAddSuggestedWord(sb.toString(), dicTypeId, dataTypeForLog);
@@ -602,12 +577,12 @@ public class Suggest implements Dictionary.WordCallback {
}
private void collectGarbage(ArrayList<CharSequence> suggestions, int prefMaxSuggestions) {
- int poolSize = mStringPool.size();
+ int poolSize = StringBuilderPool.getSize();
int garbageSize = suggestions.size();
while (poolSize < prefMaxSuggestions && garbageSize > 0) {
- CharSequence garbage = suggestions.get(garbageSize - 1);
- if (garbage != null && garbage instanceof StringBuilder) {
- mStringPool.add(garbage);
+ final CharSequence garbage = suggestions.get(garbageSize - 1);
+ if (garbage instanceof StringBuilder) {
+ StringBuilderPool.recycle((StringBuilder)garbage);
poolSize++;
}
garbageSize--;