Fix a bug with languages without spaces and predictions
This is simpler and more correct. Change-Id: I41806d2fc12b4ca25f76e32972b38f91f3d05c2b
This commit is contained in:
parent
b767715651
commit
7cd7cf73f4
5 changed files with 73 additions and 32 deletions
|
@ -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() {
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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 */);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in a new issue