aboutsummaryrefslogtreecommitdiffstats
path: root/java/src/com/android/inputmethod/latin/RichInputConnection.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/src/com/android/inputmethod/latin/RichInputConnection.java')
-rw-r--r--java/src/com/android/inputmethod/latin/RichInputConnection.java213
1 files changed, 76 insertions, 137 deletions
diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java
index 37311acf2..673d1b4c2 100644
--- a/java/src/com/android/inputmethod/latin/RichInputConnection.java
+++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java
@@ -57,19 +57,14 @@ public final class RichInputConnection {
private static final int INVALID_CURSOR_POSITION = -1;
/**
- * This variable contains an expected value for the selection start position. This is where the
- * cursor or selection start may end up after all the keyboard-triggered updates have passed. We
- * keep this to compare it to the actual selection start to guess whether the move was caused by
- * a keyboard command or not.
- * It's not really the selection start position: the selection start may not be there yet, and
- * in some cases, it may never arrive there.
+ * This variable contains an expected value for the cursor position. This is where the
+ * cursor may end up after all the keyboard-triggered updates have passed. We keep this to
+ * compare it to the actual cursor position to guess whether the move was caused by a
+ * keyboard command or not.
+ * It's not really the cursor position: the cursor may not be there yet, and it's also expected
+ * there be cases where it never actually comes to be there.
*/
- private int mExpectedSelStart = INVALID_CURSOR_POSITION; // in chars, not code points
- /**
- * The expected selection end. Only differs from mExpectedSelStart if a non-empty selection is
- * expected. The same caveats as mExpectedSelStart apply.
- */
- private int mExpectedSelEnd = INVALID_CURSOR_POSITION; // in chars, not code points
+ private int mExpectedCursorPosition = INVALID_CURSOR_POSITION; // in chars, not code points
/**
* This contains the committed text immediately preceding the cursor and the composing
* text if any. It is refreshed when the cursor moves by calling upon the TextView.
@@ -108,16 +103,16 @@ public final class RichInputConnection {
final String reference = (beforeCursor.length() <= actualLength) ? beforeCursor.toString()
: beforeCursor.subSequence(beforeCursor.length() - actualLength,
beforeCursor.length()).toString();
- if (et.selectionStart != mExpectedSelStart
+ if (et.selectionStart != mExpectedCursorPosition
|| !(reference.equals(internal.toString()))) {
- final String context = "Expected selection start = " + mExpectedSelStart
- + "\nActual selection start = " + et.selectionStart
+ final String context = "Expected cursor position = " + mExpectedCursorPosition
+ + "\nActual cursor position = " + et.selectionStart
+ "\nExpected text = " + internal.length() + " " + internal
+ "\nActual text = " + reference.length() + " " + reference;
((LatinIME)mParent).debugDumpStateAndCrashWithException(context);
} else {
Log.e(TAG, DebugLogUtils.getStackTrace(2));
- Log.e(TAG, "Exp <> Actual : " + mExpectedSelStart + " <> " + et.selectionStart);
+ Log.e(TAG, "Exp <> Actual : " + mExpectedCursorPosition + " <> " + et.selectionStart);
}
}
@@ -155,50 +150,16 @@ public final class RichInputConnection {
* data, so we empty the cache and note that we don't know the new cursor position, and we
* return false so that the caller knows about this and can retry later.
*
- * @param newSelStart the new position of the selection start, as received from the system.
- * @param newSelEnd the new position of the selection end, as received from the system.
- * @param shouldFinishComposition whether we should finish the composition in progress.
+ * @param newCursorPosition The new position of the cursor, as received from the system.
+ * @param shouldFinishComposition Whether we should finish the composition in progress.
* @return true if we were able to connect to the editor successfully, false otherwise. When
* this method returns false, the caches could not be correctly refreshed so they were only
* reset: the caller should try again later to return to normal operation.
*/
- public boolean resetCachesUponCursorMoveAndReturnSuccess(final int newSelStart,
- final int newSelEnd, final boolean shouldFinishComposition) {
- mExpectedSelStart = newSelStart;
- mExpectedSelEnd = newSelEnd;
+ public boolean resetCachesUponCursorMoveAndReturnSuccess(final int newCursorPosition,
+ final boolean shouldFinishComposition) {
+ mExpectedCursorPosition = newCursorPosition;
mComposingText.setLength(0);
- final boolean didReloadTextSuccessfully = reloadTextCache();
- if (!didReloadTextSuccessfully) {
- Log.d(TAG, "Will try to retrieve text later.");
- return false;
- }
- final int lengthOfTextBeforeCursor = mCommittedTextBeforeComposingText.length();
- if (lengthOfTextBeforeCursor > newSelStart
- || (lengthOfTextBeforeCursor < Constants.EDITOR_CONTENTS_CACHE_SIZE
- && newSelStart < Constants.EDITOR_CONTENTS_CACHE_SIZE)) {
- // newSelStart and newSelEnd may be lying -- when rotating the device (probably a
- // framework bug). If we have less chars than we asked for, then we know how many chars
- // we have, and if we got more than newSelStart says, then we know it was lying. In both
- // cases the length is more reliable. Note that we only have to check newSelStart (not
- // newSelEnd) since if newSelEnd is wrong, the newSelStart will be wrong as well.
- mExpectedSelStart = lengthOfTextBeforeCursor;
- mExpectedSelEnd = lengthOfTextBeforeCursor;
- }
- if (null != mIC && shouldFinishComposition) {
- mIC.finishComposingText();
- if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- ResearchLogger.richInputConnection_finishComposingText();
- }
- }
- return true;
- }
-
- /**
- * Reload the cached text from the InputConnection.
- *
- * @return true if successful
- */
- private boolean reloadTextCache() {
mCommittedTextBeforeComposingText.setLength(0);
mIC = mParent.getCurrentInputConnection();
// Call upon the inputconnection directly since our own method is using the cache, and
@@ -208,12 +169,27 @@ public final class RichInputConnection {
if (null == textBeforeCursor) {
// For some reason the app thinks we are not connected to it. This looks like a
// framework bug... Fall back to ground state and return false.
- mExpectedSelStart = INVALID_CURSOR_POSITION;
- mExpectedSelEnd = INVALID_CURSOR_POSITION;
- Log.e(TAG, "Unable to connect to the editor to retrieve text.");
+ mExpectedCursorPosition = INVALID_CURSOR_POSITION;
+ Log.e(TAG, "Unable to connect to the editor to retrieve text... will retry later");
return false;
}
mCommittedTextBeforeComposingText.append(textBeforeCursor);
+ final int lengthOfTextBeforeCursor = textBeforeCursor.length();
+ if (lengthOfTextBeforeCursor > newCursorPosition
+ || (lengthOfTextBeforeCursor < Constants.EDITOR_CONTENTS_CACHE_SIZE
+ && newCursorPosition < Constants.EDITOR_CONTENTS_CACHE_SIZE)) {
+ // newCursorPosition may be lying -- when rotating the device (probably a framework
+ // bug). If we have less chars than we asked for, then we know how many chars we have,
+ // and if we got more than newCursorPosition says, then we know it was lying. In both
+ // cases the length is more reliable
+ mExpectedCursorPosition = lengthOfTextBeforeCursor;
+ }
+ if (null != mIC && shouldFinishComposition) {
+ mIC.finishComposingText();
+ if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
+ ResearchLogger.richInputConnection_finishComposingText();
+ }
+ }
return true;
}
@@ -242,8 +218,7 @@ public final class RichInputConnection {
if (DEBUG_BATCH_NESTING) checkBatchEdit();
if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
mCommittedTextBeforeComposingText.append(text);
- mExpectedSelStart += text.length() - mComposingText.length();
- mExpectedSelEnd = mExpectedSelStart;
+ mExpectedCursorPosition += text.length() - mComposingText.length();
mComposingText.setLength(0);
if (null != mIC) {
mIC.commitText(text, i);
@@ -256,7 +231,7 @@ public final class RichInputConnection {
}
public boolean canDeleteCharacters() {
- return mExpectedSelStart > 0;
+ return mExpectedCursorPosition > 0;
}
/**
@@ -293,10 +268,11 @@ public final class RichInputConnection {
// heavy pressing of delete, for example DEFAULT_TEXT_CACHE_SIZE - 5 times or so.
// getCapsMode should be updated to be able to return a "not enough info" result so that
// we can get more context only when needed.
- if (TextUtils.isEmpty(mCommittedTextBeforeComposingText) && 0 != mExpectedSelStart) {
- if (!reloadTextCache()) {
- Log.w(TAG, "Unable to connect to the editor. "
- + "Setting caps mode without knowing text.");
+ if (TextUtils.isEmpty(mCommittedTextBeforeComposingText) && 0 != mExpectedCursorPosition) {
+ final CharSequence textBeforeCursor = getTextBeforeCursor(
+ Constants.EDITOR_CONTENTS_CACHE_SIZE, 0);
+ if (!TextUtils.isEmpty(textBeforeCursor)) {
+ mCommittedTextBeforeComposingText.append(textBeforeCursor);
}
}
// This never calls InputConnection#getCapsMode - in fact, it's a static method that
@@ -319,8 +295,8 @@ public final class RichInputConnection {
// However, if we don't have an expected cursor position, then we should always
// go fetch the cache again (as it happens, INVALID_CURSOR_POSITION < 0, so we need to
// test for this explicitly)
- if (INVALID_CURSOR_POSITION != mExpectedSelStart
- && (cachedLength >= n || cachedLength >= mExpectedSelStart)) {
+ if (INVALID_CURSOR_POSITION != mExpectedCursorPosition
+ && (cachedLength >= n || cachedLength >= mExpectedCursorPosition)) {
final StringBuilder s = new StringBuilder(mCommittedTextBeforeComposingText);
// We call #toString() here to create a temporary object.
// In some situations, this method is called on a worker thread, and it's possible
@@ -360,14 +336,10 @@ public final class RichInputConnection {
+ remainingChars, 0);
mCommittedTextBeforeComposingText.setLength(len);
}
- if (mExpectedSelStart > beforeLength) {
- mExpectedSelStart -= beforeLength;
- mExpectedSelEnd -= beforeLength;
+ if (mExpectedCursorPosition > beforeLength) {
+ mExpectedCursorPosition -= beforeLength;
} else {
- // There are fewer characters before the cursor in the buffer than we are being asked to
- // delete. Only delete what is there.
- mExpectedSelStart = 0;
- mExpectedSelEnd -= mExpectedSelStart;
+ mExpectedCursorPosition = 0;
}
if (null != mIC) {
mIC.deleteSurroundingText(beforeLength, afterLength);
@@ -401,8 +373,7 @@ public final class RichInputConnection {
switch (keyEvent.getKeyCode()) {
case KeyEvent.KEYCODE_ENTER:
mCommittedTextBeforeComposingText.append("\n");
- mExpectedSelStart += 1;
- mExpectedSelEnd = mExpectedSelStart;
+ mExpectedCursorPosition += 1;
break;
case KeyEvent.KEYCODE_DEL:
if (0 == mComposingText.length()) {
@@ -414,24 +385,18 @@ public final class RichInputConnection {
} else {
mComposingText.delete(mComposingText.length() - 1, mComposingText.length());
}
- if (mExpectedSelStart > 0 && mExpectedSelStart == mExpectedSelEnd) {
- // TODO: Handle surrogate pairs.
- mExpectedSelStart -= 1;
- }
- mExpectedSelEnd = mExpectedSelStart;
+ if (mExpectedCursorPosition > 0) mExpectedCursorPosition -= 1;
break;
case KeyEvent.KEYCODE_UNKNOWN:
if (null != keyEvent.getCharacters()) {
mCommittedTextBeforeComposingText.append(keyEvent.getCharacters());
- mExpectedSelStart += keyEvent.getCharacters().length();
- mExpectedSelEnd = mExpectedSelStart;
+ mExpectedCursorPosition += keyEvent.getCharacters().length();
}
break;
default:
- final String text = StringUtils.newSingleCodePointString(keyEvent.getUnicodeChar());
+ final String text = new String(new int[] { keyEvent.getUnicodeChar() }, 0, 1);
mCommittedTextBeforeComposingText.append(text);
- mExpectedSelStart += text.length();
- mExpectedSelEnd = mExpectedSelStart;
+ mExpectedCursorPosition += text.length();
break;
}
}
@@ -465,12 +430,10 @@ public final class RichInputConnection {
public void setComposingText(final CharSequence text, final int newCursorPosition) {
if (DEBUG_BATCH_NESTING) checkBatchEdit();
if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
- mExpectedSelStart += text.length() - mComposingText.length();
- mExpectedSelEnd = mExpectedSelStart;
+ mExpectedCursorPosition += text.length() - mComposingText.length();
mComposingText.setLength(0);
mComposingText.append(text);
- // TODO: support values of newCursorPosition != 1. At this time, this is never called with
- // newCursorPosition != 1.
+ // TODO: support values of i != 1. At this time, this is never called with i != 1.
if (null != mIC) {
mIC.setComposingText(text, newCursorPosition);
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
@@ -480,31 +443,19 @@ public final class RichInputConnection {
if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
}
- /**
- * Set the selection of the text editor.
- *
- * Calls through to {@link InputConnection#setSelection(int, int)}.
- *
- * @param start the character index where the selection should start.
- * @param end the character index where the selection should end.
- * @return Returns true on success, false if the input connection is no longer valid either when
- * setting the selection or when retrieving the text cache at that point.
- */
- public boolean setSelection(final int start, final int end) {
+ public void setSelection(final int start, final int end) {
if (DEBUG_BATCH_NESTING) checkBatchEdit();
if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
- mExpectedSelStart = start;
- mExpectedSelEnd = end;
if (null != mIC) {
- final boolean isIcValid = mIC.setSelection(start, end);
- if (!isIcValid) {
- return false;
- }
+ mIC.setSelection(start, end);
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
ResearchLogger.richInputConnection_setSelection(start, end);
}
}
- return reloadTextCache();
+ mExpectedCursorPosition = start;
+ mCommittedTextBeforeComposingText.setLength(0);
+ mCommittedTextBeforeComposingText.append(
+ getTextBeforeCursor(Constants.EDITOR_CONTENTS_CACHE_SIZE, 0));
}
public void commitCorrection(final CorrectionInfo correctionInfo) {
@@ -525,8 +476,7 @@ public final class RichInputConnection {
// text should never be null, but just in case, it's better to insert nothing than to crash
if (null == text) text = "";
mCommittedTextBeforeComposingText.append(text);
- mExpectedSelStart += text.length() - mComposingText.length();
- mExpectedSelEnd = mExpectedSelStart;
+ mExpectedCursorPosition += text.length() - mComposingText.length();
mComposingText.setLength(0);
if (null != mIC) {
mIC.commitCompletion(completionInfo);
@@ -538,7 +488,7 @@ public final class RichInputConnection {
}
@SuppressWarnings("unused")
- public String getNthPreviousWord(final SettingsValues currentSettingsValues, final int n) {
+ public String getNthPreviousWord(final String sentenceSeperators, final int n) {
mIC = mParent.getCurrentInputConnection();
if (null == mIC) return null;
final CharSequence prev = getTextBeforeCursor(LOOKBACK_CHARACTER_NUM, 0);
@@ -557,7 +507,7 @@ public final class RichInputConnection {
}
}
}
- return getNthPreviousWord(prev, currentSettingsValues, n);
+ return getNthPreviousWord(prev, sentenceSeperators, n);
}
private static boolean isSeparator(int code, String sep) {
@@ -581,7 +531,7 @@ public final class RichInputConnection {
// (n = 2) "abc |" -> null
// (n = 2) "abc. def|" -> null
public static String getNthPreviousWord(final CharSequence prev,
- final SettingsValues currentSettingsValues, final int n) {
+ final String sentenceSeperators, final int n) {
if (prev == null) return null;
final String[] w = spaceRegex.split(prev);
@@ -593,8 +543,7 @@ public final class RichInputConnection {
// If ends in a separator, return null
final char lastChar = nthPrevWord.charAt(length - 1);
- if (currentSettingsValues.isWordSeparator(lastChar)
- || currentSettingsValues.isWordConnector(lastChar)) return null;
+ if (sentenceSeperators.contains(String.valueOf(lastChar))) return null;
return nthPrevWord;
}
@@ -809,30 +758,20 @@ public final class RichInputConnection {
* this update and not the ones in-between. This is almost impossible to achieve even trying
* very very hard.
*
- * @param oldSelStart The value of the old selection in the update.
- * @param newSelStart The value of the new selection in the update.
- * @param oldSelEnd The value of the old selection end in the update.
- * @param newSelEnd The value of the new selection end in the update.
+ * @param oldSelStart The value of the old cursor position in the update.
+ * @param newSelStart The value of the new cursor position in the update.
* @return whether this is a belated expected update or not.
*/
- public boolean isBelatedExpectedUpdate(final int oldSelStart, final int newSelStart,
- final int oldSelEnd, final int newSelEnd) {
- // This update is "belated" if we are expecting it. That is, mExpectedSelStart and
- // mExpectedSelEnd match the new values that the TextView is updating TO.
- if (mExpectedSelStart == newSelStart && mExpectedSelEnd == newSelEnd) return true;
- // This update is not belated if mExpectedSelStart and mExpeectedSelend match the old
- // values, and one of newSelStart or newSelEnd is updated to a different value. In this
- // case, there is likely something other than the IME that has moved the selection endpoint
- // to the new value.
- if (mExpectedSelStart == oldSelStart && mExpectedSelEnd == oldSelEnd
- && (oldSelStart != newSelStart || oldSelEnd != newSelEnd)) return false;
- // If nether of the above two cases holds, then the system may be having trouble keeping up
- // with updates. If 1) the selection is a cursor, 2) newSelStart is between oldSelStart
- // and mExpectedSelStart, and 3) newSelEnd is between oldSelEnd and mExpectedSelEnd, then
- // assume a belated update.
- return (newSelStart == newSelEnd)
- && (newSelStart - oldSelStart) * (mExpectedSelStart - newSelStart) >= 0
- && (newSelEnd - oldSelEnd) * (mExpectedSelEnd - newSelEnd) >= 0;
+ public boolean isBelatedExpectedUpdate(final int oldSelStart, final int newSelStart) {
+ // If this is an update that arrives at our expected position, it's a belated update.
+ if (newSelStart == mExpectedCursorPosition) return true;
+ // If this is an update that moves the cursor from our expected position, it must be
+ // an explicit move.
+ if (oldSelStart == mExpectedCursorPosition) return false;
+ // The following returns true if newSelStart is between oldSelStart and
+ // mCurrentCursorPosition. We assume that if the updated position is between the old
+ // position and the expected position, then it must be a belated update.
+ return (newSelStart - oldSelStart) * (mExpectedCursorPosition - newSelStart) >= 0;
}
/**