diff --git a/java/src/com/android/inputmethod/latin/EditingUtil.java b/java/src/com/android/inputmethod/latin/EditingUtil.java index 0c87f8d58fab4aa3e123b2fd28335c2943fe2ab0..be31cb787f2fefd0220a92a0f5744bae5867d9f8 100644 --- a/java/src/com/android/inputmethod/latin/EditingUtil.java +++ b/java/src/com/android/inputmethod/latin/EditingUtil.java @@ -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; + } } diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index b059b6cfe47caa07a81d35e80556a7ca72296af1..b1bb84d27b50ef311ec4911110797375d256da2f 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -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) { diff --git a/java/src/com/android/inputmethod/latin/TextEntryState.java b/java/src/com/android/inputmethod/latin/TextEntryState.java index bc7bf3f711a01c033df8686505a399f5ce47beeb..9011191f10cca56a316c0f04ab433c09de3b5faf 100644 --- a/java/src/com/android/inputmethod/latin/TextEntryState.java +++ b/java/src/com/android/inputmethod/latin/TextEntryState.java @@ -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; - - private static int sState = STATE_UNKNOWN; + 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 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); + } + } }