Increase target size of preferred letters while typing.

This increases the chance of hitting the correct letter when typing a word
that exists in the dictionary, rather than only correct it after the fact.
It is most effective after 2 or 3 letters of a word have been typed and gets
more accurate with more typed letters in the word.

If 2 adjacent letters have similar probabilities of occuring, then there is no
hit correction applied.
main
Amith Yamasani 2010-02-05 14:07:04 -08:00
parent 8fa317a61a
commit 1b62ff1a3d
12 changed files with 215 additions and 20 deletions

View File

@ -89,7 +89,7 @@ static jint latinime_BinaryDictionary_open
static int latinime_BinaryDictionary_getSuggestions( static int latinime_BinaryDictionary_getSuggestions(
JNIEnv *env, jobject object, jint dict, jintArray inputArray, jint arraySize, JNIEnv *env, jobject object, jint dict, jintArray inputArray, jint arraySize,
jcharArray outputArray, jintArray frequencyArray, jint maxWordLength, jint maxWords, jcharArray outputArray, jintArray frequencyArray, jint maxWordLength, jint maxWords,
jint maxAlternatives, jint skipPos) jint maxAlternatives, jint skipPos, jintArray nextLettersArray, jint nextLettersSize)
{ {
Dictionary *dictionary = (Dictionary*) dict; Dictionary *dictionary = (Dictionary*) dict;
if (dictionary == NULL) if (dictionary == NULL)
@ -98,13 +98,16 @@ static int latinime_BinaryDictionary_getSuggestions(
int *frequencies = env->GetIntArrayElements(frequencyArray, NULL); int *frequencies = env->GetIntArrayElements(frequencyArray, NULL);
int *inputCodes = env->GetIntArrayElements(inputArray, NULL); int *inputCodes = env->GetIntArrayElements(inputArray, NULL);
jchar *outputChars = env->GetCharArrayElements(outputArray, NULL); jchar *outputChars = env->GetCharArrayElements(outputArray, NULL);
int *nextLetters = nextLettersArray != NULL ? env->GetIntArrayElements(nextLettersArray, NULL)
: NULL;
int count = dictionary->getSuggestions(inputCodes, arraySize, (unsigned short*) outputChars, frequencies, int count = dictionary->getSuggestions(inputCodes, arraySize, (unsigned short*) outputChars, frequencies,
maxWordLength, maxWords, maxAlternatives, skipPos); maxWordLength, maxWords, maxAlternatives, skipPos, nextLetters, nextLettersSize);
env->ReleaseIntArrayElements(frequencyArray, frequencies, 0); env->ReleaseIntArrayElements(frequencyArray, frequencies, 0);
env->ReleaseIntArrayElements(inputArray, inputCodes, JNI_ABORT); env->ReleaseIntArrayElements(inputArray, inputCodes, JNI_ABORT);
env->ReleaseCharArrayElements(outputArray, outputChars, 0); env->ReleaseCharArrayElements(outputArray, outputChars, 0);
env->ReleaseIntArrayElements(nextLettersArray, nextLetters, 0);
return count; return count;
} }
@ -136,7 +139,7 @@ static JNINativeMethod gMethods[] = {
{"openNative", "(Landroid/content/res/AssetManager;Ljava/lang/String;II)I", {"openNative", "(Landroid/content/res/AssetManager;Ljava/lang/String;II)I",
(void*)latinime_BinaryDictionary_open}, (void*)latinime_BinaryDictionary_open},
{"closeNative", "(I)V", (void*)latinime_BinaryDictionary_close}, {"closeNative", "(I)V", (void*)latinime_BinaryDictionary_close},
{"getSuggestionsNative", "(I[II[C[IIIII)I", (void*)latinime_BinaryDictionary_getSuggestions}, {"getSuggestionsNative", "(I[II[C[IIIII[II)I", (void*)latinime_BinaryDictionary_getSuggestions},
{"isValidWordNative", "(I[CI)Z", (void*)latinime_BinaryDictionary_isValidWord} {"isValidWordNative", "(I[CI)Z", (void*)latinime_BinaryDictionary_isValidWord}
}; };

View File

@ -49,7 +49,8 @@ Dictionary::~Dictionary()
} }
int Dictionary::getSuggestions(int *codes, int codesSize, unsigned short *outWords, int *frequencies, int Dictionary::getSuggestions(int *codes, int codesSize, unsigned short *outWords, int *frequencies,
int maxWordLength, int maxWords, int maxAlternatives, int skipPos) int maxWordLength, int maxWords, int maxAlternatives, int skipPos,
int *nextLetters, int nextLettersSize)
{ {
int suggWords; int suggWords;
mFrequencies = frequencies; mFrequencies = frequencies;
@ -61,6 +62,8 @@ int Dictionary::getSuggestions(int *codes, int codesSize, unsigned short *outWor
mMaxWords = maxWords; mMaxWords = maxWords;
mSkipPos = skipPos; mSkipPos = skipPos;
mMaxEditDistance = mInputLength < 5 ? 2 : mInputLength / 2; mMaxEditDistance = mInputLength < 5 ? 2 : mInputLength / 2;
mNextLettersFrequencies = nextLetters;
mNextLettersSize = nextLettersSize;
getWordsRec(0, 0, mInputLength * 3, false, 1, 0, 0); getWordsRec(0, 0, mInputLength * 3, false, 1, 0, 0);
@ -68,9 +71,27 @@ int Dictionary::getSuggestions(int *codes, int codesSize, unsigned short *outWor
suggWords = 0; suggWords = 0;
while (suggWords < mMaxWords && mFrequencies[suggWords] > 0) suggWords++; while (suggWords < mMaxWords && mFrequencies[suggWords] > 0) suggWords++;
if (DEBUG_DICT) LOGI("Returning %d words", suggWords); if (DEBUG_DICT) LOGI("Returning %d words", suggWords);
if (DEBUG_DICT) {
LOGI("Next letters: ");
for (int k = 0; k < nextLettersSize; k++) {
if (mNextLettersFrequencies[k] > 0) {
LOGI("%c = %d,", k, mNextLettersFrequencies[k]);
}
}
LOGI("\n");
}
return suggWords; return suggWords;
} }
void
Dictionary::registerNextLetter(unsigned short c)
{
if (c < mNextLettersSize) {
mNextLettersFrequencies[c]++;
}
}
unsigned short unsigned short
Dictionary::getChar(int *pos) Dictionary::getChar(int *pos)
{ {
@ -210,6 +231,9 @@ Dictionary::getWordsRec(int pos, int depth, int maxDepth, bool completion, int s
mWord[depth] = c; mWord[depth] = c;
if (terminal) { if (terminal) {
addWord(mWord, depth + 1, freq * snr); addWord(mWord, depth + 1, freq * snr);
if (depth >= mInputLength && mSkipPos < 0) {
registerNextLetter(mWord[mInputLength]);
}
} }
if (childrenAddress != 0) { if (childrenAddress != 0) {
getWordsRec(childrenAddress, depth + 1, maxDepth, getWordsRec(childrenAddress, depth + 1, maxDepth,

View File

@ -32,7 +32,8 @@ class Dictionary {
public: public:
Dictionary(void *dict, int typedLetterMultipler, int fullWordMultiplier); Dictionary(void *dict, int typedLetterMultipler, int fullWordMultiplier);
int getSuggestions(int *codes, int codesSize, unsigned short *outWords, int *frequencies, int getSuggestions(int *codes, int codesSize, unsigned short *outWords, int *frequencies,
int maxWordLength, int maxWords, int maxAlternatives, int skipPos); int maxWordLength, int maxWords, int maxAlternatives, int skipPos,
int *nextLetters, int nextLettersSize);
bool isValidWord(unsigned short *word, int length); bool isValidWord(unsigned short *word, int length);
void setAsset(void *asset) { mAsset = asset; } void setAsset(void *asset) { mAsset = asset; }
void *getAsset() { return mAsset; } void *getAsset() { return mAsset; }
@ -53,6 +54,7 @@ private:
void getWordsRec(int pos, int depth, int maxDepth, bool completion, int frequency, void getWordsRec(int pos, int depth, int maxDepth, bool completion, int frequency,
int inputIndex, int diffs); int inputIndex, int diffs);
bool isValidWordRec(int pos, unsigned short *word, int offset, int length); bool isValidWordRec(int pos, unsigned short *word, int offset, int length);
void registerNextLetter(unsigned short c);
unsigned char *mDict; unsigned char *mDict;
void *mAsset; void *mAsset;
@ -70,6 +72,8 @@ private:
int mFullWordMultiplier; int mFullWordMultiplier;
int mTypedLetterMultiplier; int mTypedLetterMultiplier;
int *mNextLettersFrequencies;
int mNextLettersSize;
}; };
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------

View File

@ -65,7 +65,8 @@ public class BinaryDictionary extends Dictionary {
private native boolean isValidWordNative(int nativeData, char[] word, int wordLength); private native boolean isValidWordNative(int nativeData, char[] word, int wordLength);
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,
int[] nextLettersFrequencies, int nextLettersSize);
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();
@ -74,7 +75,8 @@ public class BinaryDictionary extends Dictionary {
} }
@Override @Override
public void getWords(final WordComposer codes, final WordCallback callback) { public void getWords(final WordComposer codes, final WordCallback callback,
int[] nextLettersFrequencies) {
final int codesSize = codes.size(); final int codesSize = codes.size();
// Wont deal with really long words. // Wont deal with really long words.
if (codesSize > MAX_WORD_LENGTH - 1) return; if (codesSize > MAX_WORD_LENGTH - 1) return;
@ -90,7 +92,9 @@ public class BinaryDictionary extends Dictionary {
int count = getSuggestionsNative(mNativeDict, mInputCodes, codesSize, int count = getSuggestionsNative(mNativeDict, mInputCodes, codesSize,
mOutputChars, mFrequencies, mOutputChars, mFrequencies,
MAX_WORD_LENGTH, MAX_WORDS, MAX_ALTERNATIVES, -1); MAX_WORD_LENGTH, MAX_WORDS, MAX_ALTERNATIVES, -1,
nextLettersFrequencies,
nextLettersFrequencies != null ? nextLettersFrequencies.length : 0);
// If there aren't sufficient suggestions, search for words by allowing wild cards at // If there aren't sufficient suggestions, search for words by allowing wild cards at
// the different character positions. This feature is not ready for prime-time as we need // the different character positions. This feature is not ready for prime-time as we need
@ -100,7 +104,8 @@ public class BinaryDictionary extends Dictionary {
for (int skip = 0; skip < codesSize; skip++) { for (int skip = 0; skip < codesSize; skip++) {
int tempCount = getSuggestionsNative(mNativeDict, mInputCodes, codesSize, int tempCount = getSuggestionsNative(mNativeDict, mInputCodes, codesSize,
mOutputChars, mFrequencies, mOutputChars, mFrequencies,
MAX_WORD_LENGTH, MAX_WORDS, MAX_ALTERNATIVES, skip); MAX_WORD_LENGTH, MAX_WORDS, MAX_ALTERNATIVES, skip,
null, 0);
count = Math.max(count, tempCount); count = Math.max(count, tempCount);
if (tempCount > 0) break; if (tempCount > 0) break;
} }

View File

@ -84,14 +84,15 @@ public class ContactsDictionary extends ExpandableDictionary {
} }
@Override @Override
public synchronized void getWords(final WordComposer codes, final WordCallback callback) { public synchronized void getWords(final WordComposer codes, final WordCallback callback,
int[] nextLettersFrequencies) {
synchronized (mUpdatingLock) { synchronized (mUpdatingLock) {
// If we need to update, start off a background task // If we need to update, start off a background task
if (mRequiresReload) loadDictionaryAsyncLocked(); if (mRequiresReload) loadDictionaryAsyncLocked();
// Currently updating contacts, don't return any results. // Currently updating contacts, don't return any results.
if (mUpdatingContacts) return; if (mUpdatingContacts) return;
} }
super.getWords(codes, callback); super.getWords(codes, callback, nextLettersFrequencies);
} }
@Override @Override

View File

@ -55,9 +55,14 @@ abstract public class Dictionary {
* words are added through the callback object. * words are added through the callback object.
* @param composer the key sequence to match * @param composer the key sequence to match
* @param callback the callback object to send matched words to as possible candidates * @param callback the callback object to send matched words to as possible candidates
* @param nextLettersFrequencies array of frequencies of next letters that could follow the
* word so far. For instance, "bracke" can be followed by "t", so array['t'] will have
* a non-zero value on returning from this method.
* Pass in null if you don't want the dictionary to look up next letters.
* @see WordCallback#addWord(char[], int, int) * @see WordCallback#addWord(char[], int, int)
*/ */
abstract public void getWords(final WordComposer composer, final WordCallback callback); abstract public void getWords(final WordComposer composer, final WordCallback callback,
int[] nextLettersFrequencies);
/** /**
* Checks if the given word occurs in the dictionary * Checks if the given word occurs in the dictionary

View File

@ -27,6 +27,7 @@ public class ExpandableDictionary extends Dictionary {
private char[] mWordBuilder = new char[MAX_WORD_LENGTH]; private char[] mWordBuilder = new char[MAX_WORD_LENGTH];
private int mMaxDepth; private int mMaxDepth;
private int mInputLength; private int mInputLength;
private int[] mNextLettersFrequencies;
public static final int MAX_WORD_LENGTH = 32; public static final int MAX_WORD_LENGTH = 32;
private static final char QUOTE = '\''; private static final char QUOTE = '\'';
@ -116,8 +117,10 @@ 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,
int[] nextLettersFrequencies) {
mInputLength = codes.size(); mInputLength = codes.size();
mNextLettersFrequencies = nextLettersFrequencies;
if (mCodes.length < mInputLength) mCodes = new int[mInputLength][]; if (mCodes.length < mInputLength) mCodes = new int[mInputLength][];
// Cache the codes so that we don't have to lookup an array list // 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++) {
@ -216,6 +219,11 @@ public class ExpandableDictionary extends Dictionary {
if (!callback.addWord(word, 0, depth + 1, freq * snr)) { if (!callback.addWord(word, 0, depth + 1, freq * snr)) {
return; return;
} }
// Add to frequency of next letters for predictive correction
if (mNextLettersFrequencies != null && depth >= inputIndex && skipPos < 0
&& mNextLettersFrequencies.length > word[inputIndex]) {
mNextLettersFrequencies[word[inputIndex]]++;
}
} }
if (children != null) { if (children != null) {
getWordsRec(children, codes, word, depth + 1, completion, snr, inputIndex, getWordsRec(children, codes, word, depth + 1, completion, snr, inputIndex,

View File

@ -1340,6 +1340,8 @@ public class LatinIME extends InputMethodService
private void updateSuggestions() { private void updateSuggestions() {
mSuggestionShouldReplaceCurrentWord = false; mSuggestionShouldReplaceCurrentWord = false;
((LatinKeyboard) mInputView.getKeyboard()).setPreferredLetters(null);
// Check if we have a suggestion engine attached. // Check if we have a suggestion engine attached.
if ((mSuggest == null || !isPredictionOn()) && !mVoiceInputHighlighted) { if ((mSuggest == null || !isPredictionOn()) && !mVoiceInputHighlighted) {
return; return;
@ -1351,6 +1353,10 @@ public class LatinIME extends InputMethodService
} }
List<CharSequence> stringList = mSuggest.getSuggestions(mInputView, mWord, false); List<CharSequence> stringList = mSuggest.getSuggestions(mInputView, mWord, false);
int[] nextLettersFrequencies = mSuggest.getNextLettersFrequencies();
((LatinKeyboard) mInputView.getKeyboard()).setPreferredLetters(nextLettersFrequencies);
boolean correctionAvailable = mSuggest.hasMinimalCorrection(); boolean correctionAvailable = mSuggest.hasMinimalCorrection();
//|| mCorrectionMode == mSuggest.CORRECTION_FULL; //|| mCorrectionMode == mSuggest.CORRECTION_FULL;
CharSequence typedWord = mWord.getTypedWord(); CharSequence typedWord = mWord.getTypedWord();

View File

@ -35,11 +35,15 @@ import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.inputmethodservice.Keyboard; import android.inputmethodservice.Keyboard;
import android.text.TextPaint; import android.text.TextPaint;
import android.util.Log;
import android.view.ViewConfiguration; import android.view.ViewConfiguration;
import android.view.inputmethod.EditorInfo; import android.view.inputmethod.EditorInfo;
public class LatinKeyboard extends Keyboard { public class LatinKeyboard extends Keyboard {
private static final boolean DEBUG_PREFERRED_LETTER = false;
private static final String TAG = "LatinKeyboard";
private Drawable mShiftLockIcon; private Drawable mShiftLockIcon;
private Drawable mShiftLockPreviewIcon; private Drawable mShiftLockPreviewIcon;
private Drawable mOldShiftIcon; private Drawable mOldShiftIcon;
@ -69,6 +73,12 @@ public class LatinKeyboard extends Keyboard {
private boolean mCurrentlyInSpace; private boolean mCurrentlyInSpace;
private SlidingLocaleDrawable mSlidingLocaleIcon; private SlidingLocaleDrawable mSlidingLocaleIcon;
private Rect mBounds = new Rect(); private Rect mBounds = new Rect();
private int[] mPrefLetterFrequencies;
private boolean mPreemptiveCorrection;
private int mPrefLetter;
private int mPrefLetterX;
private int mPrefLetterY;
private int mPrefDistance;
private int mExtensionResId; private int mExtensionResId;
@ -79,6 +89,8 @@ public class LatinKeyboard extends Keyboard {
private int mShiftState = SHIFT_OFF; private int mShiftState = SHIFT_OFF;
private static final float SPACEBAR_DRAG_THRESHOLD = 0.8f; private static final float SPACEBAR_DRAG_THRESHOLD = 0.8f;
private static final float OVERLAP_PERCENTAGE_LOW_PROB = 0.70f;
private static final float OVERLAP_PERCENTAGE_HIGH_PROB = 0.85f;
static int sSpacebarVerticalCorrection; static int sSpacebarVerticalCorrection;
@ -409,9 +421,18 @@ public class LatinKeyboard extends Keyboard {
return mCurrentlyInSpace; return mCurrentlyInSpace;
} }
void setPreferredLetters(int[] frequencies) {
mPrefLetterFrequencies = frequencies;
mPrefLetter = 0;
}
void keyReleased() { void keyReleased() {
mCurrentlyInSpace = false; mCurrentlyInSpace = false;
mSpaceDragLastDiff = 0; mSpaceDragLastDiff = 0;
mPrefLetter = 0;
mPrefLetterX = 0;
mPrefLetterY = 0;
mPrefDistance = Integer.MAX_VALUE;
if (mSpaceKey != null) { if (mSpaceKey != null) {
updateLocaleDrag(Integer.MAX_VALUE); updateLocaleDrag(Integer.MAX_VALUE);
} }
@ -448,6 +469,79 @@ public class LatinKeyboard extends Keyboard {
return insideSpace; return insideSpace;
} }
} }
} else if (mPrefLetterFrequencies != null) {
// New coordinate? Reset
if (mPrefLetterX != x || mPrefLetterY != y) {
mPrefLetter = 0;
mPrefDistance = Integer.MAX_VALUE;
}
// Handle preferred next letter
final int[] pref = mPrefLetterFrequencies;
if (mPrefLetter > 0) {
if (DEBUG_PREFERRED_LETTER && mPrefLetter == code
&& !key.isInsideSuper(x, y)) {
Log.d(TAG, "CORRECTED !!!!!!");
}
return mPrefLetter == code;
} else {
final boolean inside = key.isInsideSuper(x, y);
int[] nearby = getNearestKeys(x, y);
List<Key> nearbyKeys = getKeys();
if (inside) {
// If it's a preferred letter
if (inPrefList(code, pref)) {
// Check if its frequency is much lower than a nearby key
mPrefLetter = code;
mPrefLetterX = x;
mPrefLetterY = y;
for (int i = 0; i < nearby.length; i++) {
Key k = nearbyKeys.get(nearby[i]);
if (k != key && inPrefList(k.codes[0], pref)) {
final int dist = distanceFrom(k, x, y);
if (dist < (int) (k.width * OVERLAP_PERCENTAGE_LOW_PROB) &&
(pref[k.codes[0]] > pref[mPrefLetter] * 3)) {
mPrefLetter = k.codes[0];
mPrefDistance = dist;
if (DEBUG_PREFERRED_LETTER) {
Log.d(TAG, "CORRECTED ALTHOUGH PREFERRED !!!!!!");
}
break;
}
}
}
return mPrefLetter == code;
}
}
// Get the surrounding keys and intersect with the preferred list
// For all in the intersection
// if distance from touch point is within a reasonable distance
// make this the pref letter
// If no pref letter
// return inside;
// else return thiskey == prefletter;
for (int i = 0; i < nearby.length; i++) {
Key k = nearbyKeys.get(nearby[i]);
if (inPrefList(k.codes[0], pref)) {
final int dist = distanceFrom(k, x, y);
if (dist < (int) (k.width * OVERLAP_PERCENTAGE_HIGH_PROB)
&& dist < mPrefDistance) {
mPrefLetter = k.codes[0];
mPrefLetterX = x;
mPrefLetterY = y;
mPrefDistance = dist;
}
}
}
// Didn't find any
if (mPrefLetter == 0) {
return inside;
} else {
return mPrefLetter == code;
}
}
} }
// Lock into the spacebar // Lock into the spacebar
@ -456,6 +550,19 @@ public class LatinKeyboard extends Keyboard {
return key.isInsideSuper(x, y); return key.isInsideSuper(x, y);
} }
private boolean inPrefList(int code, int[] pref) {
if (code < pref.length && code >= 0) return pref[code] > 0;
return false;
}
private int distanceFrom(Key k, int x, int y) {
if (y > k.y && y < k.y + k.height) {
return Math.abs(k.x + k.width / 2 - x);
} else {
return Integer.MAX_VALUE;
}
}
@Override @Override
public int[] getNearestKeys(int x, int y) { public int[] getNearestKeys(int x, int y) {
if (mCurrentlyInSpace) { if (mCurrentlyInSpace) {
@ -512,7 +619,8 @@ public class LatinKeyboard extends Keyboard {
*/ */
@Override @Override
public boolean isInside(int x, int y) { public boolean isInside(int x, int y) {
return LatinKeyboard.this.isInside(this, x, y); boolean result = LatinKeyboard.this.isInside(this, x, y);
return result;
} }
boolean isInsideSuper(int x, int y) { boolean isInsideSuper(int x, int y) {

View File

@ -20,6 +20,7 @@ import java.util.List;
import android.content.Context; import android.content.Context;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Paint;
import android.inputmethodservice.Keyboard; import android.inputmethodservice.Keyboard;
import android.inputmethodservice.KeyboardView; import android.inputmethodservice.KeyboardView;
import android.inputmethodservice.Keyboard.Key; import android.inputmethodservice.Keyboard.Key;
@ -80,6 +81,11 @@ public class LatinKeyboardView extends KeyboardView {
@Override @Override
public boolean onTouchEvent(MotionEvent me) { public boolean onTouchEvent(MotionEvent me) {
LatinKeyboard keyboard = (LatinKeyboard) getKeyboard(); LatinKeyboard keyboard = (LatinKeyboard) getKeyboard();
if (DEBUG_LINE) {
mLastX = (int) me.getX();
mLastY = (int) me.getY();
invalidate();
}
// Reset any bounding box controls in the keyboard // Reset any bounding box controls in the keyboard
if (me.getAction() == MotionEvent.ACTION_DOWN) { if (me.getAction() == MotionEvent.ACTION_DOWN) {
keyboard.keyReleased(); keyboard.keyReleased();
@ -203,6 +209,7 @@ public class LatinKeyboardView extends KeyboardView {
/**************************** INSTRUMENTATION *******************************/ /**************************** INSTRUMENTATION *******************************/
static final boolean DEBUG_AUTO_PLAY = false; static final boolean DEBUG_AUTO_PLAY = false;
static final boolean DEBUG_LINE = false;
private static final int MSG_TOUCH_DOWN = 1; private static final int MSG_TOUCH_DOWN = 1;
private static final int MSG_TOUCH_UP = 2; private static final int MSG_TOUCH_UP = 2;
@ -213,6 +220,9 @@ public class LatinKeyboardView extends KeyboardView {
private boolean mDownDelivered; private boolean mDownDelivered;
private Key[] mAsciiKeys = new Key[256]; private Key[] mAsciiKeys = new Key[256];
private boolean mPlaying; private boolean mPlaying;
private int mLastX;
private int mLastY;
private Paint mPaint;
@Override @Override
public void setKeyboard(Keyboard k) { public void setKeyboard(Keyboard k) {
@ -309,5 +319,14 @@ public class LatinKeyboardView extends KeyboardView {
mHandler2.sendEmptyMessageDelayed(MSG_TOUCH_DOWN, 20); mHandler2.sendEmptyMessageDelayed(MSG_TOUCH_DOWN, 20);
} }
} }
if (DEBUG_LINE) {
if (mPaint == null) {
mPaint = new Paint();
mPaint.setColor(0x80FFFFFF);
mPaint.setAntiAlias(false);
}
c.drawLine(mLastX, 0, mLastX, getHeight(), mPaint);
c.drawLine(0, mLastY, getWidth(), mLastY, mPaint);
}
} }
} }

View File

@ -52,6 +52,12 @@ public class Suggest implements Dictionary.WordCallback {
private int mPrefMaxSuggestions = 12; private int mPrefMaxSuggestions = 12;
private int[] mPriorities = new int[mPrefMaxSuggestions]; private int[] mPriorities = new int[mPrefMaxSuggestions];
// Handle predictive correction for only the first 1280 characters for performance reasons
// If we support scripts that need latin characters beyond that, we should probably use some
// kind of a sparse array or language specific list with a mapping lookup table.
// 1280 is the size of the BASE_CHARS array in ExpandableDictionary, which is a basic set of
// latin characters.
private int[] mNextLettersFrequencies = new int[1280];
private ArrayList<CharSequence> mSuggestions = new ArrayList<CharSequence>(); private ArrayList<CharSequence> mSuggestions = new ArrayList<CharSequence>();
private ArrayList<CharSequence> mStringPool = new ArrayList<CharSequence>(); private ArrayList<CharSequence> mStringPool = new ArrayList<CharSequence>();
private boolean mHaveCorrection; private boolean mHaveCorrection;
@ -162,7 +168,8 @@ public class Suggest implements Dictionary.WordCallback {
mCapitalize = wordComposer.isCapitalized(); mCapitalize = wordComposer.isCapitalized();
collectGarbage(); collectGarbage();
Arrays.fill(mPriorities, 0); Arrays.fill(mPriorities, 0);
Arrays.fill(mNextLettersFrequencies, 0);
// Save a lowercase version of the original word // Save a lowercase version of the original word
mOriginalWord = wordComposer.getTypedWord(); mOriginalWord = wordComposer.getTypedWord();
if (mOriginalWord != null) { if (mOriginalWord != null) {
@ -175,17 +182,17 @@ public class Suggest implements Dictionary.WordCallback {
if (wordComposer.size() > 1) { if (wordComposer.size() > 1) {
if (mUserDictionary != null || mContactsDictionary != null) { if (mUserDictionary != null || mContactsDictionary != null) {
if (mUserDictionary != null) { if (mUserDictionary != null) {
mUserDictionary.getWords(wordComposer, this); mUserDictionary.getWords(wordComposer, this, mNextLettersFrequencies);
} }
if (mContactsDictionary != null) { if (mContactsDictionary != null) {
mContactsDictionary.getWords(wordComposer, this); mContactsDictionary.getWords(wordComposer, this, mNextLettersFrequencies);
} }
if (mSuggestions.size() > 0 && isValidWord(mOriginalWord)) { if (mSuggestions.size() > 0 && isValidWord(mOriginalWord)) {
mHaveCorrection = true; mHaveCorrection = true;
} }
} }
mMainDict.getWords(wordComposer, this); mMainDict.getWords(wordComposer, this, mNextLettersFrequencies);
if (mCorrectionMode == CORRECTION_FULL && mSuggestions.size() > 0) { if (mCorrectionMode == CORRECTION_FULL && mSuggestions.size() > 0) {
mHaveCorrection = true; mHaveCorrection = true;
} }
@ -229,6 +236,10 @@ public class Suggest implements Dictionary.WordCallback {
return mSuggestions; return mSuggestions;
} }
public int[] getNextLettersFrequencies() {
return mNextLettersFrequencies;
}
private void removeDupes() { private void removeDupes() {
final ArrayList<CharSequence> suggestions = mSuggestions; final ArrayList<CharSequence> suggestions = mSuggestions;
if (suggestions.size() < 2) return; if (suggestions.size() < 2) return;

View File

@ -94,9 +94,10 @@ public class UserDictionary extends ExpandableDictionary {
} }
@Override @Override
public synchronized void getWords(final WordComposer codes, final WordCallback callback) { public synchronized void getWords(final WordComposer codes, final WordCallback callback,
int[] nextLettersFrequencies) {
if (mRequiresReload) loadDictionary(); if (mRequiresReload) loadDictionary();
super.getWords(codes, callback); super.getWords(codes, callback, nextLettersFrequencies);
} }
@Override @Override