diff options
Diffstat (limited to 'java/src')
4 files changed, 105 insertions, 30 deletions
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. + * + * <p>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. + * </p> + * + * @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/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 779f4cc2c..f35c7dc2b 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -57,7 +57,6 @@ import android.view.inputmethod.InputMethodSubtype; import com.android.inputmethod.accessibility.AccessibilityUtils; import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.compat.CursorAnchorInfoCompatWrapper; -import com.android.inputmethod.compat.InputConnectionCompatUtils; import com.android.inputmethod.compat.InputMethodServiceCompatUtils; import com.android.inputmethod.dictionarypack.DictionaryPackConstants; import com.android.inputmethod.event.Event; @@ -778,20 +777,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // is not guaranteed. It may even be called at the same time on a different thread. final RichInputMethodSubtype richSubtype = new RichInputMethodSubtype(subtype); mSubtypeSwitcher.onSubtypeChanged(richSubtype); - mInputLogic.onSubtypeChanged(SubtypeLocaleUtils.getCombiningRulesExtraValue(subtype)); + mInputLogic.onSubtypeChanged(SubtypeLocaleUtils.getCombiningRulesExtraValue(subtype), + mSettings.getCurrent()); loadKeyboard(); } private void onStartInputInternal(final EditorInfo editorInfo, final boolean restarting) { super.onStartInput(editorInfo, restarting); - if (ProductionFlags.ENABLE_CURSOR_ANCHOR_INFO_CALLBACK) { - // AcceptTypedWord feature relies on CursorAnchorInfo. - if (mSettings.getCurrent().mShouldShowUiToAcceptTypedWord) { - InputConnectionCompatUtils.requestUpdateCursorAnchorInfo( - getCurrentInputConnection(), true /* enableMonitor */, - true /* requestImmediateCallback */); - } - } } @SuppressWarnings("deprecation") @@ -860,7 +852,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // span, so we should reset our state unconditionally, even if restarting is true. // We also tell the input logic about the combining rules for the current subtype, so // it can adjust its combiners if needed. - mInputLogic.startInput(mSubtypeSwitcher.getCombiningRulesExtraValueOfCurrentSubtype()); + mInputLogic.startInput(mSubtypeSwitcher.getCombiningRulesExtraValueOfCurrentSubtype(), + currentSettingsValues); // Note: the following does a round-trip IPC on the main thread: be careful final Locale currentLocale = mSubtypeSwitcher.getCurrentSubtypeLocale(); diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java index 035557610..497823aeb 100644 --- a/java/src/com/android/inputmethod/latin/RichInputConnection.java +++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java @@ -30,7 +30,9 @@ import android.view.inputmethod.CorrectionInfo; import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.InputConnection; +import android.view.inputmethod.InputMethodManager; +import com.android.inputmethod.compat.InputConnectionCompatUtils; import com.android.inputmethod.latin.settings.SpacingAndPunctuations; import com.android.inputmethod.latin.utils.CapsModeUtils; import com.android.inputmethod.latin.utils.DebugLogUtils; @@ -906,4 +908,33 @@ public final class RichInputConnection { mIC.setSelection(mExpectedSelStart, mExpectedSelEnd); } } + + private boolean mCursorAnchorInfoMonitorEnabled = false; + + /** + * Requests the editor to call back {@link InputMethodManager#updateCursorAnchorInfo}. + * @param enableMonitor {@code true} to request the editor to call back the method whenever the + * cursor/anchor position is changed. + * @param requestImmediateCallback {@code true} to request the editor to call back the method + * as soon as possible to notify the current cursor/anchor position to the input method. + * @return {@code true} if the request is accepted. Returns {@code false} otherwise, which + * includes "not implemented" or "rejected" or "temporarily unavailable" or whatever which + * prevents the application from fulfilling the request. (TODO: Improve the API when it turns + * out that we actually need more detailed error codes) + */ + public boolean requestUpdateCursorAnchorInfo(final boolean enableMonitor, + final boolean requestImmediateCallback) { + final boolean scheduled = InputConnectionCompatUtils.requestUpdateCursorAnchorInfo(mIC, + enableMonitor, requestImmediateCallback); + mCursorAnchorInfoMonitorEnabled = (scheduled && enableMonitor); + return scheduled; + } + + /** + * @return {@code true} if the application reported that the monitor mode of + * {@link InputMethodService#onUpdateCursorAnchorInfo(CursorAnchorInfo)} is currently enabled. + */ + public boolean isCursorAnchorInfoMonitorEnabled() { + return mCursorAnchorInfoMonitorEnabled; + } } diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java index e05725700..ad72b60b6 100644 --- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java +++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java @@ -50,6 +50,7 @@ import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.WordComposer; 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; @@ -140,8 +141,9 @@ public final class InputLogic { * Call this when input starts or restarts in some editor (typically, in onStartInputView). * * @param combiningSpec the combining spec string for this subtype + * @param settingsValues the current settings values */ - public void startInput(final String combiningSpec) { + public void startInput(final String combiningSpec, final SettingsValues settingsValues) { mEnteredText = null; mWordComposer.restartCombining(combiningSpec); resetComposingState(true /* alsoResetLastComposedWord */); @@ -159,15 +161,24 @@ public final class InputLogic { } else { mInputLogicHandler.reset(); } + + if (ProductionFlags.ENABLE_CURSOR_ANCHOR_INFO_CALLBACK) { + // AcceptTypedWord feature relies on CursorAnchorInfo. + if (settingsValues.mShouldShowUiToAcceptTypedWord) { + mConnection.requestUpdateCursorAnchorInfo(true /* enableMonitor */, + true /* requestImmediateCallback */); + } + } } /** * Call this when the subtype changes. * @param combiningSpec the spec string for the combining rules + * @param settingsValues the current settings values */ - public void onSubtypeChanged(final String combiningSpec) { + public void onSubtypeChanged(final String combiningSpec, final SettingsValues settingsValues) { finishInput(); - startInput(combiningSpec); + startInput(combiningSpec, settingsValues); } /** @@ -649,7 +660,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 +683,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 +919,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 +1082,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 +1650,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 +1983,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 +2185,24 @@ public final class InputLogic { inputStyle, sequenceNumber, callback); } + /** + * Used as an injection point for each call of + * {@link RichInputConnection#setComposingText(CharSequence, int)}. + * + * <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> + * <p>TODO: Should we move this mechanism to {@link RichInputConnection}?</p> + * + * @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. @@ -2221,6 +2249,10 @@ public final class InputLogic { */ private boolean shouldShowCommitIndicator(final SuggestedWords suggestedWords, 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; } |