If there are no suggestion span, recompute suggestions.

Bug: 8084810
Change-Id: I1743c09c43ca6835bb2f607684b037bf17d36335
main
Jean Chalard 2013-04-10 18:30:11 +09:00
parent 059e084e98
commit 0e9ee4d3bf
4 changed files with 148 additions and 16 deletions

View File

@ -1466,7 +1466,13 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
"", mWordComposer.getTypedWord(), " ", mWordComposer); "", mWordComposer.getTypedWord(), " ", mWordComposer);
} }
} }
commitTyped(LastComposedWord.NOT_A_SEPARATOR); if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) {
// If we are in the middle of a recorrection, we need to commit the recorrection
// first so that we can insert the character at the current cursor position.
resetEntireInputState(mLastSelectionStart);
} else {
commitTyped(LastComposedWord.NOT_A_SEPARATOR);
}
} }
final int keyX, keyY; final int keyX, keyY;
final Keyboard keyboard = mKeyboardSwitcher.getKeyboard(); final Keyboard keyboard = mKeyboardSwitcher.getKeyboard();
@ -1522,8 +1528,12 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
} }
final int wordComposerSize = mWordComposer.size(); final int wordComposerSize = mWordComposer.size();
// Since isComposingWord() is true, the size is at least 1. // Since isComposingWord() is true, the size is at least 1.
final int lastChar = mWordComposer.getCodeAt(wordComposerSize - 1); final int lastChar = mWordComposer.getCodeBeforeCursor();
if (wordComposerSize <= 1) { if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) {
// If we are in the middle of a recorrection, we need to commit the recorrection
// first so that we can insert the batch input at the current cursor position.
resetEntireInputState(mLastSelectionStart);
} else if (wordComposerSize <= 1) {
// We auto-correct the previous (typed, not gestured) string iff it's one character // We auto-correct the previous (typed, not gestured) string iff it's one character
// long. The reason for this is, even in the middle of gesture typing, you'll still // long. The reason for this is, even in the middle of gesture typing, you'll still
// tap one-letter words and you want them auto-corrected (typically, "i" in English // tap one-letter words and you want them auto-corrected (typically, "i" in English
@ -1734,8 +1744,11 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
// during key repeat. // during key repeat.
mHandler.postUpdateShiftState(); mHandler.postUpdateShiftState();
if (mWordComposer.isComposingWord() && !mWordComposer.isCursorAtEndOfComposingWord()) { if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) {
// If we are in the middle of a recorrection, we need to commit the recorrection
// first so that we can remove the character at the current cursor position.
resetEntireInputState(mLastSelectionStart); resetEntireInputState(mLastSelectionStart);
// When we exit this if-clause, mWordComposer.isComposingWord() will return false.
} }
if (mWordComposer.isComposingWord()) { if (mWordComposer.isComposingWord()) {
final int length = mWordComposer.size(); final int length = mWordComposer.size();
@ -1870,7 +1883,9 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
promotePhantomSpace(); promotePhantomSpace();
} }
if (mWordComposer.isComposingWord() && !mWordComposer.isCursorAtEndOfComposingWord()) { if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) {
// If we are in the middle of a recorrection, we need to commit the recorrection
// first so that we can insert the character at the current cursor position.
resetEntireInputState(mLastSelectionStart); resetEntireInputState(mLastSelectionStart);
isComposingWord = false; isComposingWord = false;
} }
@ -1935,7 +1950,11 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
ResearchLogger.latinIME_handleSeparator(primaryCode, mWordComposer.isComposingWord()); ResearchLogger.latinIME_handleSeparator(primaryCode, mWordComposer.isComposingWord());
} }
boolean didAutoCorrect = false; boolean didAutoCorrect = false;
// Handle separator if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) {
// If we are in the middle of a recorrection, we need to commit the recorrection
// first so that we can insert the separator at the current cursor position.
resetEntireInputState(mLastSelectionStart);
}
if (mWordComposer.isComposingWord()) { if (mWordComposer.isComposingWord()) {
if (mSettings.getCurrent().mCorrectionEnabled) { if (mSettings.getCurrent().mCorrectionEnabled) {
// TODO: maybe cache Strings in an <String> sparse array or something // TODO: maybe cache Strings in an <String> sparse array or something
@ -2357,9 +2376,9 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
final Range range = mConnection.getWordRangeAtCursor(mSettings.getWordSeparators(), final Range range = mConnection.getWordRangeAtCursor(mSettings.getWordSeparators(),
0 /* additionalPrecedingWordsCount */); 0 /* additionalPrecedingWordsCount */);
final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList(); final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList();
final String typedWord = range.mWord.toString();
if (range.mWord instanceof SpannableString) { if (range.mWord instanceof SpannableString) {
final SpannableString spannableString = (SpannableString)range.mWord; final SpannableString spannableString = (SpannableString)range.mWord;
final String typedWord = spannableString.toString();
int i = 0; int i = 0;
for (Object object : spannableString.getSpans(0, spannableString.length(), for (Object object : spannableString.getSpans(0, spannableString.length(),
SuggestionSpan.class)) { SuggestionSpan.class)) {
@ -2374,18 +2393,42 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
} }
} }
} }
mWordComposer.setComposingWord(range.mWord, mKeyboardSwitcher.getKeyboard()); mWordComposer.setComposingWord(typedWord, mKeyboardSwitcher.getKeyboard());
mWordComposer.setCursorPositionWithinWord(range.mCharsBefore); mWordComposer.setCursorPositionWithinWord(range.mCharsBefore);
mConnection.setComposingRegion(mLastSelectionStart - range.mCharsBefore, mConnection.setComposingRegion(mLastSelectionStart - range.mCharsBefore,
mLastSelectionEnd + range.mCharsAfter); mLastSelectionEnd + range.mCharsAfter);
final SuggestedWords suggestedWords;
if (suggestions.isEmpty()) { if (suggestions.isEmpty()) {
suggestions.add(new SuggestedWordInfo(range.mWord.toString(), 1, // We come here if there weren't any suggestion spans on this word. We will try to
SuggestedWordInfo.KIND_TYPED, Dictionary.TYPE_RESUMED)); // compute suggestions for it instead.
final SuggestedWords suggestedWordsIncludingTypedWord =
getSuggestedWords(Suggest.SESSION_TYPING);
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;
}
} else {
// We found suggestion spans in the word. We'll create the SuggestedWords out of
// them, and make willAutoCorrect false.
suggestedWords = new SuggestedWords(suggestions,
true /* typedWordValid */, false /* willAutoCorrect */,
false /* isPunctuationSuggestions */, false /* isObsoleteSuggestions */,
false /* isPrediction */);
} }
showSuggestionStrip(new SuggestedWords(suggestions,
true /* typedWordValid */, false /* willAutoCorrect */, // Note that it's very important here that suggestedWords.mWillAutoCorrect is false.
false /* isPunctuationSuggestions */, false /* isObsoleteSuggestions */, // We never want to auto-correct on a resumed suggestion. Please refer to the three
false /* isPrediction */), range.mWord.toString()); // places above where suggestedWords is affected.
showSuggestionStrip(suggestedWords, typedWord);
} }
/** /**

View File

@ -195,4 +195,21 @@ public final class SuggestedWords {
} }
} }
} }
// SuggestedWords is an immutable object, as much as possible. We must not just remove
// words from the member ArrayList as some other parties may expect the object to never change.
public SuggestedWords getSuggestedWordsExcludingTypedWord() {
final ArrayList<SuggestedWordInfo> newSuggestions = CollectionUtils.newArrayList();
for (int i = 0; i < mSuggestedWordInfoList.size(); ++i) {
final SuggestedWordInfo info = mSuggestedWordInfoList.get(i);
if (SuggestedWordInfo.KIND_TYPED != info.mKind) {
newSuggestions.add(info);
}
}
// We should never autocorrect, so we say the typed word is valid. Also, in this case,
// no auto-correction should take place hence willAutoCorrect = false.
return new SuggestedWords(newSuggestions, true /* typedWordValid */,
false /* willAutoCorrect */, mIsPunctuationSuggestions, mIsObsoleteSuggestions,
mIsPrediction);
}
} }

View File

@ -27,6 +27,7 @@ import java.util.Arrays;
*/ */
public final class WordComposer { public final class WordComposer {
private static final int MAX_WORD_LENGTH = Constants.Dictionary.MAX_WORD_LENGTH; private static final int MAX_WORD_LENGTH = Constants.Dictionary.MAX_WORD_LENGTH;
private static final boolean DBG = LatinImeLogger.sDBG;
public static final int CAPS_MODE_OFF = 0; public static final int CAPS_MODE_OFF = 0;
// 1 is shift bit, 2 is caps bit, 4 is auto bit but this is just a convention as these bits // 1 is shift bit, 2 is caps bit, 4 is auto bit but this is just a convention as these bits
@ -132,6 +133,13 @@ public final class WordComposer {
return mPrimaryKeyCodes[index]; return mPrimaryKeyCodes[index];
} }
public int getCodeBeforeCursor() {
if (mCursorPositionWithinWord < 1 || mCursorPositionWithinWord > mPrimaryKeyCodes.length) {
return Constants.NOT_A_CODE;
}
return mPrimaryKeyCodes[mCursorPositionWithinWord - 1];
}
public InputPointers getInputPointers() { public InputPointers getInputPointers() {
return mInputPointers; return mInputPointers;
} }
@ -177,8 +185,12 @@ public final class WordComposer {
mCursorPositionWithinWord = posWithinWord; mCursorPositionWithinWord = posWithinWord;
} }
public boolean isCursorAtEndOfComposingWord() { public boolean isCursorFrontOrMiddleOfComposingWord() {
return mCursorPositionWithinWord == mCodePointSize; if (DBG && mCursorPositionWithinWord > mCodePointSize) {
throw new RuntimeException("Wrong cursor position : " + mCursorPositionWithinWord
+ "in a word of size " + mCodePointSize);
}
return mCursorPositionWithinWord != mCodePointSize;
} }
public void setBatchInputPointers(final InputPointers batchPointers) { public void setBatchInputPointers(final InputPointers batchPointers) {

View File

@ -0,0 +1,60 @@
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.inputmethod.latin;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import java.util.ArrayList;
import java.util.Locale;
@SmallTest
public class SuggestedWordsTests extends AndroidTestCase {
public void testGetSuggestedWordsExcludingTypedWord() {
final String TYPED_WORD = "typed";
final int TYPED_WORD_FREQ = 5;
final int NUMBER_OF_ADDED_SUGGESTIONS = 5;
final ArrayList<SuggestedWordInfo> list = CollectionUtils.newArrayList();
list.add(new SuggestedWordInfo(TYPED_WORD, TYPED_WORD_FREQ,
SuggestedWordInfo.KIND_TYPED, ""));
for (int i = 0; i < NUMBER_OF_ADDED_SUGGESTIONS; ++i) {
list.add(new SuggestedWordInfo("" + i, 1, SuggestedWordInfo.KIND_CORRECTION, ""));
}
final SuggestedWords words = new SuggestedWords(
list,
false /* typedWordValid */,
false /* willAutoCorrect */,
false /* isPunctuationSuggestions */,
false /* isObsoleteSuggestions */,
false /* isPrediction*/);
assertEquals(NUMBER_OF_ADDED_SUGGESTIONS + 1, words.size());
assertEquals("typed", words.getWord(0));
assertEquals(SuggestedWordInfo.KIND_TYPED, words.getInfo(0).mKind);
assertEquals("0", words.getWord(1));
assertEquals(SuggestedWordInfo.KIND_CORRECTION, words.getInfo(1).mKind);
assertEquals("4", words.getWord(5));
assertEquals(SuggestedWordInfo.KIND_CORRECTION, words.getInfo(5).mKind);
final SuggestedWords wordsWithoutTyped = words.getSuggestedWordsExcludingTypedWord();
assertEquals(words.size() - 1, wordsWithoutTyped.size());
assertEquals("0", wordsWithoutTyped.getWord(0));
assertEquals(SuggestedWordInfo.KIND_CORRECTION, wordsWithoutTyped.getInfo(0).mKind);
}
}