aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--dictionaries/en_GB_wordlist.combined.gzbin859868 -> 859866 bytes
-rw-r--r--dictionaries/en_US_wordlist.combined.gzbin876936 -> 876937 bytes
-rw-r--r--dictionaries/en_wordlist.combined.gzbin908304 -> 908305 bytes
-rw-r--r--java/res/raw/main_en.dictbin1069821 -> 1069819 bytes
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java71
-rw-r--r--java/src/com/android/inputmethod/latin/SuggestedWords.java17
-rw-r--r--java/src/com/android/inputmethod/latin/WordComposer.java16
-rw-r--r--native/jni/src/correction.cpp2
-rw-r--r--native/jni/src/proximity_info_state.cpp12
-rw-r--r--native/jni/src/proximity_info_state.h8
-rw-r--r--native/jni/src/proximity_info_state_utils.cpp54
-rw-r--r--native/jni/src/proximity_info_state_utils.h12
-rw-r--r--native/jni/src/suggest/core/session/dic_traverse_session.h4
-rw-r--r--native/jni/src/suggest/policyimpl/typing/typing_weighting.h11
-rw-r--r--native/jni/src/suggest_utils.h39
-rw-r--r--tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java60
16 files changed, 242 insertions, 64 deletions
diff --git a/dictionaries/en_GB_wordlist.combined.gz b/dictionaries/en_GB_wordlist.combined.gz
index ae5463918..b3e5cfccb 100644
--- a/dictionaries/en_GB_wordlist.combined.gz
+++ b/dictionaries/en_GB_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/en_US_wordlist.combined.gz b/dictionaries/en_US_wordlist.combined.gz
index 941d2b6cc..67328d87c 100644
--- a/dictionaries/en_US_wordlist.combined.gz
+++ b/dictionaries/en_US_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/en_wordlist.combined.gz b/dictionaries/en_wordlist.combined.gz
index fe350331a..7fc6cff06 100644
--- a/dictionaries/en_wordlist.combined.gz
+++ b/dictionaries/en_wordlist.combined.gz
Binary files differ
diff --git a/java/res/raw/main_en.dict b/java/res/raw/main_en.dict
index b3ed11d28..526761ce0 100644
--- a/java/res/raw/main_en.dict
+++ b/java/res/raw/main_en.dict
Binary files differ
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index eaa095256..4db0a906e 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -1473,7 +1473,13 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
"", mWordComposer.getTypedWord(), " ", mWordComposer);
}
}
- commitTyped(LastComposedWord.NOT_A_SEPARATOR);
+ if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) {
+ // If we are in the middle of a recorrection, we need to commit the recorrection
+ // first so that we can insert the character at the current cursor position.
+ resetEntireInputState(mLastSelectionStart);
+ } else {
+ commitTyped(LastComposedWord.NOT_A_SEPARATOR);
+ }
}
final int keyX, keyY;
final Keyboard keyboard = mKeyboardSwitcher.getKeyboard();
@@ -1529,8 +1535,12 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
}
final int wordComposerSize = mWordComposer.size();
// Since isComposingWord() is true, the size is at least 1.
- final int lastChar = mWordComposer.getCodeAt(wordComposerSize - 1);
- if (wordComposerSize <= 1) {
+ final int lastChar = mWordComposer.getCodeBeforeCursor();
+ if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) {
+ // If we are in the middle of a recorrection, we need to commit the recorrection
+ // first so that we can insert the batch input at the current cursor position.
+ resetEntireInputState(mLastSelectionStart);
+ } else if (wordComposerSize <= 1) {
// We auto-correct the previous (typed, not gestured) string iff it's one character
// long. The reason for this is, even in the middle of gesture typing, you'll still
// tap one-letter words and you want them auto-corrected (typically, "i" in English
@@ -1741,8 +1751,11 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
// during key repeat.
mHandler.postUpdateShiftState();
- if (mWordComposer.isComposingWord() && !mWordComposer.isCursorAtEndOfComposingWord()) {
+ if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) {
+ // If we are in the middle of a recorrection, we need to commit the recorrection
+ // first so that we can remove the character at the current cursor position.
resetEntireInputState(mLastSelectionStart);
+ // When we exit this if-clause, mWordComposer.isComposingWord() will return false.
}
if (mWordComposer.isComposingWord()) {
final int length = mWordComposer.size();
@@ -1877,7 +1890,9 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
promotePhantomSpace();
}
- if (mWordComposer.isComposingWord() && !mWordComposer.isCursorAtEndOfComposingWord()) {
+ if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) {
+ // If we are in the middle of a recorrection, we need to commit the recorrection
+ // first so that we can insert the character at the current cursor position.
resetEntireInputState(mLastSelectionStart);
isComposingWord = false;
}
@@ -1973,7 +1988,11 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
ResearchLogger.latinIME_handleSeparator(primaryCode, mWordComposer.isComposingWord());
}
boolean didAutoCorrect = false;
- // Handle separator
+ if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) {
+ // If we are in the middle of a recorrection, we need to commit the recorrection
+ // first so that we can insert the separator at the current cursor position.
+ resetEntireInputState(mLastSelectionStart);
+ }
if (mWordComposer.isComposingWord()) {
if (mSettings.getCurrent().mCorrectionEnabled) {
// TODO: maybe cache Strings in an <String> sparse array or something
@@ -2395,9 +2414,9 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
final Range range = mConnection.getWordRangeAtCursor(mSettings.getWordSeparators(),
0 /* additionalPrecedingWordsCount */);
final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList();
+ final String typedWord = range.mWord.toString();
if (range.mWord instanceof SpannableString) {
final SpannableString spannableString = (SpannableString)range.mWord;
- final String typedWord = spannableString.toString();
int i = 0;
for (Object object : spannableString.getSpans(0, spannableString.length(),
SuggestionSpan.class)) {
@@ -2412,18 +2431,42 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
}
}
}
- mWordComposer.setComposingWord(range.mWord, mKeyboardSwitcher.getKeyboard());
+ mWordComposer.setComposingWord(typedWord, mKeyboardSwitcher.getKeyboard());
mWordComposer.setCursorPositionWithinWord(range.mCharsBefore);
mConnection.setComposingRegion(mLastSelectionStart - range.mCharsBefore,
mLastSelectionEnd + range.mCharsAfter);
+ final SuggestedWords suggestedWords;
if (suggestions.isEmpty()) {
- suggestions.add(new SuggestedWordInfo(range.mWord.toString(), 1,
- SuggestedWordInfo.KIND_TYPED, Dictionary.TYPE_RESUMED));
+ // We come here if there weren't any suggestion spans on this word. We will try to
+ // compute suggestions for it instead.
+ final SuggestedWords suggestedWordsIncludingTypedWord =
+ getSuggestedWords(Suggest.SESSION_TYPING);
+ if (suggestedWordsIncludingTypedWord.size() > 1) {
+ // We were able to compute new suggestions for this word.
+ // Remove the typed word, since we don't want to display it in this case.
+ // The #getSuggestedWordsExcludingTypedWord() method sets willAutoCorrect to false.
+ suggestedWords =
+ suggestedWordsIncludingTypedWord.getSuggestedWordsExcludingTypedWord();
+ } else {
+ // No saved suggestions, and we were unable to compute any good one either.
+ // Rather than displaying an empty suggestion strip, we'll display the original
+ // word alone in the middle.
+ // Since there is only one word, willAutoCorrect is false.
+ suggestedWords = suggestedWordsIncludingTypedWord;
+ }
+ } else {
+ // We found suggestion spans in the word. We'll create the SuggestedWords out of
+ // them, and make willAutoCorrect false.
+ suggestedWords = new SuggestedWords(suggestions,
+ true /* typedWordValid */, false /* willAutoCorrect */,
+ false /* isPunctuationSuggestions */, false /* isObsoleteSuggestions */,
+ false /* isPrediction */);
}
- showSuggestionStrip(new SuggestedWords(suggestions,
- true /* typedWordValid */, false /* willAutoCorrect */,
- false /* isPunctuationSuggestions */, false /* isObsoleteSuggestions */,
- false /* isPrediction */), range.mWord.toString());
+
+ // Note that it's very important here that suggestedWords.mWillAutoCorrect is false.
+ // We never want to auto-correct on a resumed suggestion. Please refer to the three
+ // places above where suggestedWords is affected.
+ showSuggestionStrip(suggestedWords, typedWord);
}
/**
diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java
index 158cc1155..616e1911b 100644
--- a/java/src/com/android/inputmethod/latin/SuggestedWords.java
+++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java
@@ -195,4 +195,21 @@ public final class SuggestedWords {
}
}
}
+
+ // SuggestedWords is an immutable object, as much as possible. We must not just remove
+ // words from the member ArrayList as some other parties may expect the object to never change.
+ public SuggestedWords getSuggestedWordsExcludingTypedWord() {
+ final ArrayList<SuggestedWordInfo> newSuggestions = CollectionUtils.newArrayList();
+ for (int i = 0; i < mSuggestedWordInfoList.size(); ++i) {
+ final SuggestedWordInfo info = mSuggestedWordInfoList.get(i);
+ if (SuggestedWordInfo.KIND_TYPED != info.mKind) {
+ newSuggestions.add(info);
+ }
+ }
+ // We should never autocorrect, so we say the typed word is valid. Also, in this case,
+ // no auto-correction should take place hence willAutoCorrect = false.
+ return new SuggestedWords(newSuggestions, true /* typedWordValid */,
+ false /* willAutoCorrect */, mIsPunctuationSuggestions, mIsObsoleteSuggestions,
+ mIsPrediction);
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
index 098e8ac7b..51bd901fb 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -27,6 +27,7 @@ import java.util.Arrays;
*/
public final class WordComposer {
private static final int MAX_WORD_LENGTH = Constants.Dictionary.MAX_WORD_LENGTH;
+ private static final boolean DBG = LatinImeLogger.sDBG;
public static final int CAPS_MODE_OFF = 0;
// 1 is shift bit, 2 is caps bit, 4 is auto bit but this is just a convention as these bits
@@ -132,6 +133,13 @@ public final class WordComposer {
return mPrimaryKeyCodes[index];
}
+ public int getCodeBeforeCursor() {
+ if (mCursorPositionWithinWord < 1 || mCursorPositionWithinWord > mPrimaryKeyCodes.length) {
+ return Constants.NOT_A_CODE;
+ }
+ return mPrimaryKeyCodes[mCursorPositionWithinWord - 1];
+ }
+
public InputPointers getInputPointers() {
return mInputPointers;
}
@@ -177,8 +185,12 @@ public final class WordComposer {
mCursorPositionWithinWord = posWithinWord;
}
- public boolean isCursorAtEndOfComposingWord() {
- return mCursorPositionWithinWord == mCodePointSize;
+ public boolean isCursorFrontOrMiddleOfComposingWord() {
+ if (DBG && mCursorPositionWithinWord > mCodePointSize) {
+ throw new RuntimeException("Wrong cursor position : " + mCursorPositionWithinWord
+ + "in a word of size " + mCodePointSize);
+ }
+ return mCursorPositionWithinWord != mCodePointSize;
}
public void setBatchInputPointers(final InputPointers batchPointers) {
diff --git a/native/jni/src/correction.cpp b/native/jni/src/correction.cpp
index 76234f840..0c65939e0 100644
--- a/native/jni/src/correction.cpp
+++ b/native/jni/src/correction.cpp
@@ -675,7 +675,7 @@ inline static bool isUpperCase(unsigned short c) {
multiplyIntCapped(typedLetterMultiplier, &finalFreq);
}
const float factor =
- SuggestUtils::getDistanceScalingFactor(static_cast<float>(squaredDistance));
+ SuggestUtils::getLengthScalingFactor(static_cast<float>(squaredDistance));
if (factor > 0.0f) {
multiplyRate(static_cast<int>(factor * 100.0f), &finalFreq);
} else if (squaredDistance == PROXIMITY_CHAR_WITHOUT_DISTANCE_INFO) {
diff --git a/native/jni/src/proximity_info_state.cpp b/native/jni/src/proximity_info_state.cpp
index a10b260e1..cc5b736bd 100644
--- a/native/jni/src/proximity_info_state.cpp
+++ b/native/jni/src/proximity_info_state.cpp
@@ -81,7 +81,7 @@ void ProximityInfoState::initInputParams(const int pointerId, const float maxPoi
mSampledTimes.clear();
mSampledInputIndice.clear();
mSampledLengthCache.clear();
- mSampledDistanceCache_G.clear();
+ mSampledNormalizedSquaredLengthCache.clear();
mSampledNearKeySets.clear();
mSampledSearchKeySets.clear();
mSpeedRates.clear();
@@ -122,14 +122,15 @@ void ProximityInfoState::initInputParams(const int pointerId, const float maxPoi
if (mSampledInputSize > 0) {
ProximityInfoStateUtils::initGeometricDistanceInfos(mProximityInfo, mSampledInputSize,
lastSavedInputSize, verticalSweetSpotScale, &mSampledInputXs, &mSampledInputYs,
- &mSampledNearKeySets, &mSampledDistanceCache_G);
+ &mSampledNearKeySets, &mSampledNormalizedSquaredLengthCache);
if (isGeometric) {
// updates probabilities of skipping or mapping each key for all points.
ProximityInfoStateUtils::updateAlignPointProbabilities(
mMaxPointToKeyLength, mProximityInfo->getMostCommonKeyWidth(),
mProximityInfo->getKeyCount(), lastSavedInputSize, mSampledInputSize,
&mSampledInputXs, &mSampledInputYs, &mSpeedRates, &mSampledLengthCache,
- &mSampledDistanceCache_G, &mSampledNearKeySets, &mCharProbabilities);
+ &mSampledNormalizedSquaredLengthCache, &mSampledNearKeySets,
+ &mCharProbabilities);
ProximityInfoStateUtils::updateSampledSearchKeySets(mProximityInfo,
mSampledInputSize, lastSavedInputSize, &mSampledLengthCache,
&mSampledNearKeySets, &mSampledSearchKeySets,
@@ -171,7 +172,7 @@ float ProximityInfoState::getPointToKeyLength(
const int keyId = mProximityInfo->getKeyIndexOf(codePoint);
if (keyId != NOT_AN_INDEX) {
const int index = inputIndex * mProximityInfo->getKeyCount() + keyId;
- return min(mSampledDistanceCache_G[index], mMaxPointToKeyLength);
+ return min(mSampledNormalizedSquaredLengthCache[index], mMaxPointToKeyLength);
}
if (isIntentionalOmissionCodePoint(codePoint)) {
return 0.0f;
@@ -183,7 +184,8 @@ float ProximityInfoState::getPointToKeyLength(
float ProximityInfoState::getPointToKeyByIdLength(
const int inputIndex, const int keyId) const {
return ProximityInfoStateUtils::getPointToKeyByIdLength(mMaxPointToKeyLength,
- &mSampledDistanceCache_G, mProximityInfo->getKeyCount(), inputIndex, keyId);
+ &mSampledNormalizedSquaredLengthCache, mProximityInfo->getKeyCount(), inputIndex,
+ keyId);
}
// In the following function, c is the current character of the dictionary word currently examined.
diff --git a/native/jni/src/proximity_info_state.h b/native/jni/src/proximity_info_state.h
index 9bba751d0..bbe8af240 100644
--- a/native/jni/src/proximity_info_state.h
+++ b/native/jni/src/proximity_info_state.h
@@ -49,8 +49,8 @@ class ProximityInfoState {
mKeyCount(0), mCellHeight(0), mCellWidth(0), mGridHeight(0), mGridWidth(0),
mIsContinuousSuggestionPossible(false), mSampledInputXs(), mSampledInputYs(),
mSampledTimes(), mSampledInputIndice(), mSampledLengthCache(),
- mBeelineSpeedPercentiles(), mSampledDistanceCache_G(), mSpeedRates(), mDirections(),
- mCharProbabilities(), mSampledNearKeySets(), mSampledSearchKeySets(),
+ mBeelineSpeedPercentiles(), mSampledNormalizedSquaredLengthCache(), mSpeedRates(),
+ mDirections(), mCharProbabilities(), mSampledNearKeySets(), mSampledSearchKeySets(),
mSampledSearchKeyVectors(), mTouchPositionCorrectionEnabled(false),
mSampledInputSize(0), mMostProbableStringProbability(0.0f) {
memset(mInputProximities, 0, sizeof(mInputProximities));
@@ -147,7 +147,9 @@ class ProximityInfoState {
return mIsContinuousSuggestionPossible;
}
+ // TODO: Rename s/Length/NormalizedSquaredLength/
float getPointToKeyByIdLength(const int inputIndex, const int keyId) const;
+ // TODO: Rename s/Length/NormalizedSquaredLength/
float getPointToKeyLength(const int inputIndex, const int codePoint) const;
ProximityType getProximityType(const int index, const int codePoint,
@@ -231,7 +233,7 @@ class ProximityInfoState {
std::vector<int> mSampledInputIndice;
std::vector<int> mSampledLengthCache;
std::vector<int> mBeelineSpeedPercentiles;
- std::vector<float> mSampledDistanceCache_G;
+ std::vector<float> mSampledNormalizedSquaredLengthCache;
std::vector<float> mSpeedRates;
std::vector<float> mDirections;
// probabilities of skipping or mapping to a key for each point.
diff --git a/native/jni/src/proximity_info_state_utils.cpp b/native/jni/src/proximity_info_state_utils.cpp
index df70cffdf..359673cd8 100644
--- a/native/jni/src/proximity_info_state_utils.cpp
+++ b/native/jni/src/proximity_info_state_utils.cpp
@@ -225,13 +225,13 @@ namespace latinime {
const int lastSavedInputSize, const float verticalSweetSpotScale,
const std::vector<int> *const sampledInputXs,
const std::vector<int> *const sampledInputYs,
- std::vector<NearKeycodesSet> *SampledNearKeySets,
- std::vector<float> *SampledDistanceCache_G) {
- SampledNearKeySets->resize(sampledInputSize);
+ std::vector<NearKeycodesSet> *sampledNearKeySets,
+ std::vector<float> *sampledNormalizedSquaredLengthCache) {
+ sampledNearKeySets->resize(sampledInputSize);
const int keyCount = proximityInfo->getKeyCount();
- SampledDistanceCache_G->resize(sampledInputSize * keyCount);
+ sampledNormalizedSquaredLengthCache->resize(sampledInputSize * keyCount);
for (int i = lastSavedInputSize; i < sampledInputSize; ++i) {
- (*SampledNearKeySets)[i].reset();
+ (*sampledNearKeySets)[i].reset();
for (int k = 0; k < keyCount; ++k) {
const int index = i * keyCount + k;
const int x = (*sampledInputXs)[i];
@@ -239,10 +239,10 @@ namespace latinime {
const float normalizedSquaredDistance =
proximityInfo->getNormalizedSquaredDistanceFromCenterFloatG(
k, x, y, verticalSweetSpotScale);
- (*SampledDistanceCache_G)[index] = normalizedSquaredDistance;
+ (*sampledNormalizedSquaredLengthCache)[index] = normalizedSquaredDistance;
if (normalizedSquaredDistance
< ProximityInfoParams::NEAR_KEY_NORMALIZED_SQUARED_THRESHOLD) {
- (*SampledNearKeySets)[i][k] = true;
+ (*sampledNearKeySets)[i][k] = true;
}
}
}
@@ -642,11 +642,11 @@ namespace latinime {
// This function basically converts from a length to an edit distance. Accordingly, it's obviously
// wrong to compare with mMaxPointToKeyLength.
/* static */ float ProximityInfoStateUtils::getPointToKeyByIdLength(const float maxPointToKeyLength,
- const std::vector<float> *const SampledDistanceCache_G, const int keyCount,
+ const std::vector<float> *const sampledNormalizedSquaredLengthCache, const int keyCount,
const int inputIndex, const int keyId) {
if (keyId != NOT_AN_INDEX) {
const int index = inputIndex * keyCount + keyId;
- return min((*SampledDistanceCache_G)[index], maxPointToKeyLength);
+ return min((*sampledNormalizedSquaredLengthCache)[index], maxPointToKeyLength);
}
// If the char is not a key on the keyboard then return the max length.
return static_cast<float>(MAX_VALUE_FOR_WEIGHTING);
@@ -660,8 +660,8 @@ namespace latinime {
const std::vector<int> *const sampledInputYs,
const std::vector<float> *const sampledSpeedRates,
const std::vector<int> *const sampledLengthCache,
- const std::vector<float> *const SampledDistanceCache_G,
- std::vector<NearKeycodesSet> *SampledNearKeySets,
+ const std::vector<float> *const sampledNormalizedSquaredLengthCache,
+ std::vector<NearKeycodesSet> *sampledNearKeySets,
std::vector<hash_map_compat<int, float> > *charProbabilities) {
charProbabilities->resize(sampledInputSize);
// Calculates probabilities of using a point as a correlated point with the character
@@ -677,9 +677,9 @@ namespace latinime {
float nearestKeyDistance = static_cast<float>(MAX_VALUE_FOR_WEIGHTING);
for (int j = 0; j < keyCount; ++j) {
- if ((*SampledNearKeySets)[i].test(j)) {
+ if ((*sampledNearKeySets)[i].test(j)) {
const float distance = getPointToKeyByIdLength(
- maxPointToKeyLength, SampledDistanceCache_G, keyCount, i, j);
+ maxPointToKeyLength, sampledNormalizedSquaredLengthCache, keyCount, i, j);
if (distance < nearestKeyDistance) {
nearestKeyDistance = distance;
}
@@ -758,14 +758,15 @@ namespace latinime {
// Summing up probability densities of all near keys.
float sumOfProbabilityDensities = 0.0f;
for (int j = 0; j < keyCount; ++j) {
- if ((*SampledNearKeySets)[i].test(j)) {
+ if ((*sampledNearKeySets)[i].test(j)) {
float distance = sqrtf(getPointToKeyByIdLength(
- maxPointToKeyLength, SampledDistanceCache_G, keyCount, i, j));
+ maxPointToKeyLength, sampledNormalizedSquaredLengthCache, keyCount, i, j));
if (i == 0 && i != sampledInputSize - 1) {
// For the first point, weighted average of distances from first point and the
// next point to the key is used as a point to key distance.
const float nextDistance = sqrtf(getPointToKeyByIdLength(
- maxPointToKeyLength, SampledDistanceCache_G, keyCount, i + 1, j));
+ maxPointToKeyLength, sampledNormalizedSquaredLengthCache, keyCount,
+ i + 1, j));
if (nextDistance < distance) {
// The distance of the first point tends to bigger than continuing
// points because the first touch by the user can be sloppy.
@@ -779,7 +780,8 @@ namespace latinime {
// For the first point, weighted average of distances from last point and
// the previous point to the key is used as a point to key distance.
const float previousDistance = sqrtf(getPointToKeyByIdLength(
- maxPointToKeyLength, SampledDistanceCache_G, keyCount, i - 1, j));
+ maxPointToKeyLength, sampledNormalizedSquaredLengthCache, keyCount,
+ i - 1, j));
if (previousDistance < distance) {
// The distance of the last point tends to bigger than continuing points
// because the last touch by the user can be sloppy. So we promote the
@@ -798,14 +800,15 @@ namespace latinime {
// Split the probability of an input point to keys that are close to the input point.
for (int j = 0; j < keyCount; ++j) {
- if ((*SampledNearKeySets)[i].test(j)) {
+ if ((*sampledNearKeySets)[i].test(j)) {
float distance = sqrtf(getPointToKeyByIdLength(
- maxPointToKeyLength, SampledDistanceCache_G, keyCount, i, j));
+ maxPointToKeyLength, sampledNormalizedSquaredLengthCache, keyCount, i, j));
if (i == 0 && i != sampledInputSize - 1) {
// For the first point, weighted average of distances from the first point and
// the next point to the key is used as a point to key distance.
const float prevDistance = sqrtf(getPointToKeyByIdLength(
- maxPointToKeyLength, SampledDistanceCache_G, keyCount, i + 1, j));
+ maxPointToKeyLength, sampledNormalizedSquaredLengthCache, keyCount,
+ i + 1, j));
if (prevDistance < distance) {
distance = (distance
+ prevDistance * ProximityInfoParams::NEXT_DISTANCE_WEIGHT)
@@ -815,7 +818,8 @@ namespace latinime {
// For the first point, weighted average of distances from last point and
// the previous point to the key is used as a point to key distance.
const float prevDistance = sqrtf(getPointToKeyByIdLength(
- maxPointToKeyLength, SampledDistanceCache_G, keyCount, i - 1, j));
+ maxPointToKeyLength, sampledNormalizedSquaredLengthCache, keyCount,
+ i - 1, j));
if (prevDistance < distance) {
distance = (distance
+ prevDistance * ProximityInfoParams::PREV_DISTANCE_WEIGHT)
@@ -882,10 +886,10 @@ namespace latinime {
for (int j = 0; j < keyCount; ++j) {
hash_map_compat<int, float>::iterator it = (*charProbabilities)[i].find(j);
if (it == (*charProbabilities)[i].end()){
- (*SampledNearKeySets)[i].reset(j);
+ (*sampledNearKeySets)[i].reset(j);
} else if(it->second < ProximityInfoParams::MIN_PROBABILITY) {
// Erases from near keys vector because it has very low probability.
- (*SampledNearKeySets)[i].reset(j);
+ (*sampledNearKeySets)[i].reset(j);
(*charProbabilities)[i].erase(j);
} else {
it->second = -logf(it->second);
@@ -899,7 +903,7 @@ namespace latinime {
const ProximityInfo *const proximityInfo, const int sampledInputSize,
const int lastSavedInputSize,
const std::vector<int> *const sampledLengthCache,
- const std::vector<NearKeycodesSet> *const SampledNearKeySets,
+ const std::vector<NearKeycodesSet> *const sampledNearKeySets,
std::vector<NearKeycodesSet> *sampledSearchKeySets,
std::vector<std::vector<int> > *sampledSearchKeyVectors) {
sampledSearchKeySets->resize(sampledInputSize);
@@ -916,7 +920,7 @@ namespace latinime {
if ((*sampledLengthCache)[j] - (*sampledLengthCache)[i] >= readForwordLength) {
break;
}
- (*sampledSearchKeySets)[i] |= (*SampledNearKeySets)[j];
+ (*sampledSearchKeySets)[i] |= (*sampledNearKeySets)[j];
}
}
const int keyCount = proximityInfo->getKeyCount();
diff --git a/native/jni/src/proximity_info_state_utils.h b/native/jni/src/proximity_info_state_utils.h
index c9feb59a3..1837c7ab6 100644
--- a/native/jni/src/proximity_info_state_utils.h
+++ b/native/jni/src/proximity_info_state_utils.h
@@ -71,25 +71,25 @@ class ProximityInfoStateUtils {
const std::vector<int> *const sampledInputYs,
const std::vector<float> *const sampledSpeedRates,
const std::vector<int> *const sampledLengthCache,
- const std::vector<float> *const SampledDistanceCache_G,
- std::vector<NearKeycodesSet> *SampledNearKeySets,
+ const std::vector<float> *const sampledNormalizedSquaredLengthCache,
+ std::vector<NearKeycodesSet> *sampledNearKeySets,
std::vector<hash_map_compat<int, float> > *charProbabilities);
static void updateSampledSearchKeySets(const ProximityInfo *const proximityInfo,
const int sampledInputSize, const int lastSavedInputSize,
const std::vector<int> *const sampledLengthCache,
- const std::vector<NearKeycodesSet> *const SampledNearKeySets,
+ const std::vector<NearKeycodesSet> *const sampledNearKeySets,
std::vector<NearKeycodesSet> *sampledSearchKeySets,
std::vector<std::vector<int> > *sampledSearchKeyVectors);
static float getPointToKeyByIdLength(const float maxPointToKeyLength,
- const std::vector<float> *const SampledDistanceCache_G, const int keyCount,
+ const std::vector<float> *const sampledNormalizedSquaredLengthCache, const int keyCount,
const int inputIndex, const int keyId);
static void initGeometricDistanceInfos(const ProximityInfo *const proximityInfo,
const int sampledInputSize, const int lastSavedInputSize,
const float verticalSweetSpotScale,
const std::vector<int> *const sampledInputXs,
const std::vector<int> *const sampledInputYs,
- std::vector<NearKeycodesSet> *SampledNearKeySets,
- std::vector<float> *SampledDistanceCache_G);
+ std::vector<NearKeycodesSet> *sampledNearKeySets,
+ std::vector<float> *sampledNormalizedSquaredLengthCache);
static void initPrimaryInputWord(const int inputSize, const int *const inputProximities,
int *primaryInputWord);
static void initNormalizedSquaredDistances(const ProximityInfo *const proximityInfo,
diff --git a/native/jni/src/suggest/core/session/dic_traverse_session.h b/native/jni/src/suggest/core/session/dic_traverse_session.h
index a7c042ada..fe0527639 100644
--- a/native/jni/src/suggest/core/session/dic_traverse_session.h
+++ b/native/jni/src/suggest/core/session/dic_traverse_session.h
@@ -146,6 +146,10 @@ class DicTraverseSession {
return true;
}
+ bool isTouchPositionCorrectionEnabled() const {
+ return mProximityInfoStates[0].touchPositionCorrectionEnabled();
+ }
+
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(DicTraverseSession);
// threshold to start caching
diff --git a/native/jni/src/suggest/policyimpl/typing/typing_weighting.h b/native/jni/src/suggest/policyimpl/typing/typing_weighting.h
index 52d54eb0f..2dcee343f 100644
--- a/native/jni/src/suggest/policyimpl/typing/typing_weighting.h
+++ b/native/jni/src/suggest/policyimpl/typing/typing_weighting.h
@@ -18,6 +18,7 @@
#define LATINIME_TYPING_WEIGHTING_H
#include "defines.h"
+#include "suggest_utils.h"
#include "suggest/core/dicnode/dic_node_utils.h"
#include "suggest/core/policy/weighting.h"
#include "suggest/core/session/dic_traverse_session.h"
@@ -70,10 +71,12 @@ class TypingWeighting : public Weighting {
const int pointIndex = dicNode->getInputIndex(0);
// Note: min() required since length can be MAX_POINT_TO_KEY_LENGTH for characters not on
// the keyboard (like accented letters)
- const float length = min(ScoringParams::MAX_SPATIAL_DISTANCE,
- traverseSession->getProximityInfoState(0)->getPointToKeyLength(
- pointIndex, dicNode->getNodeCodePoint()));
- const float weightedDistance = length * ScoringParams::DISTANCE_WEIGHT_LENGTH;
+ const float normalizedSquaredLength = traverseSession->getProximityInfoState(0)
+ ->getPointToKeyLength(pointIndex, dicNode->getNodeCodePoint());
+ const float normalizedDistance = SuggestUtils::getSweetSpotFactor(
+ traverseSession->isTouchPositionCorrectionEnabled(), normalizedSquaredLength);
+ const float weightedDistance = ScoringParams::DISTANCE_WEIGHT_LENGTH * normalizedDistance;
+
const bool isFirstChar = pointIndex == 0;
const bool isProximity = isProximityDicNode(traverseSession, dicNode);
const float cost = isProximity ? (isFirstChar ? ScoringParams::FIRST_PROXIMITY_COST
diff --git a/native/jni/src/suggest_utils.h b/native/jni/src/suggest_utils.h
index aab9f7ba8..e053dd662 100644
--- a/native/jni/src/suggest_utils.h
+++ b/native/jni/src/suggest_utils.h
@@ -23,10 +23,8 @@
namespace latinime {
class SuggestUtils {
public:
- static float getDistanceScalingFactor(const float normalizedSquaredDistance) {
- if (normalizedSquaredDistance < 0.0f) {
- return -1.0f;
- }
+ // TODO: (OLD) Remove
+ static float getLengthScalingFactor(const float normalizedSquaredDistance) {
// Promote or demote the score according to the distance from the sweet spot
static const float A = ZERO_DISTANCE_PROMOTION_RATE / 100.0f;
static const float B = 1.0f;
@@ -50,6 +48,39 @@ class SuggestUtils {
return factor;
}
+ static float getSweetSpotFactor(const bool isTouchPositionCorrectionEnabled,
+ const float normalizedSquaredDistance) {
+ // Promote or demote the score according to the distance from the sweet spot
+ static const float A = 0.0f;
+ static const float B = 0.24f;
+ static const float C = 1.20f;
+ static const float R0 = 0.0f;
+ static const float R1 = 0.25f; // Sweet spot
+ static const float R2 = 1.0f;
+ const float x = normalizedSquaredDistance;
+ if (!isTouchPositionCorrectionEnabled) {
+ return min(C, x);
+ }
+
+ // factor is a piecewise linear function like:
+ // C -------------.
+ // / .
+ // B / .
+ // -/ .
+ // A _-^ .
+ // .
+ // R0 R1 R2 .
+
+ if (x < R0) {
+ return A;
+ } else if (x < R1) {
+ return (A * (R1 - x) + B * (x - R0)) / (R1 - R0);
+ } else if (x < R2) {
+ return (B * (R2 - x) + C * (x - R1)) / (R2 - R1);
+ } else {
+ return C;
+ }
+ }
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(SuggestUtils);
};
diff --git a/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java b/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java
new file mode 100644
index 000000000..916252292
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin;
+
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.util.ArrayList;
+import java.util.Locale;
+
+@SmallTest
+public class SuggestedWordsTests extends AndroidTestCase {
+ public void testGetSuggestedWordsExcludingTypedWord() {
+ final String TYPED_WORD = "typed";
+ final int TYPED_WORD_FREQ = 5;
+ final int NUMBER_OF_ADDED_SUGGESTIONS = 5;
+ final ArrayList<SuggestedWordInfo> list = CollectionUtils.newArrayList();
+ list.add(new SuggestedWordInfo(TYPED_WORD, TYPED_WORD_FREQ,
+ SuggestedWordInfo.KIND_TYPED, ""));
+ for (int i = 0; i < NUMBER_OF_ADDED_SUGGESTIONS; ++i) {
+ list.add(new SuggestedWordInfo("" + i, 1, SuggestedWordInfo.KIND_CORRECTION, ""));
+ }
+
+ final SuggestedWords words = new SuggestedWords(
+ list,
+ false /* typedWordValid */,
+ false /* willAutoCorrect */,
+ false /* isPunctuationSuggestions */,
+ false /* isObsoleteSuggestions */,
+ false /* isPrediction*/);
+ assertEquals(NUMBER_OF_ADDED_SUGGESTIONS + 1, words.size());
+ assertEquals("typed", words.getWord(0));
+ assertEquals(SuggestedWordInfo.KIND_TYPED, words.getInfo(0).mKind);
+ assertEquals("0", words.getWord(1));
+ assertEquals(SuggestedWordInfo.KIND_CORRECTION, words.getInfo(1).mKind);
+ assertEquals("4", words.getWord(5));
+ assertEquals(SuggestedWordInfo.KIND_CORRECTION, words.getInfo(5).mKind);
+
+ final SuggestedWords wordsWithoutTyped = words.getSuggestedWordsExcludingTypedWord();
+ assertEquals(words.size() - 1, wordsWithoutTyped.size());
+ assertEquals("0", wordsWithoutTyped.getWord(0));
+ assertEquals(SuggestedWordInfo.KIND_CORRECTION, wordsWithoutTyped.getInfo(0).mKind);
+ }
+}