aboutsummaryrefslogtreecommitdiffstats
path: root/java/src/com/android/inputmethod/latin/inputlogic
diff options
context:
space:
mode:
Diffstat (limited to 'java/src/com/android/inputmethod/latin/inputlogic')
-rw-r--r--java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java691
-rw-r--r--java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java59
-rw-r--r--java/src/com/android/inputmethod/latin/inputlogic/PrivateCommandPerformer.java40
3 files changed, 386 insertions, 404 deletions
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
index fdab7f25f..5b3b28d75 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
@@ -17,7 +17,6 @@
package com.android.inputmethod.latin.inputlogic;
import android.graphics.Color;
-import android.inputmethodservice.InputMethodService;
import android.os.SystemClock;
import android.text.SpannableString;
import android.text.Spanned;
@@ -28,32 +27,28 @@ import android.util.Log;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.inputmethod.CorrectionInfo;
-import android.view.inputmethod.CursorAnchorInfo;
import android.view.inputmethod.EditorInfo;
-import com.android.inputmethod.compat.CursorAnchorInfoCompatWrapper;
import com.android.inputmethod.compat.SuggestionSpanUtils;
import com.android.inputmethod.event.Event;
import com.android.inputmethod.event.InputTransaction;
+import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.KeyboardSwitcher;
-import com.android.inputmethod.keyboard.ProximityInfo;
-import com.android.inputmethod.keyboard.TextDecorator;
-import com.android.inputmethod.keyboard.TextDecoratorUiOperator;
-import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.Dictionary;
import com.android.inputmethod.latin.DictionaryFacilitator;
-import com.android.inputmethod.latin.InputPointers;
import com.android.inputmethod.latin.LastComposedWord;
import com.android.inputmethod.latin.LatinIME;
-import com.android.inputmethod.latin.PrevWordsInfo;
+import com.android.inputmethod.latin.NgramContext;
import com.android.inputmethod.latin.RichInputConnection;
import com.android.inputmethod.latin.Suggest;
import com.android.inputmethod.latin.Suggest.OnGetSuggestedWordsCallback;
import com.android.inputmethod.latin.SuggestedWords;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.WordComposer;
+import com.android.inputmethod.latin.common.Constants;
+import com.android.inputmethod.latin.common.InputPointers;
+import com.android.inputmethod.latin.common.StringUtils;
import com.android.inputmethod.latin.define.DebugFlags;
-import com.android.inputmethod.latin.define.ProductionFlags;
import com.android.inputmethod.latin.settings.SettingsValues;
import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion;
import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
@@ -61,13 +56,15 @@ import com.android.inputmethod.latin.suggestions.SuggestionStripViewAccessor;
import com.android.inputmethod.latin.utils.AsyncResultHolder;
import com.android.inputmethod.latin.utils.InputTypeUtils;
import com.android.inputmethod.latin.utils.RecapitalizeStatus;
-import com.android.inputmethod.latin.utils.StringUtils;
+import com.android.inputmethod.latin.utils.StatsUtils;
import com.android.inputmethod.latin.utils.TextRange;
import java.util.ArrayList;
import java.util.TreeSet;
import java.util.concurrent.TimeUnit;
+import javax.annotation.Nonnull;
+
/**
* This class manages the input logic.
*/
@@ -75,7 +72,7 @@ public final class InputLogic {
private static final String TAG = InputLogic.class.getSimpleName();
// TODO : Remove this member when we can.
- private final LatinIME mLatinIME;
+ final LatinIME mLatinIME;
private final SuggestionStripViewAccessor mSuggestionStripViewAccessor;
// Never null.
@@ -85,18 +82,10 @@ public final class InputLogic {
// Current space state of the input method. This can be any of the above constants.
private int mSpaceState;
// Never null
- public SuggestedWords mSuggestedWords = SuggestedWords.EMPTY;
+ public SuggestedWords mSuggestedWords = SuggestedWords.getEmptyInstance();
public final Suggest mSuggest;
private final DictionaryFacilitator mDictionaryFacilitator;
- private final TextDecorator mTextDecorator = new TextDecorator(new TextDecorator.Listener() {
- @Override
- public void onClickComposingTextToAddToDictionary(final String word) {
- mLatinIME.addWordToUserDictionary(word);
- mLatinIME.dismissAddToDictionaryHint();
- }
- });
-
public LastComposedWord mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
// This has package visibility so it can be accessed from InputLogicHandler.
/* package */ final WordComposer mWordComposer;
@@ -144,13 +133,20 @@ public final class InputLogic {
*/
public void startInput(final String combiningSpec, final SettingsValues settingsValues) {
mEnteredText = null;
+ if (!mWordComposer.getTypedWord().isEmpty()) {
+ // For messaging apps that offer send button, the IME does not get the opportunity
+ // to capture the last word. This block should capture those uncommitted words.
+ // The timestamp at which it is captured is not accurate but close enough.
+ StatsUtils.onWordCommitUserTyped(
+ mWordComposer.getTypedWord(), mWordComposer.isBatchMode());
+ }
mWordComposer.restartCombining(combiningSpec);
resetComposingState(true /* alsoResetLastComposedWord */);
mDeleteCount = 0;
mSpaceState = SpaceState.NONE;
mRecapitalizeStatus.disable(); // Do not perform recapitalize until the cursor is moved once
mCurrentlyPressedHardwareKeys.clear();
- mSuggestedWords = SuggestedWords.EMPTY;
+ mSuggestedWords = SuggestedWords.getEmptyInstance();
// In some cases (namely, after rotation of the device) editorInfo.initialSelStart is lying
// so we try using some heuristics to find out about these and fix them.
mConnection.tryFixLyingCursorPosition();
@@ -161,13 +157,9 @@ public final class InputLogic {
mInputLogicHandler.reset();
}
- if (ProductionFlags.ENABLE_CURSOR_ANCHOR_INFO_CALLBACK) {
- // AcceptTypedWord feature relies on CursorAnchorInfo.
- if (settingsValues.mShouldShowUiToAcceptTypedWord) {
- mConnection.requestCursorUpdates(true /* enableMonitor */,
- true /* requestImmediateCallback */);
- }
- mTextDecorator.reset();
+ if (settingsValues.mShouldShowLxxSuggestionUi) {
+ mConnection.requestCursorUpdates(true /* enableMonitor */,
+ true /* requestImmediateCallback */);
}
}
@@ -204,6 +196,8 @@ public final class InputLogic {
public void finishInput() {
if (mWordComposer.isComposingWord()) {
mConnection.finishComposingText();
+ StatsUtils.onWordCommitUserTyped(
+ mWordComposer.getTypedWord(), mWordComposer.isBatchMode());
}
resetComposingState(true /* alsoResetLastComposedWord */);
mInputLogicHandler.reset();
@@ -231,9 +225,7 @@ public final class InputLogic {
* @return the complete transaction object
*/
public InputTransaction onTextInput(final SettingsValues settingsValues, final Event event,
- final int keyboardShiftMode,
- // TODO: remove this argument
- final LatinIME.UIHandler handler) {
+ final int keyboardShiftMode, final LatinIME.UIHandler handler) {
final String rawText = event.getTextToCommit().toString();
final InputTransaction inputTransaction = new InputTransaction(settingsValues, event,
SystemClock.uptimeMillis(), mSpaceState,
@@ -247,9 +239,10 @@ public final class InputLogic {
handler.postUpdateSuggestionStrip(SuggestedWords.INPUT_STYLE_TYPING);
final String text = performSpecificTldProcessingOnTextInput(rawText);
if (SpaceState.PHANTOM == mSpaceState) {
- promotePhantomSpace(settingsValues);
+ insertAutomaticSpaceIfOptionsAndTextAllow(settingsValues);
}
mConnection.commitText(text, 1);
+ StatsUtils.onWordCommitUserTyped(mEnteredText, mWordComposer.isBatchMode());
mConnection.endBatchEdit();
// Space state must be updated before calling updateShiftState
mSpaceState = SpaceState.NONE;
@@ -260,20 +253,6 @@ public final class InputLogic {
}
/**
- * Determines whether "Touch again to save" should be shown or not.
- * @param suggestionInfo the suggested word chosen by the user.
- * @return {@code true} if we should show the "Touch again to save" hint.
- */
- private boolean shouldShowAddToDictionaryHint(final SuggestedWordInfo suggestionInfo) {
- // We should show the "Touch again to save" hint if the user pressed the first entry
- // AND it's in none of our current dictionaries (main, user or otherwise).
- return (suggestionInfo.isKindOf(SuggestedWordInfo.KIND_TYPED)
- || suggestionInfo.isKindOf(SuggestedWordInfo.KIND_OOV_CORRECTION))
- && !mDictionaryFacilitator.isValidWord(suggestionInfo.mWord, true /* ignoreCase */)
- && mDictionaryFacilitator.isUserDictionaryEnabled();
- }
-
- /**
* A suggestion was picked from the suggestion strip.
* @param settingsValues the current values of the settings.
* @param suggestionInfo the suggestion info.
@@ -285,12 +264,14 @@ public final class InputLogic {
// interface
public InputTransaction onPickSuggestionManually(final SettingsValues settingsValues,
final SuggestedWordInfo suggestionInfo, final int keyboardShiftState,
- // TODO: remove these arguments
final int currentKeyboardScriptId, final LatinIME.UIHandler handler) {
final SuggestedWords suggestedWords = mSuggestedWords;
final String suggestion = suggestionInfo.mWord;
// If this is a punctuation picked from the suggestion strip, pass it to onCodeInput
if (suggestion.length() == 1 && suggestedWords.isPunctuationSuggestions()) {
+ // We still want to log a suggestion click.
+ StatsUtils.onPickSuggestionManually(
+ mSuggestedWords, suggestionInfo, mDictionaryFacilitator);
// Word separators are suggested before the user inputs something.
// Rely on onCodeInput to do the complicated swapping/stripping logic consistently.
final Event event = Event.createPunctuationSuggestionPickedEvent(suggestionInfo);
@@ -312,7 +293,7 @@ public final class InputLogic {
final int firstChar = Character.codePointAt(suggestion, 0);
if (!settingsValues.isWordSeparator(firstChar)
|| settingsValues.isUsuallyPrecededBySpace(firstChar)) {
- promotePhantomSpace(settingsValues);
+ insertAutomaticSpaceIfOptionsAndTextAllow(settingsValues);
}
}
@@ -321,7 +302,7 @@ public final class InputLogic {
// however need to reset the suggestion strip right away, because we know we can't take
// the risk of calling commitCompletion twice because we don't know how the app will react.
if (suggestionInfo.isKindOf(SuggestedWordInfo.KIND_APP_DEFINED)) {
- mSuggestedWords = SuggestedWords.EMPTY;
+ mSuggestedWords = SuggestedWords.getEmptyInstance();
mSuggestionStripViewAccessor.setNeutralSuggestionStrip();
inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW);
resetComposingState(true /* alsoResetLastComposedWord */);
@@ -330,7 +311,6 @@ public final class InputLogic {
return inputTransaction;
}
- final boolean shouldShowAddToDictionaryHint = shouldShowAddToDictionaryHint(suggestionInfo);
commitChosenWord(settingsValues, suggestion, LastComposedWord.COMMIT_TYPE_MANUAL_PICK,
LastComposedWord.NOT_A_SEPARATOR);
mConnection.endBatchEdit();
@@ -340,13 +320,14 @@ public final class InputLogic {
mSpaceState = SpaceState.PHANTOM;
inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW);
- if (shouldShowAddToDictionaryHint) {
- mSuggestionStripViewAccessor.showAddToDictionaryHint(suggestion);
- } else {
- // If we're not showing the "Touch again to save", then update the suggestion strip.
- // That's going to be predictions (or punctuation suggestions), so INPUT_STYLE_NONE.
- handler.postUpdateSuggestionStrip(SuggestedWords.INPUT_STYLE_NONE);
- }
+ // If we're not showing the "Touch again to save", then update the suggestion strip.
+ // That's going to be predictions (or punctuation suggestions), so INPUT_STYLE_NONE.
+ handler.postUpdateSuggestionStrip(SuggestedWords.INPUT_STYLE_NONE);
+
+ StatsUtils.onPickSuggestionManually(
+ mSuggestedWords, suggestionInfo, mDictionaryFacilitator);
+ StatsUtils.onWordCommitSuggestionPickedManually(
+ suggestionInfo.mWord, mWordComposer.isBatchMode());
return inputTransaction;
}
@@ -416,14 +397,8 @@ public final class InputLogic {
// The cursor has been moved : we now accept to perform recapitalization
mRecapitalizeStatus.enable();
- // We moved the cursor and need to invalidate the indicator right now.
- mTextDecorator.reset();
- // Remaining background color that was used for the add-to-dictionary indicator should be
- // removed.
- mConnection.removeBackgroundColorFromHighlightedTextIfNecessary();
// We moved the cursor. If we are touching a word, we need to resume suggestion.
- mLatinIME.mHandler.postResumeSuggestions(false /* shouldIncludeResumedWordInSuggestions */,
- true /* shouldDelay */);
+ mLatinIME.mHandler.postResumeSuggestions(true /* shouldDelay */);
// Stop the last recapitalization, if started.
mRecapitalizeStatus.stop();
return true;
@@ -442,9 +417,8 @@ public final class InputLogic {
* {@link com.android.inputmethod.keyboard.KeyboardSwitcher#getKeyboardShiftMode()}
* @return the complete transaction object
*/
- public InputTransaction onCodeInput(final SettingsValues settingsValues, final Event event,
- final int keyboardShiftMode,
- // TODO: remove these arguments
+ public InputTransaction onCodeInput(final SettingsValues settingsValues,
+ @Nonnull final Event event, final int keyboardShiftMode,
final int currentKeyboardScriptId, final LatinIME.UIHandler handler) {
final Event processedEvent = mWordComposer.processEvent(event);
final InputTransaction inputTransaction = new InputTransaction(settingsValues,
@@ -491,17 +465,14 @@ public final class InputLogic {
}
public void onStartBatchInput(final SettingsValues settingsValues,
- // TODO: remove these arguments
final KeyboardSwitcher keyboardSwitcher, final LatinIME.UIHandler handler) {
mInputLogicHandler.onStartBatchInput();
handler.showGesturePreviewAndSuggestionStrip(
- SuggestedWords.EMPTY, false /* dismissGestureFloatingPreviewText */);
+ SuggestedWords.getEmptyInstance(), false /* dismissGestureFloatingPreviewText */);
handler.cancelUpdateSuggestionStrip();
++mAutoCommitSequenceNumber;
mConnection.beginBatchEdit();
- if (!mWordComposer.isComposingWord()) {
- mConnection.removeBackgroundColorFromHighlightedTextIfNecessary();
- } else {
+ if (mWordComposer.isComposingWord()) {
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.
@@ -557,30 +528,7 @@ public final class InputLogic {
* earlier sequence number.
*/
private int mAutoCommitSequenceNumber = 1;
- public void onUpdateBatchInput(final SettingsValues settingsValues,
- final InputPointers batchPointers,
- // TODO: remove these arguments
- final KeyboardSwitcher keyboardSwitcher) {
- if (settingsValues.mPhraseGestureEnabled) {
- final SuggestedWordInfo candidate = mSuggestedWords.getAutoCommitCandidate();
- // 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(Constants.WORD_SEPARATOR, 2);
- batchPointers.shift(candidate.mIndexOfTouchPointOfSecondWord);
- promotePhantomSpace(settingsValues);
- mConnection.commitText(commitParts[0], 0);
- mSpaceState = SpaceState.PHANTOM;
- keyboardSwitcher.requestUpdatingShiftState(
- getCurrentAutoCapsState(settingsValues), getCurrentRecapitalizeState());
- mWordComposer.setCapitalizedModeAtStartComposingTime(getActualCapsMode(
- settingsValues, keyboardSwitcher.getKeyboardShiftMode()));
- ++mAutoCommitSequenceNumber;
- }
- }
- }
+ public void onUpdateBatchInput(final InputPointers batchPointers) {
mInputLogicHandler.onUpdateBatchInput(batchPointers, mAutoCommitSequenceNumber);
}
@@ -589,27 +537,25 @@ public final class InputLogic {
++mAutoCommitSequenceNumber;
}
- // TODO: remove this argument
public void onCancelBatchInput(final LatinIME.UIHandler handler) {
mInputLogicHandler.onCancelBatchInput();
handler.showGesturePreviewAndSuggestionStrip(
- SuggestedWords.EMPTY, true /* dismissGestureFloatingPreviewText */);
+ SuggestedWords.getEmptyInstance(), true /* dismissGestureFloatingPreviewText */);
}
// TODO: on the long term, this method should become private, but it will be difficult.
// Especially, how do we deal with InputMethodService.onDisplayCompletions?
- public void setSuggestedWords(final SuggestedWords suggestedWords,
- final SettingsValues settingsValues, final LatinIME.UIHandler handler) {
- if (SuggestedWords.EMPTY != suggestedWords) {
- final String autoCorrection;
+ public void setSuggestedWords(final SuggestedWords suggestedWords) {
+ if (!suggestedWords.isEmpty()) {
+ final SuggestedWordInfo suggestedWordInfo;
if (suggestedWords.mWillAutoCorrect) {
- autoCorrection = suggestedWords.getWord(SuggestedWords.INDEX_OF_AUTO_CORRECTION);
+ suggestedWordInfo = suggestedWords.getInfo(SuggestedWords.INDEX_OF_AUTO_CORRECTION);
} else {
// We can't use suggestedWords.getWord(SuggestedWords.INDEX_OF_TYPED_WORD)
// because it may differ from mWordComposer.mTypedWord.
- autoCorrection = suggestedWords.mTypedWord;
+ suggestedWordInfo = suggestedWords.mTypedWordInfo;
}
- mWordComposer.setAutoCorrection(autoCorrection);
+ mWordComposer.setAutoCorrection(suggestedWordInfo);
}
mSuggestedWords = suggestedWords;
final boolean newAutoCorrectionIndicator = suggestedWords.mWillAutoCorrect;
@@ -666,7 +612,6 @@ public final class InputLogic {
* @param inputTransaction The transaction in progress.
*/
private void handleFunctionalEvent(final Event event, final InputTransaction inputTransaction,
- // TODO: remove these arguments
final int currentKeyboardScriptId, final LatinIME.UIHandler handler) {
switch (event.mKeyCode) {
case Constants.CODE_DELETE:
@@ -683,7 +628,7 @@ public final class InputLogic {
break;
case Constants.CODE_CAPSLOCK:
// Note: Changing keyboard to shift lock state is handled in
- // {@link KeyboardSwitcher#onCodeInput(int)}.
+ // {@link KeyboardSwitcher#onEvent(Event)}.
break;
case Constants.CODE_SYMBOL_SHIFT:
// Note: Calling back to the keyboard on the symbol Shift key is handled in
@@ -711,14 +656,13 @@ public final class InputLogic {
break;
case Constants.CODE_EMOJI:
// Note: Switching emoji keyboard is being handled in
- // {@link KeyboardState#onCodeInput(int,int)}.
+ // {@link KeyboardState#onEvent(Event,int)}.
break;
case Constants.CODE_ALPHA_FROM_EMOJI:
// Note: Switching back from Emoji keyboard to the main keyboard is being
- // handled in {@link KeyboardState#onCodeInput(int,int)}.
+ // handled in {@link KeyboardState#onEvent(Event,int)}.
break;
case Constants.CODE_SHIFT_ENTER:
- // TODO: remove this object
final Event tmpEvent = Event.createSoftwareKeypressEvent(Constants.CODE_ENTER,
event.mKeyCode, event.mX, event.mY, event.isKeyRepeat());
handleNonSpecialCharacterEvent(tmpEvent, inputTransaction, handler);
@@ -742,7 +686,6 @@ public final class InputLogic {
*/
private void handleNonFunctionalEvent(final Event event,
final InputTransaction inputTransaction,
- // TODO: remove this argument
final LatinIME.UIHandler handler) {
inputTransaction.setDidAffectContents();
switch (event.mCodePoint) {
@@ -788,18 +731,7 @@ public final class InputLogic {
*/
private void handleNonSpecialCharacterEvent(final Event event,
final InputTransaction inputTransaction,
- // TODO: remove this argument
final LatinIME.UIHandler handler) {
- if (!mWordComposer.isComposingWord()) {
- mConnection.removeBackgroundColorFromHighlightedTextIfNecessary();
- // In case the "add to dictionary" hint was still displayed.
- // TODO: Do we really need to check if we have composing text here?
- if (mSuggestionStripViewAccessor.isShowingAddToDictionaryHint()) {
- mSuggestionStripViewAccessor.dismissAddToDictionaryHint();
- mTextDecorator.reset();
- }
- }
-
final int codePoint = event.mCodePoint;
mSpaceState = SpaceState.NONE;
if (inputTransaction.mSettingsValues.isWordSeparator(codePoint)
@@ -843,7 +775,7 @@ public final class InputLogic {
// Sanity check
throw new RuntimeException("Should not be composing here");
}
- promotePhantomSpace(settingsValues);
+ insertAutomaticSpaceIfOptionsAndTextAllow(settingsValues);
}
if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) {
@@ -904,7 +836,6 @@ public final class InputLogic {
* @param inputTransaction The transaction in progress.
*/
private void handleSeparatorEvent(final Event event, final InputTransaction inputTransaction,
- // TODO: remove this argument
final LatinIME.UIHandler handler) {
final int codePoint = event.mCodePoint;
final SettingsValues settingsValues = inputTransaction.mSettingsValues;
@@ -954,12 +885,13 @@ public final class InputLogic {
}
if (needsPrecedingSpace) {
- promotePhantomSpace(settingsValues);
+ insertAutomaticSpaceIfOptionsAndTextAllow(settingsValues);
}
if (tryPerformDoubleSpacePeriod(event, inputTransaction)) {
mSpaceState = SpaceState.DOUBLE;
inputTransaction.setRequiresUpdateSuggestions();
+ StatsUtils.onDoubleSpacePeriod();
} else if (swapWeakSpace && trySwapSwapperAndSpace(event, inputTransaction)) {
mSpaceState = SpaceState.SWAP_PUNCTUATION;
mSuggestionStripViewAccessor.setNeutralSuggestionStrip();
@@ -1011,7 +943,6 @@ public final class InputLogic {
* @param inputTransaction The transaction in progress.
*/
private void handleBackspaceEvent(final Event event, final InputTransaction inputTransaction,
- // TODO: remove this argument, put it into settingsValues
final int currentKeyboardScriptId) {
mSpaceState = SpaceState.NONE;
mDeleteCount++;
@@ -1041,10 +972,13 @@ public final class InputLogic {
mWordComposer.reset();
mWordComposer.setRejectedBatchModeSuggestion(rejectedSuggestion);
if (!TextUtils.isEmpty(rejectedSuggestion)) {
- mDictionaryFacilitator.removeWordFromPersonalizedDicts(rejectedSuggestion);
+ unlearnWord(rejectedSuggestion, inputTransaction.mSettingsValues,
+ Constants.EVENT_REJECTION);
}
+ StatsUtils.onBackspaceWordDelete(rejectedSuggestion.length());
} else {
mWordComposer.applyProcessedEvent(event);
+ StatsUtils.onBackspacePressed(1);
}
if (mWordComposer.isComposingWord()) {
setComposingTextInternal(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
@@ -1054,7 +988,24 @@ public final class InputLogic {
inputTransaction.setRequiresUpdateSuggestions();
} else {
if (mLastComposedWord.canRevertCommit()) {
+ final String lastComposedWord = mLastComposedWord.mTypedWord;
revertCommit(inputTransaction, inputTransaction.mSettingsValues);
+ StatsUtils.onRevertAutoCorrect();
+ StatsUtils.onWordCommitUserTyped(lastComposedWord, mWordComposer.isBatchMode());
+ // Restart suggestions when backspacing into a reverted word. This is required for
+ // the final corrected word to be learned, as learning only occurs when suggestions
+ // are active.
+ //
+ // Note: restartSuggestionsOnWordTouchedByCursor is already called for normal
+ // (non-revert) backspace handling.
+ if (inputTransaction.mSettingsValues.isSuggestionsEnabledPerUserSettings()
+ && inputTransaction.mSettingsValues.mSpacingAndPunctuations
+ .mCurrentLanguageHasSpaces
+ && !mConnection.isCursorFollowedByWordCharacter(
+ inputTransaction.mSettingsValues.mSpacingAndPunctuations)) {
+ restartSuggestionsOnWordTouchedByCursor(inputTransaction.mSettingsValues,
+ false /* forStartInput */, currentKeyboardScriptId);
+ }
return;
}
if (mEnteredText != null && mConnection.sameAsTextBeforeCursor(mEnteredText)) {
@@ -1062,6 +1013,7 @@ public final class InputLogic {
// This is triggered on backspace after a key that inputs multiple characters,
// like the smiley key or the .com key.
mConnection.deleteSurroundingText(mEnteredText.length(), 0);
+ StatsUtils.onDeleteMultiCharInput(mEnteredText.length());
mEnteredText = null;
// If we have mEnteredText, then we know that mHasUncommittedTypedChars == false.
// In addition we know that spaceState is false, and that we should not be
@@ -1070,21 +1022,26 @@ public final class InputLogic {
}
if (SpaceState.DOUBLE == inputTransaction.mSpaceState) {
cancelDoubleSpacePeriodCountdown();
- if (mConnection.revertDoubleSpacePeriod()) {
+ if (mConnection.revertDoubleSpacePeriod(
+ inputTransaction.mSettingsValues.mSpacingAndPunctuations)) {
// No need to reset mSpaceState, it has already be done (that's why we
// receive it as a parameter)
inputTransaction.setRequiresUpdateSuggestions();
mWordComposer.setCapitalizedModeAtStartComposingTime(
WordComposer.CAPS_MODE_OFF);
+ StatsUtils.onRevertDoubleSpacePeriod();
return;
}
} else if (SpaceState.SWAP_PUNCTUATION == inputTransaction.mSpaceState) {
if (mConnection.revertSwapPunctuation()) {
+ StatsUtils.onRevertSwapPunctuation();
// Likewise
return;
}
}
+ boolean hasUnlearnedWordBeingDeleted = false;
+
// No cancelling of commit/double space/swap: we have a regular backspace.
// We should backspace one char and restart suggestion if at the end of a word.
if (mConnection.hasSelection()) {
@@ -1094,25 +1051,36 @@ public final class InputLogic {
mConnection.setSelection(mConnection.getExpectedSelectionEnd(),
mConnection.getExpectedSelectionEnd());
mConnection.deleteSurroundingText(numCharsDeleted, 0);
+ StatsUtils.onBackspaceSelectedText(numCharsDeleted);
} else {
// There is no selection, just delete one character.
- if (Constants.NOT_A_CURSOR_POSITION == mConnection.getExpectedSelectionEnd()) {
- // This should never happen.
- Log.e(TAG, "Backspace when we don't know the selection position");
- }
- if (inputTransaction.mSettingsValues.isBeforeJellyBean() ||
- inputTransaction.mSettingsValues.mInputAttributes.isTypeNull()) {
- // There are two possible reasons to send a key event: either the field has
+ if (inputTransaction.mSettingsValues.isBeforeJellyBean()
+ || inputTransaction.mSettingsValues.mInputAttributes.isTypeNull()
+ || Constants.NOT_A_CURSOR_POSITION
+ == mConnection.getExpectedSelectionEnd()) {
+ // There are three possible reasons to send a key event: either the field has
// type TYPE_NULL, in which case the keyboard should send events, or we are
- // running in backward compatibility mode. Before Jelly bean, the keyboard
- // would simulate a hardware keyboard event on pressing enter or delete. This
- // is bad for many reasons (there are race conditions with commits) but some
- // applications are relying on this behavior so we continue to support it for
- // older apps, so we retain this behavior if the app has target SDK < JellyBean.
+ // running in backward compatibility mode, or we don't know the cursor position.
+ // Before Jelly bean, the keyboard would simulate a hardware keyboard event on
+ // pressing enter or delete. This is bad for many reasons (there are race
+ // conditions with commits) but some applications are relying on this behavior
+ // so we continue to support it for older apps, so we retain this behavior if
+ // the app has target SDK < JellyBean.
+ // As for the case where we don't know the cursor position, it can happen
+ // because of bugs in the framework. But the framework should know, so the next
+ // best thing is to leave it to whatever it thinks is best.
sendDownUpKeyEvent(KeyEvent.KEYCODE_DEL);
+ int totalDeletedLength = 1;
if (mDeleteCount > Constants.DELETE_ACCELERATE_AT) {
+ // If this is an accelerated (i.e., double) deletion, then we need to
+ // consider unlearning here because we may have already reached
+ // the previous word, and will lose it after next deletion.
+ hasUnlearnedWordBeingDeleted |= unlearnWordBeingDeleted(
+ inputTransaction.mSettingsValues, currentKeyboardScriptId);
sendDownUpKeyEvent(KeyEvent.KEYCODE_DEL);
+ totalDeletedLength++;
}
+ StatsUtils.onBackspacePressed(totalDeletedLength);
} else {
final int codePointBeforeCursor = mConnection.getCodePointBeforeCursor();
if (codePointBeforeCursor == Constants.NOT_A_CODE) {
@@ -1123,32 +1091,81 @@ public final class InputLogic {
// catch it and have their broken interface react. If you need the keyboard
// to do this, you're doing it wrong -- please fix your app.
mConnection.deleteSurroundingText(1, 0);
+ // TODO: Add a new StatsUtils method onBackspaceWhenNoText()
return;
}
final int lengthToDelete =
Character.isSupplementaryCodePoint(codePointBeforeCursor) ? 2 : 1;
mConnection.deleteSurroundingText(lengthToDelete, 0);
+ int totalDeletedLength = lengthToDelete;
if (mDeleteCount > Constants.DELETE_ACCELERATE_AT) {
+ // If this is an accelerated (i.e., double) deletion, then we need to
+ // consider unlearning here because we may have already reached
+ // the previous word, and will lose it after next deletion.
+ hasUnlearnedWordBeingDeleted |= unlearnWordBeingDeleted(
+ inputTransaction.mSettingsValues, currentKeyboardScriptId);
final int codePointBeforeCursorToDeleteAgain =
mConnection.getCodePointBeforeCursor();
if (codePointBeforeCursorToDeleteAgain != Constants.NOT_A_CODE) {
final int lengthToDeleteAgain = Character.isSupplementaryCodePoint(
codePointBeforeCursorToDeleteAgain) ? 2 : 1;
mConnection.deleteSurroundingText(lengthToDeleteAgain, 0);
+ totalDeletedLength += lengthToDeleteAgain;
}
}
+ StatsUtils.onBackspacePressed(totalDeletedLength);
}
}
- if (inputTransaction.mSettingsValues
- .isSuggestionsEnabledPerUserSettings()
+ if (!hasUnlearnedWordBeingDeleted) {
+ // Consider unlearning the word being deleted (if we have not done so already).
+ unlearnWordBeingDeleted(
+ inputTransaction.mSettingsValues, currentKeyboardScriptId);
+ }
+ if (inputTransaction.mSettingsValues.isSuggestionsEnabledPerUserSettings()
&& inputTransaction.mSettingsValues.mSpacingAndPunctuations
.mCurrentLanguageHasSpaces
&& !mConnection.isCursorFollowedByWordCharacter(
inputTransaction.mSettingsValues.mSpacingAndPunctuations)) {
restartSuggestionsOnWordTouchedByCursor(inputTransaction.mSettingsValues,
- true /* shouldIncludeResumedWordInSuggestions */, currentKeyboardScriptId);
+ false /* forStartInput */, currentKeyboardScriptId);
+ }
+ }
+ }
+
+ boolean unlearnWordBeingDeleted(
+ final SettingsValues settingsValues,final int currentKeyboardScriptId) {
+ // If we just started backspacing to delete a previous word (but have not
+ // entered the composing state yet), unlearn the word.
+ // TODO: Consider tracking whether or not this word was typed by the user.
+ if (!mConnection.hasSelection()
+ && settingsValues.isSuggestionsEnabledPerUserSettings()
+ && settingsValues.mSpacingAndPunctuations.mCurrentLanguageHasSpaces
+ && !mConnection.isCursorFollowedByWordCharacter(
+ settingsValues.mSpacingAndPunctuations)) {
+ final TextRange range = mConnection.getWordRangeAtCursor(
+ settingsValues.mSpacingAndPunctuations,
+ currentKeyboardScriptId);
+ if (range == null) {
+ // Happens if we don't have an input connection at all.
+ return false;
+ }
+ final String wordBeingDeleted = range.mWord.toString();
+ if (!wordBeingDeleted.isEmpty()) {
+ unlearnWord(wordBeingDeleted, settingsValues,
+ Constants.EVENT_BACKSPACE);
+ return true;
}
}
+ return false;
+ }
+
+ void unlearnWord(final String word, final SettingsValues settingsValues, final int eventType) {
+ final NgramContext ngramContext = mConnection.getNgramContextFromNthPreviousWord(
+ settingsValues.mSpacingAndPunctuations, 2);
+ final long timeStampInSeconds = TimeUnit.MILLISECONDS.toSeconds(
+ System.currentTimeMillis());
+ mDictionaryFacilitator.unlearnFromUserHistory(
+ word, ngramContext, timeStampInSeconds, eventType);
}
/**
@@ -1253,7 +1270,9 @@ public final class InputLogic {
if (null == lastTwo) return false;
final int length = lastTwo.length();
if (length < 2) return false;
- if (lastTwo.charAt(length - 1) != Constants.CODE_SPACE) return false;
+ if (lastTwo.charAt(length - 1) != Constants.CODE_SPACE) {
+ return false;
+ }
// We know there is a space in pos -1, and we have at least two chars. If we have only two
// chars, isSurrogatePairs can't return true as charAt(1) is a space, so this is fine.
final int firstCodePoint =
@@ -1336,7 +1355,7 @@ public final class InputLogic {
}
private void performAdditionToUserHistoryDictionary(final SettingsValues settingsValues,
- final String suggestion, final PrevWordsInfo prevWordsInfo) {
+ final String suggestion, @Nonnull final NgramContext ngramContext) {
// If correction is not enabled, we don't add words to the user history dictionary.
// That's to avoid unintended additions in some sensitive fields, or fields that
// expect to receive non-words.
@@ -1348,7 +1367,7 @@ public final class InputLogic {
final int timeStampInSeconds = (int)TimeUnit.MILLISECONDS.toSeconds(
System.currentTimeMillis());
mDictionaryFacilitator.addToUserHistory(suggestion, wasAutoCapitalized,
- prevWordsInfo, timeStampInSeconds, settingsValues.mBlockPotentiallyOffensive);
+ ngramContext, timeStampInSeconds, settingsValues.mBlockPotentiallyOffensive);
}
public void performUpdateSuggestionStripSync(final SettingsValues settingsValues,
@@ -1360,7 +1379,7 @@ public final class InputLogic {
+ "requested!");
}
// Clear the suggestions strip.
- mSuggestionStripViewAccessor.showSuggestionStrip(SuggestedWords.EMPTY);
+ mSuggestionStripViewAccessor.showSuggestionStrip(SuggestedWords.getEmptyInstance());
return;
}
@@ -1374,14 +1393,20 @@ public final class InputLogic {
new OnGetSuggestedWordsCallback() {
@Override
public void onGetSuggestedWords(final SuggestedWords suggestedWords) {
- final String typedWord = mWordComposer.getTypedWord();
+ final String typedWordString = mWordComposer.getTypedWord();
+ final SuggestedWordInfo typedWordInfo = new SuggestedWordInfo(
+ typedWordString, "" /* prevWordsContext */,
+ SuggestedWordInfo.MAX_SCORE,
+ SuggestedWordInfo.KIND_TYPED, Dictionary.DICTIONARY_USER_TYPED,
+ SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
+ SuggestedWordInfo.NOT_A_CONFIDENCE);
// Show new suggestions if we have at least one. Otherwise keep the old
// suggestions with the new typed word. Exception: if the length of the
// typed word is <= 1 (after a deletion typically) we clear old suggestions.
- if (suggestedWords.size() > 1 || typedWord.length() <= 1) {
+ if (suggestedWords.size() > 1 || typedWordString.length() <= 1) {
holder.set(suggestedWords);
} else {
- holder.set(retrieveOlderSuggestions(typedWord, mSuggestedWords));
+ holder.set(retrieveOlderSuggestions(typedWordInfo, mSuggestedWords));
}
}
}
@@ -1400,12 +1425,12 @@ public final class InputLogic {
* do nothing.
*
* @param settingsValues the current values of the settings.
- * @param shouldIncludeResumedWordInSuggestions whether to include the word on which we resume
- * suggestions in the suggestion list.
+ * @param forStartInput whether we're doing this in answer to starting the input (as opposed
+ * to a cursor move, for example). In ICS, there is a platform bug that we need to work
+ * around only when we come here at input start time.
*/
- // TODO: make this private.
public void restartSuggestionsOnWordTouchedByCursor(final SettingsValues settingsValues,
- final boolean shouldIncludeResumedWordInSuggestions,
+ final boolean forStartInput,
// TODO: remove this argument, put it into settingsValues
final int currentKeyboardScriptId) {
// HACK: We may want to special-case some apps that exhibit bad behavior in case of
@@ -1452,15 +1477,14 @@ public final class InputLogic {
final int numberOfCharsInWordBeforeCursor = range.getNumberOfCharsInWordBeforeCursor();
if (numberOfCharsInWordBeforeCursor > expectedCursorPosition) return;
final ArrayList<SuggestedWordInfo> suggestions = new ArrayList<>();
- final String typedWord = range.mWord.toString();
- if (shouldIncludeResumedWordInSuggestions) {
- suggestions.add(new SuggestedWordInfo(typedWord,
- SuggestedWords.MAX_SUGGESTIONS + 1,
- SuggestedWordInfo.KIND_TYPED, Dictionary.DICTIONARY_USER_TYPED,
- SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
- SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */));
- }
- if (!isResumableWord(settingsValues, typedWord)) {
+ final String typedWordString = range.mWord.toString();
+ final SuggestedWordInfo typedWordInfo = new SuggestedWordInfo(typedWordString,
+ "" /* prevWordsContext */, SuggestedWords.MAX_SUGGESTIONS + 1,
+ SuggestedWordInfo.KIND_TYPED, Dictionary.DICTIONARY_USER_TYPED,
+ SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
+ SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */);
+ suggestions.add(typedWordInfo);
+ if (!isResumableWord(settingsValues, typedWordString)) {
mSuggestionStripViewAccessor.setNeutralSuggestionStrip();
return;
}
@@ -1468,9 +1492,9 @@ public final class InputLogic {
for (final SuggestionSpan span : range.getSuggestionSpansAtWord()) {
for (final String s : span.getSuggestions()) {
++i;
- if (!TextUtils.equals(s, typedWord)) {
+ if (!TextUtils.equals(s, typedWordString)) {
suggestions.add(new SuggestedWordInfo(s,
- SuggestedWords.MAX_SUGGESTIONS - i,
+ "" /* prevWordsContext */, SuggestedWords.MAX_SUGGESTIONS - i,
SuggestedWordInfo.KIND_RESUMED, Dictionary.DICTIONARY_RESUMED,
SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
SuggestedWordInfo.NOT_A_CONFIDENCE
@@ -1478,47 +1502,25 @@ public final class InputLogic {
}
}
}
- final int[] codePoints = StringUtils.toCodePointArray(typedWord);
- // We want the previous word for suggestion. If we have chars in the word
- // before the cursor, then we want the word before that, hence 2; otherwise,
- // we want the word immediately before the cursor, hence 1.
- final PrevWordsInfo prevWordsInfo = getPrevWordsInfoFromNthPreviousWordForSuggestion(
- settingsValues.mSpacingAndPunctuations,
- 0 == numberOfCharsInWordBeforeCursor ? 1 : 2);
+ final int[] codePoints = StringUtils.toCodePointArray(typedWordString);
mWordComposer.setComposingWord(codePoints,
mLatinIME.getCoordinatesForCurrentKeyboard(codePoints));
mWordComposer.setCursorPositionWithinWord(
- typedWord.codePointCount(0, numberOfCharsInWordBeforeCursor));
- mConnection.maybeMoveTheCursorAroundAndRestoreToWorkaroundABug();
+ typedWordString.codePointCount(0, numberOfCharsInWordBeforeCursor));
+ if (forStartInput) {
+ mConnection.maybeMoveTheCursorAroundAndRestoreToWorkaroundABug();
+ }
mConnection.setComposingRegion(expectedCursorPosition - numberOfCharsInWordBeforeCursor,
expectedCursorPosition + range.getNumberOfCharsInWordAfterCursor());
- if (suggestions.size() <= (shouldIncludeResumedWordInSuggestions ? 1 : 0)) {
+ if (suggestions.size() <= 1) {
// If there weren't any suggestion spans on this word, suggestions#size() will be 1
// if shouldIncludeResumedWordInSuggestions is true, 0 otherwise. In this case, we
// have no useful suggestions, so we will try to compute some for it instead.
mInputLogicHandler.getSuggestedWords(Suggest.SESSION_ID_TYPING,
SuggestedWords.NOT_A_SEQUENCE_NUMBER, new OnGetSuggestedWordsCallback() {
@Override
- public void onGetSuggestedWords(
- final SuggestedWords suggestedWordsIncludingTypedWord) {
- final SuggestedWords suggestedWords;
- if (suggestedWordsIncludingTypedWord.size() > 1
- && !shouldIncludeResumedWordInSuggestions) {
- // 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 #getSuggestedWordsExcludingTypedWordForRecorrection()
- // method sets willAutoCorrect to false.
- suggestedWords = suggestedWordsIncludingTypedWord
- .getSuggestedWordsExcludingTypedWordForRecorrection();
- } 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;
- }
- mIsAutoCorrectionIndicatorOn = false;
- mLatinIME.mHandler.showSuggestionStrip(suggestedWords);
+ public void onGetSuggestedWords(final SuggestedWords suggestedWords) {
+ doShowSuggestionsAndClearAutoCorrectionIndicator(suggestedWords);
}});
} else {
// We found suggestion spans in the word. We'll create the SuggestedWords out of
@@ -1526,14 +1528,18 @@ public final class InputLogic {
// color of the word in the suggestion strip changes according to this parameter,
// and false gives the correct color.
final SuggestedWords suggestedWords = new SuggestedWords(suggestions,
- null /* rawSuggestions */, typedWord, false /* typedWordValid */,
+ null /* rawSuggestions */, typedWordInfo, false /* typedWordValid */,
false /* willAutoCorrect */, false /* isObsoleteSuggestions */,
SuggestedWords.INPUT_STYLE_RECORRECTION, SuggestedWords.NOT_A_SEQUENCE_NUMBER);
- mIsAutoCorrectionIndicatorOn = false;
- mLatinIME.mHandler.showSuggestionStrip(suggestedWords);
+ doShowSuggestionsAndClearAutoCorrectionIndicator(suggestedWords);
}
}
+ void doShowSuggestionsAndClearAutoCorrectionIndicator(final SuggestedWords suggestedWords) {
+ mIsAutoCorrectionIndicatorOn = false;
+ mLatinIME.mHandler.showSuggestionStrip(suggestedWords);
+ }
+
/**
* Reverts a previous commit with auto-correction.
*
@@ -1551,6 +1557,10 @@ public final class InputLogic {
final String committedWordString = committedWord.toString();
final int cancelLength = committedWord.length();
final String separatorString = mLastComposedWord.mSeparatorString;
+ // If our separator is a space, we won't actually commit it,
+ // but set the space state to PHANTOM so that a space will be inserted
+ // on the next keypress
+ final boolean usePhantomSpace = separatorString.equals(Constants.STRING_SPACE);
// We want java chars, not codepoints for the following.
final int separatorLength = separatorString.length();
// TODO: should we check our saved separator against the actual contents of the text view?
@@ -1569,9 +1579,11 @@ public final class InputLogic {
}
mConnection.deleteSurroundingText(deleteLength, 0);
if (!TextUtils.isEmpty(committedWord)) {
- mDictionaryFacilitator.removeWordFromPersonalizedDicts(committedWordString);
+ unlearnWord(committedWordString, inputTransaction.mSettingsValues,
+ Constants.EVENT_REVERT);
}
- final String stringToCommit = originallyTypedWord + separatorString;
+ final String stringToCommit = originallyTypedWord +
+ (usePhantomSpace ? "" : separatorString);
final SpannableString textToCommit = new SpannableString(stringToCommit);
if (committedWord instanceof SpannableString) {
final SpannableString committedWordWithSuggestionSpans = (SpannableString)committedWord;
@@ -1583,15 +1595,11 @@ public final class InputLogic {
// First, add the committed word to the list of suggestions.
suggestions.add(committedWordString);
for (final Object span : spans) {
- // If this is a suggestion span, we check that the locale is the right one, and
- // that the word is not the committed word. That should mostly be the case.
+ // If this is a suggestion span, we check that the word is not the committed word.
+ // That should mostly be the case.
// Given this, we add it to the list of suggestions, otherwise we discard it.
if (span instanceof SuggestionSpan) {
final SuggestionSpan suggestionSpan = (SuggestionSpan)span;
- if (!suggestionSpan.getLocale().equals(
- inputTransaction.mSettingsValues.mLocale.toString())) {
- continue;
- }
for (final String suggestion : suggestionSpan.getSuggestions()) {
if (!suggestion.equals(committedWordString)) {
suggestions.add(suggestion);
@@ -1604,24 +1612,17 @@ public final class InputLogic {
}
}
// Add the suggestion list to the list of suggestions.
- textToCommit.setSpan(new SuggestionSpan(inputTransaction.mSettingsValues.mLocale,
- suggestions.toArray(new String[suggestions.size()]), 0 /* flags */),
+ textToCommit.setSpan(new SuggestionSpan(mLatinIME /* context */,
+ inputTransaction.mSettingsValues.mLocale,
+ suggestions.toArray(new String[suggestions.size()]), 0 /* flags */,
+ null /* notificationTargetClass */),
0 /* start */, lastCharIndex /* end */, 0 /* flags */);
}
- final boolean shouldShowAddToDictionaryForTypedWord =
- shouldShowAddToDictionaryForTypedWord(mLastComposedWord, settingsValues);
-
if (inputTransaction.mSettingsValues.mSpacingAndPunctuations.mCurrentLanguageHasSpaces) {
- // For languages with spaces, we revert to the typed string, but the cursor is still
- // after the separator so we don't resume suggestions. If the user wants to correct
- // the word, they have to press backspace again.
- if (shouldShowAddToDictionaryForTypedWord) {
- mConnection.commitTextWithBackgroundColor(textToCommit, 1,
- settingsValues.mTextHighlightColorForAddToDictionaryIndicator,
- originallyTypedWordString.length());
- } else {
- mConnection.commitText(textToCommit, 1);
+ mConnection.commitText(textToCommit, 1);
+ if (usePhantomSpace) {
+ mSpaceState = SpaceState.PHANTOM;
}
} else {
// For languages without spaces, we revert the typed string but the cursor is flush
@@ -1629,32 +1630,13 @@ public final class InputLogic {
final int[] codePoints = StringUtils.toCodePointArray(stringToCommit);
mWordComposer.setComposingWord(codePoints,
mLatinIME.getCoordinatesForCurrentKeyboard(codePoints));
- if (shouldShowAddToDictionaryForTypedWord) {
- setComposingTextInternalWithBackgroundColor(textToCommit, 1,
- settingsValues.mTextHighlightColorForAddToDictionaryIndicator,
- originallyTypedWordString.length());
- } else {
- setComposingTextInternal(textToCommit, 1);
- }
+ setComposingTextInternal(textToCommit, 1);
}
// Don't restart suggestion yet. We'll restart if the user deletes the separator.
mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
- if (shouldShowAddToDictionaryForTypedWord) {
- // Due to the API limitation as of L, we cannot reliably retrieve the reverted text
- // when the separator causes line breaking. Until this API limitation is addressed in
- // the framework, show the indicator only when the separator doesn't contain
- // line-breaking characters.
- if (!StringUtils.hasLineBreakCharacter(separatorString)) {
- mTextDecorator.showAddToDictionaryIndicator(originallyTypedWordString,
- mConnection.getExpectedSelectionStart(),
- mConnection.getExpectedSelectionEnd());
- }
- mSuggestionStripViewAccessor.showAddToDictionaryHint(originallyTypedWordString);
- } else {
- // We have a separator between the word and the cursor: we should show predictions.
- inputTransaction.setRequiresUpdateSuggestions();
- }
+ // We have a separator between the word and the cursor: we should show predictions.
+ inputTransaction.setRequiresUpdateSuggestions();
}
/**
@@ -1720,26 +1702,25 @@ public final class InputLogic {
}
/**
- * Get information fo previous words from the nth previous word before the cursor as context
+ * Get n-gram context from the nth previous word before the cursor as context
* for the suggestion process.
* @param spacingAndPunctuations the current spacing and punctuations settings.
* @param nthPreviousWord reverse index of the word to get (1-indexed)
* @return the information of previous words
*/
- // TODO: Make this private
- public PrevWordsInfo getPrevWordsInfoFromNthPreviousWordForSuggestion(
+ public NgramContext getNgramContextFromNthPreviousWordForSuggestion(
final SpacingAndPunctuations spacingAndPunctuations, final int nthPreviousWord) {
if (spacingAndPunctuations.mCurrentLanguageHasSpaces) {
// If we are typing in a language with spaces we can just look up the previous
// word information from textview.
- return mConnection.getPrevWordsInfoFromNthPreviousWord(
+ return mConnection.getNgramContextFromNthPreviousWord(
spacingAndPunctuations, nthPreviousWord);
- } else {
- return LastComposedWord.NOT_A_COMPOSED_WORD == mLastComposedWord ?
- PrevWordsInfo.BEGINNING_OF_SENTENCE :
- new PrevWordsInfo(new PrevWordsInfo.WordInfo(
- mLastComposedWord.mCommittedWord.toString()));
}
+ if (LastComposedWord.NOT_A_COMPOSED_WORD == mLastComposedWord) {
+ return NgramContext.BEGINNING_OF_SENTENCE;
+ }
+ return new NgramContext(new NgramContext.WordInfo(
+ mLastComposedWord.mCommittedWord.toString()));
}
/**
@@ -1792,9 +1773,8 @@ public final class InputLogic {
// If no code point, #getCodePointBeforeCursor returns NOT_A_CODE_POINT.
if (Constants.CODE_PERIOD == codePointBeforeCursor) {
return text.substring(1);
- } else {
- return text;
}
+ return text;
}
/**
@@ -1845,21 +1825,21 @@ public final class InputLogic {
* Make a {@link com.android.inputmethod.latin.SuggestedWords} object containing a typed word
* and obsolete suggestions.
* See {@link com.android.inputmethod.latin.SuggestedWords#getTypedWordAndPreviousSuggestions(
- * String, com.android.inputmethod.latin.SuggestedWords)}.
- * @param typedWord The typed word as a string.
+ * SuggestedWordInfo, com.android.inputmethod.latin.SuggestedWords)}.
+ * @param typedWordInfo The typed word as a SuggestedWordInfo.
* @param previousSuggestedWords The previously suggested words.
* @return Obsolete suggestions with the newly typed word.
*/
- private SuggestedWords retrieveOlderSuggestions(final String typedWord,
+ static SuggestedWords retrieveOlderSuggestions(final SuggestedWordInfo typedWordInfo,
final SuggestedWords previousSuggestedWords) {
- final SuggestedWords oldSuggestedWords =
- previousSuggestedWords.isPunctuationSuggestions() ? SuggestedWords.EMPTY
- : previousSuggestedWords;
+ final SuggestedWords oldSuggestedWords = previousSuggestedWords.isPunctuationSuggestions()
+ ? SuggestedWords.getEmptyInstance() : previousSuggestedWords;
final ArrayList<SuggestedWords.SuggestedWordInfo> typedWordAndPreviousSuggestions =
- SuggestedWords.getTypedWordAndPreviousSuggestions(typedWord, oldSuggestedWords);
+ SuggestedWords.getTypedWordAndPreviousSuggestions(typedWordInfo, oldSuggestedWords);
return new SuggestedWords(typedWordAndPreviousSuggestions, null /* rawSuggestions */,
- false /* typedWordValid */, false /* hasAutoCorrectionCandidate */,
- true /* isObsoleteSuggestions */, oldSuggestedWords.mInputStyle);
+ typedWordInfo, false /* typedWordValid */, false /* hasAutoCorrectionCandidate */,
+ true /* isObsoleteSuggestions */, oldSuggestedWords.mInputStyle,
+ SuggestedWords.NOT_A_SEQUENCE_NUMBER);
}
/**
@@ -1936,14 +1916,14 @@ public final class InputLogic {
}
/**
- * Promote a phantom space to an actual space.
+ * Insert an automatic space, if the options allow it.
*
- * This essentially inserts a space, and that's it. It just checks the options and the text
- * before the cursor are appropriate before doing it.
+ * This checks the options and the text before the cursor are appropriate before inserting
+ * an automatic space.
*
* @param settingsValues the current values of the settings.
*/
- private void promotePhantomSpace(final SettingsValues settingsValues) {
+ private void insertAutomaticSpaceIfOptionsAndTextAllow(final SettingsValues settingsValues) {
if (settingsValues.shouldInsertSpacesAutomatically()
&& settingsValues.mSpacingAndPunctuations.mCurrentLanguageHasSpaces
&& !mConnection.textBeforeCursorLooksLikeURL()) {
@@ -1957,36 +1937,17 @@ public final class InputLogic {
* @param suggestedWords suggestedWords to use.
*/
public void onUpdateTailBatchInputCompleted(final SettingsValues settingsValues,
- final SuggestedWords suggestedWords,
- // TODO: remove this argument
- final KeyboardSwitcher keyboardSwitcher) {
+ final SuggestedWords suggestedWords, final KeyboardSwitcher keyboardSwitcher) {
final String batchInputText = suggestedWords.isEmpty() ? null : suggestedWords.getWord(0);
if (TextUtils.isEmpty(batchInputText)) {
return;
}
mConnection.beginBatchEdit();
if (SpaceState.PHANTOM == mSpaceState) {
- promotePhantomSpace(settingsValues);
- }
- final SuggestedWordInfo autoCommitCandidate = mSuggestedWords.getAutoCommitCandidate();
- // Commit except the last word for phrase gesture if the top suggestion is eligible for auto
- // commit.
- if (settingsValues.mPhraseGestureEnabled && null != autoCommitCandidate) {
- // Find the last space
- final int indexOfLastSpace = batchInputText.lastIndexOf(Constants.CODE_SPACE) + 1;
- if (0 != indexOfLastSpace) {
- mConnection.commitText(batchInputText.substring(0, indexOfLastSpace), 1);
- final SuggestedWords suggestedWordsForLastWordOfPhraseGesture =
- suggestedWords.getSuggestedWordsForLastWordOfPhraseGesture();
- mLatinIME.showSuggestionStrip(suggestedWordsForLastWordOfPhraseGesture);
- }
- final String lastWord = batchInputText.substring(indexOfLastSpace);
- mWordComposer.setBatchInputWord(lastWord);
- setComposingTextInternal(lastWord, 1);
- } else {
- mWordComposer.setBatchInputWord(batchInputText);
- setComposingTextInternal(batchInputText, 1);
+ insertAutomaticSpaceIfOptionsAndTextAllow(settingsValues);
}
+ mWordComposer.setBatchInputWord(batchInputText);
+ setComposingTextInternal(batchInputText, 1);
mConnection.endBatchEdit();
// Space state must be updated before calling updateShiftState
mSpaceState = SpaceState.PHANTOM;
@@ -2009,13 +1970,14 @@ public final class InputLogic {
* @param settingsValues the current values of the settings.
* @param separatorString the separator that's causing the commit, or NOT_A_SEPARATOR if none.
*/
- // TODO: Make this private
public void commitTyped(final SettingsValues settingsValues, final String separatorString) {
if (!mWordComposer.isComposingWord()) return;
final String typedWord = mWordComposer.getTypedWord();
if (typedWord.length() > 0) {
+ final boolean isBatchMode = mWordComposer.isBatchMode();
commitChosenWord(settingsValues, typedWord,
LastComposedWord.COMMIT_TYPE_USER_TYPED_WORD, separatorString);
+ StatsUtils.onWordCommitUserTyped(typedWord, isBatchMode);
}
}
@@ -2036,9 +1998,7 @@ public final class InputLogic {
* @param separator the separator that's causing the commit to happen.
*/
private void commitCurrentAutoCorrection(final SettingsValues settingsValues,
- final String separator,
- // TODO: Remove this argument.
- final LatinIME.UIHandler handler) {
+ final String separator, final LatinIME.UIHandler handler) {
// Complete any pending suggestions query first
if (handler.hasPendingUpdateSuggestions()) {
handler.cancelUpdateSuggestionStrip();
@@ -2052,18 +2012,19 @@ public final class InputLogic {
// INPUT_STYLE_TYPING.
performUpdateSuggestionStripSync(settingsValues, SuggestedWords.INPUT_STYLE_TYPING);
}
- final String typedAutoCorrection = mWordComposer.getAutoCorrectionOrNull();
+ final SuggestedWordInfo autoCorrectionOrNull = mWordComposer.getAutoCorrectionOrNull();
final String typedWord = mWordComposer.getTypedWord();
- final String autoCorrection = (typedAutoCorrection != null)
- ? typedAutoCorrection : typedWord;
- if (autoCorrection != null) {
+ final String stringToCommit = (autoCorrectionOrNull != null)
+ ? autoCorrectionOrNull.mWord : typedWord;
+ if (stringToCommit != null) {
if (TextUtils.isEmpty(typedWord)) {
throw new RuntimeException("We have an auto-correction but the typed word "
+ "is empty? Impossible! I must commit suicide.");
}
- commitChosenWord(settingsValues, autoCorrection,
+ final boolean isBatchMode = mWordComposer.isBatchMode();
+ commitChosenWord(settingsValues, stringToCommit,
LastComposedWord.COMMIT_TYPE_DECIDED_WORD, separator);
- if (!typedWord.equals(autoCorrection)) {
+ if (!typedWord.equals(stringToCommit)) {
// This will make the correction flash for a short while as a visual clue
// to the user that auto-correction happened. It has no other effect; in particular
// note that this won't affect the text inside the text field AT ALL: it only makes
@@ -2071,8 +2032,16 @@ public final class InputLogic {
// of the auto-correction flash. At this moment, the "typedWord" argument is
// ignored by TextView.
mConnection.commitCorrection(new CorrectionInfo(
- mConnection.getExpectedSelectionEnd() - autoCorrection.length(),
- typedWord, autoCorrection));
+ mConnection.getExpectedSelectionEnd() - stringToCommit.length(),
+ typedWord, stringToCommit));
+ String prevWordsContext = (autoCorrectionOrNull != null)
+ ? autoCorrectionOrNull.mPrevWordsContext
+ : "";
+ StatsUtils.onAutoCorrection(typedWord, stringToCommit, isBatchMode,
+ mDictionaryFacilitator, prevWordsContext);
+ StatsUtils.onWordCommitAutoCorrect(stringToCommit, isBatchMode);
+ } else {
+ StatsUtils.onWordCommitUserTyped(stringToCommit, isBatchMode);
}
}
}
@@ -2091,20 +2060,20 @@ public final class InputLogic {
final CharSequence chosenWordWithSuggestions =
SuggestionSpanUtils.getTextWithSuggestionSpan(mLatinIME, chosenWord,
suggestedWords);
- // When we are composing word, get previous words information from the 2nd previous word
- // because the 1st previous word is the word to be committed. Otherwise get previous words
- // information from the 1st previous word.
- final PrevWordsInfo prevWordsInfo = mConnection.getPrevWordsInfoFromNthPreviousWord(
+ // When we are composing word, get n-gram context from the 2nd previous word because the
+ // 1st previous word is the word to be committed. Otherwise get n-gram context from the 1st
+ // previous word.
+ final NgramContext ngramContext = mConnection.getNgramContextFromNthPreviousWord(
settingsValues.mSpacingAndPunctuations, mWordComposer.isComposingWord() ? 2 : 1);
mConnection.commitText(chosenWordWithSuggestions, 1);
// Add the word to the user history dictionary
- performAdditionToUserHistoryDictionary(settingsValues, chosenWord, prevWordsInfo);
+ performAdditionToUserHistoryDictionary(settingsValues, chosenWord, ngramContext);
// TODO: figure out here if this is an auto-correct or if the best word is actually
// what user typed. Note: currently this is done much later in
// LastComposedWord#didCommitTypedWord by string equality of the remembered
// strings.
mLastComposedWord = mWordComposer.commitWord(commitType,
- chosenWordWithSuggestions, separatorString, prevWordsInfo);
+ chosenWordWithSuggestions, separatorString, ngramContext);
}
/**
@@ -2118,11 +2087,8 @@ public final class InputLogic {
* @param remainingTries How many times we may try again before giving up.
* @return whether true if the caches were successfully reset, false otherwise.
*/
- // TODO: make this private
public boolean retryResetCachesAndReturnSuccess(final boolean tryResumeSuggestions,
- final int remainingTries,
- // TODO: remove these arguments
- final LatinIME.UIHandler handler) {
+ final int remainingTries, final LatinIME.UIHandler handler) {
final boolean shouldFinishComposition = mConnection.hasSelection()
|| !mConnection.isCursorPositionKnown();
if (!mConnection.resetCachesUponCursorMoveAndReturnSuccess(
@@ -2137,30 +2103,25 @@ public final class InputLogic {
}
mConnection.tryFixLyingCursorPosition();
if (tryResumeSuggestions) {
- // This is triggered when starting input anew, so we want to include the resumed
- // word in suggestions.
- handler.postResumeSuggestions(true /* shouldIncludeResumedWordInSuggestions */,
- true /* shouldDelay */);
+ handler.postResumeSuggestions(true /* shouldDelay */);
}
return true;
}
public void getSuggestedWords(final SettingsValues settingsValues,
- final ProximityInfo proximityInfo, final int keyboardShiftMode, final int inputStyle,
+ final Keyboard keyboard, final int keyboardShiftMode, final int inputStyle,
final int sequenceNumber, final OnGetSuggestedWordsCallback callback) {
mWordComposer.adviseCapitalizedModeBeforeFetchingSuggestions(
getActualCapsMode(settingsValues, keyboardShiftMode));
mSuggest.getSuggestedWords(mWordComposer,
- getPrevWordsInfoFromNthPreviousWordForSuggestion(
+ getNgramContextFromNthPreviousWordForSuggestion(
settingsValues.mSpacingAndPunctuations,
// Get the word on which we should search the bigrams. If we are composing
// a word, it's whatever is *before* the half-committed word in the buffer,
// hence 2; if we aren't, we should just skip whitespace if any, so 1.
mWordComposer.isComposingWord() ? 2 : 1),
- proximityInfo,
- new SettingsValuesForSuggestion(settingsValues.mBlockPotentiallyOffensive,
- settingsValues.mPhraseGestureEnabled,
- settingsValues.mAdditionalFeaturesSettingValues),
+ keyboard,
+ new SettingsValuesForSuggestion(settingsValues.mBlockPotentiallyOffensive),
settingsValues.mAutoCorrectionEnabledPerUserSettings,
inputStyle, sequenceNumber, callback);
}
@@ -2171,7 +2132,7 @@ public final class InputLogic {
*
* <p>Currently using this method is optional and you can still directly call
* {@link RichInputConnection#setComposingText(CharSequence, int)}, but it is recommended to
- * use this method whenever possible to optimize the behavior of {@link TextDecorator}.<p>
+ * use this method whenever possible.<p>
* <p>TODO: Should we move this mechanism to {@link RichInputConnection}?</p>
*
* @param newComposingText the composing text to be set
@@ -2216,70 +2177,44 @@ public final class InputLogic {
mConnection.setComposingText(composingTextToBeSet, newCursorPosition);
}
- //////////////////////////////////////////////////////////////////////////////////////////////
- // Following methods are tentatively placed in this class for the integration with
- // TextDecorator.
- // TODO: Decouple things that are not related to the input logic.
- //////////////////////////////////////////////////////////////////////////////////////////////
-
- /**
- * Sets the UI operator for {@link TextDecorator}.
- * @param uiOperator the UI operator which should be associated with {@link TextDecorator}.
- */
- public void setTextDecoratorUi(final TextDecoratorUiOperator uiOperator) {
- mTextDecorator.setUiOperator(uiOperator);
- }
-
/**
- * Must be called from {@link InputMethodService#onUpdateCursorAnchorInfo(CursorAnchorInfo)} is
- * called.
- * @param info The wrapper object with which we can access cursor/anchor info.
+ * Gets an object allowing private IME commands to be sent to the
+ * underlying editor.
+ * @return An object for sending private commands to the underlying editor.
*/
- public void onUpdateCursorAnchorInfo(final CursorAnchorInfoCompatWrapper info) {
- mTextDecorator.onUpdateCursorAnchorInfo(info);
+ public PrivateCommandPerformer getPrivateCommandPerformer() {
+ return mConnection;
}
/**
- * Must be called when {@link InputMethodService#updateFullscreenMode} is called.
- * @param isFullscreen {@code true} if the input method is in full-screen mode.
- */
- public void onUpdateFullscreenMode(final boolean isFullscreen) {
- mTextDecorator.notifyFullScreenMode(isFullscreen);
- }
-
- /**
- * Must be called from {@link LatinIME#addWordToUserDictionary(String)}.
+ * Gets the expected index of the first char of the composing span within the editor's text.
+ * Returns a negative value in case there appears to be no valid composing span.
+ *
+ * @see #getComposingLength()
+ * @see RichInputConnection#hasSelection()
+ * @see RichInputConnection#isCursorPositionKnown()
+ * @see RichInputConnection#getExpectedSelectionStart()
+ * @see RichInputConnection#getExpectedSelectionEnd()
+ * @return The expected index in Java chars of the first char of the composing span.
*/
- public void onAddWordToUserDictionary() {
- mConnection.removeBackgroundColorFromHighlightedTextIfNecessary();
- mTextDecorator.reset();
+ // TODO: try and see if we can get rid of this method. Ideally the users of this class should
+ // never need to know this.
+ public int getComposingStart() {
+ if (!mConnection.isCursorPositionKnown() || mConnection.hasSelection()) {
+ return -1;
+ }
+ return mConnection.getExpectedSelectionStart() - mWordComposer.size();
}
/**
- * Returns whether the add to dictionary indicator should be shown or not.
- * @param lastComposedWord the last composed word information.
- * @param settingsValues the current settings value.
- * @return {@code true} if the commit indicator should be shown.
+ * Gets the expected length in Java chars of the composing span.
+ * May be 0 if there is no valid composing span.
+ * @see #getComposingStart()
+ * @return The expected length of the composing span.
*/
- private boolean shouldShowAddToDictionaryForTypedWord(final LastComposedWord lastComposedWord,
- final SettingsValues settingsValues) {
- if (!mConnection.isCursorAnchorInfoMonitorEnabled()) {
- // We cannot help in this case because we are heavily relying on this new API.
- return false;
- }
- if (!settingsValues.mShouldShowUiToAcceptTypedWord) {
- return false;
- }
- if (TextUtils.isEmpty(lastComposedWord.mTypedWord)) {
- return false;
- }
- if (TextUtils.equals(lastComposedWord.mTypedWord, lastComposedWord.mCommittedWord)) {
- return false;
- }
- if (!mDictionaryFacilitator.isUserDictionaryEnabled()) {
- return false;
- }
- return !mDictionaryFacilitator.isValidWord(lastComposedWord.mTypedWord,
- true /* ignoreCase */);
+ // TODO: try and see if we can get rid of this method. Ideally the users of this class should
+ // never need to know this.
+ public int getComposingLength() {
+ return mWordComposer.size();
}
}
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java
index c6f83d0b9..ddc4ad99c 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java
@@ -21,11 +21,10 @@ import android.os.HandlerThread;
import android.os.Message;
import com.android.inputmethod.compat.LooperCompatUtils;
-import com.android.inputmethod.latin.InputPointers;
import com.android.inputmethod.latin.LatinIME;
-import com.android.inputmethod.latin.Suggest;
import com.android.inputmethod.latin.SuggestedWords;
import com.android.inputmethod.latin.Suggest.OnGetSuggestedWordsCallback;
+import com.android.inputmethod.latin.common.InputPointers;
/**
* A helper to manage deferred tasks for the input logic.
@@ -62,7 +61,7 @@ class InputLogicHandler implements Handler.Callback {
final OnGetSuggestedWordsCallback callback) {}
};
- private InputLogicHandler() {
+ InputLogicHandler() {
mNonUIThreadHandler = null;
mLatinIME = null;
mInputLogic = null;
@@ -134,30 +133,38 @@ class InputLogicHandler implements Handler.Callback {
return;
}
mInputLogic.mWordComposer.setBatchInputPointers(batchPointers);
+ final OnGetSuggestedWordsCallback callback = new OnGetSuggestedWordsCallback() {
+ @Override
+ public void onGetSuggestedWords(final SuggestedWords suggestedWords) {
+ showGestureSuggestionsWithPreviewVisuals(suggestedWords, isTailBatchInput);
+ }
+ };
getSuggestedWords(isTailBatchInput ? SuggestedWords.INPUT_STYLE_TAIL_BATCH
- : SuggestedWords.INPUT_STYLE_UPDATE_BATCH, sequenceNumber,
- new OnGetSuggestedWordsCallback() {
- @Override
- public void onGetSuggestedWords(SuggestedWords suggestedWords) {
- // We're now inside the callback. This always runs on the Non-UI thread,
- // no matter what thread updateBatchInput was originally called on.
- if (suggestedWords.isEmpty()) {
- // Use old suggestions if we don't have any new ones.
- // Previous suggestions are found in InputLogic#mSuggestedWords.
- // Since these are the most recent ones and we just recomputed
- // new ones to update them, then the previous ones are there.
- suggestedWords = mInputLogic.mSuggestedWords;
- }
- mLatinIME.mHandler.showGesturePreviewAndSuggestionStrip(suggestedWords,
- isTailBatchInput /* dismissGestureFloatingPreviewText */);
- if (isTailBatchInput) {
- mInBatchInput = false;
- // The following call schedules onEndBatchInputInternal
- // to be called on the UI thread.
- mLatinIME.mHandler.showTailBatchInputResult(suggestedWords);
- }
- }
- });
+ : SuggestedWords.INPUT_STYLE_UPDATE_BATCH, sequenceNumber, callback);
+ }
+ }
+
+ void showGestureSuggestionsWithPreviewVisuals(final SuggestedWords suggestedWordsForBatchInput,
+ final boolean isTailBatchInput) {
+ final SuggestedWords suggestedWordsToShowSuggestions;
+ // We're now inside the callback. This always runs on the Non-UI thread,
+ // no matter what thread updateBatchInput was originally called on.
+ if (suggestedWordsForBatchInput.isEmpty()) {
+ // Use old suggestions if we don't have any new ones.
+ // Previous suggestions are found in InputLogic#mSuggestedWords.
+ // Since these are the most recent ones and we just recomputed
+ // new ones to update them, then the previous ones are there.
+ suggestedWordsToShowSuggestions = mInputLogic.mSuggestedWords;
+ } else {
+ suggestedWordsToShowSuggestions = suggestedWordsForBatchInput;
+ }
+ mLatinIME.mHandler.showGesturePreviewAndSuggestionStrip(suggestedWordsToShowSuggestions,
+ isTailBatchInput /* dismissGestureFloatingPreviewText */);
+ if (isTailBatchInput) {
+ mInBatchInput = false;
+ // The following call schedules onEndBatchInputInternal
+ // to be called on the UI thread.
+ mLatinIME.mHandler.showTailBatchInputResult(suggestedWordsToShowSuggestions);
}
}
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/PrivateCommandPerformer.java b/java/src/com/android/inputmethod/latin/inputlogic/PrivateCommandPerformer.java
new file mode 100644
index 000000000..42eaa9c82
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/inputlogic/PrivateCommandPerformer.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014 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.inputlogic;
+
+import android.os.Bundle;
+
+/**
+ * Provides an interface matching
+ * {@link android.view.inputmethod.InputConnection#performPrivateCommand(String,Bundle)}.
+ */
+public interface PrivateCommandPerformer {
+ /**
+ * API to send private commands from an input method to its connected
+ * editor. This can be used to provide domain-specific features that are
+ * only known between certain input methods and their clients.
+ *
+ * @param action Name of the command to be performed. This must be a scoped
+ * name, i.e. prefixed with a package name you own, so that
+ * different developers will not create conflicting commands.
+ * @param data Any data to include with the command.
+ * @return true if the command was sent (regardless of whether the
+ * associated editor understood it), false if the input connection is no
+ * longer valid.
+ */
+ boolean performPrivateCommand(String action, Bundle data);
+}