Some performance optimizations.

Makes the user/contacts dictionary lookup faster. This is necessary because
there's more in these dictionaries now and it's written in Java.

Fix an auto-caps issue when moving the cursor. And do it a little lazily.

Fixed a bug that was causing user dictionary words to get a much
higher weightage than the main dictionary.
main
Amith Yamasani 2009-08-07 19:46:55 -07:00 committed by Jean-Baptiste Queru
parent f115088924
commit 3263841911
4 changed files with 71 additions and 41 deletions

View File

@ -66,8 +66,6 @@ public class BinaryDictionary extends Dictionary {
private native int getSuggestionsNative(int dict, int[] inputCodes, int codesSize, private native int getSuggestionsNative(int dict, int[] inputCodes, int codesSize,
char[] outputChars, int[] frequencies, char[] outputChars, int[] frequencies,
int maxWordLength, int maxWords, int maxAlternatives, int skipPos); int maxWordLength, int maxWords, int maxAlternatives, int skipPos);
private native void setParamsNative(int typedLetterMultiplier,
int fullWordMultiplier);
private final void loadDictionary(Context context, int resId) { private final void loadDictionary(Context context, int resId) {
AssetManager am = context.getResources().getAssets(); AssetManager am = context.getResources().getAssets();

View File

@ -18,13 +18,6 @@ package com.android.inputmethod.latin;
import android.content.Context; import android.content.Context;
import com.android.inputmethod.latin.Dictionary;
import com.android.inputmethod.latin.WordComposer;
import com.android.inputmethod.latin.Dictionary.WordCallback;
import java.util.ArrayList;
import java.util.List;
/** /**
* Base class for an in-memory dictionary that can grow dynamically and can * Base class for an in-memory dictionary that can grow dynamically and can
* be searched for suggestions and valid words. * be searched for suggestions and valid words.
@ -42,14 +35,38 @@ public class ExpandableDictionary extends Dictionary {
char code; char code;
int frequency; int frequency;
boolean terminal; boolean terminal;
List<Node> children; NodeArray children;
} }
private ArrayList<Node> mRoots; static class NodeArray {
Node[] data;
int length = 0;
private static final int INCREMENT = 2;
NodeArray() {
data = new Node[INCREMENT];
}
void add(Node n) {
if (length + 1 > data.length) {
Node[] tempData = new Node[length + INCREMENT];
if (length > 0) {
System.arraycopy(data, 0, tempData, 0, length);
}
data = tempData;
}
data[length++] = n;
}
}
private NodeArray mRoots;
private int[][] mCodes;
ExpandableDictionary(Context context) { ExpandableDictionary(Context context) {
mContext = context; mContext = context;
clearDictionary(); clearDictionary();
mCodes = new int[MAX_WORD_LENGTH][];
} }
Context getContext() { Context getContext() {
@ -64,17 +81,17 @@ public class ExpandableDictionary extends Dictionary {
addWordRec(mRoots, word, 0, frequency); addWordRec(mRoots, word, 0, frequency);
} }
private void addWordRec(List<Node> children, final String word, private void addWordRec(NodeArray children, final String word,
final int depth, final int frequency) { final int depth, final int frequency) {
final int wordLength = word.length(); final int wordLength = word.length();
final char c = word.charAt(depth); final char c = word.charAt(depth);
// Does children have the current character? // Does children have the current character?
final int childrenLength = children.size(); final int childrenLength = children.length;
Node childNode = null; Node childNode = null;
boolean found = false; boolean found = false;
for (int i = 0; i < childrenLength; i++) { for (int i = 0; i < childrenLength; i++) {
childNode = children.get(i); childNode = children.data[i];
if (childNode.code == c) { if (childNode.code == c) {
found = true; found = true;
break; break;
@ -93,7 +110,7 @@ public class ExpandableDictionary extends Dictionary {
return; return;
} }
if (childNode.children == null) { if (childNode.children == null) {
childNode.children = new ArrayList<Node>(); childNode.children = new NodeArray();
} }
addWordRec(childNode.children, word, depth + 1, frequency); addWordRec(childNode.children, word, depth + 1, frequency);
} }
@ -101,10 +118,15 @@ public class ExpandableDictionary extends Dictionary {
@Override @Override
public void getWords(final WordComposer codes, final WordCallback callback) { public void getWords(final WordComposer codes, final WordCallback callback) {
mInputLength = codes.size(); mInputLength = codes.size();
mMaxDepth = mInputLength * 3; if (mCodes.length < mInputLength) mCodes = new int[mInputLength][];
getWordsRec(mRoots, codes, mWordBuilder, 0, false, 1.0f, 0, -1, callback); // Cache the codes so that we don't have to lookup an array list
for (int i = 0; i < mInputLength; i++) { for (int i = 0; i < mInputLength; i++) {
getWordsRec(mRoots, codes, mWordBuilder, 0, false, 1.0f, 0, i, callback); mCodes[i] = codes.getCodesAt(i);
}
mMaxDepth = mInputLength * 3;
getWordsRec(mRoots, codes, mWordBuilder, 0, false, 1, 0, -1, callback);
for (int i = 0; i < mInputLength; i++) {
getWordsRec(mRoots, codes, mWordBuilder, 0, false, 1, 0, i, callback);
} }
} }
@ -124,12 +146,12 @@ public class ExpandableDictionary extends Dictionary {
/** /**
* Returns the word's frequency or -1 if not found * Returns the word's frequency or -1 if not found
*/ */
private int getWordFrequencyRec(final List<Node> children, final CharSequence word, private int getWordFrequencyRec(final NodeArray children, final CharSequence word,
final int offset, final int length) { final int offset, final int length) {
final int count = children.size(); final int count = children.length;
char currentChar = word.charAt(offset); char currentChar = word.charAt(offset);
for (int j = 0; j < count; j++) { for (int j = 0; j < count; j++) {
final Node node = children.get(j); final Node node = children.data[j];
if (node.code == currentChar) { if (node.code == currentChar) {
if (offset == length - 1) { if (offset == length - 1) {
if (node.terminal) { if (node.terminal) {
@ -165,10 +187,10 @@ public class ExpandableDictionary extends Dictionary {
* inputIndex * inputIndex
* @param callback the callback class for adding a word * @param callback the callback class for adding a word
*/ */
protected void getWordsRec(List<Node> roots, final WordComposer codes, final char[] word, protected void getWordsRec(NodeArray roots, final WordComposer codes, final char[] word,
final int depth, boolean completion, float snr, int inputIndex, int skipPos, final int depth, boolean completion, int snr, int inputIndex, int skipPos,
WordCallback callback) { WordCallback callback) {
final int count = roots.size(); final int count = roots.length;
final int codeSize = mInputLength; final int codeSize = mInputLength;
// Optimization: Prune out words that are too long compared to how much was typed. // Optimization: Prune out words that are too long compared to how much was typed.
if (depth > mMaxDepth) { if (depth > mMaxDepth) {
@ -178,20 +200,20 @@ public class ExpandableDictionary extends Dictionary {
if (codeSize <= inputIndex) { if (codeSize <= inputIndex) {
completion = true; completion = true;
} else { } else {
currentChars = codes.getCodesAt(inputIndex); currentChars = mCodes[inputIndex];
} }
for (int i = 0; i < count; i++) { for (int i = 0; i < count; i++) {
final Node node = roots.get(i); final Node node = roots.data[i];
final char c = node.code; final char c = node.code;
final char lowerC = toLowerCase(c); final char lowerC = toLowerCase(c);
boolean terminal = node.terminal; final boolean terminal = node.terminal;
List<Node> children = node.children; final NodeArray children = node.children;
int freq = node.frequency; final int freq = node.frequency;
if (completion) { if (completion) {
word[depth] = c; word[depth] = c;
if (terminal) { if (terminal) {
if (!callback.addWord(word, 0, depth + 1, (int) (freq * snr))) { if (!callback.addWord(word, 0, depth + 1, freq * snr)) {
return; return;
} }
} }
@ -210,14 +232,15 @@ public class ExpandableDictionary extends Dictionary {
// Don't use alternatives if we're looking for missing characters // Don't use alternatives if we're looking for missing characters
final int alternativesSize = skipPos >= 0? 1 : currentChars.length; final int alternativesSize = skipPos >= 0? 1 : currentChars.length;
for (int j = 0; j < alternativesSize; j++) { for (int j = 0; j < alternativesSize; j++) {
float addedAttenuation = (j > 0 ? 1f : 3f); final int addedAttenuation = (j > 0 ? 1 : 2);
if (currentChars[j] == -1) { final int currentChar = currentChars[j];
if (currentChar == -1) {
break; break;
} }
if (currentChars[j] == lowerC || currentChars[j] == c) { if (currentChar == lowerC || currentChar == c) {
word[depth] = c; word[depth] = c;
if (codes.size() == depth + 1) { if (codeSize == depth + 1) {
if (terminal) { if (terminal) {
if (INCLUDE_TYPED_WORD_IF_VALID if (INCLUDE_TYPED_WORD_IF_VALID
|| !same(word, depth + 1, codes.getTypedWord())) { || !same(word, depth + 1, codes.getTypedWord())) {
@ -243,7 +266,7 @@ public class ExpandableDictionary extends Dictionary {
} }
protected void clearDictionary() { protected void clearDictionary() {
mRoots = new ArrayList<Node>(); mRoots = new NodeArray();
} }
static char toLowerCase(char c) { static char toLowerCase(char c) {
@ -252,7 +275,7 @@ public class ExpandableDictionary extends Dictionary {
} }
if (c >= 'A' && c <= 'Z') { if (c >= 'A' && c <= 'Z') {
c = (char) (c | 32); c = (char) (c | 32);
} else { } else if (c > 127) {
c = Character.toLowerCase(c); c = Character.toLowerCase(c);
} }
return c; return c;

View File

@ -371,6 +371,7 @@ public class LatinIME extends InputMethodService
TextEntryState.reset(); TextEntryState.reset();
} }
mJustAccepted = false; mJustAccepted = false;
postUpdateShiftKeyState();
} }
@Override @Override
@ -498,6 +499,11 @@ public class LatinIME extends InputMethodService
} }
} }
private void postUpdateShiftKeyState() {
mHandler.removeMessages(MSG_UPDATE_SHIFT_STATE);
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_UPDATE_SHIFT_STATE), 300);
}
public void updateShiftKeyState(EditorInfo attr) { public void updateShiftKeyState(EditorInfo attr) {
InputConnection ic = getCurrentInputConnection(); InputConnection ic = getCurrentInputConnection();
if (attr != null && mInputView != null && mKeyboardSwitcher.isAlphabetMode() if (attr != null && mInputView != null && mKeyboardSwitcher.isAlphabetMode()
@ -637,8 +643,7 @@ public class LatinIME extends InputMethodService
} else { } else {
deleteChar = true; deleteChar = true;
} }
mHandler.removeMessages(MSG_UPDATE_SHIFT_STATE); postUpdateShiftKeyState();
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_UPDATE_SHIFT_STATE), 300);
TextEntryState.backspace(); TextEntryState.backspace();
if (TextEntryState.getState() == TextEntryState.STATE_UNDO_COMMIT) { if (TextEntryState.getState() == TextEntryState.STATE_UNDO_COMMIT) {
revertLastWord(deleteChar); revertLastWord(deleteChar);
@ -688,7 +693,11 @@ public class LatinIME extends InputMethodService
} else { } else {
sendKeyChar((char)primaryCode); sendKeyChar((char)primaryCode);
} }
if (mPredicting && mComposing.length() == 1) {
updateShiftKeyState(getCurrentInputEditorInfo()); updateShiftKeyState(getCurrentInputEditorInfo());
} else {
postUpdateShiftKeyState();
}
measureCps(); measureCps();
TextEntryState.typedCharacter((char) primaryCode, isWordSeparator(primaryCode)); TextEntryState.typedCharacter((char) primaryCode, isWordSeparator(primaryCode));
} }

View File

@ -26,7 +26,7 @@ public class WordComposer {
/** /**
* The list of unicode values for each keystroke (including surrounding keys) * The list of unicode values for each keystroke (including surrounding keys)
*/ */
private List<int[]> mCodes; private ArrayList<int[]> mCodes;
/** /**
* The word chosen from the candidate list, until it is committed. * The word chosen from the candidate list, until it is committed.