Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 55042ff9 authored by Jean Chalard's avatar Jean Chalard Committed by Android (Google) Code Review
Browse files

Merge "Resume suggestion when backspacing to the end of a word"

parents b230806f 6b1f500d
Loading
Loading
Loading
Loading
+58 −2
Original line number Diff line number Diff line
@@ -950,6 +950,10 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
        }
        mExpectingUpdateSelection = false;
        mHandler.postUpdateShiftKeyState();
        // TODO: Decide to call restartSuggestionsOnWordBeforeCursorIfAtEndOfWord() or not
        // here. It would probably be too expensive to call directly here but we may want to post a
        // message to delay it. The point would be to unify behavior between backspace to the
        // end of a word and manually put the pointer at the end of the word.

        // Make a note of the cursor position
        mLastSelectionStart = newSelStart;
@@ -1468,10 +1472,11 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
                // inconsistent with backspacing after selecting other suggestions.
                revertLastWord(ic);
            } else {
                sendDownUpKeyEvents(KeyEvent.KEYCODE_DEL);
                ic.deleteSurroundingText(1, 0);
                if (mDeleteCount > DELETE_ACCELERATE_AT) {
                    sendDownUpKeyEvents(KeyEvent.KEYCODE_DEL);
                    ic.deleteSurroundingText(1, 0);
                }
                restartSuggestionsOnWordBeforeCursorIfAtEndOfWord(ic);
            }
        }
        ic.endBatchEdit();
@@ -2120,6 +2125,53 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
        return TextUtils.equals(text, beforeText);
    }

    // "ic" must not be null
    /**
     * Check if the cursor is actually at the end of a word. If so, restart suggestions on this
     * word, else do nothing.
     */
    private void restartSuggestionsOnWordBeforeCursorIfAtEndOfWord(
            final InputConnection ic) {
        // Bail out if the cursor is not at the end of a word (cursor must be preceded by
        // non-whitespace, non-separator, non-start-of-text)
        // Example ("|" is the cursor here) : <SOL>"|a" " |a" " | " all get rejected here.
        final CharSequence textBeforeCursor = ic.getTextBeforeCursor(1, 0);
        if (TextUtils.isEmpty(textBeforeCursor)
                || mSettingsValues.isWordSeparator(textBeforeCursor.charAt(0))) return;

        // Bail out if the cursor is in the middle of a word (cursor must be followed by whitespace,
        // separator or end of line/text)
        // Example: "test|"<EOL> "te|st" get rejected here
        final CharSequence textAfterCursor = ic.getTextAfterCursor(1, 0);
        if (!TextUtils.isEmpty(textAfterCursor)
                && !mSettingsValues.isWordSeparator(textAfterCursor.charAt(0))) return;

        // Bail out if word before cursor is 0-length or a single non letter (like an apostrophe)
        // Example: " '|" gets rejected here but "I'|" and "I|" are okay
        final CharSequence word = EditingUtils.getWordAtCursor(ic, mSettingsValues.mWordSeparators);
        if (TextUtils.isEmpty(word)) return;
        if (word.length() == 1 && !Character.isLetter(word.charAt(0))) return;

        // Okay, we are at the end of a word. Restart suggestions.
        restartSuggestionsOnWordBeforeCursor(ic, word);
    }

    // "ic" must not be null
    private void restartSuggestionsOnWordBeforeCursor(final InputConnection ic,
            final CharSequence word) {
        mWordComposer.setComposingWord(word, mKeyboardSwitcher.getLatinKeyboard());
        mComposingStringBuilder.setLength(0);
        mComposingStringBuilder.append(word);
        // mBestWord will be set appropriately by updateSuggestions() called by the handler
        mBestWord = null;
        mHasUncommittedTypedChars = true;
        mComposingStateManager.onStartComposingText();
        TextEntryState.restartSuggestionsOnWordBeforeCursor();
        ic.deleteSurroundingText(word.length(), 0);
        ic.setComposingText(word, 1);
        mHandler.postUpdateSuggestions();
    }

    // "ic" must not be null
    private void revertLastWord(final InputConnection ic) {
        if (mHasUncommittedTypedChars || mComposingStringBuilder.length() <= 0) {
@@ -2146,6 +2198,10 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
            // Clear composing text
            mComposingStringBuilder.setLength(0);
        } else {
            // Note: this relies on the last word still being held in the WordComposer
            // Note: in the interest of code simplicity, we may want to just call
            // restartSuggestionsOnWordBeforeCursorIfAtEndOfWord instead, but retrieving
            // the old WordComposer allows to reuse the actual typed coordinates.
            mHasUncommittedTypedChars = true;
            ic.setComposingText(mComposingStringBuilder, 1);
            TextEntryState.backspace();
+15 −0
Original line number Diff line number Diff line
@@ -146,9 +146,24 @@ public class TextEntryState {
        } else if (sState == UNDO_COMMIT) {
            setState(IN_WORD);
        }
        // TODO: tidy up this logic. At the moment, for example, writing a word goes to
        // ACCEPTED_DEFAULT, backspace will go to UNDO_COMMIT, another backspace will go to IN_WORD,
        // and subsequent backspaces will leave the status at IN_WORD, even if the user backspaces
        // past the end of the word. We are not in a word any more but the state is still IN_WORD.
        if (DEBUG) displayState("backspace");
    }

    public static void restartSuggestionsOnWordBeforeCursor() {
        if (UNKNOWN == sState || ACCEPTED_DEFAULT == sState) {
            // Here we can come from pretty much any state, except the ones that we can't
            // come from after backspace, so supposedly anything except UNKNOWN and
            // ACCEPTED_DEFAULT. Note : we could be in UNDO_COMMIT if
            // LatinIME#revertLastWord() was calling LatinIME#restartSuggestions...()
            Log.e(TAG, "Strange state change : coming from state " + sState);
        }
        setState(IN_WORD);
    }

    public static void reset() {
        setState(START);
        if (DEBUG) displayState("reset");
+46 −0
Original line number Diff line number Diff line
@@ -17,7 +17,9 @@
package com.android.inputmethod.latin;

import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.Key;
import com.android.inputmethod.keyboard.KeyDetector;
import com.android.inputmethod.keyboard.LatinKeyboard;

import java.util.ArrayList;
import java.util.Arrays;
@@ -140,6 +142,50 @@ public class WordComposer {
        }
    }

    /**
     * Internal method to retrieve reasonable proximity info for a character.
     */
    private void addKeyInfo(final int codePoint, final LatinKeyboard keyboard,
            final KeyDetector keyDetector) {
        for (final Key key : keyboard.mKeys) {
            if (key.mCode == codePoint) {
                final int x = key.mX + key.mWidth / 2;
                final int y = key.mY + key.mHeight / 2;
                final int[] codes = keyDetector.newCodeArray();
                keyDetector.getKeyIndexAndNearbyCodes(x, y, codes);
                add(codePoint, codes, x, y);
                return;
            }
        }
        add(codePoint, new int[] { codePoint },
                WordComposer.NOT_A_COORDINATE, WordComposer.NOT_A_COORDINATE);
    }

    /**
     * Set the currently composing word to the one passed as an argument.
     * This will register NOT_A_COORDINATE for X and Ys, and use the passed keyboard for proximity.
     */
    public void setComposingWord(final CharSequence word, final LatinKeyboard keyboard,
            final KeyDetector keyDetector) {
        reset();
        final int length = word.length();
        for (int i = 0; i < length; ++i) {
            int codePoint = word.charAt(i);
            addKeyInfo(codePoint, keyboard, keyDetector);
        }
    }

    /**
     * Shortcut for the above method, this will create a new KeyDetector for the passed keyboard.
     */
    public void setComposingWord(final CharSequence word, final LatinKeyboard keyboard) {
        final KeyDetector keyDetector = new KeyDetector(0);
        keyDetector.setKeyboard(keyboard, 0, 0);
        keyDetector.setProximityCorrectionEnabled(true);
        keyDetector.setProximityThreshold(keyboard.mMostCommonKeyWidth);
        setComposingWord(word, keyboard, keyDetector);
    }

    /**
     * Swaps the first and second values in the codes array if the primary code is not the first
     * value in the array but the second. This happens when the preferred key is not the key that
+1 −18
Original line number Diff line number Diff line
@@ -65,26 +65,9 @@ public class SuggestHelper {
        return mSuggest.hasMainDictionary();
    }

    private void addKeyInfo(WordComposer word, char c) {
        for (final Key key : mKeyboard.mKeys) {
            if (key.mCode == c) {
                final int x = key.mX + key.mWidth / 2;
                final int y = key.mY + key.mHeight / 2;
                final int[] codes = mKeyDetector.newCodeArray();
                mKeyDetector.getKeyIndexAndNearbyCodes(x, y, codes);
                word.add(c, codes, x, y);
                return;
            }
        }
        word.add(c, new int[] { c }, WordComposer.NOT_A_COORDINATE, WordComposer.NOT_A_COORDINATE);
    }

    protected WordComposer createWordComposer(CharSequence s) {
        WordComposer word = new WordComposer();
        for (int i = 0; i < s.length(); i++) {
            final char c = s.charAt(i);
            addKeyInfo(word, c);
        }
        word.setComposingWord(s, mKeyboard, mKeyDetector);
        return word;
    }