reload the cursor position from text field when in doubt

This commit is contained in:
Helium314 2024-06-18 00:38:07 +02:00
parent 9efb22bd0c
commit 0b9fb7334d
3 changed files with 29 additions and 12 deletions

View file

@ -997,7 +997,7 @@ public class LatinIME extends InputMethodService implements
// didn't move (the keyboard having been closed with the back key), // didn't move (the keyboard having been closed with the back key),
// initialSelStart and initialSelEnd sometimes are lying. Make a best effort to // initialSelStart and initialSelEnd sometimes are lying. Make a best effort to
// work around this bug. // work around this bug.
mInputLogic.mConnection.tryFixLyingCursorPosition(); mInputLogic.mConnection.tryFixIncorrectCursorPosition();
if (mInputLogic.mConnection.isCursorTouchingWord(currentSettingsValues.mSpacingAndPunctuations, true)) { if (mInputLogic.mConnection.isCursorTouchingWord(currentSettingsValues.mSpacingAndPunctuations, true)) {
mHandler.postResumeSuggestions(true /* shouldDelay */); mHandler.postResumeSuggestions(true /* shouldDelay */);
} }

View file

@ -227,14 +227,21 @@ public final class RichInputConnection implements PrivateCommandPerformer {
*/ */
public boolean resetCachesUponCursorMoveAndReturnSuccess(final int newSelStart, public boolean resetCachesUponCursorMoveAndReturnSuccess(final int newSelStart,
final int newSelEnd, final boolean shouldFinishComposition) { final int newSelEnd, final boolean shouldFinishComposition) {
mExpectedSelStart = newSelStart;
mExpectedSelEnd = newSelEnd;
mComposingText.setLength(0); mComposingText.setLength(0);
final boolean didReloadTextSuccessfully = reloadTextCache(); final boolean didReloadTextSuccessfully = reloadTextCache();
if (!didReloadTextSuccessfully) { if (!didReloadTextSuccessfully) {
Log.d(TAG, "Will try to retrieve text later."); Log.d(TAG, "Will try to retrieve text later.");
// selection is set to INVALID_CURSOR_POSITION if reloadTextCache return false
return false; return false;
} }
if (mExpectedSelStart != newSelStart || mExpectedSelEnd != newSelEnd) {
mExpectedSelStart = newSelStart;
mExpectedSelEnd = newSelEnd;
reloadTextCache();
if (mExpectedSelStart != newSelStart || mExpectedSelEnd != newSelEnd) {
Log.i(TAG, "resetCachesUponCursorMove: tried to set "+newSelStart+"/"+newSelEnd+", but input field has "+mExpectedSelStart+"/"+mExpectedSelEnd);
}
}
if (isConnected() && shouldFinishComposition) { if (isConnected() && shouldFinishComposition) {
mIC.finishComposingText(); mIC.finishComposingText();
} }
@ -272,6 +279,14 @@ public final class RichInputConnection implements PrivateCommandPerformer {
return true; return true;
} }
private void reloadCursorPosition() {
if (!isConnected()) return;
final ExtractedText et = mIC.getExtractedText(new ExtractedTextRequest(), 0);
if (et == null) return;
mExpectedSelStart = et.selectionStart + et.startOffset;
mExpectedSelEnd = et.selectionEnd + et.startOffset;
}
private void checkBatchEdit() { private void checkBatchEdit() {
if (mNestLevel != 1) { if (mNestLevel != 1) {
// TODO: exception instead // TODO: exception instead
@ -458,10 +473,7 @@ public final class RichInputConnection implements PrivateCommandPerformer {
if (result != null) { if (result != null) {
if (!checkTextBeforeCursorConsistency(result)) { if (!checkTextBeforeCursorConsistency(result)) {
Log.w(TAG, "cached text out of sync, reloading"); Log.w(TAG, "cached text out of sync, reloading");
ExtractedTextRequest r = new ExtractedTextRequest(); reloadCursorPosition();
final ExtractedText et = mIC.getExtractedText(r, 0);
mExpectedSelStart = et.selectionStart + et.startOffset;
mExpectedSelEnd = et.selectionEnd + et.startOffset;
if (!DebugLogUtils.getStackTrace(2).contains("reloadTextCache")) // clunky bur effective protection against circular reference if (!DebugLogUtils.getStackTrace(2).contains("reloadTextCache")) // clunky bur effective protection against circular reference
reloadTextCache(); reloadTextCache();
} }
@ -1130,8 +1142,10 @@ public final class RichInputConnection implements PrivateCommandPerformer {
* means to get the real value, try at least to ask the text view for some characters and * means to get the real value, try at least to ask the text view for some characters and
* detect the most damaging cases: when the cursor position is declared to be much smaller * detect the most damaging cases: when the cursor position is declared to be much smaller
* than it really is. * than it really is.
* (renamed the method, because we clearly ask the editorInfo to provide initial selection, no reason to complain about it
* being initial and thus possibly outdated)
*/ */
public void tryFixLyingCursorPosition() { public void tryFixIncorrectCursorPosition() {
mIC = mParent.getCurrentInputConnection(); mIC = mParent.getCurrentInputConnection();
final CharSequence textBeforeCursor = getTextBeforeCursor( final CharSequence textBeforeCursor = getTextBeforeCursor(
Constants.EDITOR_CONTENTS_CACHE_SIZE, 0); Constants.EDITOR_CONTENTS_CACHE_SIZE, 0);
@ -1167,6 +1181,9 @@ public final class RichInputConnection implements PrivateCommandPerformer {
if (wasEqual || mExpectedSelStart > mExpectedSelEnd) { if (wasEqual || mExpectedSelStart > mExpectedSelEnd) {
mExpectedSelEnd = mExpectedSelStart; mExpectedSelEnd = mExpectedSelStart;
} }
} else {
// better re-read the correct position instead of guessing from incomplete data
reloadCursorPosition();
} }
} }
} }

View file

@ -148,9 +148,9 @@ public final class InputLogic {
mRecapitalizeStatus.disable(); // Do not perform recapitalize until the cursor is moved once mRecapitalizeStatus.disable(); // Do not perform recapitalize until the cursor is moved once
mCurrentlyPressedHardwareKeys.clear(); mCurrentlyPressedHardwareKeys.clear();
mSuggestedWords = SuggestedWords.getEmptyInstance(); mSuggestedWords = SuggestedWords.getEmptyInstance();
// In some cases (namely, after rotation of the device) editorInfo.initialSelStart is lying // In some cases (e.g. after rotation of the device, or when scrolling the text before bringing up keyboard)
// so we try using some heuristics to find out about these and fix them. // editorInfo.initialSelStart is not the actual cursor position, so we try using some heuristics to find the correct position.
mConnection.tryFixLyingCursorPosition(); mConnection.tryFixIncorrectCursorPosition();
cancelDoubleSpacePeriodCountdown(); cancelDoubleSpacePeriodCountdown();
if (InputLogicHandler.NULL_HANDLER == mInputLogicHandler) { if (InputLogicHandler.NULL_HANDLER == mInputLogicHandler) {
mInputLogicHandler = new InputLogicHandler(mLatinIME, this); mInputLogicHandler = new InputLogicHandler(mLatinIME, this);
@ -2380,7 +2380,7 @@ public final class InputLogic {
// If remainingTries is 0, we should stop waiting for new tries, however we'll still // If remainingTries is 0, we should stop waiting for new tries, however we'll still
// return true as we need to perform other tasks (for example, loading the keyboard). // return true as we need to perform other tasks (for example, loading the keyboard).
} }
mConnection.tryFixLyingCursorPosition(); mConnection.tryFixIncorrectCursorPosition();
if (tryResumeSuggestions) { if (tryResumeSuggestions) {
handler.postResumeSuggestions(true /* shouldDelay */); handler.postResumeSuggestions(true /* shouldDelay */);
} }