aboutsummaryrefslogtreecommitdiffstats
path: root/java/src
diff options
context:
space:
mode:
Diffstat (limited to 'java/src')
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardSet.java134
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java114
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java175
-rw-r--r--java/src/com/android/inputmethod/latin/Utils.java9
-rw-r--r--java/src/com/android/inputmethod/latin/WordComposer.java31
-rw-r--r--java/src/com/android/inputmethod/latin/define/JniLibName.java21
6 files changed, 299 insertions, 185 deletions
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardSet.java
new file mode 100644
index 000000000..a803188da
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSet.java
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.inputmethod.keyboard;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.util.DisplayMetrics;
+import android.view.inputmethod.EditorInfo;
+
+import com.android.inputmethod.latin.LatinIME;
+import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.SettingsValues;
+import com.android.inputmethod.latin.SubtypeSwitcher;
+import com.android.inputmethod.latin.Utils;
+
+import java.util.Locale;
+
+/**
+ * This class has a set of {@link KeyboardId}s. Each of them represents a different keyboard
+ * specific to a keyboard state, such as alphabet, symbols, and so on. Layouts in the same
+ * {@link KeyboardSet} are related to each other.
+ * A {@link KeyboardSet} needs to be created for each {@link android.view.inputmethod.EditorInfo}.
+ */
+public class KeyboardSet {
+ // TODO: Make these KeyboardId private.
+ public final KeyboardId mAlphabetId;
+ public final KeyboardId mSymbolsId;
+ public final KeyboardId mSymbolsShiftedId;
+
+ KeyboardSet(Builder builder) {
+ mAlphabetId = builder.getKeyboardId(false, false);
+ mSymbolsId = builder.getKeyboardId(true, false);
+ mSymbolsShiftedId = builder.getKeyboardId(true, true);
+ }
+
+ public static class Builder {
+ private final Resources mResources;
+ private final EditorInfo mEditorInfo;
+
+ private final int mMode;
+ private final boolean mVoiceKeyEnabled;
+ private final boolean mNoSettingsKey;
+ private final boolean mHasSettingsKey;
+ private final int mF2KeyMode;
+ private final boolean mVoiceKeyOnMain;
+ private final Locale mLocale;
+ private final Configuration mConf;
+ private final DisplayMetrics mMetrics;
+
+ public Builder(Context context, EditorInfo editorInfo, SettingsValues settingsValues) {
+ mResources = context.getResources();
+ mEditorInfo = editorInfo;
+ final SubtypeSwitcher subtypeSwitcher = SubtypeSwitcher.getInstance();
+ final String packageName = context.getPackageName();
+
+ mMode = Utils.getKeyboardMode(mEditorInfo);
+ final boolean settingsKeyEnabled = settingsValues.isSettingsKeyEnabled();
+ @SuppressWarnings("deprecation")
+ final boolean noMicrophone = Utils.inPrivateImeOptions(
+ packageName, LatinIME.IME_OPTION_NO_MICROPHONE, editorInfo)
+ || Utils.inPrivateImeOptions(
+ null, LatinIME.IME_OPTION_NO_MICROPHONE_COMPAT, editorInfo);
+ mVoiceKeyEnabled = settingsValues.isVoiceKeyEnabled(editorInfo) && !noMicrophone;
+ mVoiceKeyOnMain = settingsValues.isVoiceKeyOnMain();
+ mNoSettingsKey = Utils.inPrivateImeOptions(
+ packageName, LatinIME.IME_OPTION_NO_SETTINGS_KEY, editorInfo);
+ mHasSettingsKey = settingsKeyEnabled && !mNoSettingsKey;
+ mF2KeyMode = getF2KeyMode(settingsKeyEnabled, mNoSettingsKey);
+ final boolean forceAscii = Utils.inPrivateImeOptions(
+ packageName, LatinIME.IME_OPTION_FORCE_ASCII, editorInfo);
+ final boolean asciiCapable = subtypeSwitcher.currentSubtypeContainsExtraValueKey(
+ LatinIME.SUBTYPE_EXTRA_VALUE_ASCII_CAPABLE);
+ mLocale = (forceAscii && !asciiCapable) ? Locale.US : subtypeSwitcher.getInputLocale();
+ mConf = mResources.getConfiguration();
+ mMetrics = mResources.getDisplayMetrics();
+ }
+
+ public KeyboardSet build() {
+ return new KeyboardSet(this);
+ }
+
+ KeyboardId getKeyboardId(boolean isSymbols, boolean isShift) {
+ final int xmlId = getXmlId(mMode, isSymbols, isShift);
+ final boolean hasShortCutKey = mVoiceKeyEnabled && (isSymbols != mVoiceKeyOnMain);
+ return new KeyboardId(mResources.getResourceEntryName(xmlId), xmlId, mLocale,
+ mConf.orientation, mMetrics.widthPixels, mMode, mEditorInfo, mHasSettingsKey,
+ mF2KeyMode, mNoSettingsKey, mVoiceKeyEnabled, hasShortCutKey);
+ }
+
+ private static int getXmlId(int mode, boolean isSymbols, boolean isShift) {
+ switch (mode) {
+ case KeyboardId.MODE_PHONE:
+ return (isSymbols && isShift) ? R.xml.kbd_phone_shift : R.xml.kbd_phone;
+ case KeyboardId.MODE_NUMBER:
+ return R.xml.kbd_number;
+ default:
+ if (isSymbols) {
+ return isShift ? R.xml.kbd_symbols_shift : R.xml.kbd_symbols;
+ }
+ return R.xml.kbd_qwerty;
+ }
+ }
+
+ private static int getF2KeyMode(boolean settingsKeyEnabled, boolean noSettingsKey) {
+ if (noSettingsKey) {
+ // Never shows the Settings key
+ return KeyboardId.F2KEY_MODE_SHORTCUT_IME;
+ }
+
+ if (settingsKeyEnabled) {
+ return KeyboardId.F2KEY_MODE_SETTINGS;
+ } else {
+ // It should be alright to fall back to the Settings key on 7-inch layouts
+ // even when the Settings key is not explicitly enabled.
+ return KeyboardId.F2KEY_MODE_SHORTCUT_IME_OR_SETTINGS;
+ }
+ }
+ }
+}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index cae0edd9f..165e9aec7 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -18,9 +18,7 @@ package com.android.inputmethod.keyboard;
import android.content.Context;
import android.content.SharedPreferences;
-import android.content.res.Configuration;
import android.content.res.Resources;
-import android.util.DisplayMetrics;
import android.util.Log;
import android.view.ContextThemeWrapper;
import android.view.InflateException;
@@ -65,16 +63,12 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions,
private InputView mCurrentInputView;
private LatinKeyboardView mKeyboardView;
private LatinIME mInputMethodService;
- private String mPackageName;
private Resources mResources;
private KeyboardState mState;
- private KeyboardId mMainKeyboardId;
- private KeyboardId mSymbolsKeyboardId;
- private KeyboardId mSymbolsShiftedKeyboardId;
+ private KeyboardSet mKeyboardSet;
- private KeyboardId mCurrentId;
private final HashMap<KeyboardId, SoftReference<LatinKeyboard>> mKeyboardCache =
new HashMap<KeyboardId, SoftReference<LatinKeyboard>>();
@@ -101,7 +95,6 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions,
private void initInternal(LatinIME ims, SharedPreferences prefs) {
mInputMethodService = ims;
- mPackageName = ims.getPackageName();
mResources = ims.getResources();
mPrefs = prefs;
mSubtypeSwitcher = SubtypeSwitcher.getInstance();
@@ -134,25 +127,24 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions,
public void loadKeyboard(EditorInfo editorInfo, SettingsValues settingsValues) {
try {
- mMainKeyboardId = getKeyboardId(editorInfo, false, false, settingsValues);
- mSymbolsKeyboardId = getKeyboardId(editorInfo, true, false, settingsValues);
- mSymbolsShiftedKeyboardId = getKeyboardId(editorInfo, true, true, settingsValues);
+ mKeyboardSet = new KeyboardSet.Builder(mInputMethodService, editorInfo, settingsValues)
+ .build();
mState.onLoadKeyboard(mResources.getString(R.string.layout_switch_back_symbols),
hasDistinctMultitouch());
// TODO: Should get rid of this special case handling for Phone Number layouts once we
// have separate layouts with unique KeyboardIds for alphabet and alphabet-shifted
// respectively.
- if (mMainKeyboardId.isPhoneKeyboard()) {
+ if (mKeyboardSet.mAlphabetId.isPhoneKeyboard()) {
mState.onToggleAlphabetAndSymbols();
}
} catch (RuntimeException e) {
- Log.w(TAG, "loading keyboard failed: " + mMainKeyboardId, e);
- LatinImeLogger.logOnException(mMainKeyboardId.toString(), e);
+ Log.w(TAG, "loading keyboard failed: " + mKeyboardSet.mAlphabetId, e);
+ LatinImeLogger.logOnException(mKeyboardSet.mAlphabetId.toString(), e);
}
}
public void saveKeyboardState() {
- if (mCurrentId != null) {
+ if (isKeyboardAvailable()) {
mState.onSaveKeyboardState();
}
}
@@ -169,8 +161,6 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions,
final Keyboard oldKeyboard = mKeyboardView.getKeyboard();
mKeyboardView.setKeyboard(keyboard);
mCurrentInputView.setKeyboardGeometry(keyboard.mTopPadding);
- mCurrentId = keyboard.mId;
- updateShiftLockState(keyboard);
mKeyboardView.setKeyPreviewPopupEnabled(
SettingsValues.isKeyPreviewPopupEnabled(mPrefs, mResources),
SettingsValues.getKeyPreviewPopupDismissDelay(mPrefs, mResources));
@@ -180,19 +170,7 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions,
updateShiftState();
}
- private void updateShiftLockState(Keyboard keyboard) {
- if (mCurrentId.equals(mSymbolsShiftedKeyboardId)) {
- // Symbol keyboard may have an ALT key that has a caps lock style indicator (a.k.a.
- // sticky shift key). To show or dismiss the indicator, we need to call setShiftLocked()
- // that takes care of the current keyboard having such ALT key or not.
- keyboard.setShiftLocked(keyboard.hasShiftLockKey());
- } else if (mCurrentId.equals(mSymbolsKeyboardId)) {
- // Symbol keyboard has an ALT key that has a caps lock style indicator. To disable the
- // indicator, we need to call setShiftLocked(false).
- keyboard.setShiftLocked(false);
- }
- }
-
+ // TODO: Move this method to KeyboardSet.
private LatinKeyboard getKeyboard(KeyboardId id) {
final SoftReference<LatinKeyboard> ref = mKeyboardCache.get(id);
LatinKeyboard keyboard = (ref == null) ? null : ref.get();
@@ -231,55 +209,6 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions,
return keyboard;
}
- private KeyboardId getKeyboardId(EditorInfo editorInfo, final boolean isSymbols,
- final boolean isShift, SettingsValues settingsValues) {
- final int mode = Utils.getKeyboardMode(editorInfo);
- final int xmlId;
- switch (mode) {
- case KeyboardId.MODE_PHONE:
- xmlId = (isSymbols && isShift) ? R.xml.kbd_phone_shift : R.xml.kbd_phone;
- break;
- case KeyboardId.MODE_NUMBER:
- xmlId = R.xml.kbd_number;
- break;
- default:
- if (isSymbols) {
- xmlId = isShift ? R.xml.kbd_symbols_shift : R.xml.kbd_symbols;
- } else {
- xmlId = R.xml.kbd_qwerty;
- }
- break;
- }
-
- final boolean settingsKeyEnabled = settingsValues.isSettingsKeyEnabled();
- @SuppressWarnings("deprecation")
- final boolean noMicrophone = Utils.inPrivateImeOptions(
- mPackageName, LatinIME.IME_OPTION_NO_MICROPHONE, editorInfo)
- || Utils.inPrivateImeOptions(
- null, LatinIME.IME_OPTION_NO_MICROPHONE_COMPAT, editorInfo);
- final boolean voiceKeyEnabled = settingsValues.isVoiceKeyEnabled(editorInfo)
- && !noMicrophone;
- final boolean voiceKeyOnMain = settingsValues.isVoiceKeyOnMain();
- final boolean noSettingsKey = Utils.inPrivateImeOptions(
- mPackageName, LatinIME.IME_OPTION_NO_SETTINGS_KEY, editorInfo);
- final boolean hasSettingsKey = settingsKeyEnabled && !noSettingsKey;
- final int f2KeyMode = getF2KeyMode(settingsKeyEnabled, noSettingsKey);
- final boolean hasShortcutKey = voiceKeyEnabled && (isSymbols != voiceKeyOnMain);
- final boolean forceAscii = Utils.inPrivateImeOptions(
- mPackageName, LatinIME.IME_OPTION_FORCE_ASCII, editorInfo);
- final boolean asciiCapable = mSubtypeSwitcher.currentSubtypeContainsExtraValueKey(
- LatinIME.SUBTYPE_EXTRA_VALUE_ASCII_CAPABLE);
- final Locale locale = (forceAscii && !asciiCapable)
- ? Locale.US : mSubtypeSwitcher.getInputLocale();
- final Configuration conf = mResources.getConfiguration();
- final DisplayMetrics dm = mResources.getDisplayMetrics();
-
- return new KeyboardId(
- mResources.getResourceEntryName(xmlId), xmlId, locale, conf.orientation,
- dm.widthPixels, mode, editorInfo, hasSettingsKey, f2KeyMode, noSettingsKey,
- voiceKeyEnabled, hasShortcutKey);
- }
-
public boolean isAlphabetMode() {
final Keyboard keyboard = getLatinKeyboard();
return keyboard != null && keyboard.mId.isAlphabetKeyboard();
@@ -409,19 +338,25 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions,
// Implements {@link KeyboardState.SwitchActions}.
@Override
public void setSymbolsKeyboard() {
- setKeyboard(getKeyboard(mSymbolsKeyboardId));
+ setKeyboard(getKeyboard(mKeyboardSet.mSymbolsId));
}
// Implements {@link KeyboardState.SwitchActions}.
@Override
public void setAlphabetKeyboard() {
- setKeyboard(getKeyboard(mMainKeyboardId));
+ setKeyboard(getKeyboard(mKeyboardSet.mAlphabetId));
}
// Implements {@link KeyboardState.SwitchActions}.
@Override
public void setSymbolsShiftedKeyboard() {
- setKeyboard(getKeyboard(mSymbolsShiftedKeyboardId));
+ final Keyboard keyboard = getKeyboard(mKeyboardSet.mSymbolsShiftedId);
+ setKeyboard(keyboard);
+ // TODO: Remove this logic once we introduce initial keyboard shift state attribute.
+ // Symbol shift keyboard may have a shift key that has a caps lock style indicator (a.k.a.
+ // sticky shift key). To show or dismiss the indicator, we need to call setShiftLocked()
+ // that takes care of the current keyboard having such shift key or not.
+ keyboard.setShiftLocked(keyboard.hasShiftLockKey());
}
public boolean isInMomentarySwitchState() {
@@ -528,19 +463,4 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions,
}
}
}
-
- private static int getF2KeyMode(boolean settingsKeyEnabled, boolean noSettingsKey) {
- if (noSettingsKey) {
- // Never shows the Settings key
- return KeyboardId.F2KEY_MODE_SHORTCUT_IME;
- }
-
- if (settingsKeyEnabled) {
- return KeyboardId.F2KEY_MODE_SETTINGS;
- } else {
- // It should be alright to fall back to the Settings key on 7-inch layouts
- // even when the Settings key is not explicitly enabled.
- return KeyboardId.F2KEY_MODE_SHORTCUT_IME_OR_SETTINGS;
- }
- }
}
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 6ea642c92..2d7eed7ab 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -203,7 +203,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
private boolean mApplicationSpecifiedCompletionOn;
private WordComposer mWordComposer = new WordComposer();
- private CharSequence mBestWord;
private boolean mHasUncommittedTypedChars;
private int mCorrectionMode;
@@ -1019,7 +1018,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
.setHasMinimalSuggestion(false);
// When in fullscreen mode, show completions generated by the application
setSuggestions(builder.build());
- mBestWord = null;
+ mWordComposer.deleteAutoCorrection();
setSuggestionStripShown(true);
}
}
@@ -1386,13 +1385,20 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
private void handleBackspace(final int spaceState) {
if (mVoiceProxy.logAndRevertVoiceInput()) return;
-
final InputConnection ic = getCurrentInputConnection();
if (ic == null) return;
ic.beginBatchEdit();
+ handleBackspaceWhileInBatchEdit(spaceState, ic);
+ ic.endBatchEdit();
+ }
+ // "ic" may not be null.
+ private void handleBackspaceWhileInBatchEdit(final int spaceState, final InputConnection ic) {
mVoiceProxy.handleBackspace();
+ // In many cases, we may have to put the keyboard in auto-shift state again.
+ mHandler.postUpdateShiftKeyState();
+
if (mEnteredText != null && sameAsTextBeforeCursor(ic, mEnteredText)) {
// Cancel multi-character input: remove the text we just entered.
// This is triggered on backspace after a key that inputs multiple characters,
@@ -1401,27 +1407,18 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
// If we have mEnteredText, then we know that mHasUncommittedTypedChars == false.
// In addition we know that spaceState is false, and that we should not be
// reverting any autocorrect at this point. So we can safely return.
- ic.endBatchEdit();
return;
}
- final boolean deleteChar = !mHasUncommittedTypedChars;
if (mHasUncommittedTypedChars) {
final int length = mWordComposer.size();
if (length > 0) {
mWordComposer.deleteLast();
- final CharSequence textWithUnderline =
- mComposingStateManager.isAutoCorrectionIndicatorOn()
- ? SuggestionSpanUtils.getTextWithAutoCorrectionIndicatorUnderline(
- this, mWordComposer.getTypedWord())
- : mWordComposer.getTypedWord();
- ic.setComposingText(textWithUnderline, 1);
+ ic.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
if (mWordComposer.size() == 0) {
mHasUncommittedTypedChars = false;
- }
- if (1 == length) {
- // 1 == length means we are about to erase the last character of the word,
- // so we can show bigrams.
+ // Remaining size equals zero means we just erased the last character of the
+ // word, so we can show bigrams.
mHandler.postUpdateBigramPredictions();
} else {
// length > 1, so we still have letters to deduce a suggestion from.
@@ -1430,42 +1427,29 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
} else {
ic.deleteSurroundingText(1, 0);
}
- // If we deleted the last remaining char of a word, we may have to put the keyboard
- // in auto-shift state again.
- mHandler.postUpdateShiftKeyState();
- // If we had uncommitted chars then we know it's not time to revert any auto-correct
- // and that spaceState is NONE.
- ic.endBatchEdit();
- return;
- }
- mHandler.postUpdateShiftKeyState();
-
- if (null != mWordSavedForAutoCorrectCancellation) {
- Utils.Stats.onAutoCorrectionCancellation();
- cancelAutoCorrect(ic);
- mWordSavedForAutoCorrectCancellation = null;
- ic.endBatchEdit();
- return;
} else {
- mWordSavedForAutoCorrectCancellation = null;
- }
-
- if (SPACE_STATE_DOUBLE == spaceState) {
- if (revertDoubleSpace(ic)) {
- ic.endBatchEdit();
- // No need to reset mSpaceState, it has already be done (that's why we
- // receive it as a parameter)
+ if (null != mWordSavedForAutoCorrectCancellation) {
+ Utils.Stats.onAutoCorrectionCancellation();
+ cancelAutoCorrect(ic);
+ mWordSavedForAutoCorrectCancellation = null;
return;
+ } else {
+ mWordSavedForAutoCorrectCancellation = null;
}
- } else if (SPACE_STATE_SWAP_PUNCTUATION == spaceState) {
- if (revertSwapPunctuation(ic)) {
- ic.endBatchEdit();
- // Likewise
- return;
+
+ if (SPACE_STATE_DOUBLE == spaceState) {
+ if (revertDoubleSpace(ic)) {
+ // No need to reset mSpaceState, it has already be done (that's why we
+ // receive it as a parameter)
+ return;
+ }
+ } else if (SPACE_STATE_SWAP_PUNCTUATION == spaceState) {
+ if (revertSwapPunctuation(ic)) {
+ // Likewise
+ return;
+ }
}
- }
- if (deleteChar) {
if (mSuggestionsView != null && mSuggestionsView.dismissAddToDictionaryHint()) {
// Go back to the suggestion mode if the user canceled the
// "Touch again to save".
@@ -1483,7 +1467,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
restartSuggestionsOnWordBeforeCursorIfAtEndOfWord(ic);
}
}
- ic.endBatchEdit();
}
private void handleTab() {
@@ -1512,12 +1495,19 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
private void handleCharacter(final int primaryCode, final int[] keyCodes, final int x,
final int y, final int spaceState) {
mVoiceProxy.handleCharacter();
-
final InputConnection ic = getCurrentInputConnection();
- if (ic != null) ic.beginBatchEdit();
+ if (null != ic) ic.beginBatchEdit();
+ // TODO: if ic is null, does it make any sense to call this?
+ handleCharacterWhileInBatchEdit(primaryCode, keyCodes, x, y, spaceState, ic);
+ if (null != ic) ic.endBatchEdit();
+ }
+
+ // "ic" may be null without this crashing, but the behavior will be really strange
+ private void handleCharacterWhileInBatchEdit(final int primaryCode, final int[] keyCodes,
+ final int x, final int y, final int spaceState, final InputConnection ic) {
if (SPACE_STATE_MAGIC == spaceState
&& mSettingsValues.isMagicSpaceStripper(primaryCode)) {
- removeTrailingSpaceWhileInBatchEdit(ic);
+ if (null != ic) removeTrailingSpaceWhileInBatchEdit(ic);
}
int code = primaryCode;
@@ -1536,7 +1526,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
if (switcher.isShiftedOrShiftLocked()) {
if (keyCodes == null || keyCodes[0] < Character.MIN_CODE_POINT
|| keyCodes[0] > Character.MAX_CODE_POINT) {
- if (null != ic) ic.endBatchEdit();
return;
}
code = keyCodes[0];
@@ -1550,7 +1539,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
} else {
// Some keys, such as [eszett], have upper case as multi-characters.
onTextInput(upperCaseString);
- if (null != ic) ic.endBatchEdit();
return;
}
}
@@ -1563,12 +1551,7 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
mWordComposer.setAutoCapitalized(getCurrentAutoCapsState());
mComposingStateManager.onStartComposingText();
}
- final CharSequence textWithUnderline =
- mComposingStateManager.isAutoCorrectionIndicatorOn()
- ? SuggestionSpanUtils.getTextWithAutoCorrectionIndicatorUnderline(
- this, mWordComposer.getTypedWord())
- : mWordComposer.getTypedWord();
- ic.setComposingText(textWithUnderline, 1);
+ ic.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
}
mHandler.postUpdateSuggestions();
} else {
@@ -1585,7 +1568,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
} else {
Utils.Stats.onNonSeparator((char)code, x, y);
}
- if (null != ic) ic.endBatchEdit();
}
private void handleSeparator(final int primaryCode, final int x, final int y,
@@ -1670,10 +1652,18 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
Utils.Stats.onSeparator((char)primaryCode, x, y);
if (pickedDefault) {
- CharSequence typedWord = mWordComposer.getTypedWord();
- if (!TextUtils.isEmpty(typedWord) && !typedWord.equals(mBestWord)) {
+ final CharSequence autoCorrection = mWordComposer.getAutoCorrectionOrNull();
+ final String typedWord = mWordComposer.getTypedWord();
+ if (TextUtils.isEmpty(typedWord)) {
+ throw new RuntimeException("We have non-committed chars but the typed word "
+ + "is empty? Impossible! I must commit suicide.");
+ }
+ if (!typedWord.equals(autoCorrection)) {
+ // TODO: if the commitCorrection method is not supported by the platform
+ // this will do nothing and the correction will not be committed at all. What
+ // happens on Froyo/Gingerbread, where this API is not present?
InputConnectionCompatUtils.commitCorrection(
- ic, mLastSelectionEnd - typedWord.length(), typedWord, mBestWord);
+ ic, mLastSelectionEnd - typedWord.length(), typedWord, autoCorrection);
}
}
mKeyboardSwitcher.updateShiftState();
@@ -1682,6 +1672,12 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
}
}
+ private CharSequence getTextWithUnderline(final CharSequence text) {
+ return mComposingStateManager.isAutoCorrectionIndicatorOn()
+ ? SuggestionSpanUtils.getTextWithAutoCorrectionIndicatorUnderline(this, text)
+ : mWordComposer.getTypedWord();
+ }
+
private void handleClose() {
commitTyped(getCurrentInputConnection());
mVoiceProxy.handleClose();
@@ -1755,18 +1751,21 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
mComposingStateManager.isAutoCorrectionIndicatorOn();
final boolean newAutoCorrectionIndicator = Utils.willAutoCorrect(words);
if (oldAutoCorrectionIndicator != newAutoCorrectionIndicator) {
- if (LatinImeLogger.sDBG) {
+ mComposingStateManager.setAutoCorrectionIndicatorOn(newAutoCorrectionIndicator);
+ if (DEBUG) {
Log.d(TAG, "Flip the indicator. " + oldAutoCorrectionIndicator
+ " -> " + newAutoCorrectionIndicator);
+ if (newAutoCorrectionIndicator
+ != mComposingStateManager.isAutoCorrectionIndicatorOn()) {
+ throw new RuntimeException("Couldn't flip the indicator! We are not "
+ + "composing a word right now.");
+ }
}
- final CharSequence textWithUnderline = newAutoCorrectionIndicator
- ? SuggestionSpanUtils.getTextWithAutoCorrectionIndicatorUnderline(
- this, mWordComposer.getTypedWord())
- : mWordComposer.getTypedWord();
+ final CharSequence textWithUnderline =
+ getTextWithUnderline(mWordComposer.getTypedWord());
if (!TextUtils.isEmpty(textWithUnderline)) {
ic.setComposingText(textWithUnderline, 1);
}
- mComposingStateManager.setAutoCorrectionIndicatorOn(newAutoCorrectionIndicator);
}
}
}
@@ -1859,14 +1858,15 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
setSuggestions(suggestedWords);
if (suggestedWords.size() > 0) {
if (shouldBlockAutoCorrectionBySafetyNet) {
- mBestWord = typedWord;
+ mWordComposer.setAutoCorrection(typedWord);
} else if (suggestedWords.hasAutoCorrectionWord()) {
- mBestWord = suggestedWords.getWord(1);
+ mWordComposer.setAutoCorrection(suggestedWords.getWord(1));
} else {
- mBestWord = typedWord;
+ mWordComposer.setAutoCorrection(typedWord);
}
} else {
- mBestWord = null;
+ // TODO: replace with mWordComposer.deleteAutoCorrection()?
+ mWordComposer.setAutoCorrection(null);
}
setSuggestionStripShown(isSuggestionsStripVisible());
}
@@ -1877,16 +1877,17 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
mHandler.cancelUpdateSuggestions();
updateSuggestions();
}
- if (mBestWord != null && mBestWord.length() > 0) {
- Utils.Stats.onAutoCorrection(mWordComposer.getTypedWord(), mBestWord.toString(),
- separatorCode);
+ final CharSequence autoCorrection = mWordComposer.getAutoCorrectionOrNull();
+ if (autoCorrection != null) {
+ final String typedWord = mWordComposer.getTypedWord();
+ Utils.Stats.onAutoCorrection(typedWord, autoCorrection.toString(), separatorCode);
mExpectingUpdateSelection = true;
- commitBestWord(mBestWord);
- if (!mBestWord.equals(mWordComposer.getTypedWord())) {
- mWordSavedForAutoCorrectCancellation = mBestWord.toString();
+ commitBestWord(autoCorrection);
+ if (!autoCorrection.equals(typedWord)) {
+ mWordSavedForAutoCorrectCancellation = autoCorrection.toString();
}
// Add the word to the user unigram dictionary if it's not a known word
- addToUserUnigramAndBigramDictionaries(mBestWord,
+ addToUserUnigramAndBigramDictionaries(autoCorrection,
UserUnigramDictionary.FREQUENCY_FOR_TYPED);
return true;
}
@@ -2165,8 +2166,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
private void restartSuggestionsOnWordBeforeCursor(final InputConnection ic,
final CharSequence word) {
mWordComposer.setComposingWord(word, mKeyboardSwitcher.getLatinKeyboard());
- // mBestWord will be set appropriately by updateSuggestions() called by the handler
- mBestWord = null;
mHasUncommittedTypedChars = true;
mComposingStateManager.onStartComposingText();
ic.deleteSurroundingText(word.length(), 0);
@@ -2237,8 +2236,11 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
// Here we test whether we indeed have a period and a space before us. This should not
// be needed, but it's there just in case something went wrong.
final CharSequence textBeforeCursor = ic.getTextBeforeCursor(2, 0);
- if (!". ".equals(textBeforeCursor))
- return false;
+ if (!". ".equals(textBeforeCursor)) {
+ // We should not have come here if we aren't just after a ". ".
+ throw new RuntimeException("Tried to revert double-space combo but we didn't find "
+ + "\". \" just before the cursor.");
+ }
ic.beginBatchEdit();
ic.deleteSurroundingText(2, 0);
ic.commitText(" ", 1);
@@ -2252,8 +2254,11 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar
final CharSequence textBeforeCursor = ic.getTextBeforeCursor(2, 0);
// NOTE: This does not work with surrogate pairs. Hopefully when the keyboard is able to
// enter surrogate pairs this code will have been removed.
- if (Keyboard.CODE_SPACE != textBeforeCursor.charAt(1))
- return false;
+ if (Keyboard.CODE_SPACE != textBeforeCursor.charAt(1)) {
+ // We should not have come here if the text before the cursor is not a space.
+ throw new RuntimeException("Tried to revert a swap of punctiation but we didn't "
+ + "find a space just before the cursor.");
+ }
ic.beginBatchEdit();
ic.deleteSurroundingText(2, 0);
ic.commitText(" " + textBeforeCursor.subSequence(0, 1), 1);
diff --git a/java/src/com/android/inputmethod/latin/Utils.java b/java/src/com/android/inputmethod/latin/Utils.java
index de29e8f74..64f4d058b 100644
--- a/java/src/com/android/inputmethod/latin/Utils.java
+++ b/java/src/com/android/inputmethod/latin/Utils.java
@@ -22,6 +22,7 @@ import com.android.inputmethod.compat.InputMethodSubtypeCompatWrapper;
import com.android.inputmethod.compat.InputTypeCompatUtils;
import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.KeyboardId;
+import com.android.inputmethod.latin.define.JniLibName;
import android.content.Context;
import android.content.Intent;
@@ -691,9 +692,13 @@ public class Utils {
public static void loadNativeLibrary() {
try {
- System.loadLibrary("jni_latinime");
+ System.loadLibrary(JniLibName.JNI_LIB_NAME);
} catch (UnsatisfiedLinkError ule) {
- Log.e(TAG, "Could not load native library jni_latinime");
+ Log.e(TAG, "Could not load native library " + JniLibName.JNI_LIB_NAME);
+ if (LatinImeLogger.sDBG) {
+ throw new RuntimeException(
+ "Could not load native library " + JniLibName.JNI_LIB_NAME);
+ }
}
}
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
index dfb00c8ab..fcaf81cd5 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -41,6 +41,8 @@ public class WordComposer {
private int[] mYCoordinates;
private StringBuilder mTypedWord;
+ // An auto-correction for this word out of the dictionary.
+ private CharSequence mAutoCorrection;
private int mCapsCount;
@@ -60,6 +62,7 @@ public class WordComposer {
mXCoordinates = new int[N];
mYCoordinates = new int[N];
mTrailingSingleQuotesCount = 0;
+ mAutoCorrection = null;
}
public WordComposer(WordComposer source) {
@@ -75,6 +78,7 @@ public class WordComposer {
mIsFirstCharCapitalized = source.mIsFirstCharCapitalized;
mAutoCapitalized = source.mAutoCapitalized;
mTrailingSingleQuotesCount = source.mTrailingSingleQuotesCount;
+ mAutoCorrection = null;
}
/**
@@ -86,6 +90,7 @@ public class WordComposer {
mCapsCount = 0;
mIsFirstCharCapitalized = false;
mTrailingSingleQuotesCount = 0;
+ mAutoCorrection = null;
}
/**
@@ -140,6 +145,7 @@ public class WordComposer {
} else {
mTrailingSingleQuotesCount = 0;
}
+ mAutoCorrection = null;
}
/**
@@ -173,6 +179,7 @@ public class WordComposer {
int codePoint = word.charAt(i);
addKeyInfo(codePoint, keyboard, keyDetector);
}
+ mAutoCorrection = null;
}
/**
@@ -224,11 +231,12 @@ public class WordComposer {
++mTrailingSingleQuotesCount;
}
}
+ mAutoCorrection = null;
}
/**
* Returns the word as it was typed, without any correction applied.
- * @return the word that was typed so far
+ * @return the word that was typed so far. Never returns null.
*/
public String getTypedWord() {
return mTypedWord.toString();
@@ -277,4 +285,25 @@ public class WordComposer {
public boolean isAutoCapitalized() {
return mAutoCapitalized;
}
+
+ /**
+ * Sets the auto-correction for this word.
+ */
+ public void setAutoCorrection(final CharSequence correction) {
+ mAutoCorrection = correction;
+ }
+
+ /**
+ * Remove any auto-correction that may have been set.
+ */
+ public void deleteAutoCorrection() {
+ mAutoCorrection = null;
+ }
+
+ /**
+ * @return the auto-correction for this world, or null if none.
+ */
+ public CharSequence getAutoCorrectionOrNull() {
+ return mAutoCorrection;
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/define/JniLibName.java b/java/src/com/android/inputmethod/latin/define/JniLibName.java
new file mode 100644
index 000000000..3e94a3c07
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/define/JniLibName.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.inputmethod.latin.define;
+
+public class JniLibName {
+ public static final String JNI_LIB_NAME = "jni_latinime";
+}