diff options
Diffstat (limited to 'java/src/com/android/inputmethod/latin')
20 files changed, 309 insertions, 211 deletions
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java index 31a892e19..fa301b5a6 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java @@ -18,7 +18,6 @@ package com.android.inputmethod.latin; import android.content.Context; import android.content.SharedPreferences; -import android.content.pm.PackageManager; import android.content.res.AssetFileDescriptor; import android.util.Log; diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 9366abd73..614c14308 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -76,7 +76,7 @@ import com.android.inputmethod.keyboard.MainKeyboardView; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.define.ProductionFlag; import com.android.inputmethod.latin.personalization.PersonalizationDictionaryHelper; -import com.android.inputmethod.latin.personalization.UserHistoryDictionary; +import com.android.inputmethod.latin.personalization.UserHistoryPredictionDictionary; import com.android.inputmethod.latin.settings.Settings; import com.android.inputmethod.latin.settings.SettingsActivity; import com.android.inputmethod.latin.settings.SettingsValues; @@ -169,7 +169,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private boolean mIsMainDictionaryAvailable; private UserBinaryDictionary mUserDictionary; - private UserHistoryDictionary mUserHistoryDictionary; + private UserHistoryPredictionDictionary mUserHistoryPredictionDictionary; private boolean mIsUserDictionaryAvailable; private LastComposedWord mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD; @@ -539,34 +539,32 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final Locale subtypeLocale = mSubtypeSwitcher.getCurrentSubtypeLocale(); final String localeStr = subtypeLocale.toString(); - final ContactsBinaryDictionary oldContactsDictionary; - if (mSuggest != null) { - oldContactsDictionary = mSuggest.getContactsDictionary(); - mSuggest.close(); - } else { - oldContactsDictionary = null; - } - mSuggest = new Suggest(this /* Context */, subtypeLocale, + final Suggest newSuggest = new Suggest(this /* Context */, subtypeLocale, this /* SuggestInitializationListener */); - if (mSettings.getCurrent().mCorrectionEnabled) { - mSuggest.setAutoCorrectionThreshold(mSettings.getCurrent().mAutoCorrectionThreshold); + final SettingsValues settingsValues = mSettings.getCurrent(); + if (settingsValues.mCorrectionEnabled) { + newSuggest.setAutoCorrectionThreshold(settingsValues.mAutoCorrectionThreshold); } mIsMainDictionaryAvailable = DictionaryFactory.isDictionaryAvailable(this, subtypeLocale); if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.getInstance().initSuggest(mSuggest); + ResearchLogger.getInstance().initSuggest(newSuggest); } mUserDictionary = new UserBinaryDictionary(this, localeStr); mIsUserDictionaryAvailable = mUserDictionary.isEnabled(); - mSuggest.setUserDictionary(mUserDictionary); - - resetContactsDictionary(oldContactsDictionary); + newSuggest.setUserDictionary(mUserDictionary); final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); - mUserHistoryDictionary = - PersonalizationDictionaryHelper.getUserHistoryDictionary(this, localeStr, prefs); - mSuggest.setUserHistoryDictionary(mUserHistoryDictionary); + + mUserHistoryPredictionDictionary = PersonalizationDictionaryHelper + .getUserHistoryPredictionDictionary(this, localeStr, prefs); + newSuggest.setUserHistoryPredictionDictionary(mUserHistoryPredictionDictionary); + + final Suggest oldSuggest = mSuggest; + resetContactsDictionary(null != oldSuggest ? oldSuggest.getContactsDictionary() : null); + mSuggest = newSuggest; + if (oldSuggest != null) oldSuggest.close(); } /** @@ -578,8 +576,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen * @param oldContactsDictionary an optional dictionary to use, or null */ private void resetContactsDictionary(final ContactsBinaryDictionary oldContactsDictionary) { + final Suggest suggest = mSuggest; final boolean shouldSetDictionary = - (null != mSuggest && mSettings.getCurrent().mUseContactsDict); + (null != suggest && mSettings.getCurrent().mUseContactsDict); final ContactsBinaryDictionary dictionaryToUse; if (!shouldSetDictionary) { @@ -606,8 +605,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } - if (null != mSuggest) { - mSuggest.setContactsDictionary(dictionaryToUse); + if (null != suggest) { + suggest.setContactsDictionary(dictionaryToUse); } } @@ -619,8 +618,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen @Override public void onDestroy() { - if (mSuggest != null) { - mSuggest.close(); + final Suggest suggest = mSuggest; + if (suggest != null) { + suggest.close(); mSuggest = null; } mSettings.onDestroy(); @@ -714,7 +714,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen super.onStartInputView(editorInfo, restarting); final KeyboardSwitcher switcher = mKeyboardSwitcher; final MainKeyboardView mainKeyboardView = switcher.getMainKeyboardView(); - final SettingsValues currentSettingsValues = mSettings.getCurrent(); + // If we are starting input in a different text field from before, we'll have to reload + // settings, so currentSettingsValues can't be final. + SettingsValues currentSettingsValues = mSettings.getCurrent(); if (editorInfo == null) { Log.e(TAG, "Null EditorInfo in onStartInputView()"); @@ -792,7 +794,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // Note: the following does a round-trip IPC on the main thread: be careful final Locale currentLocale = mSubtypeSwitcher.getCurrentSubtypeLocale(); - if (null != mSuggest && null != currentLocale && !currentLocale.equals(mSuggest.mLocale)) { + final Suggest suggest = mSuggest; + if (null != suggest && null != currentLocale && !currentLocale.equals(suggest.mLocale)) { initSuggest(); } if (mSuggestionStripView != null) { @@ -808,9 +811,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (isDifferentTextField) { mainKeyboardView.closing(); loadSettings(); - // TODO: Need to update currentSettingsValues after loadSettings() - if (mSuggest != null && currentSettingsValues.mCorrectionEnabled) { - mSuggest.setAutoCorrectionThreshold(currentSettingsValues.mAutoCorrectionThreshold); + currentSettingsValues = mSettings.getCurrent(); + + if (suggest != null && currentSettingsValues.mCorrectionEnabled) { + suggest.setAutoCorrectionThreshold(currentSettingsValues.mAutoCorrectionThreshold); } switcher.loadKeyboard(editorInfo, currentSettingsValues); @@ -890,6 +894,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView(); if (mainKeyboardView != null) { mainKeyboardView.cancelAllOngoingEvents(); + mainKeyboardView.deallocateMemory(); } // Remove pending messages related to update suggestions mHandler.cancelUpdateSuggestionStrip(); @@ -1213,10 +1218,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private void resetEntireInputState(final int newCursorPosition) { final boolean shouldFinishComposition = mWordComposer.isComposingWord(); resetComposingState(true /* alsoResetLastComposedWord */); - if (mSettings.getCurrent().mBigramPredictionEnabled) { + final SettingsValues settingsValues = mSettings.getCurrent(); + if (settingsValues.mBigramPredictionEnabled) { clearSuggestionStrip(); } else { - setSuggestedWords(mSettings.getCurrent().mSuggestPuncList, false); + setSuggestedWords(settingsValues.mSuggestPuncList, false); } mConnection.resetCachesUponCursorMove(newCursorPosition, shouldFinishComposition); } @@ -1290,8 +1296,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } private boolean maybeDoubleSpacePeriod() { - if (!mSettings.getCurrent().mCorrectionEnabled) return false; - if (!mSettings.getCurrent().mUseDoubleSpacePeriod) return false; + final SettingsValues settingsValues = mSettings.getCurrent(); + if (!settingsValues.mCorrectionEnabled) return false; + if (!settingsValues.mUseDoubleSpacePeriod) return false; if (!mHandler.isAcceptingDoubleSpacePeriod()) return false; final CharSequence lastThree = mConnection.getTextBeforeCursor(3, 0); if (lastThree != null && lastThree.length() == 3 @@ -1551,12 +1558,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final int spaceState) { mSpaceState = SPACE_STATE_NONE; final boolean didAutoCorrect; - if (mSettings.getCurrent().isWordSeparator(primaryCode)) { + final SettingsValues settingsValues = mSettings.getCurrent(); + if (settingsValues.isWordSeparator(primaryCode)) { didAutoCorrect = handleSeparator(primaryCode, x, y, spaceState); } else { didAutoCorrect = false; if (SPACE_STATE_PHANTOM == spaceState) { - if (mSettings.isInternal()) { + if (settingsValues.mIsInternal) { if (mWordComposer.isComposingWord() && mWordComposer.isBatchMode()) { LatinImeLoggerUtils.onAutoCorrection( "", mWordComposer.getTypedWord(), " ", mWordComposer); @@ -1616,8 +1624,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen BatchInputUpdater.getInstance().onStartBatchInput(this); mHandler.cancelUpdateSuggestionStrip(); mConnection.beginBatchEdit(); + final SettingsValues settingsValues = mSettings.getCurrent(); if (mWordComposer.isComposingWord()) { - if (mSettings.isInternal()) { + if (settingsValues.mIsInternal) { if (mWordComposer.isBatchMode()) { LatinImeLoggerUtils.onAutoCorrection( "", mWordComposer.getTypedWord(), " ", mWordComposer); @@ -1646,7 +1655,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } final int codePointBeforeCursor = mConnection.getCodePointBeforeCursor(); if (Character.isLetterOrDigit(codePointBeforeCursor) - || mSettings.getCurrent().isUsuallyFollowedBySpace(codePointBeforeCursor)) { + || settingsValues.isUsuallyFollowedBySpace(codePointBeforeCursor)) { mSpaceState = SPACE_STATE_PHANTOM; } mConnection.endBatchEdit(); @@ -1866,8 +1875,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mConnection.deleteSurroundingText(1, 0); } } else { + final SettingsValues currentSettings = mSettings.getCurrent(); if (mLastComposedWord.canRevertCommit()) { - if (mSettings.isInternal()) { + if (currentSettings.mIsInternal) { LatinImeLoggerUtils.onAutoCorrectionCancellation(); } revertCommit(); @@ -1944,7 +1954,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } } - if (mSettings.getCurrent().isSuggestionsRequested(mDisplayOrientation)) { + if (currentSettings.isSuggestionsRequested(mDisplayOrientation)) { restartSuggestionsOnWordBeforeCursorIfAtEndOfWord(); } } @@ -1961,8 +1971,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } if ((SPACE_STATE_WEAK == spaceState || SPACE_STATE_SWAP_PUNCTUATION == spaceState) && isFromSuggestionStrip) { - if (mSettings.getCurrent().isUsuallyPrecededBySpace(code)) return false; - if (mSettings.getCurrent().isUsuallyFollowedBySpace(code)) return true; + final SettingsValues currentSettings = mSettings.getCurrent(); + if (currentSettings.isUsuallyPrecededBySpace(code)) return false; + if (currentSettings.isUsuallyFollowedBySpace(code)) return true; mConnection.removeTrailingSpace(); } return false; @@ -1974,8 +1985,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // TODO: remove isWordConnector() and use isUsuallyFollowedBySpace() instead. // See onStartBatchInput() to see how to do it. - if (SPACE_STATE_PHANTOM == spaceState && - !mSettings.getCurrent().isWordConnector(primaryCode)) { + final SettingsValues currentSettings = mSettings.getCurrent(); + if (SPACE_STATE_PHANTOM == spaceState && !currentSettings.isWordConnector(primaryCode)) { if (isComposingWord) { // Sanity check throw new RuntimeException("Should not be composing here"); @@ -1993,9 +2004,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // dozen milliseconds. Avoid calling it as much as possible, since we are on the UI // thread here. if (!isComposingWord && (isAlphabet(primaryCode) - || mSettings.getCurrent().isWordConnector(primaryCode)) - && mSettings.getCurrent().isSuggestionsRequested(mDisplayOrientation) && - !mConnection.isCursorTouchingWord(mSettings.getCurrent())) { + || currentSettings.isWordConnector(primaryCode)) + && currentSettings.isSuggestionsRequested(mDisplayOrientation) && + !mConnection.isCursorTouchingWord(currentSettings)) { // Reset entirely the composing state anyway, then start composing a new word unless // the character is a single quote. The idea here is, single quote is not a // separator and it should be treated as a normal character, except in the first @@ -2038,7 +2049,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (null != mSuggestionStripView) mSuggestionStripView.dismissAddToDictionaryHint(); } mHandler.postUpdateSuggestionStrip(); - if (mSettings.isInternal()) { + if (currentSettings.mIsInternal) { LatinImeLoggerUtils.onNonSeparator((char)primaryCode, x, y); } } @@ -2051,9 +2062,10 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen final CharSequence selectedText = mConnection.getSelectedText(0 /* flags, 0 for no styles */); if (TextUtils.isEmpty(selectedText)) return; // Race condition with the input connection + final SettingsValues currentSettings = mSettings.getCurrent(); mRecapitalizeStatus.initialize(mLastSelectionStart, mLastSelectionEnd, - selectedText.toString(), mSettings.getCurrentLocale(), - mSettings.getWordSeparators()); + selectedText.toString(), currentSettings.mLocale, + currentSettings.mWordSeparators); // We trim leading and trailing whitespace. mRecapitalizeStatus.trim(); // Trimming the object may have changed the length of the string, and we need to @@ -2087,8 +2099,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // first so that we can insert the separator at the current cursor position. resetEntireInputState(mLastSelectionStart); } + final SettingsValues currentSettings = mSettings.getCurrent(); if (mWordComposer.isComposingWord()) { - if (mSettings.getCurrent().mCorrectionEnabled) { + if (currentSettings.mCorrectionEnabled) { // TODO: maybe cache Strings in an <String> sparse array or something commitCurrentAutoCorrection(new String(new int[]{primaryCode}, 0, 1)); didAutoCorrect = true; @@ -2101,7 +2114,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen Constants.SUGGESTION_STRIP_COORDINATE == x); if (SPACE_STATE_PHANTOM == spaceState && - mSettings.getCurrent().isUsuallyPrecededBySpace(primaryCode)) { + currentSettings.isUsuallyPrecededBySpace(primaryCode)) { promotePhantomSpace(); } if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { @@ -2110,7 +2123,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen sendKeyCodePoint(primaryCode); if (Constants.CODE_SPACE == primaryCode) { - if (mSettings.getCurrent().isSuggestionsRequested(mDisplayOrientation)) { + if (currentSettings.isSuggestionsRequested(mDisplayOrientation)) { if (maybeDoubleSpacePeriod()) { mSpaceState = SPACE_STATE_DOUBLE; } else if (!isShowingPunctuationList()) { @@ -2125,7 +2138,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen swapSwapperAndSpace(); mSpaceState = SPACE_STATE_SWAP_PUNCTUATION; } else if (SPACE_STATE_PHANTOM == spaceState - && mSettings.getCurrent().isUsuallyFollowedBySpace(primaryCode)) { + && currentSettings.isUsuallyFollowedBySpace(primaryCode)) { // If we are in phantom space state, and the user presses a separator, we want to // stay in phantom space state so that the next keypress has a chance to add the // space. For example, if I type "Good dat", pick "day" from the suggestion strip @@ -2143,7 +2156,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // already displayed or not, so it's okay. setPunctuationSuggestions(); } - if (mSettings.isInternal()) { + if (currentSettings.mIsInternal) { LatinImeLoggerUtils.onSeparator((char)primaryCode, x, y); } @@ -2176,17 +2189,18 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } private boolean isSuggestionsStripVisible() { + final SettingsValues currentSettings = mSettings.getCurrent(); if (mSuggestionStripView == null) return false; if (mSuggestionStripView.isShowingAddToDictionaryHint()) return true; - if (null == mSettings.getCurrent()) + if (null == currentSettings) return false; - if (!mSettings.getCurrent().isSuggestionStripVisibleInOrientation(mDisplayOrientation)) + if (!currentSettings.isSuggestionStripVisibleInOrientation(mDisplayOrientation)) return false; - if (mSettings.getCurrent().isApplicationSpecifiedCompletionsOn()) + if (currentSettings.isApplicationSpecifiedCompletionsOn()) return true; - return mSettings.getCurrent().isSuggestionsRequested(mDisplayOrientation); + return currentSettings.isSuggestionsRequested(mDisplayOrientation); } private void clearSuggestionStrip() { @@ -2219,10 +2233,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private void updateSuggestionStrip() { mHandler.cancelUpdateSuggestionStrip(); + final SettingsValues currentSettings = mSettings.getCurrent(); // Check if we have a suggestion engine attached. if (mSuggest == null - || !mSettings.getCurrent().isSuggestionsRequested(mDisplayOrientation)) { + || !currentSettings.isSuggestionsRequested(mDisplayOrientation)) { if (mWordComposer.isComposingWord()) { Log.w(TAG, "Called updateSuggestionsOrPredictions but suggestions were not " + "requested!"); @@ -2230,7 +2245,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen return; } - if (!mWordComposer.isComposingWord() && !mSettings.getCurrent().mBigramPredictionEnabled) { + if (!mWordComposer.isComposingWord() && !currentSettings.mBigramPredictionEnabled) { setPunctuationSuggestions(); return; } @@ -2251,12 +2266,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // whatever is *before* the half-committed word in the buffer, hence 2; if we aren't, we // should just skip whitespace if any, so 1. // TODO: this is slow (2-way IPC) - we should probably cache this instead. + final SettingsValues currentSettings = mSettings.getCurrent(); final String prevWord = - mConnection.getNthPreviousWord(mSettings.getCurrent().mWordSeparators, + mConnection.getNthPreviousWord(currentSettings.mWordSeparators, mWordComposer.isComposingWord() ? 2 : 1); return suggest.getSuggestedWords(mWordComposer, prevWord, keyboard.getProximityInfo(), - mSettings.getBlockPotentiallyOffensive(), - mSettings.getCurrent().mCorrectionEnabled, sessionId); + currentSettings.mBlockPotentiallyOffensive, + currentSettings.mCorrectionEnabled, sessionId); } private SuggestedWords getSuggestedWordsOrOlderSuggestions(final int sessionId) { @@ -2382,18 +2398,19 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } mConnection.beginBatchEdit(); + final SettingsValues currentSettings = mSettings.getCurrent(); if (SPACE_STATE_PHANTOM == mSpaceState && suggestion.length() > 0 // In the batch input mode, a manually picked suggested word should just replace // the current batch input text and there is no need for a phantom space. && !mWordComposer.isBatchMode()) { final int firstChar = Character.codePointAt(suggestion, 0); - if (!mSettings.getCurrent().isWordSeparator(firstChar) - || mSettings.getCurrent().isUsuallyPrecededBySpace(firstChar)) { + if (!currentSettings.isWordSeparator(firstChar) + || currentSettings.isUsuallyPrecededBySpace(firstChar)) { promotePhantomSpace(); } } - if (mSettings.getCurrent().isApplicationSpecifiedCompletionsOn() + if (currentSettings.isApplicationSpecifiedCompletionsOn() && mApplicationSpecifiedCompletions != null && index >= 0 && index < mApplicationSpecifiedCompletions.length) { mSuggestedWords = SuggestedWords.EMPTY; @@ -2431,20 +2448,21 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // AND it's in none of our current dictionaries (main, user or otherwise). // Please note that if mSuggest is null, it means that everything is off: suggestion // and correction, so we shouldn't try to show the hint + final Suggest suggest = mSuggest; final boolean showingAddToDictionaryHint = (SuggestedWordInfo.KIND_TYPED == suggestionInfo.mKind || SuggestedWordInfo.KIND_OOV_CORRECTION == suggestionInfo.mKind) - && mSuggest != null + && suggest != null // If the suggestion is not in the dictionary, the hint should be shown. - && !AutoCorrectionUtils.isValidWord(mSuggest, suggestion, true); + && !AutoCorrectionUtils.isValidWord(suggest, suggestion, true); - if (mSettings.isInternal()) { + if (currentSettings.mIsInternal) { LatinImeLoggerUtils.onSeparator((char)Constants.CODE_SPACE, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE); } if (showingAddToDictionaryHint && mIsUserDictionaryAvailable) { mSuggestionStripView.showAddToDictionaryHint( - suggestion, mSettings.getCurrent().mHintToSaveText); + suggestion, currentSettings.mHintToSaveText); } else { // If we're not showing the "Touch again to save", then update the suggestion strip. mHandler.postUpdateSuggestionStrip(); @@ -2470,10 +2488,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } private void setPunctuationSuggestions() { - if (mSettings.getCurrent().mBigramPredictionEnabled) { + final SettingsValues currentSettings = mSettings.getCurrent(); + if (currentSettings.mBigramPredictionEnabled) { clearSuggestionStrip(); } else { - setSuggestedWords(mSettings.getCurrent().mSuggestPuncList, false); + setSuggestedWords(currentSettings.mSuggestPuncList, false); } setAutoCorrectionIndicator(false); setSuggestionStripShown(isSuggestionsStripVisible()); @@ -2481,21 +2500,20 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private String addToUserHistoryDictionary(final String suggestion) { if (TextUtils.isEmpty(suggestion)) return null; - if (mSuggest == null) return null; + final Suggest suggest = mSuggest; + if (suggest == null) return null; // If correction is not enabled, we don't add words to the user history dictionary. // That's to avoid unintended additions in some sensitive fields, or fields that // expect to receive non-words. - if (!mSettings.getCurrent().mCorrectionEnabled) return null; + final SettingsValues currentSettings = mSettings.getCurrent(); + if (!currentSettings.mCorrectionEnabled) return null; - final Suggest suggest = mSuggest; - final UserHistoryDictionary userHistoryDictionary = mUserHistoryDictionary; - if (suggest == null || userHistoryDictionary == null) { - // Avoid concurrent issue - return null; - } - final String prevWord - = mConnection.getNthPreviousWord(mSettings.getCurrent().mWordSeparators, 2); + final UserHistoryPredictionDictionary userHistoryDictionary = + mUserHistoryPredictionDictionary; + if (userHistoryDictionary == null) return null; + + final String prevWord = mConnection.getNthPreviousWord(currentSettings.mWordSeparators, 2); final String secondWord; if (mWordComposer.wasAutoCapitalized() && !mWordComposer.isMostlyCaps()) { secondWord = suggestion.toLowerCase(mSubtypeSwitcher.getCurrentSubtypeLocale()); @@ -2524,8 +2542,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen if (mLastSelectionStart != mLastSelectionEnd) return; // If we don't know the cursor location, return. if (mLastSelectionStart < 0) return; - if (!mConnection.isCursorTouchingWord(mSettings.getCurrent())) return; - final TextRange range = mConnection.getWordRangeAtCursor(mSettings.getWordSeparators(), + final SettingsValues currentSettings = mSettings.getCurrent(); + if (!mConnection.isCursorTouchingWord(currentSettings)) return; + final TextRange range = mConnection.getWordRangeAtCursor(currentSettings.mWordSeparators, 0 /* additionalPrecedingWordsCount */); if (null == range) return; // Happens if we don't have an input connection at all // If for some strange reason (editor bug or so) we measure the text before the cursor as @@ -2639,7 +2658,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } mConnection.deleteSurroundingText(deleteLength, 0); if (!TextUtils.isEmpty(previousWord) && !TextUtils.isEmpty(committedWord)) { - mUserHistoryDictionary.cancelAddingUserHistory(previousWord, committedWord); + mUserHistoryPredictionDictionary.cancelAddingUserHistory(previousWord, committedWord); } mConnection.commitText(originallyTypedWord + mLastComposedWord.mSeparatorString, 1); if (mSettings.isInternal()) { diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java index d07fa47d6..b69e3f8d2 100644 --- a/java/src/com/android/inputmethod/latin/RichInputConnection.java +++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java @@ -56,11 +56,14 @@ public final class RichInputConnection { private static final int INVALID_CURSOR_POSITION = -1; /** - * This variable contains the value LatinIME thinks the cursor position should be at now. - * This is a few steps in advance of what the TextView thinks it is, because TextView will - * only know after the IPC calls gets through. + * This variable contains an expected value for the cursor position. This is where the + * cursor may end up after all the keyboard-triggered updates have passed. We keep this to + * compare it to the actual cursor position to guess whether the move was caused by a + * keyboard command or not. + * It's not really the cursor position: the cursor may not be there yet, and it's also expected + * there be cases where it never actually comes to be there. */ - private int mCurrentCursorPosition = INVALID_CURSOR_POSITION; // in chars, not code points + private int mExpectedCursorPosition = INVALID_CURSOR_POSITION; // in chars, not code points /** * This contains the committed text immediately preceding the cursor and the composing * text if any. It is refreshed when the cursor moves by calling upon the TextView. @@ -101,16 +104,16 @@ public final class RichInputConnection { final String reference = (beforeCursor.length() <= actualLength) ? beforeCursor.toString() : beforeCursor.subSequence(beforeCursor.length() - actualLength, beforeCursor.length()).toString(); - if (et.selectionStart != mCurrentCursorPosition + if (et.selectionStart != mExpectedCursorPosition || !(reference.equals(internal.toString()))) { - final String context = "Expected cursor position = " + mCurrentCursorPosition + final String context = "Expected cursor position = " + mExpectedCursorPosition + "\nActual cursor position = " + et.selectionStart + "\nExpected text = " + internal.length() + " " + internal + "\nActual text = " + reference.length() + " " + reference; ((LatinIME)mParent).debugDumpStateAndCrashWithException(context); } else { Log.e(TAG, DebugLogUtils.getStackTrace(2)); - Log.e(TAG, "Exp <> Actual : " + mCurrentCursorPosition + " <> " + et.selectionStart); + Log.e(TAG, "Exp <> Actual : " + mExpectedCursorPosition + " <> " + et.selectionStart); } } @@ -141,7 +144,7 @@ public final class RichInputConnection { public void resetCachesUponCursorMove(final int newCursorPosition, final boolean shouldFinishComposition) { - mCurrentCursorPosition = newCursorPosition; + mExpectedCursorPosition = newCursorPosition; mComposingText.setLength(0); mCommittedTextBeforeComposingText.setLength(0); final CharSequence textBeforeCursor = getTextBeforeCursor(DEFAULT_TEXT_CACHE_SIZE, 0); @@ -166,7 +169,7 @@ public final class RichInputConnection { if (DEBUG_BATCH_NESTING) checkBatchEdit(); if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug(); mCommittedTextBeforeComposingText.append(mComposingText); - mCurrentCursorPosition += mComposingText.length(); + mExpectedCursorPosition += mComposingText.length(); mComposingText.setLength(0); if (null != mIC) { mIC.finishComposingText(); @@ -180,7 +183,7 @@ public final class RichInputConnection { if (DEBUG_BATCH_NESTING) checkBatchEdit(); if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug(); mCommittedTextBeforeComposingText.append(text); - mCurrentCursorPosition += text.length() - mComposingText.length(); + mExpectedCursorPosition += text.length() - mComposingText.length(); mComposingText.setLength(0); if (null != mIC) { mIC.commitText(text, i); @@ -193,7 +196,7 @@ public final class RichInputConnection { } public boolean canDeleteCharacters() { - return mCurrentCursorPosition > 0; + return mExpectedCursorPosition > 0; } /** @@ -230,7 +233,7 @@ public final class RichInputConnection { // heavy pressing of delete, for example DEFAULT_TEXT_CACHE_SIZE - 5 times or so. // getCapsMode should be updated to be able to return a "not enough info" result so that // we can get more context only when needed. - if (TextUtils.isEmpty(mCommittedTextBeforeComposingText) && 0 != mCurrentCursorPosition) { + if (TextUtils.isEmpty(mCommittedTextBeforeComposingText) && 0 != mExpectedCursorPosition) { mCommittedTextBeforeComposingText.append( getTextBeforeCursor(DEFAULT_TEXT_CACHE_SIZE, 0)); } @@ -251,7 +254,7 @@ public final class RichInputConnection { mCommittedTextBeforeComposingText.length() + mComposingText.length(); // If we have enough characters to satisfy the request, or if we have all characters in // the text field, then we can return the cached version right away. - if (cachedLength >= n || cachedLength >= mCurrentCursorPosition) { + if (cachedLength >= n || cachedLength >= mExpectedCursorPosition) { final StringBuilder s = new StringBuilder(mCommittedTextBeforeComposingText); s.append(mComposingText); if (s.length() > n) { @@ -284,10 +287,10 @@ public final class RichInputConnection { + remainingChars, 0); mCommittedTextBeforeComposingText.setLength(len); } - if (mCurrentCursorPosition > beforeLength) { - mCurrentCursorPosition -= beforeLength; + if (mExpectedCursorPosition > beforeLength) { + mExpectedCursorPosition -= beforeLength; } else { - mCurrentCursorPosition = 0; + mExpectedCursorPosition = 0; } if (null != mIC) { mIC.deleteSurroundingText(beforeLength, afterLength); @@ -321,7 +324,7 @@ public final class RichInputConnection { switch (keyEvent.getKeyCode()) { case KeyEvent.KEYCODE_ENTER: mCommittedTextBeforeComposingText.append("\n"); - mCurrentCursorPosition += 1; + mExpectedCursorPosition += 1; break; case KeyEvent.KEYCODE_DEL: if (0 == mComposingText.length()) { @@ -333,18 +336,18 @@ public final class RichInputConnection { } else { mComposingText.delete(mComposingText.length() - 1, mComposingText.length()); } - if (mCurrentCursorPosition > 0) mCurrentCursorPosition -= 1; + if (mExpectedCursorPosition > 0) mExpectedCursorPosition -= 1; break; case KeyEvent.KEYCODE_UNKNOWN: if (null != keyEvent.getCharacters()) { mCommittedTextBeforeComposingText.append(keyEvent.getCharacters()); - mCurrentCursorPosition += keyEvent.getCharacters().length(); + mExpectedCursorPosition += keyEvent.getCharacters().length(); } break; default: final String text = new String(new int[] { keyEvent.getUnicodeChar() }, 0, 1); mCommittedTextBeforeComposingText.append(text); - mCurrentCursorPosition += text.length(); + mExpectedCursorPosition += text.length(); break; } } @@ -378,7 +381,7 @@ public final class RichInputConnection { public void setComposingText(final CharSequence text, final int newCursorPosition) { if (DEBUG_BATCH_NESTING) checkBatchEdit(); if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug(); - mCurrentCursorPosition += text.length() - mComposingText.length(); + mExpectedCursorPosition += text.length() - mComposingText.length(); mComposingText.setLength(0); mComposingText.append(text); // TODO: support values of i != 1. At this time, this is never called with i != 1. @@ -400,7 +403,7 @@ public final class RichInputConnection { ResearchLogger.richInputConnection_setSelection(start, end); } } - mCurrentCursorPosition = start; + mExpectedCursorPosition = start; mCommittedTextBeforeComposingText.setLength(0); mCommittedTextBeforeComposingText.append(getTextBeforeCursor(DEFAULT_TEXT_CACHE_SIZE, 0)); } @@ -423,7 +426,7 @@ public final class RichInputConnection { // text should never be null, but just in case, it's better to insert nothing than to crash if (null == text) text = ""; mCommittedTextBeforeComposingText.append(text); - mCurrentCursorPosition += text.length() - mComposingText.length(); + mExpectedCursorPosition += text.length() - mComposingText.length(); mComposingText.setLength(0); if (null != mIC) { mIC.commitCompletion(completionInfo); @@ -705,14 +708,14 @@ public final class RichInputConnection { */ public boolean isBelatedExpectedUpdate(final int oldSelStart, final int newSelStart) { // If this is an update that arrives at our expected position, it's a belated update. - if (newSelStart == mCurrentCursorPosition) return true; + if (newSelStart == mExpectedCursorPosition) return true; // If this is an update that moves the cursor from our expected position, it must be // an explicit move. - if (oldSelStart == mCurrentCursorPosition) return false; + if (oldSelStart == mExpectedCursorPosition) return false; // The following returns true if newSelStart is between oldSelStart and // mCurrentCursorPosition. We assume that if the updated position is between the old // position and the expected position, then it must be a belated update. - return (newSelStart - oldSelStart) * (mCurrentCursorPosition - newSelStart) >= 0; + return (newSelStart - oldSelStart) * (mExpectedCursorPosition - newSelStart) >= 0; } /** diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index 647c6f6e1..6b016675a 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -22,7 +22,7 @@ import android.text.TextUtils; import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; -import com.android.inputmethod.latin.personalization.UserHistoryDictionary; +import com.android.inputmethod.latin.personalization.UserHistoryPredictionDictionary; import com.android.inputmethod.latin.utils.AutoCorrectionUtils; import com.android.inputmethod.latin.utils.BoundedTreeSet; import com.android.inputmethod.latin.utils.CollectionUtils; @@ -168,8 +168,10 @@ public final class Suggest { addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_CONTACTS, contactsDictionary); } - public void setUserHistoryDictionary(final UserHistoryDictionary userHistoryDictionary) { - addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_USER_HISTORY, userHistoryDictionary); + public void setUserHistoryPredictionDictionary( + final UserHistoryPredictionDictionary userHistoryPredictionDictionary) { + addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_USER_HISTORY, + userHistoryPredictionDictionary); } public void setAutoCorrectionThreshold(float threshold) { diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java index 000c25270..167c6915c 100644 --- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java @@ -982,6 +982,7 @@ public final class BinaryDictIOUtils { return null; } + private static final int HEADER_READING_BUFFER_SIZE = 16384; /** * Convenience method to read the header of a binary file. * @@ -991,7 +992,6 @@ public final class BinaryDictIOUtils { * @param offset The offset in the file where to start reading the data. * @param length The length of the data file. */ - private static final int HEADER_READING_BUFFER_SIZE = 16384; public static FileHeader getDictionaryFileHeader( final File file, final long offset, final long length) throws FileNotFoundException, IOException, UnsupportedFormatException { diff --git a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java index 5a2b24c58..118dc22b8 100644 --- a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java +++ b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java @@ -34,6 +34,8 @@ import java.util.LinkedList; public final class FusionDictionary implements Iterable<Word> { private static final boolean DBG = MakedictLog.DBG; + private static int CHARACTER_NOT_FOUND_INDEX = -1; + /** * A node of the dictionary, containing several CharGroups. * @@ -473,7 +475,7 @@ public final class FusionDictionary implements Iterable<Word> { CharGroup currentGroup = null; int differentCharIndex = 0; // Set by the loop to the index of the char that differs int nodeIndex = findIndexOfChar(mRoot, word[charIndex]); - while (CHARACTER_NOT_FOUND != nodeIndex) { + while (CHARACTER_NOT_FOUND_INDEX != nodeIndex) { currentGroup = currentNode.mData.get(nodeIndex); differentCharIndex = compareArrays(currentGroup.mChars, word, charIndex); if (ARRAYS_ARE_EQUAL != differentCharIndex @@ -485,7 +487,7 @@ public final class FusionDictionary implements Iterable<Word> { nodeIndex = findIndexOfChar(currentNode, word[charIndex]); } - if (-1 == nodeIndex) { + if (CHARACTER_NOT_FOUND_INDEX == nodeIndex) { // No node at this point to accept the word. Create one. final int insertionIndex = findInsertionIndex(currentNode, word[charIndex]); final CharGroup newGroup = new CharGroup( @@ -612,20 +614,18 @@ public final class FusionDictionary implements Iterable<Word> { return result >= 0 ? result : -result - 1; } - private static int CHARACTER_NOT_FOUND = -1; - /** * Find the index of a char in a node, if it exists. * * @param node the node to search in. * @param character the character to search for. - * @return the position of the character if it's there, or CHARACTER_NOT_FOUND = -1 else. + * @return the position of the character if it's there, or CHARACTER_NOT_FOUND_INDEX = -1 else. */ private static int findIndexOfChar(final Node node, int character) { final int insertionIndex = findInsertionIndex(node, character); - if (node.mData.size() <= insertionIndex) return CHARACTER_NOT_FOUND; + if (node.mData.size() <= insertionIndex) return CHARACTER_NOT_FOUND_INDEX; return character == node.mData.get(insertionIndex).mChars[0] ? insertionIndex - : CHARACTER_NOT_FOUND; + : CHARACTER_NOT_FOUND_INDEX; } /** @@ -640,7 +640,7 @@ public final class FusionDictionary implements Iterable<Word> { CharGroup currentGroup; do { int indexOfGroup = findIndexOfChar(node, codePoints[index]); - if (CHARACTER_NOT_FOUND == indexOfGroup) return null; + if (CHARACTER_NOT_FOUND_INDEX == indexOfGroup) return null; currentGroup = node.mData.get(indexOfGroup); if (codePoints.length - index < currentGroup.mChars.length) return null; diff --git a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java b/java/src/com/android/inputmethod/latin/personalization/DynamicPredictionDictionaryBase.java index c76dea0bb..9d041f4eb 100644 --- a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java +++ b/java/src/com/android/inputmethod/latin/personalization/DynamicPredictionDictionaryBase.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2013 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. @@ -24,7 +24,6 @@ import android.util.Log; import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.Constants; -import com.android.inputmethod.latin.Dictionary; import com.android.inputmethod.latin.ExpandableDictionary; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; @@ -47,16 +46,17 @@ import java.util.ArrayList; import java.util.concurrent.locks.ReentrantLock; /** - * Locally gathers stats about the words user types and various other signals like auto-correction - * cancellation or manual picks. This allows the keyboard to adapt to the typist over time. + * This class is a base class of a dictionary for the personalized prediction language model. */ -public class UserHistoryDictionary extends ExpandableDictionary { - private static final String TAG = UserHistoryDictionary.class.getSimpleName(); - private static final String NAME = UserHistoryDictionary.class.getSimpleName(); +public abstract class DynamicPredictionDictionaryBase extends ExpandableDictionary { + public static void registerUpdateListener(PersonalizationDictionaryUpdateListener listener) { + // TODO: Implement + } + + private static final String TAG = DynamicPredictionDictionaryBase.class.getSimpleName(); public static final boolean DBG_SAVE_RESTORE = false; - public static final boolean DBG_STRESS_TEST = false; - public static final boolean DBG_ALWAYS_WRITE = false; - public static final boolean PROFILE_SAVE_RESTORE = LatinImeLogger.sDBG; + private static final boolean DBG_STRESS_TEST = false; + private static final boolean PROFILE_SAVE_RESTORE = LatinImeLogger.sDBG; private static final FormatOptions VERSION3 = new FormatOptions(3, true /* supportsDynamicUpdate */); @@ -65,14 +65,7 @@ public class UserHistoryDictionary extends ExpandableDictionary { private static final int FREQUENCY_FOR_TYPED = 2; /** Maximum number of pairs. Pruning will start when databases goes above this number. */ - public static final int MAX_HISTORY_BIGRAMS = 10000; - - /** - * When it hits maximum bigram pair, it will delete until you are left with - * only (sMaxHistoryBigrams - sDeleteHistoryBigrams) pairs. - * Do not keep this number small to avoid deleting too often. - */ - public static final int DELETE_HISTORY_BIGRAMS = 1000; + private static final int MAX_HISTORY_BIGRAMS = 10000; /** Locale for which this user history dictionary is storing words */ private final String mLocale; @@ -85,9 +78,9 @@ public class UserHistoryDictionary extends ExpandableDictionary { // Should always be false except when we use this class for test @UsedForTesting boolean isTest = false; - /* package */ UserHistoryDictionary(final Context context, final String locale, - final SharedPreferences sp) { - super(context, Dictionary.TYPE_USER_HISTORY); + /* package */ DynamicPredictionDictionaryBase(final Context context, final String locale, + final SharedPreferences sp, final String dictionaryType) { + super(context, dictionaryType); mLocale = locale; mPrefs = sp; if (mLocale != null && mLocale.length() > 1) { @@ -102,8 +95,8 @@ public class UserHistoryDictionary extends ExpandableDictionary { // Also, the database is written to somewhat frequently, so it needs to be kept alive // throughout the life of the process. // mOpenHelper.close(); - // Ignore close because we cache UserHistoryDictionary for each language. See getInstance() - // above. + // Ignore close because we cache PersonalizationPredictionDictionary for each language. + // See getInstance() above. // super.close(); } @@ -184,7 +177,7 @@ public class UserHistoryDictionary extends ExpandableDictionary { } @Override - public void loadDictionaryAsync() { + public final void loadDictionaryAsync() { // This must be run on non-main thread mBigramListLock.lock(); try { @@ -194,48 +187,47 @@ public class UserHistoryDictionary extends ExpandableDictionary { } } - private int profTotal; - private void loadDictionaryAsyncLocked() { + final int[] profTotalCount = { 0 }; + final String locale = getLocale(); if (DBG_STRESS_TEST) { try { - Log.w(TAG, "Start stress in loading: " + mLocale); + Log.w(TAG, "Start stress in loading: " + locale); Thread.sleep(15000); Log.w(TAG, "End stress in loading"); } catch (InterruptedException e) { } } - final long last = Settings.readLastUserHistoryWriteTime(mPrefs, mLocale); + final long last = Settings.readLastUserHistoryWriteTime(mPrefs, locale); final boolean initializing = last == 0; final long now = System.currentTimeMillis(); - profTotal = 0; - final String fileName = NAME + "." + mLocale + ".dict"; + final String fileName = getDictionaryFileName(); final ExpandableDictionary dictionary = this; final OnAddWordListener listener = new OnAddWordListener() { @Override public void setUnigram(final String word, final String shortcutTarget, final int frequency) { - profTotal++; if (DBG_SAVE_RESTORE) { Log.d(TAG, "load unigram: " + word + "," + frequency); } dictionary.addWord(word, shortcutTarget, frequency); - mBigramList.addBigram(null, word, (byte)frequency); + ++profTotalCount[0]; + addToBigramListLocked(null, word, (byte)frequency); } @Override public void setBigram(final String word1, final String word2, final int frequency) { if (word1.length() < Constants.DICTIONARY_MAX_WORD_LENGTH && word2.length() < Constants.DICTIONARY_MAX_WORD_LENGTH) { - profTotal++; if (DBG_SAVE_RESTORE) { Log.d(TAG, "load bigram: " + word1 + "," + word2 + "," + frequency); } + ++profTotalCount[0]; dictionary.setBigramAndGetFrequency( word1, word2, initializing ? new ForgettingCurveParams(true) : new ForgettingCurveParams(frequency, now, last)); } - mBigramList.addBigram(word1, word2, (byte)frequency); + addToBigramListLocked(word1, word2, (byte)frequency); } }; @@ -264,11 +256,21 @@ public class UserHistoryDictionary extends ExpandableDictionary { if (PROFILE_SAVE_RESTORE) { final long diff = System.currentTimeMillis() - now; Log.d(TAG, "PROF: Load UserHistoryDictionary: " - + mLocale + ", " + diff + "ms. load " + profTotal + "entries."); + + locale + ", " + diff + "ms. load " + profTotalCount[0] + "entries."); } } } + protected abstract String getDictionaryFileName(); + + protected String getLocale() { + return mLocale; + } + + private void addToBigramListLocked(String word0, String word1, byte fcValue) { + mBigramList.addBigram(word0, word1, fcValue); + } + /** * Async task to write pending words to the binarydicts. */ @@ -277,16 +279,16 @@ public class UserHistoryDictionary extends ExpandableDictionary { private final UserHistoryDictionaryBigramList mBigramList; private final boolean mAddLevel0Bigrams; private final String mLocale; - private final UserHistoryDictionary mUserHistoryDictionary; + private final DynamicPredictionDictionaryBase mDynamicPredictionDictionary; private final SharedPreferences mPrefs; private final Context mContext; public UpdateBinaryTask(final UserHistoryDictionaryBigramList pendingWrites, - final String locale, final UserHistoryDictionary dict, + final String locale, final DynamicPredictionDictionaryBase dict, final SharedPreferences prefs, final Context context) { mBigramList = pendingWrites; mLocale = locale; - mUserHistoryDictionary = dict; + mDynamicPredictionDictionary = dict; mPrefs = prefs; mContext = context; mAddLevel0Bigrams = mBigramList.size() <= MAX_HISTORY_BIGRAMS; @@ -294,19 +296,19 @@ public class UserHistoryDictionary extends ExpandableDictionary { @Override protected Void doInBackground(final Void... v) { - if (mUserHistoryDictionary.isTest) { + if (mDynamicPredictionDictionary.isTest) { // If isTest == true, wait until the lock is released. - mUserHistoryDictionary.mBigramListLock.lock(); + mDynamicPredictionDictionary.mBigramListLock.lock(); try { doWriteTaskLocked(); } finally { - mUserHistoryDictionary.mBigramListLock.unlock(); + mDynamicPredictionDictionary.mBigramListLock.unlock(); } - } else if (mUserHistoryDictionary.mBigramListLock.tryLock()) { + } else if (mDynamicPredictionDictionary.mBigramListLock.tryLock()) { try { doWriteTaskLocked(); } finally { - mUserHistoryDictionary.mBigramListLock.unlock(); + mDynamicPredictionDictionary.mBigramListLock.unlock(); } } return null; @@ -324,7 +326,8 @@ public class UserHistoryDictionary extends ExpandableDictionary { } final long now = PROFILE_SAVE_RESTORE ? System.currentTimeMillis() : 0; - final String fileName = NAME + "." + mLocale + ".dict"; + final String fileName = + mDynamicPredictionDictionary.getDictionaryFileName(); final File file = new File(mContext.getFilesDir(), fileName); FileOutputStream out = null; @@ -360,7 +363,8 @@ public class UserHistoryDictionary extends ExpandableDictionary { freq = FREQUENCY_FOR_TYPED; final byte prevFc = mBigramList.getBigrams(word1).get(word2); } else { // bigram - final NextWord nw = mUserHistoryDictionary.getBigramWord(word1, word2); + final NextWord nw = + mDynamicPredictionDictionary.getBigramWord(word1, word2); if (nw != null) { final ForgettingCurveParams fcp = nw.getFcParams(); final byte prevFc = mBigramList.getBigrams(word1).get(word2); diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDicitonary.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java index d3e2dfec9..19554d639 100644 --- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDicitonary.java +++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java @@ -24,7 +24,7 @@ import android.content.Context; /** * This class is a dictionary for the personalized language model that uses binary dictionary. */ -public class PersonalizationDicitonary extends ExpandableBinaryDictionary { +public class PersonalizationDictionary extends ExpandableBinaryDictionary { private static final String NAME = "personalization"; public static void registerUpdateListener(PersonalizationDictionaryUpdateListener listener) { @@ -35,7 +35,7 @@ public class PersonalizationDicitonary extends ExpandableBinaryDictionary { private final String mLocale; // Singleton - private PersonalizationDicitonary(final Context context, final String locale) { + private PersonalizationDictionary(final Context context, final String locale) { super(context, getFilenameWithLocale(NAME, locale), Dictionary.TYPE_PERSONALIZATION); mLocale = locale; } diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryHelper.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryHelper.java index e09e834bf..f5dae99ef 100644 --- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryHelper.java +++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryHelper.java @@ -29,15 +29,16 @@ public class PersonalizationDictionaryHelper { private static final String TAG = PersonalizationDictionaryHelper.class.getSimpleName(); private static final boolean DEBUG = false; - private static final ConcurrentHashMap<String, SoftReference<UserHistoryDictionary>> + private static final ConcurrentHashMap<String, SoftReference<UserHistoryPredictionDictionary>> sLangDictCache = CollectionUtils.newConcurrentHashMap(); - public static UserHistoryDictionary getUserHistoryDictionary( + public static UserHistoryPredictionDictionary getUserHistoryPredictionDictionary( final Context context, final String locale, final SharedPreferences sp) { synchronized (sLangDictCache) { if (sLangDictCache.containsKey(locale)) { - final SoftReference<UserHistoryDictionary> ref = sLangDictCache.get(locale); - final UserHistoryDictionary dict = ref == null ? null : ref.get(); + final SoftReference<UserHistoryPredictionDictionary> ref = + sLangDictCache.get(locale); + final UserHistoryPredictionDictionary dict = ref == null ? null : ref.get(); if (dict != null) { if (DEBUG) { Log.w(TAG, "Use cached UserHistoryDictionary for " + locale); @@ -45,8 +46,9 @@ public class PersonalizationDictionaryHelper { return dict; } } - final UserHistoryDictionary dict = new UserHistoryDictionary(context, locale, sp); - sLangDictCache.put(locale, new SoftReference<UserHistoryDictionary>(dict)); + final UserHistoryPredictionDictionary dict = + new UserHistoryPredictionDictionary(context, locale, sp); + sLangDictCache.put(locale, new SoftReference<UserHistoryPredictionDictionary>(dict)); return dict; } } diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdateListener.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdateListener.java index 2ec0dc00c..c78e5a95b 100644 --- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdateListener.java +++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdateListener.java @@ -16,6 +16,6 @@ package com.android.inputmethod.latin.personalization; -public class PersonalizationDictionaryUpdateListener { +public interface PersonalizationDictionaryUpdateListener { // TODO: Implement } diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationPredictionDictionary.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationPredictionDictionary.java new file mode 100644 index 000000000..955bd2762 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationPredictionDictionary.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2013 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.personalization; + +import com.android.inputmethod.latin.Dictionary; + +import android.content.Context; +import android.content.SharedPreferences; + +public class PersonalizationPredictionDictionary extends DynamicPredictionDictionaryBase { + private static final String NAME = PersonalizationPredictionDictionary.class.getSimpleName(); + + /* package */ PersonalizationPredictionDictionary(final Context context, final String locale, + final SharedPreferences sp) { + super(context, locale, sp, Dictionary.TYPE_PERSONALIZATION_PREDICTION_IN_JAVA); + } + + @Override + protected String getDictionaryFileName() { + return NAME + "." + getLocale() + ".dict"; + } +} diff --git a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryBigramList.java b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryBigramList.java index b93630a18..f21db25a6 100644 --- a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryBigramList.java +++ b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryBigramList.java @@ -53,7 +53,7 @@ public final class UserHistoryDictionaryBigramList { * Called when loaded from the SQL DB. */ public void addBigram(String word1, String word2, byte fcValue) { - if (UserHistoryDictionary.DBG_SAVE_RESTORE) { + if (UserHistoryPredictionDictionary.DBG_SAVE_RESTORE) { Log.d(TAG, "--- add bigram: " + word1 + ", " + word2 + ", " + fcValue); } final HashMap<String, Byte> map; @@ -73,7 +73,7 @@ public final class UserHistoryDictionaryBigramList { * Called when inserted to the SQL DB. */ public void updateBigram(String word1, String word2, byte fcValue) { - if (UserHistoryDictionary.DBG_SAVE_RESTORE) { + if (UserHistoryPredictionDictionary.DBG_SAVE_RESTORE) { Log.d(TAG, "--- update bigram: " + word1 + ", " + word2 + ", " + fcValue); } final HashMap<String, Byte> map; diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationPredictionDicitonary.java b/java/src/com/android/inputmethod/latin/personalization/UserHistoryPredictionDictionary.java index 3e7772584..d11784454 100644 --- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationPredictionDicitonary.java +++ b/java/src/com/android/inputmethod/latin/personalization/UserHistoryPredictionDictionary.java @@ -17,30 +17,23 @@ package com.android.inputmethod.latin.personalization; import com.android.inputmethod.latin.Dictionary; -import com.android.inputmethod.latin.ExpandableDictionary; import android.content.Context; import android.content.SharedPreferences; /** - * This class is a dictionary for the personalized prediction language model implemented in Java. + * Locally gathers stats about the words user types and various other signals like auto-correction + * cancellation or manual picks. This allows the keyboard to adapt to the typist over time. */ -public class PersonalizationPredictionDicitonary extends ExpandableDictionary { - public static void registerUpdateListener(PersonalizationDictionaryUpdateListener listener) { - // TODO: Implement - } - - /** Locale for which this user history dictionary is storing words */ - private final String mLocale; - private final SharedPreferences mPrefs; - - // Singleton - private PersonalizationPredictionDicitonary(final Context context, final String locale, +public class UserHistoryPredictionDictionary extends DynamicPredictionDictionaryBase { + private static final String NAME = UserHistoryPredictionDictionary.class.getSimpleName(); + /* package */ UserHistoryPredictionDictionary(final Context context, final String locale, final SharedPreferences sp) { - super(context, Dictionary.TYPE_PERSONALIZATION_PREDICTION_IN_JAVA); - mLocale = locale; - mPrefs = sp; + super(context, locale, sp, Dictionary.TYPE_USER_HISTORY); } - // TODO: Implement + @Override + protected String getDictionaryFileName() { + return NAME + "." + getLocale() + ".dict"; + } } diff --git a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java index 34ea2279d..e675f428e 100644 --- a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java +++ b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java @@ -32,7 +32,6 @@ import com.android.inputmethod.latin.utils.ApplicationUtils; public final class DebugSettings extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener { - private static final String TAG = DebugSettings.class.getSimpleName(); public static final String PREF_DEBUG_MODE = "debug_mode"; public static final String PREF_FORCE_NON_DISTINCT_MULTITOUCH = "force_non_distinct_multitouch"; diff --git a/java/src/com/android/inputmethod/latin/settings/Settings.java b/java/src/com/android/inputmethod/latin/settings/Settings.java index b690fed78..2631cd317 100644 --- a/java/src/com/android/inputmethod/latin/settings/Settings.java +++ b/java/src/com/android/inputmethod/latin/settings/Settings.java @@ -93,7 +93,6 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang private Resources mRes; private SharedPreferences mPrefs; - private Locale mCurrentLocale; private SettingsValues mSettingsValues; private static final Settings sInstance = new Settings(); @@ -128,16 +127,15 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang Log.w(TAG, "onSharedPreferenceChanged called before loadSettings."); return; } - loadSettings(mCurrentLocale, mSettingsValues.mInputAttributes); + loadSettings(mSettingsValues.mLocale, mSettingsValues.mInputAttributes); } public void loadSettings(final Locale locale, final InputAttributes inputAttributes) { - mCurrentLocale = locale; final SharedPreferences prefs = mPrefs; final RunInLocale<SettingsValues> job = new RunInLocale<SettingsValues>() { @Override protected SettingsValues job(final Resources res) { - return new SettingsValues(prefs, res, inputAttributes); + return new SettingsValues(prefs, locale, res, inputAttributes); } }; mSettingsValues = job.runInLocale(mRes, locale); @@ -160,10 +158,6 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang return mSettingsValues.isWordSeparator(code); } - public Locale getCurrentLocale() { - return mCurrentLocale; - } - public boolean getBlockPotentiallyOffensive() { return mSettingsValues.mBlockPotentiallyOffensive; } diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java index 3fa00bb2e..8aafb0738 100644 --- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java +++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java @@ -32,11 +32,11 @@ import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.InputTypeUtils; -import com.android.inputmethod.latin.utils.RunInLocale; import com.android.inputmethod.latin.utils.StringUtils; import java.util.ArrayList; import java.util.Arrays; +import java.util.Locale; /** * When you call the constructor of this class, you may want to change the current system locale by @@ -75,6 +75,7 @@ public final class SettingsValues { public final boolean mGestureFloatingPreviewTextEnabled; public final boolean mSlidingKeyInputPreviewEnabled; public final int mKeyLongpressTimeout; + public final Locale mLocale; // From the input box public final InputAttributes mInputAttributes; @@ -97,8 +98,9 @@ public final class SettingsValues { // Debug settings public final boolean mIsInternal; - public SettingsValues(final SharedPreferences prefs, final Resources res, + public SettingsValues(final SharedPreferences prefs, final Locale locale, final Resources res, final InputAttributes inputAttributes) { + mLocale = locale; // Get the resources mDelayUpdateOldSuggestions = res.getInteger(R.integer.config_delay_update_old_suggestions); mSymbolsPrecededBySpace = diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java index ba5a68460..21426d1eb 100644 --- a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java +++ b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java @@ -274,4 +274,8 @@ public class UserDictionaryAddWordContents { localesList.add(new LocaleRenderer(activity, null)); // meaning: select another locale return localesList; } + + public String getCurrentUserDictionaryLocale() { + return mLocale; + } } diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordFragment.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordFragment.java index 8b8bd5e03..4fc132f68 100644 --- a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordFragment.java +++ b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordFragment.java @@ -60,6 +60,7 @@ public class UserDictionaryAddWordFragment extends Fragment public void onActivityCreated(final Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); setHasOptionsMenu(true); + getActivity().getActionBar().setTitle(R.string.edit_personal_dictionary); // Keep the instance so that we remember mContents when configuration changes (eg rotation) setRetainInstance(true); } @@ -82,6 +83,8 @@ public class UserDictionaryAddWordFragment extends Fragment mContents = new UserDictionaryAddWordContents(mRootView, mContents /* oldInstanceToBeEdited */); } + getActivity().getActionBar().setSubtitle(UserDictionarySettingsUtils.getLocaleDisplayName( + getActivity(), mContents.getCurrentUserDictionaryLocale())); return mRootView; } @@ -100,7 +103,7 @@ public class UserDictionaryAddWordFragment extends Fragment /** * Callback for the framework when a menu option is pressed. * - * @param MenuItem the item that was pressed + * @param item the item that was pressed * @return false to allow normal menu processing to proceed, true to consume it here */ @Override diff --git a/java/src/com/android/inputmethod/latin/utils/CsvUtils.java b/java/src/com/android/inputmethod/latin/utils/CsvUtils.java index 159ebb1b9..36b927eea 100644 --- a/java/src/com/android/inputmethod/latin/utils/CsvUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/CsvUtils.java @@ -22,7 +22,7 @@ import java.util.ArrayList; /** * Utility methods for parsing and serializing Comma-Separated Values. The public APIs of this - * utility class are {@link #split(String)}, {@link #split(int,String)}, {@link #join(String)}, + * utility class are {@link #split(String)}, {@link #split(int,String)}, {@link #join(String...)}, * {@link #join(int,String...)}, and {@link #join(int,int[],String...)}. * * This class implements CSV parsing and serializing methods conforming to RFC 4180 with an diff --git a/java/src/com/android/inputmethod/latin/utils/UsabilityStudyLogUtils.java b/java/src/com/android/inputmethod/latin/utils/UsabilityStudyLogUtils.java index ef9cacf61..06826dac0 100644 --- a/java/src/com/android/inputmethod/latin/utils/UsabilityStudyLogUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/UsabilityStudyLogUtils.java @@ -25,6 +25,7 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.Process; import android.util.Log; +import android.view.MotionEvent; import com.android.inputmethod.latin.LatinImeLogger; @@ -109,6 +110,43 @@ public final class UsabilityStudyLogUtils { LatinImeLogger.onPrintAllUsabilityStudyLogs(); } + public static void writeMotionEvent(final MotionEvent me) { + final int action = me.getActionMasked(); + final long eventTime = me.getEventTime(); + final int pointerCount = me.getPointerCount(); + for (int index = 0; index < pointerCount; index++) { + final int id = me.getPointerId(index); + final int x = (int)me.getX(index); + final int y = (int)me.getY(index); + final float size = me.getSize(index); + final float pressure = me.getPressure(index); + + final String eventTag; + switch (action) { + case MotionEvent.ACTION_UP: + eventTag = "[Up]"; + break; + case MotionEvent.ACTION_DOWN: + eventTag = "[Down]"; + break; + case MotionEvent.ACTION_POINTER_UP: + eventTag = "[PointerUp]"; + break; + case MotionEvent.ACTION_POINTER_DOWN: + eventTag = "[PointerDown]"; + break; + case MotionEvent.ACTION_MOVE: + eventTag = "[Move]"; + break; + default: + eventTag = "[Action" + action + "]"; + break; + } + getInstance().write(eventTag + eventTime + "," + id + "," + x + "," + y + "," + size + + "," + pressure); + } + } + public void write(final String log) { mLoggingHandler.post(new Runnable() { @Override @@ -191,7 +229,7 @@ public final class UsabilityStudyLogUtils { Log.w(USABILITY_TAG, e2); return; } - if (destFile == null || !destFile.exists()) { + if (!destFile.exists()) { Log.w(USABILITY_TAG, "Dest file doesn't exist."); return; } |