[DO NOT MERGE] Add a sequence number to SuggestedWords.

This allows testing for suggestion freshness in an asynchronous
suggestions world.
Cherry-pick of Ic76cd175

Bug: 11301597
Change-Id: I45a84de0632062475eebe30234b3147f1c680359
This commit is contained in:
Jean Chalard 2013-10-22 10:51:11 +09:00
parent 47568d5e19
commit 6b8d2d31f9
3 changed files with 100 additions and 57 deletions

View file

@ -1835,12 +1835,15 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
@Override @Override
public boolean handleMessage(final Message msg) { public boolean handleMessage(final Message msg) {
// TODO: straighten message passing - we don't need two kinds of messages calling
// each other.
switch (msg.what) { switch (msg.what) {
case MSG_UPDATE_GESTURE_PREVIEW_AND_SUGGESTION_STRIP: case MSG_UPDATE_GESTURE_PREVIEW_AND_SUGGESTION_STRIP:
updateBatchInput((InputPointers)msg.obj); updateBatchInput((InputPointers)msg.obj, msg.arg2 /* sequenceNumber */);
break; break;
case MSG_GET_SUGGESTED_WORDS: case MSG_GET_SUGGESTED_WORDS:
mLatinIme.getSuggestedWords(msg.arg1, (OnGetSuggestedWordsCallback) msg.obj); mLatinIme.getSuggestedWords(msg.arg1 /* sessionId */,
msg.arg2 /* sequenceNumber */, (OnGetSuggestedWordsCallback) msg.obj);
break; break;
} }
return true; return true;
@ -1857,14 +1860,15 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
} }
// Run in the Handler thread. // Run in the Handler thread.
private void updateBatchInput(final InputPointers batchPointers) { private void updateBatchInput(final InputPointers batchPointers, final int sequenceNumber) {
synchronized (mLock) { synchronized (mLock) {
if (!mInBatchInput) { if (!mInBatchInput) {
// Batch input has ended or canceled while the message was being delivered. // Batch input has ended or canceled while the message was being delivered.
return; return;
} }
getSuggestedWordsGestureLocked(batchPointers, new OnGetSuggestedWordsCallback() { getSuggestedWordsGestureLocked(batchPointers, sequenceNumber,
new OnGetSuggestedWordsCallback() {
@Override @Override
public void onGetSuggestedWords(final SuggestedWords suggestedWords) { public void onGetSuggestedWords(final SuggestedWords suggestedWords) {
mLatinIme.mHandler.showGesturePreviewAndSuggestionStrip( mLatinIme.mHandler.showGesturePreviewAndSuggestionStrip(
@ -1875,13 +1879,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
} }
// Run in the UI thread. // Run in the UI thread.
public void onUpdateBatchInput(final InputPointers batchPointers) { public void onUpdateBatchInput(final InputPointers batchPointers,
final int sequenceNumber) {
if (mHandler.hasMessages(MSG_UPDATE_GESTURE_PREVIEW_AND_SUGGESTION_STRIP)) { if (mHandler.hasMessages(MSG_UPDATE_GESTURE_PREVIEW_AND_SUGGESTION_STRIP)) {
return; return;
} }
mHandler.obtainMessage( mHandler.obtainMessage(MSG_UPDATE_GESTURE_PREVIEW_AND_SUGGESTION_STRIP, 0 /* arg1 */,
MSG_UPDATE_GESTURE_PREVIEW_AND_SUGGESTION_STRIP, batchPointers) sequenceNumber /* arg2 */, batchPointers /* obj */).sendToTarget();
.sendToTarget();
} }
public void onCancelBatchInput() { public void onCancelBatchInput() {
@ -1895,7 +1899,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// Run in the UI thread. // Run in the UI thread.
public void onEndBatchInput(final InputPointers batchPointers) { public void onEndBatchInput(final InputPointers batchPointers) {
synchronized(mLock) { synchronized(mLock) {
getSuggestedWordsGestureLocked(batchPointers, new OnGetSuggestedWordsCallback() { getSuggestedWordsGestureLocked(batchPointers, SuggestedWords.NOT_A_SEQUENCE_NUMBER,
new OnGetSuggestedWordsCallback() {
@Override @Override
public void onGetSuggestedWords(final SuggestedWords suggestedWords) { public void onGetSuggestedWords(final SuggestedWords suggestedWords) {
mInBatchInput = false; mInBatchInput = false;
@ -1910,10 +1915,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// {@link LatinIME#getSuggestedWords(int)} method calls with same session id have to // {@link LatinIME#getSuggestedWords(int)} method calls with same session id have to
// be synchronized. // be synchronized.
private void getSuggestedWordsGestureLocked(final InputPointers batchPointers, private void getSuggestedWordsGestureLocked(final InputPointers batchPointers,
final OnGetSuggestedWordsCallback callback) { final int sequenceNumber, final OnGetSuggestedWordsCallback callback) {
mLatinIme.mWordComposer.setBatchInputPointers(batchPointers); mLatinIme.mWordComposer.setBatchInputPointers(batchPointers);
mLatinIme.getSuggestedWordsOrOlderSuggestionsAsync(Suggest.SESSION_GESTURE, mLatinIme.getSuggestedWordsOrOlderSuggestionsAsync(Suggest.SESSION_GESTURE,
new OnGetSuggestedWordsCallback() { sequenceNumber, new OnGetSuggestedWordsCallback() {
@Override @Override
public void onGetSuggestedWords(SuggestedWords suggestedWords) { public void onGetSuggestedWords(SuggestedWords suggestedWords) {
final int suggestionCount = suggestedWords.size(); final int suggestionCount = suggestedWords.size();
@ -1928,9 +1933,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}); });
} }
public void getSuggestedWords(final int sessionId, public void getSuggestedWords(final int sessionId, final int sequenceNumber,
final OnGetSuggestedWordsCallback callback) { final OnGetSuggestedWordsCallback callback) {
mHandler.obtainMessage(MSG_GET_SUGGESTED_WORDS, sessionId, 0, callback).sendToTarget(); mHandler.obtainMessage(MSG_GET_SUGGESTED_WORDS, sessionId, sequenceNumber, callback)
.sendToTarget();
} }
private void onDestroy() { private void onDestroy() {
@ -1951,11 +1957,28 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
} }
} }
/* The sequence number member is only used in onUpdateBatchInput. It is increased each time
* auto-commit happens. The reason we need this is, when auto-commit happens we trim the
* input pointers that are held in a singleton, and to know how much to trim we rely on the
* results of the suggestion process that is held in mSuggestedWords.
* However, the suggestion process is asynchronous, and sometimes we may enter the
* onUpdateBatchInput method twice without having recomputed suggestions yet, or having
* received new suggestions generated from not-yet-trimmed input pointers. In this case, the
* mIndexOfTouchPointOfSecondWords member will be out of date, and we must not use it lest we
* remove an unrelated number of pointers (possibly even more than are left in the input
* pointers, leading to a crash).
* To avoid that, we increase the sequence number each time we auto-commit and trim the
* input pointers, and we do not use any suggested words that have been generated with an
* earlier sequence number.
*/
private int mAutoCommitSequenceNumber = 1;
@Override @Override
public void onUpdateBatchInput(final InputPointers batchPointers) { public void onUpdateBatchInput(final InputPointers batchPointers) {
if (mSettings.getCurrent().mPhraseGestureEnabled) { if (mSettings.getCurrent().mPhraseGestureEnabled) {
final SuggestedWordInfo candidate = mSuggestedWords.getAutoCommitCandidate(); final SuggestedWordInfo candidate = mSuggestedWords.getAutoCommitCandidate();
if (null != candidate) { // If these suggested words have been generated with out of date input pointers, then
// we skip auto-commit (see comments above on the mSequenceNumber member).
if (null != candidate && mSuggestedWords.mSequenceNumber >= mAutoCommitSequenceNumber) {
if (candidate.mSourceDict.shouldAutoCommit(candidate)) { if (candidate.mSourceDict.shouldAutoCommit(candidate)) {
final String[] commitParts = candidate.mWord.split(" ", 2); final String[] commitParts = candidate.mWord.split(" ", 2);
batchPointers.shift(candidate.mIndexOfTouchPointOfSecondWord); batchPointers.shift(candidate.mIndexOfTouchPointOfSecondWord);
@ -1964,10 +1987,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mSpaceState = SPACE_STATE_PHANTOM; mSpaceState = SPACE_STATE_PHANTOM;
mKeyboardSwitcher.updateShiftState(); mKeyboardSwitcher.updateShiftState();
mWordComposer.setCapitalizedModeAtStartComposingTime(getActualCapsMode()); mWordComposer.setCapitalizedModeAtStartComposingTime(getActualCapsMode());
++mAutoCommitSequenceNumber;
} }
} }
} }
mInputUpdater.onUpdateBatchInput(batchPointers); mInputUpdater.onUpdateBatchInput(batchPointers, mAutoCommitSequenceNumber);
} }
// This method must run in UI Thread. // This method must run in UI Thread.
@ -2502,7 +2526,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
final AsyncResultHolder<SuggestedWords> holder = new AsyncResultHolder<SuggestedWords>(); final AsyncResultHolder<SuggestedWords> holder = new AsyncResultHolder<SuggestedWords>();
getSuggestedWordsOrOlderSuggestionsAsync(Suggest.SESSION_TYPING, getSuggestedWordsOrOlderSuggestionsAsync(Suggest.SESSION_TYPING,
new OnGetSuggestedWordsCallback() { SuggestedWords.NOT_A_SEQUENCE_NUMBER, new OnGetSuggestedWordsCallback() {
@Override @Override
public void onGetSuggestedWords(final SuggestedWords suggestedWords) { public void onGetSuggestedWords(final SuggestedWords suggestedWords) {
holder.set(suggestedWords); holder.set(suggestedWords);
@ -2517,7 +2541,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
} }
} }
private void getSuggestedWords(final int sessionId, private void getSuggestedWords(final int sessionId, final int sequenceNumber,
final OnGetSuggestedWordsCallback callback) { final OnGetSuggestedWordsCallback callback) {
final Keyboard keyboard = mKeyboardSwitcher.getKeyboard(); final Keyboard keyboard = mKeyboardSwitcher.getKeyboard();
final Suggest suggest = mSuggest; final Suggest suggest = mSuggest;
@ -2542,18 +2566,19 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
} }
suggest.getSuggestedWords(mWordComposer, prevWord, keyboard.getProximityInfo(), suggest.getSuggestedWords(mWordComposer, prevWord, keyboard.getProximityInfo(),
currentSettings.mBlockPotentiallyOffensive, currentSettings.mCorrectionEnabled, currentSettings.mBlockPotentiallyOffensive, currentSettings.mCorrectionEnabled,
additionalFeaturesOptions, sessionId, callback); additionalFeaturesOptions, sessionId, sequenceNumber, callback);
} }
private void getSuggestedWordsOrOlderSuggestionsAsync(final int sessionId, private void getSuggestedWordsOrOlderSuggestionsAsync(final int sessionId,
final OnGetSuggestedWordsCallback callback) { final int sequenceNumber, final OnGetSuggestedWordsCallback callback) {
mInputUpdater.getSuggestedWords(sessionId, new OnGetSuggestedWordsCallback() { mInputUpdater.getSuggestedWords(sessionId, sequenceNumber,
@Override new OnGetSuggestedWordsCallback() {
public void onGetSuggestedWords(SuggestedWords suggestedWords) { @Override
callback.onGetSuggestedWords(maybeRetrieveOlderSuggestions( public void onGetSuggestedWords(SuggestedWords suggestedWords) {
mWordComposer.getTypedWord(), suggestedWords)); callback.onGetSuggestedWords(maybeRetrieveOlderSuggestions(
} mWordComposer.getTypedWord(), suggestedWords));
}); }
});
} }
private SuggestedWords maybeRetrieveOlderSuggestions(final String typedWord, private SuggestedWords maybeRetrieveOlderSuggestions(final String typedWord,
@ -2888,30 +2913,30 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// We come here if there weren't any suggestion spans on this word. We will try to // We come here if there weren't any suggestion spans on this word. We will try to
// compute suggestions for it instead. // compute suggestions for it instead.
mInputUpdater.getSuggestedWords(Suggest.SESSION_TYPING, mInputUpdater.getSuggestedWords(Suggest.SESSION_TYPING,
new OnGetSuggestedWordsCallback() { SuggestedWords.NOT_A_SEQUENCE_NUMBER, new OnGetSuggestedWordsCallback() {
@Override @Override
public void onGetSuggestedWords( public void onGetSuggestedWords(
final SuggestedWords suggestedWordsIncludingTypedWord) { final SuggestedWords suggestedWordsIncludingTypedWord) {
final SuggestedWords suggestedWords; final SuggestedWords suggestedWords;
if (suggestedWordsIncludingTypedWord.size() > 1) { if (suggestedWordsIncludingTypedWord.size() > 1) {
// We were able to compute new suggestions for this word. // 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. // Remove the typed word, since we don't want to display it in this
// The #getSuggestedWordsExcludingTypedWord() method sets willAutoCorrect to // case. The #getSuggestedWordsExcludingTypedWord() method sets
// false. // willAutoCorrect to false.
suggestedWords = suggestedWordsIncludingTypedWord suggestedWords = suggestedWordsIncludingTypedWord
.getSuggestedWordsExcludingTypedWord(); .getSuggestedWordsExcludingTypedWord();
} else { } else {
// No saved suggestions, and we were unable to compute any good one either. // No saved suggestions, and we were unable to compute any good one
// Rather than displaying an empty suggestion strip, we'll display the // either. Rather than displaying an empty suggestion strip, we'll
// original word alone in the middle. // display the original word alone in the middle.
// Since there is only one word, willAutoCorrect is false. // Since there is only one word, willAutoCorrect is false.
suggestedWords = suggestedWordsIncludingTypedWord; suggestedWords = suggestedWordsIncludingTypedWord;
} }
// We need to pass typedWord because mWordComposer.mTypedWord may differ from // We need to pass typedWord because mWordComposer.mTypedWord may
// typedWord. // differ from typedWord.
unsetIsAutoCorrectionIndicatorOnAndCallShowSuggestionStrip(suggestedWords, unsetIsAutoCorrectionIndicatorOnAndCallShowSuggestionStrip(
typedWord); suggestedWords, typedWord);
}}); }});
} else { } else {
// We found suggestion spans in the word. We'll create the SuggestedWords out of // We found suggestion spans in the word. We'll create the SuggestedWords out of
// them, and make willAutoCorrect false. // them, and make willAutoCorrect false.

View file

@ -217,15 +217,17 @@ public final class Suggest {
public void getSuggestedWords(final WordComposer wordComposer, public void getSuggestedWords(final WordComposer wordComposer,
final String prevWordForBigram, final ProximityInfo proximityInfo, final String prevWordForBigram, final ProximityInfo proximityInfo,
final boolean blockOffensiveWords, final boolean isCorrectionEnabled, final boolean blockOffensiveWords, final boolean isCorrectionEnabled,
final int[] additionalFeaturesOptions, final int sessionId, final int[] additionalFeaturesOptions, final int sessionId, final int sequenceNumber,
final OnGetSuggestedWordsCallback callback) { final OnGetSuggestedWordsCallback callback) {
LatinImeLogger.onStartSuggestion(prevWordForBigram); LatinImeLogger.onStartSuggestion(prevWordForBigram);
if (wordComposer.isBatchMode()) { if (wordComposer.isBatchMode()) {
getSuggestedWordsForBatchInput(wordComposer, prevWordForBigram, proximityInfo, getSuggestedWordsForBatchInput(wordComposer, prevWordForBigram, proximityInfo,
blockOffensiveWords, additionalFeaturesOptions, sessionId, callback); blockOffensiveWords, additionalFeaturesOptions, sessionId, sequenceNumber,
callback);
} else { } else {
getSuggestedWordsForTypingInput(wordComposer, prevWordForBigram, proximityInfo, getSuggestedWordsForTypingInput(wordComposer, prevWordForBigram, proximityInfo,
blockOffensiveWords, isCorrectionEnabled, additionalFeaturesOptions, callback); blockOffensiveWords, isCorrectionEnabled, additionalFeaturesOptions,
sequenceNumber, callback);
} }
} }
@ -234,7 +236,8 @@ public final class Suggest {
private void getSuggestedWordsForTypingInput(final WordComposer wordComposer, private void getSuggestedWordsForTypingInput(final WordComposer wordComposer,
final String prevWordForBigram, final ProximityInfo proximityInfo, final String prevWordForBigram, final ProximityInfo proximityInfo,
final boolean blockOffensiveWords, final boolean isCorrectionEnabled, final boolean blockOffensiveWords, final boolean isCorrectionEnabled,
final int[] additionalFeaturesOptions, final OnGetSuggestedWordsCallback callback) { final int[] additionalFeaturesOptions, final int sequenceNumber,
final OnGetSuggestedWordsCallback callback) {
final int trailingSingleQuotesCount = wordComposer.trailingSingleQuotesCount(); final int trailingSingleQuotesCount = wordComposer.trailingSingleQuotesCount();
final BoundedTreeSet suggestionsSet = new BoundedTreeSet(sSuggestedWordInfoComparator, final BoundedTreeSet suggestionsSet = new BoundedTreeSet(sSuggestedWordInfoComparator,
MAX_SUGGESTIONS); MAX_SUGGESTIONS);
@ -347,7 +350,7 @@ public final class Suggest {
hasAutoCorrection, /* willAutoCorrect */ hasAutoCorrection, /* willAutoCorrect */
false /* isPunctuationSuggestions */, false /* isPunctuationSuggestions */,
false /* isObsoleteSuggestions */, false /* isObsoleteSuggestions */,
!wordComposer.isComposingWord() /* isPrediction */)); !wordComposer.isComposingWord() /* isPrediction */, sequenceNumber));
} }
// Retrieves suggestions for the batch input // Retrieves suggestions for the batch input
@ -355,7 +358,8 @@ public final class Suggest {
private void getSuggestedWordsForBatchInput(final WordComposer wordComposer, private void getSuggestedWordsForBatchInput(final WordComposer wordComposer,
final String prevWordForBigram, final ProximityInfo proximityInfo, final String prevWordForBigram, final ProximityInfo proximityInfo,
final boolean blockOffensiveWords, final int[] additionalFeaturesOptions, final boolean blockOffensiveWords, final int[] additionalFeaturesOptions,
final int sessionId, final OnGetSuggestedWordsCallback callback) { final int sessionId, final int sequenceNumber,
final OnGetSuggestedWordsCallback callback) {
final BoundedTreeSet suggestionsSet = new BoundedTreeSet(sSuggestedWordInfoComparator, final BoundedTreeSet suggestionsSet = new BoundedTreeSet(sSuggestedWordInfoComparator,
MAX_SUGGESTIONS); MAX_SUGGESTIONS);
@ -408,7 +412,7 @@ public final class Suggest {
false /* willAutoCorrect */, false /* willAutoCorrect */,
false /* isPunctuationSuggestions */, false /* isPunctuationSuggestions */,
false /* isObsoleteSuggestions */, false /* isObsoleteSuggestions */,
false /* isPrediction */)); false /* isPrediction */, sequenceNumber));
} }
private static ArrayList<SuggestedWordInfo> getSuggestionsInfoListWithDebugInfo( private static ArrayList<SuggestedWordInfo> getSuggestionsInfoListWithDebugInfo(

View file

@ -29,6 +29,7 @@ import java.util.HashSet;
public final class SuggestedWords { public final class SuggestedWords {
public static final int INDEX_OF_TYPED_WORD = 0; public static final int INDEX_OF_TYPED_WORD = 0;
public static final int INDEX_OF_AUTO_CORRECTION = 1; public static final int INDEX_OF_AUTO_CORRECTION = 1;
public static final int NOT_A_SEQUENCE_NUMBER = -1;
private static final ArrayList<SuggestedWordInfo> EMPTY_WORD_INFO_LIST = private static final ArrayList<SuggestedWordInfo> EMPTY_WORD_INFO_LIST =
CollectionUtils.newArrayList(0); CollectionUtils.newArrayList(0);
@ -43,6 +44,7 @@ public final class SuggestedWords {
public final boolean mIsPunctuationSuggestions; public final boolean mIsPunctuationSuggestions;
public final boolean mIsObsoleteSuggestions; public final boolean mIsObsoleteSuggestions;
public final boolean mIsPrediction; public final boolean mIsPrediction;
public final int mSequenceNumber; // Sequence number for auto-commit.
private final ArrayList<SuggestedWordInfo> mSuggestedWordInfoList; private final ArrayList<SuggestedWordInfo> mSuggestedWordInfoList;
public SuggestedWords(final ArrayList<SuggestedWordInfo> suggestedWordInfoList, public SuggestedWords(final ArrayList<SuggestedWordInfo> suggestedWordInfoList,
@ -51,12 +53,24 @@ public final class SuggestedWords {
final boolean isPunctuationSuggestions, final boolean isPunctuationSuggestions,
final boolean isObsoleteSuggestions, final boolean isObsoleteSuggestions,
final boolean isPrediction) { final boolean isPrediction) {
this(suggestedWordInfoList, typedWordValid, willAutoCorrect, isPunctuationSuggestions,
isObsoleteSuggestions, isPrediction, NOT_A_SEQUENCE_NUMBER);
}
public SuggestedWords(final ArrayList<SuggestedWordInfo> suggestedWordInfoList,
final boolean typedWordValid,
final boolean willAutoCorrect,
final boolean isPunctuationSuggestions,
final boolean isObsoleteSuggestions,
final boolean isPrediction,
final int sequenceNumber) {
mSuggestedWordInfoList = suggestedWordInfoList; mSuggestedWordInfoList = suggestedWordInfoList;
mTypedWordValid = typedWordValid; mTypedWordValid = typedWordValid;
mWillAutoCorrect = willAutoCorrect; mWillAutoCorrect = willAutoCorrect;
mIsPunctuationSuggestions = isPunctuationSuggestions; mIsPunctuationSuggestions = isPunctuationSuggestions;
mIsObsoleteSuggestions = isObsoleteSuggestions; mIsObsoleteSuggestions = isObsoleteSuggestions;
mIsPrediction = isPrediction; mIsPrediction = isPrediction;
mSequenceNumber = sequenceNumber;
} }
public boolean isEmpty() { public boolean isEmpty() {