diff --git a/java/res/xml-sw600dp/key_styles_common.xml b/java/res/xml-sw600dp/key_styles_common.xml index a1b2eb475..bf2e76a6b 100644 --- a/java/res/xml-sw600dp/key_styles_common.xml +++ b/java/res/xml-sw600dp/key_styles_common.xml @@ -133,6 +133,17 @@ latin:keyIconPreview="!icon/tab_key_preview" latin:backgroundType="functional" /> + + + + + + 0) { + showSuggestions(suggestions, typedWord); + } else { + clearSuggestions(); + } + } + + private SuggestedWords updateSuggestions(final CharSequence typedWord) { // TODO: May need a better way of retrieving previous word final CharSequence prevWord = mConnection.getPreviousWord(mCurrentSettings.mWordSeparators); - final CharSequence typedWord = mWordComposer.getTypedWord(); // getSuggestedWords handles gracefully a null value of prevWord final SuggestedWords suggestedWords = mSuggest.getSuggestedWords(mWordComposer, prevWord, mKeyboardSwitcher.getKeyboard().getProximityInfo(), @@ -1741,7 +1710,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (suggestedWords.size() > 1 || typedWord.length() == 1 || !suggestedWords.mTypedWordValid || mSuggestionsView.isShowingAddToDictionaryHint()) { - showSuggestions(suggestedWords, typedWord); + return suggestedWords; } else { SuggestedWords previousSuggestions = mSuggestionsView.getSuggestions(); if (previousSuggestions == mCurrentSettings.mSuggestPuncList) { @@ -1750,18 +1719,18 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final ArrayList typedWordAndPreviousSuggestions = SuggestedWords.getTypedWordAndPreviousSuggestions( typedWord, previousSuggestions); - final SuggestedWords obsoleteSuggestedWords = - new SuggestedWords(typedWordAndPreviousSuggestions, + return new SuggestedWords(typedWordAndPreviousSuggestions, false /* typedWordValid */, false /* hasAutoCorrectionCandidate */, false /* isPunctuationSuggestions */, true /* isObsoleteSuggestions */, false /* isPrediction */); - showSuggestions(obsoleteSuggestedWords, typedWord); } } - public void showSuggestions(final SuggestedWords suggestedWords, final CharSequence typedWord) { + private void showSuggestions(final SuggestedWords suggestedWords, + final CharSequence typedWord) { + // This method is only ever called by updateSuggestions or updateBigramPredictions. final CharSequence autoCorrection; if (suggestedWords.size() > 0) { if (suggestedWords.mWillAutoCorrect) { @@ -1782,7 +1751,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private void commitCurrentAutoCorrection(final int separatorCodePoint) { // Complete any pending suggestions query first if (mHandler.hasPendingUpdateSuggestions()) { - mHandler.cancelUpdateSuggestions(); + mHandler.cancelUpdateSuggestionStrip(); updateSuggestionsOrPredictions(false /* isPredictions */); } final CharSequence autoCorrection = mWordComposer.getAutoCorrectionOrNull(); @@ -1847,7 +1816,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mKeyboardSwitcher.updateShiftState(); resetComposingState(true /* alsoResetLastComposedWord */); final CompletionInfo completionInfo = mApplicationSpecifiedCompletions[index]; - mConnection.beginBatchEdit(getCurrentInputConnection()); + mConnection.beginBatchEdit(); mConnection.commitCompletion(completionInfo); mConnection.endBatchEdit(); if (ProductionFlag.IS_EXPERIMENTAL) { @@ -1890,20 +1859,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen Utils.Stats.onSeparator((char)Keyboard.CODE_SPACE, WordComposer.NOT_A_COORDINATE, WordComposer.NOT_A_COORDINATE); - if (!showingAddToDictionaryHint) { - // If we're not showing the "Touch again to save", then show corrections again. - // In case the cursor position doesn't change, make sure we show the suggestions again. - updateSuggestionsOrPredictions(true /* isPredictions */); - // Updating the predictions right away may be slow and feel unresponsive on slower - // terminals. On the other hand if we just postUpdateBigramPredictions() it will - // take a noticeable delay to update them which may feel uneasy. + if (showingAddToDictionaryHint && mIsUserDictionaryAvailable) { + mSuggestionsView.showAddToDictionaryHint(suggestion, mCurrentSettings.mHintToSaveText); } else { - if (mIsUserDictionaryAvailable) { - mSuggestionsView.showAddToDictionaryHint( - suggestion, mCurrentSettings.mHintToSaveText); - } else { - mHandler.postUpdateSuggestions(); - } + // If we're not showing the "Touch again to save", then show predictions. + mHandler.postUpdateBigramPredictions(); } } @@ -1928,44 +1888,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen separatorCode, prevWord); } - public void updateBigramPredictions() { - mHandler.cancelUpdateSuggestions(); - mHandler.cancelUpdateBigramPredictions(); - - if (mSuggest == null || !mCurrentSettings.isSuggestionsRequested(mDisplayOrientation)) { - if (mWordComposer.isComposingWord()) { - Log.w(TAG, "Called updateBigramPredictions but suggestions were not requested!"); - mWordComposer.setAutoCorrection(mWordComposer.getTypedWord()); - } - return; - } - - if (!mCurrentSettings.mBigramPredictionEnabled) { - setPunctuationSuggestions(); - return; - } - - final SuggestedWords suggestedWords; - if (mCurrentSettings.mCorrectionEnabled) { - final CharSequence prevWord = mConnection.getThisWord(mCurrentSettings.mWordSeparators); - if (!TextUtils.isEmpty(prevWord)) { - suggestedWords = mSuggest.getSuggestedWords(mWordComposer, - prevWord, mKeyboardSwitcher.getKeyboard().getProximityInfo(), - mCurrentSettings.mCorrectionEnabled, true); - } else { - suggestedWords = null; - } - } else { - suggestedWords = null; - } - - if (null != suggestedWords && suggestedWords.size() > 0) { - // Explicitly supply an empty typed word (the no-second-arg version of - // showSuggestions will retrieve the word near the cursor, we don't want that here) - showSuggestions(suggestedWords, ""); - } else { - clearSuggestions(); - } + private SuggestedWords updateBigramPredictions(final CharSequence typedWord) { + final CharSequence prevWord = mConnection.getThisWord(mCurrentSettings.mWordSeparators); + return mSuggest.getSuggestedWords(mWordComposer, + prevWord, mKeyboardSwitcher.getKeyboard().getProximityInfo(), + mCurrentSettings.mCorrectionEnabled, true); } public void setPunctuationSuggestions() { @@ -2061,26 +1988,19 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mUserHistoryDictionary.cancelAddingUserHistory( previousWord.toString(), committedWord.toString()); } - 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); - mConnection.setComposingText(originallyTypedWord, 1); - } else { - mConnection.commitText(originallyTypedWord, 1); - // Re-insert the separator - sendKeyCodePoint(mLastComposedWord.mSeparatorCode); - Utils.Stats.onSeparator(mLastComposedWord.mSeparatorCode, WordComposer.NOT_A_COORDINATE, - WordComposer.NOT_A_COORDINATE); - if (ProductionFlag.IS_EXPERIMENTAL) { - ResearchLogger.latinIME_revertCommit(originallyTypedWord); - } - // Don't restart suggestion yet. We'll restart if the user deletes the - // separator. + mConnection.commitText(originallyTypedWord, 1); + // Re-insert the separator + sendKeyCodePoint(mLastComposedWord.mSeparatorCode); + Utils.Stats.onSeparator(mLastComposedWord.mSeparatorCode, WordComposer.NOT_A_COORDINATE, + WordComposer.NOT_A_COORDINATE); + if (ProductionFlag.IS_EXPERIMENTAL) { + ResearchLogger.latinIME_revertCommit(originallyTypedWord); } + // Don't restart suggestion yet. We'll restart if the user deletes the + // separator. mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD; - mHandler.cancelUpdateBigramPredictions(); - mHandler.postUpdateSuggestions(); + // We have a separator between the word and the cursor: we should show predictions. + mHandler.postUpdateBigramPredictions(); } public boolean isWordSeparator(int code) { diff --git a/java/src/com/android/inputmethod/latin/ResearchLogger.java b/java/src/com/android/inputmethod/latin/ResearchLogger.java index e83d7c84a..2de0194fd 100644 --- a/java/src/com/android/inputmethod/latin/ResearchLogger.java +++ b/java/src/com/android/inputmethod/latin/ResearchLogger.java @@ -933,13 +933,6 @@ public class ResearchLogger implements SharedPreferences.OnSharedPreferenceChang EVENTKEYS_NULLVALUES); } - private static final String[] EVENTKEYS_LATINIME_SWITCHTOKEYBOARDVIEW = { - "LatinIMESwitchToKeyboardView" - }; - public static void latinIME_switchToKeyboardView() { - getInstance().enqueueEvent(EVENTKEYS_LATINIME_SWITCHTOKEYBOARDVIEW, EVENTKEYS_NULLVALUES); - } - private static final String[] EVENTKEYS_LATINKEYBOARDVIEW_ONLONGPRESS = { "LatinKeyboardViewOnLongPress" }; diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java index 40d327ebb..a37f480b7 100644 --- a/java/src/com/android/inputmethod/latin/RichInputConnection.java +++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java @@ -16,6 +16,7 @@ package com.android.inputmethod.latin; +import android.inputmethodservice.InputMethodService; import android.text.TextUtils; import android.util.Log; import android.view.KeyEvent; @@ -41,16 +42,18 @@ public class RichInputConnection { private static final Pattern spaceRegex = Pattern.compile("\\s+"); private static final int INVALID_CURSOR_POSITION = -1; + private final InputMethodService mParent; InputConnection mIC; int mNestLevel; - public RichInputConnection() { + public RichInputConnection(final InputMethodService parent) { + mParent = parent; mIC = null; mNestLevel = 0; } - public void beginBatchEdit(final InputConnection newInputConnection) { + public void beginBatchEdit() { if (++mNestLevel == 1) { - mIC = newInputConnection; + mIC = mParent.getCurrentInputConnection(); if (null != mIC) mIC.beginBatchEdit(); } else { if (DBG) { @@ -84,16 +87,19 @@ public class RichInputConnection { } public int getCursorCapsMode(final int inputType) { + mIC = mParent.getCurrentInputConnection(); if (null == mIC) return Constants.TextUtils.CAP_MODE_OFF; return mIC.getCursorCapsMode(inputType); } public CharSequence getTextBeforeCursor(final int i, final int j) { + mIC = mParent.getCurrentInputConnection(); if (null != mIC) return mIC.getTextBeforeCursor(i, j); return null; } public CharSequence getTextAfterCursor(final int i, final int j) { + mIC = mParent.getCurrentInputConnection(); if (null != mIC) return mIC.getTextAfterCursor(i, j); return null; } @@ -104,6 +110,7 @@ public class RichInputConnection { } public void performEditorAction(final int actionId) { + mIC = mParent.getCurrentInputConnection(); if (null != mIC) mIC.performEditorAction(actionId); } @@ -133,6 +140,7 @@ public class RichInputConnection { } public CharSequence getPreviousWord(final String sentenceSeperators) { + mIC = mParent.getCurrentInputConnection(); //TODO: Should fix this. This could be slow! if (null == mIC) return null; CharSequence prev = mIC.getTextBeforeCursor(LOOKBACK_CHARACTER_NUM, 0); @@ -194,6 +202,7 @@ public class RichInputConnection { } public CharSequence getThisWord(String sentenceSeperators) { + mIC = mParent.getCurrentInputConnection(); if (null == mIC) return null; final CharSequence prev = mIC.getTextBeforeCursor(LOOKBACK_CHARACTER_NUM, 0); return getThisWord(prev, sentenceSeperators); @@ -233,6 +242,7 @@ public class RichInputConnection { } private int getCursorPosition() { + mIC = mParent.getCurrentInputConnection(); if (null == mIC) return INVALID_CURSOR_POSITION; final ExtractedText extracted = mIC.getExtractedText(new ExtractedTextRequest(), 0); if (extracted == null) { @@ -250,6 +260,7 @@ public class RichInputConnection { * @return a range containing the text surrounding the cursor */ public Range getWordRangeAtCursor(String sep, int additionalPrecedingWordsCount) { + mIC = mParent.getCurrentInputConnection(); if (mIC == null || sep == null) { return null; } diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java index 4c89a6e91..70acdc771 100644 --- a/java/src/com/android/inputmethod/latin/Settings.java +++ b/java/src/com/android/inputmethod/latin/Settings.java @@ -111,6 +111,10 @@ public class Settings extends InputMethodSettingsFragment final Resources res = getResources(); final Context context = getActivity(); + // When we are called from the Settings application but we are not already running, the + // {@link SubtypeLocale} class may not have been initialized. It is safe to call + // {@link SubtypeLocale#init(Context)} multiple times. + SubtypeLocale.init(context); mVoicePreference = (ListPreference) findPreference(PREF_VOICE_MODE); mShowCorrectionSuggestionsPreference = (ListPreference) findPreference(PREF_SHOW_SUGGESTIONS_SETTING); diff --git a/java/src/com/android/inputmethod/latin/SettingsValues.java b/java/src/com/android/inputmethod/latin/SettingsValues.java index aab84fccd..10025daf8 100644 --- a/java/src/com/android/inputmethod/latin/SettingsValues.java +++ b/java/src/com/android/inputmethod/latin/SettingsValues.java @@ -224,10 +224,6 @@ public class SettingsValues { return mInputAttributes.mApplicationSpecifiedCompletionOn; } - public boolean isEditorActionNext() { - return mInputAttributes.mEditorAction == EditorInfo.IME_ACTION_NEXT; - } - public boolean isSuggestionsRequested(final int displayOrientation) { return mInputAttributes.mIsSettingsSuggestionStripOn && (mCorrectionEnabled diff --git a/java/src/com/android/inputmethod/latin/SubtypeLocale.java b/java/src/com/android/inputmethod/latin/SubtypeLocale.java index acc17ef3f..21c9c0d1e 100644 --- a/java/src/com/android/inputmethod/latin/SubtypeLocale.java +++ b/java/src/com/android/inputmethod/latin/SubtypeLocale.java @@ -41,6 +41,7 @@ public class SubtypeLocale { public static final String QWERTY = "qwerty"; public static final int UNKNOWN_KEYBOARD_LAYOUT = R.string.subtype_generic; + private static boolean sInitialized = false; private static String[] sPredefinedKeyboardLayoutSet; // Keyboard layout to its display name map. private static final HashMap sKeyboardLayoutToDisplayNameMap = @@ -69,7 +70,10 @@ public class SubtypeLocale { // Intentional empty constructor for utility class. } - public static void init(Context context) { + // Note that this initialization method can be called multiple times. + public static synchronized void init(Context context) { + if (sInitialized) return; + final Resources res = context.getResources(); final String[] predefinedLayoutSet = res.getStringArray(R.array.predefined_layouts); @@ -109,6 +113,8 @@ public class SubtypeLocale { final String keyboardLayoutSet = keyboardLayoutSetMap[i + 1]; sLocaleAndExtraValueToKeyboardLayoutSetMap.put(key, keyboardLayoutSet); } + + sInitialized = true; } public static String[] getPredefinedKeyboardLayoutSet() { diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionsView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionsView.java index 642a551ce..4d33f4ba5 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionsView.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionsView.java @@ -71,7 +71,7 @@ import java.util.ArrayList; public class SuggestionsView extends RelativeLayout implements OnClickListener, OnLongClickListener { public interface Listener { - public boolean addWordToDictionary(String word); + public boolean addWordToUserDictionary(String word); public void pickSuggestionManually(int index, CharSequence word, int x, int y); } @@ -718,10 +718,6 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener, mPreviewPopup.dismiss(); } - private void addToDictionary(CharSequence word) { - mListener.addWordToDictionary(word.toString()); - } - private final KeyboardActionListener mMoreSuggestionsListener = new KeyboardActionListener.Adapter() { @Override @@ -863,7 +859,7 @@ public class SuggestionsView extends RelativeLayout implements OnClickListener, @Override public void onClick(View view) { if (mParams.isAddToDictionaryShowing(view)) { - addToDictionary(mParams.getAddToDictionaryWord()); + mListener.addWordToUserDictionary(mParams.getAddToDictionaryWord().toString()); clear(); return; } diff --git a/native/jni/Android.mk b/native/jni/Android.mk index 419318513..a7486ae90 100644 --- a/native/jni/Android.mk +++ b/native/jni/Android.mk @@ -68,9 +68,13 @@ endif # FLAG_DO_PROFILE LOCAL_MODULE := libjni_latinime_common_static LOCAL_MODULE_TAGS := optional +ifdef ANDROID_BUILD_TOP # In the platform build system +include external/stlport/libstlport.mk +else # In the unbundled build system LOCAL_NDK_VERSION := 7 LOCAL_SDK_VERSION := 14 LOCAL_NDK_STL_VARIANT := stlport_static +endif include $(BUILD_STATIC_LIBRARY) ###################################### @@ -92,9 +96,13 @@ endif # FLAG_DO_PROFILE LOCAL_MODULE := libjni_latinime LOCAL_MODULE_TAGS := optional +ifdef ANDROID_BUILD_TOP # In the platform build system +LOCAL_STATIC_LIBRARIES += libstlport_static +else # In the unbundled build system LOCAL_NDK_VERSION := 7 LOCAL_SDK_VERSION := 14 LOCAL_NDK_STL_VARIANT := stlport_static +endif include $(BUILD_SHARED_LIBRARY) diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTests.java b/tests/src/com/android/inputmethod/latin/InputLogicTests.java index f1ccfdd1d..7790299b0 100644 --- a/tests/src/com/android/inputmethod/latin/InputLogicTests.java +++ b/tests/src/com/android/inputmethod/latin/InputLogicTests.java @@ -126,6 +126,26 @@ public class InputLogicTests extends InputTestsBase { mTextView.getText().toString()); } + public void testAutoCorrectWithSpaceThenRevert() { + final String STRING_TO_TYPE = "tgis "; + final String EXPECTED_RESULT = "tgis "; + type(STRING_TO_TYPE); + mLatinIME.onUpdateSelection(0, 0, STRING_TO_TYPE.length(), STRING_TO_TYPE.length(), -1, -1); + type(Keyboard.CODE_DELETE); + assertEquals("auto-correct with space then revert", EXPECTED_RESULT, + mTextView.getText().toString()); + } + + public void testAutoCorrectToSelfDoesNotRevert() { + final String STRING_TO_TYPE = "this "; + final String EXPECTED_RESULT = "this"; + type(STRING_TO_TYPE); + mLatinIME.onUpdateSelection(0, 0, STRING_TO_TYPE.length(), STRING_TO_TYPE.length(), -1, -1); + type(Keyboard.CODE_DELETE); + assertEquals("auto-correct with space does not revert", EXPECTED_RESULT, + mTextView.getText().toString()); + } + public void testDoubleSpace() { final String STRING_TO_TYPE = "this "; final String EXPECTED_RESULT = "this. "; diff --git a/tests/src/com/android/inputmethod/latin/RichInputConnectionTests.java b/tests/src/com/android/inputmethod/latin/RichInputConnectionTests.java index 9ce581df8..7bd7b0e5a 100644 --- a/tests/src/com/android/inputmethod/latin/RichInputConnectionTests.java +++ b/tests/src/com/android/inputmethod/latin/RichInputConnectionTests.java @@ -16,6 +16,7 @@ package com.android.inputmethod.latin; +import android.inputmethodservice.InputMethodService; import android.test.AndroidTestCase; import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; @@ -83,6 +84,17 @@ public class RichInputConnectionTests extends AndroidTestCase { } } + private class MockInputMethodService extends InputMethodService { + InputConnection mInputConnection; + public void setInputConnection(final InputConnection inputConnection) { + mInputConnection = inputConnection; + } + @Override + public InputConnection getCurrentInputConnection() { + return mInputConnection; + } + } + /************************** Tests ************************/ /** @@ -122,14 +134,14 @@ public class RichInputConnectionTests extends AndroidTestCase { */ public void testGetWordRangeAtCursor() { ExtractedText et = new ExtractedText(); - final RichInputConnection ic = new RichInputConnection(); - InputConnection mockConnection; - mockConnection = new MockConnection("word wo", "rd", et); + final MockInputMethodService mockInputMethodService = new MockInputMethodService(); + final RichInputConnection ic = new RichInputConnection(mockInputMethodService); + mockInputMethodService.setInputConnection(new MockConnection("word wo", "rd", et)); et.startOffset = 0; et.selectionStart = 7; Range r; - ic.beginBatchEdit(mockConnection); + ic.beginBatchEdit(); // basic case r = ic.getWordRangeAtCursor(" ", 0); assertEquals("word", r.mWord); @@ -140,37 +152,38 @@ public class RichInputConnectionTests extends AndroidTestCase { ic.endBatchEdit(); // tab character instead of space - mockConnection = new MockConnection("one\tword\two", "rd", et); - ic.beginBatchEdit(mockConnection); + mockInputMethodService.setInputConnection(new MockConnection("one\tword\two", "rd", et)); + ic.beginBatchEdit(); r = ic.getWordRangeAtCursor("\t", 1); ic.endBatchEdit(); assertEquals("word\tword", r.mWord); // only one word doesn't go too far - mockConnection = new MockConnection("one\tword\two", "rd", et); - ic.beginBatchEdit(mockConnection); + mockInputMethodService.setInputConnection(new MockConnection("one\tword\two", "rd", et)); + ic.beginBatchEdit(); r = ic.getWordRangeAtCursor("\t", 1); ic.endBatchEdit(); assertEquals("word\tword", r.mWord); // tab or space - mockConnection = new MockConnection("one word\two", "rd", et); - ic.beginBatchEdit(mockConnection); + mockInputMethodService.setInputConnection(new MockConnection("one word\two", "rd", et)); + ic.beginBatchEdit(); r = ic.getWordRangeAtCursor(" \t", 1); ic.endBatchEdit(); assertEquals("word\tword", r.mWord); // tab or space multiword - mockConnection = new MockConnection("one word\two", "rd", et); - ic.beginBatchEdit(mockConnection); + mockInputMethodService.setInputConnection(new MockConnection("one word\two", "rd", et)); + ic.beginBatchEdit(); r = ic.getWordRangeAtCursor(" \t", 2); ic.endBatchEdit(); assertEquals("one word\tword", r.mWord); // splitting on supplementary character final String supplementaryChar = "\uD840\uDC8A"; - mockConnection = new MockConnection("one word" + supplementaryChar + "wo", "rd", et); - ic.beginBatchEdit(mockConnection); + mockInputMethodService.setInputConnection( + new MockConnection("one word" + supplementaryChar + "wo", "rd", et)); + ic.beginBatchEdit(); r = ic.getWordRangeAtCursor(supplementaryChar, 0); ic.endBatchEdit(); assertEquals("word", r.mWord);