Fix a bug with languages without spaces and predictions

This is simpler and more correct.

Change-Id: I41806d2fc12b4ca25f76e32972b38f91f3d05c2b
This commit is contained in:
Jean Chalard 2013-12-16 21:41:03 +09:00
parent b767715651
commit 7cd7cf73f4
5 changed files with 73 additions and 32 deletions

View file

@ -1469,7 +1469,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
ResearchLogger.latinIME_maybeDoubleSpacePeriod(textToInsert, ResearchLogger.latinIME_maybeDoubleSpacePeriod(textToInsert,
false /* isBatchMode */); false /* isBatchMode */);
} }
mWordComposer.doubleSpacePeriod(); mWordComposer.discardPreviousWordForSuggestion();
mKeyboardSwitcher.updateShiftState(); mKeyboardSwitcher.updateShiftState();
return true; return true;
} }
@ -2567,7 +2567,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (DEBUG) { if (DEBUG) {
if (mWordComposer.isComposingWord() || mWordComposer.isBatchMode()) { if (mWordComposer.isComposingWord() || mWordComposer.isBatchMode()) {
final String previousWord = mWordComposer.getPreviousWord(); final String previousWord = mWordComposer.getPreviousWordForSuggestion();
// TODO: this is for checking consistency with older versions. Remove this when // TODO: this is for checking consistency with older versions. Remove this when
// we are confident this is stable. // we are confident this is stable.
// We're checking the previous word in the text field against the memorized previous // We're checking the previous word in the text field against the memorized previous
@ -2581,7 +2581,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
} }
} }
} }
suggest.getSuggestedWords(mWordComposer, mWordComposer.getPreviousWord(), suggest.getSuggestedWords(mWordComposer, mWordComposer.getPreviousWordForSuggestion(),
keyboard.getProximityInfo(), keyboard.getProximityInfo(),
currentSettings.mBlockPotentiallyOffensive, currentSettings.mCorrectionEnabled, currentSettings.mBlockPotentiallyOffensive, currentSettings.mCorrectionEnabled,
additionalFeaturesOptions, sessionId, sequenceNumber, callback); additionalFeaturesOptions, sessionId, sequenceNumber, callback);
@ -2824,6 +2824,19 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// strings. // strings.
mLastComposedWord = mWordComposer.commitWord(commitType, chosenWord, separatorString, mLastComposedWord = mWordComposer.commitWord(commitType, chosenWord, separatorString,
prevWord); prevWord);
final boolean shouldDiscardPreviousWordForSuggestion;
if (0 == StringUtils.codePointCount(separatorString)) {
// Separator is 0-length. Discard the word only if the current language has spaces.
shouldDiscardPreviousWordForSuggestion =
mSettings.getCurrent().mCurrentLanguageHasSpaces;
} else {
// Otherwise, we discard if the separator contains any non-whitespace.
shouldDiscardPreviousWordForSuggestion =
!StringUtils.containsOnlyWhitespace(separatorString);
}
if (shouldDiscardPreviousWordForSuggestion) {
mWordComposer.discardPreviousWordForSuggestion();
}
} }
private void setPunctuationSuggestions() { private void setPunctuationSuggestions() {

View file

@ -50,8 +50,9 @@ public final class WordComposer {
private final StringBuilder mTypedWord; private final StringBuilder mTypedWord;
// The previous word (before the composing word). Used as context for suggestions. May be null // The previous word (before the composing word). Used as context for suggestions. May be null
// after resetting and before starting a new composing word, or when there is no context like // after resetting and before starting a new composing word, or when there is no context like
// at the start of text for example. // at the start of text for example. It can also be set to null externally when the user
private String mPreviousWord; // enters a separator that does not let bigrams across, like a period or a comma.
private String mPreviousWordForSuggestion;
private String mAutoCorrection; private String mAutoCorrection;
private boolean mIsResumed; private boolean mIsResumed;
private boolean mIsBatchMode; private boolean mIsBatchMode;
@ -89,7 +90,7 @@ public final class WordComposer {
mIsBatchMode = false; mIsBatchMode = false;
mCursorPositionWithinWord = 0; mCursorPositionWithinWord = 0;
mRejectedBatchModeSuggestion = null; mRejectedBatchModeSuggestion = null;
mPreviousWord = null; mPreviousWordForSuggestion = null;
refreshSize(); refreshSize();
} }
@ -106,7 +107,7 @@ public final class WordComposer {
mIsBatchMode = source.mIsBatchMode; mIsBatchMode = source.mIsBatchMode;
mCursorPositionWithinWord = source.mCursorPositionWithinWord; mCursorPositionWithinWord = source.mCursorPositionWithinWord;
mRejectedBatchModeSuggestion = source.mRejectedBatchModeSuggestion; mRejectedBatchModeSuggestion = source.mRejectedBatchModeSuggestion;
mPreviousWord = source.mPreviousWord; mPreviousWordForSuggestion = source.mPreviousWordForSuggestion;
refreshSize(); refreshSize();
} }
@ -124,7 +125,7 @@ public final class WordComposer {
mIsBatchMode = false; mIsBatchMode = false;
mCursorPositionWithinWord = 0; mCursorPositionWithinWord = 0;
mRejectedBatchModeSuggestion = null; mRejectedBatchModeSuggestion = null;
mPreviousWord = null; mPreviousWordForSuggestion = null;
refreshSize(); refreshSize();
} }
@ -305,7 +306,7 @@ public final class WordComposer {
addKeyInfo(codePoint, keyboard); addKeyInfo(codePoint, keyboard);
} }
mIsResumed = true; mIsResumed = true;
mPreviousWord = previousWord; mPreviousWordForSuggestion = previousWord;
} }
/** /**
@ -356,8 +357,8 @@ public final class WordComposer {
return mTypedWord.toString(); return mTypedWord.toString();
} }
public String getPreviousWord() { public String getPreviousWordForSuggestion() {
return mPreviousWord; return mPreviousWordForSuggestion;
} }
/** /**
@ -419,7 +420,7 @@ public final class WordComposer {
public void setCapitalizedModeAndPreviousWordAtStartComposingTime(final int mode, public void setCapitalizedModeAndPreviousWordAtStartComposingTime(final int mode,
final String previousWord) { final String previousWord) {
mCapitalizedMode = mode; mCapitalizedMode = mode;
mPreviousWord = previousWord; mPreviousWordForSuggestion = previousWord;
} }
/** /**
@ -471,12 +472,7 @@ public final class WordComposer {
mCapsCount = 0; mCapsCount = 0;
mDigitsCount = 0; mDigitsCount = 0;
mIsBatchMode = false; mIsBatchMode = false;
final boolean isWhitespace = 1 == StringUtils.codePointCount(separatorString) mPreviousWordForSuggestion = mTypedWord.toString();
&& Character.isWhitespace(separatorString.codePointAt(0));
// If not whitespace, we don't use the previous word for suggestion. This is consistent
// with how we get the previous word for suggestion: see RichInputConnection#spaceRegex and
// LatinIME#getNthPreviousWordForSuggestion.
mPreviousWord = isWhitespace ? mTypedWord.toString() : null;
mTypedWord.setLength(0); mTypedWord.setLength(0);
mCodePointSize = 0; mCodePointSize = 0;
mTrailingSingleQuotesCount = 0; mTrailingSingleQuotesCount = 0;
@ -490,11 +486,11 @@ public final class WordComposer {
return lastComposedWord; return lastComposedWord;
} }
public void doubleSpacePeriod() { // Call this when the recorded previous word should be discarded. This is typically called
// When a period was entered with a double space, the separator we got has been // when the user inputs a separator that's not whitespace (including the case of the
// changed by a period (see #commitWord). We should not use the previous word for // double-space-to-period feature).
// suggestion. public void discardPreviousWordForSuggestion() {
mPreviousWord = null; mPreviousWordForSuggestion = null;
} }
public void resumeSuggestionOnLastComposedWord(final LastComposedWord lastComposedWord, public void resumeSuggestionOnLastComposedWord(final LastComposedWord lastComposedWord,
@ -509,7 +505,7 @@ public final class WordComposer {
mCursorPositionWithinWord = mCodePointSize; mCursorPositionWithinWord = mCodePointSize;
mRejectedBatchModeSuggestion = null; mRejectedBatchModeSuggestion = null;
mIsResumed = true; mIsResumed = true;
mPreviousWord = previousWord; mPreviousWordForSuggestion = previousWord;
} }
public boolean isBatchMode() { public boolean isBatchMode() {

View file

@ -250,6 +250,24 @@ public final class StringUtils {
return true; return true;
} }
/**
* Returns true if all code points in text are whitespace, false otherwise. Empty is true.
*/
// Interestingly enough, U+00A0 NO-BREAK SPACE and U+200B ZERO-WIDTH SPACE are not considered
// whitespace, while EN SPACE, EM SPACE and IDEOGRAPHIC SPACES are.
public static boolean containsOnlyWhitespace(final String text) {
final int length = text.length();
int i = 0;
while (i < length) {
final int codePoint = text.codePointAt(i);
if (!Character.isWhitespace(codePoint)) {
return false;
}
i += Character.charCount(codePoint);
}
return true;
}
@UsedForTesting @UsedForTesting
public static boolean looksValidForDictionaryInsertion(final CharSequence text, public static boolean looksValidForDictionaryInsertion(final CharSequence text,
final SettingsValues settings) { final SettingsValues settings) {

View file

@ -51,14 +51,14 @@ public class WordComposerTests extends AndroidTestCase {
assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(1)); assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(1));
assertFalse(wc.isCursorFrontOrMiddleOfComposingWord()); assertFalse(wc.isCursorFrontOrMiddleOfComposingWord());
// Check the previous word is still there // Check the previous word is still there
assertEquals(PREVWORD, wc.getPreviousWord()); assertEquals(PREVWORD, wc.getPreviousWordForSuggestion());
// Move the cursor past the end of the word // Move the cursor past the end of the word
assertFalse(wc.moveCursorByAndReturnIfInsideComposingWord(1)); assertFalse(wc.moveCursorByAndReturnIfInsideComposingWord(1));
assertFalse(wc.moveCursorByAndReturnIfInsideComposingWord(15)); assertFalse(wc.moveCursorByAndReturnIfInsideComposingWord(15));
// Do what LatinIME does when the cursor is moved outside of the word, // Do what LatinIME does when the cursor is moved outside of the word,
// and check the behavior is correct. // and check the behavior is correct.
wc.reset(); wc.reset();
assertNull(wc.getPreviousWord()); assertNull(wc.getPreviousWordForSuggestion());
// \uD861\uDED7 is 𨛗, a character outside the BMP // \uD861\uDED7 is 𨛗, a character outside the BMP
final String STR_WITH_SUPPLEMENTARY_CHAR = "abcde\uD861\uDED7fgh"; final String STR_WITH_SUPPLEMENTARY_CHAR = "abcde\uD861\uDED7fgh";
@ -73,35 +73,35 @@ public class WordComposerTests extends AndroidTestCase {
assertTrue(wc.isCursorFrontOrMiddleOfComposingWord()); assertTrue(wc.isCursorFrontOrMiddleOfComposingWord());
assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(1)); assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(1));
assertFalse(wc.isCursorFrontOrMiddleOfComposingWord()); assertFalse(wc.isCursorFrontOrMiddleOfComposingWord());
assertNull(wc.getPreviousWord()); assertNull(wc.getPreviousWordForSuggestion());
wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, STR_WITHIN_BMP, null /* keyboard */); wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, STR_WITHIN_BMP, null /* keyboard */);
wc.setCursorPositionWithinWord(3); wc.setCursorPositionWithinWord(3);
assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(7)); assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(7));
assertEquals(STR_WITHIN_BMP, wc.getPreviousWord()); assertEquals(STR_WITHIN_BMP, wc.getPreviousWordForSuggestion());
wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, STR_WITH_SUPPLEMENTARY_CHAR, wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, STR_WITH_SUPPLEMENTARY_CHAR,
null /* keyboard */); null /* keyboard */);
wc.setCursorPositionWithinWord(3); wc.setCursorPositionWithinWord(3);
assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(7)); assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(7));
assertEquals(STR_WITH_SUPPLEMENTARY_CHAR, wc.getPreviousWord()); assertEquals(STR_WITH_SUPPLEMENTARY_CHAR, wc.getPreviousWordForSuggestion());
wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, STR_WITHIN_BMP, null /* keyboard */); wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, STR_WITHIN_BMP, null /* keyboard */);
wc.setCursorPositionWithinWord(3); wc.setCursorPositionWithinWord(3);
assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(-3)); assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(-3));
assertFalse(wc.moveCursorByAndReturnIfInsideComposingWord(-1)); assertFalse(wc.moveCursorByAndReturnIfInsideComposingWord(-1));
assertEquals(STR_WITHIN_BMP, wc.getPreviousWord()); assertEquals(STR_WITHIN_BMP, wc.getPreviousWordForSuggestion());
wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null /* previousWord */, wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null /* previousWord */,
null /* keyboard */); null /* keyboard */);
wc.setCursorPositionWithinWord(3); wc.setCursorPositionWithinWord(3);
assertFalse(wc.moveCursorByAndReturnIfInsideComposingWord(-9)); assertFalse(wc.moveCursorByAndReturnIfInsideComposingWord(-9));
assertNull(wc.getPreviousWord()); assertNull(wc.getPreviousWordForSuggestion());
wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, STR_WITH_SUPPLEMENTARY_CHAR, wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, STR_WITH_SUPPLEMENTARY_CHAR,
null /* keyboard */); null /* keyboard */);
assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(-10)); assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(-10));
assertEquals(STR_WITH_SUPPLEMENTARY_CHAR, wc.getPreviousWord()); assertEquals(STR_WITH_SUPPLEMENTARY_CHAR, wc.getPreviousWordForSuggestion());
wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null /* previousWord */, wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null /* previousWord */,
null /* keyboard */); null /* keyboard */);

View file

@ -292,6 +292,20 @@ public class StringAndJsonUtilsTests extends AndroidTestCase {
assertTrue(bytesStr.equals(bytesStr2)); assertTrue(bytesStr.equals(bytesStr2));
} }
public void testContainsOnlyWhitespace() {
assertTrue(StringUtils.containsOnlyWhitespace(" "));
assertTrue(StringUtils.containsOnlyWhitespace(""));
assertTrue(StringUtils.containsOnlyWhitespace(" \n\t\t"));
// U+2002 : EN SPACE
// U+2003 : EM SPACE
// U+3000 : IDEOGRAPHIC SPACE (commonly "double-width space")
assertTrue(StringUtils.containsOnlyWhitespace("\u2002\u2003\u3000"));
assertFalse(StringUtils.containsOnlyWhitespace(" a "));
assertFalse(StringUtils.containsOnlyWhitespace(". "));
assertFalse(StringUtils.containsOnlyWhitespace("."));
assertTrue(StringUtils.containsOnlyWhitespace(""));
}
public void testJsonUtils() { public void testJsonUtils() {
final Object[] objs = new Object[] { 1, "aaa", "bbb", 3 }; final Object[] objs = new Object[] { 1, "aaa", "bbb", 3 };
final List<Object> objArray = Arrays.asList(objs); final List<Object> objArray = Arrays.asList(objs);