Fixes for correction mode - reliably show the corrections when tapping on a word.

Also, continue to show the corrections when user keeps replacing the word repeatedly
with different corrections from the suggestion strip. There were problems with
tapping suggestions quickly or tapping the same suggestion more than once (same length).

Also fixes Bug: 2852891 - Text suggestion appears incorrectly when selecting text that's
not a whole word.

Changed the TextEntryState states to an enum type instead of int. Needed it to show the
states for debugging purposes.
main
Amith Yamasani 2010-08-13 13:29:49 -07:00
parent c9a181b1b3
commit c20c5a8d4c
3 changed files with 142 additions and 79 deletions

View File

@ -16,12 +16,12 @@
package com.android.inputmethod.latin;
import java.util.regex.Pattern;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
import java.util.regex.Pattern;
/**
* Utility methods to deal with editing text through an InputConnection.
*/
@ -203,4 +203,27 @@ public class EditingUtil {
return null;
}
}
/**
* Checks if the cursor is touching/inside a word or the selection is for a whole
* word and no more and no less.
* @param range the Range object that contains the bounds of the word around the cursor
* @param start the start of the selection
* @param end the end of the selection, which could be the same as the start, if text is not
* in selection mode
* @return false if the selection is a partial word or straddling multiple words, true if
* the selection is a full word or there is no selection.
*/
public static boolean isFullWordOrInside(Range range, int start, int end) {
// Is the cursor inside or touching a word?
if (start == end) return true;
// Is it a selection? Then is the start of the selection the start of the word and
// the size of the selection the size of the word? Then return true
if (start < end
&& (range.charsBefore == 0 && range.charsAfter == end - start)) {
return true;
}
return false;
}
}

View File

@ -752,10 +752,10 @@ public class LatinIME extends InputMethodService
mVoiceInputHighlighted = false;
} else if (!mPredicting && !mJustAccepted) {
switch (TextEntryState.getState()) {
case TextEntryState.STATE_ACCEPTED_DEFAULT:
case ACCEPTED_DEFAULT:
TextEntryState.reset();
// fall through
case TextEntryState.STATE_SPACE_AFTER_PICKED:
case SPACE_AFTER_PICKED:
mJustAddedAutoSpace = false; // The user moved the cursor.
break;
}
@ -768,10 +768,10 @@ public class LatinIME extends InputMethodService
mLastSelectionEnd = newSelEnd;
// TODO: Uncomment this block when we enable re-editing feature
// If a word is selected
// Check if we should go in or out of correction mode.
if (isPredictionOn() && mJustRevertedSeparator == null
&& (candidatesStart == candidatesEnd || newSelStart != oldSelStart)
&& (candidatesStart == candidatesEnd || newSelStart != oldSelStart
|| TextEntryState.isCorrecting())
&& (newSelStart < newSelEnd - 1 || (!mPredicting))
&& !mVoiceInputHighlighted) {
if (isCursorTouchingWord() || mLastSelectionStart < mLastSelectionEnd) {
@ -1207,7 +1207,7 @@ public class LatinIME extends InputMethodService
}
postUpdateShiftKeyState();
TextEntryState.backspace();
if (TextEntryState.getState() == TextEntryState.STATE_UNDO_COMMIT) {
if (TextEntryState.getState() == TextEntryState.State.UNDO_COMMIT) {
revertLastWord(deleteChar);
ic.endBatchEdit();
return;
@ -1358,13 +1358,13 @@ public class LatinIME extends InputMethodService
// Handle the case of ". ." -> " .." with auto-space if necessary
// before changing the TextEntryState.
if (TextEntryState.getState() == TextEntryState.STATE_PUNCTUATION_AFTER_ACCEPTED
if (TextEntryState.getState() == TextEntryState.State.PUNCTUATION_AFTER_ACCEPTED
&& primaryCode == KEYCODE_PERIOD) {
reswapPeriodAndSpace();
}
TextEntryState.typedCharacter((char) primaryCode, true);
if (TextEntryState.getState() == TextEntryState.STATE_PUNCTUATION_AFTER_ACCEPTED
if (TextEntryState.getState() == TextEntryState.State.PUNCTUATION_AFTER_ACCEPTED
&& primaryCode != KEYCODE_ENTER) {
swapPunctuationAndSpace();
} else if (isPredictionOn() && primaryCode == KEYCODE_SPACE) {
@ -1790,8 +1790,16 @@ public class LatinIME extends InputMethodService
mJustAddedAutoSpace = true;
}
// Fool the state watcher so that a subsequent backspace will not do a revert
TextEntryState.typedCharacter((char) KEYCODE_SPACE, true);
// Fool the state watcher so that a subsequent backspace will not do a revert, unless
// we just did a correction, in which case we need to stay in
// TextEntryState.State.PICKED_SUGGESTION state.
if (!correcting) {
TextEntryState.typedCharacter((char) KEYCODE_SPACE, true);
setNextSuggestions();
} else {
// In case the cursor position doesn't change, make sure we show the suggestions again.
postUpdateOldSuggestions();
}
if (index == 0 && mCorrectionMode > 0 && !mSuggest.isValidWord(suggestion)
&& !mSuggest.isValidWord(suggestion.toString().toLowerCase())) {
mCandidateView.showAddToDictionaryHint(suggestion);
@ -1820,7 +1828,6 @@ public class LatinIME extends InputMethodService
mWordToSuggestions.put(suggestion.toString(), suggestions);
}
}
// TODO: implement rememberReplacedWord for typed words
}
/**
@ -1860,7 +1867,10 @@ public class LatinIME extends InputMethodService
mPredicting = false;
mCommittedLength = suggestion.length();
((LatinKeyboard) inputView.getKeyboard()).setPreferredLetters(null);
setNextSuggestions();
// If we just corrected a word, then don't show punctuations
if (!correcting) {
setNextSuggestions();
}
updateShiftKeyState(getCurrentInputEditorInfo());
}
@ -1880,13 +1890,16 @@ public class LatinIME extends InputMethodService
EditingUtil.Range range = new EditingUtil.Range();
CharSequence touching = EditingUtil.getWordAtCursor(getCurrentInputConnection(),
mWordSeparators, range);
if (touching != null && touching.length() > 1) {
// If it's a selection, check if it's an entire word and no more, no less.
boolean fullword = EditingUtil.isFullWordOrInside(range, mLastSelectionStart,
mLastSelectionEnd);
if (fullword && touching != null && touching.length() > 1) {
// Strip out any trailing word separator
if (mWordSeparators.indexOf(touching.charAt(touching.length() - 1)) > 0) {
touching = touching.toString().substring(0, touching.length() - 1);
}
// Search for result in spoken word alternatives
// TODO: possibly combine the spoken suggestions with the typed suggestions.
String selectedWord = touching.toString().trim();
if (!mWordToSuggestions.containsKey(selectedWord)){
selectedWord = selectedWord.toLowerCase();
@ -1911,7 +1924,8 @@ public class LatinIME extends InputMethodService
ic.endBatchEdit();
return;
}
// If we didn't find a match, search for result in word history
// If we didn't find a match, search for result in typed word history
WordComposer foundWord = null;
WordAlternatives alternatives = null;
for (WordAlternatives entry : mWordHistory) {

View File

@ -17,19 +17,22 @@
package com.android.inputmethod.latin;
import android.content.Context;
import android.inputmethodservice.Keyboard.Key;
import android.text.format.DateFormat;
import android.util.Log;
import android.inputmethodservice.Keyboard.Key;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Calendar;
public class TextEntryState {
private static final boolean DBG = false;
private static final String TAG = "TextEntryState";
private static boolean LOGGING = false;
private static int sBackspaceCount = 0;
private static int sAutoSuggestCount = 0;
@ -46,20 +49,22 @@ public class TextEntryState {
private static int sActualChars;
public static final int STATE_UNKNOWN = 0;
public static final int STATE_START = 1;
public static final int STATE_IN_WORD = 2;
public static final int STATE_ACCEPTED_DEFAULT = 3;
public static final int STATE_PICKED_SUGGESTION = 4;
public static final int STATE_PUNCTUATION_AFTER_WORD = 5;
public static final int STATE_PUNCTUATION_AFTER_ACCEPTED = 6;
public static final int STATE_SPACE_AFTER_ACCEPTED = 7;
public static final int STATE_SPACE_AFTER_PICKED = 8;
public static final int STATE_UNDO_COMMIT = 9;
public static final int STATE_CORRECTING = 10;
public static final int STATE_PICKED_CORRECTION = 11;
public enum State {
UNKNOWN,
START,
IN_WORD,
ACCEPTED_DEFAULT,
PICKED_SUGGESTION,
PUNCTUATION_AFTER_WORD,
PUNCTUATION_AFTER_ACCEPTED,
SPACE_AFTER_ACCEPTED,
SPACE_AFTER_PICKED,
UNDO_COMMIT,
CORRECTING,
PICKED_CORRECTION;
}
private static int sState = STATE_UNKNOWN;
private static State sState = State.UNKNOWN;
private static FileOutputStream sKeyLocationFile;
private static FileOutputStream sUserActionFile;
@ -73,7 +78,7 @@ public class TextEntryState {
sWordNotInDictionaryCount = 0;
sTypedChars = 0;
sActualChars = 0;
sState = STATE_START;
sState = State.START;
if (LOGGING) {
try {
@ -118,118 +123,133 @@ public class TextEntryState {
}
sTypedChars += typedWord.length();
sActualChars += actualWord.length();
sState = STATE_ACCEPTED_DEFAULT;
sState = State.ACCEPTED_DEFAULT;
LatinImeLogger.logOnAutoSuggestion(typedWord.toString(), actualWord.toString());
displayState();
}
// STATE_ACCEPTED_DEFAULT will be changed to other sub-states
// (see "case STATE_ACCEPTED_DEFAULT" in typedCharacter() below),
// and should be restored back to STATE_ACCEPTED_DEFAULT after processing for each sub-state.
// State.ACCEPTED_DEFAULT will be changed to other sub-states
// (see "case ACCEPTED_DEFAULT" in typedCharacter() below),
// and should be restored back to State.ACCEPTED_DEFAULT after processing for each sub-state.
public static void backToAcceptedDefault(CharSequence typedWord) {
if (typedWord == null) return;
switch (sState) {
case STATE_SPACE_AFTER_ACCEPTED:
case STATE_PUNCTUATION_AFTER_ACCEPTED:
case STATE_IN_WORD:
sState = STATE_ACCEPTED_DEFAULT;
case SPACE_AFTER_ACCEPTED:
case PUNCTUATION_AFTER_ACCEPTED:
case IN_WORD:
sState = State.ACCEPTED_DEFAULT;
break;
}
displayState();
}
public static void acceptedTyped(CharSequence typedWord) {
sWordNotInDictionaryCount++;
sState = STATE_PICKED_SUGGESTION;
sState = State.PICKED_SUGGESTION;
displayState();
}
public static void acceptedSuggestion(CharSequence typedWord, CharSequence actualWord) {
sManualSuggestCount++;
int oldState = sState;
State oldState = sState;
if (typedWord.equals(actualWord)) {
acceptedTyped(typedWord);
}
sState = oldState == STATE_CORRECTING ? STATE_PICKED_CORRECTION : STATE_PICKED_SUGGESTION;
if (oldState == State.CORRECTING || oldState == State.PICKED_CORRECTION) {
sState = State.PICKED_CORRECTION;
} else {
sState = State.PICKED_SUGGESTION;
}
displayState();
}
public static void selectedForCorrection() {
sState = STATE_CORRECTING;
sState = State.CORRECTING;
displayState();
}
public static void typedCharacter(char c, boolean isSeparator) {
boolean isSpace = c == ' ';
switch (sState) {
case STATE_IN_WORD:
case IN_WORD:
if (isSpace || isSeparator) {
sState = STATE_START;
sState = State.START;
} else {
// State hasn't changed.
}
break;
case STATE_ACCEPTED_DEFAULT:
case STATE_SPACE_AFTER_PICKED:
case ACCEPTED_DEFAULT:
case SPACE_AFTER_PICKED:
if (isSpace) {
sState = STATE_SPACE_AFTER_ACCEPTED;
sState = State.SPACE_AFTER_ACCEPTED;
} else if (isSeparator) {
sState = STATE_PUNCTUATION_AFTER_ACCEPTED;
sState = State.PUNCTUATION_AFTER_ACCEPTED;
} else {
sState = STATE_IN_WORD;
sState = State.IN_WORD;
}
break;
case STATE_PICKED_SUGGESTION:
case STATE_PICKED_CORRECTION:
case PICKED_SUGGESTION:
case PICKED_CORRECTION:
if (isSpace) {
sState = STATE_SPACE_AFTER_PICKED;
sState = State.SPACE_AFTER_PICKED;
} else if (isSeparator) {
// Swap
sState = STATE_PUNCTUATION_AFTER_ACCEPTED;
sState = State.PUNCTUATION_AFTER_ACCEPTED;
} else {
sState = STATE_IN_WORD;
sState = State.IN_WORD;
}
break;
case STATE_START:
case STATE_UNKNOWN:
case STATE_SPACE_AFTER_ACCEPTED:
case STATE_PUNCTUATION_AFTER_ACCEPTED:
case STATE_PUNCTUATION_AFTER_WORD:
case START:
case UNKNOWN:
case SPACE_AFTER_ACCEPTED:
case PUNCTUATION_AFTER_ACCEPTED:
case PUNCTUATION_AFTER_WORD:
if (!isSpace && !isSeparator) {
sState = STATE_IN_WORD;
sState = State.IN_WORD;
} else {
sState = STATE_START;
sState = State.START;
}
break;
case STATE_UNDO_COMMIT:
case UNDO_COMMIT:
if (isSpace || isSeparator) {
sState = STATE_ACCEPTED_DEFAULT;
sState = State.ACCEPTED_DEFAULT;
} else {
sState = STATE_IN_WORD;
sState = State.IN_WORD;
}
break;
case STATE_CORRECTING:
sState = STATE_START;
case CORRECTING:
sState = State.START;
break;
}
displayState();
}
public static void backspace() {
if (sState == STATE_ACCEPTED_DEFAULT) {
sState = STATE_UNDO_COMMIT;
if (sState == State.ACCEPTED_DEFAULT) {
sState = State.UNDO_COMMIT;
sAutoSuggestUndoneCount++;
LatinImeLogger.logOnAutoSuggestionCanceled();
} else if (sState == STATE_UNDO_COMMIT) {
sState = STATE_IN_WORD;
} else if (sState == State.UNDO_COMMIT) {
sState = State.IN_WORD;
}
sBackspaceCount++;
displayState();
}
public static void reset() {
sState = STATE_START;
sState = State.START;
displayState();
}
public static int getState() {
public static State getState() {
if (DBG) {
Log.d(TAG, "Returning state = " + sState);
}
return sState;
}
public static boolean isCorrecting() {
return sState == STATE_CORRECTING || sState == STATE_PICKED_CORRECTION;
return sState == State.CORRECTING || sState == State.PICKED_CORRECTION;
}
public static void keyPressedAt(Key key, int x, int y) {
@ -248,5 +268,11 @@ public class TextEntryState {
}
}
}
private static void displayState() {
if (DBG) {
Log.d(TAG, "State = " + sState);
}
}
}