2013-12-26 11:28:37 +00:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
|
|
|
|
import android.content.Context;
|
|
|
|
import android.text.TextUtils;
|
|
|
|
import android.util.Log;
|
|
|
|
|
|
|
|
import com.android.inputmethod.annotations.UsedForTesting;
|
|
|
|
import com.android.inputmethod.keyboard.ProximityInfo;
|
|
|
|
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
|
|
|
|
import com.android.inputmethod.latin.personalization.PersonalizationDictionary;
|
|
|
|
import com.android.inputmethod.latin.personalization.PersonalizationHelper;
|
|
|
|
import com.android.inputmethod.latin.personalization.UserHistoryDictionary;
|
|
|
|
import com.android.inputmethod.latin.settings.SettingsValues;
|
|
|
|
import com.android.inputmethod.latin.utils.CollectionUtils;
|
2014-02-24 12:05:24 +00:00
|
|
|
import com.android.inputmethod.latin.utils.ExecutorUtils;
|
2014-01-16 01:54:17 +00:00
|
|
|
import com.android.inputmethod.latin.utils.LanguageModelParam;
|
2013-12-26 11:28:37 +00:00
|
|
|
|
2013-12-26 09:26:28 +00:00
|
|
|
import java.io.File;
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.HashMap;
|
2013-12-26 11:28:37 +00:00
|
|
|
import java.util.HashSet;
|
|
|
|
import java.util.Locale;
|
|
|
|
import java.util.Set;
|
|
|
|
import java.util.concurrent.ConcurrentHashMap;
|
2014-01-08 09:59:43 +00:00
|
|
|
import java.util.concurrent.CountDownLatch;
|
2013-12-26 11:28:37 +00:00
|
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
|
|
|
|
// TODO: Consolidate dictionaries in native code.
|
|
|
|
public class DictionaryFacilitatorForSuggest {
|
|
|
|
public static final String TAG = DictionaryFacilitatorForSuggest.class.getSimpleName();
|
|
|
|
|
2014-01-28 07:31:37 +00:00
|
|
|
// HACK: This threshold is being used when adding a capitalized entry in the User History
|
|
|
|
// dictionary.
|
|
|
|
private static final int CAPITALIZED_FORM_MAX_PROBABILITY_FOR_INSERT = 140;
|
|
|
|
|
2013-12-26 11:28:37 +00:00
|
|
|
private final Context mContext;
|
2014-01-15 08:06:34 +00:00
|
|
|
public final Locale mLocale;
|
2013-12-26 11:28:37 +00:00
|
|
|
|
|
|
|
private final ConcurrentHashMap<String, Dictionary> mDictionaries =
|
|
|
|
CollectionUtils.newConcurrentHashMap();
|
2013-12-27 05:49:32 +00:00
|
|
|
private HashSet<String> mDictionarySubsetForDebug = null;
|
2013-12-26 11:28:37 +00:00
|
|
|
|
|
|
|
private Dictionary mMainDictionary;
|
|
|
|
private ContactsBinaryDictionary mContactsDictionary;
|
|
|
|
private UserBinaryDictionary mUserDictionary;
|
|
|
|
private UserHistoryDictionary mUserHistoryDictionary;
|
|
|
|
private PersonalizationDictionary mPersonalizationDictionary;
|
|
|
|
|
2014-01-08 09:59:43 +00:00
|
|
|
private final CountDownLatch mLatchForWaitingLoadingMainDictionary;
|
2013-12-26 11:28:37 +00:00
|
|
|
|
2013-12-27 05:49:32 +00:00
|
|
|
public interface DictionaryInitializationListener {
|
|
|
|
public void onUpdateMainDictionaryAvailability(boolean isMainDictionaryAvailable);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates instance for initialization or when the locale is changed.
|
|
|
|
*
|
|
|
|
* @param context the context
|
|
|
|
* @param locale the locale
|
|
|
|
* @param settingsValues current settings values to control what dictionaries should be used
|
|
|
|
* @param listener the listener
|
|
|
|
* @param oldDictionaryFacilitator the instance having old dictionaries. This is null when the
|
|
|
|
* instance is initially created.
|
|
|
|
*/
|
2013-12-26 11:28:37 +00:00
|
|
|
public DictionaryFacilitatorForSuggest(final Context context, final Locale locale,
|
2013-12-27 05:49:32 +00:00
|
|
|
final SettingsValues settingsValues, final DictionaryInitializationListener listener,
|
|
|
|
final DictionaryFacilitatorForSuggest oldDictionaryFacilitator) {
|
2013-12-26 11:28:37 +00:00
|
|
|
mContext = context;
|
|
|
|
mLocale = locale;
|
2014-01-08 09:59:43 +00:00
|
|
|
mLatchForWaitingLoadingMainDictionary = new CountDownLatch(1);
|
2013-12-27 05:49:32 +00:00
|
|
|
initForDebug(settingsValues);
|
2014-01-08 09:59:43 +00:00
|
|
|
loadMainDict(context, locale, listener);
|
2013-12-26 11:28:37 +00:00
|
|
|
setUserDictionary(new UserBinaryDictionary(context, locale));
|
2013-12-27 05:49:32 +00:00
|
|
|
resetAdditionalDictionaries(oldDictionaryFacilitator, settingsValues);
|
|
|
|
}
|
|
|
|
|
2014-01-08 09:59:43 +00:00
|
|
|
/**
|
|
|
|
* Creates instance for reloading the main dict.
|
|
|
|
*
|
|
|
|
* @param listener the listener
|
|
|
|
* @param oldDictionaryFacilitator the instance having old dictionaries. This must not be null.
|
|
|
|
*/
|
|
|
|
public DictionaryFacilitatorForSuggest(final DictionaryInitializationListener listener,
|
|
|
|
final DictionaryFacilitatorForSuggest oldDictionaryFacilitator) {
|
|
|
|
mContext = oldDictionaryFacilitator.mContext;
|
|
|
|
mLocale = oldDictionaryFacilitator.mLocale;
|
|
|
|
mDictionarySubsetForDebug = oldDictionaryFacilitator.mDictionarySubsetForDebug;
|
|
|
|
mLatchForWaitingLoadingMainDictionary = new CountDownLatch(1);
|
|
|
|
loadMainDict(mContext, mLocale, listener);
|
|
|
|
// Transfer user dictionary.
|
|
|
|
setUserDictionary(oldDictionaryFacilitator.mUserDictionary);
|
|
|
|
oldDictionaryFacilitator.removeDictionary(Dictionary.TYPE_USER);
|
|
|
|
// Transfer contacts dictionary.
|
|
|
|
setContactsDictionary(oldDictionaryFacilitator.mContactsDictionary);
|
|
|
|
oldDictionaryFacilitator.removeDictionary(Dictionary.TYPE_CONTACTS);
|
|
|
|
// Transfer user history dictionary.
|
|
|
|
setUserHistoryDictionary(oldDictionaryFacilitator.mUserHistoryDictionary);
|
|
|
|
oldDictionaryFacilitator.removeDictionary(Dictionary.TYPE_USER_HISTORY);
|
|
|
|
// Transfer personalization dictionary.
|
|
|
|
setPersonalizationDictionary(oldDictionaryFacilitator.mPersonalizationDictionary);
|
|
|
|
oldDictionaryFacilitator.removeDictionary(Dictionary.TYPE_PERSONALIZATION);
|
|
|
|
}
|
|
|
|
|
2013-12-27 05:49:32 +00:00
|
|
|
/**
|
|
|
|
* Creates instance for when the settings values have been changed.
|
|
|
|
*
|
|
|
|
* @param settingsValues the new settings values
|
|
|
|
* @param oldDictionaryFacilitator the instance having old dictionaries. This must not be null.
|
|
|
|
*/
|
|
|
|
//
|
|
|
|
public DictionaryFacilitatorForSuggest(final SettingsValues settingsValues,
|
|
|
|
final DictionaryFacilitatorForSuggest oldDictionaryFacilitator) {
|
|
|
|
mContext = oldDictionaryFacilitator.mContext;
|
|
|
|
mLocale = oldDictionaryFacilitator.mLocale;
|
2014-01-08 09:59:43 +00:00
|
|
|
mLatchForWaitingLoadingMainDictionary = new CountDownLatch(0);
|
2013-12-27 05:49:32 +00:00
|
|
|
initForDebug(settingsValues);
|
|
|
|
// Transfer main dictionary.
|
|
|
|
setMainDictionary(oldDictionaryFacilitator.mMainDictionary);
|
|
|
|
oldDictionaryFacilitator.removeDictionary(Dictionary.TYPE_MAIN);
|
|
|
|
// Transfer user dictionary.
|
|
|
|
setUserDictionary(oldDictionaryFacilitator.mUserDictionary);
|
|
|
|
oldDictionaryFacilitator.removeDictionary(Dictionary.TYPE_USER);
|
|
|
|
// Transfer or create additional dictionaries depending on the settings values.
|
|
|
|
resetAdditionalDictionaries(oldDictionaryFacilitator, settingsValues);
|
2013-12-26 11:28:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@UsedForTesting
|
2013-12-26 09:26:28 +00:00
|
|
|
public DictionaryFacilitatorForSuggest(final Context context, final Locale locale,
|
|
|
|
final ArrayList<String> dictionaryTypes, final HashMap<String, File> dictionaryFiles) {
|
2013-12-26 11:28:37 +00:00
|
|
|
mContext = context;
|
|
|
|
mLocale = locale;
|
2014-01-08 09:59:43 +00:00
|
|
|
mLatchForWaitingLoadingMainDictionary = new CountDownLatch(0);
|
2013-12-26 09:26:28 +00:00
|
|
|
for (final String dictType : dictionaryTypes) {
|
|
|
|
if (dictType.equals(Dictionary.TYPE_MAIN)) {
|
|
|
|
final DictionaryCollection mainDictionary =
|
|
|
|
DictionaryFactory.createMainDictionaryFromManager(context, locale);
|
|
|
|
setMainDictionary(mainDictionary);
|
|
|
|
} else if (dictType.equals(Dictionary.TYPE_USER_HISTORY)) {
|
|
|
|
final UserHistoryDictionary userHistoryDictionary =
|
|
|
|
PersonalizationHelper.getUserHistoryDictionary(context, locale);
|
|
|
|
// Staring with an empty user history dictionary for testing.
|
|
|
|
// Testing program may populate this dictionary before actual testing.
|
|
|
|
userHistoryDictionary.reloadDictionaryIfRequired();
|
|
|
|
userHistoryDictionary.waitAllTasksForTests();
|
|
|
|
setUserHistoryDictionary(userHistoryDictionary);
|
|
|
|
} else if (dictType.equals(Dictionary.TYPE_PERSONALIZATION)) {
|
|
|
|
final PersonalizationDictionary personalizationDictionary =
|
|
|
|
PersonalizationHelper.getPersonalizationDictionary(context, locale);
|
|
|
|
// Staring with an empty personalization dictionary for testing.
|
|
|
|
// Testing program may populate this dictionary before actual testing.
|
|
|
|
personalizationDictionary.reloadDictionaryIfRequired();
|
|
|
|
personalizationDictionary.waitAllTasksForTests();
|
|
|
|
setPersonalizationDictionary(personalizationDictionary);
|
|
|
|
} else if (dictType.equals(Dictionary.TYPE_USER)) {
|
|
|
|
final File file = dictionaryFiles.get(dictType);
|
|
|
|
final UserBinaryDictionary userDictionary = new UserBinaryDictionary(
|
|
|
|
context, locale, file);
|
|
|
|
userDictionary.reloadDictionaryIfRequired();
|
|
|
|
userDictionary.waitAllTasksForTests();
|
|
|
|
setUserDictionary(userDictionary);
|
|
|
|
} else if (dictType.equals(Dictionary.TYPE_CONTACTS)) {
|
|
|
|
final File file = dictionaryFiles.get(dictType);
|
|
|
|
final ContactsBinaryDictionary contactsDictionary = new ContactsBinaryDictionary(
|
|
|
|
context, locale, file);
|
|
|
|
contactsDictionary.reloadDictionaryIfRequired();
|
|
|
|
contactsDictionary.waitAllTasksForTests();
|
|
|
|
setContactsDictionary(contactsDictionary);
|
|
|
|
} else {
|
|
|
|
throw new RuntimeException("Unknown dictionary type: " + dictType);
|
|
|
|
}
|
|
|
|
}
|
2013-12-27 05:49:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// initialize a debug flag for the personalization
|
|
|
|
private void initForDebug(final SettingsValues settingsValues) {
|
|
|
|
if (settingsValues.mUseOnlyPersonalizationDictionaryForDebug) {
|
|
|
|
mDictionarySubsetForDebug = new HashSet<String>();
|
|
|
|
mDictionarySubsetForDebug.add(Dictionary.TYPE_PERSONALIZATION);
|
|
|
|
}
|
2013-12-26 11:28:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void close() {
|
|
|
|
final HashSet<Dictionary> dictionaries = CollectionUtils.newHashSet();
|
|
|
|
dictionaries.addAll(mDictionaries.values());
|
|
|
|
for (final Dictionary dictionary : dictionaries) {
|
|
|
|
dictionary.close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-08 09:59:43 +00:00
|
|
|
private void loadMainDict(final Context context, final Locale locale,
|
2013-12-27 05:49:32 +00:00
|
|
|
final DictionaryInitializationListener listener) {
|
2013-12-26 11:28:37 +00:00
|
|
|
mMainDictionary = null;
|
|
|
|
if (listener != null) {
|
|
|
|
listener.onUpdateMainDictionaryAvailability(hasMainDictionary());
|
|
|
|
}
|
2014-02-24 12:05:24 +00:00
|
|
|
ExecutorUtils.getExecutor("InitializeBinaryDictionary").execute(new Runnable() {
|
2013-12-26 11:28:37 +00:00
|
|
|
public void run() {
|
|
|
|
final DictionaryCollection newMainDict =
|
|
|
|
DictionaryFactory.createMainDictionaryFromManager(context, locale);
|
|
|
|
setMainDictionary(newMainDict);
|
|
|
|
if (listener != null) {
|
|
|
|
listener.onUpdateMainDictionaryAvailability(hasMainDictionary());
|
|
|
|
}
|
2014-01-08 09:59:43 +00:00
|
|
|
mLatchForWaitingLoadingMainDictionary.countDown();
|
2013-12-26 11:28:37 +00:00
|
|
|
}
|
2014-02-24 12:05:24 +00:00
|
|
|
});
|
2013-12-26 11:28:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// The main dictionary could have been loaded asynchronously. Don't cache the return value
|
|
|
|
// of this method.
|
|
|
|
public boolean hasMainDictionary() {
|
|
|
|
return null != mMainDictionary && mMainDictionary.isInitialized();
|
|
|
|
}
|
|
|
|
|
2014-01-16 01:54:17 +00:00
|
|
|
public boolean hasPersonalizationDictionary() {
|
|
|
|
return null != mPersonalizationDictionary;
|
|
|
|
}
|
|
|
|
|
2014-02-24 05:47:54 +00:00
|
|
|
public void flushPersonalizationDictionary() {
|
|
|
|
if (hasPersonalizationDictionary()) {
|
|
|
|
mPersonalizationDictionary.flush();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-08 09:59:43 +00:00
|
|
|
public void waitForLoadingMainDictionary(final long timeout, final TimeUnit unit)
|
|
|
|
throws InterruptedException {
|
|
|
|
mLatchForWaitingLoadingMainDictionary.await(timeout, unit);
|
2013-12-26 11:28:37 +00:00
|
|
|
}
|
|
|
|
|
2014-02-20 13:20:43 +00:00
|
|
|
@UsedForTesting
|
|
|
|
public void waitForLoadingDictionariesForTesting(final long timeout, final TimeUnit unit)
|
|
|
|
throws InterruptedException {
|
|
|
|
waitForLoadingMainDictionary(timeout, unit);
|
|
|
|
if (mContactsDictionary != null) {
|
|
|
|
mContactsDictionary.waitAllTasksForTests();
|
|
|
|
}
|
|
|
|
if (mUserDictionary != null) {
|
|
|
|
mUserDictionary.waitAllTasksForTests();
|
|
|
|
}
|
|
|
|
if (mUserHistoryDictionary != null) {
|
|
|
|
mUserHistoryDictionary.waitAllTasksForTests();
|
|
|
|
}
|
|
|
|
if (mPersonalizationDictionary != null) {
|
|
|
|
mPersonalizationDictionary.waitAllTasksForTests();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-26 11:28:37 +00:00
|
|
|
private void setMainDictionary(final Dictionary mainDictionary) {
|
|
|
|
mMainDictionary = mainDictionary;
|
|
|
|
addOrReplaceDictionary(Dictionary.TYPE_MAIN, mainDictionary);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets an optional user dictionary resource to be loaded. The user dictionary is consulted
|
|
|
|
* before the main dictionary, if set. This refers to the system-managed user dictionary.
|
|
|
|
*/
|
2013-12-26 09:26:28 +00:00
|
|
|
private void setUserDictionary(final UserBinaryDictionary userDictionary) {
|
2013-12-26 11:28:37 +00:00
|
|
|
mUserDictionary = userDictionary;
|
|
|
|
addOrReplaceDictionary(Dictionary.TYPE_USER, userDictionary);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets an optional contacts dictionary resource to be loaded. It is also possible to remove
|
|
|
|
* the contacts dictionary by passing null to this method. In this case no contacts dictionary
|
|
|
|
* won't be used.
|
|
|
|
*/
|
2013-12-26 09:26:28 +00:00
|
|
|
private void setContactsDictionary(final ContactsBinaryDictionary contactsDictionary) {
|
2013-12-26 11:28:37 +00:00
|
|
|
mContactsDictionary = contactsDictionary;
|
|
|
|
addOrReplaceDictionary(Dictionary.TYPE_CONTACTS, contactsDictionary);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void setUserHistoryDictionary(final UserHistoryDictionary userHistoryDictionary) {
|
|
|
|
mUserHistoryDictionary = userHistoryDictionary;
|
|
|
|
addOrReplaceDictionary(Dictionary.TYPE_USER_HISTORY, userHistoryDictionary);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void setPersonalizationDictionary(
|
|
|
|
final PersonalizationDictionary personalizationDictionary) {
|
|
|
|
mPersonalizationDictionary = personalizationDictionary;
|
|
|
|
addOrReplaceDictionary(Dictionary.TYPE_PERSONALIZATION, personalizationDictionary);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2013-12-27 05:49:32 +00:00
|
|
|
* Reset dictionaries that can be turned off according to the user settings.
|
2013-12-26 11:28:37 +00:00
|
|
|
*
|
|
|
|
* @param oldDictionaryFacilitator the instance having old dictionaries
|
|
|
|
* @param settingsValues current SettingsValues
|
|
|
|
*/
|
2013-12-27 05:49:32 +00:00
|
|
|
private void resetAdditionalDictionaries(
|
2013-12-26 11:28:37 +00:00
|
|
|
final DictionaryFacilitatorForSuggest oldDictionaryFacilitator,
|
|
|
|
final SettingsValues settingsValues) {
|
|
|
|
// Contacts dictionary
|
|
|
|
resetContactsDictionary(null != oldDictionaryFacilitator ?
|
|
|
|
oldDictionaryFacilitator.mContactsDictionary : null, settingsValues);
|
|
|
|
// User history dictionary & Personalization dictionary
|
|
|
|
resetPersonalizedDictionaries(oldDictionaryFacilitator, settingsValues);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the user history dictionary and personalization dictionary according to the user
|
|
|
|
* settings.
|
|
|
|
*
|
|
|
|
* @param oldDictionaryFacilitator the instance that has been used
|
|
|
|
* @param settingsValues current settingsValues
|
|
|
|
*/
|
|
|
|
// TODO: Consolidate resetPersonalizedDictionaries() and resetContactsDictionary(). Call up the
|
|
|
|
// new method for each dictionary.
|
|
|
|
private void resetPersonalizedDictionaries(
|
|
|
|
final DictionaryFacilitatorForSuggest oldDictionaryFacilitator,
|
|
|
|
final SettingsValues settingsValues) {
|
|
|
|
final boolean shouldSetDictionaries = settingsValues.mUsePersonalizedDicts;
|
|
|
|
|
|
|
|
final UserHistoryDictionary oldUserHistoryDictionary = (null == oldDictionaryFacilitator) ?
|
|
|
|
null : oldDictionaryFacilitator.mUserHistoryDictionary;
|
|
|
|
final PersonalizationDictionary oldPersonalizationDictionary =
|
|
|
|
(null == oldDictionaryFacilitator) ? null :
|
|
|
|
oldDictionaryFacilitator.mPersonalizationDictionary;
|
|
|
|
final UserHistoryDictionary userHistoryDictionaryToUse;
|
|
|
|
final PersonalizationDictionary personalizationDictionaryToUse;
|
|
|
|
if (!shouldSetDictionaries) {
|
|
|
|
userHistoryDictionaryToUse = null;
|
|
|
|
personalizationDictionaryToUse = null;
|
|
|
|
} else {
|
|
|
|
if (null != oldUserHistoryDictionary
|
|
|
|
&& oldUserHistoryDictionary.mLocale.equals(mLocale)) {
|
|
|
|
userHistoryDictionaryToUse = oldUserHistoryDictionary;
|
|
|
|
} else {
|
|
|
|
userHistoryDictionaryToUse =
|
|
|
|
PersonalizationHelper.getUserHistoryDictionary(mContext, mLocale);
|
|
|
|
}
|
|
|
|
if (null != oldPersonalizationDictionary
|
|
|
|
&& oldPersonalizationDictionary.mLocale.equals(mLocale)) {
|
|
|
|
personalizationDictionaryToUse = oldPersonalizationDictionary;
|
|
|
|
} else {
|
|
|
|
personalizationDictionaryToUse =
|
|
|
|
PersonalizationHelper.getPersonalizationDictionary(mContext, mLocale);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
setUserHistoryDictionary(userHistoryDictionaryToUse);
|
|
|
|
setPersonalizationDictionary(personalizationDictionaryToUse);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the contacts dictionary according to the user settings.
|
|
|
|
*
|
|
|
|
* This method takes an optional contacts dictionary to use when the locale hasn't changed
|
|
|
|
* since the contacts dictionary can be opened or closed as necessary depending on the settings.
|
|
|
|
*
|
|
|
|
* @param oldContactsDictionary an optional dictionary to use, or null
|
|
|
|
* @param settingsValues current settingsValues
|
|
|
|
*/
|
|
|
|
private void resetContactsDictionary(final ContactsBinaryDictionary oldContactsDictionary,
|
|
|
|
final SettingsValues settingsValues) {
|
|
|
|
final boolean shouldSetDictionary = settingsValues.mUseContactsDict;
|
|
|
|
final ContactsBinaryDictionary dictionaryToUse;
|
|
|
|
if (!shouldSetDictionary) {
|
|
|
|
// Make sure the dictionary is closed. If it is already closed, this is a no-op,
|
|
|
|
// so it's safe to call it anyways.
|
|
|
|
if (null != oldContactsDictionary) oldContactsDictionary.close();
|
|
|
|
dictionaryToUse = null;
|
|
|
|
} else {
|
|
|
|
if (null != oldContactsDictionary) {
|
|
|
|
if (!oldContactsDictionary.mLocale.equals(mLocale)) {
|
|
|
|
// If the locale has changed then recreate the contacts dictionary. This
|
|
|
|
// allows locale dependent rules for handling bigram name predictions.
|
|
|
|
oldContactsDictionary.close();
|
|
|
|
dictionaryToUse = new ContactsBinaryDictionary(mContext, mLocale);
|
|
|
|
} else {
|
|
|
|
// Make sure the old contacts dictionary is opened. If it is already open,
|
|
|
|
// this is a no-op, so it's safe to call it anyways.
|
|
|
|
oldContactsDictionary.reopen(mContext);
|
|
|
|
dictionaryToUse = oldContactsDictionary;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
dictionaryToUse = new ContactsBinaryDictionary(mContext, mLocale);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
setContactsDictionary(dictionaryToUse);
|
|
|
|
}
|
|
|
|
|
|
|
|
public boolean isUserDictionaryEnabled() {
|
|
|
|
if (mUserDictionary == null) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return mUserDictionary.mEnabled;
|
|
|
|
}
|
|
|
|
|
|
|
|
public void addWordToUserDictionary(String word) {
|
|
|
|
if (mUserDictionary == null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
mUserDictionary.addWordToUserDictionary(word);
|
|
|
|
}
|
|
|
|
|
2014-01-14 06:23:58 +00:00
|
|
|
public void addToUserHistory(final String suggestion, final boolean wasAutoCapitalized,
|
|
|
|
final String previousWord, final int timeStampInSeconds) {
|
2013-12-26 11:28:37 +00:00
|
|
|
if (mUserHistoryDictionary == null) {
|
2013-12-26 13:24:59 +00:00
|
|
|
return;
|
2013-12-26 11:28:37 +00:00
|
|
|
}
|
|
|
|
final int maxFreq = getMaxFrequency(suggestion);
|
|
|
|
if (maxFreq == 0) {
|
2013-12-26 13:24:59 +00:00
|
|
|
return;
|
2013-12-26 11:28:37 +00:00
|
|
|
}
|
2014-01-28 07:31:37 +00:00
|
|
|
final String suggestionLowerCase = suggestion.toLowerCase(mLocale);
|
|
|
|
final String secondWord;
|
|
|
|
if (wasAutoCapitalized) {
|
|
|
|
secondWord = suggestionLowerCase;
|
|
|
|
} else {
|
|
|
|
// HACK: We'd like to avoid adding the capitalized form of common words to the User
|
|
|
|
// History dictionary in order to avoid suggesting them until the dictionary
|
|
|
|
// consolidation is done.
|
|
|
|
// TODO: Remove this hack when ready.
|
|
|
|
final int lowerCasefreqInMainDict = mMainDictionary != null ?
|
|
|
|
mMainDictionary.getFrequency(suggestionLowerCase) :
|
|
|
|
Dictionary.NOT_A_PROBABILITY;
|
|
|
|
if (maxFreq < lowerCasefreqInMainDict
|
|
|
|
&& lowerCasefreqInMainDict >= CAPITALIZED_FORM_MAX_PROBABILITY_FOR_INSERT) {
|
|
|
|
// Use lower cased word as the word can be a distracter of the popular word.
|
|
|
|
secondWord = suggestionLowerCase;
|
|
|
|
} else {
|
|
|
|
secondWord = suggestion;
|
|
|
|
}
|
|
|
|
}
|
2014-01-14 06:23:58 +00:00
|
|
|
// We demote unrecognized words (frequency < 0, below) by specifying them as "invalid".
|
|
|
|
// We don't add words with 0-frequency (assuming they would be profanity etc.).
|
2013-12-26 11:28:37 +00:00
|
|
|
final boolean isValid = maxFreq > 0;
|
2014-01-14 06:23:58 +00:00
|
|
|
mUserHistoryDictionary.addToDictionary(
|
|
|
|
previousWord, secondWord, isValid, timeStampInSeconds);
|
2013-12-26 11:28:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public void cancelAddingUserHistory(final String previousWord, final String committedWord) {
|
|
|
|
if (mUserHistoryDictionary != null) {
|
|
|
|
mUserHistoryDictionary.cancelAddingUserHistory(previousWord, committedWord);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Revise the way to fusion suggestion results.
|
|
|
|
public void getSuggestions(final WordComposer composer,
|
|
|
|
final String prevWord, final ProximityInfo proximityInfo,
|
|
|
|
final boolean blockOffensiveWords, final int[] additionalFeaturesOptions,
|
2014-02-06 06:51:04 +00:00
|
|
|
final int sessionId, final Set<SuggestedWordInfo> suggestionSet,
|
|
|
|
final ArrayList<SuggestedWordInfo> rawSuggestions) {
|
2013-12-26 11:28:37 +00:00
|
|
|
for (final String key : mDictionaries.keySet()) {
|
|
|
|
final Dictionary dictionary = mDictionaries.get(key);
|
2014-01-10 12:42:08 +00:00
|
|
|
if (null == dictionary) continue;
|
2014-02-06 06:51:04 +00:00
|
|
|
final ArrayList<SuggestedWordInfo> dictionarySuggestions =
|
|
|
|
dictionary.getSuggestionsWithSessionId(composer, prevWord, proximityInfo,
|
|
|
|
blockOffensiveWords, additionalFeaturesOptions, sessionId);
|
|
|
|
if (null == dictionarySuggestions) continue;
|
|
|
|
suggestionSet.addAll(dictionarySuggestions);
|
|
|
|
if (null != rawSuggestions) {
|
|
|
|
rawSuggestions.addAll(dictionarySuggestions);
|
|
|
|
}
|
2013-12-26 11:28:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-12-26 11:47:46 +00:00
|
|
|
public boolean isValidMainDictWord(final String word) {
|
|
|
|
if (TextUtils.isEmpty(word) || !hasMainDictionary()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return mMainDictionary.isValidWord(word);
|
|
|
|
}
|
|
|
|
|
2013-12-26 11:28:37 +00:00
|
|
|
public boolean isValidWord(final String word, final boolean ignoreCase) {
|
|
|
|
if (TextUtils.isEmpty(word)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
final String lowerCasedWord = word.toLowerCase(mLocale);
|
|
|
|
for (final String key : mDictionaries.keySet()) {
|
|
|
|
final Dictionary dictionary = mDictionaries.get(key);
|
|
|
|
// It's unclear how realistically 'dictionary' can be null, but the monkey is somehow
|
|
|
|
// managing to get null in here. Presumably the language is changing to a language with
|
|
|
|
// no main dictionary and the monkey manages to type a whole word before the thread
|
|
|
|
// that reads the dictionary is started or something?
|
|
|
|
// Ideally the passed map would come out of a {@link java.util.concurrent.Future} and
|
|
|
|
// would be immutable once it's finished initializing, but concretely a null test is
|
|
|
|
// probably good enough for the time being.
|
|
|
|
if (null == dictionary) continue;
|
|
|
|
if (dictionary.isValidWord(word)
|
|
|
|
|| (ignoreCase && dictionary.isValidWord(lowerCasedWord))) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
private int getMaxFrequency(final String word) {
|
|
|
|
if (TextUtils.isEmpty(word)) {
|
|
|
|
return Dictionary.NOT_A_PROBABILITY;
|
|
|
|
}
|
|
|
|
int maxFreq = -1;
|
|
|
|
for (final String key : mDictionaries.keySet()) {
|
|
|
|
final Dictionary dictionary = mDictionaries.get(key);
|
|
|
|
if (null == dictionary) continue;
|
|
|
|
final int tempFreq = dictionary.getFrequency(word);
|
|
|
|
if (tempFreq >= maxFreq) {
|
|
|
|
maxFreq = tempFreq;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return maxFreq;
|
|
|
|
}
|
|
|
|
|
2013-12-27 05:49:32 +00:00
|
|
|
private void removeDictionary(final String key) {
|
|
|
|
mDictionaries.remove(key);
|
|
|
|
}
|
|
|
|
|
2013-12-26 11:28:37 +00:00
|
|
|
private void addOrReplaceDictionary(final String key, final Dictionary dict) {
|
2013-12-27 05:49:32 +00:00
|
|
|
if (mDictionarySubsetForDebug != null && !mDictionarySubsetForDebug.contains(key)) {
|
2013-12-26 11:28:37 +00:00
|
|
|
Log.w(TAG, "Ignore add " + key + " dictionary for debug.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
final Dictionary oldDict;
|
|
|
|
if (dict == null) {
|
|
|
|
oldDict = mDictionaries.remove(key);
|
|
|
|
} else {
|
|
|
|
oldDict = mDictionaries.put(key, dict);
|
|
|
|
}
|
|
|
|
if (oldDict != null && dict != oldDict) {
|
|
|
|
oldDict.close();
|
|
|
|
}
|
|
|
|
}
|
2014-01-16 01:54:17 +00:00
|
|
|
|
2014-01-29 03:00:33 +00:00
|
|
|
public void clearUserHistoryDictionary() {
|
|
|
|
if (mUserHistoryDictionary == null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
mUserHistoryDictionary.clearAndFlushDictionary();
|
|
|
|
}
|
|
|
|
|
2014-01-16 01:54:17 +00:00
|
|
|
// This method gets called only when the IME receives a notification to remove the
|
|
|
|
// personalization dictionary.
|
|
|
|
public void clearPersonalizationDictionary() {
|
|
|
|
if (!hasPersonalizationDictionary()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
mPersonalizationDictionary.clearAndFlushDictionary();
|
|
|
|
}
|
|
|
|
|
|
|
|
public void addMultipleDictionaryEntriesToPersonalizationDictionary(
|
|
|
|
final ArrayList<LanguageModelParam> languageModelParams,
|
|
|
|
final ExpandableBinaryDictionary.AddMultipleDictionaryEntriesCallback callback) {
|
|
|
|
if (!hasPersonalizationDictionary()) {
|
|
|
|
if (callback != null) {
|
|
|
|
callback.onFinished();
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
mPersonalizationDictionary.addMultipleDictionaryEntriesToDictionary(languageModelParams,
|
|
|
|
callback);
|
|
|
|
}
|
2014-02-04 12:29:09 +00:00
|
|
|
|
|
|
|
public void dumpDictionaryForDebug(final String dictName) {
|
|
|
|
final ExpandableBinaryDictionary dictToDump;
|
|
|
|
if (dictName.equals(Dictionary.TYPE_CONTACTS)) {
|
|
|
|
dictToDump = mContactsDictionary;
|
|
|
|
} else if (dictName.equals(Dictionary.TYPE_USER)) {
|
|
|
|
dictToDump = mUserDictionary;
|
|
|
|
} else if (dictName.equals(Dictionary.TYPE_USER_HISTORY)) {
|
|
|
|
dictToDump = mUserHistoryDictionary;
|
|
|
|
} else if (dictName.equals(Dictionary.TYPE_PERSONALIZATION)) {
|
|
|
|
dictToDump = mPersonalizationDictionary;
|
|
|
|
} else {
|
|
|
|
dictToDump = null;
|
|
|
|
}
|
|
|
|
if (dictToDump == null) {
|
|
|
|
Log.e(TAG, "Cannot dump " + dictName + ". "
|
|
|
|
+ "The dictionary is not being used for suggestion or cannot be dumped.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
dictToDump.dumpAllWordsForDebug();
|
|
|
|
}
|
2013-12-26 11:28:37 +00:00
|
|
|
}
|