aboutsummaryrefslogtreecommitdiffstats
path: root/java/src/com
diff options
context:
space:
mode:
Diffstat (limited to 'java/src/com')
-rw-r--r--java/src/com/android/inputmethod/compat/EditorInfoCompatUtils.java10
-rw-r--r--java/src/com/android/inputmethod/deprecated/VoiceProxy.java3
-rw-r--r--java/src/com/android/inputmethod/keyboard/Keyboard.java30
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardId.java34
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java8
-rw-r--r--java/src/com/android/inputmethod/latin/LastComposedWord.java26
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java261
-rw-r--r--java/src/com/android/inputmethod/latin/SettingsValues.java36
-rw-r--r--java/src/com/android/inputmethod/latin/Utils.java6
-rw-r--r--java/src/com/android/inputmethod/latin/WordComposer.java24
-rw-r--r--java/src/com/android/inputmethod/latin/XmlParseUtils.java3
11 files changed, 208 insertions, 233 deletions
diff --git a/java/src/com/android/inputmethod/compat/EditorInfoCompatUtils.java b/java/src/com/android/inputmethod/compat/EditorInfoCompatUtils.java
index 3247997f6..938388d6c 100644
--- a/java/src/com/android/inputmethod/compat/EditorInfoCompatUtils.java
+++ b/java/src/com/android/inputmethod/compat/EditorInfoCompatUtils.java
@@ -39,30 +39,30 @@ public class EditorInfoCompatUtils {
private static final Integer OBJ_IME_ACTION_PREVIOUS = (Integer) CompatUtils
.getFieldValue(null, null, FIELD_IME_ACTION_PREVIOUS);
+ // EditorInfo.IME_FLAG_NAVIGATE_NEXT has been introduced since API#11 (Honeycomb).
public static boolean hasFlagNavigateNext(int imeOptions) {
if (OBJ_IME_FLAG_NAVIGATE_NEXT == null)
return false;
return (imeOptions & OBJ_IME_FLAG_NAVIGATE_NEXT) != 0;
}
+ // EditorInfo.IME_FLAG_NAVIGATE_PREVIOUS has been introduced since API#11 (Honeycomb).
public static boolean hasFlagNavigatePrevious(int imeOptions) {
if (OBJ_IME_FLAG_NAVIGATE_PREVIOUS == null)
return false;
return (imeOptions & OBJ_IME_FLAG_NAVIGATE_PREVIOUS) != 0;
}
+ // EditorInfo.IME_FLAG_FORCE_ASCII has been introduced since API#16 (JellyBean).
public static boolean hasFlagForceAscii(int imeOptions) {
if (OBJ_IME_FLAG_FORCE_ASCII == null)
return false;
return (imeOptions & OBJ_IME_FLAG_FORCE_ASCII) != 0;
}
- public static void performEditorActionNext(InputConnection ic) {
- ic.performEditorAction(EditorInfo.IME_ACTION_NEXT);
- }
-
+ // EditorInfo.IME_ACTION_PREVIOUS has been introduced since API#11 (Honeycomb).
public static void performEditorActionPrevious(InputConnection ic) {
- if (OBJ_IME_ACTION_PREVIOUS == null)
+ if (OBJ_IME_ACTION_PREVIOUS == null || ic == null)
return;
ic.performEditorAction(OBJ_IME_ACTION_PREVIOUS);
}
diff --git a/java/src/com/android/inputmethod/deprecated/VoiceProxy.java b/java/src/com/android/inputmethod/deprecated/VoiceProxy.java
index f632b0e02..5c4e9af68 100644
--- a/java/src/com/android/inputmethod/deprecated/VoiceProxy.java
+++ b/java/src/com/android/inputmethod/deprecated/VoiceProxy.java
@@ -56,6 +56,7 @@ import com.android.inputmethod.deprecated.voice.VoiceInput;
import com.android.inputmethod.keyboard.KeyboardSwitcher;
import com.android.inputmethod.keyboard.LatinKeyboardView;
import com.android.inputmethod.latin.EditingUtils;
+import com.android.inputmethod.latin.LastComposedWord;
import com.android.inputmethod.latin.LatinIME;
import com.android.inputmethod.latin.LatinIME.UIHandler;
import com.android.inputmethod.latin.LatinImeLogger;
@@ -553,7 +554,7 @@ public class VoiceProxy implements VoiceInput.UiListener {
mHints.registerVoiceResult(bestResult);
if (ic != null) ic.beginBatchEdit(); // To avoid extra updates on committing older text
- mService.commitTyped(ic);
+ mService.commitTyped(ic, LastComposedWord.NOT_A_SEPARATOR);
EditingUtils.appendText(ic, bestResult);
if (ic != null) ic.endBatchEdit();
diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java
index 60e506914..30ed59e18 100644
--- a/java/src/com/android/inputmethod/keyboard/Keyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java
@@ -99,8 +99,10 @@ public class Keyboard {
public static final int CODE_SETTINGS = -5;
public static final int CODE_SHORTCUT = -6;
public static final int CODE_ACTION_ENTER = -7;
+ public static final int CODE_ACTION_NEXT = -8;
+ public static final int CODE_ACTION_PREVIOUS = -9;
// Code value representing the code is not specified.
- public static final int CODE_UNSPECIFIED = -9;
+ public static final int CODE_UNSPECIFIED = -10;
public final KeyboardId mId;
public final int mThemeId;
@@ -381,6 +383,8 @@ public class Keyboard {
case CODE_SETTINGS: return "settings";
case CODE_SHORTCUT: return "shortcut";
case CODE_ACTION_ENTER: return "actionEnter";
+ case CODE_ACTION_NEXT: return "actionNext";
+ case CODE_ACTION_PREVIOUS: return "actionPrevious";
case CODE_UNSPECIFIED: return "unspec";
case CODE_TAB: return "tab";
case CODE_ENTER: return "enter";
@@ -1069,8 +1073,10 @@ public class Keyboard {
KeyboardId.elementIdToName(id.mElementId));
final boolean modeMatched = matchTypedValue(a,
R.styleable.Keyboard_Case_mode, id.mMode, KeyboardId.modeName(id.mMode));
- final boolean navigateActionMatched = matchBoolean(a,
- R.styleable.Keyboard_Case_navigateAction, id.navigateAction());
+ final boolean navigateNextMatched = matchBoolean(a,
+ R.styleable.Keyboard_Case_navigateNext, id.navigateNext());
+ final boolean navigatePreviousMatched = matchBoolean(a,
+ R.styleable.Keyboard_Case_navigatePrevious, id.navigatePrevious());
final boolean passwordInputMatched = matchBoolean(a,
R.styleable.Keyboard_Case_passwordInput, id.passwordInput());
final boolean clobberSettingsKeyMatched = matchBoolean(a,
@@ -1090,30 +1096,32 @@ public class Keyboard {
final boolean countryCodeMatched = matchString(a,
R.styleable.Keyboard_Case_countryCode, id.mLocale.getCountry());
final boolean selected = keyboardSetElementMatched && modeMatched
- && navigateActionMatched && passwordInputMatched
+ && navigateNextMatched && navigatePreviousMatched && passwordInputMatched
&& clobberSettingsKeyMatched && shortcutKeyEnabledMatched
&& hasShortcutKeyMatched && isMultiLineMatched && imeActionMatched
&& localeCodeMatched && languageCodeMatched && countryCodeMatched;
if (DEBUG) {
- startTag("<%s%s%s%s%s%s%s%s%s%s%s%s%s>%s", TAG_CASE,
+ startTag("<%s%s%s%s%s%s%s%s%s%s%s%s%s%s>%s", TAG_CASE,
textAttr(a.getString(R.styleable.Keyboard_Case_keyboardSetElement),
"keyboardSetElement"),
textAttr(a.getString(R.styleable.Keyboard_Case_mode), "mode"),
- booleanAttr(a, R.styleable.Keyboard_Case_navigateAction,
- "navigateAction"),
- booleanAttr(a, R.styleable.Keyboard_Case_passwordInput,
- "passwordInput"),
+ textAttr(a.getString(R.styleable.Keyboard_Case_imeAction),
+ "imeAction"),
+ booleanAttr(a, R.styleable.Keyboard_Case_navigateNext,
+ "navigateNext"),
+ booleanAttr(a, R.styleable.Keyboard_Case_navigatePrevious,
+ "navigatePrevious"),
booleanAttr(a, R.styleable.Keyboard_Case_clobberSettingsKey,
"clobberSettingsKey"),
+ booleanAttr(a, R.styleable.Keyboard_Case_passwordInput,
+ "passwordInput"),
booleanAttr(a, R.styleable.Keyboard_Case_shortcutKeyEnabled,
"shortcutKeyEnabled"),
booleanAttr(a, R.styleable.Keyboard_Case_hasShortcutKey,
"hasShortcutKey"),
booleanAttr(a, R.styleable.Keyboard_Case_isMultiLine,
"isMultiLine"),
- textAttr(a.getString(R.styleable.Keyboard_Case_imeAction),
- "imeAction"),
textAttr(a.getString(R.styleable.Keyboard_Case_localeCode),
"localeCode"),
textAttr(a.getString(R.styleable.Keyboard_Case_languageCode),
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardId.java b/java/src/com/android/inputmethod/keyboard/KeyboardId.java
index a75caf262..ed4a89e0f 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardId.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardId.java
@@ -87,7 +87,6 @@ public class KeyboardId {
id.mElementId,
id.mMode,
id.mWidth,
- id.navigateAction(),
id.passwordInput(),
id.mClobberSettingsKey,
id.mShortcutKeyEnabled,
@@ -95,6 +94,8 @@ public class KeyboardId {
id.isMultiLine(),
id.imeAction(),
id.mCustomActionLabel,
+ id.navigateNext(),
+ id.navigatePrevious(),
id.mLocale
});
}
@@ -106,7 +107,6 @@ public class KeyboardId {
&& other.mElementId == this.mElementId
&& other.mMode == this.mMode
&& other.mWidth == this.mWidth
- && other.navigateAction() == this.navigateAction()
&& other.passwordInput() == this.passwordInput()
&& other.mClobberSettingsKey == this.mClobberSettingsKey
&& other.mShortcutKeyEnabled == this.mShortcutKeyEnabled
@@ -114,6 +114,8 @@ public class KeyboardId {
&& other.isMultiLine() == this.isMultiLine()
&& other.imeAction() == this.imeAction()
&& TextUtils.equals(other.mCustomActionLabel, this.mCustomActionLabel)
+ && other.navigateNext() == this.navigateNext()
+ && other.navigatePrevious() == this.navigatePrevious()
&& other.mLocale.equals(this.mLocale);
}
@@ -146,12 +148,12 @@ public class KeyboardId {
return mElementId == ELEMENT_PHONE_SYMBOLS;
}
- public boolean navigateAction() {
- // Note: Turn off checking navigation flag to show TAB key for now.
- boolean navigateAction = InputTypeCompatUtils.isWebInputType(mEditorInfo.inputType);
-// || EditorInfoCompatUtils.hasFlagNavigateNext(mImeOptions)
-// || EditorInfoCompatUtils.hasFlagNavigatePrevious(mImeOptions);
- return navigateAction;
+ public boolean navigateNext() {
+ return EditorInfoCompatUtils.hasFlagNavigateNext(mEditorInfo.imeOptions);
+ }
+
+ public boolean navigatePrevious() {
+ return EditorInfoCompatUtils.hasFlagNavigatePrevious(mEditorInfo.imeOptions);
}
public boolean passwordInput() {
@@ -165,15 +167,21 @@ public class KeyboardId {
}
public int imeAction() {
+ final int actionId = mEditorInfo.imeOptions & EditorInfo.IME_MASK_ACTION;
if ((mEditorInfo.imeOptions & EditorInfo.IME_FLAG_NO_ENTER_ACTION) != 0) {
return EditorInfo.IME_ACTION_NONE;
} else if (mEditorInfo.actionLabel != null) {
return IME_ACTION_CUSTOM_LABEL;
} else {
- return mEditorInfo.imeOptions & EditorInfo.IME_MASK_ACTION;
+ return actionId;
}
}
+ public int imeActionId() {
+ final int actionId = imeAction();
+ return actionId == IME_ACTION_CUSTOM_LABEL ? mEditorInfo.actionId : actionId;
+ }
+
@Override
public boolean equals(Object other) {
return other instanceof KeyboardId && equals((KeyboardId) other);
@@ -186,17 +194,19 @@ public class KeyboardId {
@Override
public String toString() {
- return String.format("[%s %s %s%d %s %s %s%s%s%s%s]",
+ return String.format("[%s %s %s%d %s %s %s%s%s%s%s%s%s]",
elementIdToName(mElementId),
mLocale,
(mOrientation == 1 ? "port" : "land"), mWidth,
modeName(mMode),
imeAction(),
+ (navigateNext() ? "navigateNext" : ""),
+ (navigatePrevious() ? "navigatePrevious" : ""),
(mClobberSettingsKey ? " clobberSettingsKey" : ""),
- (navigateAction() ? " navigateAction" : ""),
(passwordInput() ? " passwordInput" : ""),
(mShortcutKeyEnabled ? " shortcutKeyEnabled" : ""),
- (mHasShortcutKey ? " hasShortcutKey" : "")
+ (mHasShortcutKey ? " hasShortcutKey" : ""),
+ (isMultiLine() ? "isMultiLine" : "")
);
}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java
index 12a9c51f2..9e5c227eb 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyStyles.java
@@ -163,10 +163,10 @@ public class KeyStyles {
if (DEBUG) {
Log.d(TAG, String.format("<%s styleName=%s />",
Keyboard.Builder.TAG_KEY_STYLE, styleName));
- }
- if (mStyles.containsKey(styleName)) {
- throw new XmlParseUtils.ParseException(
- "duplicate key style declared: " + styleName, parser);
+ if (mStyles.containsKey(styleName)) {
+ Log.d(TAG, "key-style " + styleName + " is overridden at "
+ + parser.getPositionDescription());
+ }
}
final DeclaredKeyStyle style = new DeclaredKeyStyle();
diff --git a/java/src/com/android/inputmethod/latin/LastComposedWord.java b/java/src/com/android/inputmethod/latin/LastComposedWord.java
index f34cb5ff9..bc0792434 100644
--- a/java/src/com/android/inputmethod/latin/LastComposedWord.java
+++ b/java/src/com/android/inputmethod/latin/LastComposedWord.java
@@ -40,26 +40,31 @@ public class LastComposedWord {
// an auto-correction.
public static final int COMMIT_TYPE_CANCEL_AUTO_CORRECT = 3;
+ public static final int NOT_A_SEPARATOR = -1;
+
public final ArrayList<int[]> mCodes;
public final int[] mXCoordinates;
public final int[] mYCoordinates;
public final String mTypedWord;
- public final String mAutoCorrection;
+ public final String mCommittedWord;
+ public final int mSeparatorCode;
private boolean mActive;
public static final LastComposedWord NOT_A_COMPOSED_WORD =
- new LastComposedWord(null, null, null, "", "");
+ new LastComposedWord(null, null, null, "", "", NOT_A_SEPARATOR);
// Warning: this is using the passed objects as is and fully expects them to be
// immutable. Do not fiddle with their contents after you passed them to this constructor.
public LastComposedWord(final ArrayList<int[]> codes, final int[] xCoordinates,
- final int[] yCoordinates, final String typedWord, final String autoCorrection) {
+ final int[] yCoordinates, final String typedWord, final String committedWord,
+ final int separatorCode) {
mCodes = codes;
mXCoordinates = xCoordinates;
mYCoordinates = yCoordinates;
mTypedWord = typedWord;
- mAutoCorrection = autoCorrection;
+ mCommittedWord = committedWord;
+ mSeparatorCode = separatorCode;
mActive = true;
}
@@ -67,8 +72,15 @@ public class LastComposedWord {
mActive = false;
}
- public boolean canCancelAutoCorrect() {
- return mActive && !TextUtils.isEmpty(mAutoCorrection)
- && !TextUtils.equals(mTypedWord, mAutoCorrection);
+ public boolean canRevertCommit() {
+ return mActive && !TextUtils.isEmpty(mCommittedWord);
+ }
+
+ public boolean didCommitTypedWord() {
+ return TextUtils.equals(mTypedWord, mCommittedWord);
+ }
+
+ public static int getSeparatorLength(final int separatorCode) {
+ return NOT_A_SEPARATOR == separatorCode ? 0 : 1;
}
}
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index ce306eaad..47ec40f99 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -165,8 +165,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
// Double space: the state where the user pressed space twice quickly, which LatinIME
// resolved as period-space. Undoing this converts the period to a space.
private static final int SPACE_STATE_DOUBLE = 1;
- // Swap punctuation: the state where a (weak or magic) space and a punctuation from the
- // suggestion strip have just been swapped. Undoing this swaps them back.
+ // Swap punctuation: the state where a weak space and a punctuation from the suggestion strip
+ // have just been swapped. Undoing this swaps them back; the space is still considered weak.
private static final int SPACE_STATE_SWAP_PUNCTUATION = 2;
// Weak space: a space that should be swapped only by suggestion strip punctuation. Weak
// spaces happen when the user presses space, accepting the current suggestion (whether
@@ -658,7 +658,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
mDisplayOrientation = conf.orientation;
mHandler.startOrientationChanging();
final InputConnection ic = getCurrentInputConnection();
- commitTyped(ic);
+ commitTyped(ic, LastComposedWord.NOT_A_SEPARATOR);
if (ic != null) ic.finishComposingText(); // For voice input
if (isShowingOptionDialog())
mOptionsDialog.dismiss();
@@ -887,26 +887,16 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
// the second one - the first call successfully avoids this test, but the second one
// enters. For the moment we rely on noComposingSpan to further reduce the impact.
+ // 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 postUpdateShiftState.
// We set this to NONE because after a cursor move, we don't want the space
// state-related special processing to kick in.
mSpaceState = SPACE_STATE_NONE;
- if (((mWordComposer.isComposingWord())
- || mVoiceProxy.isVoiceInputHighlighted())
- && (selectionChanged || noComposingSpan)) {
- resetComposingState(true /* alsoResetLastComposedWord */);
- updateSuggestions();
- final InputConnection ic = getCurrentInputConnection();
- if (ic != null) {
- ic.finishComposingText();
- }
- mComposingStateManager.onFinishComposingText();
- mVoiceProxy.setVoiceInputHighlighted(false);
- } else if (!mWordComposer.isComposingWord()) {
- // TODO: is the following reset still needed, given that we are not composing
- // a word?
- resetComposingState(true /* alsoResetLastComposedWord */);
- updateSuggestions();
+ if ((!mWordComposer.isComposingWord()) || selectionChanged || noComposingSpan) {
+ resetEntireInputState();
}
mHandler.postUpdateShiftState();
@@ -1116,17 +1106,33 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
return super.onKeyUp(keyCode, event);
}
+ // This will reset the whole input state to the starting state. It will clear
+ // the composing word, reset the last composed word, tell the inputconnection
+ // and the composingStateManager about it.
+ private void resetEntireInputState() {
+ resetComposingState(true /* alsoResetLastComposedWord */);
+ updateSuggestions();
+ final InputConnection ic = getCurrentInputConnection();
+ if (ic != null) {
+ ic.finishComposingText();
+ }
+ mComposingStateManager.onFinishComposingText();
+ mVoiceProxy.setVoiceInputHighlighted(false);
+ }
+
private void resetComposingState(final boolean alsoResetLastComposedWord) {
mWordComposer.reset();
if (alsoResetLastComposedWord)
mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
}
- public void commitTyped(final InputConnection ic) {
+ public void commitTyped(final InputConnection ic, final int separatorCode) {
if (!mWordComposer.isComposingWord()) return;
final CharSequence typedWord = mWordComposer.getTypedWord();
- mLastComposedWord = mWordComposer.commitWord(LastComposedWord.COMMIT_TYPE_USER_TYPED_WORD);
if (typedWord.length() > 0) {
+ mLastComposedWord = mWordComposer.commitWord(
+ LastComposedWord.COMMIT_TYPE_USER_TYPED_WORD, typedWord.toString(),
+ separatorCode);
if (ic != null) {
ic.commitText(typedWord, 1);
}
@@ -1242,11 +1248,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
KeyboardActionListener.SUGGESTION_STRIP_COORDINATE);
}
- private static int getEditorActionId(EditorInfo editorInfo) {
- if (editorInfo == null) return 0;
- return (editorInfo.actionLabel != null)
- ? editorInfo.actionId
- : (editorInfo.imeOptions & EditorInfo.IME_MASK_ACTION);
+ private static int getActionId(Keyboard keyboard) {
+ return keyboard != null ? keyboard.mId.imeActionId() : EditorInfo.IME_ACTION_NONE;
}
private void performeEditorAction(int actionId) {
@@ -1312,18 +1315,13 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
mSubtypeSwitcher.switchToShortcutIME();
break;
case Keyboard.CODE_ACTION_ENTER:
- performeEditorAction(getEditorActionId(getCurrentInputEditorInfo()));
+ performeEditorAction(getActionId(switcher.getKeyboard()));
+ break;
+ case Keyboard.CODE_ACTION_NEXT:
+ performeEditorAction(EditorInfo.IME_ACTION_NEXT);
break;
- case Keyboard.CODE_TAB:
- handleTab();
- // There are two cases for tab. Either we send a "next" event, that may change the
- // focus but will never move the cursor. Or, we send a real tab keycode, which some
- // applications may accept or ignore, and we don't know whether this will move the
- // cursor or not. So actually, we don't really know.
- // So to go with the safer option, we'd rather behave as if the user moved the
- // cursor when they didn't than the opposite. We also expect that most applications
- // will actually use tab only for focus movement.
- // To sum it up: do not update mExpectingUpdateSelection here.
+ case Keyboard.CODE_ACTION_PREVIOUS:
+ EditorInfoCompatUtils.performEditorActionPrevious(getCurrentInputConnection());
break;
default:
mSpaceState = SPACE_STATE_NONE;
@@ -1348,7 +1346,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
final InputConnection ic = getCurrentInputConnection();
if (ic == null) return;
ic.beginBatchEdit();
- commitTyped(ic);
+ commitTyped(ic, LastComposedWord.NOT_A_SEPARATOR);
text = specificTldProcessingOnTextInput(ic, text);
if (SPACE_STATE_PHANTOM == mSpaceState) {
sendKeyCodePoint(Keyboard.CODE_SPACE);
@@ -1433,16 +1431,11 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
ic.deleteSurroundingText(1, 0);
}
} else {
- // We should be very careful about auto-correction cancellation and suggestion
- // resuming here. The behavior needs to be different according to text field types,
- // and it would be much clearer to test for them explicitly here rather than
- // relying on implicit values like "whether the suggestion strip is displayed".
- if (mLastComposedWord.canCancelAutoCorrect()) {
+ if (mLastComposedWord.canRevertCommit()) {
Utils.Stats.onAutoCorrectionCancellation();
- cancelAutoCorrect(ic);
+ revertCommit(ic);
return;
}
-
if (SPACE_STATE_DOUBLE == spaceState) {
if (revertDoubleSpaceWhileInBatchEdit(ic)) {
// No need to reset mSpaceState, it has already be done (that's why we
@@ -1456,61 +1449,27 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
}
}
- // See the comment above: must be careful about resuming auto-suggestion.
- if (mSuggestionsView != null && mSuggestionsView.dismissAddToDictionaryHint()) {
- // Go back to the suggestion mode if the user canceled the
- // "Touch again to save".
- // NOTE: In general, we don't revert the word when backspacing
- // from a manual suggestion pick. We deliberately chose a
- // different behavior only in the case of picking the first
- // suggestion (typed word). It's intentional to have made this
- // inconsistent with backspacing after selecting other suggestions.
- restartSuggestionsOnManuallyPickedTypedWord(ic);
+ // No cancelling of commit/double space/swap: we have a regular backspace.
+ // We should backspace one char and restart suggestion if at the end of a word.
+ if (mLastSelectionStart != mLastSelectionEnd) {
+ // If there is a selection, remove it.
+ final int lengthToDelete = mLastSelectionEnd - mLastSelectionStart;
+ ic.setSelection(mLastSelectionEnd, mLastSelectionEnd);
+ ic.deleteSurroundingText(lengthToDelete, 0);
} else {
- // Here we must check whether there is a selection. If so we should remove the
- // selected text, otherwise we should just delete the character before the cursor.
- if (mLastSelectionStart != mLastSelectionEnd) {
- final int lengthToDelete = mLastSelectionEnd - mLastSelectionStart;
- ic.setSelection(mLastSelectionEnd, mLastSelectionEnd);
- ic.deleteSurroundingText(lengthToDelete, 0);
- } else {
- if (NOT_A_CURSOR_POSITION == mLastSelectionEnd) {
- // This should never happen.
- Log.e(TAG, "Backspace when we don't know the selection position");
- }
- ic.deleteSurroundingText(1, 0);
- if (mDeleteCount > DELETE_ACCELERATE_AT) {
- ic.deleteSurroundingText(1, 0);
- }
+ // There is no selection, just delete one character.
+ if (NOT_A_CURSOR_POSITION == mLastSelectionEnd) {
+ // This should never happen.
+ Log.e(TAG, "Backspace when we don't know the selection position");
}
- if (isSuggestionsRequested()) {
- restartSuggestionsOnWordBeforeCursorIfAtEndOfWord(ic);
+ ic.deleteSurroundingText(1, 0);
+ if (mDeleteCount > DELETE_ACCELERATE_AT) {
+ ic.deleteSurroundingText(1, 0);
}
}
- }
- }
-
- // TODO: Implement next and previous actions using other key code than tab's code.
- private void handleTab() {
- final int imeOptions = getCurrentInputEditorInfo().imeOptions;
- if (!EditorInfoCompatUtils.hasFlagNavigateNext(imeOptions)
- && !EditorInfoCompatUtils.hasFlagNavigatePrevious(imeOptions)) {
- // TODO: This should be {@link #sendKeyCodePoint(int)}.
- sendDownUpKeyEvents(KeyEvent.KEYCODE_TAB);
- return;
- }
-
- final InputConnection ic = getCurrentInputConnection();
- if (ic == null)
- return;
-
- final Keyboard keyboard = mKeyboardSwitcher.getKeyboard();
- // True if keyboard is in either shift chording or manual shifted state.
- final boolean isManualShifted = (keyboard != null && keyboard.isManualShifted());
- if (EditorInfoCompatUtils.hasFlagNavigateNext(imeOptions) && !isManualShifted) {
- EditorInfoCompatUtils.performEditorActionNext(ic);
- } else if (EditorInfoCompatUtils.hasFlagNavigatePrevious(imeOptions) && isManualShifted) {
- EditorInfoCompatUtils.performEditorActionPrevious(ic);
+ if (isSuggestionsRequested()) {
+ restartSuggestionsOnWordBeforeCursorIfAtEndOfWord(ic);
+ }
}
}
@@ -1523,10 +1482,10 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
} else if ((SPACE_STATE_WEAK == spaceState
|| SPACE_STATE_SWAP_PUNCTUATION == spaceState)
&& isFromSuggestionStrip) {
- if (mSettingsValues.isMagicSpaceSwapper(code)) {
+ if (mSettingsValues.isWeakSpaceSwapper(code)) {
return true;
} else {
- if (mSettingsValues.isMagicSpaceStripper(code)) {
+ if (mSettingsValues.isWeakSpaceStripper(code)) {
removeTrailingSpaceWhileInBatchEdit(ic);
}
return false;
@@ -1599,6 +1558,15 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
swapSwapperAndSpaceWhileInBatchEdit(ic);
mSpaceState = SPACE_STATE_WEAK;
}
+ // Some characters are not word separators, yet they don't start a new
+ // composing span. For these, we haven't changed the suggestion strip, and
+ // if the "add to dictionary" hint is shown, we should do so now. Examples of
+ // such characters include single quote, dollar, and others; the exact list is
+ // the list of characters for which we enter handleCharacterWhileInBatchEdit
+ // that don't match the test if ((isAlphabet...)) at the top of this method.
+ if (null != mSuggestionsView && mSuggestionsView.dismissAddToDictionaryHint()) {
+ mHandler.postUpdateBigramPredictions();
+ }
}
Utils.Stats.onNonSeparator((char)primaryCode, x, y);
}
@@ -1632,7 +1600,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
commitCurrentAutoCorrection(primaryCode, ic);
didAutoCorrect = true;
} else {
- commitTyped(ic);
+ commitTyped(ic, primaryCode);
}
}
@@ -1689,7 +1657,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
}
private void handleClose() {
- commitTyped(getCurrentInputConnection());
+ commitTyped(getCurrentInputConnection(), LastComposedWord.NOT_A_SEPARATOR);
mVoiceProxy.handleClose();
requestHideSelf(0);
LatinKeyboardView inputView = mKeyboardSwitcher.getKeyboardView();
@@ -1900,7 +1868,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
}
Utils.Stats.onAutoCorrection(typedWord, autoCorrection.toString(), separatorCodePoint);
mExpectingUpdateSelection = true;
- commitChosenWord(autoCorrection, LastComposedWord.COMMIT_TYPE_DECIDED_WORD);
+ commitChosenWord(autoCorrection, LastComposedWord.COMMIT_TYPE_DECIDED_WORD,
+ separatorCodePoint);
// Add the word to the user unigram dictionary if it's not a known word
addToUserUnigramAndBigramDictionaries(autoCorrection,
UserUnigramDictionary.FREQUENCY_FOR_TYPED);
@@ -1955,7 +1924,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
LatinImeLogger.logOnManualSuggestion(mWordComposer.getTypedWord().toString(),
suggestion.toString(), index, suggestions.mWords);
mExpectingUpdateSelection = true;
- commitChosenWord(suggestion, LastComposedWord.COMMIT_TYPE_MANUAL_PICK);
+ commitChosenWord(suggestion, LastComposedWord.COMMIT_TYPE_MANUAL_PICK,
+ LastComposedWord.NOT_A_SEPARATOR);
// Add the word to the auto dictionary if it's not a known word
if (index == 0) {
addToUserUnigramAndBigramDictionaries(suggestion,
@@ -2005,7 +1975,8 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
/**
* Commits the chosen word to the text field and saves it for later retrieval.
*/
- private void commitChosenWord(final CharSequence bestWord, final int commitType) {
+ private void commitChosenWord(final CharSequence bestWord, final int commitType,
+ final int separatorCode) {
final InputConnection ic = getCurrentInputConnection();
if (ic != null) {
mVoiceProxy.rememberReplacedWord(bestWord, mSettingsValues.mWordSeparators);
@@ -2019,9 +1990,10 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
}
// TODO: figure out here if this is an auto-correct or if the best word is actually
// what user typed. Note: currently this is done much later in
- // LastComposedWord#canCancelAutoCorrect by string equality of the remembered
+ // LastComposedWord#didCommitTypedWord by string equality of the remembered
// strings.
- mLastComposedWord = mWordComposer.commitWord(commitType);
+ mLastComposedWord = mWordComposer.commitWord(commitType, bestWord.toString(),
+ separatorCode);
}
private static final WordComposer sEmptyWordComposer = new WordComposer();
@@ -2183,66 +2155,43 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
}
// "ic" must not be null
- private void cancelAutoCorrect(final InputConnection ic) {
+ private void revertCommit(final InputConnection ic) {
final String originallyTypedWord = mLastComposedWord.mTypedWord;
- final CharSequence autoCorrectedTo = mLastComposedWord.mAutoCorrection;
- final int cancelLength = autoCorrectedTo.length();
- final CharSequence separator = ic.getTextBeforeCursor(1, 0);
+ final CharSequence committedWord = mLastComposedWord.mCommittedWord;
+ final int cancelLength = committedWord.length();
+ final int separatorLength = mLastComposedWord.getSeparatorLength(
+ mLastComposedWord.mSeparatorCode);
+ // TODO: should we check our saved separator against the actual contents of the text view?
if (DEBUG) {
if (mWordComposer.isComposingWord()) {
- throw new RuntimeException("cancelAutoCorrect, but we are composing a word");
+ throw new RuntimeException("revertCommit, but we are composing a word");
}
final String wordBeforeCursor =
- ic.getTextBeforeCursor(cancelLength + 1, 0).subSequence(0, cancelLength)
- .toString();
- if (!TextUtils.equals(autoCorrectedTo, wordBeforeCursor)) {
- throw new RuntimeException("cancelAutoCorrect check failed: we thought we were "
- + "reverting \"" + autoCorrectedTo
+ ic.getTextBeforeCursor(cancelLength + separatorLength, 0)
+ .subSequence(0, cancelLength).toString();
+ if (!TextUtils.equals(committedWord, wordBeforeCursor)) {
+ throw new RuntimeException("revertCommit check failed: we thought we were "
+ + "reverting \"" + committedWord
+ "\", but before the cursor we found \"" + wordBeforeCursor + "\"");
}
- if (TextUtils.equals(originallyTypedWord, wordBeforeCursor)) {
- throw new RuntimeException("cancelAutoCorrect check failed: we wanted to cancel "
- + "auto correction and revert to \"" + originallyTypedWord
- + "\" but we found this very string before the cursor");
- }
}
- ic.deleteSurroundingText(cancelLength + 1, 0);
- ic.commitText(originallyTypedWord, 1);
- // Re-insert the separator
- ic.commitText(separator, 1);
- mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
- Utils.Stats.onSeparator(separator.charAt(0), WordComposer.NOT_A_COORDINATE,
- WordComposer.NOT_A_COORDINATE);
- mHandler.cancelUpdateBigramPredictions();
- mHandler.postUpdateSuggestions();
- }
-
- // "ic" must not be null
- private void restartSuggestionsOnManuallyPickedTypedWord(final InputConnection ic) {
- // Note: this relies on the last word still being held in the WordComposer, in
- // the field for suggestion resuming.
- // 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.
- mWordComposer.resumeSuggestionOnLastComposedWord(mLastComposedWord);
- // We resume suggestion, and then we want to set the composing text to the content
- // of the word composer again. But since we just manually picked a word, there is
- // no composing text at the moment, so we have to delete the word before we set a
- // new composing text.
- final int restartLength = mWordComposer.size();
- if (DEBUG) {
- final String wordBeforeCursor = ic.getTextBeforeCursor(restartLength, 0).toString();
- if (!TextUtils.equals(mWordComposer.getTypedWord(), wordBeforeCursor)) {
- throw new RuntimeException("restartSuggestionsOnManuallyPickedTypedWord "
- + "check failed: we thought we were reverting \""
- + mWordComposer.getTypedWord()
- + "\", but before the cursor we found \""
- + wordBeforeCursor + "\"");
- }
+ ic.deleteSurroundingText(cancelLength + separatorLength, 0);
+ if (0 == separatorLength || mLastComposedWord.didCommitTypedWord()) {
+ // This is the case when we cancel a manual pick.
+ // We should restart suggestion on the word right away.
+ mWordComposer.resumeSuggestionOnLastComposedWord(mLastComposedWord);
+ mComposingStateManager.onStartComposingText();
+ ic.setComposingText(originallyTypedWord, 1);
+ } else {
+ ic.commitText(originallyTypedWord, 1);
+ // Re-insert the separator
+ sendKeyCodePoint(mLastComposedWord.mSeparatorCode);
+ Utils.Stats.onSeparator(mLastComposedWord.mSeparatorCode, WordComposer.NOT_A_COORDINATE,
+ WordComposer.NOT_A_COORDINATE);
+ // Don't restart suggestion yet. We'll restart if the user deletes the
+ // separator.
}
- ic.deleteSurroundingText(restartLength, 0);
- mComposingStateManager.onStartComposingText();
- ic.setComposingText(mWordComposer.getTypedWord(), 1);
+ mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
mHandler.cancelUpdateBigramPredictions();
mHandler.postUpdateSuggestions();
}
diff --git a/java/src/com/android/inputmethod/latin/SettingsValues.java b/java/src/com/android/inputmethod/latin/SettingsValues.java
index 7a43cb827..d123b608f 100644
--- a/java/src/com/android/inputmethod/latin/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/SettingsValues.java
@@ -35,8 +35,8 @@ public class SettingsValues {
// From resources:
public final int mDelayUpdateOldSuggestions;
- public final String mMagicSpaceStrippers;
- public final String mMagicSpaceSwappers;
+ public final String mWeakSpaceStrippers;
+ public final String mWeakSpaceSwappers;
private final String mSuggestPuncs;
public final SuggestedWords mSuggestPuncList;
public final SuggestedWords mSuggestPuncOutputTextList;
@@ -89,14 +89,14 @@ public class SettingsValues {
// Get the resources
mDelayUpdateOldSuggestions = res.getInteger(R.integer.config_delay_update_old_suggestions);
- mMagicSpaceStrippers = res.getString(R.string.magic_space_stripping_symbols);
- mMagicSpaceSwappers = res.getString(R.string.magic_space_swapping_symbols);
+ mWeakSpaceStrippers = res.getString(R.string.weak_space_stripping_symbols);
+ mWeakSpaceSwappers = res.getString(R.string.weak_space_swapping_symbols);
if (LatinImeLogger.sDBG) {
- final int length = mMagicSpaceStrippers.length();
- for (int i = 0; i < length; i = mMagicSpaceStrippers.offsetByCodePoints(i, 1)) {
- if (isMagicSpaceSwapper(mMagicSpaceStrippers.codePointAt(i))) {
- throw new RuntimeException("Char code " + mMagicSpaceStrippers.codePointAt(i)
- + " is both a magic space swapper and stripper.");
+ final int length = mWeakSpaceStrippers.length();
+ for (int i = 0; i < length; i = mWeakSpaceStrippers.offsetByCodePoints(i, 1)) {
+ if (isWeakSpaceSwapper(mWeakSpaceStrippers.codePointAt(i))) {
+ throw new RuntimeException("Char code " + mWeakSpaceStrippers.codePointAt(i)
+ + " is both a weak space swapper and stripper.");
}
}
}
@@ -107,7 +107,7 @@ public class SettingsValues {
mSuggestPuncOutputTextList = createSuggestPuncOutputTextList(suggestPuncsSpec);
mSymbolsExcludedFromWordSeparators =
res.getString(R.string.symbols_excluded_from_word_separators);
- mWordSeparators = createWordSeparators(mMagicSpaceStrippers, mMagicSpaceSwappers,
+ mWordSeparators = createWordSeparators(mWeakSpaceStrippers, mWeakSpaceSwappers,
mSymbolsExcludedFromWordSeparators, res);
mHintToSaveText = context.getText(R.string.hint_add_to_dictionary);
@@ -188,11 +188,11 @@ public class SettingsValues {
return builder.setIsPunctuationSuggestions().build();
}
- private static String createWordSeparators(final String magicSpaceStrippers,
- final String magicSpaceSwappers, final String symbolsExcludedFromWordSeparators,
+ private static String createWordSeparators(final String weakSpaceStrippers,
+ final String weakSpaceSwappers, final String symbolsExcludedFromWordSeparators,
final Resources res) {
- String wordSeparators = magicSpaceStrippers + magicSpaceSwappers
- + res.getString(R.string.magic_space_promoting_symbols);
+ String wordSeparators = weakSpaceStrippers + weakSpaceSwappers
+ + res.getString(R.string.weak_space_promoting_symbols);
for (int i = symbolsExcludedFromWordSeparators.length() - 1; i >= 0; --i) {
wordSeparators = wordSeparators.replace(
symbolsExcludedFromWordSeparators.substring(i, i + 1), "");
@@ -215,14 +215,14 @@ public class SettingsValues {
return mSymbolsExcludedFromWordSeparators.contains(String.valueOf((char)code));
}
- public boolean isMagicSpaceStripper(int code) {
+ public boolean isWeakSpaceStripper(int code) {
// TODO: this does not work if the code does not fit in a char
- return mMagicSpaceStrippers.contains(String.valueOf((char)code));
+ return mWeakSpaceStrippers.contains(String.valueOf((char)code));
}
- public boolean isMagicSpaceSwapper(int code) {
+ public boolean isWeakSpaceSwapper(int code) {
// TODO: this does not work if the code does not fit in a char
- return mMagicSpaceSwappers.contains(String.valueOf((char)code));
+ return mWeakSpaceSwappers.contains(String.valueOf((char)code));
}
private static boolean isAutoCorrectEnabled(final Resources resources,
diff --git a/java/src/com/android/inputmethod/latin/Utils.java b/java/src/com/android/inputmethod/latin/Utils.java
index 3975dddeb..6d63e95f6 100644
--- a/java/src/com/android/inputmethod/latin/Utils.java
+++ b/java/src/com/android/inputmethod/latin/Utils.java
@@ -264,6 +264,7 @@ public class Utils {
int ret = in % BUFSIZE;
return ret < 0 ? ret + BUFSIZE : ret;
}
+ // TODO: accept code points
public void push(char c, int x, int y) {
if (!mEnabled) return;
if (mUsabilityStudy) {
@@ -777,9 +778,10 @@ public class Utils {
LatinImeLogger.logOnInputChar();
}
- public static void onSeparator(final char code, final int x,
+ public static void onSeparator(final int code, final int x,
final int y) {
- RingCharBuffer.getInstance().push(code, x, y);
+ // TODO: accept code points
+ RingCharBuffer.getInstance().push((char)code, x, y);
LatinImeLogger.logOnInputSeparator();
}
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
index a1a329a8d..8121ada7f 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -308,17 +308,11 @@ public class WordComposer {
}
// `type' should be one of the LastComposedWord.COMMIT_TYPE_* constants above.
- public LastComposedWord commitWord(final int type) {
- // Note: currently, we come here whenever we commit a word. If it's any *other* kind than
- // DECIDED_WORD, we should reset mAutoCorrection so that we don't attempt to cancel later.
- // If it's a DECIDED_WORD, it may be an actual auto-correction by the IME, or what the user
- // typed because the IME decided *not* to auto-correct for whatever reason.
- // Ideally we would also null it when it was a DECIDED_WORD that was not an auto-correct.
- // As it happens these two cases should behave differently, because the former can be
- // canceled while the latter can't. Currently, we figure this out in
- // LastComposedWord#didAutoCorrectToAnotherWord with #equals(). It would be marginally
- // cleaner to do it here, but it would be slower (since we would #equals() for each commit,
- // instead of only on cancel), and ultimately we want to figure it out even earlier anyway.
+ public LastComposedWord commitWord(final int type, final String committedWord,
+ final int separatorCode) {
+ // Note: currently, we come here whenever we commit a word. If it's a MANUAL_PICK
+ // or a DECIDED_WORD we may cancel the commit later; otherwise, we should deactivate
+ // the last composed word to ensure this does not happen.
final ArrayList<int[]> codes = mCodes;
final int[] xCoordinates = mXCoordinates;
final int[] yCoordinates = mYCoordinates;
@@ -326,9 +320,9 @@ public class WordComposer {
mXCoordinates = new int[N];
mYCoordinates = new int[N];
final LastComposedWord lastComposedWord = new LastComposedWord(codes,
- xCoordinates, yCoordinates, mTypedWord.toString(),
- null == mAutoCorrection ? null : mAutoCorrection.toString());
- if (type != LastComposedWord.COMMIT_TYPE_DECIDED_WORD) {
+ xCoordinates, yCoordinates, mTypedWord.toString(), committedWord, separatorCode);
+ if (type != LastComposedWord.COMMIT_TYPE_DECIDED_WORD
+ && type != LastComposedWord.COMMIT_TYPE_MANUAL_PICK) {
lastComposedWord.deactivate();
}
mTypedWord.setLength(0);
@@ -342,6 +336,6 @@ public class WordComposer {
mYCoordinates = lastComposedWord.mYCoordinates;
mTypedWord.setLength(0);
mTypedWord.append(lastComposedWord.mTypedWord);
- mAutoCorrection = lastComposedWord.mAutoCorrection;
+ mAutoCorrection = null; // This will be filled by the next call to updateSuggestion.
}
}
diff --git a/java/src/com/android/inputmethod/latin/XmlParseUtils.java b/java/src/com/android/inputmethod/latin/XmlParseUtils.java
index d747a024c..e14c71cb5 100644
--- a/java/src/com/android/inputmethod/latin/XmlParseUtils.java
+++ b/java/src/com/android/inputmethod/latin/XmlParseUtils.java
@@ -27,8 +27,7 @@ public class XmlParseUtils {
@SuppressWarnings("serial")
public static class ParseException extends XmlPullParserException {
public ParseException(String msg, XmlPullParser parser) {
- super(msg + " at line " + parser.getLineNumber()
- + ", column " + parser.getColumnNumber());
+ super(msg + " at " + parser.getPositionDescription());
}
}