diff --git a/java/src/com/android/inputmethod/keyboard/TextDecorator.java b/java/src/com/android/inputmethod/keyboard/TextDecorator.java index 0eb8b443a..178516134 100644 --- a/java/src/com/android/inputmethod/keyboard/TextDecorator.java +++ b/java/src/com/android/inputmethod/keyboard/TextDecorator.java @@ -158,7 +158,7 @@ public class TextDecorator { if (!currentFullScreenMode && fullScreenMode) { // Currently full screen mode is not supported. // TODO: Support full screen mode. - hideIndicator(); + mUiOperator.hideUi(); } mIsFullScreenMode = fullScreenMode; } @@ -193,17 +193,36 @@ public class TextDecorator { layoutImmediately(); } - private void hideIndicator() { - mUiOperator.hideUi(); + /** + * Hides indicator if the new composing text doesn't match the expected one. + * + *

Calling this method is optional but recommended whenever the new composition is passed to + * the application. The motivation of this method is to reduce the UI latency. With this method, + * we can hide the indicator without waiting the arrival of the + * {@link InputMethodService#onUpdateCursorAnchorInfo(CursorAnchorInfo)} callback, assuming that + * the application accepts the new composing text without any modification. Even if this + * assumption is false, the indicator will be shown again when + * {@link InputMethodService#onUpdateCursorAnchorInfo(CursorAnchorInfo)} is actually received. + *

+ * + * @param newComposingText the new composing text that is being passed to the application. + */ + public void hideIndicatorIfNecessary(final CharSequence newComposingText) { + if (mMode != MODE_COMMIT && mMode != MODE_ADD_TO_DICTIONARY) { + return; + } + if (!TextUtils.equals(newComposingText, mWaitingWord.mWord)) { + mUiOperator.hideUi(); + } } private void cancelLayoutInternalUnexpectedly(final String message) { - hideIndicator(); + mUiOperator.hideUi(); Log.d(TAG, message); } private void cancelLayoutInternalExpectedly(final String message) { - hideIndicator(); + mUiOperator.hideUi(); if (DEBUG) { Log.d(TAG, message); } @@ -261,7 +280,7 @@ public class TextDecorator { lastCharRectFlag & CursorAnchorInfoCompatWrapper.CHARACTER_RECT_TYPE_MASK; if (lastCharRect == null || matrix == null || lastCharRectType != CursorAnchorInfoCompatWrapper.CHARACTER_RECT_TYPE_FULLY_VISIBLE) { - hideIndicator(); + mUiOperator.hideUi(); return; } final RectF segmentStartCharRect = new RectF(lastCharRect); @@ -312,13 +331,13 @@ public class TextDecorator { if (!TextUtils.isEmpty(composingText)) { // This is an unexpected case. // TODO: Document this. - hideIndicator(); + mUiOperator.hideUi(); return; } // In MODE_ADD_TO_DICTIONARY, we cannot retrieve the character position at all because // of the lack of composing text. We will use the insertion marker position instead. if (info.isInsertionMarkerClipped()) { - hideIndicator(); + mUiOperator.hideUi(); return; } final float insertionMarkerHolizontal = info.getInsertionMarkerHorizontal(); diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java index 0f2ba53d5..e83f494b3 100644 --- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java +++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java @@ -649,7 +649,7 @@ public final class InputLogic { // message, this is called outside any batch edit. Potentially, this may result in some // janky flickering of the screen, although the display speed makes it unlikely in // the practice. - mConnection.setComposingText(textWithUnderline, 1); + setComposingTextInternal(textWithUnderline, 1); } } @@ -672,7 +672,7 @@ public final class InputLogic { inputTransaction.setDidAffectContents(); } if (mWordComposer.isComposingWord()) { - mConnection.setComposingText(mWordComposer.getTypedWord(), 1); + setComposingTextInternal(mWordComposer.getTypedWord(), 1); inputTransaction.setDidAffectContents(); inputTransaction.setRequiresUpdateSuggestions(); } @@ -908,8 +908,7 @@ public final class InputLogic { if (mWordComposer.isSingleLetter()) { mWordComposer.setCapitalizedModeAtStartComposingTime(inputTransaction.mShiftState); } - mConnection.setComposingText(getTextWithUnderline( - mWordComposer.getTypedWord()), 1); + setComposingTextInternal(getTextWithUnderline(mWordComposer.getTypedWord()), 1); } else { final boolean swapWeakSpace = tryStripSpaceAndReturnWhetherShouldSwapInstead(event, inputTransaction); @@ -1072,7 +1071,7 @@ public final class InputLogic { mWordComposer.applyProcessedEvent(event); } if (mWordComposer.isComposingWord()) { - mConnection.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1); + setComposingTextInternal(getTextWithUnderline(mWordComposer.getTypedWord()), 1); } else { mConnection.commitText("", 1); } @@ -1640,7 +1639,7 @@ public final class InputLogic { final int[] codePoints = StringUtils.toCodePointArray(stringToCommit); mWordComposer.setComposingWord(codePoints, mLatinIME.getCoordinatesForCurrentKeyboard(codePoints)); - mConnection.setComposingText(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; @@ -1973,10 +1972,10 @@ public final class InputLogic { } final String lastWord = batchInputText.substring(indexOfLastSpace); mWordComposer.setBatchInputWord(lastWord); - mConnection.setComposingText(lastWord, 1); + setComposingTextInternal(lastWord, 1); } else { mWordComposer.setBatchInputWord(batchInputText); - mConnection.setComposingText(batchInputText, 1); + setComposingTextInternal(batchInputText, 1); } mConnection.endBatchEdit(); // Space state must be updated before calling updateShiftState @@ -2175,6 +2174,24 @@ public final class InputLogic { inputStyle, sequenceNumber, callback); } + /** + * Used as an injection point for each call of + * {@link RichInputConnection#setComposingText(CharSequence, int)}. + * + *

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}.

+ *

TODO: Should we move this mechanism to {@link RichInputConnection}?

+ * + * @param newComposingText the composing text to be set + * @param newCursorPosition the new cursor position + */ + private void setComposingTextInternal(final CharSequence newComposingText, + final int newCursorPosition) { + mConnection.setComposingText(newComposingText, newCursorPosition); + mTextDecorator.hideIndicatorIfNecessary(newComposingText); + } + ////////////////////////////////////////////////////////////////////////////////////////////// // Following methods are tentatively placed in this class for the integration with // TextDecorator.