2009-03-13 22:11:42 +00:00
|
|
|
/*
|
2010-03-26 22:07:10 +00:00
|
|
|
* Copyright (C) 2008 The Android Open Source Project
|
2011-01-07 06:01:51 +00:00
|
|
|
*
|
2009-03-13 22:11:42 +00:00
|
|
|
* 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
|
2011-01-07 06:01:51 +00:00
|
|
|
*
|
2009-03-13 22:11:42 +00:00
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
2011-01-07 06:01:51 +00:00
|
|
|
*
|
2009-03-13 22:11:42 +00:00
|
|
|
* 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.AutoText;
|
|
|
|
import android.text.TextUtils;
|
|
|
|
import android.util.Log;
|
|
|
|
import android.view.View;
|
|
|
|
|
2011-02-08 08:12:13 +00:00
|
|
|
import java.io.File;
|
2010-11-29 08:57:48 +00:00
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.Arrays;
|
2011-03-04 00:02:28 +00:00
|
|
|
import java.util.HashMap;
|
|
|
|
import java.util.HashSet;
|
|
|
|
import java.util.Map;
|
|
|
|
import java.util.Set;
|
2010-11-29 08:57:48 +00:00
|
|
|
|
2009-03-13 22:11:42 +00:00
|
|
|
/**
|
2011-01-07 06:01:51 +00:00
|
|
|
* This class loads a dictionary and provides a list of suggestions for a given sequence of
|
2009-03-13 22:11:42 +00:00
|
|
|
* characters. This includes corrections and completions.
|
|
|
|
*/
|
|
|
|
public class Suggest implements Dictionary.WordCallback {
|
|
|
|
|
2011-01-18 08:22:01 +00:00
|
|
|
public static final String TAG = Suggest.class.getSimpleName();
|
2010-12-08 07:04:16 +00:00
|
|
|
|
2010-08-20 05:35:02 +00:00
|
|
|
public static final int APPROX_MAX_WORD_LENGTH = 32;
|
|
|
|
|
2009-03-13 22:11:42 +00:00
|
|
|
public static final int CORRECTION_NONE = 0;
|
|
|
|
public static final int CORRECTION_BASIC = 1;
|
|
|
|
public static final int CORRECTION_FULL = 2;
|
2010-08-20 05:35:02 +00:00
|
|
|
public static final int CORRECTION_FULL_BIGRAM = 3;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Words that appear in both bigram and unigram data gets multiplier ranging from
|
|
|
|
* BIGRAM_MULTIPLIER_MIN to BIGRAM_MULTIPLIER_MAX depending on the frequency score from
|
|
|
|
* bigram data.
|
|
|
|
*/
|
|
|
|
public static final double BIGRAM_MULTIPLIER_MIN = 1.2;
|
|
|
|
public static final double BIGRAM_MULTIPLIER_MAX = 1.5;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Maximum possible bigram frequency. Will depend on how many bits are being used in data
|
|
|
|
* structure. Maximum bigram freqeuncy will get the BIGRAM_MULTIPLIER_MAX as the multiplier.
|
|
|
|
*/
|
|
|
|
public static final int MAXIMUM_BIGRAM_FREQUENCY = 127;
|
|
|
|
|
|
|
|
public static final int DIC_USER_TYPED = 0;
|
|
|
|
public static final int DIC_MAIN = 1;
|
|
|
|
public static final int DIC_USER = 2;
|
|
|
|
public static final int DIC_AUTO = 3;
|
|
|
|
public static final int DIC_CONTACTS = 4;
|
|
|
|
// If you add a type of dictionary, increment DIC_TYPE_LAST_ID
|
|
|
|
public static final int DIC_TYPE_LAST_ID = 4;
|
2009-07-17 19:51:45 +00:00
|
|
|
|
2010-03-17 22:36:23 +00:00
|
|
|
static final int LARGE_DICTIONARY_THRESHOLD = 200 * 1000;
|
2009-12-18 21:39:18 +00:00
|
|
|
|
2011-01-31 05:49:33 +00:00
|
|
|
private static final boolean DBG = LatinImeLogger.sDBG;
|
2011-01-18 08:22:01 +00:00
|
|
|
|
2011-03-02 06:40:08 +00:00
|
|
|
private AutoCorrection mAutoCorrection;
|
|
|
|
|
2009-12-18 21:39:18 +00:00
|
|
|
private BinaryDictionary mMainDict;
|
2011-03-04 00:02:28 +00:00
|
|
|
private static final String DICT_KEY_MAIN = "main";
|
|
|
|
private static final String DICT_KEY_CONTACTS = "contacts";
|
|
|
|
private static final String DICT_KEY_AUTO = "auto";
|
|
|
|
private static final String DICT_KEY_USER = "user";
|
|
|
|
private static final String DICT_KEY_USER_BIGRAM = "user_bigram";
|
|
|
|
private final Map<String, Dictionary> mUnigramDictionaries = new HashMap<String, Dictionary>();
|
|
|
|
private final Map<String, Dictionary> mBigramDictionaries = new HashMap<String, Dictionary>();
|
2010-08-20 05:35:02 +00:00
|
|
|
|
2009-03-13 22:11:42 +00:00
|
|
|
private int mPrefMaxSuggestions = 12;
|
2009-07-17 19:51:45 +00:00
|
|
|
|
2010-08-20 05:35:02 +00:00
|
|
|
private static final int PREF_MAX_BIGRAMS = 60;
|
|
|
|
|
2011-02-07 11:40:34 +00:00
|
|
|
private boolean mQuickFixesEnabled;
|
2010-02-18 19:09:43 +00:00
|
|
|
|
2010-12-11 08:06:24 +00:00
|
|
|
private double mAutoCorrectionThreshold;
|
2009-03-13 22:11:42 +00:00
|
|
|
private int[] mPriorities = new int[mPrefMaxSuggestions];
|
2010-08-20 05:35:02 +00:00
|
|
|
private int[] mBigramPriorities = new int[PREF_MAX_BIGRAMS];
|
|
|
|
|
2009-10-28 01:08:19 +00:00
|
|
|
private ArrayList<CharSequence> mSuggestions = new ArrayList<CharSequence>();
|
2010-08-20 05:35:02 +00:00
|
|
|
ArrayList<CharSequence> mBigramSuggestions = new ArrayList<CharSequence>();
|
2009-10-28 01:08:19 +00:00
|
|
|
private ArrayList<CharSequence> mStringPool = new ArrayList<CharSequence>();
|
2009-03-13 22:11:42 +00:00
|
|
|
private String mLowerOriginalWord;
|
2010-09-27 15:32:35 +00:00
|
|
|
|
|
|
|
// TODO: Remove these member variables by passing more context to addWord() callback method
|
|
|
|
private boolean mIsFirstCharCapitalized;
|
|
|
|
private boolean mIsAllUpperCase;
|
2009-03-13 22:11:42 +00:00
|
|
|
|
|
|
|
private int mCorrectionMode = CORRECTION_BASIC;
|
|
|
|
|
2011-01-04 15:39:41 +00:00
|
|
|
public Suggest(Context context, int dictionaryResId) {
|
2011-03-04 00:02:28 +00:00
|
|
|
init(BinaryDictionary.initDictionary(context, dictionaryResId, DIC_MAIN));
|
2010-08-20 05:35:02 +00:00
|
|
|
}
|
|
|
|
|
2011-03-02 06:40:08 +00:00
|
|
|
/* package for test */ Suggest(File dictionary, long startOffset, long length) {
|
2011-03-04 00:02:28 +00:00
|
|
|
init(BinaryDictionary.initDictionary(dictionary, startOffset, length, DIC_MAIN));
|
2011-03-02 06:40:08 +00:00
|
|
|
}
|
|
|
|
|
2011-03-04 00:02:28 +00:00
|
|
|
private void init(BinaryDictionary mainDict) {
|
|
|
|
if (mainDict != null) {
|
|
|
|
mMainDict = mainDict;
|
|
|
|
mUnigramDictionaries.put(DICT_KEY_MAIN, mainDict);
|
|
|
|
mBigramDictionaries.put(DICT_KEY_MAIN, mainDict);
|
|
|
|
}
|
2011-03-02 06:40:08 +00:00
|
|
|
mAutoCorrection = new AutoCorrection();
|
2011-02-08 08:12:13 +00:00
|
|
|
initPool();
|
|
|
|
}
|
|
|
|
|
2010-08-20 05:35:02 +00:00
|
|
|
private void initPool() {
|
2009-03-13 22:11:42 +00:00
|
|
|
for (int i = 0; i < mPrefMaxSuggestions; i++) {
|
2010-08-20 05:35:02 +00:00
|
|
|
StringBuilder sb = new StringBuilder(getApproxMaxWordLength());
|
2009-03-13 22:11:42 +00:00
|
|
|
mStringPool.add(sb);
|
|
|
|
}
|
|
|
|
}
|
2009-07-17 19:51:45 +00:00
|
|
|
|
2011-02-07 11:40:34 +00:00
|
|
|
public void setQuickFixesEnabled(boolean enabled) {
|
|
|
|
mQuickFixesEnabled = enabled;
|
2010-02-18 19:09:43 +00:00
|
|
|
}
|
|
|
|
|
2009-03-13 22:11:42 +00:00
|
|
|
public int getCorrectionMode() {
|
|
|
|
return mCorrectionMode;
|
|
|
|
}
|
2009-07-17 19:51:45 +00:00
|
|
|
|
2009-03-13 22:11:42 +00:00
|
|
|
public void setCorrectionMode(int mode) {
|
|
|
|
mCorrectionMode = mode;
|
|
|
|
}
|
|
|
|
|
2009-12-18 21:39:18 +00:00
|
|
|
public boolean hasMainDictionary() {
|
2011-01-17 06:13:45 +00:00
|
|
|
return mMainDict != null && mMainDict.getSize() > LARGE_DICTIONARY_THRESHOLD;
|
2009-12-18 21:39:18 +00:00
|
|
|
}
|
|
|
|
|
2010-08-20 05:35:02 +00:00
|
|
|
public int getApproxMaxWordLength() {
|
|
|
|
return APPROX_MAX_WORD_LENGTH;
|
|
|
|
}
|
|
|
|
|
2009-03-13 22:11:42 +00:00
|
|
|
/**
|
|
|
|
* Sets an optional user dictionary resource to be loaded. The user dictionary is consulted
|
|
|
|
* before the main dictionary, if set.
|
|
|
|
*/
|
|
|
|
public void setUserDictionary(Dictionary userDictionary) {
|
2011-03-04 00:02:28 +00:00
|
|
|
if (userDictionary != null)
|
|
|
|
mUnigramDictionaries.put(DICT_KEY_USER, userDictionary);
|
2009-03-13 22:11:42 +00:00
|
|
|
}
|
2009-07-28 23:48:47 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets an optional contacts dictionary resource to be loaded.
|
|
|
|
*/
|
2011-03-04 00:02:28 +00:00
|
|
|
public void setContactsDictionary(Dictionary contactsDictionary) {
|
|
|
|
if (contactsDictionary != null) {
|
|
|
|
mUnigramDictionaries.put(DICT_KEY_CONTACTS, contactsDictionary);
|
|
|
|
mBigramDictionaries.put(DICT_KEY_CONTACTS, contactsDictionary);
|
|
|
|
}
|
2009-07-28 23:48:47 +00:00
|
|
|
}
|
2011-01-07 06:01:51 +00:00
|
|
|
|
2009-07-17 19:51:45 +00:00
|
|
|
public void setAutoDictionary(Dictionary autoDictionary) {
|
2011-03-04 00:02:28 +00:00
|
|
|
if (autoDictionary != null)
|
|
|
|
mUnigramDictionaries.put(DICT_KEY_AUTO, autoDictionary);
|
2009-07-17 19:51:45 +00:00
|
|
|
}
|
2009-03-13 22:11:42 +00:00
|
|
|
|
2010-08-20 05:35:02 +00:00
|
|
|
public void setUserBigramDictionary(Dictionary userBigramDictionary) {
|
2011-03-04 00:02:28 +00:00
|
|
|
if (userBigramDictionary != null)
|
|
|
|
mBigramDictionaries.put(DICT_KEY_USER_BIGRAM, userBigramDictionary);
|
2010-08-20 05:35:02 +00:00
|
|
|
}
|
|
|
|
|
2010-12-11 08:06:24 +00:00
|
|
|
public void setAutoCorrectionThreshold(double threshold) {
|
|
|
|
mAutoCorrectionThreshold = threshold;
|
2010-09-28 03:23:26 +00:00
|
|
|
}
|
|
|
|
|
2011-01-26 16:43:06 +00:00
|
|
|
public boolean isAggressiveAutoCorrectionMode() {
|
|
|
|
return (mAutoCorrectionThreshold == 0);
|
|
|
|
}
|
|
|
|
|
2009-03-13 22:11:42 +00:00
|
|
|
/**
|
|
|
|
* Number of suggestions to generate from the input key sequence. This has
|
|
|
|
* to be a number between 1 and 100 (inclusive).
|
|
|
|
* @param maxSuggestions
|
|
|
|
* @throws IllegalArgumentException if the number is out of range
|
|
|
|
*/
|
|
|
|
public void setMaxSuggestions(int maxSuggestions) {
|
|
|
|
if (maxSuggestions < 1 || maxSuggestions > 100) {
|
|
|
|
throw new IllegalArgumentException("maxSuggestions must be between 1 and 100");
|
|
|
|
}
|
|
|
|
mPrefMaxSuggestions = maxSuggestions;
|
|
|
|
mPriorities = new int[mPrefMaxSuggestions];
|
2010-08-20 05:35:02 +00:00
|
|
|
mBigramPriorities = new int[PREF_MAX_BIGRAMS];
|
|
|
|
collectGarbage(mSuggestions, mPrefMaxSuggestions);
|
2009-03-13 22:11:42 +00:00
|
|
|
while (mStringPool.size() < mPrefMaxSuggestions) {
|
2010-08-20 05:35:02 +00:00
|
|
|
StringBuilder sb = new StringBuilder(getApproxMaxWordLength());
|
2009-03-13 22:11:42 +00:00
|
|
|
mStringPool.add(sb);
|
|
|
|
}
|
|
|
|
}
|
2009-07-30 17:11:33 +00:00
|
|
|
|
2009-03-13 22:11:42 +00:00
|
|
|
/**
|
2010-12-10 11:50:30 +00:00
|
|
|
* Returns a object which represents suggested words that match the list of character codes
|
|
|
|
* passed in. This object contents will be overwritten the next time this function is called.
|
2010-08-20 05:35:02 +00:00
|
|
|
* @param view a view for retrieving the context for AutoText
|
|
|
|
* @param wordComposer contains what is currently being typed
|
|
|
|
* @param prevWordForBigram previous word (used only for bigram)
|
2010-12-10 11:50:30 +00:00
|
|
|
* @return suggested words object.
|
2009-03-13 22:11:42 +00:00
|
|
|
*/
|
2010-12-10 11:50:30 +00:00
|
|
|
public SuggestedWords getSuggestions(View view, WordComposer wordComposer,
|
2010-12-13 03:37:23 +00:00
|
|
|
CharSequence prevWordForBigram) {
|
|
|
|
return getSuggestedWordBuilder(view, wordComposer, prevWordForBigram).build();
|
2010-12-10 11:50:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: cleanup dictionaries looking up and suggestions building with SuggestedWords.Builder
|
|
|
|
public SuggestedWords.Builder getSuggestedWordBuilder(View view, WordComposer wordComposer,
|
2010-12-13 03:37:23 +00:00
|
|
|
CharSequence prevWordForBigram) {
|
2010-08-20 05:35:02 +00:00
|
|
|
LatinImeLogger.onStartSuggestion(prevWordForBigram);
|
2011-03-02 06:40:08 +00:00
|
|
|
mAutoCorrection.init();
|
2010-09-27 15:32:35 +00:00
|
|
|
mIsFirstCharCapitalized = wordComposer.isFirstCharCapitalized();
|
|
|
|
mIsAllUpperCase = wordComposer.isAllUpperCase();
|
2010-08-20 05:35:02 +00:00
|
|
|
collectGarbage(mSuggestions, mPrefMaxSuggestions);
|
2009-03-13 22:11:42 +00:00
|
|
|
Arrays.fill(mPriorities, 0);
|
2010-02-05 22:07:04 +00:00
|
|
|
|
2009-03-13 22:11:42 +00:00
|
|
|
// Save a lowercase version of the original word
|
2010-12-13 03:37:23 +00:00
|
|
|
CharSequence typedWord = wordComposer.getTypedWord();
|
|
|
|
if (typedWord != null) {
|
|
|
|
final String typedWordString = typedWord.toString();
|
|
|
|
typedWord = typedWordString;
|
|
|
|
mLowerOriginalWord = typedWordString.toLowerCase();
|
2010-08-20 05:35:02 +00:00
|
|
|
// Treating USER_TYPED as UNIGRAM suggestion for logging now.
|
2010-12-13 03:37:23 +00:00
|
|
|
LatinImeLogger.onAddSuggestedWord(typedWordString, Suggest.DIC_USER_TYPED,
|
2010-08-20 05:35:02 +00:00
|
|
|
Dictionary.DataType.UNIGRAM);
|
2009-03-13 22:11:42 +00:00
|
|
|
} else {
|
|
|
|
mLowerOriginalWord = "";
|
|
|
|
}
|
2010-08-20 05:35:02 +00:00
|
|
|
|
|
|
|
if (wordComposer.size() == 1 && (mCorrectionMode == CORRECTION_FULL_BIGRAM
|
|
|
|
|| mCorrectionMode == CORRECTION_BASIC)) {
|
|
|
|
// At first character typed, search only the bigrams
|
|
|
|
Arrays.fill(mBigramPriorities, 0);
|
|
|
|
collectGarbage(mBigramSuggestions, PREF_MAX_BIGRAMS);
|
|
|
|
|
|
|
|
if (!TextUtils.isEmpty(prevWordForBigram)) {
|
|
|
|
CharSequence lowerPrevWord = prevWordForBigram.toString().toLowerCase();
|
2011-01-07 06:01:51 +00:00
|
|
|
if (mMainDict != null && mMainDict.isValidWord(lowerPrevWord)) {
|
2010-08-20 05:35:02 +00:00
|
|
|
prevWordForBigram = lowerPrevWord;
|
|
|
|
}
|
2011-03-04 00:02:28 +00:00
|
|
|
for (final Dictionary dictionary : mBigramDictionaries.values()) {
|
|
|
|
dictionary.getBigrams(wordComposer, prevWordForBigram, this);
|
2010-08-20 05:35:02 +00:00
|
|
|
}
|
|
|
|
char currentChar = wordComposer.getTypedWord().charAt(0);
|
|
|
|
char currentCharUpper = Character.toUpperCase(currentChar);
|
|
|
|
int count = 0;
|
|
|
|
int bigramSuggestionSize = mBigramSuggestions.size();
|
|
|
|
for (int i = 0; i < bigramSuggestionSize; i++) {
|
|
|
|
if (mBigramSuggestions.get(i).charAt(0) == currentChar
|
|
|
|
|| mBigramSuggestions.get(i).charAt(0) == currentCharUpper) {
|
|
|
|
int poolSize = mStringPool.size();
|
|
|
|
StringBuilder sb = poolSize > 0 ?
|
|
|
|
(StringBuilder) mStringPool.remove(poolSize - 1)
|
|
|
|
: new StringBuilder(getApproxMaxWordLength());
|
|
|
|
sb.setLength(0);
|
|
|
|
sb.append(mBigramSuggestions.get(i));
|
|
|
|
mSuggestions.add(count++, sb);
|
|
|
|
if (count > mPrefMaxSuggestions) break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if (wordComposer.size() > 1) {
|
|
|
|
// At second character typed, search the unigrams (scores being affected by bigrams)
|
2011-03-04 00:02:28 +00:00
|
|
|
for (final String key : mUnigramDictionaries.keySet()) {
|
|
|
|
// Skip AutoDictionary to lookup
|
|
|
|
if (key.equals(DICT_KEY_AUTO))
|
|
|
|
continue;
|
|
|
|
final Dictionary dictionary = mUnigramDictionaries.get(key);
|
|
|
|
dictionary.getWords(wordComposer, this);
|
2009-03-13 22:11:42 +00:00
|
|
|
}
|
|
|
|
}
|
2011-03-02 06:40:08 +00:00
|
|
|
CharSequence autoText = null;
|
|
|
|
final String typedWordString = typedWord == null ? null : typedWord.toString();
|
2010-12-13 03:37:23 +00:00
|
|
|
if (typedWord != null) {
|
2011-03-02 06:40:08 +00:00
|
|
|
// Apply quick fix only for the typed word.
|
|
|
|
if (mQuickFixesEnabled) {
|
|
|
|
final String lowerCaseTypedWord = typedWordString.toLowerCase();
|
|
|
|
CharSequence tempAutoText =
|
|
|
|
AutoText.get(lowerCaseTypedWord, 0, lowerCaseTypedWord.length(), view);
|
2010-11-01 11:25:07 +00:00
|
|
|
// Is there an AutoText (also known as Quick Fixes) correction?
|
|
|
|
// Capitalize as needed
|
2011-03-02 06:40:08 +00:00
|
|
|
if (!TextUtils.isEmpty(tempAutoText)
|
|
|
|
&& (mIsAllUpperCase || mIsFirstCharCapitalized)) {
|
|
|
|
final int tempAutoTextLength = tempAutoText.length();
|
|
|
|
final int poolSize = mStringPool.size();
|
|
|
|
final StringBuilder sb =
|
|
|
|
poolSize > 0 ? (StringBuilder) mStringPool.remove(poolSize - 1)
|
|
|
|
: new StringBuilder(getApproxMaxWordLength());
|
2010-11-01 11:25:07 +00:00
|
|
|
sb.setLength(0);
|
|
|
|
if (mIsAllUpperCase) {
|
2011-03-02 06:40:08 +00:00
|
|
|
sb.append(tempAutoText.toString().toUpperCase());
|
2010-11-01 11:25:07 +00:00
|
|
|
} else if (mIsFirstCharCapitalized) {
|
2011-03-02 06:40:08 +00:00
|
|
|
sb.append(Character.toUpperCase(tempAutoText.charAt(0)));
|
|
|
|
if (tempAutoTextLength > 1) {
|
|
|
|
sb.append(tempAutoText.subSequence(1, tempAutoTextLength));
|
2010-11-01 11:25:07 +00:00
|
|
|
}
|
|
|
|
}
|
2011-03-02 06:40:08 +00:00
|
|
|
tempAutoText = sb.toString();
|
2010-11-01 11:25:07 +00:00
|
|
|
}
|
2011-03-02 06:40:08 +00:00
|
|
|
boolean canAdd = tempAutoText != null;
|
2010-02-18 19:09:43 +00:00
|
|
|
// Is that correction already the current prediction (or original word)?
|
2011-03-02 06:40:08 +00:00
|
|
|
canAdd &= !TextUtils.equals(tempAutoText, typedWord);
|
2010-02-18 19:09:43 +00:00
|
|
|
// Is that correction already the next predicted word?
|
2011-03-02 06:40:08 +00:00
|
|
|
if (canAdd && mSuggestions.size() > 0 && mCorrectionMode != CORRECTION_BASIC) {
|
|
|
|
canAdd &= !TextUtils.equals(tempAutoText, mSuggestions.get(0));
|
2010-02-18 19:09:43 +00:00
|
|
|
}
|
|
|
|
if (canAdd) {
|
2011-01-18 08:22:01 +00:00
|
|
|
if (DBG) {
|
|
|
|
Log.d(TAG, "Auto corrected by AUTOTEXT.");
|
|
|
|
}
|
2011-03-02 06:40:08 +00:00
|
|
|
autoText = tempAutoText;
|
2010-02-18 19:09:43 +00:00
|
|
|
}
|
2009-03-13 22:11:42 +00:00
|
|
|
}
|
|
|
|
}
|
2011-03-02 06:40:08 +00:00
|
|
|
|
2011-03-04 00:02:28 +00:00
|
|
|
mAutoCorrection.updateAutoCorrectionStatus(mUnigramDictionaries.values(), wordComposer,
|
|
|
|
mSuggestions, mPriorities, typedWord, mAutoCorrectionThreshold, mCorrectionMode,
|
|
|
|
autoText);
|
2011-03-02 06:40:08 +00:00
|
|
|
|
|
|
|
if (autoText != null) {
|
|
|
|
mSuggestions.add(0, autoText);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (typedWord != null) {
|
|
|
|
mSuggestions.add(0, typedWordString);
|
|
|
|
}
|
2009-10-28 01:08:19 +00:00
|
|
|
removeDupes();
|
2011-03-02 06:40:08 +00:00
|
|
|
|
2011-01-31 05:49:33 +00:00
|
|
|
if (DBG) {
|
2011-03-02 06:40:08 +00:00
|
|
|
double normalizedScore = mAutoCorrection.getNormalizedScore();
|
2011-01-31 05:49:33 +00:00
|
|
|
ArrayList<SuggestedWords.SuggestedWordInfo> frequencyInfoList =
|
|
|
|
new ArrayList<SuggestedWords.SuggestedWordInfo>();
|
|
|
|
frequencyInfoList.add(new SuggestedWords.SuggestedWordInfo("+", false));
|
|
|
|
final int priorityLength = mPriorities.length;
|
|
|
|
for (int i = 0; i < priorityLength; ++i) {
|
|
|
|
if (normalizedScore > 0) {
|
|
|
|
final String priorityThreshold = Integer.toString(mPriorities[i]) + " (" +
|
|
|
|
normalizedScore + ")";
|
|
|
|
frequencyInfoList.add(
|
|
|
|
new SuggestedWords.SuggestedWordInfo(priorityThreshold, false));
|
|
|
|
normalizedScore = 0.0;
|
|
|
|
} else {
|
|
|
|
final String priority = Integer.toString(mPriorities[i]);
|
|
|
|
frequencyInfoList.add(new SuggestedWords.SuggestedWordInfo(priority, false));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (int i = priorityLength; i < mSuggestions.size(); ++i) {
|
|
|
|
frequencyInfoList.add(new SuggestedWords.SuggestedWordInfo("--", false));
|
|
|
|
}
|
|
|
|
return new SuggestedWords.Builder().addWords(mSuggestions, frequencyInfoList);
|
|
|
|
}
|
2011-03-02 06:40:08 +00:00
|
|
|
return new SuggestedWords.Builder().addWords(mSuggestions, null);
|
2009-03-13 22:11:42 +00:00
|
|
|
}
|
|
|
|
|
2009-10-28 01:08:19 +00:00
|
|
|
private void removeDupes() {
|
|
|
|
final ArrayList<CharSequence> suggestions = mSuggestions;
|
|
|
|
if (suggestions.size() < 2) return;
|
|
|
|
int i = 1;
|
|
|
|
// Don't cache suggestions.size(), since we may be removing items
|
|
|
|
while (i < suggestions.size()) {
|
|
|
|
final CharSequence cur = suggestions.get(i);
|
|
|
|
// Compare each candidate with each previous candidate
|
|
|
|
for (int j = 0; j < i; j++) {
|
|
|
|
CharSequence previous = suggestions.get(j);
|
|
|
|
if (TextUtils.equals(cur, previous)) {
|
|
|
|
removeFromSuggestions(i);
|
|
|
|
i--;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void removeFromSuggestions(int index) {
|
|
|
|
CharSequence garbage = mSuggestions.remove(index);
|
|
|
|
if (garbage != null && garbage instanceof StringBuilder) {
|
|
|
|
mStringPool.add(garbage);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-01-19 08:29:27 +00:00
|
|
|
public boolean hasAutoCorrection() {
|
2011-03-02 06:40:08 +00:00
|
|
|
return mAutoCorrection.hasAutoCorrection();
|
2009-03-13 22:11:42 +00:00
|
|
|
}
|
|
|
|
|
2011-02-07 11:40:34 +00:00
|
|
|
private static boolean compareCaseInsensitive(final String lowerOriginalWord,
|
2009-03-13 22:11:42 +00:00
|
|
|
final char[] word, final int offset, final int length) {
|
2011-02-07 11:40:34 +00:00
|
|
|
final int originalLength = lowerOriginalWord.length();
|
2009-03-13 22:11:42 +00:00
|
|
|
if (originalLength == length && Character.isUpperCase(word[offset])) {
|
|
|
|
for (int i = 0; i < originalLength; i++) {
|
2011-02-07 11:40:34 +00:00
|
|
|
if (lowerOriginalWord.charAt(i) != Character.toLowerCase(word[offset+i])) {
|
2009-03-13 22:11:42 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2010-12-02 09:46:21 +00:00
|
|
|
@Override
|
2010-08-20 05:35:02 +00:00
|
|
|
public boolean addWord(final char[] word, final int offset, final int length, int freq,
|
|
|
|
final int dicTypeId, final Dictionary.DataType dataType) {
|
|
|
|
Dictionary.DataType dataTypeForLog = dataType;
|
|
|
|
ArrayList<CharSequence> suggestions;
|
|
|
|
int[] priorities;
|
|
|
|
int prefMaxSuggestions;
|
|
|
|
if(dataType == Dictionary.DataType.BIGRAM) {
|
|
|
|
suggestions = mBigramSuggestions;
|
|
|
|
priorities = mBigramPriorities;
|
|
|
|
prefMaxSuggestions = PREF_MAX_BIGRAMS;
|
|
|
|
} else {
|
|
|
|
suggestions = mSuggestions;
|
|
|
|
priorities = mPriorities;
|
|
|
|
prefMaxSuggestions = mPrefMaxSuggestions;
|
|
|
|
}
|
|
|
|
|
2009-03-13 22:11:42 +00:00
|
|
|
int pos = 0;
|
2010-08-20 05:35:02 +00:00
|
|
|
|
2009-03-13 22:11:42 +00:00
|
|
|
// Check if it's the same word, only caps are different
|
|
|
|
if (compareCaseInsensitive(mLowerOriginalWord, word, offset, length)) {
|
|
|
|
pos = 0;
|
|
|
|
} else {
|
2010-08-20 05:35:02 +00:00
|
|
|
if (dataType == Dictionary.DataType.UNIGRAM) {
|
|
|
|
// Check if the word was already added before (by bigram data)
|
|
|
|
int bigramSuggestion = searchBigramSuggestion(word,offset,length);
|
|
|
|
if(bigramSuggestion >= 0) {
|
|
|
|
dataTypeForLog = Dictionary.DataType.BIGRAM;
|
|
|
|
// turn freq from bigram into multiplier specified above
|
|
|
|
double multiplier = (((double) mBigramPriorities[bigramSuggestion])
|
|
|
|
/ MAXIMUM_BIGRAM_FREQUENCY)
|
|
|
|
* (BIGRAM_MULTIPLIER_MAX - BIGRAM_MULTIPLIER_MIN)
|
|
|
|
+ BIGRAM_MULTIPLIER_MIN;
|
|
|
|
/* Log.d(TAG,"bigram num: " + bigramSuggestion
|
|
|
|
+ " wordB: " + mBigramSuggestions.get(bigramSuggestion).toString()
|
|
|
|
+ " currentPriority: " + freq + " bigramPriority: "
|
|
|
|
+ mBigramPriorities[bigramSuggestion]
|
|
|
|
+ " multiplier: " + multiplier); */
|
|
|
|
freq = (int)Math.round((freq * multiplier));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-03-13 22:11:42 +00:00
|
|
|
// Check the last one's priority and bail
|
|
|
|
if (priorities[prefMaxSuggestions - 1] >= freq) return true;
|
|
|
|
while (pos < prefMaxSuggestions) {
|
|
|
|
if (priorities[pos] < freq
|
2010-08-20 05:35:02 +00:00
|
|
|
|| (priorities[pos] == freq && length < suggestions.get(pos).length())) {
|
2009-03-13 22:11:42 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
pos++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (pos >= prefMaxSuggestions) {
|
|
|
|
return true;
|
|
|
|
}
|
2010-08-20 05:35:02 +00:00
|
|
|
|
2010-11-01 11:25:07 +00:00
|
|
|
System.arraycopy(priorities, pos, priorities, pos + 1, prefMaxSuggestions - pos - 1);
|
2009-03-13 22:11:42 +00:00
|
|
|
priorities[pos] = freq;
|
|
|
|
int poolSize = mStringPool.size();
|
2011-01-07 06:01:51 +00:00
|
|
|
StringBuilder sb = poolSize > 0 ? (StringBuilder) mStringPool.remove(poolSize - 1)
|
2010-08-20 05:35:02 +00:00
|
|
|
: new StringBuilder(getApproxMaxWordLength());
|
2009-03-13 22:11:42 +00:00
|
|
|
sb.setLength(0);
|
2010-09-27 15:32:35 +00:00
|
|
|
if (mIsAllUpperCase) {
|
|
|
|
sb.append(new String(word, offset, length).toUpperCase());
|
|
|
|
} else if (mIsFirstCharCapitalized) {
|
2010-01-16 02:21:58 +00:00
|
|
|
sb.append(Character.toUpperCase(word[offset]));
|
|
|
|
if (length > 1) {
|
|
|
|
sb.append(word, offset + 1, length - 1);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
sb.append(word, offset, length);
|
|
|
|
}
|
2010-08-20 05:35:02 +00:00
|
|
|
suggestions.add(pos, sb);
|
|
|
|
if (suggestions.size() > prefMaxSuggestions) {
|
|
|
|
CharSequence garbage = suggestions.remove(prefMaxSuggestions);
|
2009-03-13 22:11:42 +00:00
|
|
|
if (garbage instanceof StringBuilder) {
|
|
|
|
mStringPool.add(garbage);
|
|
|
|
}
|
2010-08-20 05:35:02 +00:00
|
|
|
} else {
|
|
|
|
LatinImeLogger.onAddSuggestedWord(sb.toString(), dicTypeId, dataTypeForLog);
|
2009-03-13 22:11:42 +00:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2010-08-20 05:35:02 +00:00
|
|
|
private int searchBigramSuggestion(final char[] word, final int offset, final int length) {
|
|
|
|
// TODO This is almost O(n^2). Might need fix.
|
|
|
|
// search whether the word appeared in bigram data
|
|
|
|
int bigramSuggestSize = mBigramSuggestions.size();
|
|
|
|
for(int i = 0; i < bigramSuggestSize; i++) {
|
|
|
|
if(mBigramSuggestions.get(i).length() == length) {
|
|
|
|
boolean chk = true;
|
|
|
|
for(int j = 0; j < length; j++) {
|
|
|
|
if(mBigramSuggestions.get(i).charAt(j) != word[offset+j]) {
|
|
|
|
chk = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(chk) return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2009-03-13 22:11:42 +00:00
|
|
|
public boolean isValidWord(final CharSequence word) {
|
2011-01-17 06:13:45 +00:00
|
|
|
if (word == null || word.length() == 0 || mMainDict == null) {
|
2009-03-13 22:11:42 +00:00
|
|
|
return false;
|
|
|
|
}
|
2011-03-04 00:02:28 +00:00
|
|
|
for (final Dictionary dictionary : mUnigramDictionaries.values()) {
|
|
|
|
if (dictionary.isValidWord(word))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2009-03-13 22:11:42 +00:00
|
|
|
}
|
2011-01-07 06:01:51 +00:00
|
|
|
|
2010-08-20 05:35:02 +00:00
|
|
|
private void collectGarbage(ArrayList<CharSequence> suggestions, int prefMaxSuggestions) {
|
2009-03-13 22:11:42 +00:00
|
|
|
int poolSize = mStringPool.size();
|
2010-08-20 05:35:02 +00:00
|
|
|
int garbageSize = suggestions.size();
|
|
|
|
while (poolSize < prefMaxSuggestions && garbageSize > 0) {
|
|
|
|
CharSequence garbage = suggestions.get(garbageSize - 1);
|
2009-03-13 22:11:42 +00:00
|
|
|
if (garbage != null && garbage instanceof StringBuilder) {
|
|
|
|
mStringPool.add(garbage);
|
|
|
|
poolSize++;
|
|
|
|
}
|
|
|
|
garbageSize--;
|
|
|
|
}
|
2010-08-20 05:35:02 +00:00
|
|
|
if (poolSize == prefMaxSuggestions + 1) {
|
2009-03-13 22:11:42 +00:00
|
|
|
Log.w("Suggest", "String pool got too big: " + poolSize);
|
|
|
|
}
|
2010-08-20 05:35:02 +00:00
|
|
|
suggestions.clear();
|
2009-03-13 22:11:42 +00:00
|
|
|
}
|
2009-10-12 20:48:35 +00:00
|
|
|
|
|
|
|
public void close() {
|
2011-03-04 00:02:28 +00:00
|
|
|
final Set<Dictionary> dictionaries = new HashSet<Dictionary>();
|
|
|
|
dictionaries.addAll(mUnigramDictionaries.values());
|
|
|
|
dictionaries.addAll(mBigramDictionaries.values());
|
|
|
|
for (final Dictionary dictionary : dictionaries) {
|
|
|
|
dictionary.close();
|
2009-10-12 20:48:35 +00:00
|
|
|
}
|
2011-03-04 00:02:28 +00:00
|
|
|
mMainDict = null;
|
2009-10-12 20:48:35 +00:00
|
|
|
}
|
2009-03-13 22:11:42 +00:00
|
|
|
}
|