aboutsummaryrefslogtreecommitdiffstats
path: root/java/src
diff options
context:
space:
mode:
Diffstat (limited to 'java/src')
-rw-r--r--java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java2
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java95
-rw-r--r--java/src/com/android/inputmethod/latin/RichInputConnection.java11
-rw-r--r--java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java97
-rw-r--r--java/src/com/android/inputmethod/latin/utils/StringUtils.java44
5 files changed, 161 insertions, 88 deletions
diff --git a/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java b/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java
index 10fb9fef4..216a825e0 100644
--- a/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java
+++ b/java/src/com/android/inputmethod/accessibility/AccessibilityUtils.java
@@ -158,7 +158,7 @@ public final class AccessibilityUtils {
* @param typedWord the currently typed word
*/
public void setAutoCorrection(final SuggestedWords suggestedWords, final String typedWord) {
- if (suggestedWords != null && suggestedWords.mWillAutoCorrect) {
+ if (suggestedWords.mWillAutoCorrect) {
mAutoCorrectionWord = suggestedWords.getWord(SuggestedWords.INDEX_OF_AUTO_CORRECTION);
mTypedWord = typedWord;
} else {
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 82d824b8f..ba7503dae 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -789,7 +789,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (mSuggestionStripView != null) {
// This will set the punctuation suggestions if next word suggestion is off;
// otherwise it will clear the suggestion strip.
- setPunctuationSuggestions();
+ setNeutralSuggestionStrip();
}
// Sometimes, while rotating, for some reason the framework tells the app we are not
@@ -912,63 +912,14 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
composingSpanEnd, mInputLogic.mConnection);
}
- final boolean selectionChanged = oldSelStart != newSelStart || oldSelEnd != newSelEnd;
-
- // if composingSpanStart and composingSpanEnd are -1, it means there is no composing
- // span in the view - we can use that to narrow down whether the cursor was moved
- // by us or not. If we are composing a word but there is no composing span, then
- // we know for sure the cursor moved while we were composing and we should reset
- // the state. TODO: rescind this policy: the framework never removes the composing
- // span on its own accord while editing. This test is useless.
- final boolean noComposingSpan = composingSpanStart == -1 && composingSpanEnd == -1;
-
// If the keyboard is not visible, we don't need to do all the housekeeping work, as it
// will be reset when the keyboard shows up anyway.
// TODO: revisit this when LatinIME supports hardware keyboards.
// NOTE: the test harness subclasses LatinIME and overrides isInputViewShown().
// TODO: find a better way to simulate actual execution.
- if (isInputViewShown() && !mInputLogic.mConnection.isBelatedExpectedUpdate(oldSelStart,
- newSelStart, oldSelEnd, newSelEnd)) {
- // TODO: the following is probably better done in resetEntireInputState().
- // it should only happen when the cursor moved, and the very purpose of the
- // test below is to narrow down whether this happened or not. Likewise with
- // the call to updateShiftState.
- // We set this to NONE because after a cursor move, we don't want the space
- // state-related special processing to kick in.
- mInputLogic.mSpaceState = SpaceState.NONE;
-
- // TODO: is it still necessary to test for composingSpan related stuff?
- final boolean selectionChangedOrSafeToReset = selectionChanged
- || (!mInputLogic.mWordComposer.isComposingWord()) || noComposingSpan;
- final boolean hasOrHadSelection = (oldSelStart != oldSelEnd
- || newSelStart != newSelEnd);
- final int moveAmount = newSelStart - oldSelStart;
- if (selectionChangedOrSafeToReset && (hasOrHadSelection
- || !mInputLogic.mWordComposer.moveCursorByAndReturnIfInsideComposingWord(
- moveAmount))) {
- // If we are composing a word and moving the cursor, we would want to set a
- // suggestion span for recorrection to work correctly. Unfortunately, that
- // would involve the keyboard committing some new text, which would move the
- // cursor back to where it was. Latin IME could then fix the position of the cursor
- // again, but the asynchronous nature of the calls results in this wreaking havoc
- // with selection on double tap and the like.
- // Another option would be to send suggestions each time we set the composing
- // text, but that is probably too expensive to do, so we decided to leave things
- // as is.
- mInputLogic.resetEntireInputState(mSettings.getCurrent(), newSelStart, newSelEnd);
- } else {
- // resetEntireInputState calls resetCachesUponCursorMove, but forcing the
- // composition to end. But in all cases where we don't reset the entire input
- // state, we still want to tell the rich input connection about the new cursor
- // position so that it can update its caches.
- mInputLogic.mConnection.resetCachesUponCursorMoveAndReturnSuccess(
- newSelStart, newSelEnd, false /* shouldFinishComposition */);
- }
-
- // We moved the cursor. If we are touching a word, we need to resume suggestion.
- mHandler.postResumeSuggestions();
- // Reset the last recapitalization.
- mInputLogic.mRecapitalizeStatus.deactivate();
+ if (isInputViewShown() &&
+ mInputLogic.onUpdateSelection(mSettings.getCurrent(), oldSelStart, oldSelEnd,
+ newSelStart, newSelEnd, composingSpanStart, composingSpanEnd)) {
mKeyboardSwitcher.updateShiftState();
}
@@ -1039,7 +990,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
if (!mSettings.getCurrent().isApplicationSpecifiedCompletionsOn()) return;
if (applicationSpecifiedCompletions == null) {
- clearSuggestionStrip();
+ setNeutralSuggestionStrip();
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
ResearchLogger.latinIME_onDisplayCompletions(null);
}
@@ -1054,7 +1005,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
final SuggestedWords suggestedWords = new SuggestedWords(
applicationSuggestedWords,
false /* typedWordValid */,
- false /* hasAutoCorrectionCandidate */,
+ false /* willAutoCorrect */,
false /* isPunctuationSuggestions */,
false /* isObsoleteSuggestions */,
false /* isPrediction */);
@@ -1183,7 +1134,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// the right layout.
// TODO[IL]: Remove this, pass the input logic to the keyboard switcher instead?
public int getCurrentAutoCapsState() {
- return mInputLogic.getCurrentAutoCapsState(Settings.getInstance().getCurrent());
+ return mInputLogic.getCurrentAutoCapsState(mSettings.getCurrent());
}
// Called from the KeyboardSwitcher which needs to know recaps state to display
@@ -1289,7 +1240,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mSubtypeSwitcher.switchToShortcutIME(this);
// Still call the *#onCodeInput methods for readability.
}
- mInputLogic.onCodeInput(codeToSend, keyX, keyY, Settings.getInstance().getCurrent(),
+ mInputLogic.onCodeInput(codeToSend, keyX, keyY, mSettings.getCurrent(),
mHandler, mKeyboardSwitcher);
mKeyboardSwitcher.onCodeInput(codePoint);
}
@@ -1379,12 +1330,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
// TODO[IL]: Define a clear interface for this
- public void clearSuggestionStrip() {
- setSuggestedWords(SuggestedWords.EMPTY);
- setAutoCorrectionIndicator(false);
- }
-
- // TODO[IL]: Define a clear interface for this
public void setSuggestedWords(final SuggestedWords words) {
mInputLogic.mSuggestedWords = words;
if (mSuggestionStripView != null) {
@@ -1479,12 +1424,16 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
}
- private void showSuggestionStripWithTypedWord(final SuggestedWords suggestedWords,
+ private void showSuggestionStripWithTypedWord(final SuggestedWords sourceSuggestedWords,
final String typedWord) {
+ // TODO: refactor this
+ final SuggestedWords suggestedWords =
+ sourceSuggestedWords.isEmpty() ? SuggestedWords.EMPTY : sourceSuggestedWords;
if (suggestedWords.isEmpty()) {
// No auto-correction is available, clear the cached values.
- AccessibilityUtils.getInstance().setAutoCorrection(null, null);
- clearSuggestionStrip();
+ AccessibilityUtils.getInstance().setAutoCorrection(suggestedWords, typedWord);
+ setSuggestedWords(suggestedWords);
+ setAutoCorrectionIndicator(false);
return;
}
final String autoCorrection;
@@ -1506,12 +1455,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// TODO[IL]: Define a clean interface for this
public void showSuggestionStrip(final SuggestedWords suggestedWords) {
- if (suggestedWords.isEmpty()) {
- clearSuggestionStrip();
- return;
- }
- showSuggestionStripWithTypedWord(suggestedWords,
- suggestedWords.getWord(SuggestedWords.INDEX_OF_TYPED_WORD));
+ showSuggestionStripWithTypedWord(suggestedWords, suggestedWords.isEmpty() ? null
+ : suggestedWords.getWord(SuggestedWords.INDEX_OF_TYPED_WORD));
}
// Called from {@link SuggestionStripView} through the {@link SuggestionStripView#Listener}
@@ -1610,10 +1555,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
// TODO[IL]: Define a clean interface for this
- public void setPunctuationSuggestions() {
+ // This will show either an empty suggestion strip (if prediction is enabled) or
+ // punctuation suggestions (if it's disabled).
+ public void setNeutralSuggestionStrip() {
final SettingsValues currentSettings = mSettings.getCurrent();
if (currentSettings.mBigramPredictionEnabled) {
- clearSuggestionStrip();
+ setSuggestedWords(SuggestedWords.EMPTY);
} else {
setSuggestedWords(currentSettings.mSpacingAndPunctuations.mSuggestPuncList);
}
diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java
index 325a0d981..0d0b7a160 100644
--- a/java/src/com/android/inputmethod/latin/RichInputConnection.java
+++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java
@@ -815,6 +815,17 @@ public final class RichInputConnection {
}
/**
+ * Looks at the text just before the cursor to find out if we are inside a double quote.
+ *
+ * As with #textBeforeCursorLooksLikeURL, this is dependent on how much text we have cached.
+ * However this won't be a concrete problem in most situations, as the cache is almost always
+ * long enough for this use.
+ */
+ public boolean isInsideDoubleQuoteOrAfterDigit() {
+ return StringUtils.isInsideDoubleQuoteOrAfterDigit(mCommittedTextBeforeComposingText);
+ }
+
+ /**
* 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
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
index 6e3543216..43d75330d 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
@@ -179,6 +179,67 @@ public final class InputLogic {
}
/**
+ * Consider an update to the cursor position. Evaluate whether this update has happened as
+ * part of normal typing or whether it was an explicit cursor move by the user. In any case,
+ * do the necessary adjustments.
+ * @param settingsValues the current settings
+ * @param oldSelStart old selection start
+ * @param oldSelEnd old selection end
+ * @param newSelStart new selection start
+ * @param newSelEnd new selection end
+ * @param composingSpanStart composing span start
+ * @param composingSpanEnd composing span end
+ * @return whether the cursor has moved as a result of user interaction.
+ */
+ public boolean onUpdateSelection(final SettingsValues settingsValues,
+ final int oldSelStart, final int oldSelEnd,
+ final int newSelStart, final int newSelEnd,
+ final int composingSpanStart, final int composingSpanEnd) {
+ if (mConnection.isBelatedExpectedUpdate(oldSelStart, newSelStart, oldSelEnd, newSelEnd)) {
+ return false;
+ }
+ // TODO: the following is probably better done in resetEntireInputState().
+ // it should only happen when the cursor moved, and the very purpose of the
+ // test below is to narrow down whether this happened or not. Likewise with
+ // the call to updateShiftState.
+ // We set this to NONE because after a cursor move, we don't want the space
+ // state-related special processing to kick in.
+ mSpaceState = SpaceState.NONE;
+
+ final boolean selectionChangedOrSafeToReset =
+ oldSelStart != newSelStart || oldSelEnd != newSelEnd // selection changed
+ || !mWordComposer.isComposingWord(); // safe to reset
+ final boolean hasOrHadSelection = (oldSelStart != oldSelEnd || newSelStart != newSelEnd);
+ final int moveAmount = newSelStart - oldSelStart;
+ if (selectionChangedOrSafeToReset && (hasOrHadSelection
+ || !mWordComposer.moveCursorByAndReturnIfInsideComposingWord(moveAmount))) {
+ // If we are composing a word and moving the cursor, we would want to set a
+ // suggestion span for recorrection to work correctly. Unfortunately, that
+ // would involve the keyboard committing some new text, which would move the
+ // cursor back to where it was. Latin IME could then fix the position of the cursor
+ // again, but the asynchronous nature of the calls results in this wreaking havoc
+ // with selection on double tap and the like.
+ // Another option would be to send suggestions each time we set the composing
+ // text, but that is probably too expensive to do, so we decided to leave things
+ // as is.
+ resetEntireInputState(settingsValues, newSelStart, newSelEnd);
+ } else {
+ // resetEntireInputState calls resetCachesUponCursorMove, but forcing the
+ // composition to end. But in all cases where we don't reset the entire input
+ // state, we still want to tell the rich input connection about the new cursor
+ // position so that it can update its caches.
+ mConnection.resetCachesUponCursorMoveAndReturnSuccess(
+ newSelStart, newSelEnd, false /* shouldFinishComposition */);
+ }
+
+ // We moved the cursor. If we are touching a word, we need to resume suggestion.
+ mLatinIME.mHandler.postResumeSuggestions();
+ // Reset the last recapitalization.
+ mRecapitalizeStatus.deactivate();
+ return true;
+ }
+
+ /**
* React to a code input. It may be a code point to insert, or a symbolic value that influences
* the keyboard behavior.
*
@@ -600,8 +661,21 @@ public final class InputLogic {
final boolean swapWeakSpace = maybeStripSpace(settingsValues, codePoint, spaceState,
isFromSuggestionStrip);
- if (SpaceState.PHANTOM == spaceState &&
- settingsValues.isUsuallyPrecededBySpace(codePoint)) {
+ final boolean isInsideDoubleQuoteOrAfterDigit = Constants.CODE_DOUBLE_QUOTE == codePoint
+ && mConnection.isInsideDoubleQuoteOrAfterDigit();
+
+ final boolean needsPrecedingSpace;
+ if (SpaceState.PHANTOM != spaceState) {
+ needsPrecedingSpace = false;
+ } else if (Constants.CODE_DOUBLE_QUOTE == codePoint) {
+ // Double quotes behave like they are usually preceded by space iff we are
+ // not inside a double quote or after a digit.
+ needsPrecedingSpace = !isInsideDoubleQuoteOrAfterDigit;
+ } else {
+ needsPrecedingSpace = settingsValues.isUsuallyPrecededBySpace(codePoint);
+ }
+
+ if (needsPrecedingSpace) {
promotePhantomSpace(settingsValues);
}
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
@@ -628,14 +702,17 @@ public final class InputLogic {
if (swapWeakSpace) {
swapSwapperAndSpace(keyboardSwitcher);
mSpaceState = SpaceState.SWAP_PUNCTUATION;
- } else if (SpaceState.PHANTOM == spaceState
- && settingsValues.isUsuallyFollowedBySpace(codePoint)) {
+ } else if ((SpaceState.PHANTOM == spaceState
+ && settingsValues.isUsuallyFollowedBySpace(codePoint))
+ || (Constants.CODE_DOUBLE_QUOTE == codePoint
+ && isInsideDoubleQuoteOrAfterDigit)) {
// If we are in phantom space state, and the user presses a separator, we want to
// stay in phantom space state so that the next keypress has a chance to add the
// space. For example, if I type "Good dat", pick "day" from the suggestion strip
// then insert a comma and go on to typing the next word, I want the space to be
// inserted automatically before the next word, the same way it is when I don't
- // input the comma.
+ // input the comma. A double quote behaves like it's usually followed by space if
+ // we're inside a double quote.
// The case is a little different if the separator is a space stripper. Such a
// separator does not normally need a space on the right (that's the difference
// between swappers and strippers), so we should not stay in phantom space state if
@@ -645,7 +722,7 @@ public final class InputLogic {
// Set punctuation right away. onUpdateSelection will fire but tests whether it is
// already displayed or not, so it's okay.
- mLatinIME.setPunctuationSuggestions();
+ mLatinIME.setNeutralSuggestionStrip();
}
keyboardSwitcher.updateShiftState();
@@ -998,7 +1075,7 @@ public final class InputLogic {
}
if (!mWordComposer.isComposingWord() && !settingsValues.mBigramPredictionEnabled) {
- mLatinIME.setPunctuationSuggestions();
+ mLatinIME.setNeutralSuggestionStrip();
return;
}
@@ -1383,11 +1460,7 @@ public final class InputLogic {
final int newSelStart, final int newSelEnd) {
final boolean shouldFinishComposition = mWordComposer.isComposingWord();
resetComposingState(true /* alsoResetLastComposedWord */);
- if (settingsValues.mBigramPredictionEnabled) {
- mLatinIME.clearSuggestionStrip();
- } else {
- mLatinIME.setSuggestedWords(settingsValues.mSpacingAndPunctuations.mSuggestPuncList);
- }
+ mLatinIME.setNeutralSuggestionStrip();
mConnection.resetCachesUponCursorMoveAndReturnSuccess(newSelStart, newSelEnd,
shouldFinishComposition);
}
diff --git a/java/src/com/android/inputmethod/latin/utils/StringUtils.java b/java/src/com/android/inputmethod/latin/utils/StringUtils.java
index 6f15b11bf..b154623ae 100644
--- a/java/src/com/android/inputmethod/latin/utils/StringUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/StringUtils.java
@@ -348,7 +348,7 @@ public final class StringUtils {
boolean hasPeriod = false;
int codePoint = 0;
while (i > 0) {
- codePoint = Character.codePointBefore(text, i);
+ codePoint = Character.codePointBefore(text, i);
if (codePoint < Constants.CODE_PERIOD || codePoint > 'z') {
// Handwavy heuristic to see if that's a URL character. Anything between period
// and z. This includes all lower- and upper-case ascii letters, period,
@@ -387,6 +387,48 @@ public final class StringUtils {
return false;
}
+ /**
+ * Examines the string and returns whether we're inside a double quote.
+ *
+ * This is used to decide whether we should put an automatic space before or after a double
+ * quote character. If we're inside a quotation, then we want to close it, so we want a space
+ * after and not before. Otherwise, we want to open the quotation, so we want a space before
+ * and not after. Exception: after a digit, we never want a space because the "inch" or
+ * "minutes" use cases is dominant after digits.
+ * In the practice, we determine whether we are in a quotation or not by finding the previous
+ * double quote character, and looking at whether it's followed by whitespace. If so, that
+ * was a closing quotation mark, so we're not inside a double quote. If it's not followed
+ * by whitespace, then it was an opening quotation mark, and we're inside a quotation.
+ *
+ * @param text the text to examine.
+ * @return whether we're inside a double quote.
+ */
+ public static boolean isInsideDoubleQuoteOrAfterDigit(final CharSequence text) {
+ int i = text.length();
+ if (0 == i) return false;
+ int codePoint = Character.codePointBefore(text, i);
+ if (Character.isDigit(codePoint)) return true;
+ int prevCodePoint = 0;
+ while (i > 0) {
+ codePoint = Character.codePointBefore(text, i);
+ if (Constants.CODE_DOUBLE_QUOTE == codePoint) {
+ // If we see a double quote followed by whitespace, then that
+ // was a closing quote.
+ if (Character.isWhitespace(prevCodePoint)) return false;
+ }
+ if (Character.isWhitespace(codePoint) && Constants.CODE_DOUBLE_QUOTE == prevCodePoint) {
+ // If we see a double quote preceded by whitespace, then that
+ // was an opening quote. No need to continue seeking.
+ return true;
+ }
+ i -= Character.charCount(codePoint);
+ prevCodePoint = codePoint;
+ }
+ // We reached the start of text. If the first char is a double quote, then we're inside
+ // a double quote. Otherwise we're not.
+ return Constants.CODE_DOUBLE_QUOTE == codePoint;
+ }
+
public static boolean isEmptyStringOrWhiteSpaces(final String s) {
final int N = codePointCount(s);
for (int i = 0; i < N; ++i) {