diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 5aae010ac..64e404601 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -926,8 +926,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // mLastSelection{Start,End} are reset later in this method, no need to do it here needToCallLoadKeyboardLater = true; } else { - // When rotating, initialSelStart and initialSelEnd sometimes are lying. Make a best - // effort to work around this bug. + // When rotating, and when input is starting again in a field from where the focus + // 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(); mHandler.postResumeSuggestions(true /* shouldIncludeResumedWordInSuggestions */, true /* shouldDelay */); diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java index 750706113..a7ea2a1c8 100644 --- a/java/src/com/android/inputmethod/latin/RichInputConnection.java +++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java @@ -849,8 +849,9 @@ public final class RichInputConnection { /** * Try to get the text from the editor to expose lies the framework may have been - * telling us. Concretely, when the device rotates, the frameworks tells us about where the - * cursor used to be initially in the editor at the time it first received the focus; this + * telling us. Concretely, when the device rotates and when the keyboard reopens in the same + * text field after having been closed with the back key, the frameworks tells us about where + * the cursor used to be initially in the editor at the time it first received the focus; this * may be completely different from the place it is upon rotation. Since we don't have any * 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 @@ -859,7 +860,20 @@ public final class RichInputConnection { public void tryFixLyingCursorPosition() { final CharSequence textBeforeCursor = getTextBeforeCursor( Constants.EDITOR_CONTENTS_CACHE_SIZE, 0); - if (null == textBeforeCursor) { + final CharSequence selectedText = mIC.getSelectedText(0 /* flags */); + if (null == textBeforeCursor || + (!TextUtils.isEmpty(selectedText) && mExpectedSelEnd == mExpectedSelStart)) { + // If textBeforeCursor is null, we have no idea what kind of text field we have or if + // thinking about the "cursor position" actually makes any sense. In this case we + // remember a meaningless cursor position. Contrast this with an empty string, which is + // valid and should mean the cursor is at the start of the text. + // Also, if we expect we don't have a selection but we DO have non-empty selected text, + // then the framework lied to us about the cursor position. In this case, we should just + // revert to the most basic behavior possible for the next action (backspace in + // particular comes to mind), so we remember a meaningless cursor position which should + // result in degraded behavior from the next input. + // Interestingly, in either case, chances are any action the user takes next will result + // in a call to onUpdateSelection, which should set things right. mExpectedSelStart = mExpectedSelEnd = Constants.NOT_A_CURSOR_POSITION; } else { final int textLength = textBeforeCursor.length(); diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java index 48c8afa2a..834b89e46 100644 --- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java +++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java @@ -1127,19 +1127,21 @@ public final class InputLogic { StatsUtils.onBackspaceSelectedText(numCharsDeleted); } else { // There is no selection, just delete one character. - if (Constants.NOT_A_CURSOR_POSITION == mConnection.getExpectedSelectionEnd()) { - // This should never happen. - Log.e(TAG, "Backspace when we don't know the selection position"); - } - if (inputTransaction.mSettingsValues.isBeforeJellyBean() || - inputTransaction.mSettingsValues.mInputAttributes.isTypeNull()) { - // There are two possible reasons to send a key event: either the field has + if (inputTransaction.mSettingsValues.isBeforeJellyBean() + || inputTransaction.mSettingsValues.mInputAttributes.isTypeNull() + || Constants.NOT_A_CURSOR_POSITION + == mConnection.getExpectedSelectionEnd()) { + // There are three possible reasons to send a key event: either the field has // type TYPE_NULL, in which case the keyboard should send events, or we are - // running in backward compatibility mode. Before Jelly bean, the keyboard - // would simulate a hardware keyboard event on pressing enter or delete. This - // is bad for many reasons (there are race conditions with commits) but some - // applications are relying on this behavior so we continue to support it for - // older apps, so we retain this behavior if the app has target SDK < JellyBean. + // running in backward compatibility mode, or we don't know the cursor position. + // Before Jelly bean, the keyboard would simulate a hardware keyboard event on + // pressing enter or delete. This is bad for many reasons (there are race + // conditions with commits) but some applications are relying on this behavior + // so we continue to support it for older apps, so we retain this behavior if + // the app has target SDK < JellyBean. + // As for the case where we don't know the cursor position, it can happen + // because of bugs in the framework. But the framework should know, so the next + // best thing is to leave it to whatever it thinks is best. sendDownUpKeyEvent(KeyEvent.KEYCODE_DEL); int totalDeletedLength = 1; if (mDeleteCount > Constants.DELETE_ACCELERATE_AT) {