Change the timing of reading the previous word.

Bug: 11328842
Change-Id: I08229e895fc34403932648b9b931583d965f0e01
main
Jean Chalard 2013-10-22 19:20:45 +09:00
parent da459787e2
commit 4866a3e918
3 changed files with 121 additions and 32 deletions

View File

@ -1779,9 +1779,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mInputUpdater.onStartBatchInput();
mHandler.cancelUpdateSuggestionStrip();
mConnection.beginBatchEdit();
final SettingsValues settingsValues = mSettings.getCurrent();
final SettingsValues currentSettingsValues = mSettings.getCurrent();
if (mWordComposer.isComposingWord()) {
if (settingsValues.mIsInternal) {
if (currentSettingsValues.mIsInternal) {
if (mWordComposer.isBatchMode()) {
LatinImeLoggerUtils.onAutoCorrection(
"", mWordComposer.getTypedWord(), " ", mWordComposer);
@ -1808,12 +1808,14 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
final int codePointBeforeCursor = mConnection.getCodePointBeforeCursor();
if (Character.isLetterOrDigit(codePointBeforeCursor)
|| settingsValues.isUsuallyFollowedBySpace(codePointBeforeCursor)) {
|| currentSettingsValues.isUsuallyFollowedBySpace(codePointBeforeCursor)) {
mSpaceState = SPACE_STATE_PHANTOM;
}
mConnection.endBatchEdit();
mKeyboardSwitcher.updateShiftState();
mWordComposer.setCapitalizedModeAtStartComposingTime(getActualCapsMode());
mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime(getActualCapsMode(),
// Prev word is 1st word before cursor
getNthPreviousWordForSuggestion(currentSettingsValues, 1 /* nthPreviousWord */));
}
static final class InputUpdater implements Handler.Callback {
@ -1986,7 +1988,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mConnection.commitText(commitParts[0], 0);
mSpaceState = SPACE_STATE_PHANTOM;
mKeyboardSwitcher.updateShiftState();
mWordComposer.setCapitalizedModeAtStartComposingTime(getActualCapsMode());
mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime(
getActualCapsMode(), commitParts[0]);
++mAutoCommitSequenceNumber;
}
}
@ -2295,7 +2298,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mWordComposer.add(primaryCode, keyX, keyY);
// If it's the first letter, make note of auto-caps state
if (mWordComposer.size() == 1) {
mWordComposer.setCapitalizedModeAtStartComposingTime(getActualCapsMode());
// We pass 1 to getPreviousWordForSuggestion because we were not composing a word
// yet, so the word we want is the 1st word before the cursor.
mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime(
getActualCapsMode(),
getNthPreviousWordForSuggestion(currentSettings, 1 /* nthPreviousWord */));
}
mConnection.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
} else {
@ -2537,12 +2544,18 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
}
private String getPreviousWordForSuggestion(final SettingsValues currentSettings) {
/**
* Get the nth previous word before the cursor as context for the suggestion process.
* @param currentSettings the current settings values.
* @param nthPreviousWord reverse index of the word to get (1-indexed)
* @return the nth previous word before the cursor.
*/
private String getNthPreviousWordForSuggestion(final SettingsValues currentSettings,
final int nthPreviousWord) {
if (currentSettings.mCurrentLanguageHasSpaces) {
// If we are typing in a language with spaces we can just look up the previous
// word from textview.
return mConnection.getNthPreviousWord(currentSettings,
mWordComposer.isComposingWord() ? 2 : 1);
return mConnection.getNthPreviousWord(currentSettings, nthPreviousWord);
} else {
return LastComposedWord.NOT_A_COMPOSED_WORD == mLastComposedWord ? null
: mLastComposedWord.mCommittedWord;
@ -2562,8 +2575,31 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// should just skip whitespace if any, so 1.
final SettingsValues currentSettings = mSettings.getCurrent();
final int[] additionalFeaturesOptions = currentSettings.mAdditionalFeaturesSettingValues;
final String prevWord = getPreviousWordForSuggestion(currentSettings);
suggest.getSuggestedWords(mWordComposer, prevWord, keyboard.getProximityInfo(),
final String previousWord;
if (mWordComposer.isComposingWord() || mWordComposer.isBatchMode()) {
previousWord = mWordComposer.getPreviousWord();
} else {
// Not composing: this is for prediction.
// TODO: read the previous word earlier for prediction, like we are doing for
// normal suggestions.
previousWord = getNthPreviousWordForSuggestion(currentSettings, 1 /* nthPreviousWord*/);
}
if (DEBUG) {
// TODO: this is for checking consistency with older versions. Remove this when
// we are confident this is stable.
// We're checking the previous word in the text field against the memorized previous
// word. If we are composing a word we should have the second word before the cursor
// memorized, otherwise we should have the first.
final String rereadPrevWord = getNthPreviousWordForSuggestion(currentSettings,
mWordComposer.isComposingWord() ? 2 : 1);
if (!TextUtils.equals(previousWord, rereadPrevWord)) {
throw new RuntimeException("Unexpected previous word: "
+ previousWord + " <> " + rereadPrevWord);
}
}
suggest.getSuggestedWords(mWordComposer, mWordComposer.getPreviousWord(),
keyboard.getProximityInfo(),
currentSettings.mBlockPotentiallyOffensive, currentSettings.mCorrectionEnabled,
additionalFeaturesOptions, sessionId, sequenceNumber, callback);
}
@ -2900,7 +2936,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
}
}
mWordComposer.setComposingWord(typedWord, mKeyboardSwitcher.getKeyboard());
mWordComposer.setComposingWord(typedWord,
getNthPreviousWordForSuggestion(currentSettings,
// We want the previous word for suggestion. If we have chars in the word
// before the cursor, then we want the word before that, hence 2; otherwise,
// we want the word immediately before the cursor, hence 1.
0 == numberOfCharsInWordBeforeCursor ? 1 : 2),
mKeyboardSwitcher.getKeyboard());
mWordComposer.setCursorPositionWithinWord(
typedWord.codePointCount(0, numberOfCharsInWordBeforeCursor));
mConnection.setComposingRegion(
@ -2978,7 +3020,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
private void restartSuggestionsOnWordBeforeCursor(final String word) {
mWordComposer.setComposingWord(word, mKeyboardSwitcher.getKeyboard());
mWordComposer.setComposingWord(word,
// Previous word is the 2nd word before cursor because we are restarting on the
// 1st word before cursor.
getNthPreviousWordForSuggestion(mSettings.getCurrent(), 2 /* nthPreviousWord */),
mKeyboardSwitcher.getKeyboard());
final int length = word.length();
mConnection.deleteSurroundingText(length, 0);
mConnection.setComposingText(word, 1);
@ -3044,7 +3090,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
} else {
// For languages without spaces, we revert the typed string but the cursor is flush
// with the typed word, so we need to resume suggestions right away.
mWordComposer.setComposingWord(stringToCommit, mKeyboardSwitcher.getKeyboard());
mWordComposer.setComposingWord(stringToCommit, previousWord,
mKeyboardSwitcher.getKeyboard());
mConnection.setComposingText(stringToCommit, 1);
}
if (mSettings.isInternal()) {

View File

@ -48,6 +48,10 @@ public final class WordComposer {
// at any given time. However this is not limited in size, while mPrimaryKeyCodes is limited
// to MAX_WORD_LENGTH code points.
private final StringBuilder mTypedWord;
// 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
// at the start of text for example.
private String mPreviousWord;
private String mAutoCorrection;
private boolean mIsResumed;
private boolean mIsBatchMode;
@ -85,6 +89,7 @@ public final class WordComposer {
mIsBatchMode = false;
mCursorPositionWithinWord = 0;
mRejectedBatchModeSuggestion = null;
mPreviousWord = null;
refreshSize();
}
@ -101,6 +106,7 @@ public final class WordComposer {
mIsBatchMode = source.mIsBatchMode;
mCursorPositionWithinWord = source.mCursorPositionWithinWord;
mRejectedBatchModeSuggestion = source.mRejectedBatchModeSuggestion;
mPreviousWord = source.mPreviousWord;
refreshSize();
}
@ -118,6 +124,7 @@ public final class WordComposer {
mIsBatchMode = false;
mCursorPositionWithinWord = 0;
mRejectedBatchModeSuggestion = null;
mPreviousWord = null;
refreshSize();
}
@ -284,8 +291,13 @@ public final class WordComposer {
/**
* Set the currently composing word to the one passed as an argument.
* This will register NOT_A_COORDINATE for X and Ys, and use the passed keyboard for proximity.
* @param word the char sequence to set as the composing word.
* @param previousWord the previous word, to use as context for suggestions. Can be null if
* the context is nil (typically, at start of text).
* @param keyboard the keyboard this is typed on, for coordinate info/proximity.
*/
public void setComposingWord(final CharSequence word, final Keyboard keyboard) {
public void setComposingWord(final CharSequence word, final String previousWord,
final Keyboard keyboard) {
reset();
final int length = word.length();
for (int i = 0; i < length; i = Character.offsetByCodePoints(word, i, 1)) {
@ -293,6 +305,7 @@ public final class WordComposer {
addKeyInfo(codePoint, keyboard);
}
mIsResumed = true;
mPreviousWord = previousWord;
}
/**
@ -343,6 +356,10 @@ public final class WordComposer {
return mTypedWord.toString();
}
public String getPreviousWord() {
return mPreviousWord;
}
/**
* Whether or not the user typed a capital letter as the first letter in the word
* @return capitalization preference
@ -388,18 +405,21 @@ public final class WordComposer {
}
/**
* Saves the caps mode at the start of composing.
* Saves the caps mode and the previous word at the start of composing.
*
* WordComposer needs to know about this for several reasons. The first is, we need to know
* after the fact what the reason was, to register the correct form into the user history
* dictionary: if the word was automatically capitalized, we should insert it in all-lower
* case but if it's a manual pressing of shift, then it should be inserted as is.
* WordComposer needs to know about the caps mode for several reasons. The first is, we need
* to know after the fact what the reason was, to register the correct form into the user
* history dictionary: if the word was automatically capitalized, we should insert it in
* all-lower case but if it's a manual pressing of shift, then it should be inserted as is.
* Also, batch input needs to know about the current caps mode to display correctly
* capitalized suggestions.
* @param mode the mode at the time of start
* @param previousWord the previous word as context for suggestions. May be null if none.
*/
public void setCapitalizedModeAtStartComposingTime(final int mode) {
public void setCapitalizedModeAndPreviousWordAtStartComposingTime(final int mode,
final String previousWord) {
mCapitalizedMode = mode;
mPreviousWord = previousWord;
}
/**
@ -451,6 +471,7 @@ public final class WordComposer {
mCapsCount = 0;
mDigitsCount = 0;
mIsBatchMode = false;
mPreviousWord = mTypedWord.toString();
mTypedWord.setLength(0);
mCodePointSize = 0;
mTrailingSingleQuotesCount = 0;
@ -464,7 +485,8 @@ public final class WordComposer {
return lastComposedWord;
}
public void resumeSuggestionOnLastComposedWord(final LastComposedWord lastComposedWord) {
public void resumeSuggestionOnLastComposedWord(final LastComposedWord lastComposedWord,
final String previousWord) {
mPrimaryKeyCodes = lastComposedWord.mPrimaryKeyCodes;
mInputPointers.set(lastComposedWord.mInputPointers);
mTypedWord.setLength(0);
@ -475,6 +497,7 @@ public final class WordComposer {
mCursorPositionWithinWord = mCodePointSize;
mRejectedBatchModeSuggestion = null;
mIsResumed = true;
mPreviousWord = previousWord;
}
public boolean isBatchMode() {

View File

@ -26,8 +26,15 @@ import android.test.suitebuilder.annotation.SmallTest;
public class WordComposerTests extends AndroidTestCase {
public void testMoveCursor() {
final WordComposer wc = new WordComposer();
// BMP is the Basic Multilingual Plane, as defined by Unicode. This includes
// most characters for most scripts, including all Roman alphabet languages,
// CJK, Arabic, Hebrew. Notable exceptions include some emoji and some
// very rare Chinese ideograms. BMP characters can be encoded on 2 bytes
// in UTF-16, whereas those outside the BMP need 4 bytes.
// http://en.wikipedia.org/wiki/Plane_(Unicode)#Basic_Multilingual_Plane
final String STR_WITHIN_BMP = "abcdef";
wc.setComposingWord(STR_WITHIN_BMP, null);
final String PREVWORD = "prevword";
wc.setComposingWord(STR_WITHIN_BMP, PREVWORD, null);
assertEquals(wc.size(),
STR_WITHIN_BMP.codePointCount(0, STR_WITHIN_BMP.length()));
assertFalse(wc.isCursorFrontOrMiddleOfComposingWord());
@ -43,13 +50,19 @@ public class WordComposerTests extends AndroidTestCase {
// Move the cursor to after the 'f'
assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(1));
assertFalse(wc.isCursorFrontOrMiddleOfComposingWord());
// Check the previous word is still there
assertEquals(PREVWORD, wc.getPreviousWord());
// Move the cursor past the end of the word
assertFalse(wc.moveCursorByAndReturnIfInsideComposingWord(1));
assertFalse(wc.moveCursorByAndReturnIfInsideComposingWord(15));
// Do what LatinIME does when the cursor is moved outside of the word,
// and check the behavior is correct.
wc.reset();
assertNull(wc.getPreviousWord());
// \uD861\uDED7 is 𨛗, a character outside the BMP
final String STR_WITH_SUPPLEMENTARY_CHAR = "abcde\uD861\uDED7fgh";
wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null);
wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null, null);
assertEquals(wc.size(), STR_WITH_SUPPLEMENTARY_CHAR.codePointCount(0,
STR_WITH_SUPPLEMENTARY_CHAR.length()));
assertFalse(wc.isCursorFrontOrMiddleOfComposingWord());
@ -59,34 +72,40 @@ public class WordComposerTests extends AndroidTestCase {
assertTrue(wc.isCursorFrontOrMiddleOfComposingWord());
assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(1));
assertFalse(wc.isCursorFrontOrMiddleOfComposingWord());
assertNull(wc.getPreviousWord());
wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null);
wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, STR_WITHIN_BMP, null);
wc.setCursorPositionWithinWord(3);
assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(7));
assertEquals(STR_WITHIN_BMP, wc.getPreviousWord());
wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null);
wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, STR_WITH_SUPPLEMENTARY_CHAR, null);
wc.setCursorPositionWithinWord(3);
assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(7));
assertEquals(STR_WITH_SUPPLEMENTARY_CHAR, wc.getPreviousWord());
wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null);
wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, STR_WITHIN_BMP, null);
wc.setCursorPositionWithinWord(3);
assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(-3));
assertFalse(wc.moveCursorByAndReturnIfInsideComposingWord(-1));
assertEquals(STR_WITHIN_BMP, wc.getPreviousWord());
wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null);
wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null, null);
wc.setCursorPositionWithinWord(3);
assertFalse(wc.moveCursorByAndReturnIfInsideComposingWord(-9));
assertNull(wc.getPreviousWord());
wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null);
wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, STR_WITH_SUPPLEMENTARY_CHAR, null);
assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(-10));
assertEquals(STR_WITH_SUPPLEMENTARY_CHAR, wc.getPreviousWord());
wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null);
wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null, null);
assertFalse(wc.moveCursorByAndReturnIfInsideComposingWord(-11));
wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null);
wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null, null);
assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(0));
wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null);
wc.setComposingWord(STR_WITH_SUPPLEMENTARY_CHAR, null, null);
wc.setCursorPositionWithinWord(2);
assertTrue(wc.moveCursorByAndReturnIfInsideComposingWord(0));
}