Fix a bug where the cache would be out of sync

During recorrection, the cursor position when calling
commitText is not necessarily at the end of the
composing text.
Besides, RichInputConnection assumes the cursor is
always after any composing text. This is not correct,
but in the practice, it seems all code paths work.
We should fix this in the future.

Bug: 13060691
Change-Id: I15f71fff62d36e80cf6e4a022c5e78af634b199d
This commit is contained in:
Jean Chalard 2014-02-18 16:58:04 +09:00
parent db21fad18f
commit fe92c174ea
2 changed files with 54 additions and 1 deletions

View File

@ -230,6 +230,9 @@ public final class RichInputConnection {
public void finishComposingText() {
if (DEBUG_BATCH_NESTING) checkBatchEdit();
if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
// TODO: this is not correct! The cursor is not necessarily after the composing text.
// In the practice right now this is only called when input ends so it will be reset so
// it works, but it's wrong and should be fixed.
mCommittedTextBeforeComposingText.append(mComposingText);
mComposingText.setLength(0);
if (null != mIC) {
@ -244,6 +247,9 @@ public final class RichInputConnection {
if (DEBUG_BATCH_NESTING) checkBatchEdit();
if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
mCommittedTextBeforeComposingText.append(text);
// TODO: the following is exceedingly error-prone. Right now when the cursor is in the
// middle of the composing word mComposingText only holds the part of the composing text
// that is before the cursor, so this actually works, but it's terribly confusing. Fix this.
mExpectedSelStart += text.length() - mComposingText.length();
mExpectedSelEnd = mExpectedSelStart;
mComposingText.setLength(0);
@ -347,6 +353,9 @@ public final class RichInputConnection {
public void deleteSurroundingText(final int beforeLength, final int afterLength) {
if (DEBUG_BATCH_NESTING) checkBatchEdit();
// TODO: the following is incorrect if the cursor is not immediately after the composition.
// Right now we never come here in this case because we reset the composing state before we
// come here in this case, but we need to fix this.
final int remainingChars = mComposingText.length() - beforeLength;
if (remainingChars >= 0) {
mComposingText.setLength(remainingChars);
@ -447,8 +456,12 @@ public final class RichInputConnection {
getTextBeforeCursor(Constants.EDITOR_CONTENTS_CACHE_SIZE + (end - start), 0);
mCommittedTextBeforeComposingText.setLength(0);
if (!TextUtils.isEmpty(textBeforeCursor)) {
// The cursor is not necessarily at the end of the composing text, but we have its
// position in mExpectedSelStart and mExpectedSelEnd. In this case we want the start
// of the text, so we should use mExpectedSelStart. In other words, the composing
// text starts (mExpectedSelStart - start) characters before the end of textBeforeCursor
final int indexOfStartOfComposingText =
Math.max(textBeforeCursor.length() - (end - start), 0);
Math.max(textBeforeCursor.length() - (mExpectedSelStart - start), 0);
mComposingText.append(textBeforeCursor.subSequence(indexOfStartOfComposingText,
textBeforeCursor.length()));
mCommittedTextBeforeComposingText.append(
@ -544,6 +557,9 @@ public final class RichInputConnection {
final int checkLength = LOOKBACK_CHARACTER_NUM - 1;
final String reference = prev.length() <= checkLength ? prev.toString()
: prev.subSequence(prev.length() - checkLength, prev.length()).toString();
// TODO: right now the following works because mComposingText holds the part of the
// composing text that is before the cursor, but this is very confusing. We should
// fix it.
final StringBuilder internal = new StringBuilder()
.append(mCommittedTextBeforeComposingText).append(mComposingText);
if (internal.length() > checkLength) {

View File

@ -385,4 +385,41 @@ public class InputLogicTests extends InputTestsBase {
final SuggestedWords suggestedWords = mLatinIME.getSuggestedWords();
assertEquals("no prediction after period", 0, suggestedWords.size());
}
public void testPredictionsAfterRecorrection() {
final String PREFIX = "A ";
final String WORD_TO_TYPE = "Barack";
final String FIRST_NON_TYPED_SUGGESTION = "Barrack";
final int endOfPrefix = PREFIX.length();
final int endOfWord = endOfPrefix + WORD_TO_TYPE.length();
final int endOfSuggestion = endOfPrefix + FIRST_NON_TYPED_SUGGESTION.length();
final int indexForManualCursor = endOfPrefix + 3; // +3 because it's after "Bar" in "Barack"
type(PREFIX);
mLatinIME.onUpdateSelection(0, 0, endOfPrefix, endOfPrefix, -1, -1);
type(WORD_TO_TYPE);
pickSuggestionManually(1, FIRST_NON_TYPED_SUGGESTION);
mLatinIME.onUpdateSelection(endOfPrefix, endOfPrefix, endOfSuggestion, endOfSuggestion,
-1, -1);
runMessages();
type(" ");
mLatinIME.onUpdateSelection(endOfSuggestion, endOfSuggestion,
endOfSuggestion + 1, endOfSuggestion + 1, -1, -1);
sleep(DELAY_TO_WAIT_FOR_PREDICTIONS);
runMessages();
// Simulate a manual cursor move
mInputConnection.setSelection(indexForManualCursor, indexForManualCursor);
mLatinIME.onUpdateSelection(endOfSuggestion + 1, endOfSuggestion + 1,
indexForManualCursor, indexForManualCursor, -1, -1);
sleep(DELAY_TO_WAIT_FOR_PREDICTIONS);
runMessages();
pickSuggestionManually(0, WORD_TO_TYPE);
mLatinIME.onUpdateSelection(indexForManualCursor, indexForManualCursor,
endOfWord, endOfWord, -1, -1);
sleep(DELAY_TO_WAIT_FOR_PREDICTIONS);
runMessages();
// Test the first prediction is displayed
final SuggestedWords suggestedWords = mLatinIME.getSuggestedWords();
assertEquals("predictions after recorrection", "Obama",
suggestedWords.size() > 0 ? suggestedWords.getWord(0) : null);
}
}