diff options
Diffstat (limited to 'java/src')
7 files changed, 119 insertions, 76 deletions
diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java index b2b68f0ad..ced763841 100644 --- a/java/src/com/android/inputmethod/keyboard/Key.java +++ b/java/src/com/android/inputmethod/keyboard/Key.java @@ -22,6 +22,7 @@ import android.graphics.Rect; import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.text.TextUtils; +import android.util.Log; import android.util.Xml; import com.android.inputmethod.keyboard.internal.KeyStyles; @@ -42,6 +43,8 @@ import java.util.Map; * Class for describing the position and characteristics of a single key in the keyboard. */ public class Key { + private static final String TAG = Key.class.getSimpleName(); + /** * The key code (unicode or custom code) that this key generates. */ @@ -284,7 +287,11 @@ public class Key { // specified. final int code = style.getInt(keyAttr, R.styleable.Keyboard_Key_code, Keyboard.CODE_UNSPECIFIED); - if (code == Keyboard.CODE_UNSPECIFIED && !TextUtils.isEmpty(mLabel)) { + if (code == Keyboard.CODE_UNSPECIFIED && mOutputText == null + && !TextUtils.isEmpty(mLabel)) { + if (mLabel.length() != 1) { + Log.w(TAG, "Label is not a single letter: label=" + mLabel); + } final int firstChar = mLabel.charAt(0); mCode = getRtlParenthesisCode(firstChar, params.mIsRtlKeyboard); } else if (code != Keyboard.CODE_UNSPECIFIED) { diff --git a/java/src/com/android/inputmethod/keyboard/KeyDetector.java b/java/src/com/android/inputmethod/keyboard/KeyDetector.java index 2a6e0a2de..8e325b65c 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyDetector.java +++ b/java/src/com/android/inputmethod/keyboard/KeyDetector.java @@ -222,13 +222,7 @@ public class KeyDetector { } public static String printableCode(Key key) { - return key != null ? printableCode(key.mCode) : "none"; - } - - public static String printableCode(int primaryCode) { - if (primaryCode < 0) return String.format("%4d", primaryCode); - if (primaryCode < 0x100) return String.format("\\u%02x", primaryCode); - return String.format("\\u04x", primaryCode); + return key != null ? Keyboard.printableCode(key.mCode) : "none"; } public static String printableCodes(int[] codes) { diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java index f234507d5..e267aa65c 100644 --- a/java/src/com/android/inputmethod/keyboard/Keyboard.java +++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java @@ -18,6 +18,7 @@ package com.android.inputmethod.keyboard; import android.graphics.drawable.Drawable; import android.text.TextUtils; +import android.util.Log; import com.android.inputmethod.keyboard.internal.KeyboardIconsSet; import com.android.inputmethod.keyboard.internal.KeyboardParams; @@ -48,6 +49,8 @@ import java.util.Set; * </pre> */ public class Keyboard { + private static final String TAG = Keyboard.class.getSimpleName(); + /** Some common keys code. These should be aligned with values/keycodes.xml */ public static final int CODE_ENTER = '\n'; public static final int CODE_TAB = '\t'; @@ -241,4 +244,20 @@ public class Keyboard { default: return null; } } + + public static String printableCode(int code) { + switch (code) { + case CODE_SHIFT: return "shift"; + case CODE_SWITCH_ALPHA_SYMBOL: return "symbol"; + case CODE_CAPSLOCK: return "capslock"; + case CODE_DELETE: return "delete"; + case CODE_SHORTCUT: return "shortcut"; + case CODE_DUMMY: return "dummy"; + case CODE_UNSPECIFIED: return "unspec"; + default: + if (code < 0) Log.w(TAG, "Unknow negative key code=" + code); + if (code < 0x100) return String.format("\\u%02x", code); + return String.format("\\u04x", code); + } + } } diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index 9e0c5ce02..3a07cdf4d 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -239,7 +239,7 @@ public class PointerTracker { private boolean callListenerOnPressAndCheckKeyboardLayoutChange(Key key, boolean withSliding) { final boolean ignoreModifierKey = mIgnoreModifierKey && key.isModifier(); if (DEBUG_LISTENER) { - Log.d(TAG, "onPress : " + KeyDetector.printableCode(key.mCode) + Log.d(TAG, "onPress : " + KeyDetector.printableCode(key) + " sliding=" + withSliding + " ignoreModifier=" + ignoreModifierKey + " enabled=" + key.isEnabled()); } @@ -264,7 +264,7 @@ public class PointerTracker { // If code is CODE_DUMMY here, this key will be ignored or generate text. final CharSequence text = (code != Keyboard.CODE_DUMMY) ? null : key.mOutputText; if (DEBUG_LISTENER) { - Log.d(TAG, "onCodeInput: " + KeyDetector.printableCode(code) + " text=" + text + Log.d(TAG, "onCodeInput: " + Keyboard.printableCode(code) + " text=" + text + " codes="+ KeyDetector.printableCodes(keyCodes) + " x=" + x + " y=" + y + " ignoreModifier=" + ignoreModifierKey + " alterCode=" + alterCode + " enabled=" + key.isEnabled()); @@ -289,7 +289,7 @@ public class PointerTracker { private void callListenerOnRelease(Key key, int primaryCode, boolean withSliding) { final boolean ignoreModifierKey = mIgnoreModifierKey && key.isModifier(); if (DEBUG_LISTENER) { - Log.d(TAG, "onRelease : " + KeyDetector.printableCode(primaryCode) + Log.d(TAG, "onRelease : " + Keyboard.printableCode(primaryCode) + " sliding=" + withSliding + " ignoreModifier=" + ignoreModifierKey + " enabled="+ key.isEnabled()); } diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 54989bb9a..b4ad2255c 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -203,13 +203,12 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar private boolean mIsSettingsSuggestionStripOn; private boolean mApplicationSpecifiedCompletionOn; - private final StringBuilder mComposingStringBuilder = new StringBuilder(); private WordComposer mWordComposer = new WordComposer(); private CharSequence mBestWord; private boolean mHasUncommittedTypedChars; private int mCorrectionMode; - private int mCommittedLength; + private String mWordSavedForAutoCorrectCancellation; // Keep track of the last selection range to decide if we need to show word alternatives private int mLastSelectionStart; private int mLastSelectionEnd; @@ -756,7 +755,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar inputView.closing(); mEnteredText = null; - mComposingStringBuilder.setLength(0); + mWordComposer.reset(); mHasUncommittedTypedChars = false; mDeleteCount = 0; mSpaceState = SPACE_STATE_NONE; @@ -928,10 +927,10 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar // newly inserted punctuation. mSpaceState = SPACE_STATE_NONE; } - if (((mComposingStringBuilder.length() > 0 && mHasUncommittedTypedChars) + if (((mWordComposer.size() > 0 && mHasUncommittedTypedChars) || mVoiceProxy.isVoiceInputHighlighted()) && (selectionChanged || candidatesCleared)) { - mComposingStringBuilder.setLength(0); + mWordComposer.reset(); mHasUncommittedTypedChars = false; TextEntryState.reset(); updateSuggestions(); @@ -1146,13 +1145,13 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar public void commitTyped(final InputConnection ic) { if (!mHasUncommittedTypedChars) return; mHasUncommittedTypedChars = false; - if (mComposingStringBuilder.length() > 0) { + final CharSequence typedWord = mWordComposer.getTypedWord(); + if (typedWord.length() > 0) { if (ic != null) { - ic.commitText(mComposingStringBuilder, 1); + ic.commitText(typedWord, 1); } - mCommittedLength = mComposingStringBuilder.length(); - TextEntryState.acceptedTyped(mComposingStringBuilder); - addToUserUnigramAndBigramDictionaries(mComposingStringBuilder, + TextEntryState.acceptedTyped(typedWord); + addToUserUnigramAndBigramDictionaries(typedWord, UserUnigramDictionary.FREQUENCY_FOR_TYPED); } updateSuggestions(); @@ -1403,17 +1402,16 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar final boolean deleteChar = !mHasUncommittedTypedChars; if (mHasUncommittedTypedChars) { - final int length = mComposingStringBuilder.length(); + final int length = mWordComposer.size(); if (length > 0) { - mComposingStringBuilder.delete(length - 1, length); mWordComposer.deleteLast(); final CharSequence textWithUnderline = mComposingStateManager.isAutoCorrectionIndicatorOn() ? SuggestionSpanUtils.getTextWithAutoCorrectionIndicatorUnderline( - this, mComposingStringBuilder) - : mComposingStringBuilder; + this, mWordComposer.getTypedWord()) + : mWordComposer.getTypedWord(); ic.setComposingText(textWithUnderline, 1); - if (mComposingStringBuilder.length() == 0) { + if (mWordComposer.size() == 0) { mHasUncommittedTypedChars = false; } if (1 == length) { @@ -1432,11 +1430,15 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar // TODO: Merge space state with TextEntryState TextEntryState.backspace(); - if (TextEntryState.isUndoCommit()) { - revertLastWord(ic); + if (null != mWordSavedForAutoCorrectCancellation) { + cancelAutoCorrect(ic); + mWordSavedForAutoCorrectCancellation = null; ic.endBatchEdit(); return; + } else { + mWordSavedForAutoCorrectCancellation = null; } + if (SPACE_STATE_DOUBLE == spaceState) { if (revertDoubleSpace(ic)) { ic.endBatchEdit(); @@ -1453,6 +1455,9 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } if (mEnteredText != null && sameAsTextBeforeCursor(ic, mEnteredText)) { + // Cancel multi-character input: remove the text we just entered. + // This is triggered on backspace after a key that inputs multiple characters, + // like the smiley key or the .com key. ic.deleteSurroundingText(mEnteredText.length(), 0); } else if (deleteChar) { if (mSuggestionsView != null && mSuggestionsView.dismissAddToDictionaryHint()) { @@ -1463,7 +1468,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar // different behavior only in the case of picking the first // suggestion (typed word). It's intentional to have made this // inconsistent with backspacing after selecting other suggestions. - revertLastWord(ic); + restartSuggestionsOnManuallyPickedTypedWord(ic); } else { ic.deleteSurroundingText(1, 0); if (mDeleteCount > DELETE_ACCELERATE_AT) { @@ -1516,7 +1521,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar // Reset entirely the composing state anyway, then start composing a new word unless // the character is a single quote. mHasUncommittedTypedChars = (Keyboard.CODE_SINGLE_QUOTE != code); - mComposingStringBuilder.setLength(0); mWordComposer.reset(); clearSuggestions(); mComposingStateManager.onFinishComposingText(); @@ -1546,7 +1550,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } } if (mHasUncommittedTypedChars) { - mComposingStringBuilder.append((char) code); mWordComposer.add(code, keyCodes, x, y); if (ic != null) { // If it's the first letter, make note of auto-caps state @@ -1557,8 +1560,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar final CharSequence textWithUnderline = mComposingStateManager.isAutoCorrectionIndicatorOn() ? SuggestionSpanUtils.getTextWithAutoCorrectionIndicatorUnderline( - this, mComposingStringBuilder) - : mComposingStringBuilder; + this, mWordComposer.getTypedWord()) + : mWordComposer.getTypedWord(); ic.setComposingText(textWithUnderline, 1); } mHandler.postUpdateSuggestions(); @@ -1604,6 +1607,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } else { commitTyped(ic); } + } else { + mWordSavedForAutoCorrectCancellation = null; } final boolean swapMagicSpace; @@ -1746,8 +1751,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } final CharSequence textWithUnderline = newAutoCorrectionIndicator ? SuggestionSpanUtils.getTextWithAutoCorrectionIndicatorUnderline( - this, mComposingStringBuilder) - : mComposingStringBuilder; + this, mWordComposer.getTypedWord()) + : mWordComposer.getTypedWord(); if (!TextUtils.isEmpty(textWithUnderline)) { ic.setComposingText(textWithUnderline, 1); } @@ -1866,6 +1871,9 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar TextEntryState.acceptedDefault(mWordComposer.getTypedWord(), mBestWord, separatorCode); mExpectingUpdateSelection = true; commitBestWord(mBestWord); + if (!mBestWord.equals(mWordComposer.getTypedWord())) { + mWordSavedForAutoCorrectCancellation = mBestWord.toString(); + } // Add the word to the user unigram dictionary if it's not a known word addToUserUnigramAndBigramDictionaries(mBestWord, UserUnigramDictionary.FREQUENCY_FOR_TYPED); @@ -1891,7 +1899,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar final CompletionInfo completionInfo = mApplicationSpecifiedCompletions[index]; ic.commitCompletion(completionInfo); } - mCommittedLength = suggestion.length(); if (mSuggestionsView != null) { mSuggestionsView.clear(); } @@ -1940,9 +1947,9 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } else { addToOnlyBigramDictionary(suggestion, 1); } - LatinImeLogger.logOnManualSuggestion(mComposingStringBuilder.toString(), + LatinImeLogger.logOnManualSuggestion(mWordComposer.getTypedWord().toString(), suggestion.toString(), index, suggestions.mWords); - TextEntryState.acceptedSuggestion(mComposingStringBuilder.toString(), suggestion); + TextEntryState.acceptedSuggestion(mWordComposer.getTypedWord().toString(), suggestion); // Follow it with a space if (mInsertSpaceOnPickSuggestionManually) { sendMagicSpace(); @@ -2009,7 +2016,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } } mHasUncommittedTypedChars = false; - mCommittedLength = bestWord.length(); } private static final WordComposer sEmptyWordComposer = new WordComposer(); @@ -2152,8 +2158,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar private void restartSuggestionsOnWordBeforeCursor(final InputConnection ic, final CharSequence word) { mWordComposer.setComposingWord(word, mKeyboardSwitcher.getLatinKeyboard()); - mComposingStringBuilder.setLength(0); - mComposingStringBuilder.append(word); // mBestWord will be set appropriately by updateSuggestions() called by the handler mBestWord = null; mHasUncommittedTypedChars = true; @@ -2165,39 +2169,61 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar } // "ic" must not be null - private void revertLastWord(final InputConnection ic) { - if (mHasUncommittedTypedChars || mComposingStringBuilder.length() <= 0) { - sendDownUpKeyEvents(KeyEvent.KEYCODE_DEL); - return; + private void cancelAutoCorrect(final InputConnection ic) { + final int cancelLength = mWordSavedForAutoCorrectCancellation.length(); + final CharSequence separator = ic.getTextBeforeCursor(1, 0); + if (DEBUG) { + final String wordBeforeCursor = + ic.getTextBeforeCursor(cancelLength + 1, 0).subSequence(0, cancelLength) + .toString(); + if (!mWordSavedForAutoCorrectCancellation.equals(wordBeforeCursor)) { + throw new RuntimeException("cancelAutoCorrect check failed: we thought we were " + + "reverting \"" + mWordSavedForAutoCorrectCancellation + + "\", but before the cursor we found \"" + wordBeforeCursor + "\""); + } + if (mWordComposer.getTypedWord().equals(wordBeforeCursor)) { + throw new RuntimeException("cancelAutoCorrect check failed: we wanted to cancel " + + "auto correction and revert to \"" + mWordComposer.getTypedWord() + + "\" but we found this very string before the cursor"); + } } + ic.deleteSurroundingText(cancelLength + 1, 0); + + // Re-insert the separator + ic.commitText(mWordComposer.getTypedWord(), 1); + TextEntryState.acceptedTyped(mWordComposer.getTypedWord()); + ic.commitText(separator, 1); + TextEntryState.typedCharacter(separator.charAt(0), true, + WordComposer.NOT_A_COORDINATE, WordComposer.NOT_A_COORDINATE); + mHandler.cancelUpdateBigramPredictions(); + mHandler.postUpdateSuggestions(); + } + // "ic" must not be null + private void restartSuggestionsOnManuallyPickedTypedWord(final InputConnection ic) { final CharSequence separator = ic.getTextBeforeCursor(1, 0); - ic.deleteSurroundingText(1, 0); - final CharSequence textToTheLeft = ic.getTextBeforeCursor(mCommittedLength, 0); - ic.deleteSurroundingText(mCommittedLength, 0); - - // Re-insert "separator" only when the deleted character was word separator and the - // composing text wasn't equal to the auto-corrected text which can be found before - // the cursor. - if (!TextUtils.isEmpty(separator) - && mSettingsValues.isWordSeparator(separator.charAt(0)) - && !TextUtils.equals(mComposingStringBuilder, textToTheLeft)) { - ic.commitText(mComposingStringBuilder, 1); - TextEntryState.acceptedTyped(mComposingStringBuilder); - ic.commitText(separator, 1); - TextEntryState.typedCharacter(separator.charAt(0), true, - WordComposer.NOT_A_COORDINATE, WordComposer.NOT_A_COORDINATE); - // Clear composing text - mComposingStringBuilder.setLength(0); - } else { - // Note: this relies on the last word still being held in the WordComposer - // Note: in the interest of code simplicity, we may want to just call - // restartSuggestionsOnWordBeforeCursorIfAtEndOfWord instead, but retrieving - // the old WordComposer allows to reuse the actual typed coordinates. - mHasUncommittedTypedChars = true; - ic.setComposingText(mComposingStringBuilder, 1); - TextEntryState.backspace(); + final int restartLength = mWordComposer.size(); + if (DEBUG) { + final String wordBeforeCursor = + ic.getTextBeforeCursor(restartLength + 1, 0).subSequence(0, restartLength) + .toString(); + if (!mWordComposer.getTypedWord().equals(wordBeforeCursor)) { + throw new RuntimeException("restartSuggestionsOnManuallyPickedTypedWord " + + "check failed: we thought we were reverting \"" + + mWordComposer.getTypedWord() + + "\", but before the cursor we found \"" + + wordBeforeCursor + "\""); + } } + ic.deleteSurroundingText(restartLength + 1, 0); + + // Note: this relies on the last word still being held in the WordComposer + // Note: in the interest of code simplicity, we may want to just call + // restartSuggestionsOnWordBeforeCursorIfAtEndOfWord instead, but retrieving + // the old WordComposer allows to reuse the actual typed coordinates. + mHasUncommittedTypedChars = true; + ic.setComposingText(mWordComposer.getTypedWord(), 1); + TextEntryState.backspace(); mHandler.cancelUpdateBigramPredictions(); mHandler.postUpdateSuggestions(); } @@ -2474,7 +2500,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar final Keyboard keyboard = mKeyboardSwitcher.getLatinKeyboard(); final int keyboardMode = keyboard != null ? keyboard.mId.mMode : -1; p.println(" Keyboard mode = " + keyboardMode); - p.println(" mComposingStringBuilder=" + mComposingStringBuilder.toString()); p.println(" mIsSuggestionsRequested=" + mIsSettingsSuggestionStripOn); p.println(" mCorrectionMode=" + mCorrectionMode); p.println(" mHasUncommittedTypedChars=" + mHasUncommittedTypedChars); diff --git a/java/src/com/android/inputmethod/latin/TextEntryState.java b/java/src/com/android/inputmethod/latin/TextEntryState.java index a6041b310..6b44fc500 100644 --- a/java/src/com/android/inputmethod/latin/TextEntryState.java +++ b/java/src/com/android/inputmethod/latin/TextEntryState.java @@ -19,6 +19,7 @@ package com.android.inputmethod.latin; import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.latin.Utils.RingCharBuffer; +import android.text.TextUtils; import android.util.Log; public class TextEntryState { @@ -45,7 +46,7 @@ public class TextEntryState { public static void acceptedDefault(CharSequence typedWord, CharSequence actualWord, int separatorCode) { - if (typedWord == null) return; + if (TextUtils.isEmpty(typedWord)) return; setState(ACCEPTED_DEFAULT); LatinImeLogger.logOnAutoCorrection( typedWord.toString(), actualWord.toString(), separatorCode); @@ -57,7 +58,7 @@ public class TextEntryState { // (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; + if (TextUtils.isEmpty(typedWord)) return; switch (sState) { case SPACE_AFTER_ACCEPTED: case PUNCTUATION_AFTER_ACCEPTED: diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java index 44c89f73c..dfb00c8ab 100644 --- a/java/src/com/android/inputmethod/latin/WordComposer.java +++ b/java/src/com/android/inputmethod/latin/WordComposer.java @@ -231,9 +231,6 @@ public class WordComposer { * @return the word that was typed so far */ public String getTypedWord() { - if (size() == 0) { - return null; - } return mTypedWord.toString(); } |