diff options
Diffstat (limited to 'java/src/com/android/inputmethod')
14 files changed, 278 insertions, 202 deletions
diff --git a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java index b8d1651dc..a32d76c30 100644 --- a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java +++ b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java @@ -74,7 +74,6 @@ public final class SuggestionSpanUtils { return pickedWord; } - boolean hasSuggestionFromMainDictionary = false; final ArrayList<String> suggestionsList = CollectionUtils.newArrayList(); for (int i = 0; i < suggestedWords.size(); ++i) { if (suggestionsList.size() >= SuggestionSpan.SUGGESTIONS_MAX_SIZE) { @@ -84,22 +83,11 @@ public final class SuggestionSpanUtils { if (info.mKind == SuggestedWordInfo.KIND_PREDICTION) { continue; } - if (info.mSourceDict.mDictType == Dictionary.TYPE_MAIN) { - hasSuggestionFromMainDictionary = true; - } final String word = suggestedWords.getWord(i); if (!TextUtils.equals(pickedWord, word)) { suggestionsList.add(word.toString()); } } - if (!hasSuggestionFromMainDictionary) { - // If we don't have any suggestions from the dictionary, it probably looks bad - // enough as it is already because suggestions come pretty much only from contacts. - // Let's not embed these bad suggestions in the text view so as to avoid using - // them with recorrection. - return pickedWord; - } - final SuggestionSpan suggestionSpan = new SuggestionSpan(context, null /* locale */, suggestionsList.toArray(new String[suggestionsList.size()]), 0 /* flags */, SuggestionSpanPickedNotificationReceiver.class); diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index 708f75a06..e66cfca49 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -25,6 +25,7 @@ import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.settings.NativeSuggestOptions; import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.JniUtils; +import com.android.inputmethod.latin.utils.LanguageModelParam; import com.android.inputmethod.latin.utils.StringUtils; import com.android.inputmethod.latin.utils.UnigramProperty; @@ -164,6 +165,7 @@ public final class BinaryDictionary extends Dictionary { LanguageModelParam[] languageModelParams, int startIndex); private static native int calculateProbabilityNative(long dict, int unigramProbability, int bigramProbability); + private static native int setCurrentTimeForTestNative(int currentTime); private static native String getPropertyNative(long dict, String query); @UsedForTesting @@ -364,51 +366,6 @@ public final class BinaryDictionary extends Dictionary { removeBigramWordsNative(mNativeDict, codePoints0, codePoints1); } - public static class LanguageModelParam { - public final String mTargetWord; - public final int[] mWord0; - public final int[] mWord1; - // TODO: this needs to be a list of shortcuts - public final int[] mShortcutTarget; - public final int mUnigramProbability; - public final int mBigramProbability; - public final int mShortcutProbability; - public final boolean mIsNotAWord; - public final boolean mIsBlacklisted; - public final int mTimestamp; - - // Constructor for unigram. TODO: support shortcuts - public LanguageModelParam(final String word, final int unigramProbability, - final int timestamp) { - mTargetWord = word; - mWord0 = null; - mWord1 = StringUtils.toCodePointArray(word); - mShortcutTarget = null; - mUnigramProbability = unigramProbability; - mBigramProbability = NOT_A_PROBABILITY; - mShortcutProbability = NOT_A_PROBABILITY; - mIsNotAWord = false; - mIsBlacklisted = false; - mTimestamp = timestamp; - } - - // Constructor for unigram and bigram. - public LanguageModelParam(final String word0, final String word1, - final int unigramProbability, final int bigramProbability, - final int timestamp) { - mTargetWord = word1; - mWord0 = StringUtils.toCodePointArray(word0); - mWord1 = StringUtils.toCodePointArray(word1); - mShortcutTarget = null; - mUnigramProbability = unigramProbability; - mBigramProbability = bigramProbability; - mShortcutProbability = NOT_A_PROBABILITY; - mIsNotAWord = false; - mIsBlacklisted = false; - mTimestamp = timestamp; - } - } - public void addMultipleDictionaryEntries(final LanguageModelParam[] languageModelParams) { if (!isValidDictionary()) return; int processedParamCount = 0; @@ -464,8 +421,22 @@ public final class BinaryDictionary extends Dictionary { return calculateProbabilityNative(mNativeDict, unigramProbability, bigramProbability); } + /** + * Control the current time to be used in the native code. If currentTime >= 0, this method sets + * the current time and gets into test mode. + * In test mode, set timestamp is used as the current time in the native code. + * If currentTime < 0, quit the test mode and returns to using time() to get the current time. + * + * @param currentTime seconds since the unix epoch + * @return current time got in the native code. + */ + @UsedForTesting + public static int setCurrentTimeForTest(final int currentTime) { + return setCurrentTimeForTestNative(currentTime); + } + @UsedForTesting - public String getPropertyForTests(String query) { + public String getPropertyForTest(final String query) { if (!isValidDictionary()) return ""; return getPropertyNative(mNativeDict, query); } diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java index 7e4f0e85c..fb9517220 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java @@ -28,6 +28,7 @@ import com.android.inputmethod.latin.personalization.PersonalizationHelper; import com.android.inputmethod.latin.personalization.UserHistoryDictionary; import com.android.inputmethod.latin.settings.SettingsValues; import com.android.inputmethod.latin.utils.CollectionUtils; +import com.android.inputmethod.latin.utils.LanguageModelParam; import java.io.File; import java.util.ArrayList; @@ -44,7 +45,7 @@ public class DictionaryFacilitatorForSuggest { public static final String TAG = DictionaryFacilitatorForSuggest.class.getSimpleName(); private final Context mContext; - private final Locale mLocale; + public final Locale mLocale; private final ConcurrentHashMap<String, Dictionary> mDictionaries = CollectionUtils.newConcurrentHashMap(); @@ -223,6 +224,10 @@ public class DictionaryFacilitatorForSuggest { return null != mMainDictionary && mMainDictionary.isInitialized(); } + public boolean hasPersonalizationDictionary() { + return null != mPersonalizationDictionary; + } + public void waitForLoadingMainDictionary(final long timeout, final TimeUnit unit) throws InterruptedException { mLatchForWaitingLoadingMainDictionary.await(timeout, unit); @@ -476,4 +481,26 @@ public class DictionaryFacilitatorForSuggest { oldDict.close(); } } + + // This method gets called only when the IME receives a notification to remove the + // personalization dictionary. + public void clearPersonalizationDictionary() { + if (!hasPersonalizationDictionary()) { + return; + } + mPersonalizationDictionary.clearAndFlushDictionary(); + } + + public void addMultipleDictionaryEntriesToPersonalizationDictionary( + final ArrayList<LanguageModelParam> languageModelParams, + final ExpandableBinaryDictionary.AddMultipleDictionaryEntriesCallback callback) { + if (!hasPersonalizationDictionary()) { + if (callback != null) { + callback.onFinished(); + } + return; + } + mPersonalizationDictionary.addMultipleDictionaryEntriesToDictionary(languageModelParams, + callback); + } } diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java index 7757d2910..ecef20efc 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java @@ -21,12 +21,12 @@ import android.util.Log; import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.keyboard.ProximityInfo; -import com.android.inputmethod.latin.BinaryDictionary.LanguageModelParam; import com.android.inputmethod.latin.makedict.FormatSpec; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.utils.AsyncResultHolder; import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.FileUtils; +import com.android.inputmethod.latin.utils.LanguageModelParam; import com.android.inputmethod.latin.utils.PrioritizedSerialExecutor; import java.io.File; @@ -58,6 +58,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { private static final boolean DBG_STRESS_TEST = false; private static final int TIMEOUT_FOR_READ_OPS_IN_MILLISECONDS = 100; + private static final int TIMEOUT_FOR_READ_OPS_FOR_TESTS_IN_MILLISECONDS = 1000; /** * The maximum length of a word in this dictionary. @@ -761,7 +762,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { } } }); - return holder.get(false, TIMEOUT_FOR_READ_OPS_IN_MILLISECONDS); + return holder.get(false, TIMEOUT_FOR_READ_OPS_FOR_TESTS_IN_MILLISECONDS); } @UsedForTesting diff --git a/java/src/com/android/inputmethod/latin/LastComposedWord.java b/java/src/com/android/inputmethod/latin/LastComposedWord.java index 2e9280c77..8546cebd5 100644 --- a/java/src/com/android/inputmethod/latin/LastComposedWord.java +++ b/java/src/com/android/inputmethod/latin/LastComposedWord.java @@ -42,7 +42,7 @@ public final class LastComposedWord { public final int[] mPrimaryKeyCodes; public final String mTypedWord; - public final String mCommittedWord; + public final CharSequence mCommittedWord; public final String mSeparatorString; public final String mPrevWord; public final int mCapitalizedMode; @@ -58,7 +58,7 @@ public final class LastComposedWord { // Warning: this is using the passed objects as is and fully expects them to be // immutable. Do not fiddle with their contents after you passed them to this constructor. public LastComposedWord(final int[] primaryKeyCodes, final InputPointers inputPointers, - final String typedWord, final String committedWord, final String separatorString, + final String typedWord, final CharSequence committedWord, final String separatorString, final String prevWord, final int capitalizedMode) { mPrimaryKeyCodes = primaryKeyCodes; if (inputPointers != null) { diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 40391694c..3fca4fd19 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -213,7 +213,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen postUpdateSuggestionStrip(); break; case MSG_ON_END_BATCH_INPUT: - latinIme.mInputLogic.endBatchInputAsyncInternal(latinIme.mSettings.getCurrent(), + latinIme.mInputLogic.endBatchInputInternal(latinIme.mSettings.getCurrent(), (SuggestedWords) msg.obj, latinIme.mKeyboardSwitcher); break; case MSG_RESET_CACHES: @@ -234,6 +234,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } public void postResumeSuggestions() { + if (!getOwnerInstance().mSettings.getCurrent().isSuggestionStripVisible()) { + return; + } removeMessages(MSG_RESUME_SUGGESTIONS); sendMessageDelayed(obtainMessage(MSG_RESUME_SUGGESTIONS), mDelayUpdateSuggestions); } @@ -959,11 +962,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen newSelStart, newSelEnd, false /* shouldFinishComposition */); } - // We moved the cursor. If we are touching a word, we need to resume suggestion, - // unless suggestions are off. - if (isSuggestionsStripVisible()) { - mHandler.postResumeSuggestions(); - } + // We moved the cursor. If we are touching a word, we need to resume suggestion. + mHandler.postResumeSuggestions(); // Reset the last recapitalization. mInputLogic.mRecapitalizeStatus.deactivate(); mKeyboardSwitcher.updateShiftState(); @@ -1389,7 +1389,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // We're checking the previous word in the text field against the memorized previous // word. If we are composing a word we should have the second word before the cursor // memorized, otherwise we should have the first. - final String rereadPrevWord = mInputLogic.getNthPreviousWordForSuggestion( + final CharSequence rereadPrevWord = mInputLogic.getNthPreviousWordForSuggestion( currentSettings.mSpacingAndPunctuations, mInputLogic.mWordComposer.isComposingWord() ? 2 : 1); if (!TextUtils.equals(previousWord, rereadPrevWord)) { diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java index b4f2d1a58..ebf65630e 100644 --- a/java/src/com/android/inputmethod/latin/WordComposer.java +++ b/java/src/com/android/inputmethod/latin/WordComposer.java @@ -297,7 +297,7 @@ public final class WordComposer { * the context is nil (typically, at start of text). * @param keyboard the keyboard this is typed on, for coordinate info/proximity. */ - public void setComposingWord(final CharSequence word, final String previousWord, + public void setComposingWord(final CharSequence word, final CharSequence previousWord, final Keyboard keyboard) { reset(); final int length = word.length(); @@ -306,7 +306,7 @@ public final class WordComposer { addKeyInfo(codePoint, keyboard); } mIsResumed = true; - mPreviousWordForSuggestion = previousWord; + mPreviousWordForSuggestion = null == previousWord ? null : previousWord.toString(); } /** @@ -418,9 +418,9 @@ public final class WordComposer { * @param previousWord the previous word as context for suggestions. May be null if none. */ public void setCapitalizedModeAndPreviousWordAtStartComposingTime(final int mode, - final String previousWord) { + final CharSequence previousWord) { mCapitalizedMode = mode; - mPreviousWordForSuggestion = previousWord; + mPreviousWordForSuggestion = null == previousWord ? null : previousWord.toString(); } /** @@ -454,7 +454,8 @@ public final class WordComposer { } // `type' should be one of the LastComposedWord.COMMIT_TYPE_* constants above. - public LastComposedWord commitWord(final int type, final String committedWord, + // committedWord should contain suggestion spans if applicable. + public LastComposedWord commitWord(final int type, final CharSequence committedWord, final String separatorString, final String prevWord) { // Note: currently, we come here whenever we commit a word. If it's a MANUAL_PICK // or a DECIDED_WORD we may cancel the commit later; otherwise, we should deactivate @@ -472,7 +473,7 @@ public final class WordComposer { mCapsCount = 0; mDigitsCount = 0; mIsBatchMode = false; - mPreviousWordForSuggestion = committedWord; + mPreviousWordForSuggestion = committedWord.toString(); mTypedWord.setLength(0); mCodePointSize = 0; mTrailingSingleQuotesCount = 0; diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java index 71b88703a..fcad0470a 100644 --- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java +++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java @@ -17,6 +17,7 @@ package com.android.inputmethod.latin.inputlogic; import android.os.SystemClock; +import android.text.SpannableString; import android.text.TextUtils; import android.text.style.SuggestionSpan; import android.util.Log; @@ -1060,8 +1061,6 @@ public final class InputLogic { // recorrection. This is a temporary, stopgap measure that will be removed later. // TODO: remove this. if (settingsValues.isBrokenByRecorrection()) return; - // A simple way to test for support from the TextView. - if (!mLatinIME.isSuggestionsStripVisible()) return; // Recorrection is not supported in languages without spaces because we don't know // how to segment them yet. if (!settingsValues.mSpacingAndPunctuations.mCurrentLanguageHasSpaces) return; @@ -1168,8 +1167,8 @@ public final class InputLogic { // TODO: remove these arguments final KeyboardSwitcher keyboardSwitcher, final LatinIME.UIHandler handler) { final String previousWord = mLastComposedWord.mPrevWord; - final String originallyTypedWord = mLastComposedWord.mTypedWord; - final String committedWord = mLastComposedWord.mCommittedWord; + final CharSequence originallyTypedWord = mLastComposedWord.mTypedWord; + final CharSequence committedWord = mLastComposedWord.mCommittedWord; final int cancelLength = committedWord.length(); // We want java chars, not codepoints for the following. final int separatorLength = mLastComposedWord.mSeparatorString.length(); @@ -1191,28 +1190,53 @@ public final class InputLogic { if (!TextUtils.isEmpty(previousWord) && !TextUtils.isEmpty(committedWord)) { if (mSuggest != null) { mSuggest.mDictionaryFacilitator.cancelAddingUserHistory( - previousWord, committedWord); + previousWord, committedWord.toString()); + } + } + final SpannableString textToCommit = + new SpannableString(originallyTypedWord + mLastComposedWord.mSeparatorString); + if (committedWord instanceof SpannableString) { + final int lastCharIndex = textToCommit.length() - 1; + // Add the auto-correction to the list of suggestions. + textToCommit.setSpan(new SuggestionSpan(settingsValues.mLocale, + new String[] { committedWord.toString() }, 0 /* flags */), + 0 /* start */, lastCharIndex /* end */, 0 /* flags */); + final SpannableString committedWordWithSuggestionSpans = (SpannableString)committedWord; + final Object[] spans = committedWordWithSuggestionSpans.getSpans(0, + committedWord.length(), Object.class); + for (final Object span : spans) { + // Put all the spans in the original text on this new text. We could remove the + // typed word from the suggestions, but we'd have to make more dynamic instanceof + // checks, to copy the span, copy all suggestions and attributes... And there is + // the risk to drop the originally typed string if there is a subtle bug. There is + // still the committed auto-correction that we reverted from, which is not included + // in the suggestions, that's why we added it with another call to setSpan a few + // lines above. + // The code that re-reads these spans already knows to do the right thing whether + // the typed word is included or not. That should be enough. + textToCommit.setSpan(span, 0 /* start */, lastCharIndex /* end */, + committedWordWithSuggestionSpans.getSpanFlags(span)); } } - final String stringToCommit = originallyTypedWord + mLastComposedWord.mSeparatorString; if (settingsValues.mSpacingAndPunctuations.mCurrentLanguageHasSpaces) { // For languages with spaces, we revert to the typed string, but the cursor is still // after the separator so we don't resume suggestions. If the user wants to correct // the word, they have to press backspace again. - mConnection.commitText(stringToCommit, 1); + mConnection.commitText(textToCommit, 1); } else { // For languages without spaces, we revert the typed string but the cursor is flush // with the typed word, so we need to resume suggestions right away. - mWordComposer.setComposingWord(stringToCommit, previousWord, + mWordComposer.setComposingWord(textToCommit, previousWord, keyboardSwitcher.getKeyboard()); - mConnection.setComposingText(stringToCommit, 1); + mConnection.setComposingText(textToCommit, 1); } if (settingsValues.mIsInternal) { LatinImeLoggerUtils.onSeparator(mLastComposedWord.mSeparatorString, Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE); } if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { - ResearchLogger.latinIME_revertCommit(committedWord, originallyTypedWord, + ResearchLogger.latinIME_revertCommit(committedWord.toString(), + originallyTypedWord.toString(), mWordComposer.isBatchMode(), mLastComposedWord.mSeparatorString); } // Don't restart suggestion yet. We'll restart if the user deletes the @@ -1294,7 +1318,7 @@ public final class InputLogic { * @return the nth previous word before the cursor. */ // TODO: Make this private - public String getNthPreviousWordForSuggestion( + public CharSequence getNthPreviousWordForSuggestion( final SpacingAndPunctuations spacingAndPunctuations, final int nthPreviousWord) { if (spacingAndPunctuations.mCurrentLanguageHasSpaces) { // If we are typing in a language with spaces we can just look up the previous @@ -1510,7 +1534,7 @@ public final class InputLogic { * @param settingsValues the current values of the settings. * @param suggestedWords suggestedWords to use. */ - public void endBatchInputAsyncInternal(final SettingsValues settingsValues, + public void endBatchInputInternal(final SettingsValues settingsValues, final SuggestedWords suggestedWords, // TODO: remove this argument final KeyboardSwitcher keyboardSwitcher) { @@ -1645,8 +1669,10 @@ public final class InputLogic { public void commitChosenWord(final SettingsValues settingsValues, final String chosenWord, final int commitType, final String separatorString) { final SuggestedWords suggestedWords = mSuggestedWords; - mConnection.commitText(SuggestionSpanUtils.getTextWithSuggestionSpan(mLatinIME, chosenWord, - suggestedWords), 1); + final CharSequence chosenWordWithSuggestions = + SuggestionSpanUtils.getTextWithSuggestionSpan(mLatinIME, chosenWord, + suggestedWords); + mConnection.commitText(chosenWordWithSuggestions, 1); // TODO: we pass 2 here, but would it be better to move this above and pass 1 instead? final String prevWord = mConnection.getNthPreviousWord( settingsValues.mSpacingAndPunctuations, 2); @@ -1657,7 +1683,7 @@ public final class InputLogic { // LastComposedWord#didCommitTypedWord by string equality of the remembered // strings. mLastComposedWord = mWordComposer.commitWord(commitType, - chosenWord, separatorString, prevWord); + chosenWordWithSuggestions, separatorString, prevWord); final boolean shouldDiscardPreviousWordForSuggestion; if (0 == StringUtils.codePointCount(separatorString)) { // Separator is 0-length, we can keep the previous word for suggestion. Either this diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java index ea010b6f5..b09e20591 100644 --- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java +++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogicHandler.java @@ -29,8 +29,7 @@ import com.android.inputmethod.latin.Suggest.OnGetSuggestedWordsCallback; /** * A helper to manage deferred tasks for the input logic. */ -// TODO: Make this package private -public class InputLogicHandler implements Handler.Callback { +class InputLogicHandler implements Handler.Callback { final Handler mNonUIThreadHandler; // TODO: remove this reference. final LatinIME mLatinIME; diff --git a/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java b/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java index 701c29023..cc57d13dc 100644 --- a/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java +++ b/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java @@ -20,13 +20,13 @@ import android.content.Context; import android.util.Log; import com.android.inputmethod.annotations.UsedForTesting; -import com.android.inputmethod.latin.BinaryDictionary.LanguageModelParam; import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.Dictionary; import com.android.inputmethod.latin.ExpandableBinaryDictionary; import com.android.inputmethod.latin.makedict.DictDecoder; import com.android.inputmethod.latin.makedict.FormatSpec; import com.android.inputmethod.latin.makedict.UnsupportedFormatException; +import com.android.inputmethod.latin.utils.LanguageModelParam; import com.android.inputmethod.latin.utils.UserHistoryDictIOUtils; import com.android.inputmethod.latin.utils.UserHistoryDictIOUtils.OnAddWordListener; diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java index 9b2b981d5..7c9b2a169 100644 --- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java +++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java @@ -18,10 +18,8 @@ package com.android.inputmethod.latin.personalization; import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.Dictionary; -import com.android.inputmethod.latin.utils.CollectionUtils; import java.io.File; -import java.util.ArrayList; import java.util.Locale; import android.content.Context; @@ -29,9 +27,6 @@ import android.content.Context; public class PersonalizationDictionary extends DecayingExpandableBinaryDictionaryBase { /* package */ static final String NAME = PersonalizationDictionary.class.getSimpleName(); - private final ArrayList<PersonalizationDictionaryUpdateSession> mSessions = - CollectionUtils.newArrayList(); - /* package */ PersonalizationDictionary(final Context context, final Locale locale) { super(context, locale, Dictionary.TYPE_PERSONALIZATION, getDictNameWithLocale(NAME, locale)); @@ -44,14 +39,4 @@ public class PersonalizationDictionary extends DecayingExpandableBinaryDictionar super(context, locale, Dictionary.TYPE_PERSONALIZATION, getDictNameWithLocale(NAME, locale), dictFile); } - - public void registerUpdateSession(PersonalizationDictionaryUpdateSession session) { - session.setPredictionDictionary(this); - mSessions.add(session); - session.onDictionaryReady(); - } - - public void unRegisterUpdateSession(PersonalizationDictionaryUpdateSession session) { - mSessions.remove(session); - } } diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdateSession.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdateSession.java deleted file mode 100644 index 3eb8f35a9..000000000 --- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionaryUpdateSession.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * 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 android.content.Context; - -import com.android.inputmethod.latin.BinaryDictionary.LanguageModelParam; -import com.android.inputmethod.latin.ExpandableBinaryDictionary; - -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.Locale; - -/** - * This class is a session where a data provider can communicate with a personalization - * dictionary. - */ -public abstract class PersonalizationDictionaryUpdateSession { - public WeakReference<PersonalizationDictionary> mDictionary; - public final Locale mSystemLocale; - - public PersonalizationDictionaryUpdateSession(final Locale locale) { - mSystemLocale = locale; - } - - public abstract void onDictionaryReady(); - - public abstract void onDictionaryClosed(final Context context); - - public void setPredictionDictionary(final PersonalizationDictionary dictionary) { - mDictionary = new WeakReference<PersonalizationDictionary>(dictionary); - } - - protected PersonalizationDictionary getDictionary() { - return mDictionary == null ? null : mDictionary.get(); - } - - private void unsetDictionary() { - final PersonalizationDictionary dictionary = getDictionary(); - if (dictionary == null) { - return; - } - dictionary.unRegisterUpdateSession(this); - } - - public void clearAndFlushDictionary() { - final PersonalizationDictionary dictionary = getDictionary(); - if (dictionary == null) { - return; - } - dictionary.clearAndFlushDictionary(); - } - - public void closeSession(final Context context) { - unsetDictionary(); - onDictionaryClosed(context); - } - - // TODO: Support multi locale. - public void addMultipleDictionaryEntriesToDictionary( - final ArrayList<LanguageModelParam> languageModelParams, - final ExpandableBinaryDictionary.AddMultipleDictionaryEntriesCallback callback) { - final PersonalizationDictionary dictionary = getDictionary(); - if (dictionary == null) { - if (callback != null) { - callback.onFinished(); - } - return; - } - dictionary.addMultipleDictionaryEntriesToDictionary(languageModelParams, callback); - } -} diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java index 38b22e5f6..df64bcec1 100644 --- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java +++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java @@ -71,13 +71,6 @@ public class PersonalizationHelper { } } - public static void registerPersonalizationDictionaryUpdateSession(final Context context, - final PersonalizationDictionaryUpdateSession session, final Locale locale) { - final PersonalizationDictionary personalizationDictionary = - getPersonalizationDictionary(context, locale); - personalizationDictionary.registerUpdateSession(session); - } - public static PersonalizationDictionary getPersonalizationDictionary( final Context context, final Locale locale) { final String localeStr = locale.toString(); diff --git a/java/src/com/android/inputmethod/latin/utils/LanguageModelParam.java b/java/src/com/android/inputmethod/latin/utils/LanguageModelParam.java new file mode 100644 index 000000000..a1d641508 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/utils/LanguageModelParam.java @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2014 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.utils; + +import android.util.Log; + +import com.android.inputmethod.latin.Dictionary; +import com.android.inputmethod.latin.DictionaryFacilitatorForSuggest; +import com.android.inputmethod.latin.settings.SpacingAndPunctuations; + +import java.util.ArrayList; +import java.util.Locale; + +// Note: this class is used as a parameter type of a native method. You should be careful when you +// rename this class or field name. See BinaryDictionary#addMultipleDictionaryEntriesNative(). +public final class LanguageModelParam { + private static final String TAG = LanguageModelParam.class.getSimpleName(); + private static final boolean DEBUG = false; + private static final boolean DEBUG_TOKEN = false; + + // For now, these probability values are being referred to only when we add new entries to + // decaying dynamic binary dictionaries. When these are referred to, what matters is 0 or + // non-0. Thus, it's not meaningful to compare 10, 100, and so on. + // TODO: Revise the logic in ForgettingCurveUtils in native code. + private static final int UNIGRAM_PROBABILITY_FOR_VALID_WORD = 100; + private static final int UNIGRAM_PROBABILITY_FOR_OOV_WORD = 10; + private static final int BIGRAM_PROBABILITY_FOR_VALID_WORD = 0; + private static final int BIGRAM_PROBABILITY_FOR_OOV_WORD = 0; + + public final String mTargetWord; + public final int[] mWord0; + public final int[] mWord1; + // TODO: this needs to be a list of shortcuts + public final int[] mShortcutTarget; + public final int mUnigramProbability; + public final int mBigramProbability; + public final int mShortcutProbability; + public final boolean mIsNotAWord; + public final boolean mIsBlacklisted; + // Time stamp in seconds. + public final int mTimestamp; + + // Constructor for unigram. TODO: support shortcuts + public LanguageModelParam(final String word, final int unigramProbability, + final int timestamp) { + this(null /* word0 */, word, unigramProbability, Dictionary.NOT_A_PROBABILITY, timestamp); + } + + // Constructor for unigram and bigram. + public LanguageModelParam(final String word0, final String word1, + final int unigramProbability, final int bigramProbability, + final int timestamp) { + mTargetWord = word1; + mWord0 = (word0 == null) ? null : StringUtils.toCodePointArray(word0); + mWord1 = StringUtils.toCodePointArray(word1); + mShortcutTarget = null; + mUnigramProbability = unigramProbability; + mBigramProbability = bigramProbability; + mShortcutProbability = Dictionary.NOT_A_PROBABILITY; + mIsNotAWord = false; + mIsBlacklisted = false; + mTimestamp = timestamp; + } + + // Process a list of words and return a list of {@link LanguageModelParam} objects. + public static ArrayList<LanguageModelParam> createLanguageModelParamsFrom( + final ArrayList<String> tokens, final int timestamp, + final DictionaryFacilitatorForSuggest dictionaryFacilitator, + final SpacingAndPunctuations spacingAndPunctuations) { + final ArrayList<LanguageModelParam> languageModelParams = + CollectionUtils.newArrayList(); + final int N = tokens.size(); + String prevWord = null; + for (int i = 0; i < N; ++i) { + final String tempWord = tokens.get(i); + if (StringUtils.isEmptyStringOrWhiteSpaces(tempWord)) { + // just skip this token + if (DEBUG_TOKEN) { + Log.d(TAG, "--- isEmptyStringOrWhiteSpaces: \"" + tempWord + "\""); + } + continue; + } + if (!DictionaryInfoUtils.looksValidForDictionaryInsertion( + tempWord, spacingAndPunctuations)) { + if (DEBUG_TOKEN) { + Log.d(TAG, "--- not looksValidForDictionaryInsertion: \"" + + tempWord + "\""); + } + // Sentence terminator found. Split. + prevWord = null; + continue; + } + if (DEBUG_TOKEN) { + Log.d(TAG, "--- word: \"" + tempWord + "\""); + } + final LanguageModelParam languageModelParam = + detectWhetherVaildWordOrNotAndGetLanguageModelParam( + prevWord, tempWord, timestamp, dictionaryFacilitator); + languageModelParams.add(languageModelParam); + prevWord = languageModelParam.mTargetWord; + } + return languageModelParams; + } + + private static LanguageModelParam detectWhetherVaildWordOrNotAndGetLanguageModelParam( + final String prevWord, final String targetWord, final int timestamp, + final DictionaryFacilitatorForSuggest dictionaryFacilitator) { + final Locale locale = dictionaryFacilitator.mLocale; + if (!dictionaryFacilitator.isValidWord(targetWord, true /* ignoreCase */)) { + // OOV word. + return createAndGetLanguageModelParamOfWord(prevWord, targetWord, timestamp, + false /* isValidWord */, locale); + } + if (dictionaryFacilitator.isValidWord(targetWord, false /* ignoreCase */)) { + return createAndGetLanguageModelParamOfWord(prevWord, targetWord, timestamp, + true /* isValidWord */, locale); + } + final String lowerCaseTargetWord = targetWord.toLowerCase(locale); + if (dictionaryFacilitator.isValidWord(lowerCaseTargetWord, false /* ignoreCase */)) { + // Add the lower-cased word. + return createAndGetLanguageModelParamOfWord(prevWord, lowerCaseTargetWord, + timestamp, true /* isValidWord */, locale); + } + // Treat the word as an OOV word. + return createAndGetLanguageModelParamOfWord(prevWord, targetWord, timestamp, + false /* isValidWord */, locale); + } + + private static LanguageModelParam createAndGetLanguageModelParamOfWord( + final String prevWord, final String targetWord, final int timestamp, + final boolean isValidWord, final Locale locale) { + final String word; + if (StringUtils.getCapitalizationType(targetWord) == StringUtils.CAPITALIZE_FIRST + && prevWord == null && !isValidWord) { + word = targetWord.toLowerCase(locale); + } else { + word = targetWord; + } + final int unigramProbability = isValidWord ? + UNIGRAM_PROBABILITY_FOR_VALID_WORD : UNIGRAM_PROBABILITY_FOR_OOV_WORD; + if (prevWord == null) { + if (DEBUG) { + Log.d(TAG, "--- add unigram: current(" + + (isValidWord ? "Valid" : "OOV") + ") = " + word); + } + return new LanguageModelParam(word, unigramProbability, timestamp); + } + if (DEBUG) { + Log.d(TAG, "--- add bigram: prev = " + prevWord + ", current(" + + (isValidWord ? "Valid" : "OOV") + ") = " + word); + } + final int bigramProbability = isValidWord ? + BIGRAM_PROBABILITY_FOR_VALID_WORD : BIGRAM_PROBABILITY_FOR_OOV_WORD; + return new LanguageModelParam(prevWord, word, unigramProbability, + bigramProbability, timestamp); + } +} |