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
parent
8fa317a61a
commit
1b62ff1a3d
|
@ -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}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,6 +168,7 @@ 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();
|
||||||
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue