diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 78c65e0c7..d8b1c292b 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -700,6 +700,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } + mConnection.resetCachesUponCursorMove(mLastSelectionStart); + if (isDifferentTextField) { mainKeyboardView.closing(); loadSettings(); @@ -733,8 +735,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mainKeyboardView.setGesturePreviewMode(mCurrentSettings.mGesturePreviewTrailEnabled, mCurrentSettings.mGestureFloatingPreviewTextEnabled); - mConnection.resetCachesUponCursorMove(mLastSelectionStart); - if (TRACE) Debug.startMethodTracing("/data/trace/latinime"); } diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java index 37e1dbb69..efda623e5 100644 --- a/java/src/com/android/inputmethod/latin/RichInputConnection.java +++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java @@ -192,7 +192,20 @@ public class RichInputConnection { public int getCursorCapsMode(final int inputType) { mIC = mParent.getCurrentInputConnection(); if (null == mIC) return Constants.TextUtils.CAP_MODE_OFF; - return mIC.getCursorCapsMode(inputType); + if (!TextUtils.isEmpty(mComposingText)) return Constants.TextUtils.CAP_MODE_OFF; + // TODO: this will generally work, but there may be cases where the buffer contains SOME + // information but not enough to determine the caps mode accurately. This may happen after + // 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 != mCurrentCursorPosition) { + mCommittedTextBeforeComposingText.append( + getTextBeforeCursor(DEFAULT_TEXT_CACHE_SIZE, 0)); + } + // This never calls InputConnection#getCapsMode - in fact, it's a static method that + // never blocks or initiates IPC. + return StringUtils.getCapsMode(mCommittedTextBeforeComposingText, + mCommittedTextBeforeComposingText.length(), inputType); } public CharSequence getTextBeforeCursor(final int i, final int j) { diff --git a/java/src/com/android/inputmethod/latin/StringUtils.java b/java/src/com/android/inputmethod/latin/StringUtils.java index 9c47a38c2..d6509d6a6 100644 --- a/java/src/com/android/inputmethod/latin/StringUtils.java +++ b/java/src/com/android/inputmethod/latin/StringUtils.java @@ -197,4 +197,98 @@ public final class StringUtils { codePoints[dsti] = codePoint; return codePoints; } + + /** + * Determine what caps mode should be in effect at the current offset in + * the text. Only the mode bits set in reqModes will be + * checked. Note that the caps mode flags here are explicitly defined + * to match those in {@link InputType}. + * + * This code is a straight copy of TextUtils.getCapsMode (modulo namespace and formatting + * issues). This will change in the future as we simplify the code for our use and fix bugs. + * + * @param cs The text that should be checked for caps modes. + * @param off Location in the text at which to check. + * @param reqModes The modes to be checked: may be any combination of + * {@link #CAP_MODE_CHARACTERS}, {@link #CAP_MODE_WORDS}, and + * {@link #CAP_MODE_SENTENCES}. + * + * @return Returns the actual capitalization modes that can be in effect + * at the current position, which is any combination of + * {@link #CAP_MODE_CHARACTERS}, {@link #CAP_MODE_WORDS}, and + * {@link #CAP_MODE_SENTENCES}. + */ + public static int getCapsMode(CharSequence cs, int off, int reqModes) { + if (off < 0) { + return 0; + } + + int i; + char c; + int mode = 0; + + if ((reqModes & TextUtils.CAP_MODE_CHARACTERS) != 0) { + mode |= TextUtils.CAP_MODE_CHARACTERS; + } + if ((reqModes & (TextUtils.CAP_MODE_WORDS | TextUtils.CAP_MODE_SENTENCES)) == 0) { + return mode; + } + + // Back over allowed opening punctuation. + for (i = off; i > 0; i--) { + c = cs.charAt(i - 1); + if (c != '"' && c != '\'' && Character.getType(c) != Character.START_PUNCTUATION) { + break; + } + } + + // Start of paragraph, with optional whitespace. + int j = i; + while (j > 0 && ((c = cs.charAt(j - 1)) == ' ' || c == '\t')) { + j--; + } + if (j == 0 || cs.charAt(j - 1) == '\n') { + return mode | TextUtils.CAP_MODE_WORDS; + } + + // Or start of word if we are that style. + if ((reqModes & TextUtils.CAP_MODE_SENTENCES) == 0) { + if (i != j) mode |= TextUtils.CAP_MODE_WORDS; + return mode; + } + + // There must be a space if not the start of paragraph. + if (i == j) { + return mode; + } + + // Back over allowed closing punctuation. + for (; j > 0; j--) { + c = cs.charAt(j - 1); + if (c != '"' && c != '\'' && Character.getType(c) != Character.END_PUNCTUATION) { + break; + } + } + + if (j > 0) { + c = cs.charAt(j - 1); + if (c == '.' || c == '?' || c == '!') { + // Do not capitalize if the word ends with a period but + // also contains a period, in which case it is an abbreviation. + if (c == '.') { + for (int k = j - 2; k >= 0; k--) { + c = cs.charAt(k); + if (c == '.') { + return mode; + } + if (!Character.isLetter(c)) { + break; + } + } + } + return mode | TextUtils.CAP_MODE_SENTENCES; + } + } + return mode; + } }