diff options
Diffstat (limited to 'java/src/com/android/inputmethod')
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"; +} |