Add a sequence number to SuggestedWords.

This allows testing for suggestion freshness in an asynchronous
suggestions world.

Bug: 11301597
Change-Id: Ic76cd17568598d8534aec81e037f9e37f52eb6b4
main
Jean Chalard 2013-10-22 10:51:11 +09:00
parent 859b117118
commit ac76463306
3 changed files with 100 additions and 56 deletions

View File

@ -1818,12 +1818,15 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
@Override
public boolean handleMessage(final Message msg) {
// TODO: straighten message passing - we don't need two kinds of messages calling
// each other.
switch (msg.what) {
case MSG_UPDATE_GESTURE_PREVIEW_AND_SUGGESTION_STRIP:
updateBatchInput((InputPointers)msg.obj);
updateBatchInput((InputPointers)msg.obj, msg.arg2 /* sequenceNumber */);
break;
case MSG_GET_SUGGESTED_WORDS:
mLatinIme.getSuggestedWords(msg.arg1, (OnGetSuggestedWordsCallback) msg.obj);
mLatinIme.getSuggestedWords(msg.arg1 /* sessionId */,
msg.arg2 /* sequenceNumber */, (OnGetSuggestedWordsCallback) msg.obj);
break;
}
return true;
@ -1840,14 +1843,15 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
// Run in the Handler thread.
private void updateBatchInput(final InputPointers batchPointers) {
private void updateBatchInput(final InputPointers batchPointers, final int sequenceNumber) {
synchronized (mLock) {
if (!mInBatchInput) {
// Batch input has ended or canceled while the message was being delivered.
return;
}
getSuggestedWordsGestureLocked(batchPointers, new OnGetSuggestedWordsCallback() {
getSuggestedWordsGestureLocked(batchPointers, sequenceNumber,
new OnGetSuggestedWordsCallback() {
@Override
public void onGetSuggestedWords(final SuggestedWords suggestedWords) {
mLatinIme.mHandler.showGesturePreviewAndSuggestionStrip(
@ -1858,12 +1862,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
// 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)) {
return;
}
mHandler.obtainMessage(MSG_UPDATE_GESTURE_PREVIEW_AND_SUGGESTION_STRIP, batchPointers)
.sendToTarget();
mHandler.obtainMessage(MSG_UPDATE_GESTURE_PREVIEW_AND_SUGGESTION_STRIP, 0 /* arg1 */,
sequenceNumber /* arg2 */, batchPointers /* obj */).sendToTarget();
}
public void onCancelBatchInput() {
@ -1877,7 +1882,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// Run in the UI thread.
public void onEndBatchInput(final InputPointers batchPointers) {
synchronized(mLock) {
getSuggestedWordsGestureLocked(batchPointers, new OnGetSuggestedWordsCallback() {
getSuggestedWordsGestureLocked(batchPointers, SuggestedWords.NOT_A_SEQUENCE_NUMBER,
new OnGetSuggestedWordsCallback() {
@Override
public void onGetSuggestedWords(final SuggestedWords suggestedWords) {
mInBatchInput = false;
@ -1892,10 +1898,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// {@link LatinIME#getSuggestedWords(int)} method calls with same session id have to
// be synchronized.
private void getSuggestedWordsGestureLocked(final InputPointers batchPointers,
final OnGetSuggestedWordsCallback callback) {
final int sequenceNumber, final OnGetSuggestedWordsCallback callback) {
mLatinIme.mWordComposer.setBatchInputPointers(batchPointers);
mLatinIme.getSuggestedWordsOrOlderSuggestionsAsync(Suggest.SESSION_GESTURE,
new OnGetSuggestedWordsCallback() {
sequenceNumber, new OnGetSuggestedWordsCallback() {
@Override
public void onGetSuggestedWords(SuggestedWords suggestedWords) {
final int suggestionCount = suggestedWords.size();
@ -1910,9 +1916,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) {
mHandler.obtainMessage(MSG_GET_SUGGESTED_WORDS, sessionId, 0, callback).sendToTarget();
mHandler.obtainMessage(MSG_GET_SUGGESTED_WORDS, sessionId, sequenceNumber, callback)
.sendToTarget();
}
private void onDestroy() {
@ -1933,11 +1940,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
public void onUpdateBatchInput(final InputPointers batchPointers) {
if (mSettings.getCurrent().mPhraseGestureEnabled) {
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)) {
final String[] commitParts = candidate.mWord.split(" ", 2);
batchPointers.shift(candidate.mIndexOfTouchPointOfSecondWord);
@ -1946,10 +1970,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mSpaceState = SPACE_STATE_PHANTOM;
mKeyboardSwitcher.updateShiftState();
mWordComposer.setCapitalizedModeAtStartComposingTime(getActualCapsMode());
++mAutoCommitSequenceNumber;
}
}
}
mInputUpdater.onUpdateBatchInput(batchPointers);
mInputUpdater.onUpdateBatchInput(batchPointers, mAutoCommitSequenceNumber);
}
// This method must run in UI Thread.
@ -2477,7 +2502,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
final AsyncResultHolder<SuggestedWords> holder = new AsyncResultHolder<SuggestedWords>();
getSuggestedWordsOrOlderSuggestionsAsync(Suggest.SESSION_TYPING,
new OnGetSuggestedWordsCallback() {
SuggestedWords.NOT_A_SEQUENCE_NUMBER, new OnGetSuggestedWordsCallback() {
@Override
public void onGetSuggestedWords(final SuggestedWords suggestedWords) {
holder.set(suggestedWords);
@ -2492,7 +2517,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 Keyboard keyboard = mKeyboardSwitcher.getKeyboard();
final Suggest suggest = mSuggest;
@ -2517,18 +2542,19 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
suggest.getSuggestedWords(mWordComposer, prevWord, keyboard.getProximityInfo(),
currentSettings.mBlockPotentiallyOffensive, currentSettings.mCorrectionEnabled,
additionalFeaturesOptions, sessionId, callback);
additionalFeaturesOptions, sessionId, sequenceNumber, callback);
}
private void getSuggestedWordsOrOlderSuggestionsAsync(final int sessionId,
final OnGetSuggestedWordsCallback callback) {
mInputUpdater.getSuggestedWords(sessionId, new OnGetSuggestedWordsCallback() {
@Override
public void onGetSuggestedWords(SuggestedWords suggestedWords) {
callback.onGetSuggestedWords(maybeRetrieveOlderSuggestions(
mWordComposer.getTypedWord(), suggestedWords));
}
});
final int sequenceNumber, final OnGetSuggestedWordsCallback callback) {
mInputUpdater.getSuggestedWords(sessionId, sequenceNumber,
new OnGetSuggestedWordsCallback() {
@Override
public void onGetSuggestedWords(SuggestedWords suggestedWords) {
callback.onGetSuggestedWords(maybeRetrieveOlderSuggestions(
mWordComposer.getTypedWord(), suggestedWords));
}
});
}
private SuggestedWords maybeRetrieveOlderSuggestions(final String typedWord,
@ -2861,30 +2887,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
// compute suggestions for it instead.
mInputUpdater.getSuggestedWords(Suggest.SESSION_TYPING,
new OnGetSuggestedWordsCallback() {
@Override
public void onGetSuggestedWords(
final SuggestedWords suggestedWordsIncludingTypedWord) {
final SuggestedWords suggestedWords;
if (suggestedWordsIncludingTypedWord.size() > 1) {
// 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.
// The #getSuggestedWordsExcludingTypedWord() method sets willAutoCorrect to
// false.
suggestedWords = suggestedWordsIncludingTypedWord
.getSuggestedWordsExcludingTypedWord();
} else {
// No saved suggestions, and we were unable to compute any good one either.
// Rather than displaying an empty suggestion strip, we'll display the
// original word alone in the middle.
// Since there is only one word, willAutoCorrect is false.
suggestedWords = suggestedWordsIncludingTypedWord;
}
// We need to pass typedWord because mWordComposer.mTypedWord may differ from
// typedWord.
unsetIsAutoCorrectionIndicatorOnAndCallShowSuggestionStrip(suggestedWords,
typedWord);
}});
SuggestedWords.NOT_A_SEQUENCE_NUMBER, new OnGetSuggestedWordsCallback() {
@Override
public void onGetSuggestedWords(
final SuggestedWords suggestedWordsIncludingTypedWord) {
final SuggestedWords suggestedWords;
if (suggestedWordsIncludingTypedWord.size() > 1) {
// 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. The #getSuggestedWordsExcludingTypedWord() method sets
// willAutoCorrect to false.
suggestedWords = suggestedWordsIncludingTypedWord
.getSuggestedWordsExcludingTypedWord();
} else {
// No saved suggestions, and we were unable to compute any good one
// either. Rather than displaying an empty suggestion strip, we'll
// display the original word alone in the middle.
// Since there is only one word, willAutoCorrect is false.
suggestedWords = suggestedWordsIncludingTypedWord;
}
// We need to pass typedWord because mWordComposer.mTypedWord may
// differ from typedWord.
unsetIsAutoCorrectionIndicatorOnAndCallShowSuggestionStrip(
suggestedWords, typedWord);
}});
} else {
// We found suggestion spans in the word. We'll create the SuggestedWords out of
// them, and make willAutoCorrect false.

View File

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

View File

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