Read and use user dictionary shortcuts.

Bug: 4646172

Change-Id: I51002c73d5bad1a698110c5cda02253348be8eed
This commit is contained in:
Jean Chalard 2012-05-11 16:33:01 +09:00
parent a9aeb6f3cc
commit 19ad9bf145
8 changed files with 90 additions and 35 deletions

View file

@ -149,7 +149,8 @@ public class ContactsDictionary extends ExpandableDictionary {
// capitalization of i.
final int wordLen = word.length();
if (wordLen < maxWordLength && wordLen > 1) {
super.addWord(word, FREQUENCY_FOR_CONTACTS);
super.addWord(word, null /* shortcut */,
FREQUENCY_FOR_CONTACTS);
if (!TextUtils.isEmpty(prevWord)) {
super.setBigram(prevWord, word,
FREQUENCY_FOR_CONTACTS_BIGRAM);

View file

@ -23,11 +23,6 @@ import com.android.inputmethod.keyboard.ProximityInfo;
* strokes.
*/
public abstract class Dictionary {
/**
* Whether or not to replicate the typed word in the suggested list, even if it's valid.
*/
protected static final boolean INCLUDE_TYPED_WORD_IF_VALID = false;
/**
* The weight to give to a word if it's length is the same as the number of typed characters.
*/

View file

@ -22,6 +22,7 @@ import com.android.inputmethod.keyboard.KeyDetector;
import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.ProximityInfo;
import java.util.ArrayList;
import java.util.LinkedList;
/**
@ -53,6 +54,8 @@ public class ExpandableDictionary extends Dictionary {
boolean mTerminal;
Node mParent;
NodeArray mChildren;
ArrayList<char[]> mShortcutTargets;
boolean mShortcutOnly;
LinkedList<NextWord> mNGrams; // Supports ngram
}
@ -150,15 +153,15 @@ public class ExpandableDictionary extends Dictionary {
return BinaryDictionary.MAX_WORD_LENGTH;
}
public void addWord(String word, int frequency) {
public void addWord(final String word, final String shortcutTarget, final int frequency) {
if (word.length() >= BinaryDictionary.MAX_WORD_LENGTH) {
return;
}
addWordRec(mRoots, word, 0, frequency, null);
addWordRec(mRoots, word, 0, shortcutTarget, frequency, null);
}
private void addWordRec(NodeArray children, final String word, final int depth,
final int frequency, Node parentNode) {
final String shortcutTarget, final int frequency, Node parentNode) {
final int wordLength = word.length();
if (wordLength <= depth) return;
final char c = word.charAt(depth);
@ -172,15 +175,25 @@ public class ExpandableDictionary extends Dictionary {
break;
}
}
final boolean isShortcutOnly = (null != shortcutTarget);
if (childNode == null) {
childNode = new Node();
childNode.mCode = c;
childNode.mParent = parentNode;
childNode.mShortcutOnly = isShortcutOnly;
children.add(childNode);
}
if (wordLength == depth + 1) {
// Terminate this word
childNode.mTerminal = true;
if (isShortcutOnly) {
if (null == childNode.mShortcutTargets) {
childNode.mShortcutTargets = new ArrayList<char[]>();
}
childNode.mShortcutTargets.add(shortcutTarget.toCharArray());
} else {
childNode.mShortcutOnly = false;
}
childNode.mFrequency = Math.max(frequency, childNode.mFrequency);
if (childNode.mFrequency > 255) childNode.mFrequency = 255;
return;
@ -188,7 +201,7 @@ public class ExpandableDictionary extends Dictionary {
if (childNode.mChildren == null) {
childNode.mChildren = new NodeArray();
}
addWordRec(childNode.mChildren, word, depth + 1, frequency, childNode);
addWordRec(childNode.mChildren, word, depth + 1, shortcutTarget, frequency, childNode);
}
@Override
@ -239,7 +252,13 @@ public class ExpandableDictionary extends Dictionary {
if (mRequiresReload) startDictionaryLoadingTaskLocked();
if (mUpdatingDictionary) return false;
}
return getWordFrequency(word) > -1;
final Node node = searchNode(mRoots, word, 0, word.length());
// If node is null, we didn't find the word, so it's not valid.
// If node.mShortcutOnly is true, then it exists as a shortcut but not as a word,
// so that means it's not a valid word.
// If node.mShortcutOnly is false, then it exists as a word (it may also exist as
// a shortcut, but this does not matter), so it's a valid word.
return (node == null) ? false : !node.mShortcutOnly;
}
/**
@ -247,7 +266,7 @@ public class ExpandableDictionary extends Dictionary {
*/
protected int getWordFrequency(CharSequence word) {
// Case-sensitive search
Node node = searchNode(mRoots, word, 0, word.length());
final Node node = searchNode(mRoots, word, 0, word.length());
return (node == null) ? -1 : node.mFrequency;
}
@ -261,6 +280,35 @@ public class ExpandableDictionary extends Dictionary {
}
}
/**
* Helper method to add a word and its shortcuts.
*
* @param node the terminal node
* @param word the word to insert, as an array of code points
* @param depth the depth of the node in the tree
* @param finalFreq the frequency for this word
* @return whether there is still space for more words. {@see Dictionary.WordCallback#addWord}.
*/
private boolean addWordAndShortcutsFromNode(final Node node, final char[] word, final int depth,
final int finalFreq, final WordCallback callback) {
if (finalFreq > 0 && !node.mShortcutOnly) {
if (!callback.addWord(word, 0, depth + 1, finalFreq, mDicTypeId, Dictionary.UNIGRAM)) {
return false;
}
}
if (null != node.mShortcutTargets) {
final int length = node.mShortcutTargets.size();
for (int shortcutIndex = 0; shortcutIndex < length; ++shortcutIndex) {
final char[] shortcut = node.mShortcutTargets.get(shortcutIndex);
if (!callback.addWord(shortcut, 0, shortcut.length, finalFreq, mDicTypeId,
Dictionary.UNIGRAM)) {
return false;
}
}
}
return true;
}
/**
* Recursively traverse the tree for words that match the input. Input consists of
* a list of arrays. Each item in the list is one input character position. An input
@ -313,8 +361,8 @@ public class ExpandableDictionary extends Dictionary {
} else {
finalFreq = computeSkippedWordFinalFreq(freq, snr, mInputLength);
}
if (!callback.addWord(word, 0, depth + 1, finalFreq, mDicTypeId,
Dictionary.UNIGRAM)) {
if (!addWordAndShortcutsFromNode(node, word, depth, finalFreq, callback)) {
// No space left in the queue, bail out
return;
}
}
@ -344,18 +392,18 @@ public class ExpandableDictionary extends Dictionary {
if (codeSize == inputIndex + 1) {
if (terminal) {
if (INCLUDE_TYPED_WORD_IF_VALID
|| !same(word, depth + 1, codes.getTypedWord())) {
final int finalFreq;
if (skipPos < 0) {
finalFreq = freq * snr * addedAttenuation
* FULL_WORD_SCORE_MULTIPLIER;
} else {
finalFreq = computeSkippedWordFinalFreq(freq,
snr * addedAttenuation, mInputLength);
}
callback.addWord(word, 0, depth + 1, finalFreq, mDicTypeId,
Dictionary.UNIGRAM);
final int finalFreq;
if (skipPos < 0) {
finalFreq = freq * snr * addedAttenuation
* FULL_WORD_SCORE_MULTIPLIER;
} else {
finalFreq = computeSkippedWordFinalFreq(freq,
snr * addedAttenuation, mInputLength);
}
if (!addWordAndShortcutsFromNode(node, word, depth, finalFreq,
callback)) {
// No space left in the queue, bail out
return;
}
}
if (children != null) {

View file

@ -1120,7 +1120,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
@Override
public boolean addWordToDictionary(String word) {
mUserDictionary.addWord(word, 128);
mUserDictionary.addWordToUserDictionary(word, 128);
// Suggestion strip should be updated after the operation of adding word to the
// user dictionary
mHandler.postUpdateSuggestions();

View file

@ -42,6 +42,6 @@ public class SynchronouslyLoadedUserDictionary extends UserDictionary {
@Override
public synchronized boolean isValidWord(CharSequence word) {
blockingReloadDictionaryIfRequired();
return getWordFrequency(word) > -1;
return super.isValidWord(word);
}
}

View file

@ -31,8 +31,11 @@ import java.util.Arrays;
public class UserDictionary extends ExpandableDictionary {
// TODO: use Words.SHORTCUT when it's public in the SDK
final static String SHORTCUT = "shortcut";
private static final String[] PROJECTION_QUERY = {
Words.WORD,
SHORTCUT,
Words.FREQUENCY,
};
@ -149,15 +152,18 @@ public class UserDictionary extends ExpandableDictionary {
}
/**
* Adds a word to the dictionary and makes it persistent.
* Adds a word to the user dictionary and makes it persistent.
*
* This will call upon the system interface to do the actual work through the intent
* readied by the system to this effect.
*
* @param word the word to add. If the word is capitalized, then the dictionary will
* recognize it as a capitalized word when searched.
* @param frequency the frequency of occurrence of the word. A frequency of 255 is considered
* the highest.
* @TODO use a higher or float range for frequency
*/
@Override
public synchronized void addWord(final String word, final int frequency) {
public synchronized void addWordToUserDictionary(final String word, final int frequency) {
// Force load the dictionary here synchronously
if (getRequiresReload()) loadDictionaryAsync();
// TODO: do something for the UI. With the following, any sufficiently long word will
@ -191,14 +197,19 @@ public class UserDictionary extends ExpandableDictionary {
final int maxWordLength = getMaxWordLength();
if (cursor.moveToFirst()) {
final int indexWord = cursor.getColumnIndex(Words.WORD);
final int indexShortcut = cursor.getColumnIndex(SHORTCUT);
final int indexFrequency = cursor.getColumnIndex(Words.FREQUENCY);
while (!cursor.isAfterLast()) {
String word = cursor.getString(indexWord);
String shortcut = cursor.getString(indexShortcut);
int frequency = cursor.getInt(indexFrequency);
// Safeguard against adding really long words. Stack may overflow due
// to recursion
if (word.length() < maxWordLength) {
super.addWord(word, frequency);
super.addWord(word, null, frequency);
}
if (null != shortcut && shortcut.length() < maxWordLength) {
super.addWord(shortcut, word, frequency);
}
cursor.moveToNext();
}

View file

@ -176,7 +176,7 @@ public class UserHistoryDictionary extends ExpandableDictionary {
* The second word may not be null (a NullPointerException would be thrown).
*/
public int addToUserHistory(final String word1, String word2) {
super.addWord(word2, FREQUENCY_FOR_TYPED);
super.addWord(word2, null /* shortcut */, FREQUENCY_FOR_TYPED);
// Do not insert a word as a bigram of itself
if (word2.equals(word1)) {
return 0;
@ -246,7 +246,7 @@ public class UserHistoryDictionary extends ExpandableDictionary {
// Safeguard against adding really long words. Stack may overflow due
// to recursive lookup
if (null == word1) {
super.addWord(word2, frequency);
super.addWord(word2, null /* shortcut */, frequency);
} else if (word1.length() < BinaryDictionary.MAX_WORD_LENGTH
&& word2.length() < BinaryDictionary.MAX_WORD_LENGTH) {
super.setBigram(word1, word2, frequency);

View file

@ -66,7 +66,7 @@ public class WhitelistDictionary extends ExpandableDictionary {
if (before != null && after != null) {
mWhitelistWords.put(
before.toLowerCase(), new Pair<Integer, String>(score, after));
addWord(after, score);
addWord(after, null /* shortcut */, score);
}
}
} catch (NumberFormatException e) {