Introduce a mechanism to hide the indicator speculatively
This is an optional optimization to reduce the UI latency. Imagine that the commit indicator is now displayed and the composing text is being updated, it is highly likely that the commit indicator will disappear unless the application rejects the setComposingText request. If we assume that the application will accept the new composing text without any modifications, we can hide the indicator without waiting for the arrival of new CursorAnchorInfo event. This optimization isn't dangerous because we can show the indicator again when we receive new CursorAnchorInfo event and the assumption is turned out to be invalid. Change-Id: Id59c6607a1029782410611e768791af9984f14acmain
parent
bea17c49ec
commit
a475c85480
|
@ -158,7 +158,7 @@ public class TextDecorator {
|
||||||
if (!currentFullScreenMode && fullScreenMode) {
|
if (!currentFullScreenMode && fullScreenMode) {
|
||||||
// Currently full screen mode is not supported.
|
// Currently full screen mode is not supported.
|
||||||
// TODO: Support full screen mode.
|
// TODO: Support full screen mode.
|
||||||
hideIndicator();
|
mUiOperator.hideUi();
|
||||||
}
|
}
|
||||||
mIsFullScreenMode = fullScreenMode;
|
mIsFullScreenMode = fullScreenMode;
|
||||||
}
|
}
|
||||||
|
@ -193,17 +193,36 @@ public class TextDecorator {
|
||||||
layoutImmediately();
|
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) {
|
private void cancelLayoutInternalUnexpectedly(final String message) {
|
||||||
hideIndicator();
|
mUiOperator.hideUi();
|
||||||
Log.d(TAG, message);
|
Log.d(TAG, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void cancelLayoutInternalExpectedly(final String message) {
|
private void cancelLayoutInternalExpectedly(final String message) {
|
||||||
hideIndicator();
|
mUiOperator.hideUi();
|
||||||
if (DEBUG) {
|
if (DEBUG) {
|
||||||
Log.d(TAG, message);
|
Log.d(TAG, message);
|
||||||
}
|
}
|
||||||
|
@ -261,7 +280,7 @@ public class TextDecorator {
|
||||||
lastCharRectFlag & CursorAnchorInfoCompatWrapper.CHARACTER_RECT_TYPE_MASK;
|
lastCharRectFlag & CursorAnchorInfoCompatWrapper.CHARACTER_RECT_TYPE_MASK;
|
||||||
if (lastCharRect == null || matrix == null || lastCharRectType !=
|
if (lastCharRect == null || matrix == null || lastCharRectType !=
|
||||||
CursorAnchorInfoCompatWrapper.CHARACTER_RECT_TYPE_FULLY_VISIBLE) {
|
CursorAnchorInfoCompatWrapper.CHARACTER_RECT_TYPE_FULLY_VISIBLE) {
|
||||||
hideIndicator();
|
mUiOperator.hideUi();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final RectF segmentStartCharRect = new RectF(lastCharRect);
|
final RectF segmentStartCharRect = new RectF(lastCharRect);
|
||||||
|
@ -312,13 +331,13 @@ public class TextDecorator {
|
||||||
if (!TextUtils.isEmpty(composingText)) {
|
if (!TextUtils.isEmpty(composingText)) {
|
||||||
// This is an unexpected case.
|
// This is an unexpected case.
|
||||||
// TODO: Document this.
|
// TODO: Document this.
|
||||||
hideIndicator();
|
mUiOperator.hideUi();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// In MODE_ADD_TO_DICTIONARY, we cannot retrieve the character position at all because
|
// 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.
|
// of the lack of composing text. We will use the insertion marker position instead.
|
||||||
if (info.isInsertionMarkerClipped()) {
|
if (info.isInsertionMarkerClipped()) {
|
||||||
hideIndicator();
|
mUiOperator.hideUi();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final float insertionMarkerHolizontal = info.getInsertionMarkerHorizontal();
|
final float insertionMarkerHolizontal = info.getInsertionMarkerHorizontal();
|
||||||
|
|
|
@ -649,7 +649,7 @@ public final class InputLogic {
|
||||||
// message, this is called outside any batch edit. Potentially, this may result in some
|
// 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
|
// janky flickering of the screen, although the display speed makes it unlikely in
|
||||||
// the practice.
|
// the practice.
|
||||||
mConnection.setComposingText(textWithUnderline, 1);
|
setComposingTextInternal(textWithUnderline, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -672,7 +672,7 @@ public final class InputLogic {
|
||||||
inputTransaction.setDidAffectContents();
|
inputTransaction.setDidAffectContents();
|
||||||
}
|
}
|
||||||
if (mWordComposer.isComposingWord()) {
|
if (mWordComposer.isComposingWord()) {
|
||||||
mConnection.setComposingText(mWordComposer.getTypedWord(), 1);
|
setComposingTextInternal(mWordComposer.getTypedWord(), 1);
|
||||||
inputTransaction.setDidAffectContents();
|
inputTransaction.setDidAffectContents();
|
||||||
inputTransaction.setRequiresUpdateSuggestions();
|
inputTransaction.setRequiresUpdateSuggestions();
|
||||||
}
|
}
|
||||||
|
@ -908,8 +908,7 @@ public final class InputLogic {
|
||||||
if (mWordComposer.isSingleLetter()) {
|
if (mWordComposer.isSingleLetter()) {
|
||||||
mWordComposer.setCapitalizedModeAtStartComposingTime(inputTransaction.mShiftState);
|
mWordComposer.setCapitalizedModeAtStartComposingTime(inputTransaction.mShiftState);
|
||||||
}
|
}
|
||||||
mConnection.setComposingText(getTextWithUnderline(
|
setComposingTextInternal(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
|
||||||
mWordComposer.getTypedWord()), 1);
|
|
||||||
} else {
|
} else {
|
||||||
final boolean swapWeakSpace = tryStripSpaceAndReturnWhetherShouldSwapInstead(event,
|
final boolean swapWeakSpace = tryStripSpaceAndReturnWhetherShouldSwapInstead(event,
|
||||||
inputTransaction);
|
inputTransaction);
|
||||||
|
@ -1072,7 +1071,7 @@ public final class InputLogic {
|
||||||
mWordComposer.applyProcessedEvent(event);
|
mWordComposer.applyProcessedEvent(event);
|
||||||
}
|
}
|
||||||
if (mWordComposer.isComposingWord()) {
|
if (mWordComposer.isComposingWord()) {
|
||||||
mConnection.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
|
setComposingTextInternal(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
|
||||||
} else {
|
} else {
|
||||||
mConnection.commitText("", 1);
|
mConnection.commitText("", 1);
|
||||||
}
|
}
|
||||||
|
@ -1640,7 +1639,7 @@ public final class InputLogic {
|
||||||
final int[] codePoints = StringUtils.toCodePointArray(stringToCommit);
|
final int[] codePoints = StringUtils.toCodePointArray(stringToCommit);
|
||||||
mWordComposer.setComposingWord(codePoints,
|
mWordComposer.setComposingWord(codePoints,
|
||||||
mLatinIME.getCoordinatesForCurrentKeyboard(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.
|
// Don't restart suggestion yet. We'll restart if the user deletes the separator.
|
||||||
mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
|
mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
|
||||||
|
@ -1973,10 +1972,10 @@ public final class InputLogic {
|
||||||
}
|
}
|
||||||
final String lastWord = batchInputText.substring(indexOfLastSpace);
|
final String lastWord = batchInputText.substring(indexOfLastSpace);
|
||||||
mWordComposer.setBatchInputWord(lastWord);
|
mWordComposer.setBatchInputWord(lastWord);
|
||||||
mConnection.setComposingText(lastWord, 1);
|
setComposingTextInternal(lastWord, 1);
|
||||||
} else {
|
} else {
|
||||||
mWordComposer.setBatchInputWord(batchInputText);
|
mWordComposer.setBatchInputWord(batchInputText);
|
||||||
mConnection.setComposingText(batchInputText, 1);
|
setComposingTextInternal(batchInputText, 1);
|
||||||
}
|
}
|
||||||
mConnection.endBatchEdit();
|
mConnection.endBatchEdit();
|
||||||
// Space state must be updated before calling updateShiftState
|
// Space state must be updated before calling updateShiftState
|
||||||
|
@ -2175,6 +2174,24 @@ public final class InputLogic {
|
||||||
inputStyle, sequenceNumber, callback);
|
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
|
// Following methods are tentatively placed in this class for the integration with
|
||||||
// TextDecorator.
|
// TextDecorator.
|
||||||
|
|
Loading…
Reference in New Issue