aboutsummaryrefslogtreecommitdiffstats
path: root/java/src/com/android/inputmethod/latin/personalization
diff options
context:
space:
mode:
Diffstat (limited to 'java/src/com/android/inputmethod/latin/personalization')
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java231
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/DynamicPersonalizationDictionaryWriter.java44
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/DynamicPredictionDictionaryBase.java416
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdateSession.java17
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java (renamed from java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryHelper.java)5
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/PersonalizationPredictionDictionary.java10
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryBigramList.java9
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/UserHistoryPredictionDictionary.java9
8 files changed, 293 insertions, 448 deletions
diff --git a/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java b/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java
new file mode 100644
index 000000000..66517a800
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.personalization;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.util.Log;
+
+import com.android.inputmethod.annotations.UsedForTesting;
+import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.latin.ExpandableBinaryDictionary;
+import com.android.inputmethod.latin.LatinImeLogger;
+import com.android.inputmethod.latin.makedict.DictDecoder;
+import com.android.inputmethod.latin.makedict.FormatSpec;
+import com.android.inputmethod.latin.settings.Settings;
+import com.android.inputmethod.latin.utils.CollectionUtils;
+import com.android.inputmethod.latin.utils.UserHistoryDictIOUtils;
+import com.android.inputmethod.latin.utils.UserHistoryDictIOUtils.OnAddWordListener;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This class is a base class of a dictionary that supports decaying for the personalized language
+ * model.
+ */
+public abstract class DecayingExpandableBinaryDictionaryBase extends ExpandableBinaryDictionary {
+ private static final String TAG = DecayingExpandableBinaryDictionaryBase.class.getSimpleName();
+ public static final boolean DBG_SAVE_RESTORE = false;
+ private static final boolean DBG_STRESS_TEST = false;
+ private static final boolean PROFILE_SAVE_RESTORE = LatinImeLogger.sDBG;
+
+ /** Any pair being typed or picked */
+ public static final int FREQUENCY_FOR_TYPED = 2;
+
+ /** Locale for which this user history dictionary is storing words */
+ private final String mLocale;
+
+ private final String mFileName;
+
+ private final SharedPreferences mPrefs;
+
+ private final ArrayList<PersonalizationDictionaryUpdateSession> mSessions =
+ CollectionUtils.newArrayList();
+
+ // Should always be false except when we use this class for test
+ @UsedForTesting boolean mIsTest = false;
+
+ /* package */ DecayingExpandableBinaryDictionaryBase(final Context context,
+ final String locale, final SharedPreferences sp, final String dictionaryType,
+ final String fileName) {
+ super(context, fileName, dictionaryType, true);
+ mLocale = locale;
+ mFileName = fileName;
+ mPrefs = sp;
+ if (mLocale != null && mLocale.length() > 1) {
+ asyncLoadDictionaryToMemory();
+ reloadDictionaryIfRequired();
+ }
+ }
+
+ @Override
+ public void close() {
+ if (!ExpandableBinaryDictionary.ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) {
+ closeBinaryDictionary();
+ }
+ // Flush pending writes.
+ // TODO: Remove after this class become to use a dynamic binary dictionary.
+ asyncFlashAllBinaryDictionary();
+ Settings.writeLastUserHistoryWriteTime(mPrefs, mLocale);
+ }
+
+ @Override
+ protected Map<String, String> getHeaderAttributeMap() {
+ HashMap<String, String> attributeMap = new HashMap<String, String>();
+ attributeMap.put(FormatSpec.FileHeader.SUPPORTS_DYNAMIC_UPDATE_ATTRIBUTE,
+ FormatSpec.FileHeader.ATTRIBUTE_VALUE_TRUE);
+ attributeMap.put(FormatSpec.FileHeader.USES_FORGETTING_CURVE_ATTRIBUTE,
+ FormatSpec.FileHeader.ATTRIBUTE_VALUE_TRUE);
+ return attributeMap;
+ }
+
+ @Override
+ protected boolean hasContentChanged() {
+ return false;
+ }
+
+ @Override
+ protected boolean needsToReloadBeforeWriting() {
+ return false;
+ }
+
+ /**
+ * Return whether the passed charsequence is in the dictionary.
+ */
+ @Override
+ public boolean isValidWord(final String word) {
+ // Words included only in the user history should be treated as not in dictionary words.
+ return false;
+ }
+
+ /**
+ * Pair will be added to the personalization prediction dictionary.
+ *
+ * The first word may be null. That means we don't know the context, in other words,
+ * it's only a unigram. The first word may also be an empty string : this means start
+ * context, as in beginning of a sentence for example.
+ * The second word may not be null (a NullPointerException would be thrown).
+ */
+ public void addToPersonalizationPredictionDictionary(
+ final String word0, final String word1, final boolean isValid) {
+ if (word1.length() >= Constants.DICTIONARY_MAX_WORD_LENGTH ||
+ (word0 != null && word0.length() >= Constants.DICTIONARY_MAX_WORD_LENGTH)) {
+ return;
+ }
+ addWordDynamically(word1, null /* the "shortcut" parameter is null */, FREQUENCY_FOR_TYPED,
+ false /* isNotAWord */);
+ // Do not insert a word as a bigram of itself
+ if (word1.equals(word0)) {
+ return;
+ }
+ if (null != word0) {
+ addBigramDynamically(word0, word1, FREQUENCY_FOR_TYPED, isValid);
+ }
+ }
+
+ public void cancelAddingUserHistory(final String word0, final String word1) {
+ removeBigramDynamically(word0, word1);
+ }
+
+ @Override
+ protected void loadDictionaryAsync() {
+ final int[] profTotalCount = { 0 };
+ final String locale = getLocale();
+ if (DBG_STRESS_TEST) {
+ try {
+ Log.w(TAG, "Start stress in loading: " + locale);
+ Thread.sleep(15000);
+ Log.w(TAG, "End stress in loading");
+ } catch (InterruptedException e) {
+ }
+ }
+ final long last = Settings.readLastUserHistoryWriteTime(mPrefs, locale);
+ final long now = System.currentTimeMillis();
+ final ExpandableBinaryDictionary dictionary = this;
+ final OnAddWordListener listener = new OnAddWordListener() {
+ @Override
+ public void setUnigram(final String word, final String shortcutTarget,
+ final int frequency) {
+ if (DBG_SAVE_RESTORE) {
+ Log.d(TAG, "load unigram: " + word + "," + frequency);
+ }
+ addWord(word, shortcutTarget, frequency, false /* isNotAWord */);
+ ++profTotalCount[0];
+ }
+
+ @Override
+ public void setBigram(final String word0, final String word1, final int frequency) {
+ if (word0.length() < Constants.DICTIONARY_MAX_WORD_LENGTH
+ && word1.length() < Constants.DICTIONARY_MAX_WORD_LENGTH) {
+ if (DBG_SAVE_RESTORE) {
+ Log.d(TAG, "load bigram: " + word0 + "," + word1 + "," + frequency);
+ }
+ ++profTotalCount[0];
+ addBigram(word0, word1, frequency, last);
+ }
+ }
+ };
+
+ // Load the dictionary from binary file
+ final File dictFile = new File(mContext.getFilesDir(), mFileName);
+ final DictDecoder dictDecoder = FormatSpec.getDictDecoder(dictFile,
+ DictDecoder.USE_BYTEARRAY);
+ if (dictDecoder == null) {
+ // This is an expected condition: we don't have a user history dictionary for this
+ // language yet. It will be created sometime later.
+ return;
+ }
+
+ try {
+ dictDecoder.openDictBuffer();
+ UserHistoryDictIOUtils.readDictionaryBinary(dictDecoder, listener);
+ } catch (IOException e) {
+ Log.d(TAG, "IOException on opening a bytebuffer", e);
+ } finally {
+ if (PROFILE_SAVE_RESTORE) {
+ final long diff = System.currentTimeMillis() - now;
+ Log.d(TAG, "PROF: Load UserHistoryDictionary: "
+ + locale + ", " + diff + "ms. load " + profTotalCount[0] + "entries.");
+ }
+ }
+ }
+
+ protected String getLocale() {
+ return mLocale;
+ }
+
+ public void registerUpdateSession(PersonalizationDictionaryUpdateSession session) {
+ session.setPredictionDictionary(this);
+ mSessions.add(session);
+ session.onDictionaryReady();
+ }
+
+ public void unRegisterUpdateSession(PersonalizationDictionaryUpdateSession session) {
+ mSessions.remove(session);
+ }
+
+ public void clearAndFlushDictionary() {
+ // Clear the node structure on memory
+ clear();
+ // Then flush the cleared state of the dictionary on disk.
+ asyncFlashAllBinaryDictionary();
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/personalization/DynamicPersonalizationDictionaryWriter.java b/java/src/com/android/inputmethod/latin/personalization/DynamicPersonalizationDictionaryWriter.java
index a1d93efc4..0af028a9e 100644
--- a/java/src/com/android/inputmethod/latin/personalization/DynamicPersonalizationDictionaryWriter.java
+++ b/java/src/com/android/inputmethod/latin/personalization/DynamicPersonalizationDictionaryWriter.java
@@ -18,6 +18,8 @@ package com.android.inputmethod.latin.personalization;
import android.content.Context;
+import com.android.inputmethod.annotations.UsedForTesting;
+import com.android.inputmethod.compat.ActivityManagerCompatUtils;
import com.android.inputmethod.keyboard.ProximityInfo;
import com.android.inputmethod.latin.AbstractDictionaryWriter;
import com.android.inputmethod.latin.ExpandableDictionary;
@@ -40,7 +42,8 @@ import java.util.ArrayList;
public class DynamicPersonalizationDictionaryWriter extends AbstractDictionaryWriter {
private static final String TAG = DynamicPersonalizationDictionaryWriter.class.getSimpleName();
/** Maximum number of pairs. Pruning will start when databases goes above this number. */
- public static final int MAX_HISTORY_BIGRAMS = 10000;
+ public static final int DEFAULT_MAX_HISTORY_BIGRAMS = 10000;
+ public static final int LOW_MEMORY_MAX_HISTORY_BIGRAMS = 2000;
/** Any pair being typed or picked */
private static final int FREQUENCY_FOR_TYPED = 2;
@@ -52,10 +55,14 @@ public class DynamicPersonalizationDictionaryWriter extends AbstractDictionaryWr
private final UserHistoryDictionaryBigramList mBigramList =
new UserHistoryDictionaryBigramList();
private final ExpandableDictionary mExpandableDictionary;
+ private final int mMaxHistoryBigrams;
public DynamicPersonalizationDictionaryWriter(final Context context, final String dictType) {
super(context, dictType);
- mExpandableDictionary = new ExpandableDictionary(context, dictType);
+ mExpandableDictionary = new ExpandableDictionary(dictType);
+ final boolean isLowRamDevice = ActivityManagerCompatUtils.isLowRamDevice(context);
+ mMaxHistoryBigrams = isLowRamDevice ?
+ LOW_MEMORY_MAX_HISTORY_BIGRAMS : DEFAULT_MAX_HISTORY_BIGRAMS;
}
@Override
@@ -71,6 +78,10 @@ public class DynamicPersonalizationDictionaryWriter extends AbstractDictionaryWr
@Override
public void addUnigramWord(final String word, final String shortcutTarget, final int frequency,
final boolean isNotAWord) {
+ if (mBigramList.size() > mMaxHistoryBigrams * 2) {
+ // Too many entries: just stop adding new vocabrary and wait next refresh.
+ return;
+ }
mExpandableDictionary.addWord(word, shortcutTarget, frequency);
mBigramList.addBigram(null, word, (byte)frequency);
}
@@ -78,6 +89,10 @@ public class DynamicPersonalizationDictionaryWriter extends AbstractDictionaryWr
@Override
public void addBigramWords(final String word0, final String word1, final int frequency,
final boolean isValid, final long lastModifiedTime) {
+ if (mBigramList.size() > mMaxHistoryBigrams * 2) {
+ // Too many entries: just stop adding new vocabrary and wait next refresh.
+ return;
+ }
if (lastModifiedTime > 0) {
mExpandableDictionary.setBigramAndGetFrequency(word0, word1,
new ForgettingCurveParams(frequency, System.currentTimeMillis(),
@@ -101,19 +116,22 @@ public class DynamicPersonalizationDictionaryWriter extends AbstractDictionaryWr
protected void writeDictionary(final DictEncoder dictEncoder)
throws IOException, UnsupportedFormatException {
UserHistoryDictIOUtils.writeDictionary(dictEncoder,
- new FrequencyProvider(mBigramList, mExpandableDictionary), mBigramList,
- FORMAT_OPTIONS);
+ new FrequencyProvider(mBigramList, mExpandableDictionary, mMaxHistoryBigrams),
+ mBigramList, FORMAT_OPTIONS);
}
private static class FrequencyProvider implements BigramDictionaryInterface {
- final private UserHistoryDictionaryBigramList mBigramList;
- final private ExpandableDictionary mExpandableDictionary;
+ private final UserHistoryDictionaryBigramList mBigramList;
+ private final ExpandableDictionary mExpandableDictionary;
+ private final int mMaxHistoryBigrams;
public FrequencyProvider(final UserHistoryDictionaryBigramList bigramList,
- final ExpandableDictionary expandableDictionary) {
+ final ExpandableDictionary expandableDictionary, final int maxHistoryBigrams) {
mBigramList = bigramList;
mExpandableDictionary = expandableDictionary;
+ mMaxHistoryBigrams = maxHistoryBigrams;
}
+
@Override
public int getFrequency(final String word0, final String word1) {
final int freq;
@@ -129,7 +147,7 @@ public class DynamicPersonalizationDictionaryWriter extends AbstractDictionaryWr
if (prevFc > 0 && prevFc == fc) {
freq = fc & 0xFF;
} else if (UserHistoryForgettingCurveUtils.
- needsToSave(fc, isValid, mBigramList.size() <= MAX_HISTORY_BIGRAMS)) {
+ needsToSave(fc, isValid, mBigramList.size() <= mMaxHistoryBigrams)) {
freq = fc & 0xFF;
} else {
// Delete this entry
@@ -147,13 +165,19 @@ public class DynamicPersonalizationDictionaryWriter extends AbstractDictionaryWr
@Override
public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
final String prevWord, final ProximityInfo proximityInfo,
- boolean blockOffensiveWords) {
+ boolean blockOffensiveWords, final int[] additionalFeaturesOptions) {
return mExpandableDictionary.getSuggestions(composer, prevWord, proximityInfo,
- blockOffensiveWords);
+ blockOffensiveWords, additionalFeaturesOptions);
}
@Override
public boolean isValidWord(final String word) {
return mExpandableDictionary.isValidWord(word);
}
+
+ @UsedForTesting
+ public boolean isInDictionaryForTests(final String word) {
+ // TODO: Use native method to determine whether the word is in dictionary or not
+ return mBigramList.containsKey(word);
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/personalization/DynamicPredictionDictionaryBase.java b/java/src/com/android/inputmethod/latin/personalization/DynamicPredictionDictionaryBase.java
deleted file mode 100644
index d6456a3b9..000000000
--- a/java/src/com/android/inputmethod/latin/personalization/DynamicPredictionDictionaryBase.java
+++ /dev/null
@@ -1,416 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.latin.personalization;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.os.AsyncTask;
-import android.util.Log;
-
-import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.keyboard.ProximityInfo;
-import com.android.inputmethod.latin.Constants;
-import com.android.inputmethod.latin.ExpandableDictionary;
-import com.android.inputmethod.latin.LatinImeLogger;
-import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
-import com.android.inputmethod.latin.WordComposer;
-import com.android.inputmethod.latin.makedict.DictDecoder;
-import com.android.inputmethod.latin.makedict.DictEncoder;
-import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
-import com.android.inputmethod.latin.makedict.Ver3DictDecoder;
-import com.android.inputmethod.latin.makedict.Ver3DictEncoder;
-import com.android.inputmethod.latin.settings.Settings;
-import com.android.inputmethod.latin.utils.CollectionUtils;
-import com.android.inputmethod.latin.utils.UserHistoryDictIOUtils;
-import com.android.inputmethod.latin.utils.UserHistoryDictIOUtils.BigramDictionaryInterface;
-import com.android.inputmethod.latin.utils.UserHistoryDictIOUtils.OnAddWordListener;
-import com.android.inputmethod.latin.utils.UserHistoryForgettingCurveUtils;
-import com.android.inputmethod.latin.utils.UserHistoryForgettingCurveUtils.ForgettingCurveParams;
-
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.concurrent.locks.ReentrantLock;
-
-/**
- * This class is a base class of a dictionary for the personalized prediction language model.
- */
-public abstract class DynamicPredictionDictionaryBase extends ExpandableDictionary {
-
- private static final String TAG = DynamicPredictionDictionaryBase.class.getSimpleName();
- public static final boolean DBG_SAVE_RESTORE = false;
- private static final boolean DBG_STRESS_TEST = false;
- private static final boolean PROFILE_SAVE_RESTORE = LatinImeLogger.sDBG;
-
- private static final FormatOptions VERSION3 = new FormatOptions(3,
- true /* supportsDynamicUpdate */);
-
- /** Any pair being typed or picked */
- private static final int FREQUENCY_FOR_TYPED = 2;
-
- /** Maximum number of pairs. Pruning will start when databases goes above this number. */
- private static final int MAX_HISTORY_BIGRAMS = 10000;
-
- /** Locale for which this user history dictionary is storing words */
- private final String mLocale;
-
- private final UserHistoryDictionaryBigramList mBigramList =
- new UserHistoryDictionaryBigramList();
- private final ReentrantLock mBigramListLock = new ReentrantLock();
- private final SharedPreferences mPrefs;
-
- private final ArrayList<PersonalizationDictionaryUpdateSession> mSessions =
- CollectionUtils.newArrayList();
-
- private final AtomicReference<AsyncTask<Void, Void, Void>> mWaitingTask;
-
- // Should always be false except when we use this class for test
- @UsedForTesting boolean mIsTest = false;
-
- /* package */ DynamicPredictionDictionaryBase(final Context context, final String locale,
- final SharedPreferences sp, final String dictionaryType) {
- super(context, dictionaryType);
- mLocale = locale;
- mPrefs = sp;
- mWaitingTask = new AtomicReference<AsyncTask<Void, Void, Void>>();
- if (mLocale != null && mLocale.length() > 1) {
- loadDictionary();
- }
- }
-
- @Override
- public void close() {
- flushPendingWrites();
- // Don't close the database as locale changes will require it to be reopened anyway
- // Also, the database is written to somewhat frequently, so it needs to be kept alive
- // throughout the life of the process.
- // mOpenHelper.close();
- // Ignore close because we cache PersonalizationPredictionDictionary for each language.
- // See getInstance() above.
- // super.close();
- }
-
- @Override
- protected ArrayList<SuggestedWordInfo> getWordsInner(final WordComposer composer,
- final String prevWord, final ProximityInfo proximityInfo) {
- // Inhibit suggestions (not predictions) for user history for now. Removing this method
- // is enough to use it through the standard ExpandableDictionary way.
- return null;
- }
-
- /**
- * Return whether the passed charsequence is in the dictionary.
- */
- @Override
- public synchronized boolean isValidWord(final String word) {
- // TODO: figure out what is the correct thing to do here.
- return false;
- }
-
- /**
- * Pair will be added to the personalization prediction dictionary.
- *
- * The first word may be null. That means we don't know the context, in other words,
- * it's only a unigram. The first word may also be an empty string : this means start
- * context, as in beginning of a sentence for example.
- * The second word may not be null (a NullPointerException would be thrown).
- */
- public int addToPersonalizationPredictionDictionary(
- final String word1, final String word2, final boolean isValid) {
- if (word2.length() >= Constants.DICTIONARY_MAX_WORD_LENGTH ||
- (word1 != null && word1.length() >= Constants.DICTIONARY_MAX_WORD_LENGTH)) {
- return -1;
- }
- if (mBigramListLock.tryLock()) {
- try {
- super.addWord(
- word2, null /* the "shortcut" parameter is null */, FREQUENCY_FOR_TYPED);
- mBigramList.addBigram(null, word2, (byte)FREQUENCY_FOR_TYPED);
- // Do not insert a word as a bigram of itself
- if (word2.equals(word1)) {
- return 0;
- }
- final int freq;
- if (null == word1) {
- freq = FREQUENCY_FOR_TYPED;
- } else {
- freq = super.setBigramAndGetFrequency(
- word1, word2, new ForgettingCurveParams(isValid));
- }
- mBigramList.addBigram(word1, word2);
- return freq;
- } finally {
- mBigramListLock.unlock();
- }
- }
- return -1;
- }
-
- public boolean cancelAddingUserHistory(final String word1, final String word2) {
- if (mBigramListLock.tryLock()) {
- try {
- if (mBigramList.removeBigram(word1, word2)) {
- return super.removeBigram(word1, word2);
- }
- } finally {
- mBigramListLock.unlock();
- }
- }
- return false;
- }
-
- /**
- * Schedules a background thread to write any pending words to the database.
- */
- private void flushPendingWrites() {
- // Create a background thread to write the pending entries
- final AsyncTask<Void, Void, Void> old = mWaitingTask.getAndSet(new UpdateBinaryTask(
- mBigramList, mLocale, this, mPrefs, getContext()).execute());
- if (old != null) {
- old.cancel(false);
- }
- }
-
- @Override
- public final void loadDictionaryAsync() {
- // This must be run on non-main thread
- mBigramListLock.lock();
- try {
- loadDictionaryAsyncLocked();
- } finally {
- mBigramListLock.unlock();
- }
- }
-
- private void loadDictionaryAsyncLocked() {
- final int[] profTotalCount = { 0 };
- final String locale = getLocale();
- if (DBG_STRESS_TEST) {
- try {
- Log.w(TAG, "Start stress in loading: " + locale);
- Thread.sleep(15000);
- Log.w(TAG, "End stress in loading");
- } catch (InterruptedException e) {
- }
- }
- final long last = Settings.readLastUserHistoryWriteTime(mPrefs, locale);
- final boolean initializing = last == 0;
- final long now = System.currentTimeMillis();
- final String fileName = getDictionaryFileName();
- final ExpandableDictionary dictionary = this;
- final OnAddWordListener listener = new OnAddWordListener() {
- @Override
- public void setUnigram(final String word, final String shortcutTarget,
- final int frequency) {
- if (DBG_SAVE_RESTORE) {
- Log.d(TAG, "load unigram: " + word + "," + frequency);
- }
- dictionary.addWord(word, shortcutTarget, frequency);
- ++profTotalCount[0];
- addToBigramListLocked(null, word, (byte)frequency);
- }
-
- @Override
- public void setBigram(final String word1, final String word2, final int frequency) {
- if (word1.length() < Constants.DICTIONARY_MAX_WORD_LENGTH
- && word2.length() < Constants.DICTIONARY_MAX_WORD_LENGTH) {
- if (DBG_SAVE_RESTORE) {
- Log.d(TAG, "load bigram: " + word1 + "," + word2 + "," + frequency);
- }
- ++profTotalCount[0];
- dictionary.setBigramAndGetFrequency(
- word1, word2, initializing ? new ForgettingCurveParams(true)
- : new ForgettingCurveParams(frequency, now, last));
- }
- addToBigramListLocked(word1, word2, (byte)frequency);
- }
- };
-
- // Load the dictionary from binary file
- final File dictFile = new File(getContext().getFilesDir(), fileName);
- final Ver3DictDecoder dictDecoder = new Ver3DictDecoder(dictFile,
- DictDecoder.USE_BYTEARRAY);
- try {
- dictDecoder.openDictBuffer();
- UserHistoryDictIOUtils.readDictionaryBinary(dictDecoder, listener);
- } catch (FileNotFoundException e) {
- // This is an expected condition: we don't have a user history dictionary for this
- // language yet. It will be created sometime later.
- } catch (IOException e) {
- Log.e(TAG, "IOException on opening a bytebuffer", e);
- } finally {
- if (PROFILE_SAVE_RESTORE) {
- final long diff = System.currentTimeMillis() - now;
- Log.d(TAG, "PROF: Load UserHistoryDictionary: "
- + locale + ", " + diff + "ms. load " + profTotalCount[0] + "entries.");
- }
- }
- }
-
- protected abstract String getDictionaryFileName();
-
- protected String getLocale() {
- return mLocale;
- }
-
- private void addToBigramListLocked(String word0, String word1, byte fcValue) {
- mBigramList.addBigram(word0, word1, fcValue);
- }
-
- /**
- * Async task to write pending words to the binarydicts.
- */
- private static final class UpdateBinaryTask extends AsyncTask<Void, Void, Void>
- implements BigramDictionaryInterface {
- private final UserHistoryDictionaryBigramList mBigramList;
- private final boolean mAddLevel0Bigrams;
- private final String mLocale;
- private final DynamicPredictionDictionaryBase mDynamicPredictionDictionary;
- private final SharedPreferences mPrefs;
- private final Context mContext;
-
- public UpdateBinaryTask(final UserHistoryDictionaryBigramList pendingWrites,
- final String locale, final DynamicPredictionDictionaryBase dict,
- final SharedPreferences prefs, final Context context) {
- mBigramList = pendingWrites;
- mLocale = locale;
- mDynamicPredictionDictionary = dict;
- mPrefs = prefs;
- mContext = context;
- mAddLevel0Bigrams = mBigramList.size() <= MAX_HISTORY_BIGRAMS;
- }
-
- @Override
- protected Void doInBackground(final Void... v) {
- if (isCancelled()) return null;
- if (mDynamicPredictionDictionary.mIsTest) {
- // If mIsTest == true, wait until the lock is released.
- mDynamicPredictionDictionary.mBigramListLock.lock();
- try {
- doWriteTaskLocked();
- } finally {
- mDynamicPredictionDictionary.mBigramListLock.unlock();
- }
- } else if (mDynamicPredictionDictionary.mBigramListLock.tryLock()) {
- try {
- doWriteTaskLocked();
- } finally {
- mDynamicPredictionDictionary.mBigramListLock.unlock();
- }
- }
- return null;
- }
-
- private void doWriteTaskLocked() {
- if (isCancelled()) return;
- mDynamicPredictionDictionary.mWaitingTask.compareAndSet(this, null);
-
- if (DBG_STRESS_TEST) {
- try {
- Log.w(TAG, "Start stress in closing: " + mLocale);
- Thread.sleep(15000);
- Log.w(TAG, "End stress in closing");
- } catch (InterruptedException e) {
- Log.e(TAG, "In stress test", e);
- }
- }
-
- final long now = PROFILE_SAVE_RESTORE ? System.currentTimeMillis() : 0;
- final String fileName =
- mDynamicPredictionDictionary.getDictionaryFileName();
- final File file = new File(mContext.getFilesDir(), fileName);
-
- final DictEncoder dictEncoder = new Ver3DictEncoder(file);
- UserHistoryDictIOUtils.writeDictionary(dictEncoder, this, mBigramList, VERSION3);
-
- // Save the timestamp after we finish writing the binary dictionary.
- Settings.writeLastUserHistoryWriteTime(mPrefs, mLocale);
- if (PROFILE_SAVE_RESTORE) {
- final long diff = System.currentTimeMillis() - now;
- Log.w(TAG, "PROF: Write User HistoryDictionary: " + mLocale + ", " + diff + "ms.");
- }
- }
-
- @Override
- public int getFrequency(final String word1, final String word2) {
- final int freq;
- if (word1 == null) { // unigram
- freq = FREQUENCY_FOR_TYPED;
- final byte prevFc = mBigramList.getBigrams(word1).get(word2);
- } else { // bigram
- final NextWord nw =
- mDynamicPredictionDictionary.getBigramWord(word1, word2);
- if (nw != null) {
- final ForgettingCurveParams fcp = nw.getFcParams();
- final byte prevFc = mBigramList.getBigrams(word1).get(word2);
- final byte fc = fcp.getFc();
- final boolean isValid = fcp.isValid();
- if (prevFc > 0 && prevFc == fc) {
- freq = fc & 0xFF;
- } else if (UserHistoryForgettingCurveUtils.
- needsToSave(fc, isValid, mAddLevel0Bigrams)) {
- freq = fc & 0xFF;
- } else {
- // Delete this entry
- freq = -1;
- }
- } else {
- // Delete this entry
- freq = -1;
- }
- }
- return freq;
- }
- }
-
- @UsedForTesting
- /* package for test */ void forceAddWordForTest(
- final String word1, final String word2, final boolean isValid) {
- mBigramListLock.lock();
- try {
- addToPersonalizationPredictionDictionary(word1, word2, isValid);
- } finally {
- mBigramListLock.unlock();
- }
- }
-
- public void registerUpdateSession(PersonalizationDictionaryUpdateSession session) {
- session.setPredictionDictionary(this);
- mSessions.add(session);
- session.onDictionaryReady();
- }
-
- public void unRegisterUpdateSession(PersonalizationDictionaryUpdateSession session) {
- mSessions.remove(session);
- }
-
- public void clearAndFlushDictionary() {
- // Clear the node structure on memory
- clearDictionary();
- mBigramListLock.lock();
- try {
- // Clear the bigram list on memory
- mBigramList.evictAll();
- } finally {
- mBigramListLock.unlock();
- }
- // Then flush the cleared state of the dictionary on disk.
- flushPendingWrites();
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdateSession.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdateSession.java
index ab3de801c..c616a296c 100644
--- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdateSession.java
+++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdateSession.java
@@ -46,7 +46,7 @@ public abstract class PersonalizationDictionaryUpdateSession {
// TODO: Use a dynamic binary dictionary instead
public WeakReference<PersonalizationDictionary> mDictionary;
- public WeakReference<DynamicPredictionDictionaryBase> mPredictionDictionary;
+ public WeakReference<DecayingExpandableBinaryDictionaryBase> mPredictionDictionary;
public final String mSystemLocale;
public PersonalizationDictionaryUpdateSession(String locale) {
mSystemLocale = locale;
@@ -60,15 +60,16 @@ public abstract class PersonalizationDictionaryUpdateSession {
mDictionary = new WeakReference<PersonalizationDictionary>(dictionary);
}
- public void setPredictionDictionary(DynamicPredictionDictionaryBase dictionary) {
- mPredictionDictionary = new WeakReference<DynamicPredictionDictionaryBase>(dictionary);
+ public void setPredictionDictionary(DecayingExpandableBinaryDictionaryBase dictionary) {
+ mPredictionDictionary =
+ new WeakReference<DecayingExpandableBinaryDictionaryBase>(dictionary);
}
protected PersonalizationDictionary getDictionary() {
return mDictionary == null ? null : mDictionary.get();
}
- protected DynamicPredictionDictionaryBase getPredictionDictionary() {
+ protected DecayingExpandableBinaryDictionaryBase getPredictionDictionary() {
return mPredictionDictionary == null ? null : mPredictionDictionary.get();
}
@@ -81,7 +82,7 @@ public abstract class PersonalizationDictionaryUpdateSession {
}
private void unsetPredictionDictionary() {
- final DynamicPredictionDictionaryBase dictionary = getPredictionDictionary();
+ final DecayingExpandableBinaryDictionaryBase dictionary = getPredictionDictionary();
if (dictionary == null) {
return;
}
@@ -89,7 +90,7 @@ public abstract class PersonalizationDictionaryUpdateSession {
}
public void clearAndFlushPredictionDictionary(Context context) {
- final DynamicPredictionDictionaryBase dictionary = getPredictionDictionary();
+ final DecayingExpandableBinaryDictionaryBase dictionary = getPredictionDictionary();
if (dictionary == null) {
return;
}
@@ -105,7 +106,7 @@ public abstract class PersonalizationDictionaryUpdateSession {
// TODO: Support multi locale to add bigram
public void addBigramToPersonalizationDictionary(String word0, String word1, boolean isValid,
int frequency) {
- final DynamicPredictionDictionaryBase dictionary = getPredictionDictionary();
+ final DecayingExpandableBinaryDictionaryBase dictionary = getPredictionDictionary();
if (dictionary == null) {
return;
}
@@ -116,7 +117,7 @@ public abstract class PersonalizationDictionaryUpdateSession {
// TODO: Support multi locale to add bigram
public void addBigramsToPersonalizationDictionary(
final ArrayList<PersonalizationLanguageModelParam> lmParams) {
- final DynamicPredictionDictionaryBase dictionary = getPredictionDictionary();
+ final DecayingExpandableBinaryDictionaryBase dictionary = getPredictionDictionary();
if (dictionary == null) {
return;
}
diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryHelper.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java
index 7c2f29c86..5f702ee3f 100644
--- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryHelper.java
+++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java
@@ -26,8 +26,8 @@ import android.util.Log;
import java.lang.ref.SoftReference;
import java.util.concurrent.ConcurrentHashMap;
-public class PersonalizationDictionaryHelper {
- private static final String TAG = PersonalizationDictionaryHelper.class.getSimpleName();
+public class PersonalizationHelper {
+ private static final String TAG = PersonalizationHelper.class.getSimpleName();
private static final boolean DEBUG = false;
private static final ConcurrentHashMap<String, SoftReference<UserHistoryPredictionDictionary>>
@@ -52,6 +52,7 @@ public class PersonalizationDictionaryHelper {
if (DEBUG) {
Log.w(TAG, "Use cached UserHistoryPredictionDictionary for " + locale);
}
+ dict.reloadDictionaryIfRequired();
return dict;
}
}
diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationPredictionDictionary.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationPredictionDictionary.java
index a038d0ab2..432954453 100644
--- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationPredictionDictionary.java
+++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationPredictionDictionary.java
@@ -22,16 +22,16 @@ import com.android.inputmethod.latin.ExpandableBinaryDictionary;
import android.content.Context;
import android.content.SharedPreferences;
-public class PersonalizationPredictionDictionary extends DynamicPredictionDictionaryBase {
+public class PersonalizationPredictionDictionary extends DecayingExpandableBinaryDictionaryBase {
private static final String NAME = PersonalizationPredictionDictionary.class.getSimpleName();
/* package */ PersonalizationPredictionDictionary(final Context context, final String locale,
final SharedPreferences sp) {
- super(context, locale, sp, Dictionary.TYPE_PERSONALIZATION_PREDICTION_IN_JAVA);
+ super(context, locale, sp, Dictionary.TYPE_PERSONALIZATION_PREDICTION_IN_JAVA,
+ getDictionaryFileName(locale));
}
- @Override
- protected String getDictionaryFileName() {
- return NAME + "." + getLocale() + ExpandableBinaryDictionary.DICT_FILE_EXTENSION;
+ private static String getDictionaryFileName(final String locale) {
+ return NAME + "." + locale + ExpandableBinaryDictionary.DICT_FILE_EXTENSION;
}
}
diff --git a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryBigramList.java b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryBigramList.java
index 9f289e9ff..55a90ee51 100644
--- a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryBigramList.java
+++ b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryBigramList.java
@@ -45,6 +45,7 @@ public final class UserHistoryDictionaryBigramList {
/**
* Called when the user typed a word.
*/
+ @UsedForTesting
public void addBigram(String word1, String word2) {
addBigram(word1, word2, FORGETTING_CURVE_INITIAL_VALUE);
}
@@ -53,7 +54,7 @@ public final class UserHistoryDictionaryBigramList {
* Called when loaded from the SQL DB.
*/
public void addBigram(String word1, String word2, byte fcValue) {
- if (DynamicPredictionDictionaryBase.DBG_SAVE_RESTORE) {
+ if (DecayingExpandableBinaryDictionaryBase.DBG_SAVE_RESTORE) {
Log.d(TAG, "--- add bigram: " + word1 + ", " + word2 + ", " + fcValue);
}
final HashMap<String, Byte> map;
@@ -73,7 +74,7 @@ public final class UserHistoryDictionaryBigramList {
* Called when inserted to the SQL DB.
*/
public void updateBigram(String word1, String word2, byte fcValue) {
- if (DynamicPredictionDictionaryBase.DBG_SAVE_RESTORE) {
+ if (DecayingExpandableBinaryDictionaryBase.DBG_SAVE_RESTORE) {
Log.d(TAG, "--- update bigram: " + word1 + ", " + word2 + ", " + fcValue);
}
final HashMap<String, Byte> map;
@@ -96,6 +97,10 @@ public final class UserHistoryDictionaryBigramList {
return mBigramMap.isEmpty();
}
+ public boolean containsKey(String word) {
+ return mBigramMap.containsKey(word);
+ }
+
public Set<String> keySet() {
return mBigramMap.keySet();
}
diff --git a/java/src/com/android/inputmethod/latin/personalization/UserHistoryPredictionDictionary.java b/java/src/com/android/inputmethod/latin/personalization/UserHistoryPredictionDictionary.java
index 76e48c744..38e308a4e 100644
--- a/java/src/com/android/inputmethod/latin/personalization/UserHistoryPredictionDictionary.java
+++ b/java/src/com/android/inputmethod/latin/personalization/UserHistoryPredictionDictionary.java
@@ -26,16 +26,15 @@ import android.content.SharedPreferences;
* Locally gathers stats about the words user types and various other signals like auto-correction
* cancellation or manual picks. This allows the keyboard to adapt to the typist over time.
*/
-public class UserHistoryPredictionDictionary extends DynamicPredictionDictionaryBase {
+public class UserHistoryPredictionDictionary extends DecayingExpandableBinaryDictionaryBase {
/* package for tests */ static final String NAME =
UserHistoryPredictionDictionary.class.getSimpleName();
/* package */ UserHistoryPredictionDictionary(final Context context, final String locale,
final SharedPreferences sp) {
- super(context, locale, sp, Dictionary.TYPE_USER_HISTORY);
+ super(context, locale, sp, Dictionary.TYPE_USER_HISTORY, getDictionaryFileName(locale));
}
- @Override
- protected String getDictionaryFileName() {
- return NAME + "." + getLocale() + ExpandableBinaryDictionary.DICT_FILE_EXTENSION;
+ private static String getDictionaryFileName(final String locale) {
+ return NAME + "." + locale + ExpandableBinaryDictionary.DICT_FILE_EXTENSION;
}
}