Merge "Remove next letters frequency handling"

main
Tadashi G. Takaoka 2011-02-16 21:38:44 -08:00 committed by Android (Google) Code Review
commit 80275c7d1c
18 changed files with 137 additions and 236 deletions

View File

@ -22,6 +22,7 @@ import java.util.List;
public abstract class KeyDetector { public abstract class KeyDetector {
public static final int NOT_A_KEY = -1; public static final int NOT_A_KEY = -1;
public static final int NOT_A_CODE = -1;
protected Keyboard mKeyboard; protected Keyboard mKeyboard;
@ -105,10 +106,10 @@ public abstract class KeyDetector {
* *
* @param x The x-coordinate of a touch point * @param x The x-coordinate of a touch point
* @param y The y-coordinate of a touch point * @param y The y-coordinate of a touch point
* @param allKeys All nearby key indices are returned in this array * @param allCodes All nearby key code except functional key are returned in this array
* @return The nearest key index * @return The nearest key index
*/ */
abstract public int getKeyIndexAndNearbyCodes(int x, int y, int[] allKeys); abstract public int getKeyIndexAndNearbyCodes(int x, int y, final int[] allCodes);
/** /**
* Compute the most common key width in order to use it as proximity key detection threshold. * Compute the most common key width in order to use it as proximity key detection threshold.
@ -116,14 +117,14 @@ public abstract class KeyDetector {
* @param keyboard The keyboard to compute the most common key width * @param keyboard The keyboard to compute the most common key width
* @return The most common key width in the keyboard * @return The most common key width in the keyboard
*/ */
public static int getMostCommonKeyWidth(Keyboard keyboard) { public static int getMostCommonKeyWidth(final Keyboard keyboard) {
if (keyboard == null) return 0; if (keyboard == null) return 0;
final List<Key> keys = keyboard.getKeys(); final List<Key> keys = keyboard.getKeys();
if (keys == null || keys.size() == 0) return 0; if (keys == null || keys.size() == 0) return 0;
final HashMap<Integer, Integer> histogram = new HashMap<Integer, Integer>(); final HashMap<Integer, Integer> histogram = new HashMap<Integer, Integer>();
int maxCount = 0; int maxCount = 0;
int mostCommonWidth = 0; int mostCommonWidth = 0;
for (Key key : keys) { for (final Key key : keys) {
final Integer width = key.mWidth + key.mGap; final Integer width = key.mWidth + key.mGap;
Integer count = histogram.get(width); Integer count = histogram.get(width);
if (count == null) if (count == null)

View File

@ -288,12 +288,6 @@ public class KeyboardSwitcher implements SharedPreferences.OnSharedPreferenceCha
return null; return null;
} }
public void setPreferredLetters(int[] frequencies) {
LatinKeyboard latinKeyboard = getLatinKeyboard();
if (latinKeyboard != null)
latinKeyboard.setPreferredLetters(frequencies);
}
public void keyReleased() { public void keyReleased() {
LatinKeyboard latinKeyboard = getLatinKeyboard(); LatinKeyboard latinKeyboard = getLatinKeyboard();
if (latinKeyboard != null) if (latinKeyboard != null)

View File

@ -21,6 +21,7 @@ import com.android.inputmethod.latin.SubtypeSwitcher;
import android.content.Context; import android.content.Context;
import android.content.res.Resources; import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.content.res.TypedArray; import android.content.res.TypedArray;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Canvas; import android.graphics.Canvas;
@ -31,17 +32,12 @@ import android.graphics.PorterDuff;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.util.Log;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
// TODO: We should remove this class // TODO: We should remove this class
public class LatinKeyboard extends Keyboard { public class LatinKeyboard extends Keyboard {
private static final boolean DEBUG_PREFERRED_LETTER = false;
private static final String TAG = "LatinKeyboard";
public static final int OPACITY_FULLY_OPAQUE = 255; public static final int OPACITY_FULLY_OPAQUE = 255;
private static final int SPACE_LED_LENGTH_PERCENT = 80; private static final int SPACE_LED_LENGTH_PERCENT = 80;
@ -69,15 +65,7 @@ public class LatinKeyboard extends Keyboard {
private final Drawable mEnabledShortcutIcon; private final Drawable mEnabledShortcutIcon;
private final Drawable mDisabledShortcutIcon; private final Drawable mDisabledShortcutIcon;
private int[] mPrefLetterFrequencies;
private int mPrefLetter;
private int mPrefLetterX;
private int mPrefLetterY;
private int mPrefDistance;
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;
// Minimum width of space key preview (proportional to keyboard width) // Minimum width of space key preview (proportional to keyboard width)
private static final float SPACEBAR_POPUP_MIN_RATIO = 0.4f; private static final float SPACEBAR_POPUP_MIN_RATIO = 0.4f;
// Height in space key the language name will be drawn. (proportional to space key height) // Height in space key the language name will be drawn. (proportional to space key height)
@ -265,7 +253,7 @@ public class LatinKeyboard extends Keyboard {
final boolean allowVariableTextSize = true; final boolean allowVariableTextSize = true;
final String language = layoutSpacebar(paint, subtypeSwitcher.getInputLocale(), final String language = layoutSpacebar(paint, subtypeSwitcher.getInputLocale(),
mButtonArrowLeftIcon, mButtonArrowRightIcon, width, height, mButtonArrowLeftIcon, mButtonArrowRightIcon, width, height,
getTextSizeFromTheme(textStyle, defaultTextSize), getTextSizeFromTheme(mContext.getTheme(), textStyle, defaultTextSize),
allowVariableTextSize); allowVariableTextSize);
// Draw language text with shadow // Draw language text with shadow
@ -334,18 +322,9 @@ public class LatinKeyboard extends Keyboard {
return mSpaceDragLastDiff > 0 ? 1 : -1; return mSpaceDragLastDiff > 0 ? 1 : -1;
} }
public void setPreferredLetters(int[] frequencies) {
mPrefLetterFrequencies = frequencies;
mPrefLetter = 0;
}
public void keyReleased() { public 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);
} }
@ -381,80 +360,6 @@ public class LatinKeyboard extends Keyboard {
return isOnSpace; return isOnSpace;
} }
} }
} 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) {
if (mPrefLetter == code && !key.isOnKey(x, y)) {
Log.d(TAG, "CORRECTED !!!!!!");
}
}
return mPrefLetter == code;
} else {
final boolean isOnKey = key.isOnKey(x, y);
int[] nearby = getNearestKeys(x, y);
List<Key> nearbyKeys = getKeys();
if (isOnKey) {
// 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.mCode, pref)) {
final int dist = distanceFrom(k, x, y);
if (dist < (int) (k.mWidth * OVERLAP_PERCENTAGE_LOW_PROB) &&
(pref[k.mCode] > pref[mPrefLetter] * 3)) {
mPrefLetter = k.mCode;
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.mCode, pref)) {
final int dist = distanceFrom(k, x, y);
if (dist < (int) (k.mWidth * OVERLAP_PERCENTAGE_HIGH_PROB)
&& dist < mPrefDistance) {
mPrefLetter = k.mCode;
mPrefLetterX = x;
mPrefLetterY = y;
mPrefDistance = dist;
}
}
}
// Didn't find any
if (mPrefLetter == 0) {
return isOnKey;
} else {
return mPrefLetter == code;
}
}
} }
// Lock into the spacebar // Lock into the spacebar
@ -463,19 +368,6 @@ public class LatinKeyboard extends Keyboard {
return key.isOnKey(x, y); return key.isOnKey(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.mY && y < k.mY + k.mHeight) {
return Math.abs(k.mX + k.mWidth / 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) {
@ -487,8 +379,8 @@ public class LatinKeyboard extends Keyboard {
} }
} }
private int getTextSizeFromTheme(int style, int defValue) { private static int getTextSizeFromTheme(Theme theme, int style, int defValue) {
TypedArray array = mContext.getTheme().obtainStyledAttributes( TypedArray array = theme.obtainStyledAttributes(
style, new int[] { android.R.attr.textSize }); style, new int[] { android.R.attr.textSize });
int textSize = array.getDimensionPixelSize(array.getResourceId(0, 0), defValue); int textSize = array.getDimensionPixelSize(array.getResourceId(0, 0), defValue);
return textSize; return textSize;

View File

@ -35,24 +35,24 @@ public class MiniKeyboardKeyDetector extends KeyDetector {
} }
@Override @Override
public int getKeyIndexAndNearbyCodes(int x, int y, int[] allKeys) { public int getKeyIndexAndNearbyCodes(int x, int y, final int[] allCodes) {
final Key[] keys = getKeys(); final Key[] keys = getKeys();
final int touchX = getTouchX(x); final int touchX = getTouchX(x);
final int touchY = getTouchY(y); final int touchY = getTouchY(y);
int closestKeyIndex = NOT_A_KEY; int nearestIndex = NOT_A_KEY;
int closestKeyDist = (y < 0) ? mSlideAllowanceSquareTop : mSlideAllowanceSquare; int nearestDist = (y < 0) ? mSlideAllowanceSquareTop : mSlideAllowanceSquare;
final int keyCount = keys.length; final int keyCount = keys.length;
for (int index = 0; index < keyCount; index++) { for (int index = 0; index < keyCount; index++) {
final int dist = keys[index].squaredDistanceToEdge(touchX, touchY); final int dist = keys[index].squaredDistanceToEdge(touchX, touchY);
if (dist < closestKeyDist) { if (dist < nearestDist) {
closestKeyIndex = index; nearestIndex = index;
closestKeyDist = dist; nearestDist = dist;
} }
} }
if (allKeys != null && closestKeyIndex != NOT_A_KEY) if (allCodes != null && nearestIndex != NOT_A_KEY)
allKeys[0] = keys[closestKeyIndex].mCode; allCodes[0] = keys[nearestIndex].mCode;
return closestKeyIndex; return nearestIndex;
} }
} }

View File

@ -16,49 +16,106 @@
package com.android.inputmethod.keyboard; package com.android.inputmethod.keyboard;
import android.util.Log;
import java.util.Arrays; import java.util.Arrays;
public class ProximityKeyDetector extends KeyDetector { public class ProximityKeyDetector extends KeyDetector {
private static final String TAG = ProximityKeyDetector.class.getSimpleName();
private static final boolean DEBUG = false;
private static final int MAX_NEARBY_KEYS = 12; private static final int MAX_NEARBY_KEYS = 12;
// working area // working area
private int[] mDistances = new int[MAX_NEARBY_KEYS]; private final int[] mDistances = new int[MAX_NEARBY_KEYS];
private final int[] mIndices = new int[MAX_NEARBY_KEYS];
@Override @Override
protected int getMaxNearbyKeys() { protected int getMaxNearbyKeys() {
return MAX_NEARBY_KEYS; return MAX_NEARBY_KEYS;
} }
private void initializeNearbyKeys() {
Arrays.fill(mDistances, Integer.MAX_VALUE);
Arrays.fill(mIndices, NOT_A_KEY);
}
/**
* Insert the key into nearby keys buffer and sort nearby keys by ascending order of distance.
*
* @param keyIndex index of the key.
* @param distance distance between the key's edge and user touched point.
* @return order of the key in the nearby buffer, 0 if it is the nearest key.
*/
private int sortNearbyKeys(int keyIndex, int distance) {
final int[] distances = mDistances;
final int[] indices = mIndices;
for (int insertPos = 0; insertPos < distances.length; insertPos++) {
if (distance < distances[insertPos]) {
final int nextPos = insertPos + 1;
if (nextPos < distances.length) {
System.arraycopy(distances, insertPos, distances, nextPos,
distances.length - nextPos);
System.arraycopy(indices, insertPos, indices, nextPos,
indices.length - nextPos);
}
distances[insertPos] = distance;
indices[insertPos] = keyIndex;
return insertPos;
}
}
return distances.length;
}
private void getNearbyKeyCodes(final int[] allCodes) {
final Key[] keys = getKeys();
final int[] indices = mIndices;
// allCodes[0] should always have the key code even if it is a non-letter key.
if (indices[0] == NOT_A_KEY) {
allCodes[0] = NOT_A_CODE;
return;
}
int numCodes = 0;
for (int j = 0; j < indices.length && numCodes < allCodes.length; j++) {
final int index = indices[j];
if (index == NOT_A_KEY)
break;
final int code = keys[index].mCode;
// filter out a non-letter key from nearby keys
if (code < Keyboard.CODE_SPACE)
continue;
allCodes[numCodes++] = code;
}
}
@Override @Override
public int getKeyIndexAndNearbyCodes(int x, int y, int[] allKeys) { public int getKeyIndexAndNearbyCodes(int x, int y, final int[] allCodes) {
final Key[] keys = getKeys(); final Key[] keys = getKeys();
final int touchX = getTouchX(x); final int touchX = getTouchX(x);
final int touchY = getTouchY(y); final int touchY = getTouchY(y);
initializeNearbyKeys();
int primaryIndex = NOT_A_KEY; int primaryIndex = NOT_A_KEY;
final int[] distances = mDistances;
Arrays.fill(distances, Integer.MAX_VALUE);
for (final int index : mKeyboard.getNearestKeys(touchX, touchY)) { for (final int index : mKeyboard.getNearestKeys(touchX, touchY)) {
final Key key = keys[index]; final Key key = keys[index];
final boolean isInside = key.isInside(touchX, touchY); final boolean isInside = key.isInside(touchX, touchY);
if (isInside) final int distance = key.squaredDistanceToEdge(touchX, touchY);
if (isInside || (mProximityCorrectOn && distance < mProximityThresholdSquare)) {
final int insertedPosition = sortNearbyKeys(index, distance);
if (insertedPosition == 0 && isInside)
primaryIndex = index; primaryIndex = index;
final int dist = key.squaredDistanceToEdge(touchX, touchY);
if (isInside || (mProximityCorrectOn && dist < mProximityThresholdSquare)) {
if (allKeys == null) continue;
// Find insertion point
for (int j = 0; j < distances.length; j++) {
if (distances[j] > dist) {
final int nextPos = j + 1;
System.arraycopy(distances, j, distances, nextPos,
distances.length - nextPos);
System.arraycopy(allKeys, j, allKeys, nextPos,
allKeys.length - nextPos);
distances[j] = dist;
allKeys[j] = key.mCode;
break;
} }
} }
if (allCodes != null && allCodes.length > 0) {
getNearbyKeyCodes(allCodes);
if (DEBUG) {
Log.d(TAG, "x=" + x + " y=" + y
+ " primary="
+ (primaryIndex == NOT_A_KEY ? "none" : keys[primaryIndex].mCode)
+ " codes=" + Arrays.toString(allCodes));
} }
} }

View File

@ -118,8 +118,7 @@ public class BinaryDictionary extends Dictionary {
private native void closeNative(int dict); private native void closeNative(int dict);
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[] nextLettersFrequencies, int nextLettersSize);
private native int getBigramsNative(int dict, char[] prevWord, int prevWordLength, private native int getBigramsNative(int dict, char[] prevWord, int prevWordLength,
int[] inputCodes, int inputCodesLength, char[] outputChars, int[] frequencies, int[] inputCodes, int inputCodesLength, char[] outputChars, int[] frequencies,
int maxWordLength, int maxBigrams, int maxAlternatives); int maxWordLength, int maxBigrams, int maxAlternatives);
@ -133,7 +132,7 @@ public class BinaryDictionary extends Dictionary {
@Override @Override
public void getBigrams(final WordComposer codes, final CharSequence previousWord, public void getBigrams(final WordComposer codes, final CharSequence previousWord,
final WordCallback callback, int[] nextLettersFrequencies) { final WordCallback callback) {
if (mNativeDict == 0) return; if (mNativeDict == 0) return;
char[] chars = previousWord.toString().toCharArray(); char[] chars = previousWord.toString().toCharArray();
@ -165,15 +164,14 @@ 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) {
if (mNativeDict == 0) return; if (mNativeDict == 0) return;
final int codesSize = codes.size(); final int codesSize = codes.size();
// Won't deal with really long words. // Won't deal with really long words.
if (codesSize > MAX_WORD_LENGTH - 1) return; if (codesSize > MAX_WORD_LENGTH - 1) return;
Arrays.fill(mInputCodes, -1); Arrays.fill(mInputCodes, WordComposer.NOT_A_CODE);
for (int i = 0; i < codesSize; i++) { for (int i = 0; i < codesSize; i++) {
int[] alternatives = codes.getCodesAt(i); int[] alternatives = codes.getCodesAt(i);
System.arraycopy(alternatives, 0, mInputCodes, i * MAX_ALTERNATIVES, System.arraycopy(alternatives, 0, mInputCodes, i * MAX_ALTERNATIVES,
@ -183,8 +181,7 @@ public class BinaryDictionary extends Dictionary {
Arrays.fill(mFrequencies, 0); Arrays.fill(mFrequencies, 0);
int count = getSuggestionsNative(mNativeDict, mInputCodes, codesSize, mOutputChars, int count = getSuggestionsNative(mNativeDict, mInputCodes, codesSize, mOutputChars,
mFrequencies, nextLettersFrequencies, mFrequencies);
nextLettersFrequencies != null ? nextLettersFrequencies.length : 0);
for (int j = 0; j < count; ++j) { for (int j = 0; j < count; ++j) {
if (mFrequencies[j] < 1) break; if (mFrequencies[j] < 1) break;

View File

@ -61,14 +61,9 @@ public abstract 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);
/** /**
* Searches for pairs in the bigram dictionary that matches the previous word and all the * Searches for pairs in the bigram dictionary that matches the previous word and all the
@ -76,13 +71,9 @@ public abstract class Dictionary {
* @param composer the key sequence to match * @param composer the key sequence to match
* @param previousWord the word before * @param previousWord the word before
* @param callback the callback object to send possible word following previous word * @param callback the callback object to send possible word following previous word
* @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.
*/ */
public void getBigrams(final WordComposer composer, final CharSequence previousWord, public void getBigrams(final WordComposer composer, final CharSequence previousWord,
final WordCallback callback, int[] nextLettersFrequencies) { final WordCallback callback) {
// empty base implementation // empty base implementation
} }

View File

@ -37,7 +37,6 @@ public class ExpandableDictionary extends Dictionary {
private int mDicTypeId; private int mDicTypeId;
private int mMaxDepth; private int mMaxDepth;
private int mInputLength; private int mInputLength;
private int[] mNextLettersFrequencies;
private StringBuilder sb = new StringBuilder(MAX_WORD_LENGTH); private StringBuilder sb = new StringBuilder(MAX_WORD_LENGTH);
private static final char QUOTE = '\''; private static final char QUOTE = '\'';
@ -191,8 +190,7 @@ 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) {
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) startDictionaryLoadingTaskLocked(); if (mRequiresReload) startDictionaryLoadingTaskLocked();
@ -201,7 +199,6 @@ public class ExpandableDictionary extends Dictionary {
} }
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++) {
@ -282,11 +279,6 @@ public class ExpandableDictionary extends Dictionary {
DataType.UNIGRAM)) { DataType.UNIGRAM)) {
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,
@ -427,7 +419,7 @@ public class ExpandableDictionary extends Dictionary {
@Override @Override
public void getBigrams(final WordComposer codes, final CharSequence previousWord, public void getBigrams(final WordComposer codes, final CharSequence previousWord,
final WordCallback callback, int[] nextLettersFrequencies) { final WordCallback callback) {
if (!reloadDictionaryIfRequired()) { if (!reloadDictionaryIfRequired()) {
runReverseLookUp(previousWord, callback); runReverseLookUp(previousWord, callback);
} }
@ -516,7 +508,7 @@ public class ExpandableDictionary extends Dictionary {
} }
} }
static char toLowerCase(char c) { private static char toLowerCase(char c) {
char baseChar = c; char baseChar = c;
if (c < BASE_CHARS.length) { if (c < BASE_CHARS.length) {
baseChar = BASE_CHARS[c]; baseChar = BASE_CHARS[c];
@ -535,7 +527,7 @@ public class ExpandableDictionary extends Dictionary {
* if c is not a combined character, or the base character if it * if c is not a combined character, or the base character if it
* is combined. * is combined.
*/ */
static final char BASE_CHARS[] = { private static final char BASE_CHARS[] = {
0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,

View File

@ -1500,8 +1500,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
} }
public void updateSuggestions() { public void updateSuggestions() {
mKeyboardSwitcher.setPreferredLetters(null);
// Check if we have a suggestion engine attached. // Check if we have a suggestion engine attached.
if ((mSuggest == null || !isSuggestionsRequested()) if ((mSuggest == null || !isSuggestionsRequested())
&& !mVoiceConnector.isVoiceInputHighlighted()) { && !mVoiceConnector.isVoiceInputHighlighted()) {
@ -1520,7 +1518,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
} }
private void showCorrections(WordAlternatives alternatives) { private void showCorrections(WordAlternatives alternatives) {
mKeyboardSwitcher.setPreferredLetters(null);
SuggestedWords.Builder builder = alternatives.getAlternatives(); SuggestedWords.Builder builder = alternatives.getAlternatives();
builder.setTypedWordValid(false).setHasMinimalSuggestion(false); builder.setTypedWordValid(false).setHasMinimalSuggestion(false);
showSuggestions(builder.build(), alternatives.getOriginalWord()); showSuggestions(builder.build(), alternatives.getOriginalWord());
@ -1533,9 +1530,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
SuggestedWords.Builder builder = mSuggest.getSuggestedWordBuilder( SuggestedWords.Builder builder = mSuggest.getSuggestedWordBuilder(
mKeyboardSwitcher.getInputView(), word, prevWord); mKeyboardSwitcher.getInputView(), word, prevWord);
int[] nextLettersFrequencies = mSuggest.getNextLettersFrequencies();
mKeyboardSwitcher.setPreferredLetters(nextLettersFrequencies);
boolean correctionAvailable = !mInputTypeNoAutoCorrect && !mJustReverted boolean correctionAvailable = !mInputTypeNoAutoCorrect && !mJustReverted
&& mSuggest.hasAutoCorrection(); && mSuggest.hasAutoCorrection();
final CharSequence typedWord = word.getTypedWord(); final CharSequence typedWord = word.getTypedWord();
@ -1704,7 +1698,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
saveWordInHistory(suggestion); saveWordInHistory(suggestion);
mHasValidSuggestions = false; mHasValidSuggestions = false;
mCommittedLength = suggestion.length(); mCommittedLength = suggestion.length();
switcher.setPreferredLetters(null);
} }
/** /**

View File

@ -87,12 +87,6 @@ public class Suggest implements Dictionary.WordCallback {
private int[] mPriorities = new int[mPrefMaxSuggestions]; private int[] mPriorities = new int[mPrefMaxSuggestions];
private int[] mBigramPriorities = new int[PREF_MAX_BIGRAMS]; private int[] mBigramPriorities = new int[PREF_MAX_BIGRAMS];
// 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>();
ArrayList<CharSequence> mBigramSuggestions = new ArrayList<CharSequence>(); ArrayList<CharSequence> mBigramSuggestions = new ArrayList<CharSequence>();
private ArrayList<CharSequence> mStringPool = new ArrayList<CharSequence>(); private ArrayList<CharSequence> mStringPool = new ArrayList<CharSequence>();
@ -216,7 +210,6 @@ public class Suggest implements Dictionary.WordCallback {
mIsAllUpperCase = wordComposer.isAllUpperCase(); mIsAllUpperCase = wordComposer.isAllUpperCase();
collectGarbage(mSuggestions, mPrefMaxSuggestions); collectGarbage(mSuggestions, mPrefMaxSuggestions);
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
CharSequence typedWord = wordComposer.getTypedWord(); CharSequence typedWord = wordComposer.getTypedWord();
@ -244,16 +237,13 @@ public class Suggest implements Dictionary.WordCallback {
prevWordForBigram = lowerPrevWord; prevWordForBigram = lowerPrevWord;
} }
if (mUserBigramDictionary != null) { if (mUserBigramDictionary != null) {
mUserBigramDictionary.getBigrams(wordComposer, prevWordForBigram, this, mUserBigramDictionary.getBigrams(wordComposer, prevWordForBigram, this);
mNextLettersFrequencies);
} }
if (mContactsDictionary != null) { if (mContactsDictionary != null) {
mContactsDictionary.getBigrams(wordComposer, prevWordForBigram, this, mContactsDictionary.getBigrams(wordComposer, prevWordForBigram, this);
mNextLettersFrequencies);
} }
if (mMainDict != null) { if (mMainDict != null) {
mMainDict.getBigrams(wordComposer, prevWordForBigram, this, mMainDict.getBigrams(wordComposer, prevWordForBigram, this);
mNextLettersFrequencies);
} }
char currentChar = wordComposer.getTypedWord().charAt(0); char currentChar = wordComposer.getTypedWord().charAt(0);
char currentCharUpper = Character.toUpperCase(currentChar); char currentCharUpper = Character.toUpperCase(currentChar);
@ -278,10 +268,10 @@ public class Suggest implements Dictionary.WordCallback {
// At second character typed, search the unigrams (scores being affected by bigrams) // At second character typed, search the unigrams (scores being affected by bigrams)
if (mUserDictionary != null || mContactsDictionary != null) { if (mUserDictionary != null || mContactsDictionary != null) {
if (mUserDictionary != null) { if (mUserDictionary != null) {
mUserDictionary.getWords(wordComposer, this, mNextLettersFrequencies); mUserDictionary.getWords(wordComposer, this);
} }
if (mContactsDictionary != null) { if (mContactsDictionary != null) {
mContactsDictionary.getWords(wordComposer, this, mNextLettersFrequencies); mContactsDictionary.getWords(wordComposer, this);
} }
if (mSuggestions.size() > 0 && isValidWord(typedWord) if (mSuggestions.size() > 0 && isValidWord(typedWord)
@ -293,7 +283,7 @@ public class Suggest implements Dictionary.WordCallback {
mHasAutoCorrection = true; mHasAutoCorrection = true;
} }
} }
if (mMainDict != null) mMainDict.getWords(wordComposer, this, mNextLettersFrequencies); if (mMainDict != null) mMainDict.getWords(wordComposer, this);
if ((mCorrectionMode == CORRECTION_FULL || mCorrectionMode == CORRECTION_FULL_BIGRAM) if ((mCorrectionMode == CORRECTION_FULL || mCorrectionMode == CORRECTION_FULL_BIGRAM)
&& mSuggestions.size() > 0 && mPriorities.length > 0) { && mSuggestions.size() > 0 && mPriorities.length > 0) {
// TODO: when the normalized score of the first suggestion is nearly equals to // TODO: when the normalized score of the first suggestion is nearly equals to
@ -388,10 +378,6 @@ public class Suggest implements Dictionary.WordCallback {
} }
} }
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

@ -126,9 +126,8 @@ 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) { super.getWords(codes, callback);
super.getWords(codes, callback, nextLettersFrequencies);
} }
@Override @Override

View File

@ -16,12 +16,16 @@
package com.android.inputmethod.latin; package com.android.inputmethod.latin;
import com.android.inputmethod.keyboard.KeyDetector;
import java.util.ArrayList; import java.util.ArrayList;
/** /**
* A place to store the currently composing word with information such as adjacent key codes as well * A place to store the currently composing word with information such as adjacent key codes as well
*/ */
public class WordComposer { public class WordComposer {
public static final int NOT_A_CODE = KeyDetector.NOT_A_CODE;
/** /**
* The list of unicode values for each keystroke (including surrounding keys) * The list of unicode values for each keystroke (including surrounding keys)
*/ */

View File

@ -123,26 +123,20 @@ static jint latinime_BinaryDictionary_open(JNIEnv *env, jobject object,
} }
static int latinime_BinaryDictionary_getSuggestions(JNIEnv *env, jobject object, jint dict, static int latinime_BinaryDictionary_getSuggestions(JNIEnv *env, jobject object, jint dict,
jintArray inputArray, jint arraySize, jcharArray outputArray, jintArray frequencyArray, jintArray inputArray, jint arraySize, jcharArray outputArray, jintArray frequencyArray) {
jintArray nextLettersArray, jint nextLettersSize) {
Dictionary *dictionary = (Dictionary*)dict; Dictionary *dictionary = (Dictionary*)dict;
if (!dictionary) return 0; if (!dictionary) return 0;
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, int count = dictionary->getSuggestions(inputCodes, arraySize, (unsigned short*) outputChars,
frequencies, nextLetters, nextLettersSize); frequencies);
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);
if (nextLetters) {
env->ReleaseIntArrayElements(nextLettersArray, nextLetters, 0);
}
return count; return count;
} }
@ -209,7 +203,7 @@ static void latinime_BinaryDictionary_close(JNIEnv *env, jobject object, jint di
static JNINativeMethod gMethods[] = { static JNINativeMethod gMethods[] = {
{"openNative", "(Ljava/lang/String;JJIIIII)I", (void*)latinime_BinaryDictionary_open}, {"openNative", "(Ljava/lang/String;JJIIIII)I", (void*)latinime_BinaryDictionary_open},
{"closeNative", "(I)V", (void*)latinime_BinaryDictionary_close}, {"closeNative", "(I)V", (void*)latinime_BinaryDictionary_close},
{"getSuggestionsNative", "(I[II[C[I[II)I", (void*)latinime_BinaryDictionary_getSuggestions}, {"getSuggestionsNative", "(I[II[C[I)I", (void*)latinime_BinaryDictionary_getSuggestions},
{"isValidWordNative", "(I[CI)Z", (void*)latinime_BinaryDictionary_isValidWord}, {"isValidWordNative", "(I[CI)Z", (void*)latinime_BinaryDictionary_isValidWord},
{"getBigramsNative", "(I[CI[II[C[IIII)I", (void*)latinime_BinaryDictionary_getBigrams} {"getBigramsNative", "(I[CI[II[C[IIII)I", (void*)latinime_BinaryDictionary_getBigrams}
}; };

View File

@ -151,6 +151,9 @@ static void prof_out(void) {
#define MIN_USER_TYPED_LENGTH_FOR_MISSING_SPACE_SUGGESTION 3 #define MIN_USER_TYPED_LENGTH_FOR_MISSING_SPACE_SUGGESTION 3
#define MIN_USER_TYPED_LENGTH_FOR_EXCESSIVE_CHARACTER_SUGGESTION 3 #define MIN_USER_TYPED_LENGTH_FOR_EXCESSIVE_CHARACTER_SUGGESTION 3
// The size of next letters frequency array. Zero will disable the feature.
#define NEXT_LETTERS_SIZE 0
#define min(a,b) ((a)<(b)?(a):(b)) #define min(a,b) ((a)<(b)?(a):(b))
#endif // LATINIME_DEFINES_H #endif // LATINIME_DEFINES_H

View File

@ -27,10 +27,8 @@ class Dictionary {
public: public:
Dictionary(void *dict, int dictSize, int mmapFd, int dictBufAdjust, int typedLetterMultipler, Dictionary(void *dict, int dictSize, int mmapFd, int dictBufAdjust, int typedLetterMultipler,
int fullWordMultiplier, int maxWordLength, int maxWords, int maxAlternatives); int fullWordMultiplier, int maxWordLength, int maxWords, int maxAlternatives);
int getSuggestions(int *codes, int codesSize, unsigned short *outWords, int *frequencies, int getSuggestions(int *codes, int codesSize, unsigned short *outWords, int *frequencies) {
int *nextLetters, int nextLettersSize) { return mUnigramDictionary->getSuggestions(codes, codesSize, outWords, frequencies);
return mUnigramDictionary->getSuggestions(codes, codesSize, outWords, frequencies,
nextLetters, nextLettersSize);
} }
// TODO: Call mBigramDictionary instead of mUnigramDictionary // TODO: Call mBigramDictionary instead of mUnigramDictionary

View File

@ -42,7 +42,7 @@ UnigramDictionary::UnigramDictionary(const unsigned char *dict, int typedLetterM
UnigramDictionary::~UnigramDictionary() {} UnigramDictionary::~UnigramDictionary() {}
int UnigramDictionary::getSuggestions(int *codes, int codesSize, unsigned short *outWords, int UnigramDictionary::getSuggestions(int *codes, int codesSize, unsigned short *outWords,
int *frequencies, int *nextLetters, int nextLettersSize) { int *frequencies) {
PROF_OPEN; PROF_OPEN;
PROF_START(0); PROF_START(0);
initSuggestions(codes, codesSize, outWords, frequencies); initSuggestions(codes, codesSize, outWords, frequencies);
@ -52,7 +52,7 @@ int UnigramDictionary::getSuggestions(int *codes, int codesSize, unsigned short
PROF_END(0); PROF_END(0);
PROF_START(1); PROF_START(1);
getSuggestionCandidates(-1, -1, -1, nextLetters, nextLettersSize, MAX_DEPTH); getSuggestionCandidates(-1, -1, -1, mNextLettersFrequency, NEXT_LETTERS_SIZE, MAX_DEPTH);
PROF_END(1); PROF_END(1);
PROF_START(2); PROF_START(2);
@ -108,9 +108,9 @@ int UnigramDictionary::getSuggestions(int *codes, int codesSize, unsigned short
if (DEBUG_DICT) { if (DEBUG_DICT) {
LOGI("Returning %d words", suggestedWordsCount); LOGI("Returning %d words", suggestedWordsCount);
LOGI("Next letters: "); LOGI("Next letters: ");
for (int k = 0; k < nextLettersSize; k++) { for (int k = 0; k < NEXT_LETTERS_SIZE; k++) {
if (nextLetters[k] > 0) { if (mNextLettersFrequency[k] > 0) {
LOGI("%c = %d,", k, nextLetters[k]); LOGI("%c = %d,", k, mNextLettersFrequency[k]);
} }
} }
} }

View File

@ -32,8 +32,7 @@ class UnigramDictionary {
public: public:
UnigramDictionary(const unsigned char *dict, int typedLetterMultipler, int fullWordMultiplier, UnigramDictionary(const unsigned char *dict, int typedLetterMultipler, int fullWordMultiplier,
int maxWordLength, int maxWords, int maxProximityChars, const bool isLatestDictVersion); int maxWordLength, int maxWords, int maxProximityChars, const bool isLatestDictVersion);
int getSuggestions(int *codes, int codesSize, unsigned short *outWords, int *frequencies, int getSuggestions(int *codes, int codesSize, unsigned short *outWords, int *frequencies);
int *nextLetters, int nextLettersSize);
~UnigramDictionary(); ~UnigramDictionary();
private: private:
@ -109,6 +108,7 @@ private:
int mStackInputIndex[MAX_WORD_LENGTH_INTERNAL]; int mStackInputIndex[MAX_WORD_LENGTH_INTERNAL];
int mStackDiffs[MAX_WORD_LENGTH_INTERNAL]; int mStackDiffs[MAX_WORD_LENGTH_INTERNAL];
int mStackSiblingPos[MAX_WORD_LENGTH_INTERNAL]; int mStackSiblingPos[MAX_WORD_LENGTH_INTERNAL];
int mNextLettersFrequency[NEXT_LETTERS_SIZE];
}; };
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------

View File

@ -61,7 +61,7 @@ public class UserBigramSuggestHelper extends SuggestHelper {
mSuggest.getSuggestions(null, firstChar, previous); mSuggest.getSuggestions(null, firstChar, previous);
boolean reloading = mUserBigram.reloadDictionaryIfRequired(); boolean reloading = mUserBigram.reloadDictionaryIfRequired();
if (reloading) mUserBigram.waitForDictionaryLoading(); if (reloading) mUserBigram.waitForDictionaryLoading();
mUserBigram.getBigrams(firstChar, previous, mSuggest, null); mUserBigram.getBigrams(firstChar, previous, mSuggest);
} }
for (int i = 0; i < mSuggest.mBigramSuggestions.size(); i++) { for (int i = 0; i < mSuggest.mBigramSuggestions.size(); i++) {