diff --git a/app/src/main/java/helium314/keyboard/latin/LatinIME.java b/app/src/main/java/helium314/keyboard/latin/LatinIME.java index 589fabc67..5eb7665a6 100644 --- a/app/src/main/java/helium314/keyboard/latin/LatinIME.java +++ b/app/src/main/java/helium314/keyboard/latin/LatinIME.java @@ -997,7 +997,7 @@ public class LatinIME extends InputMethodService implements // didn't move (the keyboard having been closed with the back key), // initialSelStart and initialSelEnd sometimes are lying. Make a best effort to // work around this bug. - mInputLogic.mConnection.tryFixLyingCursorPosition(); + mInputLogic.mConnection.tryFixIncorrectCursorPosition(); if (mInputLogic.mConnection.isCursorTouchingWord(currentSettingsValues.mSpacingAndPunctuations, true)) { mHandler.postResumeSuggestions(true /* shouldDelay */); } diff --git a/app/src/main/java/helium314/keyboard/latin/RichInputConnection.java b/app/src/main/java/helium314/keyboard/latin/RichInputConnection.java index 87e699fe6..1bbd40a52 100644 --- a/app/src/main/java/helium314/keyboard/latin/RichInputConnection.java +++ b/app/src/main/java/helium314/keyboard/latin/RichInputConnection.java @@ -227,14 +227,21 @@ public final class RichInputConnection implements PrivateCommandPerformer { */ public boolean resetCachesUponCursorMoveAndReturnSuccess(final int newSelStart, final int newSelEnd, final boolean shouldFinishComposition) { - mExpectedSelStart = newSelStart; - mExpectedSelEnd = newSelEnd; mComposingText.setLength(0); final boolean didReloadTextSuccessfully = reloadTextCache(); if (!didReloadTextSuccessfully) { Log.d(TAG, "Will try to retrieve text later."); + // selection is set to INVALID_CURSOR_POSITION if reloadTextCache 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) { mIC.finishComposingText(); } @@ -272,6 +279,14 @@ public final class RichInputConnection implements PrivateCommandPerformer { 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() { if (mNestLevel != 1) { // TODO: exception instead @@ -458,10 +473,7 @@ public final class RichInputConnection implements PrivateCommandPerformer { if (result != null) { if (!checkTextBeforeCursorConsistency(result)) { Log.w(TAG, "cached text out of sync, reloading"); - ExtractedTextRequest r = new ExtractedTextRequest(); - final ExtractedText et = mIC.getExtractedText(r, 0); - mExpectedSelStart = et.selectionStart + et.startOffset; - mExpectedSelEnd = et.selectionEnd + et.startOffset; + reloadCursorPosition(); if (!DebugLogUtils.getStackTrace(2).contains("reloadTextCache")) // clunky bur effective protection against circular reference 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 * detect the most damaging cases: when the cursor position is declared to be much smaller * 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(); final CharSequence textBeforeCursor = getTextBeforeCursor( Constants.EDITOR_CONTENTS_CACHE_SIZE, 0); @@ -1167,6 +1181,9 @@ public final class RichInputConnection implements PrivateCommandPerformer { if (wasEqual || mExpectedSelStart > mExpectedSelEnd) { mExpectedSelEnd = mExpectedSelStart; } + } else { + // better re-read the correct position instead of guessing from incomplete data + reloadCursorPosition(); } } } diff --git a/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java b/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java index 2c7b2b248..272424b66 100644 --- a/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java +++ b/app/src/main/java/helium314/keyboard/latin/inputlogic/InputLogic.java @@ -148,9 +148,9 @@ public final class InputLogic { mRecapitalizeStatus.disable(); // Do not perform recapitalize until the cursor is moved once mCurrentlyPressedHardwareKeys.clear(); 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(); + // In some cases (e.g. after rotation of the device, or when scrolling the text before bringing up keyboard) + // editorInfo.initialSelStart is not the actual cursor position, so we try using some heuristics to find the correct position. + mConnection.tryFixIncorrectCursorPosition(); cancelDoubleSpacePeriodCountdown(); if (InputLogicHandler.NULL_HANDLER == mInputLogicHandler) { 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 // return true as we need to perform other tasks (for example, loading the keyboard). } - mConnection.tryFixLyingCursorPosition(); + mConnection.tryFixIncorrectCursorPosition(); if (tryResumeSuggestions) { handler.postResumeSuggestions(true /* shouldDelay */); }