aboutsummaryrefslogtreecommitdiffstats
path: root/java/src/com/android/inputmethod/latin
diff options
context:
space:
mode:
Diffstat (limited to 'java/src/com/android/inputmethod/latin')
-rw-r--r--java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java55
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionary.java154
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java5
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java5
-rw-r--r--java/src/com/android/inputmethod/latin/Constants.java17
-rw-r--r--java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java7
-rw-r--r--java/src/com/android/inputmethod/latin/Dictionary.java21
-rw-r--r--java/src/com/android/inputmethod/latin/DictionaryCollection.java29
-rw-r--r--java/src/com/android/inputmethod/latin/DictionaryFacilitator.java (renamed from java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java)98
-rw-r--r--java/src/com/android/inputmethod/latin/DictionaryFactory.java3
-rw-r--r--java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java85
-rw-r--r--java/src/com/android/inputmethod/latin/InputAttributes.java32
-rw-r--r--java/src/com/android/inputmethod/latin/InputView.java20
-rw-r--r--java/src/com/android/inputmethod/latin/LastComposedWord.java2
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java247
-rw-r--r--java/src/com/android/inputmethod/latin/LatinImeLogger.java74
-rw-r--r--java/src/com/android/inputmethod/latin/PrevWordsInfo.java35
-rw-r--r--java/src/com/android/inputmethod/latin/PunctuationSuggestions.java4
-rw-r--r--java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java16
-rw-r--r--java/src/com/android/inputmethod/latin/RichInputConnection.java66
-rw-r--r--java/src/com/android/inputmethod/latin/RichInputMethodManager.java5
-rw-r--r--java/src/com/android/inputmethod/latin/SubtypeSwitcher.java3
-rw-r--r--java/src/com/android/inputmethod/latin/Suggest.java53
-rw-r--r--java/src/com/android/inputmethod/latin/SuggestedWords.java56
-rw-r--r--java/src/com/android/inputmethod/latin/WordComposer.java78
-rw-r--r--java/src/com/android/inputmethod/latin/debug/ExternalDictionaryGetterForDebug.java3
-rw-r--r--java/src/com/android/inputmethod/latin/define/ProductionFlag.java7
-rw-r--r--java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java308
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/FormatSpec.java5
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/WordProperty.java9
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/AccountUtils.java4
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java7
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/PersonalizationDataChunk.java37
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java6
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionarySessionRegistrar.java9
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java15
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java15
-rw-r--r--java/src/com/android/inputmethod/latin/settings/AdditionalSubtypeSettings.java9
-rw-r--r--java/src/com/android/inputmethod/latin/settings/DebugSettings.java88
-rw-r--r--java/src/com/android/inputmethod/latin/settings/Settings.java9
-rw-r--r--java/src/com/android/inputmethod/latin/settings/SettingsFragment.java33
-rw-r--r--java/src/com/android/inputmethod/latin/settings/SettingsValues.java50
-rw-r--r--java/src/com/android/inputmethod/latin/setup/SetupStartIndicatorView.java6
-rw-r--r--java/src/com/android/inputmethod/latin/setup/SetupStepIndicatorView.java6
-rw-r--r--java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java3
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java10
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java68
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java5
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/DictAndKeyboard.java6
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java5
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/SentenceLevelAdapter.java185
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/SynchronouslyLoadedContactsBinaryDictionary.java4
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/SynchronouslyLoadedUserBinaryDictionary.java4
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java6
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java29
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java14
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java75
-rw-r--r--java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java6
-rw-r--r--java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordFragment.java4
-rw-r--r--java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryList.java2
-rw-r--r--java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java3
-rw-r--r--java/src/com/android/inputmethod/latin/utils/AutoCorrectionUtils.java9
-rw-r--r--java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java43
-rw-r--r--java/src/com/android/inputmethod/latin/utils/CollectionUtils.java76
-rw-r--r--java/src/com/android/inputmethod/latin/utils/CsvUtils.java2
-rw-r--r--java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java2
-rw-r--r--java/src/com/android/inputmethod/latin/utils/DistracterFilter.java182
-rw-r--r--java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatches.java129
-rw-r--r--java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingIsInDictionary.java59
-rw-r--r--java/src/com/android/inputmethod/latin/utils/ExecutorUtils.java36
-rw-r--r--java/src/com/android/inputmethod/latin/utils/FragmentUtils.java4
-rw-r--r--java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java13
-rw-r--r--java/src/com/android/inputmethod/latin/utils/JsonUtils.java2
-rw-r--r--java/src/com/android/inputmethod/latin/utils/LanguageModelParam.java45
-rw-r--r--java/src/com/android/inputmethod/latin/utils/LatinImeLoggerUtils.java77
-rw-r--r--java/src/com/android/inputmethod/latin/utils/LeakGuardHandlerWrapper.java2
-rw-r--r--java/src/com/android/inputmethod/latin/utils/LocaleUtils.java2
-rw-r--r--java/src/com/android/inputmethod/latin/utils/PrioritizedSerialExecutor.java122
-rw-r--r--java/src/com/android/inputmethod/latin/utils/RecapitalizeStatus.java34
-rw-r--r--java/src/com/android/inputmethod/latin/utils/ResourceUtils.java7
-rw-r--r--java/src/com/android/inputmethod/latin/utils/StringUtils.java21
-rw-r--r--java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java13
-rw-r--r--java/src/com/android/inputmethod/latin/utils/TargetPackageInfoGetterTask.java3
-rw-r--r--java/src/com/android/inputmethod/latin/utils/TypefaceUtils.java4
-rw-r--r--java/src/com/android/inputmethod/latin/utils/UsabilityStudyLogUtils.java293
-rw-r--r--java/src/com/android/inputmethod/latin/utils/UserLogRingCharBuffer.java137
86 files changed, 1532 insertions, 2010 deletions
diff --git a/java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java b/java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java
index 54bc29559..eb8b34ccd 100644
--- a/java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java
+++ b/java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java
@@ -16,14 +16,14 @@
package com.android.inputmethod.latin;
-import com.android.inputmethod.latin.settings.SettingsValues;
-
import android.content.Context;
import android.media.AudioManager;
import android.os.Vibrator;
import android.view.HapticFeedbackConstants;
import android.view.View;
+import com.android.inputmethod.latin.settings.SettingsValues;
+
/**
* This class gathers audio feedback and haptic feedback functions.
*
@@ -86,40 +86,41 @@ public final class AudioAndHapticFeedbackManager {
if (mAudioManager == null) {
return;
}
- if (mSoundOn) {
- final int sound;
- switch (code) {
- case Constants.CODE_DELETE:
- sound = AudioManager.FX_KEYPRESS_DELETE;
- break;
- case Constants.CODE_ENTER:
- sound = AudioManager.FX_KEYPRESS_RETURN;
- break;
- case Constants.CODE_SPACE:
- sound = AudioManager.FX_KEYPRESS_SPACEBAR;
- break;
- default:
- sound = AudioManager.FX_KEYPRESS_STANDARD;
- break;
- }
- mAudioManager.playSoundEffect(sound, mSettingsValues.mKeypressSoundVolume);
+ if (!mSoundOn) {
+ return;
+ }
+ final int sound;
+ switch (code) {
+ case Constants.CODE_DELETE:
+ sound = AudioManager.FX_KEYPRESS_DELETE;
+ break;
+ case Constants.CODE_ENTER:
+ sound = AudioManager.FX_KEYPRESS_RETURN;
+ break;
+ case Constants.CODE_SPACE:
+ sound = AudioManager.FX_KEYPRESS_SPACEBAR;
+ break;
+ default:
+ sound = AudioManager.FX_KEYPRESS_STANDARD;
+ break;
}
+ mAudioManager.playSoundEffect(sound, mSettingsValues.mKeypressSoundVolume);
}
public void performHapticFeedback(final View viewToPerformHapticFeedbackOn) {
if (!mSettingsValues.mVibrateOn) {
return;
}
- if (mSettingsValues.mKeypressVibrationDuration < 0) {
- // Go ahead with the system default
- if (viewToPerformHapticFeedbackOn != null) {
- viewToPerformHapticFeedbackOn.performHapticFeedback(
- HapticFeedbackConstants.KEYBOARD_TAP,
- HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
- }
+ if (mSettingsValues.mKeypressVibrationDuration >= 0) {
+ vibrate(mSettingsValues.mKeypressVibrationDuration);
return;
}
- vibrate(mSettingsValues.mKeypressVibrationDuration);
+ // Go ahead with the system default
+ if (viewToPerformHapticFeedbackOn != null) {
+ viewToPerformHapticFeedbackOn.performHapticFeedback(
+ HapticFeedbackConstants.KEYBOARD_TAP,
+ HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING);
+ }
}
public void onSettingsChanged(final SettingsValues settingsValues) {
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index e7ab02ac1..1d087439d 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -30,7 +30,6 @@ import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
import com.android.inputmethod.latin.makedict.WordProperty;
import com.android.inputmethod.latin.settings.NativeSuggestOptions;
import com.android.inputmethod.latin.utils.BinaryDictionaryUtils;
-import com.android.inputmethod.latin.utils.CollectionUtils;
import com.android.inputmethod.latin.utils.FileUtils;
import com.android.inputmethod.latin.utils.JniUtils;
import com.android.inputmethod.latin.utils.LanguageModelParam;
@@ -104,8 +103,7 @@ public final class BinaryDictionary extends Dictionary {
private final NativeSuggestOptions mNativeSuggestOptions = new NativeSuggestOptions();
- private final SparseArray<DicTraverseSession> mDicTraverseSessions =
- CollectionUtils.newSparseArray();
+ private final SparseArray<DicTraverseSession> mDicTraverseSessions = new SparseArray<>();
// TODO: There should be a way to remove used DicTraverseSession objects from
// {@code mDicTraverseSessions}.
@@ -185,13 +183,15 @@ public final class BinaryDictionary extends Dictionary {
private static native void getHeaderInfoNative(long dict, int[] outHeaderSize,
int[] outFormatVersion, ArrayList<int[]> outAttributeKeys,
ArrayList<int[]> outAttributeValues);
- private static native void flushNative(long dict, String filePath);
+ private static native boolean flushNative(long dict, String filePath);
private static native boolean needsToRunGCNative(long dict, boolean mindsBlockByGC);
- private static native void flushWithGCNative(long dict, String filePath);
+ private static native boolean flushWithGCNative(long dict, String filePath);
private static native void closeNative(long dict);
private static native int getFormatVersionNative(long dict);
private static native int getProbabilityNative(long dict, int[] word);
- private static native int getBigramProbabilityNative(long dict, int[] word0, int[] word1);
+ private static native int getMaxProbabilityOfExactMatchesNative(long dict, int[] word);
+ private static native int getBigramProbabilityNative(long dict, int[] word0,
+ boolean isBeginningOfSentence, int[] word1);
private static native void getWordPropertyNative(long dict, int[] word,
int[] outCodePoints, boolean[] outFlags, int[] outProbabilityInfo,
ArrayList<int[]> outBigramTargets, ArrayList<int[]> outBigramProbabilityInfo,
@@ -200,15 +200,17 @@ public final class BinaryDictionary extends Dictionary {
private static native void getSuggestionsNative(long dict, long proximityInfo,
long traverseSession, int[] xCoordinates, int[] yCoordinates, int[] times,
int[] pointerIds, int[] inputCodePoints, int inputSize, int[] suggestOptions,
- int[] prevWordCodePointArray, int[] outputSuggestionCount, int[] outputCodePoints,
- int[] outputScores, int[] outputIndices, int[] outputTypes,
- int[] outputAutoCommitFirstWordConfidence, float[] inOutLanguageWeight);
- private static native void addUnigramWordNative(long dict, int[] word, int probability,
- int[] shortcutTarget, int shortcutProbability, boolean isNotAWord,
- boolean isBlacklisted, int timestamp);
- private static native void addBigramWordsNative(long dict, int[] word0, int[] word1,
- int probability, int timestamp);
- private static native void removeBigramWordsNative(long dict, int[] word0, int[] word1);
+ int[] prevWordCodePointArray, boolean isBeginningOfSentence,
+ int[] outputSuggestionCount, int[] outputCodePoints, int[] outputScores,
+ int[] outputIndices, int[] outputTypes, int[] outputAutoCommitFirstWordConfidence,
+ float[] inOutLanguageWeight);
+ private static native boolean addUnigramWordNative(long dict, int[] word, int probability,
+ int[] shortcutTarget, int shortcutProbability, boolean isBeginningOfSentence,
+ boolean isNotAWord, boolean isBlacklisted, int timestamp);
+ private static native boolean addBigramWordsNative(long dict, int[] word0,
+ boolean isBeginningOfSentence, int[] word1, int probability, int timestamp);
+ private static native boolean removeBigramWordsNative(long dict, int[] word0,
+ boolean isBeginningOfSentence, int[] word1);
private static native int addMultipleDictionaryEntriesNative(long dict,
LanguageModelParam[] languageModelParams, int startIndex);
private static native String getPropertyNative(long dict, String query);
@@ -245,11 +247,11 @@ public final class BinaryDictionary extends Dictionary {
}
final int[] outHeaderSize = new int[1];
final int[] outFormatVersion = new int[1];
- final ArrayList<int[]> outAttributeKeys = CollectionUtils.newArrayList();
- final ArrayList<int[]> outAttributeValues = CollectionUtils.newArrayList();
+ final ArrayList<int[]> outAttributeKeys = new ArrayList<>();
+ final ArrayList<int[]> outAttributeValues = new ArrayList<>();
getHeaderInfoNative(mNativeDict, outHeaderSize, outFormatVersion, outAttributeKeys,
outAttributeValues);
- final HashMap<String, String> attributes = new HashMap<String, String>();
+ final HashMap<String, String> attributes = new HashMap<>();
for (int i = 0; i < outAttributeKeys.size(); i++) {
final String attributeKey = StringUtils.getStringFromNullTerminatedCodePointArray(
outAttributeKeys.get(i));
@@ -301,14 +303,15 @@ public final class BinaryDictionary extends Dictionary {
getTraverseSession(sessionId).getSession(), inputPointers.getXCoordinates(),
inputPointers.getYCoordinates(), inputPointers.getTimes(),
inputPointers.getPointerIds(), mInputCodePoints, inputSize,
- mNativeSuggestOptions.getOptions(), prevWordCodePointArray, mOutputSuggestionCount,
+ mNativeSuggestOptions.getOptions(), prevWordCodePointArray,
+ prevWordsInfo.mIsBeginningOfSentence, mOutputSuggestionCount,
mOutputCodePoints, mOutputScores, mSpaceIndices, mOutputTypes,
mOutputAutoCommitFirstWordConfidence, mInputOutputLanguageWeight);
if (inOutLanguageWeight != null) {
inOutLanguageWeight[0] = mInputOutputLanguageWeight[0];
}
final int count = mOutputSuggestionCount[0];
- final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList();
+ final ArrayList<SuggestedWordInfo> suggestions = new ArrayList<>();
for (int j = 0; j < count; ++j) {
final int start = j * MAX_WORD_LENGTH;
int len = 0;
@@ -316,23 +319,18 @@ public final class BinaryDictionary extends Dictionary {
++len;
}
if (len > 0) {
- final int flags = mOutputTypes[j] & SuggestedWordInfo.KIND_MASK_FLAGS;
- if (blockOffensiveWords
- && 0 != (flags & SuggestedWordInfo.KIND_FLAG_POSSIBLY_OFFENSIVE)
- && 0 == (flags & SuggestedWordInfo.KIND_FLAG_EXACT_MATCH)) {
+ final SuggestedWordInfo suggestedWordInfo =
+ new SuggestedWordInfo(new String(mOutputCodePoints, start, len),
+ mOutputScores[j], mOutputTypes[j], this /* sourceDict */,
+ mSpaceIndices[j] /* indexOfTouchPointOfSecondWord */,
+ mOutputAutoCommitFirstWordConfidence[0]);
+ if (blockOffensiveWords && suggestedWordInfo.isPossiblyOffensive()
+ && !suggestedWordInfo.isExactMatch()) {
// If we block potentially offensive words, and if the word is possibly
// offensive, then we don't output it unless it's also an exact match.
continue;
}
- final int kind = mOutputTypes[j] & SuggestedWordInfo.KIND_MASK_KIND;
- final int score = SuggestedWordInfo.KIND_WHITELIST == kind
- ? SuggestedWordInfo.MAX_SCORE : mOutputScores[j];
- // TODO: check that all users of the `kind' parameter are ready to accept
- // flags too and pass mOutputTypes[j] instead of kind
- suggestions.add(new SuggestedWordInfo(new String(mOutputCodePoints, start, len),
- score, kind, this /* sourceDict */,
- mSpaceIndices[j] /* indexOfTouchPointOfSecondWord */,
- mOutputAutoCommitFirstWordConfidence[0]));
+ suggestions.add(suggestedWordInfo);
}
}
return suggestions;
@@ -347,29 +345,37 @@ public final class BinaryDictionary extends Dictionary {
}
@Override
- public boolean isValidWord(final String word) {
+ public boolean isInDictionary(final String word) {
return getFrequency(word) != NOT_A_PROBABILITY;
}
@Override
public int getFrequency(final String word) {
- if (word == null) return NOT_A_PROBABILITY;
+ if (TextUtils.isEmpty(word)) return NOT_A_PROBABILITY;
int[] codePoints = StringUtils.toCodePointArray(word);
return getProbabilityNative(mNativeDict, codePoints);
}
+ @Override
+ public int getMaxFrequencyOfExactMatches(final String word) {
+ if (TextUtils.isEmpty(word)) return NOT_A_PROBABILITY;
+ int[] codePoints = StringUtils.toCodePointArray(word);
+ return getMaxProbabilityOfExactMatchesNative(mNativeDict, codePoints);
+ }
+
@UsedForTesting
public boolean isValidNgram(final PrevWordsInfo prevWordsInfo, final String word) {
return getNgramProbability(prevWordsInfo, word) != NOT_A_PROBABILITY;
}
public int getNgramProbability(final PrevWordsInfo prevWordsInfo, final String word) {
- if (TextUtils.isEmpty(prevWordsInfo.mPrevWord) || TextUtils.isEmpty(word)) {
+ if (!prevWordsInfo.isValid() || TextUtils.isEmpty(word)) {
return NOT_A_PROBABILITY;
}
final int[] codePoints0 = StringUtils.toCodePointArray(prevWordsInfo.mPrevWord);
final int[] codePoints1 = StringUtils.toCodePointArray(word);
- return getBigramProbabilityNative(mNativeDict, codePoints0, codePoints1);
+ return getBigramProbabilityNative(mNativeDict, codePoints0,
+ prevWordsInfo.mIsBeginningOfSentence, codePoints1);
}
public WordProperty getWordProperty(final String word) {
@@ -381,10 +387,10 @@ public final class BinaryDictionary extends Dictionary {
final boolean[] outFlags = new boolean[FORMAT_WORD_PROPERTY_OUTPUT_FLAG_COUNT];
final int[] outProbabilityInfo =
new int[FORMAT_WORD_PROPERTY_OUTPUT_PROBABILITY_INFO_COUNT];
- final ArrayList<int[]> outBigramTargets = CollectionUtils.newArrayList();
- final ArrayList<int[]> outBigramProbabilityInfo = CollectionUtils.newArrayList();
- final ArrayList<int[]> outShortcutTargets = CollectionUtils.newArrayList();
- final ArrayList<Integer> outShortcutProbabilities = CollectionUtils.newArrayList();
+ final ArrayList<int[]> outBigramTargets = new ArrayList<>();
+ final ArrayList<int[]> outBigramProbabilityInfo = new ArrayList<>();
+ final ArrayList<int[]> outShortcutTargets = new ArrayList<>();
+ final ArrayList<Integer> outShortcutProbabilities = new ArrayList<>();
getWordPropertyNative(mNativeDict, codePoints, outCodePoints, outFlags, outProbabilityInfo,
outBigramTargets, outBigramProbabilityInfo, outShortcutTargets,
outShortcutProbabilities);
@@ -419,42 +425,53 @@ public final class BinaryDictionary extends Dictionary {
}
// Add a unigram entry to binary dictionary with unigram attributes in native code.
- public void addUnigramEntry(final String word, final int probability,
- final String shortcutTarget, final int shortcutProbability, final boolean isNotAWord,
+ public boolean addUnigramEntry(final String word, final int probability,
+ final String shortcutTarget, final int shortcutProbability,
+ final boolean isBeginningOfSentence, final boolean isNotAWord,
final boolean isBlacklisted, final int timestamp) {
- if (TextUtils.isEmpty(word)) {
- return;
+ if (word == null || (word.isEmpty() && !isBeginningOfSentence)) {
+ return false;
}
final int[] codePoints = StringUtils.toCodePointArray(word);
final int[] shortcutTargetCodePoints = (shortcutTarget != null) ?
StringUtils.toCodePointArray(shortcutTarget) : null;
- addUnigramWordNative(mNativeDict, codePoints, probability, shortcutTargetCodePoints,
- shortcutProbability, isNotAWord, isBlacklisted, timestamp);
+ if (!addUnigramWordNative(mNativeDict, codePoints, probability, shortcutTargetCodePoints,
+ shortcutProbability, isBeginningOfSentence, isNotAWord, isBlacklisted, timestamp)) {
+ return false;
+ }
mHasUpdated = true;
+ return true;
}
// Add an n-gram entry to the binary dictionary with timestamp in native code.
- public void addNgramEntry(final PrevWordsInfo prevWordsInfo, final String word,
- final int probability,
- final int timestamp) {
- if (TextUtils.isEmpty(prevWordsInfo.mPrevWord) || TextUtils.isEmpty(word)) {
- return;
+ public boolean addNgramEntry(final PrevWordsInfo prevWordsInfo, final String word,
+ final int probability, final int timestamp) {
+ if (!prevWordsInfo.isValid() || TextUtils.isEmpty(word)) {
+ return false;
}
final int[] codePoints0 = StringUtils.toCodePointArray(prevWordsInfo.mPrevWord);
final int[] codePoints1 = StringUtils.toCodePointArray(word);
- addBigramWordsNative(mNativeDict, codePoints0, codePoints1, probability, timestamp);
+ if (!addBigramWordsNative(mNativeDict, codePoints0, prevWordsInfo.mIsBeginningOfSentence,
+ codePoints1, probability, timestamp)) {
+ return false;
+ }
mHasUpdated = true;
+ return true;
}
// Remove an n-gram entry from the binary dictionary in native code.
- public void removeNgramEntry(final PrevWordsInfo prevWordsInfo, final String word) {
- if (TextUtils.isEmpty(prevWordsInfo.mPrevWord) || TextUtils.isEmpty(word)) {
- return;
+ public boolean removeNgramEntry(final PrevWordsInfo prevWordsInfo, final String word) {
+ if (!prevWordsInfo.isValid() || TextUtils.isEmpty(word)) {
+ return false;
}
final int[] codePoints0 = StringUtils.toCodePointArray(prevWordsInfo.mPrevWord);
final int[] codePoints1 = StringUtils.toCodePointArray(word);
- removeBigramWordsNative(mNativeDict, codePoints0, codePoints1);
+ if (!removeBigramWordsNative(mNativeDict, codePoints0, prevWordsInfo.mIsBeginningOfSentence,
+ codePoints1)) {
+ return false;
+ }
mHasUpdated = true;
+ return true;
}
public void addMultipleDictionaryEntries(final LanguageModelParam[] languageModelParams) {
@@ -484,26 +501,33 @@ public final class BinaryDictionary extends Dictionary {
}
// Flush to dict file if the dictionary has been updated.
- public void flush() {
- if (!isValidDictionary()) return;
+ public boolean flush() {
+ if (!isValidDictionary()) return false;
if (mHasUpdated) {
- flushNative(mNativeDict, mDictFilePath);
+ if (!flushNative(mNativeDict, mDictFilePath)) {
+ return false;
+ }
reopen();
}
+ return true;
}
// Run GC and flush to dict file if the dictionary has been updated.
- public void flushWithGCIfHasUpdated() {
+ public boolean flushWithGCIfHasUpdated() {
if (mHasUpdated) {
- flushWithGC();
+ return flushWithGC();
}
+ return true;
}
// Run GC and flush to dict file.
- public void flushWithGC() {
- if (!isValidDictionary()) return;
- flushWithGCNative(mNativeDict, mDictFilePath);
+ public boolean flushWithGC() {
+ if (!isValidDictionary()) return false;
+ if (!flushWithGCNative(mNativeDict, mDictFilePath)) {
+ return false;
+ }
reopen();
+ return true;
}
/**
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
index 72757e086..10b1f1b77 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
@@ -29,7 +29,6 @@ import android.util.Log;
import com.android.inputmethod.dictionarypack.DictionaryPackConstants;
import com.android.inputmethod.dictionarypack.MD5Calculator;
-import com.android.inputmethod.latin.utils.CollectionUtils;
import com.android.inputmethod.latin.utils.DictionaryInfoUtils;
import com.android.inputmethod.latin.utils.DictionaryInfoUtils.DictionaryInfo;
import com.android.inputmethod.latin.utils.FileTransforms;
@@ -44,8 +43,8 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.util.Arrays;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
@@ -165,7 +164,7 @@ public final class BinaryDictionaryFileDumper {
if (cursor.getCount() <= 0 || !cursor.moveToFirst()) {
return Collections.<WordListInfo>emptyList();
}
- final ArrayList<WordListInfo> list = CollectionUtils.newArrayList();
+ final ArrayList<WordListInfo> list = new ArrayList<>();
do {
final String wordListId = cursor.getString(0);
final String wordListLocale = cursor.getString(1);
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
index 4c49cb31c..867c18686 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
@@ -24,7 +24,6 @@ import android.util.Log;
import com.android.inputmethod.latin.makedict.DictionaryHeader;
import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
import com.android.inputmethod.latin.utils.BinaryDictionaryUtils;
-import com.android.inputmethod.latin.utils.CollectionUtils;
import com.android.inputmethod.latin.utils.DictionaryInfoUtils;
import com.android.inputmethod.latin.utils.LocaleUtils;
@@ -160,7 +159,7 @@ final public class BinaryDictionaryGetter {
public static File[] getCachedWordLists(final String locale, final Context context) {
final File[] directoryList = DictionaryInfoUtils.getCachedDirectoryList(context);
if (null == directoryList) return EMPTY_FILE_ARRAY;
- final HashMap<String, FileAndMatchLevel> cacheFiles = CollectionUtils.newHashMap();
+ final HashMap<String, FileAndMatchLevel> cacheFiles = new HashMap<>();
for (File directory : directoryList) {
if (!directory.isDirectory()) continue;
final String dirLocale =
@@ -273,7 +272,7 @@ final public class BinaryDictionaryGetter {
final DictPackSettings dictPackSettings = new DictPackSettings(context);
boolean foundMainDict = false;
- final ArrayList<AssetFileAddress> fileList = CollectionUtils.newArrayList();
+ final ArrayList<AssetFileAddress> fileList = new ArrayList<>();
// cachedWordLists may not be null, see doc for getCachedDictionaryList
for (final File f : cachedWordLists) {
final String wordListId = DictionaryInfoUtils.getWordListIdFromFileName(f.getName());
diff --git a/java/src/com/android/inputmethod/latin/Constants.java b/java/src/com/android/inputmethod/latin/Constants.java
index 67ca59540..35012a452 100644
--- a/java/src/com/android/inputmethod/latin/Constants.java
+++ b/java/src/com/android/inputmethod/latin/Constants.java
@@ -158,6 +158,10 @@ public final class Constants {
// A hint on how many characters to cache from the TextView. A good value of this is given by
// how many characters we need to be able to almost always find the caps mode.
public static final int EDITOR_CONTENTS_CACHE_SIZE = 1024;
+ // How many characters we accept for the recapitalization functionality. This needs to be
+ // large enough for all reasonable purposes, but avoid purposeful attacks. 100k sounds about
+ // right for this.
+ public static final int MAX_CHARACTERS_FOR_RECAPITALIZATION = 1024 * 100;
// Must be equal to MAX_WORD_LENGTH in native/jni/src/defines.h
public static final int DICTIONARY_MAX_WORD_LENGTH = 48;
@@ -192,7 +196,6 @@ public final class Constants {
public static final int CODE_SPACE = ' ';
public static final int CODE_PERIOD = '.';
public static final int CODE_COMMA = ',';
- public static final int CODE_ARMENIAN_PERIOD = 0x0589;
public static final int CODE_DASH = '-';
public static final int CODE_SINGLE_QUOTE = '\'';
public static final int CODE_DOUBLE_QUOTE = '"';
@@ -208,6 +211,8 @@ public final class Constants {
public static final int CODE_CLOSING_SQUARE_BRACKET = ']';
public static final int CODE_CLOSING_CURLY_BRACKET = '}';
public static final int CODE_CLOSING_ANGLE_BRACKET = '>';
+ public static final int CODE_INVERTED_QUESTION_MARK = 0xBF; // ¿
+ public static final int CODE_INVERTED_EXCLAMATION_MARK = 0xA1; // ¡
/**
* Special keys code. Must be negative.
@@ -249,14 +254,16 @@ public final class Constants {
case CODE_LANGUAGE_SWITCH: return "languageSwitch";
case CODE_EMOJI: return "emoji";
case CODE_SHIFT_ENTER: return "shiftEnter";
+ case CODE_ALPHA_FROM_EMOJI: return "alpha";
case CODE_UNSPECIFIED: return "unspec";
case CODE_TAB: return "tab";
case CODE_ENTER: return "enter";
- case CODE_ALPHA_FROM_EMOJI: return "alpha";
+ case CODE_SPACE: return "space";
default:
- if (code < CODE_SPACE) return String.format("'\\u%02x'", code);
- if (code < 0x100) return String.format("'%c'", code);
- return String.format("'\\u%04x'", code);
+ if (code < CODE_SPACE) return String.format("\\u%02x", code);
+ if (code < 0x100) return String.format("%c", code);
+ if (code < 0x10000) return String.format("\\u04x", code);
+ return String.format("\\U%05x", code);
}
}
diff --git a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
index 3fb76b142..dd5b376a1 100644
--- a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
@@ -31,7 +31,6 @@ import android.util.Log;
import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.latin.personalization.AccountUtils;
-import com.android.inputmethod.latin.utils.CollectionUtils;
import com.android.inputmethod.latin.utils.ExecutorUtils;
import com.android.inputmethod.latin.utils.StringUtils;
@@ -180,7 +179,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
private void addWordsLocked(final Cursor cursor) {
int count = 0;
- final ArrayList<String> names = CollectionUtils.newArrayList();
+ final ArrayList<String> names = new ArrayList<>();
while (!cursor.isAfterLast() && count < MAX_CONTACT_COUNT) {
String name = cursor.getString(INDEX_NAME);
if (isValidName(name)) {
@@ -224,7 +223,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
*/
private void addNameLocked(final String name) {
int len = StringUtils.codePointCount(name);
- PrevWordsInfo prevWordsInfo = new PrevWordsInfo(null);
+ PrevWordsInfo prevWordsInfo = PrevWordsInfo.EMPTY_PREV_WORDS_INFO;
// TODO: Better tokenization for non-Latin writing systems
for (int i = 0; i < len; i++) {
if (Character.isLetter(name.codePointAt(i))) {
@@ -298,7 +297,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
if (null == cursor) {
return false;
}
- final ArrayList<String> names = CollectionUtils.newArrayList();
+ final ArrayList<String> names = new ArrayList<>();
try {
if (cursor.moveToFirst()) {
while (!cursor.isAfterLast()) {
diff --git a/java/src/com/android/inputmethod/latin/Dictionary.java b/java/src/com/android/inputmethod/latin/Dictionary.java
index aab16653e..b55ed125f 100644
--- a/java/src/com/android/inputmethod/latin/Dictionary.java
+++ b/java/src/com/android/inputmethod/latin/Dictionary.java
@@ -16,6 +16,7 @@
package com.android.inputmethod.latin;
+import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.keyboard.ProximityInfo;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
@@ -85,16 +86,28 @@ public abstract class Dictionary {
final int sessionId, final float[] inOutLanguageWeight);
/**
- * Checks if the given word occurs in the dictionary
+ * Checks if the given word has to be treated as a valid word. Please note that some
+ * dictionaries have entries that should be treated as invalid words.
* @param word the word to search for. The search should be case-insensitive.
- * @return true if the word exists, false otherwise
+ * @return true if the word is valid, false otherwise
*/
- abstract public boolean isValidWord(final String word);
+ public boolean isValidWord(final String word) {
+ return isInDictionary(word);
+ }
+
+ /**
+ * Checks if the given word is in the dictionary regardless of it being valid or not.
+ */
+ abstract public boolean isInDictionary(final String word);
public int getFrequency(final String word) {
return NOT_A_PROBABILITY;
}
+ public int getMaxFrequencyOfExactMatches(final String word) {
+ return NOT_A_PROBABILITY;
+ }
+
/**
* Compares the contents of the character array with the typed word and returns true if they
* are the same.
@@ -161,7 +174,7 @@ public abstract class Dictionary {
}
@Override
- public boolean isValidWord(String word) {
+ public boolean isInDictionary(String word) {
return false;
}
}
diff --git a/java/src/com/android/inputmethod/latin/DictionaryCollection.java b/java/src/com/android/inputmethod/latin/DictionaryCollection.java
index e6e4e0938..89d61ce2a 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryCollection.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryCollection.java
@@ -20,7 +20,6 @@ import android.util.Log;
import com.android.inputmethod.keyboard.ProximityInfo;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
-import com.android.inputmethod.latin.utils.CollectionUtils;
import java.util.ArrayList;
import java.util.Collection;
@@ -36,22 +35,22 @@ public final class DictionaryCollection extends Dictionary {
public DictionaryCollection(final String dictType) {
super(dictType);
- mDictionaries = CollectionUtils.newCopyOnWriteArrayList();
+ mDictionaries = new CopyOnWriteArrayList<>();
}
public DictionaryCollection(final String dictType, final Dictionary... dictionaries) {
super(dictType);
if (null == dictionaries) {
- mDictionaries = CollectionUtils.newCopyOnWriteArrayList();
+ mDictionaries = new CopyOnWriteArrayList<>();
} else {
- mDictionaries = CollectionUtils.newCopyOnWriteArrayList(dictionaries);
+ mDictionaries = new CopyOnWriteArrayList<>(dictionaries);
mDictionaries.removeAll(Collections.singleton(null));
}
}
public DictionaryCollection(final String dictType, final Collection<Dictionary> dictionaries) {
super(dictType);
- mDictionaries = CollectionUtils.newCopyOnWriteArrayList(dictionaries);
+ mDictionaries = new CopyOnWriteArrayList<>(dictionaries);
mDictionaries.removeAll(Collections.singleton(null));
}
@@ -67,7 +66,7 @@ public final class DictionaryCollection extends Dictionary {
ArrayList<SuggestedWordInfo> suggestions = dictionaries.get(0).getSuggestions(composer,
prevWordsInfo, proximityInfo, blockOffensiveWords, additionalFeaturesOptions,
sessionId, inOutLanguageWeight);
- if (null == suggestions) suggestions = CollectionUtils.newArrayList();
+ if (null == suggestions) suggestions = new ArrayList<>();
final int length = dictionaries.size();
for (int i = 1; i < length; ++ i) {
final ArrayList<SuggestedWordInfo> sugg = dictionaries.get(i).getSuggestions(composer,
@@ -79,9 +78,9 @@ public final class DictionaryCollection extends Dictionary {
}
@Override
- public boolean isValidWord(final String word) {
+ public boolean isInDictionary(final String word) {
for (int i = mDictionaries.size() - 1; i >= 0; --i)
- if (mDictionaries.get(i).isValidWord(word)) return true;
+ if (mDictionaries.get(i).isInDictionary(word)) return true;
return false;
}
@@ -90,9 +89,17 @@ public final class DictionaryCollection extends Dictionary {
int maxFreq = -1;
for (int i = mDictionaries.size() - 1; i >= 0; --i) {
final int tempFreq = mDictionaries.get(i).getFrequency(word);
- if (tempFreq >= maxFreq) {
- maxFreq = tempFreq;
- }
+ maxFreq = Math.max(tempFreq, maxFreq);
+ }
+ return maxFreq;
+ }
+
+ @Override
+ public int getMaxFrequencyOfExactMatches(final String word) {
+ int maxFreq = -1;
+ for (int i = mDictionaries.size() - 1; i >= 0; --i) {
+ final int tempFreq = mDictionaries.get(i).getMaxFrequencyOfExactMatches(word);
+ maxFreq = Math.max(tempFreq, maxFreq);
}
return maxFreq;
}
diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
index 301b832b6..09401c0c6 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
@@ -19,14 +19,18 @@ package com.android.inputmethod.latin;
import android.content.Context;
import android.text.TextUtils;
import android.util.Log;
+import android.view.inputmethod.InputMethodSubtype;
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.ContextualDictionary;
+import com.android.inputmethod.latin.personalization.PersonalizationDataChunk;
import com.android.inputmethod.latin.personalization.PersonalizationDictionary;
import com.android.inputmethod.latin.personalization.UserHistoryDictionary;
-import com.android.inputmethod.latin.utils.CollectionUtils;
+import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
+import com.android.inputmethod.latin.utils.DistracterFilter;
+import com.android.inputmethod.latin.utils.DistracterFilterCheckingIsInDictionary;
import com.android.inputmethod.latin.utils.ExecutorUtils;
import com.android.inputmethod.latin.utils.LanguageModelParam;
import com.android.inputmethod.latin.utils.SuggestionResults;
@@ -37,16 +41,17 @@ import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
import java.util.Locale;
import java.util.Map;
-import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
// TODO: Consolidate dictionaries in native code.
-public class DictionaryFacilitatorForSuggest {
- public static final String TAG = DictionaryFacilitatorForSuggest.class.getSimpleName();
+public class DictionaryFacilitator {
+ public static final String TAG = DictionaryFacilitator.class.getSimpleName();
// HACK: This threshold is being used when adding a capitalized entry in the User History
// dictionary.
@@ -57,8 +62,9 @@ public class DictionaryFacilitatorForSuggest {
private volatile CountDownLatch mLatchForWaitingLoadingMainDictionary = new CountDownLatch(0);
// To synchronize assigning mDictionaries to ensure closing dictionaries.
private final Object mLock = new Object();
+ private final DistracterFilter mDistracterFilter;
- private static final String[] DICT_TYPES_ORDERED_TO_GET_SUGGESTION =
+ private static final String[] DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS =
new String[] {
Dictionary.TYPE_MAIN,
Dictionary.TYPE_USER_HISTORY,
@@ -68,8 +74,8 @@ public class DictionaryFacilitatorForSuggest {
Dictionary.TYPE_CONTEXTUAL
};
- private static final Map<String, Class<? extends ExpandableBinaryDictionary>>
- DICT_TYPE_TO_CLASS = CollectionUtils.newHashMap();
+ public static final Map<String, Class<? extends ExpandableBinaryDictionary>>
+ DICT_TYPE_TO_CLASS = new HashMap<>();
static {
DICT_TYPE_TO_CLASS.put(Dictionary.TYPE_USER_HISTORY, UserHistoryDictionary.class);
@@ -84,8 +90,8 @@ public class DictionaryFacilitatorForSuggest {
new Class[] { Context.class, Locale.class, File.class };
private static final String[] SUB_DICT_TYPES =
- Arrays.copyOfRange(DICT_TYPES_ORDERED_TO_GET_SUGGESTION, 1 /* start */,
- DICT_TYPES_ORDERED_TO_GET_SUGGESTION.length);
+ Arrays.copyOfRange(DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS, 1 /* start */,
+ DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS.length);
/**
* Class contains dictionaries for a locale.
@@ -94,7 +100,7 @@ public class DictionaryFacilitatorForSuggest {
public final Locale mLocale;
private Dictionary mMainDict;
public final ConcurrentHashMap<String, ExpandableBinaryDictionary> mSubDictMap =
- CollectionUtils.newConcurrentHashMap();
+ new ConcurrentHashMap<>();
public Dictionaries() {
mLocale = null;
@@ -162,7 +168,17 @@ public class DictionaryFacilitatorForSuggest {
public void onUpdateMainDictionaryAvailability(boolean isMainDictionaryAvailable);
}
- public DictionaryFacilitatorForSuggest() {}
+ public DictionaryFacilitator() {
+ mDistracterFilter = DistracterFilter.EMPTY_DISTRACTER_FILTER;
+ }
+
+ public DictionaryFacilitator(final DistracterFilter distracterFilter) {
+ mDistracterFilter = distracterFilter;
+ }
+
+ public void updateEnabledSubtypes(final List<InputMethodSubtype> enabledSubtypes) {
+ mDistracterFilter.updateEnabledSubtypes(enabledSubtypes);
+ }
public Locale getLocale() {
return mDictionaries.mLocale;
@@ -196,7 +212,7 @@ public class DictionaryFacilitatorForSuggest {
// We always try to have the main dictionary. Other dictionaries can be unused.
final boolean reloadMainDictionary = localeHasBeenChanged || forceReloadMainDictionary;
// TODO: Make subDictTypesToUse configurable by resource or a static final list.
- final Set<String> subDictTypesToUse = CollectionUtils.newHashSet();
+ final HashSet<String> subDictTypesToUse = new HashSet<>();
if (useContactsDict) {
subDictTypesToUse.add(Dictionary.TYPE_CONTACTS);
}
@@ -215,7 +231,7 @@ public class DictionaryFacilitatorForSuggest {
newMainDict = mDictionaries.getDict(Dictionary.TYPE_MAIN);
}
- final Map<String, ExpandableBinaryDictionary> subDicts = CollectionUtils.newHashMap();
+ final Map<String, ExpandableBinaryDictionary> subDicts = new HashMap<>();
for (final String dictType : SUB_DICT_TYPES) {
if (!subDictTypesToUse.contains(dictType)) {
// This dictionary will not be used.
@@ -288,7 +304,7 @@ public class DictionaryFacilitatorForSuggest {
final ArrayList<String> dictionaryTypes, final HashMap<String, File> dictionaryFiles,
final Map<String, Map<String, String>> additionalDictAttributes) {
Dictionary mainDictionary = null;
- final Map<String, ExpandableBinaryDictionary> subDicts = CollectionUtils.newHashMap();
+ final Map<String, ExpandableBinaryDictionary> subDicts = new HashMap<>();
for (final String dictType : dictionaryTypes) {
if (dictType.equals(Dictionary.TYPE_MAIN)) {
@@ -318,9 +334,10 @@ public class DictionaryFacilitatorForSuggest {
dictionaries = mDictionaries;
mDictionaries = new Dictionaries();
}
- for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTION) {
+ for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) {
dictionaries.closeDict(dictType);
}
+ mDistracterFilter.close();
}
// The main dictionary could have been loaded asynchronously. Don't cache the return value
@@ -392,7 +409,7 @@ public class DictionaryFacilitatorForSuggest {
if (userHistoryDictionary == null) {
return;
}
- final int maxFreq = getMaxFrequency(word);
+ final int maxFreq = getFrequency(word);
if (maxFreq == 0 && blockPotentiallyOffensive) {
return;
}
@@ -432,7 +449,7 @@ public class DictionaryFacilitatorForSuggest {
// We don't add words with 0-frequency (assuming they would be profanity etc.).
final boolean isValid = maxFreq > 0;
UserHistoryDictionary.addToDictionary(userHistoryDictionary, prevWordsInfo, secondWord,
- isValid, timeStampInSeconds);
+ isValid, timeStampInSeconds, mDistracterFilter);
}
public void cancelAddingUserHistory(final PrevWordsInfo prevWordsInfo,
@@ -453,7 +470,7 @@ public class DictionaryFacilitatorForSuggest {
final SuggestionResults suggestionResults =
new SuggestionResults(dictionaries.mLocale, SuggestedWords.MAX_SUGGESTIONS);
final float[] languageWeight = new float[] { Dictionary.NOT_A_LANGUAGE_WEIGHT };
- for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTION) {
+ for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) {
final Dictionary dictionary = dictionaries.getDict(dictType);
if (null == dictionary) continue;
final ArrayList<SuggestedWordInfo> dictionarySuggestions =
@@ -486,7 +503,7 @@ public class DictionaryFacilitatorForSuggest {
return false;
}
final String lowerCasedWord = word.toLowerCase(dictionaries.mLocale);
- for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTION) {
+ for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) {
final Dictionary dictionary = dictionaries.getDict(dictType);
// Ideally the passed map would come out of a {@link java.util.concurrent.Future} and
// would be immutable once it's finished initializing, but concretely a null test is
@@ -500,16 +517,22 @@ public class DictionaryFacilitatorForSuggest {
return false;
}
- private int getMaxFrequency(final String word) {
+ private int getFrequencyInternal(final String word,
+ final boolean isGettingMaxFrequencyOfExactMatches) {
if (TextUtils.isEmpty(word)) {
return Dictionary.NOT_A_PROBABILITY;
}
- int maxFreq = -1;
+ int maxFreq = Dictionary.NOT_A_PROBABILITY;
final Dictionaries dictionaries = mDictionaries;
- for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTION) {
+ for (final String dictType : DICT_TYPES_ORDERED_TO_GET_SUGGESTIONS) {
final Dictionary dictionary = dictionaries.getDict(dictType);
if (dictionary == null) continue;
- final int tempFreq = dictionary.getFrequency(word);
+ final int tempFreq;
+ if (isGettingMaxFrequencyOfExactMatches) {
+ tempFreq = dictionary.getMaxFrequencyOfExactMatches(word);
+ } else {
+ tempFreq = dictionary.getFrequency(word);
+ }
if (tempFreq >= maxFreq) {
maxFreq = tempFreq;
}
@@ -517,6 +540,14 @@ public class DictionaryFacilitatorForSuggest {
return maxFreq;
}
+ public int getFrequency(final String word) {
+ return getFrequencyInternal(word, false /* isGettingMaxFrequencyOfExactMatches */);
+ }
+
+ public int getMaxFrequencyOfExactMatches(final String word) {
+ return getFrequencyInternal(word, true /* isGettingMaxFrequencyOfExactMatches */);
+ }
+
public void clearUserHistoryDictionary() {
final ExpandableBinaryDictionary userHistoryDict =
mDictionaries.getSubDict(Dictionary.TYPE_USER_HISTORY);
@@ -537,13 +568,26 @@ public class DictionaryFacilitatorForSuggest {
personalizationDict.clear();
}
- public void addMultipleDictionaryEntriesToPersonalizationDictionary(
- final ArrayList<LanguageModelParam> languageModelParams,
+ public void addEntriesToPersonalizationDictionary(
+ final PersonalizationDataChunk personalizationDataChunk,
+ final SpacingAndPunctuations spacingAndPunctuations,
final ExpandableBinaryDictionary.AddMultipleDictionaryEntriesCallback callback) {
final ExpandableBinaryDictionary personalizationDict =
mDictionaries.getSubDict(Dictionary.TYPE_PERSONALIZATION);
- if (personalizationDict == null || languageModelParams == null
- || languageModelParams.isEmpty()) {
+ if (personalizationDict == null) {
+ if (callback != null) {
+ callback.onFinished();
+ }
+ return;
+ }
+ final ArrayList<LanguageModelParam> languageModelParams =
+ LanguageModelParam.createLanguageModelParamsFrom(
+ personalizationDataChunk.mTokens,
+ personalizationDataChunk.mTimestampInSeconds,
+ this /* dictionaryFacilitator */, spacingAndPunctuations,
+ new DistracterFilterCheckingIsInDictionary(
+ mDistracterFilter, personalizationDict));
+ if (languageModelParams == null || languageModelParams.isEmpty()) {
if (callback != null) {
callback.onFinished();
}
diff --git a/java/src/com/android/inputmethod/latin/DictionaryFactory.java b/java/src/com/android/inputmethod/latin/DictionaryFactory.java
index e09c309ea..59de4f82a 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryFactory.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryFactory.java
@@ -23,7 +23,6 @@ import android.content.res.Resources;
import android.util.Log;
import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.latin.utils.CollectionUtils;
import com.android.inputmethod.latin.utils.DictionaryInfoUtils;
import java.io.File;
@@ -55,7 +54,7 @@ public final class DictionaryFactory {
createReadOnlyBinaryDictionary(context, locale));
}
- final LinkedList<Dictionary> dictList = CollectionUtils.newLinkedList();
+ final LinkedList<Dictionary> dictList = new LinkedList<>();
final ArrayList<AssetFileAddress> assetFileList =
BinaryDictionaryGetter.getDictionaryFiles(locale, context);
if (null != assetFileList) {
diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
index d67253c3b..4dbfa0bac 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
@@ -27,6 +27,7 @@ import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
import com.android.inputmethod.latin.makedict.WordProperty;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.utils.CombinedFormatUtils;
+import com.android.inputmethod.latin.utils.DistracterFilter;
import com.android.inputmethod.latin.utils.ExecutorUtils;
import com.android.inputmethod.latin.utils.FileUtils;
import com.android.inputmethod.latin.utils.LanguageModelParam;
@@ -48,12 +49,12 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
* queries in native code. This binary dictionary is written to internal storage.
*/
abstract public class ExpandableBinaryDictionary extends Dictionary {
+ private static final boolean DEBUG = false;
/** Used for Log actions from this class */
private static final String TAG = ExpandableBinaryDictionary.class.getSimpleName();
/** Whether to print debug output to log */
- private static boolean DEBUG = false;
private static final boolean DBG_STRESS_TEST = false;
private static final int TIMEOUT_FOR_READ_OPS_IN_MILLISECONDS = 100;
@@ -191,7 +192,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
}
protected Map<String, String> getHeaderAttributeMap() {
- HashMap<String, String> attributeMap = new HashMap<String, String>();
+ HashMap<String, String> attributeMap = new HashMap<>();
if (mAdditionalAttributeMap != null) {
attributeMap.putAll(mAdditionalAttributeMap);
}
@@ -271,9 +272,10 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
/**
* Adds unigram information of a word to the dictionary. May overwrite an existing entry.
*/
- public void addUnigramEntry(final String word, final int frequency,
+ public void addUnigramEntryWithCheckingDistracter(final String word, final int frequency,
final String shortcutTarget, final int shortcutFreq, final boolean isNotAWord,
- final boolean isBlacklisted, final int timestamp) {
+ final boolean isBlacklisted, final int timestamp,
+ final DistracterFilter distracterFilter) {
reloadDictionaryIfRequired();
asyncExecuteTaskWithWriteLock(new Runnable() {
@Override
@@ -281,6 +283,11 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
if (mBinaryDictionary == null) {
return;
}
+ if (distracterFilter.isDistracterToWordsInDictionaries(
+ PrevWordsInfo.EMPTY_PREV_WORDS_INFO, word, mLocale)) {
+ // The word is a distracter.
+ return;
+ }
runGCIfRequiredLocked(true /* mindsBlockByGC */);
addUnigramLocked(word, frequency, shortcutTarget, shortcutFreq,
isNotAWord, isBlacklisted, timestamp);
@@ -291,8 +298,10 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
protected void addUnigramLocked(final String word, final int frequency,
final String shortcutTarget, final int shortcutFreq, final boolean isNotAWord,
final boolean isBlacklisted, final int timestamp) {
- mBinaryDictionary.addUnigramEntry(word, frequency, shortcutTarget, shortcutFreq,
- isNotAWord, isBlacklisted, timestamp);
+ if (!mBinaryDictionary.addUnigramEntry(word, frequency, shortcutTarget, shortcutFreq,
+ false /* isBeginningOfSentence */, isNotAWord, isBlacklisted, timestamp)) {
+ Log.e(TAG, "Cannot add unigram entry. word: " + word);
+ }
}
/**
@@ -315,13 +324,18 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
protected void addNgramEntryLocked(final PrevWordsInfo prevWordsInfo, final String word,
final int frequency, final int timestamp) {
- mBinaryDictionary.addNgramEntry(prevWordsInfo, word, frequency, timestamp);
+ if (!mBinaryDictionary.addNgramEntry(prevWordsInfo, word, frequency, timestamp)) {
+ if (DEBUG) {
+ Log.i(TAG, "Cannot add n-gram entry.");
+ Log.i(TAG, " PrevWordsInfo: " + prevWordsInfo + ", word: " + word);
+ }
+ }
}
/**
* Dynamically remove the n-gram entry in the dictionary.
*/
- public void removeNgramDynamically(final PrevWordsInfo prevWordsInfo, final String word1) {
+ public void removeNgramDynamically(final PrevWordsInfo prevWordsInfo, final String word) {
reloadDictionaryIfRequired();
asyncExecuteTaskWithWriteLock(new Runnable() {
@Override
@@ -330,7 +344,12 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
return;
}
runGCIfRequiredLocked(true /* mindsBlockByGC */);
- mBinaryDictionary.removeNgramEntry(prevWordsInfo, word1);
+ if (!mBinaryDictionary.removeNgramEntry(prevWordsInfo, word)) {
+ if (DEBUG) {
+ Log.i(TAG, "Cannot remove n-gram entry.");
+ Log.i(TAG, " PrevWordsInfo: " + prevWordsInfo + ", word: " + word);
+ }
+ }
}
});
}
@@ -401,7 +420,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
}
@Override
- public boolean isValidWord(final String word) {
+ public boolean isInDictionary(final String word) {
reloadDictionaryIfRequired();
boolean lockAcquired = false;
try {
@@ -411,10 +430,10 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
if (mBinaryDictionary == null) {
return false;
}
- return isValidWordLocked(word);
+ return isInDictionaryLocked(word);
}
} catch (final InterruptedException e) {
- Log.e(TAG, "Interrupted tryLock() in isValidWord().", e);
+ Log.e(TAG, "Interrupted tryLock() in isInDictionary().", e);
} finally {
if (lockAcquired) {
mLock.readLock().unlock();
@@ -423,11 +442,35 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
return false;
}
- protected boolean isValidWordLocked(final String word) {
+ protected boolean isInDictionaryLocked(final String word) {
if (mBinaryDictionary == null) return false;
- return mBinaryDictionary.isValidWord(word);
+ return mBinaryDictionary.isInDictionary(word);
+ }
+
+ @Override
+ public int getMaxFrequencyOfExactMatches(final String word) {
+ reloadDictionaryIfRequired();
+ boolean lockAcquired = false;
+ try {
+ lockAcquired = mLock.readLock().tryLock(
+ TIMEOUT_FOR_READ_OPS_IN_MILLISECONDS, TimeUnit.MILLISECONDS);
+ if (lockAcquired) {
+ if (mBinaryDictionary == null) {
+ return NOT_A_PROBABILITY;
+ }
+ return mBinaryDictionary.getMaxFrequencyOfExactMatches(word);
+ }
+ } catch (final InterruptedException e) {
+ Log.e(TAG, "Interrupted tryLock() in getMaxFrequencyOfExactMatches().", e);
+ } finally {
+ if (lockAcquired) {
+ mLock.readLock().unlock();
+ }
+ }
+ return NOT_A_PROBABILITY;
}
+
protected boolean isValidNgramLocked(final PrevWordsInfo prevWordsInfo, final String word) {
if (mBinaryDictionary == null) return false;
return mBinaryDictionary.isValidNgram(prevWordsInfo, word);
@@ -553,20 +596,6 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
});
}
- // TODO: Implement BinaryDictionary.isInDictionary().
- @UsedForTesting
- public boolean isInUnderlyingBinaryDictionaryForTests(final String word) {
- mLock.readLock().lock();
- try {
- if (mBinaryDictionary != null && mDictType == Dictionary.TYPE_USER_HISTORY) {
- return mBinaryDictionary.isValidWord(word);
- }
- return false;
- } finally {
- mLock.readLock().unlock();
- }
- }
-
@UsedForTesting
public void waitAllTasksForTests() {
final CountDownLatch countDownLatch = new CountDownLatch(1);
diff --git a/java/src/com/android/inputmethod/latin/InputAttributes.java b/java/src/com/android/inputmethod/latin/InputAttributes.java
index df4948322..e1ae3dfe3 100644
--- a/java/src/com/android/inputmethod/latin/InputAttributes.java
+++ b/java/src/com/android/inputmethod/latin/InputAttributes.java
@@ -16,11 +16,13 @@
package com.android.inputmethod.latin;
+import static com.android.inputmethod.latin.Constants.ImeOption.NO_MICROPHONE;
+import static com.android.inputmethod.latin.Constants.ImeOption.NO_MICROPHONE_COMPAT;
+
import android.text.InputType;
import android.util.Log;
import android.view.inputmethod.EditorInfo;
-import com.android.inputmethod.latin.utils.CollectionUtils;
import com.android.inputmethod.latin.utils.InputTypeUtils;
import com.android.inputmethod.latin.utils.StringUtils;
@@ -36,12 +38,17 @@ public final class InputAttributes {
final public String mTargetApplicationPackageName;
final public boolean mInputTypeNoAutoCorrect;
final public boolean mIsPasswordField;
- final public boolean mIsSettingsSuggestionStripOn;
+ final public boolean mShouldShowSuggestions;
final public boolean mApplicationSpecifiedCompletionOn;
final public boolean mShouldInsertSpacesAutomatically;
final private int mInputType;
+ final private EditorInfo mEditorInfo;
+ final private String mPackageNameForPrivateImeOptions;
- public InputAttributes(final EditorInfo editorInfo, final boolean isFullscreenMode) {
+ public InputAttributes(final EditorInfo editorInfo, final boolean isFullscreenMode,
+ final String packageNameForPrivateImeOptions) {
+ mEditorInfo = editorInfo;
+ mPackageNameForPrivateImeOptions = packageNameForPrivateImeOptions;
mTargetApplicationPackageName = null != editorInfo ? editorInfo.packageName : null;
final int inputType = null != editorInfo ? editorInfo.inputType : 0;
final int inputClass = inputType & InputType.TYPE_MASK_CLASS;
@@ -63,7 +70,7 @@ public final class InputAttributes {
Log.w(TAG, String.format("Unexpected input class: inputType=0x%08x"
+ " imeOptions=0x%08x", inputType, editorInfo.imeOptions));
}
- mIsSettingsSuggestionStripOn = false;
+ mShouldShowSuggestions = false;
mInputTypeNoAutoCorrect = false;
mApplicationSpecifiedCompletionOn = false;
mShouldInsertSpacesAutomatically = false;
@@ -82,13 +89,13 @@ public final class InputAttributes {
// TODO: Have a helper method in InputTypeUtils
// Make sure that passwords are not displayed in {@link SuggestionStripView}.
- final boolean noSuggestionStrip = mIsPasswordField
+ final boolean shouldSuppressSuggestions = mIsPasswordField
|| InputTypeUtils.isEmailVariation(variation)
|| InputType.TYPE_TEXT_VARIATION_URI == variation
|| InputType.TYPE_TEXT_VARIATION_FILTER == variation
|| flagNoSuggestions
|| flagAutoComplete;
- mIsSettingsSuggestionStripOn = !noSuggestionStrip;
+ mShouldShowSuggestions = !shouldSuppressSuggestions;
mShouldInsertSpacesAutomatically = InputTypeUtils.isAutoSpaceFriendlyType(inputType);
@@ -112,6 +119,15 @@ public final class InputAttributes {
return editorInfo.inputType == mInputType;
}
+ public boolean hasNoMicrophoneKeyOption() {
+ @SuppressWarnings("deprecation")
+ final boolean deprecatedNoMicrophone = InputAttributes.inPrivateImeOptions(
+ null, NO_MICROPHONE_COMPAT, mEditorInfo);
+ final boolean noMicrophone = InputAttributes.inPrivateImeOptions(
+ mPackageNameForPrivateImeOptions, NO_MICROPHONE, mEditorInfo);
+ return noMicrophone || deprecatedNoMicrophone;
+ }
+
@SuppressWarnings("unused")
private void dumpFlags(final int inputType) {
final int inputClass = inputType & InputType.TYPE_MASK_CLASS;
@@ -214,7 +230,7 @@ public final class InputAttributes {
}
private static String toFlagsString(final int flags) {
- final ArrayList<String> flagsArray = CollectionUtils.newArrayList();
+ final ArrayList<String> flagsArray = new ArrayList<>();
if (0 != (flags & InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS))
flagsArray.add("TYPE_TEXT_FLAG_NO_SUGGESTIONS");
if (0 != (flags & InputType.TYPE_TEXT_FLAG_MULTI_LINE))
@@ -242,7 +258,7 @@ public final class InputAttributes {
mInputType,
(mInputTypeNoAutoCorrect ? " noAutoCorrect" : ""),
(mIsPasswordField ? " password" : ""),
- (mIsSettingsSuggestionStripOn ? " suggestionStrip" : ""),
+ (mShouldShowSuggestions ? " shouldShowSuggestions" : ""),
(mApplicationSpecifiedCompletionOn ? " appSpecified" : ""),
(mShouldInsertSpacesAutomatically ? " insertSpaces" : ""),
mTargetApplicationPackageName);
diff --git a/java/src/com/android/inputmethod/latin/InputView.java b/java/src/com/android/inputmethod/latin/InputView.java
index ea7859e60..0801cfa88 100644
--- a/java/src/com/android/inputmethod/latin/InputView.java
+++ b/java/src/com/android/inputmethod/latin/InputView.java
@@ -23,12 +23,14 @@ import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;
+import com.android.inputmethod.accessibility.AccessibilityUtils;
import com.android.inputmethod.keyboard.MainKeyboardView;
import com.android.inputmethod.latin.suggestions.MoreSuggestionsView;
import com.android.inputmethod.latin.suggestions.SuggestionStripView;
public final class InputView extends LinearLayout {
private final Rect mInputViewRect = new Rect();
+ private MainKeyboardView mMainKeyboardView;
private KeyboardTopPaddingForwarder mKeyboardTopPaddingForwarder;
private MoreSuggestionsViewCanceler mMoreSuggestionsViewCanceler;
private MotionEventForwarder<?, ?> mActiveForwarder;
@@ -41,12 +43,11 @@ public final class InputView extends LinearLayout {
protected void onFinishInflate() {
final SuggestionStripView suggestionStripView =
(SuggestionStripView)findViewById(R.id.suggestion_strip_view);
- final MainKeyboardView mainKeyboardView =
- (MainKeyboardView)findViewById(R.id.keyboard_view);
+ mMainKeyboardView = (MainKeyboardView)findViewById(R.id.keyboard_view);
mKeyboardTopPaddingForwarder = new KeyboardTopPaddingForwarder(
- mainKeyboardView, suggestionStripView);
+ mMainKeyboardView, suggestionStripView);
mMoreSuggestionsViewCanceler = new MoreSuggestionsViewCanceler(
- mainKeyboardView, suggestionStripView);
+ mMainKeyboardView, suggestionStripView);
}
public void setKeyboardTopPadding(final int keyboardTopPadding) {
@@ -54,6 +55,17 @@ public final class InputView extends LinearLayout {
}
@Override
+ protected boolean dispatchHoverEvent(final MotionEvent event) {
+ if (AccessibilityUtils.getInstance().isTouchExplorationEnabled()
+ && mMainKeyboardView.isShowingMoreKeysPanel()) {
+ // With accessibility mode on, discard hover events while a more keys keyboard is shown.
+ // The {@link MoreKeysKeyboard} receives hover events directly from the platform.
+ return true;
+ }
+ return super.dispatchHoverEvent(event);
+ }
+
+ @Override
public boolean onInterceptTouchEvent(final MotionEvent me) {
final Rect rect = mInputViewRect;
getGlobalVisibleRect(rect);
diff --git a/java/src/com/android/inputmethod/latin/LastComposedWord.java b/java/src/com/android/inputmethod/latin/LastComposedWord.java
index 9caec3e01..8cbf8379b 100644
--- a/java/src/com/android/inputmethod/latin/LastComposedWord.java
+++ b/java/src/com/android/inputmethod/latin/LastComposedWord.java
@@ -69,7 +69,7 @@ public final class LastComposedWord {
mInputPointers.copy(inputPointers);
}
mTypedWord = typedWord;
- mEvents = new ArrayList<Event>(events);
+ mEvents = new ArrayList<>(events);
mCommittedWord = committedWord;
mSeparatorString = separatorString;
mActive = true;
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index ab7e66a09..35966bb71 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -28,7 +28,6 @@ import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Rect;
@@ -38,7 +37,6 @@ import android.net.ConnectivityManager;
import android.os.Debug;
import android.os.IBinder;
import android.os.Message;
-import android.preference.PreferenceManager;
import android.text.InputType;
import android.text.TextUtils;
import android.util.Log;
@@ -83,18 +81,18 @@ import com.android.inputmethod.latin.utils.ApplicationUtils;
import com.android.inputmethod.latin.utils.CapsModeUtils;
import com.android.inputmethod.latin.utils.CoordinateUtils;
import com.android.inputmethod.latin.utils.DialogUtils;
-import com.android.inputmethod.latin.utils.DistracterFilter;
+import com.android.inputmethod.latin.utils.DistracterFilterCheckingExactMatches;
import com.android.inputmethod.latin.utils.ImportantNoticeUtils;
import com.android.inputmethod.latin.utils.IntentUtils;
import com.android.inputmethod.latin.utils.JniUtils;
import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper;
import com.android.inputmethod.latin.utils.StatsUtils;
import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
-import com.android.inputmethod.research.ResearchLogger;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.List;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
@@ -103,7 +101,7 @@ import java.util.concurrent.TimeUnit;
*/
public class LatinIME extends InputMethodService implements KeyboardActionListener,
SuggestionStripView.Listener, SuggestionStripViewAccessor,
- DictionaryFacilitatorForSuggest.DictionaryInitializationListener,
+ DictionaryFacilitator.DictionaryInitializationListener,
ImportantNoticeDialog.ImportantNoticeDialogListener {
private static final String TAG = LatinIME.class.getSimpleName();
private static final boolean TRACE = false;
@@ -122,12 +120,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private static final String SCHEME_PACKAGE = "package";
private final Settings mSettings;
+ private final DictionaryFacilitator mDictionaryFacilitator =
+ new DictionaryFacilitator(new DistracterFilterCheckingExactMatches(this /* context */));
private final InputLogic mInputLogic = new InputLogic(this /* LatinIME */,
- this /* SuggestionStripViewAccessor */);
+ this /* SuggestionStripViewAccessor */, mDictionaryFacilitator);
// We expect to have only one decoder in almost all cases, hence the default capacity of 1.
// If it turns out we need several, it will get grown seamlessly.
- final SparseArray<HardwareEventDecoder> mHardwareEventDecoders
- = new SparseArray<HardwareEventDecoder>(1);
+ final SparseArray<HardwareEventDecoder> mHardwareEventDecoders = new SparseArray<>(1);
private View mExtractArea;
private View mKeyPreviewBackingView;
@@ -167,6 +166,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private static final int ARG1_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT = 1;
private static final int ARG1_SHOW_GESTURE_FLOATING_PREVIEW_TEXT = 2;
private static final int ARG2_UNUSED = 0;
+ private static final int ARG1_FALSE = 0;
+ private static final int ARG1_TRUE = 1;
private int mDelayUpdateSuggestions;
private int mDelayUpdateShiftState;
@@ -214,7 +215,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
case MSG_RESUME_SUGGESTIONS:
latinIme.mInputLogic.restartSuggestionsOnWordTouchedByCursor(
latinIme.mSettings.getCurrent(),
- false /* includeResumedWordInSuggestions */);
+ msg.arg1 == ARG1_TRUE /* shouldIncludeResumedWordInSuggestions */);
break;
case MSG_REOPEN_DICTIONARIES:
latinIme.resetSuggest();
@@ -251,16 +252,20 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
sendMessage(obtainMessage(MSG_REOPEN_DICTIONARIES));
}
- public void postResumeSuggestions() {
+ public void postResumeSuggestions(final boolean shouldIncludeResumedWordInSuggestions) {
final LatinIME latinIme = getOwnerInstance();
if (latinIme == null) {
return;
}
- if (!latinIme.mSettings.getCurrent().isSuggestionStripVisible()) {
+ if (!latinIme.mSettings.getCurrent()
+ .isCurrentOrientationAllowingSuggestionsPerUserSettings()) {
return;
}
removeMessages(MSG_RESUME_SUGGESTIONS);
- sendMessageDelayed(obtainMessage(MSG_RESUME_SUGGESTIONS), mDelayUpdateSuggestions);
+ sendMessageDelayed(obtainMessage(MSG_RESUME_SUGGESTIONS,
+ shouldIncludeResumedWordInSuggestions ? ARG1_TRUE : ARG1_FALSE,
+ 0 /* ignored */),
+ mDelayUpdateSuggestions);
}
public void postResetCaches(final boolean tryResumeSuggestions, final int remainingTries) {
@@ -491,12 +496,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
loadSettings();
resetSuggest();
- if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- ResearchLogger.getInstance().init(this, mKeyboardSwitcher);
- ResearchLogger.getInstance().initDictionary(
- mInputLogic.mSuggest.mDictionaryFacilitator);
- }
-
// Register to receive ringer mode change and network state change.
// Also receive installation and removal of a dictionary pack.
final IntentFilter filter = new IntentFilter();
@@ -528,7 +527,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
void loadSettings() {
final Locale locale = mSubtypeSwitcher.getCurrentSubtypeLocale();
final EditorInfo editorInfo = getCurrentInputEditorInfo();
- final InputAttributes inputAttributes = new InputAttributes(editorInfo, isFullscreenMode());
+ final InputAttributes inputAttributes = new InputAttributes(
+ editorInfo, isFullscreenMode(), getPackageName());
mSettings.loadSettings(this, locale, inputAttributes);
final SettingsValues currentSettingsValues = mSettings.getCurrent();
AudioAndHapticFeedbackManager.getInstance().onSettingsChanged(currentSettingsValues);
@@ -538,13 +538,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (!mHandler.hasPendingReopenDictionaries()) {
resetSuggestForLocale(locale);
}
+ mDictionaryFacilitator.updateEnabledSubtypes(mRichImm.getMyEnabledInputMethodSubtypeList(
+ true /* allowsImplicitlySelectedSubtypes */));
refreshPersonalizationDictionarySession();
StatsUtils.onLoadSettings(currentSettingsValues);
}
private void refreshPersonalizationDictionarySession() {
- final DictionaryFacilitatorForSuggest dictionaryFacilitator =
- mInputLogic.mSuggest.mDictionaryFacilitator;
final boolean shouldKeepUserHistoryDictionaries;
final boolean shouldKeepPersonalizationDictionaries;
if (mSettings.getCurrent().mUsePersonalizedDicts) {
@@ -559,16 +559,14 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (!shouldKeepUserHistoryDictionaries) {
// Remove user history dictionaries.
PersonalizationHelper.removeAllUserHistoryDictionaries(this);
- dictionaryFacilitator.clearUserHistoryDictionary();
+ mDictionaryFacilitator.clearUserHistoryDictionary();
}
if (!shouldKeepPersonalizationDictionaries) {
// Remove personalization dictionaries.
PersonalizationHelper.removeAllPersonalizationDictionaries(this);
PersonalizationDictionarySessionRegistrar.resetAll(this);
} else {
- final DistracterFilter distracterFilter = createDistracterFilter();
- PersonalizationDictionarySessionRegistrar.init(
- this, dictionaryFacilitator, distracterFilter);
+ PersonalizationDictionarySessionRegistrar.init(this, mDictionaryFacilitator);
}
}
@@ -606,13 +604,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
* @param locale the locale
*/
private void resetSuggestForLocale(final Locale locale) {
- final DictionaryFacilitatorForSuggest dictionaryFacilitator =
- mInputLogic.mSuggest.mDictionaryFacilitator;
final SettingsValues settingsValues = mSettings.getCurrent();
- dictionaryFacilitator.resetDictionaries(this /* context */, locale,
+ mDictionaryFacilitator.resetDictionaries(this /* context */, locale,
settingsValues.mUseContactsDict, settingsValues.mUsePersonalizedDicts,
false /* forceReloadMainDictionary */, this);
- if (settingsValues.mCorrectionEnabled) {
+ if (settingsValues.mAutoCorrectionEnabled) {
mInputLogic.mSuggest.setAutoCorrectionThreshold(
settingsValues.mAutoCorrectionThreshold);
}
@@ -622,27 +618,20 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
* Reset suggest by loading the main dictionary of the current locale.
*/
/* package private */ void resetSuggestMainDict() {
- final DictionaryFacilitatorForSuggest dictionaryFacilitator =
- mInputLogic.mSuggest.mDictionaryFacilitator;
final SettingsValues settingsValues = mSettings.getCurrent();
- dictionaryFacilitator.resetDictionaries(this /* context */,
- dictionaryFacilitator.getLocale(), settingsValues.mUseContactsDict,
+ mDictionaryFacilitator.resetDictionaries(this /* context */,
+ mDictionaryFacilitator.getLocale(), settingsValues.mUseContactsDict,
settingsValues.mUsePersonalizedDicts, true /* forceReloadMainDictionary */, this);
}
@Override
public void onDestroy() {
- mInputLogic.mSuggest.mDictionaryFacilitator.closeDictionaries();
+ mDictionaryFacilitator.closeDictionaries();
mSettings.onDestroy();
unregisterReceiver(mConnectivityAndRingerModeChangeReceiver);
- if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- ResearchLogger.getInstance().onDestroy();
- }
unregisterReceiver(mDictionaryPackInstallReceiver);
unregisterReceiver(mDictionaryDumpBroadcastReceiver);
PersonalizationDictionarySessionRegistrar.close(this);
- LatinImeLogger.commit();
- LatinImeLogger.onDestroy();
StatsUtils.onDestroy();
super.onDestroy();
}
@@ -657,18 +646,22 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
@Override
public void onConfigurationChanged(final Configuration conf) {
- // If orientation changed while predicting, commit the change
final SettingsValues settingsValues = mSettings.getCurrent();
if (settingsValues.mDisplayOrientation != conf.orientation) {
mHandler.startOrientationChanging();
- mInputLogic.mConnection.beginBatchEdit();
- mInputLogic.commitTyped(mSettings.getCurrent(), LastComposedWord.NOT_A_SEPARATOR);
- mInputLogic.mConnection.finishComposingText();
- mInputLogic.mConnection.endBatchEdit();
+ // If !isComposingWord, #commitTyped() is a no-op, but still, it's better to avoid
+ // the useless IPC of {begin,end}BatchEdit.
+ if (mInputLogic.mWordComposer.isComposingWord()) {
+ mInputLogic.mConnection.beginBatchEdit();
+ // If we had a composition in progress, we need to commit the word so that the
+ // suggestionsSpan will be added. This will allow resuming on the same suggestions
+ // after rotation is finished.
+ mInputLogic.commitTyped(mSettings.getCurrent(), LastComposedWord.NOT_A_SEPARATOR);
+ mInputLogic.mConnection.endBatchEdit();
+ }
}
- final DistracterFilter distracterFilter = createDistracterFilter();
PersonalizationDictionarySessionRegistrar.onConfigurationChanged(this, conf,
- mInputLogic.mSuggest.mDictionaryFacilitator, distracterFilter);
+ mDictionaryFacilitator);
super.onConfigurationChanged(conf);
}
@@ -687,9 +680,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (hasSuggestionStripView()) {
mSuggestionStripView.setListener(this, view);
}
- if (LatinImeLogger.sVISUALDEBUG) {
- mKeyPreviewBackingView.setBackgroundColor(0x10FF0000);
- }
}
@Override
@@ -762,10 +752,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
Log.i(TAG, "Starting input. Cursor position = "
+ editorInfo.initialSelStart + "," + editorInfo.initialSelEnd);
- if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
- ResearchLogger.latinIME_onStartInputViewInternal(editorInfo, prefs);
- }
+ // TODO: Consolidate these checks with {@link InputAttributes}.
if (InputAttributes.inPrivateImeOptions(null, NO_MICROPHONE_COMPAT, editorInfo)) {
Log.w(TAG, "Deprecated private IME option specified: " + editorInfo.privateImeOptions);
Log.w(TAG, "Use " + getPackageName() + "." + NO_MICROPHONE + " instead");
@@ -775,7 +762,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
Log.w(TAG, "Use EditorInfo.IME_FLAG_FORCE_ASCII flag instead");
}
- LatinImeLogger.onStartInputView(editorInfo);
// In landscape mode, this method gets called without the input view being created.
if (mainKeyboardView == null) {
return;
@@ -828,7 +814,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// When rotating, initialSelStart and initialSelEnd sometimes are lying. Make a best
// effort to work around this bug.
mInputLogic.mConnection.tryFixLyingCursorPosition();
- mHandler.postResumeSuggestions();
+ mHandler.postResumeSuggestions(true /* shouldIncludeResumedWordInSuggestions */);
canReachInputConnection = true;
}
@@ -840,8 +826,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mainKeyboardView.closing();
currentSettingsValues = mSettings.getCurrent();
- if (currentSettingsValues.mCorrectionEnabled) {
- suggest.setAutoCorrectionThreshold(currentSettingsValues.mAutoCorrectionThreshold);
+ if (currentSettingsValues.mAutoCorrectionEnabled) {
+ suggest.setAutoCorrectionThreshold(
+ currentSettingsValues.mAutoCorrectionThreshold);
}
switcher.loadKeyboard(editorInfo, currentSettingsValues, getCurrentAutoCapsState(),
@@ -870,7 +857,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mHandler.cancelUpdateSuggestionStrip();
mainKeyboardView.setMainDictionaryAvailability(
- suggest.mDictionaryFacilitator.hasInitializedMainDictionary());
+ mDictionaryFacilitator.hasInitializedMainDictionary());
mainKeyboardView.setKeyPreviewPopupEnabled(currentSettingsValues.mKeyPreviewPopupOn,
currentSettingsValues.mKeyPreviewPopupDismissDelay);
mainKeyboardView.setSlidingKeyInputPreviewEnabled(
@@ -895,7 +882,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private void onFinishInputInternal() {
super.onFinishInput();
- LatinImeLogger.commit();
final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
if (mainKeyboardView != null) {
mainKeyboardView.closing();
@@ -909,10 +895,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mHandler.cancelUpdateSuggestionStrip();
// Should do the following in onFinishInputInternal but until JB MR2 it's not called :(
mInputLogic.finishInput();
- // Notify ResearchLogger
- if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- ResearchLogger.latinIME_onFinishInputViewInternal(finishingInput);
- }
}
@Override
@@ -926,11 +908,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
+ ", nss=" + newSelStart + ", nse=" + newSelEnd
+ ", cs=" + composingSpanStart + ", ce=" + composingSpanEnd);
}
- if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- ResearchLogger.latinIME_onUpdateSelection(oldSelStart, oldSelEnd,
- oldSelStart, oldSelEnd, newSelStart, newSelEnd, composingSpanStart,
- composingSpanEnd, mInputLogic.mConnection);
- }
// If the keyboard is not visible, we don't need to do all the housekeeping work, as it
// will be reset when the keyboard shows up anyway.
@@ -991,7 +968,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
@Override
public void hideWindow() {
- LatinImeLogger.commit();
mKeyboardSwitcher.onHideWindow();
if (TRACE) Debug.stopMethodTracing();
@@ -1017,9 +993,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
if (applicationSpecifiedCompletions == null) {
setNeutralSuggestionStrip();
- if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- ResearchLogger.latinIME_onDisplayCompletions(null);
- }
return;
}
@@ -1030,10 +1003,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
null /* rawSuggestions */, false /* typedWordValid */, false /* willAutoCorrect */,
false /* isObsoleteSuggestions */, false /* isPrediction */);
// When in fullscreen mode, show completions generated by the application forcibly
- setSuggestedWords(suggestedWords, true /* isSuggestionStripVisible */);
- if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- ResearchLogger.latinIME_onDisplayCompletions(applicationSpecifiedCompletions);
- }
+ setSuggestedWords(suggestedWords);
}
private int getAdjustedBackingViewHeight() {
@@ -1167,8 +1137,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
} else {
wordToEdit = word;
}
- mInputLogic.mSuggest.mDictionaryFacilitator.addWordToUserDictionary(
- this /* context */, wordToEdit);
+ mDictionaryFacilitator.addWordToUserDictionary(this /* context */, wordToEdit);
}
// Callback for the {@link SuggestionStripView}, to call when the important notice strip is
@@ -1337,30 +1306,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// Nothing to do so far.
}
- private boolean isSuggestionStripVisible() {
- if (!hasSuggestionStripView()) {
- return false;
- }
- if (mSuggestionStripView.isShowingAddToDictionaryHint()) {
- return true;
- }
- final SettingsValues currentSettings = mSettings.getCurrent();
- if (null == currentSettings) {
- return false;
- }
- if (ImportantNoticeUtils.shouldShowImportantNotice(this,
- currentSettings.mInputAttributes)) {
- return true;
- }
- if (!currentSettings.isSuggestionStripVisible()) {
- return false;
- }
- if (currentSettings.isApplicationSpecifiedCompletionsOn()) {
- return true;
- }
- return currentSettings.isSuggestionsRequested();
- }
-
public boolean hasSuggestionStripView() {
return null != mSuggestionStripView;
}
@@ -1378,9 +1323,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mSuggestionStripView.dismissAddToDictionaryHint();
}
- // TODO[IL]: Define a clear interface for this
- public void setSuggestedWords(final SuggestedWords suggestedWords,
- final boolean isSuggestionStripVisible) {
+ private void setSuggestedWords(final SuggestedWords suggestedWords) {
mInputLogic.setSuggestedWords(suggestedWords);
// TODO: Modify this when we support suggestions with hard keyboard
if (!hasSuggestionStripView()) {
@@ -1389,22 +1332,35 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (!onEvaluateInputViewShown()) {
return;
}
- if (!isSuggestionStripVisible) {
- mSuggestionStripView.setVisibility(isFullscreenMode() ? View.GONE : View.INVISIBLE);
+
+ final SettingsValues currentSettingsValues = mSettings.getCurrent();
+ final boolean shouldShowImportantNotice =
+ ImportantNoticeUtils.shouldShowImportantNotice(this);
+ final boolean shouldShowSuggestionsStripUnlessPassword = shouldShowImportantNotice
+ || currentSettingsValues.mShowsVoiceInputKey
+ || currentSettingsValues.isSuggestionsRequested()
+ || currentSettingsValues.isApplicationSpecifiedCompletionsOn();
+ final boolean shouldShowSuggestionsStrip = shouldShowSuggestionsStripUnlessPassword
+ && !currentSettingsValues.mInputAttributes.mIsPasswordField;
+ mSuggestionStripView.updateVisibility(shouldShowSuggestionsStrip, isFullscreenMode());
+ if (!shouldShowSuggestionsStrip) {
return;
}
- mSuggestionStripView.setVisibility(View.VISIBLE);
- final SettingsValues currentSettings = mSettings.getCurrent();
- final boolean showSuggestions;
- if (SuggestedWords.EMPTY == suggestedWords || suggestedWords.isPunctuationSuggestions()
- || !currentSettings.isSuggestionsRequested()) {
- showSuggestions = !mSuggestionStripView.maybeShowImportantNoticeTitle(
- currentSettings.mInputAttributes);
+ final boolean isEmptyApplicationSpecifiedCompletions =
+ currentSettingsValues.isApplicationSpecifiedCompletionsOn()
+ && suggestedWords.isEmpty();
+ final boolean noSuggestionsToShow = (SuggestedWords.EMPTY == suggestedWords)
+ || suggestedWords.isPunctuationSuggestions()
+ || isEmptyApplicationSpecifiedCompletions;
+ final boolean isShowingImportantNotice;
+ if (shouldShowImportantNotice && noSuggestionsToShow) {
+ isShowingImportantNotice = mSuggestionStripView.maybeShowImportantNoticeTitle();
} else {
- showSuggestions = true;
+ isShowingImportantNotice = false;
}
- if (showSuggestions) {
+
+ if (currentSettingsValues.isSuggestionsRequested() && !isShowingImportantNotice) {
mSuggestionStripView.setSuggestions(suggestedWords,
SubtypeLocaleUtils.isRtlLanguage(mSubtypeSwitcher.getCurrentSubtype()));
}
@@ -1418,36 +1374,17 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
callback.onGetSuggestedWords(SuggestedWords.EMPTY);
return;
}
- // Get the word on which we should search the bigrams. If we are composing a word, it's
- // 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.
final SettingsValues currentSettings = mSettings.getCurrent();
final int[] additionalFeaturesOptions = currentSettings.mAdditionalFeaturesSettingValues;
-
- if (DEBUG) {
- if (mInputLogic.mWordComposer.isComposingWord()
- || mInputLogic.mWordComposer.isBatchMode()) {
- final PrevWordsInfo prevWordsInfo
- = mInputLogic.mWordComposer.getPrevWordsInfoForSuggestion();
- // TODO: this is for checking consistency with older versions. Remove this when
- // we are confident this is stable.
- // 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 PrevWordsInfo rereadPrevWordsInfo =
- mInputLogic.getPrevWordsInfoFromNthPreviousWordForSuggestion(
- currentSettings.mSpacingAndPunctuations,
- mInputLogic.mWordComposer.isComposingWord() ? 2 : 1);
- if (!TextUtils.equals(prevWordsInfo.mPrevWord, rereadPrevWordsInfo.mPrevWord)) {
- throw new RuntimeException("Unexpected previous word: "
- + prevWordsInfo.mPrevWord + " <> " + rereadPrevWordsInfo.mPrevWord);
- }
- }
- }
mInputLogic.mSuggest.getSuggestedWords(mInputLogic.mWordComposer,
- mInputLogic.mWordComposer.getPrevWordsInfoForSuggestion(),
+ mInputLogic.getPrevWordsInfoFromNthPreviousWordForSuggestion(
+ currentSettings.mSpacingAndPunctuations,
+ // Get the word on which we should search the bigrams. If we are composing
+ // a word, it's 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.
+ mInputLogic.mWordComposer.isComposingWord() ? 2 : 1),
keyboard.getProximityInfo(), currentSettings.mBlockPotentiallyOffensive,
- currentSettings.mCorrectionEnabled, additionalFeaturesOptions, sessionId,
+ currentSettings.mAutoCorrectionEnabled, additionalFeaturesOptions, sessionId,
sequenceNumber, callback);
}
@@ -1467,7 +1404,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
setNeutralSuggestionStrip();
} else {
mInputLogic.mWordComposer.setAutoCorrection(autoCorrection);
- setSuggestedWords(suggestedWords, isSuggestionStripVisible());
+ setSuggestedWords(suggestedWords);
}
// Cache the auto-correction in accessibility code so we can speak it if the user
// touches a key that will insert it.
@@ -1500,7 +1437,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
final SettingsValues currentSettings = mSettings.getCurrent();
final SuggestedWords neutralSuggestions = currentSettings.mBigramPredictionEnabled
? SuggestedWords.EMPTY : currentSettings.mSpacingAndPunctuations.mSuggestPuncList;
- setSuggestedWords(neutralSuggestions, isSuggestionStripVisible());
+ setSuggestedWords(neutralSuggestions);
}
// TODO: Make this private
@@ -1725,15 +1662,14 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
@UsedForTesting
/* package for test */ void waitForLoadingDictionaries(final long timeout, final TimeUnit unit)
throws InterruptedException {
- mInputLogic.mSuggest.mDictionaryFacilitator.waitForLoadingDictionariesForTesting(
- timeout, unit);
+ mDictionaryFacilitator.waitForLoadingDictionariesForTesting(timeout, unit);
}
// DO NOT USE THIS for any other purpose than testing. This can break the keyboard badly.
@UsedForTesting
/* package for test */ void replaceDictionariesForTest(final Locale locale) {
final SettingsValues settingsValues = mSettings.getCurrent();
- mInputLogic.mSuggest.mDictionaryFacilitator.resetDictionaries(this, locale,
+ mDictionaryFacilitator.resetDictionaries(this, locale,
settingsValues.mUseContactsDict, settingsValues.mUsePersonalizedDicts,
false /* forceReloadMainDictionary */, this /* listener */);
}
@@ -1741,24 +1677,21 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// DO NOT USE THIS for any other purpose than testing.
@UsedForTesting
/* package for test */ void clearPersonalizedDictionariesForTest() {
- mInputLogic.mSuggest.mDictionaryFacilitator.clearUserHistoryDictionary();
- mInputLogic.mSuggest.mDictionaryFacilitator.clearPersonalizationDictionary();
+ mDictionaryFacilitator.clearUserHistoryDictionary();
+ mDictionaryFacilitator.clearPersonalizationDictionary();
}
@UsedForTesting
- /* package for test */ DistracterFilter createDistracterFilter() {
- return new DistracterFilter(this /* Context */,
- mRichImm.getMyEnabledInputMethodSubtypeList(
- true /* allowsImplicitlySelectedSubtypes */));
+ /* package for test */ List<InputMethodSubtype> getEnabledSubtypesForTest() {
+ return (mRichImm != null) ? mRichImm.getMyEnabledInputMethodSubtypeList(
+ true /* allowsImplicitlySelectedSubtypes */) : new ArrayList<InputMethodSubtype>();
}
public void dumpDictionaryForDebug(final String dictName) {
- final DictionaryFacilitatorForSuggest dictionaryFacilitator =
- mInputLogic.mSuggest.mDictionaryFacilitator;
- if (dictionaryFacilitator.getLocale() == null) {
+ if (mDictionaryFacilitator.getLocale() == null) {
resetSuggest();
}
- mInputLogic.mSuggest.mDictionaryFacilitator.dumpDictionaryForDebug(dictName);
+ mDictionaryFacilitator.dumpDictionaryForDebug(dictName);
}
public void debugDumpStateAndCrashWithException(final String context) {
diff --git a/java/src/com/android/inputmethod/latin/LatinImeLogger.java b/java/src/com/android/inputmethod/latin/LatinImeLogger.java
index 3f2b0a3f4..8fd36b937 100644
--- a/java/src/com/android/inputmethod/latin/LatinImeLogger.java
+++ b/java/src/com/android/inputmethod/latin/LatinImeLogger.java
@@ -16,76 +16,12 @@
package com.android.inputmethod.latin;
-import android.content.SharedPreferences;
-import android.view.inputmethod.EditorInfo;
+import android.content.Context;
-import com.android.inputmethod.keyboard.Keyboard;
+// TODO: Rename this class name to make it more relevant.
+public final class LatinImeLogger {
+ public static final boolean sDBG = false;
-public final class LatinImeLogger implements SharedPreferences.OnSharedPreferenceChangeListener {
-
- public static boolean sDBG = false;
- public static boolean sVISUALDEBUG = false;
- public static boolean sUsabilityStudy = false;
-
- @Override
- public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
- }
-
- public static void init(LatinIME context) {
- }
-
- public static void commit() {
- }
-
- public static boolean getUsabilityStudyMode(final SharedPreferences prefs) {
- return false;
- }
-
- public static void onDestroy() {
- }
-
- public static void logOnManualSuggestion(
- String before, String after, int position, SuggestedWords suggestedWords) {
- }
-
- public static void logOnAutoCorrectionForTyping(
- String before, String after, int separatorCode) {
- }
-
- public static void logOnAutoCorrectionForGeometric(String before, String after,
- int separatorCode, InputPointers inputPointers) {
- }
-
- public static void logOnAutoCorrectionCancelled() {
- }
-
- public static void logOnDelete(int x, int y) {
- }
-
- public static void logOnInputChar() {
- }
-
- public static void logOnInputSeparator() {
- }
-
- public static void logOnException(String metaData, Throwable e) {
- }
-
- public static void logOnWarning(String warning) {
- }
-
- public static void onStartInputView(EditorInfo editorInfo) {
- }
-
- public static void onStartSuggestion(CharSequence previousWords) {
- }
-
- public static void onAddSuggestedWord(String word, String sourceDictionaryId) {
- }
-
- public static void onSetKeyboard(Keyboard kb) {
- }
-
- public static void onPrintAllUsabilityStudyLogs() {
+ public static void init(Context context) {
}
}
diff --git a/java/src/com/android/inputmethod/latin/PrevWordsInfo.java b/java/src/com/android/inputmethod/latin/PrevWordsInfo.java
index ecc8947db..42b311c69 100644
--- a/java/src/com/android/inputmethod/latin/PrevWordsInfo.java
+++ b/java/src/com/android/inputmethod/latin/PrevWordsInfo.java
@@ -16,23 +16,32 @@
package com.android.inputmethod.latin;
-import android.util.Log;
-
+/**
+ * Class to represent information of previous words. This class is used to add n-gram entries
+ * into binary dictionaries, to get predictions, and to get suggestions.
+ */
// TODO: Support multiple previous words for n-gram.
public class PrevWordsInfo {
- // The previous word. May be null after resetting and before starting a new composing word, or
- // when there is no context like at the start of text for example. It can also be set to null
- // externally when the user enters a separator that does not let bigrams across, like a period
- // or a comma.
+ public static final PrevWordsInfo EMPTY_PREV_WORDS_INFO = new PrevWordsInfo(null);
+ public static final PrevWordsInfo BEGINNING_OF_SENTENCE = new PrevWordsInfo();
+
+ // The word immediately before the considered word. null means we don't have any context
+ // including the "beginning of sentence context" - we just don't know what to predict.
+ // An example of that is after a comma.
+ // For simplicity of implementation, this may also be null transiently after the WordComposer
+ // was reset and before starting a new composing word, but we should never be calling
+ // getSuggetions* in this situation.
+ // This is an empty string when mIsBeginningOfSentence is true.
public final String mPrevWord;
// TODO: Have sentence separator.
- // Whether the current context is beginning of sentence or not.
+ // Whether the current context is beginning of sentence or not. This is true when composing at
+ // the beginning of an input field or composing a word after a sentence separator.
public final boolean mIsBeginningOfSentence;
// Beginning of sentence.
public PrevWordsInfo() {
- mPrevWord = null;
+ mPrevWord = "";
mIsBeginningOfSentence = true;
}
@@ -40,4 +49,14 @@ public class PrevWordsInfo {
mPrevWord = prevWord;
mIsBeginningOfSentence = false;
}
+
+ public boolean isValid() {
+ return mPrevWord != null;
+ }
+
+ @Override
+ public String toString() {
+ return "PrevWord: " + mPrevWord + ", isBeginningOfSentence: "
+ + mIsBeginningOfSentence + ".";
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/PunctuationSuggestions.java b/java/src/com/android/inputmethod/latin/PunctuationSuggestions.java
index 4911bcdf6..0fba37c8a 100644
--- a/java/src/com/android/inputmethod/latin/PunctuationSuggestions.java
+++ b/java/src/com/android/inputmethod/latin/PunctuationSuggestions.java
@@ -17,8 +17,6 @@
package com.android.inputmethod.latin;
import com.android.inputmethod.keyboard.internal.KeySpecParser;
-import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
-import com.android.inputmethod.latin.utils.CollectionUtils;
import com.android.inputmethod.latin.utils.StringUtils;
import java.util.ArrayList;
@@ -49,7 +47,7 @@ public final class PunctuationSuggestions extends SuggestedWords {
*/
public static PunctuationSuggestions newPunctuationSuggestions(
final String[] punctuationSpecs) {
- final ArrayList<SuggestedWordInfo> puncuationsList = CollectionUtils.newArrayList();
+ final ArrayList<SuggestedWordInfo> puncuationsList = new ArrayList<>();
for (final String puncSpec : punctuationSpecs) {
puncuationsList.add(newHardCodedWordInfo(puncSpec));
}
diff --git a/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java
index 8f744bef8..e59ef7563 100644
--- a/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java
@@ -66,10 +66,10 @@ public final class ReadOnlyBinaryDictionary extends Dictionary {
}
@Override
- public boolean isValidWord(final String word) {
+ public boolean isInDictionary(final String word) {
if (mLock.readLock().tryLock()) {
try {
- return mBinaryDictionary.isValidWord(word);
+ return mBinaryDictionary.isInDictionary(word);
} finally {
mLock.readLock().unlock();
}
@@ -102,6 +102,18 @@ public final class ReadOnlyBinaryDictionary extends Dictionary {
}
@Override
+ public int getMaxFrequencyOfExactMatches(final String word) {
+ if (mLock.readLock().tryLock()) {
+ try {
+ return mBinaryDictionary.getMaxFrequencyOfExactMatches(word);
+ } finally {
+ mLock.readLock().unlock();
+ }
+ }
+ return NOT_A_PROBABILITY;
+ }
+
+ @Override
public void close() {
mLock.writeLock().lock();
try {
diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java
index 2c54e10aa..96476b2ee 100644
--- a/java/src/com/android/inputmethod/latin/RichInputConnection.java
+++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java
@@ -26,14 +26,12 @@ import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
-import com.android.inputmethod.latin.define.ProductionFlag;
import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
import com.android.inputmethod.latin.utils.CapsModeUtils;
import com.android.inputmethod.latin.utils.DebugLogUtils;
import com.android.inputmethod.latin.utils.SpannableStringUtils;
import com.android.inputmethod.latin.utils.StringUtils;
import com.android.inputmethod.latin.utils.TextRange;
-import com.android.inputmethod.research.ResearchLogger;
import java.util.Arrays;
import java.util.regex.Pattern;
@@ -174,9 +172,6 @@ public final class RichInputConnection {
}
if (null != mIC && shouldFinishComposition) {
mIC.finishComposingText();
- if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- ResearchLogger.richInputConnection_finishComposingText();
- }
}
return true;
}
@@ -223,9 +218,6 @@ public final class RichInputConnection {
mComposingText.setLength(0);
if (null != mIC) {
mIC.finishComposingText();
- if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- ResearchLogger.richInputConnection_finishComposingText();
- }
}
}
@@ -363,9 +355,6 @@ public final class RichInputConnection {
}
if (null != mIC) {
mIC.deleteSurroundingText(beforeLength, afterLength);
- if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- ResearchLogger.richInputConnection_deleteSurroundingText(beforeLength, afterLength);
- }
}
if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
}
@@ -374,9 +363,6 @@ public final class RichInputConnection {
mIC = mParent.getCurrentInputConnection();
if (null != mIC) {
mIC.performEditorAction(actionId);
- if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- ResearchLogger.richInputConnection_performEditorAction(actionId);
- }
}
}
@@ -429,9 +415,6 @@ public final class RichInputConnection {
}
if (null != mIC) {
mIC.sendKeyEvent(keyEvent);
- if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- ResearchLogger.richInputConnection_sendKeyEvent(keyEvent);
- }
}
}
@@ -469,9 +452,6 @@ public final class RichInputConnection {
// newCursorPosition != 1.
if (null != mIC) {
mIC.setComposingText(text, newCursorPosition);
- if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- ResearchLogger.richInputConnection_setComposingText(text, newCursorPosition);
- }
}
if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
}
@@ -500,9 +480,6 @@ public final class RichInputConnection {
if (!isIcValid) {
return false;
}
- if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- ResearchLogger.richInputConnection_setSelection(start, end);
- }
}
return reloadTextCache();
}
@@ -530,9 +507,6 @@ public final class RichInputConnection {
mComposingText.setLength(0);
if (null != mIC) {
mIC.commitCompletion(completionInfo);
- if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- ResearchLogger.richInputConnection_commitCompletion(completionInfo);
- }
}
if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
}
@@ -542,7 +516,7 @@ public final class RichInputConnection {
final SpacingAndPunctuations spacingAndPunctuations, final int n) {
mIC = mParent.getCurrentInputConnection();
if (null == mIC) {
- return new PrevWordsInfo(null);
+ return PrevWordsInfo.EMPTY_PREV_WORDS_INFO;
}
final CharSequence prev = getTextBeforeCursor(LOOKBACK_CHARACTER_NUM, 0);
if (DEBUG_PREVIOUS_TEXT && null != prev) {
@@ -573,14 +547,17 @@ public final class RichInputConnection {
// Get information of the nth word before cursor. n = 1 retrieves the word immediately before
// the cursor, n = 2 retrieves the word before that, and so on. This splits on whitespace only.
// Also, it won't return words that end in a separator (if the nth word before the cursor
- // ends in a separator, it returns information represents beginning-of-sentence).
+ // ends in a separator, it returns information representing beginning-of-sentence).
// Example :
// (n = 1) "abc def|" -> def
// (n = 1) "abc def |" -> def
+ // (n = 1) "abc 'def|" -> 'def
// (n = 1) "abc def. |" -> beginning-of-sentence
// (n = 1) "abc def . |" -> beginning-of-sentence
// (n = 2) "abc def|" -> abc
// (n = 2) "abc def |" -> abc
+ // (n = 2) "abc 'def|" -> empty. The context is different from "abc def", but we cannot
+ // represent this situation using PrevWordsInfo. See TODO in the method.
// (n = 2) "abc def. |" -> abc
// (n = 2) "abc def . |" -> def
// (n = 2) "abc|" -> beginning-of-sentence
@@ -588,30 +565,43 @@ public final class RichInputConnection {
// (n = 2) "abc. def|" -> beginning-of-sentence
public static PrevWordsInfo getPrevWordsInfoFromNthPreviousWord(final CharSequence prev,
final SpacingAndPunctuations spacingAndPunctuations, final int n) {
- if (prev == null) return new PrevWordsInfo(null);
+ if (prev == null) return PrevWordsInfo.EMPTY_PREV_WORDS_INFO;
final String[] w = spaceRegex.split(prev);
+ // Referring to the word after the nth word.
+ if ((n - 1) > 0 && (n - 1) <= w.length) {
+ final String wordFollowingTheNthPrevWord = w[w.length - n + 1];
+ if (!wordFollowingTheNthPrevWord.isEmpty()) {
+ final char firstChar = wordFollowingTheNthPrevWord.charAt(0);
+ if (spacingAndPunctuations.isWordConnector(firstChar)) {
+ // The word following the n-th prev word is starting with a word connector.
+ // TODO: Return meaningful context for this case.
+ return PrevWordsInfo.EMPTY_PREV_WORDS_INFO;
+ }
+ }
+ }
+
// If we can't find n words, or we found an empty word, the context is
// beginning-of-sentence.
if (w.length < n) {
- return new PrevWordsInfo();
+ return PrevWordsInfo.BEGINNING_OF_SENTENCE;
}
final String nthPrevWord = w[w.length - n];
final int length = nthPrevWord.length();
if (length <= 0) {
- return new PrevWordsInfo();
+ return PrevWordsInfo.BEGINNING_OF_SENTENCE;
}
// If ends in a sentence separator, the context is beginning-of-sentence.
final char lastChar = nthPrevWord.charAt(length - 1);
if (spacingAndPunctuations.isSentenceSeparator(lastChar)) {
- new PrevWordsInfo();
+ return PrevWordsInfo.BEGINNING_OF_SENTENCE;
}
// If ends in a word separator or connector, the context is unclear.
// TODO: Return meaningful context for this case.
if (spacingAndPunctuations.isWordSeparator(lastChar)
|| spacingAndPunctuations.isWordConnector(lastChar)) {
- return new PrevWordsInfo(null);
+ return PrevWordsInfo.EMPTY_PREV_WORDS_INFO;
}
return new PrevWordsInfo(nthPrevWord);
}
@@ -765,9 +755,6 @@ public final class RichInputConnection {
deleteSurroundingText(2, 0);
final String singleSpace = " ";
commitText(singleSpace, 1);
- if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- ResearchLogger.richInputConnection_revertDoubleSpacePeriod();
- }
return true;
}
@@ -790,9 +777,6 @@ public final class RichInputConnection {
deleteSurroundingText(2, 0);
final String text = " " + textBeforeCursor.subSequence(0, 1);
commitText(text, 1);
- if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- ResearchLogger.richInputConnection_revertSwapPunctuation();
- }
return true;
}
@@ -907,4 +891,8 @@ public final class RichInputConnection {
public boolean hasSelection() {
return mExpectedSelEnd != mExpectedSelStart;
}
+
+ public boolean isCursorPositionKnown() {
+ return INVALID_CURSOR_POSITION != mExpectedSelStart;
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/RichInputMethodManager.java b/java/src/com/android/inputmethod/latin/RichInputMethodManager.java
index 64cc562c8..cbdc4b9eb 100644
--- a/java/src/com/android/inputmethod/latin/RichInputMethodManager.java
+++ b/java/src/com/android/inputmethod/latin/RichInputMethodManager.java
@@ -31,7 +31,6 @@ import android.view.inputmethod.InputMethodSubtype;
import com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
import com.android.inputmethod.latin.settings.Settings;
import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils;
-import com.android.inputmethod.latin.utils.CollectionUtils;
import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
import java.util.Collections;
@@ -53,9 +52,9 @@ public final class RichInputMethodManager {
private InputMethodManagerCompatWrapper mImmWrapper;
private InputMethodInfoCache mInputMethodInfoCache;
final HashMap<InputMethodInfo, List<InputMethodSubtype>>
- mSubtypeListCacheWithImplicitlySelectedSubtypes = CollectionUtils.newHashMap();
+ mSubtypeListCacheWithImplicitlySelectedSubtypes = new HashMap<>();
final HashMap<InputMethodInfo, List<InputMethodSubtype>>
- mSubtypeListCacheWithoutImplicitlySelectedSubtypes = CollectionUtils.newHashMap();
+ mSubtypeListCacheWithoutImplicitlySelectedSubtypes = new HashMap<>();
private static final int INDEX_NOT_FOUND = -1;
diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
index c8a2fb2f9..a3d09565c 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
@@ -255,8 +255,7 @@ public final class SubtypeSwitcher {
public boolean isSystemLocaleSameAsLocaleOfAllEnabledSubtypesOfEnabledImes() {
final Locale systemLocale = mResources.getConfiguration().locale;
- final Set<InputMethodSubtype> enabledSubtypesOfEnabledImes =
- new HashSet<InputMethodSubtype>();
+ final Set<InputMethodSubtype> enabledSubtypesOfEnabledImes = new HashSet<>();
final InputMethodManager inputMethodManager = mRichImm.getInputMethodManager();
final List<InputMethodInfo> enabledInputMethodInfoList =
inputMethodManager.getEnabledInputMethodList();
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index 43daee4d2..1ba5d5ea6 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -23,7 +23,6 @@ import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.define.ProductionFlag;
import com.android.inputmethod.latin.utils.AutoCorrectionUtils;
import com.android.inputmethod.latin.utils.BinaryDictionaryUtils;
-import com.android.inputmethod.latin.utils.CollectionUtils;
import com.android.inputmethod.latin.utils.StringUtils;
import com.android.inputmethod.latin.utils.SuggestionResults;
@@ -52,11 +51,14 @@ public final class Suggest {
private static final int SUPPRESS_SUGGEST_THRESHOLD = -2000000000;
private static final boolean DBG = LatinImeLogger.sDBG;
- public final DictionaryFacilitatorForSuggest mDictionaryFacilitator =
- new DictionaryFacilitatorForSuggest();
+ private final DictionaryFacilitator mDictionaryFacilitator;
private float mAutoCorrectionThreshold;
+ public Suggest(final DictionaryFacilitator dictionaryFacilitator) {
+ mDictionaryFacilitator = dictionaryFacilitator;
+ }
+
public Locale getLocale() {
return mDictionaryFacilitator.getLocale();
}
@@ -74,7 +76,6 @@ public final class Suggest {
final boolean blockOffensiveWords, final boolean isCorrectionEnabled,
final int[] additionalFeaturesOptions, final int sessionId, final int sequenceNumber,
final OnGetSuggestedWordsCallback callback) {
- LatinImeLogger.onStartSuggestion(prevWordsInfo.mPrevWord);
if (wordComposer.isBatchMode()) {
getSuggestedWordsForBatchInput(wordComposer, prevWordsInfo, proximityInfo,
blockOffensiveWords, additionalFeaturesOptions, sessionId, sequenceNumber,
@@ -98,11 +99,10 @@ public final class Suggest {
final String consideredWord = trailingSingleQuotesCount > 0
? typedWord.substring(0, typedWord.length() - trailingSingleQuotesCount)
: typedWord;
- LatinImeLogger.onAddSuggestedWord(typedWord, Dictionary.TYPE_USER_TYPED);
final ArrayList<SuggestedWordInfo> rawSuggestions;
if (ProductionFlag.INCLUDE_RAW_SUGGESTIONS) {
- rawSuggestions = CollectionUtils.newArrayList();
+ rawSuggestions = new ArrayList<>();
} else {
rawSuggestions = null;
}
@@ -110,7 +110,7 @@ public final class Suggest {
wordComposer, prevWordsInfo, proximityInfo, blockOffensiveWords,
additionalFeaturesOptions, SESSION_TYPING, rawSuggestions);
- final boolean isFirstCharCapitalized = wordComposer.isFirstCharCapitalized();
+ final boolean isOnlyFirstCharCapitalized = wordComposer.isOnlyFirstCharCapitalized();
// If resumed, then we don't want to upcase everything: resuming on a fully-capitalized
// words is rarely done to switch to another fully-capitalized word, but usually to a
// normal, non-capitalized suggestion.
@@ -122,9 +122,9 @@ public final class Suggest {
} else {
final SuggestedWordInfo firstSuggestedWordInfo = getTransformedSuggestedWordInfo(
suggestionResults.first(), suggestionResults.mLocale, isAllUpperCase,
- isFirstCharCapitalized, trailingSingleQuotesCount);
+ isOnlyFirstCharCapitalized, trailingSingleQuotesCount);
firstSuggestion = firstSuggestedWordInfo.mWord;
- if (SuggestedWordInfo.KIND_WHITELIST != firstSuggestedWordInfo.mKind) {
+ if (!firstSuggestedWordInfo.isKindOf(SuggestedWordInfo.KIND_WHITELIST)) {
whitelistedWord = null;
} else {
whitelistedWord = firstSuggestion;
@@ -142,7 +142,7 @@ public final class Suggest {
final boolean allowsToBeAutoCorrected = (null != whitelistedWord
&& !whitelistedWord.equals(typedWord))
|| (consideredWord.length() > 1 && !mDictionaryFacilitator.isValidWord(
- consideredWord, wordComposer.isFirstCharCapitalized())
+ consideredWord, isOnlyFirstCharCapitalized)
&& !typedWord.equals(firstSuggestion));
final boolean hasAutoCorrection;
@@ -155,7 +155,7 @@ public final class Suggest {
|| suggestionResults.isEmpty() || wordComposer.hasDigits()
|| wordComposer.isMostlyCaps() || wordComposer.isResumed()
|| !mDictionaryFacilitator.hasInitializedMainDictionary()
- || SuggestedWordInfo.KIND_SHORTCUT == suggestionResults.first().mKind) {
+ || suggestionResults.first().isKindOf(SuggestedWordInfo.KIND_SHORTCUT)) {
// If we don't have a main dictionary, we never want to auto-correct. The reason for
// this is, the user may have a contact whose name happens to match a valid word in
// their language, and it will unexpectedly auto-correct. For example, if the user
@@ -171,24 +171,18 @@ public final class Suggest {
}
final ArrayList<SuggestedWordInfo> suggestionsContainer =
- CollectionUtils.newArrayList(suggestionResults);
+ new ArrayList<>(suggestionResults);
final int suggestionsCount = suggestionsContainer.size();
- if (isFirstCharCapitalized || isAllUpperCase || 0 != trailingSingleQuotesCount) {
+ if (isOnlyFirstCharCapitalized || isAllUpperCase || 0 != trailingSingleQuotesCount) {
for (int i = 0; i < suggestionsCount; ++i) {
final SuggestedWordInfo wordInfo = suggestionsContainer.get(i);
final SuggestedWordInfo transformedWordInfo = getTransformedSuggestedWordInfo(
- wordInfo, suggestionResults.mLocale, isAllUpperCase, isFirstCharCapitalized,
- trailingSingleQuotesCount);
+ wordInfo, suggestionResults.mLocale, isAllUpperCase,
+ isOnlyFirstCharCapitalized, trailingSingleQuotesCount);
suggestionsContainer.set(i, transformedWordInfo);
}
}
- for (int i = 0; i < suggestionsCount; ++i) {
- final SuggestedWordInfo wordInfo = suggestionsContainer.get(i);
- LatinImeLogger.onAddSuggestedWord(wordInfo.mWord.toString(),
- wordInfo.mSourceDict.mDictType);
- }
-
if (!TextUtils.isEmpty(typedWord)) {
suggestionsContainer.add(0, new SuggestedWordInfo(typedWord,
SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_TYPED,
@@ -223,19 +217,15 @@ public final class Suggest {
final OnGetSuggestedWordsCallback callback) {
final ArrayList<SuggestedWordInfo> rawSuggestions;
if (ProductionFlag.INCLUDE_RAW_SUGGESTIONS) {
- rawSuggestions = CollectionUtils.newArrayList();
+ rawSuggestions = new ArrayList<>();
} else {
rawSuggestions = null;
}
final SuggestionResults suggestionResults = mDictionaryFacilitator.getSuggestionResults(
wordComposer, prevWordsInfo, proximityInfo, blockOffensiveWords,
additionalFeaturesOptions, sessionId, rawSuggestions);
- for (SuggestedWordInfo wordInfo : suggestionResults) {
- LatinImeLogger.onAddSuggestedWord(wordInfo.mWord, wordInfo.mSourceDict.mDictType);
- }
-
final ArrayList<SuggestedWordInfo> suggestionsContainer =
- CollectionUtils.newArrayList(suggestionResults);
+ new ArrayList<>(suggestionResults);
final int suggestionsCount = suggestionsContainer.size();
final boolean isFirstCharCapitalized = wordComposer.wasShiftedNoLock();
final boolean isAllUpperCase = wordComposer.isAllUpperCase();
@@ -278,8 +268,7 @@ public final class Suggest {
final SuggestedWordInfo typedWordInfo = suggestions.get(0);
typedWordInfo.setDebugString("+");
final int suggestionsSize = suggestions.size();
- final ArrayList<SuggestedWordInfo> suggestionsList =
- CollectionUtils.newArrayList(suggestionsSize);
+ final ArrayList<SuggestedWordInfo> suggestionsList = new ArrayList<>(suggestionsSize);
suggestionsList.add(typedWordInfo);
// Note: i here is the index in mScores[], but the index in mSuggestions is one more
// than i because we added the typed word to mSuggestions without touching mScores.
@@ -303,11 +292,11 @@ public final class Suggest {
/* package for test */ static SuggestedWordInfo getTransformedSuggestedWordInfo(
final SuggestedWordInfo wordInfo, final Locale locale, final boolean isAllUpperCase,
- final boolean isFirstCharCapitalized, final int trailingSingleQuotesCount) {
+ final boolean isOnlyFirstCharCapitalized, final int trailingSingleQuotesCount) {
final StringBuilder sb = new StringBuilder(wordInfo.mWord.length());
if (isAllUpperCase) {
sb.append(wordInfo.mWord.toUpperCase(locale));
- } else if (isFirstCharCapitalized) {
+ } else if (isOnlyFirstCharCapitalized) {
sb.append(StringUtils.capitalizeFirstCodePoint(wordInfo.mWord, locale));
} else {
sb.append(wordInfo.mWord);
@@ -320,7 +309,7 @@ public final class Suggest {
for (int i = quotesToAppend - 1; i >= 0; --i) {
sb.appendCodePoint(Constants.CODE_SINGLE_QUOTE);
}
- return new SuggestedWordInfo(sb.toString(), wordInfo.mScore, wordInfo.mKind,
+ return new SuggestedWordInfo(sb.toString(), wordInfo.mScore, wordInfo.mKindAndFlags,
wordInfo.mSourceDict, wordInfo.mIndexOfTouchPointOfSecondWord,
wordInfo.mAutoCommitFirstWordConfidence);
}
diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java
index dc2c9fd0e..72461e17a 100644
--- a/java/src/com/android/inputmethod/latin/SuggestedWords.java
+++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java
@@ -19,7 +19,6 @@ package com.android.inputmethod.latin;
import android.text.TextUtils;
import android.view.inputmethod.CompletionInfo;
-import com.android.inputmethod.latin.utils.CollectionUtils;
import com.android.inputmethod.latin.utils.StringUtils;
import java.util.ArrayList;
@@ -34,8 +33,7 @@ public class SuggestedWords {
// The maximum number of suggestions available.
public static final int MAX_SUGGESTIONS = 18;
- private static final ArrayList<SuggestedWordInfo> EMPTY_WORD_INFO_LIST =
- CollectionUtils.newArrayList(0);
+ private static final ArrayList<SuggestedWordInfo> EMPTY_WORD_INFO_LIST = new ArrayList<>(0);
public static final SuggestedWords EMPTY = new SuggestedWords(
EMPTY_WORD_INFO_LIST, null /* rawSuggestions */, false, false, false, false);
@@ -165,7 +163,7 @@ public class SuggestedWords {
public static ArrayList<SuggestedWordInfo> getFromApplicationSpecifiedCompletions(
final CompletionInfo[] infos) {
- final ArrayList<SuggestedWordInfo> result = CollectionUtils.newArrayList();
+ final ArrayList<SuggestedWordInfo> result = new ArrayList<>();
for (final CompletionInfo info : infos) {
if (null == info || null == info.getText()) {
continue;
@@ -179,8 +177,8 @@ public class SuggestedWords {
// and replace it with what the user currently typed.
public static ArrayList<SuggestedWordInfo> getTypedWordAndPreviousSuggestions(
final String typedWord, final SuggestedWords previousSuggestions) {
- final ArrayList<SuggestedWordInfo> suggestionsList = CollectionUtils.newArrayList();
- final HashSet<String> alreadySeen = CollectionUtils.newHashSet();
+ final ArrayList<SuggestedWordInfo> suggestionsList = new ArrayList<>();
+ final HashSet<String> alreadySeen = new HashSet<>();
suggestionsList.add(new SuggestedWordInfo(typedWord, SuggestedWordInfo.MAX_SCORE,
SuggestedWordInfo.KIND_TYPED, Dictionary.DICTIONARY_USER_TYPED,
SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */,
@@ -209,7 +207,8 @@ public class SuggestedWords {
public static final int NOT_AN_INDEX = -1;
public static final int NOT_A_CONFIDENCE = -1;
public static final int MAX_SCORE = Integer.MAX_VALUE;
- public static final int KIND_MASK_KIND = 0xFF; // Mask to get only the kind
+
+ private static final int KIND_MASK_KIND = 0xFF; // Mask to get only the kind
public static final int KIND_TYPED = 0; // What user typed
public static final int KIND_CORRECTION = 1; // Simple correction/suggestion
public static final int KIND_COMPLETION = 2; // Completion (suggestion with appended chars)
@@ -224,16 +223,16 @@ public class SuggestedWords {
public static final int KIND_RESUMED = 9;
public static final int KIND_OOV_CORRECTION = 10; // Most probable string correction
- public static final int KIND_MASK_FLAGS = 0xFFFFFF00; // Mask to get the flags
public static final int KIND_FLAG_POSSIBLY_OFFENSIVE = 0x80000000;
public static final int KIND_FLAG_EXACT_MATCH = 0x40000000;
+ public static final int KIND_FLAG_EXACT_MATCH_WITH_INTENTIONAL_OMISSION = 0x20000000;
public final String mWord;
// The completion info from the application. Null for suggestions that don't come from
// the application (including keyboard-computed ones, so this is almost always null)
public final CompletionInfo mApplicationSpecifiedCompletionInfo;
public final int mScore;
- public final int mKind; // one of the KIND_* constants above
+ public final int mKindAndFlags;
public final int mCodePointCount;
public final Dictionary mSourceDict;
// For auto-commit. This keeps track of the index inside the touch coordinates array
@@ -249,18 +248,19 @@ public class SuggestedWords {
* Create a new suggested word info.
* @param word The string to suggest.
* @param score A measure of how likely this suggestion is.
- * @param kind The kind of suggestion, as one of the above KIND_* constants.
+ * @param kindAndFlags The kind of suggestion, as one of the above KIND_* constants with
+ * flags.
* @param sourceDict What instance of Dictionary produced this suggestion.
* @param indexOfTouchPointOfSecondWord See mIndexOfTouchPointOfSecondWord.
* @param autoCommitFirstWordConfidence See mAutoCommitFirstWordConfidence.
*/
- public SuggestedWordInfo(final String word, final int score, final int kind,
+ public SuggestedWordInfo(final String word, final int score, final int kindAndFlags,
final Dictionary sourceDict, final int indexOfTouchPointOfSecondWord,
final int autoCommitFirstWordConfidence) {
mWord = word;
mApplicationSpecifiedCompletionInfo = null;
mScore = score;
- mKind = kind;
+ mKindAndFlags = kindAndFlags;
mSourceDict = sourceDict;
mCodePointCount = StringUtils.codePointCount(mWord);
mIndexOfTouchPointOfSecondWord = indexOfTouchPointOfSecondWord;
@@ -276,7 +276,7 @@ public class SuggestedWords {
mWord = applicationSpecifiedCompletion.getText().toString();
mApplicationSpecifiedCompletionInfo = applicationSpecifiedCompletion;
mScore = SuggestedWordInfo.MAX_SCORE;
- mKind = SuggestedWordInfo.KIND_APP_DEFINED;
+ mKindAndFlags = SuggestedWordInfo.KIND_APP_DEFINED;
mSourceDict = Dictionary.DICTIONARY_APPLICATION_DEFINED;
mCodePointCount = StringUtils.codePointCount(mWord);
mIndexOfTouchPointOfSecondWord = SuggestedWordInfo.NOT_AN_INDEX;
@@ -284,7 +284,27 @@ public class SuggestedWords {
}
public boolean isEligibleForAutoCommit() {
- return (KIND_CORRECTION == mKind && NOT_AN_INDEX != mIndexOfTouchPointOfSecondWord);
+ return (isKindOf(KIND_CORRECTION) && NOT_AN_INDEX != mIndexOfTouchPointOfSecondWord);
+ }
+
+ public int getKind() {
+ return (mKindAndFlags & KIND_MASK_KIND);
+ }
+
+ public boolean isKindOf(final int kind) {
+ return getKind() == kind;
+ }
+
+ public boolean isPossiblyOffensive() {
+ return (mKindAndFlags & KIND_FLAG_POSSIBLY_OFFENSIVE) != 0;
+ }
+
+ public boolean isExactMatch() {
+ return (mKindAndFlags & KIND_FLAG_EXACT_MATCH) != 0;
+ }
+
+ public boolean isExactMatchWithIntentionalOmission() {
+ return (mKindAndFlags & KIND_FLAG_EXACT_MATCH_WITH_INTENTIONAL_OMISSION) != 0;
}
public void setDebugString(final String str) {
@@ -337,11 +357,11 @@ public class SuggestedWords {
// SuggestedWords is an immutable object, as much as possible. We must not just remove
// words from the member ArrayList as some other parties may expect the object to never change.
public SuggestedWords getSuggestedWordsExcludingTypedWord() {
- final ArrayList<SuggestedWordInfo> newSuggestions = CollectionUtils.newArrayList();
+ final ArrayList<SuggestedWordInfo> newSuggestions = new ArrayList<>();
String typedWord = null;
for (int i = 0; i < mSuggestedWordInfoList.size(); ++i) {
final SuggestedWordInfo info = mSuggestedWordInfoList.get(i);
- if (SuggestedWordInfo.KIND_TYPED != info.mKind) {
+ if (!info.isKindOf(SuggestedWordInfo.KIND_TYPED)) {
newSuggestions.add(info);
} else {
assert(null == typedWord);
@@ -361,12 +381,12 @@ public class SuggestedWords {
// we should only suggest replacements for this last word.
// TODO: make this work with languages without spaces.
public SuggestedWords getSuggestedWordsForLastWordOfPhraseGesture() {
- final ArrayList<SuggestedWordInfo> newSuggestions = CollectionUtils.newArrayList();
+ final ArrayList<SuggestedWordInfo> newSuggestions = new ArrayList<>();
for (int i = 0; i < mSuggestedWordInfoList.size(); ++i) {
final SuggestedWordInfo info = mSuggestedWordInfoList.get(i);
final int indexOfLastSpace = info.mWord.lastIndexOf(Constants.CODE_SPACE) + 1;
final String lastWord = info.mWord.substring(indexOfLastSpace);
- newSuggestions.add(new SuggestedWordInfo(lastWord, info.mScore, info.mKind,
+ newSuggestions.add(new SuggestedWordInfo(lastWord, info.mScore, info.mKindAndFlags,
info.mSourceDict, SuggestedWordInfo.NOT_AN_INDEX,
SuggestedWordInfo.NOT_A_CONFIDENCE));
}
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
index 6ecb37346..6ce1f85c5 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -18,7 +18,6 @@ package com.android.inputmethod.latin;
import com.android.inputmethod.event.CombinerChain;
import com.android.inputmethod.event.Event;
-import com.android.inputmethod.latin.utils.CollectionUtils;
import com.android.inputmethod.latin.utils.CoordinateUtils;
import com.android.inputmethod.latin.utils.StringUtils;
@@ -46,9 +45,6 @@ public final class WordComposer {
// The list of events that served to compose this string.
private final ArrayList<Event> mEvents;
private final InputPointers mInputPointers = new InputPointers(MAX_WORD_LENGTH);
- // The information of previous words (before the composing word). Must not be null. Used as
- // context for suggestions.
- private PrevWordsInfo mPrevWordsInfo;
private String mAutoCorrection;
private boolean mIsResumed;
private boolean mIsBatchMode;
@@ -73,19 +69,18 @@ public final class WordComposer {
private int mCursorPositionWithinWord;
/**
- * Whether the user chose to capitalize the first char of the word.
+ * Whether the composing word has the only first char capitalized.
*/
- private boolean mIsFirstCharCapitalized;
+ private boolean mIsOnlyFirstCharCapitalized;
public WordComposer() {
mCombinerChain = new CombinerChain("");
- mEvents = CollectionUtils.newArrayList();
+ mEvents = new ArrayList<>();
mAutoCorrection = null;
mIsResumed = false;
mIsBatchMode = false;
mCursorPositionWithinWord = 0;
mRejectedBatchModeSuggestion = null;
- mPrevWordsInfo = new PrevWordsInfo(null);
refreshTypedWordCache();
}
@@ -112,12 +107,11 @@ public final class WordComposer {
mAutoCorrection = null;
mCapsCount = 0;
mDigitsCount = 0;
- mIsFirstCharCapitalized = false;
+ mIsOnlyFirstCharCapitalized = false;
mIsResumed = false;
mIsBatchMode = false;
mCursorPositionWithinWord = 0;
mRejectedBatchModeSuggestion = null;
- mPrevWordsInfo = new PrevWordsInfo(null);
refreshTypedWordCache();
}
@@ -146,9 +140,12 @@ public final class WordComposer {
*/
public int copyCodePointsExceptTrailingSingleQuotesAndReturnCodePointCount(
final int[] destination) {
+ // This method can be called on a separate thread and mTypedWordCache can change while we
+ // are executing this method.
+ final String typedWord = mTypedWordCache.toString();
// lastIndex is exclusive
- final int lastIndex = mTypedWordCache.length()
- - StringUtils.getTrailingSingleQuotesCount(mTypedWordCache);
+ final int lastIndex = typedWord.length()
+ - StringUtils.getTrailingSingleQuotesCount(typedWord);
if (lastIndex <= 0) {
// The string is empty or contains only single quotes.
return 0;
@@ -156,11 +153,11 @@ public final class WordComposer {
// The following function counts the number of code points in the text range which begins
// at index 0 and extends to the character at lastIndex.
- final int codePointSize = Character.codePointCount(mTypedWordCache, 0, lastIndex);
+ final int codePointSize = Character.codePointCount(typedWord, 0, lastIndex);
if (codePointSize > destination.length) {
return -1;
}
- return StringUtils.copyCodePointsAndReturnCodePointCount(destination, mTypedWordCache, 0,
+ return StringUtils.copyCodePointsAndReturnCodePointCount(destination, typedWord, 0,
lastIndex, true /* downCase */);
}
@@ -176,12 +173,6 @@ public final class WordComposer {
return mInputPointers;
}
- private static boolean isFirstCharCapitalized(final int index, final int codePoint,
- final boolean previous) {
- if (index == 0) return Character.isUpperCase(codePoint);
- return previous && !Character.isUpperCase(codePoint);
- }
-
/**
* Process an input event.
*
@@ -201,7 +192,7 @@ public final class WordComposer {
mCursorPositionWithinWord = mCodePointSize;
// We may have deleted the last one.
if (0 == mCodePointSize) {
- mIsFirstCharCapitalized = false;
+ mIsOnlyFirstCharCapitalized = false;
}
if (Constants.CODE_DELETE != event.mKeyCode) {
if (newIndex < MAX_WORD_LENGTH) {
@@ -213,8 +204,12 @@ public final class WordComposer {
mInputPointers.addPointerAt(newIndex, keyX, keyY, 0, 0);
}
}
- mIsFirstCharCapitalized = isFirstCharCapitalized(
- newIndex, primaryCode, mIsFirstCharCapitalized);
+ if (0 == newIndex) {
+ mIsOnlyFirstCharCapitalized = Character.isUpperCase(primaryCode);
+ } else {
+ mIsOnlyFirstCharCapitalized = mIsOnlyFirstCharCapitalized
+ && !Character.isUpperCase(primaryCode);
+ }
if (Character.isUpperCase(primaryCode)) mCapsCount++;
if (Character.isDigit(primaryCode)) mDigitsCount++;
}
@@ -294,10 +289,8 @@ public final class WordComposer {
* This will register NOT_A_COORDINATE for X and Ys, and use the passed keyboard for proximity.
* @param codePoints the code points to set as the composing word.
* @param coordinates the x, y coordinates of the key in the CoordinateUtils format
- * @param prevWordsInfo the information of previous words, to use as context for suggestions
*/
- public void setComposingWord(final int[] codePoints, final int[] coordinates,
- final PrevWordsInfo prevWordsInfo) {
+ public void setComposingWord(final int[] codePoints, final int[] coordinates) {
reset();
final int length = codePoints.length;
for (int i = 0; i < length; ++i) {
@@ -306,7 +299,6 @@ public final class WordComposer {
CoordinateUtils.yFromArray(coordinates, i)));
}
mIsResumed = true;
- mPrevWordsInfo = prevWordsInfo;
}
/**
@@ -317,16 +309,13 @@ public final class WordComposer {
return mTypedWordCache.toString();
}
- public PrevWordsInfo getPrevWordsInfoForSuggestion() {
- return mPrevWordsInfo;
- }
-
/**
- * Whether or not the user typed a capital letter as the first letter in the word
+ * Whether or not the user typed a capital letter as the first letter in the word, and no
+ * other letter is capitalized
* @return capitalization preference
*/
- public boolean isFirstCharCapitalized() {
- return mIsFirstCharCapitalized;
+ public boolean isOnlyFirstCharCapitalized() {
+ return mIsOnlyFirstCharCapitalized;
}
/**
@@ -362,7 +351,7 @@ public final class WordComposer {
}
/**
- * Saves the caps mode and the previous word at the start of composing.
+ * Saves the caps mode at the start of composing.
*
* WordComposer needs to know about the caps mode for several reasons. The first is, we need
* to know after the fact what the reason was, to register the correct form into the user
@@ -371,12 +360,9 @@ public final class WordComposer {
* Also, batch input needs to know about the current caps mode to display correctly
* capitalized suggestions.
* @param mode the mode at the time of start
- * @param prevWordsInfo the information of previous words
*/
- public void setCapitalizedModeAndPreviousWordAtStartComposingTime(final int mode,
- final PrevWordsInfo prevWordsInfo) {
+ public void setCapitalizedModeAtStartComposingTime(final int mode) {
mCapitalizedMode = mode;
- mPrevWordsInfo = prevWordsInfo;
}
/**
@@ -427,11 +413,10 @@ public final class WordComposer {
mCapsCount = 0;
mDigitsCount = 0;
mIsBatchMode = false;
- mPrevWordsInfo = new PrevWordsInfo(committedWord.toString());
mCombinerChain.reset();
mEvents.clear();
mCodePointSize = 0;
- mIsFirstCharCapitalized = false;
+ mIsOnlyFirstCharCapitalized = false;
mCapitalizedMode = CAPS_MODE_OFF;
refreshTypedWordCache();
mAutoCorrection = null;
@@ -441,15 +426,7 @@ public final class WordComposer {
return lastComposedWord;
}
- // Call this when the recorded previous word should be discarded. This is typically called
- // when the user inputs a separator that's not whitespace (including the case of the
- // double-space-to-period feature).
- public void discardPreviousWordForSuggestion() {
- mPrevWordsInfo = new PrevWordsInfo(null);
- }
-
- public void resumeSuggestionOnLastComposedWord(final LastComposedWord lastComposedWord,
- final PrevWordsInfo prevWordsInfo) {
+ public void resumeSuggestionOnLastComposedWord(final LastComposedWord lastComposedWord) {
mEvents.clear();
Collections.copy(mEvents, lastComposedWord.mEvents);
mInputPointers.set(lastComposedWord.mInputPointers);
@@ -460,7 +437,6 @@ public final class WordComposer {
mCursorPositionWithinWord = mCodePointSize;
mRejectedBatchModeSuggestion = null;
mIsResumed = true;
- mPrevWordsInfo = prevWordsInfo;
}
public boolean isBatchMode() {
diff --git a/java/src/com/android/inputmethod/latin/debug/ExternalDictionaryGetterForDebug.java b/java/src/com/android/inputmethod/latin/debug/ExternalDictionaryGetterForDebug.java
index 139e73aa4..7071d8689 100644
--- a/java/src/com/android/inputmethod/latin/debug/ExternalDictionaryGetterForDebug.java
+++ b/java/src/com/android/inputmethod/latin/debug/ExternalDictionaryGetterForDebug.java
@@ -27,7 +27,6 @@ import com.android.inputmethod.latin.BinaryDictionaryFileDumper;
import com.android.inputmethod.latin.BinaryDictionaryGetter;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.makedict.DictionaryHeader;
-import com.android.inputmethod.latin.utils.CollectionUtils;
import com.android.inputmethod.latin.utils.DialogUtils;
import com.android.inputmethod.latin.utils.DictionaryInfoUtils;
import com.android.inputmethod.latin.utils.LocaleUtils;
@@ -50,7 +49,7 @@ public class ExternalDictionaryGetterForDebug {
private static String[] findDictionariesInTheDownloadedFolder() {
final File[] files = new File(SOURCE_FOLDER).listFiles();
- final ArrayList<String> eligibleList = CollectionUtils.newArrayList();
+ final ArrayList<String> eligibleList = new ArrayList<>();
for (File f : files) {
final DictionaryHeader header = DictionaryInfoUtils.getDictionaryFileHeaderOrNull(f);
if (null == header) continue;
diff --git a/java/src/com/android/inputmethod/latin/define/ProductionFlag.java b/java/src/com/android/inputmethod/latin/define/ProductionFlag.java
index 761f457ea..972580298 100644
--- a/java/src/com/android/inputmethod/latin/define/ProductionFlag.java
+++ b/java/src/com/android/inputmethod/latin/define/ProductionFlag.java
@@ -21,13 +21,6 @@ public final class ProductionFlag {
// This class is not publicly instantiable.
}
- public static final boolean USES_DEVELOPMENT_ONLY_DIAGNOSTICS = false;
-
- // When false, USES_DEVELOPMENT_ONLY_DIAGNOSTICS_DEBUG suggests that all guarded
- // class-private DEBUG flags should be false, and any privacy controls should be enforced.
- // USES_DEVELOPMENT_ONLY_DIAGNOSTICS must be false for any production build.
- public static final boolean USES_DEVELOPMENT_ONLY_DIAGNOSTICS_DEBUG = false;
-
public static final boolean IS_HARDWARE_KEYBOARD_SUPPORTED = false;
// When true, enable {@link InputMethodService#onUpdateCursor} callback with
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
index 7536ff94c..24cc1ef0d 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
@@ -32,7 +32,7 @@ import com.android.inputmethod.event.InputTransaction;
import com.android.inputmethod.keyboard.KeyboardSwitcher;
import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.Dictionary;
-import com.android.inputmethod.latin.DictionaryFacilitatorForSuggest;
+import com.android.inputmethod.latin.DictionaryFacilitator;
import com.android.inputmethod.latin.InputPointers;
import com.android.inputmethod.latin.LastComposedWord;
import com.android.inputmethod.latin.LatinIME;
@@ -44,18 +44,14 @@ import com.android.inputmethod.latin.Suggest.OnGetSuggestedWordsCallback;
import com.android.inputmethod.latin.SuggestedWords;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.WordComposer;
-import com.android.inputmethod.latin.define.ProductionFlag;
import com.android.inputmethod.latin.settings.SettingsValues;
import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
import com.android.inputmethod.latin.suggestions.SuggestionStripViewAccessor;
import com.android.inputmethod.latin.utils.AsyncResultHolder;
-import com.android.inputmethod.latin.utils.CollectionUtils;
import com.android.inputmethod.latin.utils.InputTypeUtils;
-import com.android.inputmethod.latin.utils.LatinImeLoggerUtils;
import com.android.inputmethod.latin.utils.RecapitalizeStatus;
import com.android.inputmethod.latin.utils.StringUtils;
import com.android.inputmethod.latin.utils.TextRange;
-import com.android.inputmethod.research.ResearchLogger;
import java.util.ArrayList;
import java.util.TreeSet;
@@ -79,7 +75,8 @@ public final class InputLogic {
private int mSpaceState;
// Never null
public SuggestedWords mSuggestedWords = SuggestedWords.EMPTY;
- public final Suggest mSuggest = new Suggest();
+ public final Suggest mSuggest;
+ private final DictionaryFacilitator mDictionaryFacilitator;
public LastComposedWord mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
public final WordComposer mWordComposer;
@@ -88,7 +85,7 @@ public final class InputLogic {
private int mDeleteCount;
private long mLastKeyTime;
- public final TreeSet<Long> mCurrentlyPressedHardwareKeys = CollectionUtils.newTreeSet();
+ public final TreeSet<Long> mCurrentlyPressedHardwareKeys = new TreeSet<>();
// Keeps track of most recently inserted text (multi-character key) for reverting
private String mEnteredText;
@@ -102,14 +99,19 @@ public final class InputLogic {
* Create a new instance of the input logic.
* @param latinIME the instance of the parent LatinIME. We should remove this when we can.
* @param suggestionStripViewAccessor an object to access the suggestion strip view.
+ * @param dictionaryFacilitator facilitator for getting suggestions and updating user history
+ * dictionary.
*/
public InputLogic(final LatinIME latinIME,
- final SuggestionStripViewAccessor suggestionStripViewAccessor) {
+ final SuggestionStripViewAccessor suggestionStripViewAccessor,
+ final DictionaryFacilitator dictionaryFacilitator) {
mLatinIME = latinIME;
mSuggestionStripViewAccessor = suggestionStripViewAccessor;
mWordComposer = new WordComposer();
mConnection = new RichInputConnection(latinIME);
mInputLogicHandler = InputLogicHandler.NULL_HANDLER;
+ mSuggest = new Suggest(dictionaryFacilitator);
+ mDictionaryFacilitator = dictionaryFacilitator;
}
/**
@@ -132,7 +134,7 @@ public final class InputLogic {
resetComposingState(true /* alsoResetLastComposedWord */);
mDeleteCount = 0;
mSpaceState = SpaceState.NONE;
- mRecapitalizeStatus.deactivate();
+ mRecapitalizeStatus.disable(); // Do not perform recapitalize until the cursor is moved once
mCurrentlyPressedHardwareKeys.clear();
mSuggestedWords = SuggestedWords.EMPTY;
// In some cases (namely, after rotation of the device) editorInfo.initialSelStart is lying
@@ -173,7 +175,7 @@ public final class InputLogic {
final InputLogicHandler inputLogicHandler = mInputLogicHandler;
mInputLogicHandler = InputLogicHandler.NULL_HANDLER;
inputLogicHandler.destroy();
- mSuggest.mDictionaryFacilitator.closeDictionaries();
+ mDictionaryFacilitator.closeDictionaries();
}
/**
@@ -196,19 +198,11 @@ public final class InputLogic {
resetComposingState(true /* alsoResetLastComposedWord */);
}
handler.postUpdateSuggestionStrip();
- if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS
- && ResearchLogger.RESEARCH_KEY_OUTPUT_TEXT.equals(rawText)) {
- ResearchLogger.getInstance().onResearchKeySelected(mLatinIME);
- return;
- }
final String text = performSpecificTldProcessingOnTextInput(rawText);
if (SpaceState.PHANTOM == mSpaceState) {
promotePhantomSpace(settingsValues);
}
mConnection.commitText(text, 1);
- if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- ResearchLogger.latinIME_onTextInput(text, false /* isBatchMode */);
- }
mConnection.endBatchEdit();
// Space state must be updated before calling updateShiftState
mSpaceState = SpaceState.NONE;
@@ -235,14 +229,8 @@ public final class InputLogic {
// If this is a punctuation picked from the suggestion strip, pass it to onCodeInput
if (suggestion.length() == 1 && suggestedWords.isPunctuationSuggestions()) {
// Word separators are suggested before the user inputs something.
- // So, LatinImeLogger logs "" as a user's input.
- LatinImeLogger.logOnManualSuggestion("", suggestion, index, suggestedWords);
// Rely on onCodeInput to do the complicated swapping/stripping logic consistently.
final Event event = Event.createPunctuationSuggestionPickedEvent(suggestionInfo);
- if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- ResearchLogger.latinIME_punctuationSuggestion(index, suggestion,
- false /* isBatchMode */, suggestedWords.mIsPrediction);
- }
return onCodeInput(settingsValues, event, keyboardShiftState, handler);
}
@@ -265,7 +253,7 @@ public final class InputLogic {
// code path as for other kinds, use commitChosenWord, and do everything normally. We will
// however need to reset the suggestion strip right away, because we know we can't take
// the risk of calling commitCompletion twice because we don't know how the app will react.
- if (SuggestedWordInfo.KIND_APP_DEFINED == suggestionInfo.mKind) {
+ if (suggestionInfo.isKindOf(SuggestedWordInfo.KIND_APP_DEFINED)) {
mSuggestedWords = SuggestedWords.EMPTY;
mSuggestionStripViewAccessor.setNeutralSuggestionStrip();
inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW);
@@ -278,14 +266,8 @@ public final class InputLogic {
// We need to log before we commit, because the word composer will store away the user
// typed word.
final String replacedWord = mWordComposer.getTypedWord();
- LatinImeLogger.logOnManualSuggestion(replacedWord, suggestion, index, suggestedWords);
commitChosenWord(settingsValues, suggestion,
LastComposedWord.COMMIT_TYPE_MANUAL_PICK, LastComposedWord.NOT_A_SEPARATOR);
- if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- ResearchLogger.latinIME_pickSuggestionManually(replacedWord, index, suggestion,
- mWordComposer.isBatchMode(), suggestionInfo.mScore,
- suggestionInfo.mKind, suggestionInfo.mSourceDict.mDictType);
- }
mConnection.endBatchEdit();
// Don't allow cancellation of manual pick
mLastComposedWord.deactivate();
@@ -295,18 +277,12 @@ public final class InputLogic {
// We should show the "Touch again to save" hint if the user pressed the first entry
// AND it's in none of our current dictionaries (main, user or otherwise).
- final DictionaryFacilitatorForSuggest dictionaryFacilitator =
- mSuggest.mDictionaryFacilitator;
final boolean showingAddToDictionaryHint =
- (SuggestedWordInfo.KIND_TYPED == suggestionInfo.mKind
- || SuggestedWordInfo.KIND_OOV_CORRECTION == suggestionInfo.mKind)
- && !dictionaryFacilitator.isValidWord(suggestion, true /* ignoreCase */);
+ (suggestionInfo.isKindOf(SuggestedWordInfo.KIND_TYPED)
+ || suggestionInfo.isKindOf(SuggestedWordInfo.KIND_OOV_CORRECTION))
+ && !mDictionaryFacilitator.isValidWord(suggestion, true /* ignoreCase */);
- if (settingsValues.mIsInternal) {
- LatinImeLoggerUtils.onSeparator((char)Constants.CODE_SPACE,
- Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
- }
- if (showingAddToDictionaryHint && dictionaryFacilitator.isUserDictionaryEnabled()) {
+ if (showingAddToDictionaryHint && mDictionaryFacilitator.isUserDictionaryEnabled()) {
mSuggestionStripViewAccessor.showAddToDictionaryHint(suggestion);
} else {
// If we're not showing the "Touch again to save", then update the suggestion strip.
@@ -343,8 +319,16 @@ public final class InputLogic {
|| !mWordComposer.isComposingWord(); // safe to reset
final boolean hasOrHadSelection = (oldSelStart != oldSelEnd || newSelStart != newSelEnd);
final int moveAmount = newSelStart - oldSelStart;
- if (selectionChangedOrSafeToReset && (hasOrHadSelection
- || !mWordComposer.moveCursorByAndReturnIfInsideComposingWord(moveAmount))) {
+ // As an added small gift from the framework, it happens upon rotation when there
+ // is a selection that we get a wrong cursor position delivered to startInput() that
+ // does not get reflected in the oldSel{Start,End} parameters to the next call to
+ // onUpdateSelection. In this case, we may have set a composition, and when we're here
+ // we realize we shouldn't have. In theory, in this case, selectionChangedOrSafeToReset
+ // should be true, but that is if the framework had taken that wrong cursor position
+ // into account, which means we have to reset the entire composing state whenever there
+ // is or was a selection regardless of whether it changed or not.
+ if (hasOrHadSelection || (selectionChangedOrSafeToReset
+ && !mWordComposer.moveCursorByAndReturnIfInsideComposingWord(moveAmount))) {
// If we are composing a word and moving the cursor, we would want to set a
// suggestion span for recorrection to work correctly. Unfortunately, that
// would involve the keyboard committing some new text, which would move the
@@ -369,10 +353,12 @@ public final class InputLogic {
newSelStart, newSelEnd, false /* shouldFinishComposition */);
}
+ // The cursor has been moved : we now accept to perform recapitalization
+ mRecapitalizeStatus.enable();
// We moved the cursor. If we are touching a word, we need to resume suggestion.
- mLatinIME.mHandler.postResumeSuggestions();
- // Reset the last recapitalization.
- mRecapitalizeStatus.deactivate();
+ mLatinIME.mHandler.postResumeSuggestions(false /* shouldIncludeResumedWordInSuggestions */);
+ // Stop the last recapitalization, if started.
+ mRecapitalizeStatus.stop();
return true;
}
@@ -393,16 +379,9 @@ public final class InputLogic {
final int keyboardShiftMode,
// TODO: remove this argument
final LatinIME.UIHandler handler) {
- // TODO: rework the following to not squash the keycode and the code point into the same
- // var because it's confusing. Instead the switch() should handle this in a readable manner.
- final int code =
- Event.NOT_A_CODE_POINT == event.mCodePoint ? event.mKeyCode : event.mCodePoint;
final InputTransaction inputTransaction = new InputTransaction(settingsValues, event,
SystemClock.uptimeMillis(), mSpaceState,
getActualCapsMode(settingsValues, keyboardShiftMode));
- if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- ResearchLogger.latinIME_onCodeInput(code, event.mX, event.mY);
- }
if (event.mKeyCode != Constants.CODE_DELETE
|| inputTransaction.mTimestamp > mLastKeyTime + Constants.LONG_PRESS_MILLISECONDS) {
mDeleteCount = 0;
@@ -424,7 +403,6 @@ public final class InputLogic {
switch (event.mKeyCode) {
case Constants.CODE_DELETE:
handleBackspace(inputTransaction);
- LatinImeLogger.logOnDelete(event.mX, event.mY);
break;
case Constants.CODE_SHIFT:
performRecapitalization(inputTransaction.mSettingsValues);
@@ -530,12 +508,6 @@ public final class InputLogic {
++mAutoCommitSequenceNumber;
mConnection.beginBatchEdit();
if (mWordComposer.isComposingWord()) {
- if (settingsValues.mIsInternal) {
- if (mWordComposer.isBatchMode()) {
- LatinImeLoggerUtils.onAutoCorrection("", mWordComposer.getTypedWord(), " ",
- mWordComposer);
- }
- }
if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) {
// If we are in the middle of a recorrection, we need to commit the recorrection
// first so that we can insert the batch input at the current cursor position.
@@ -572,11 +544,8 @@ public final class InputLogic {
}
}
mConnection.endBatchEdit();
- mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime(
- getActualCapsMode(settingsValues, keyboardSwitcher.getKeyboardShiftMode()),
- // Prev word is 1st word before cursor
- getPrevWordsInfoFromNthPreviousWordForSuggestion(
- settingsValues.mSpacingAndPunctuations, 1 /* nthPreviousWord */));
+ mWordComposer.setCapitalizedModeAtStartComposingTime(
+ getActualCapsMode(settingsValues, keyboardSwitcher.getKeyboardShiftMode()));
}
/* The sequence number member is only used in onUpdateBatchInput. It is increased each time
@@ -612,10 +581,8 @@ public final class InputLogic {
mSpaceState = SpaceState.PHANTOM;
keyboardSwitcher.requestUpdatingShiftState(
getCurrentAutoCapsState(settingsValues), getCurrentRecapitalizeState());
- mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime(
- getActualCapsMode(settingsValues,
- keyboardSwitcher.getKeyboardShiftMode()),
- new PrevWordsInfo(commitParts[0]));
+ mWordComposer.setCapitalizedModeAtStartComposingTime(getActualCapsMode(
+ settingsValues, keyboardSwitcher.getKeyboardShiftMode()));
++mAutoCommitSequenceNumber;
}
}
@@ -675,19 +642,9 @@ public final class InputLogic {
|| Character.getType(codePoint) == Character.OTHER_SYMBOL) {
didAutoCorrect = handleSeparator(inputTransaction,
inputTransaction.mEvent.isSuggestionStripPress(), handler);
- if (inputTransaction.mSettingsValues.mIsInternal) {
- LatinImeLoggerUtils.onSeparator((char)codePoint,
- inputTransaction.mEvent.mX, inputTransaction.mEvent.mY);
- }
} else {
didAutoCorrect = false;
if (SpaceState.PHANTOM == inputTransaction.mSpaceState) {
- if (inputTransaction.mSettingsValues.mIsInternal) {
- if (mWordComposer.isComposingWord() && mWordComposer.isBatchMode()) {
- LatinImeLoggerUtils.onAutoCorrection("", mWordComposer.getTypedWord(), " ",
- mWordComposer);
- }
- }
if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) {
// If we are in the middle of a recorrection, we need to commit the recorrection
// first so that we can insert the character at the current cursor position.
@@ -748,11 +705,10 @@ public final class InputLogic {
(!mConnection.isCursorTouchingWord(settingsValues.mSpacingAndPunctuations)
|| !settingsValues.mSpacingAndPunctuations.mCurrentLanguageHasSpaces)) {
// Reset entirely the composing state anyway, then start composing a new word unless
- // the character is a single quote or a dash. The idea here is, single quote and dash
- // are not separators and they should be treated as normal characters, except in the
- // first position where they should not start composing a word.
- isComposingWord = (Constants.CODE_SINGLE_QUOTE != codePoint
- && Constants.CODE_DASH != codePoint);
+ // the character is a word connector. The idea here is, word connectors are not
+ // separators and they should be treated as normal characters, except in the first
+ // position where they should not start composing a word.
+ isComposingWord = !settingsValues.mSpacingAndPunctuations.isWordConnector(codePoint);
// Here we don't need to reset the last composed word. It will be reset
// when we commit this one, if we ever do; if on the other hand we backspace
// it entirely and resume suggestions on the previous word, we'd like to still
@@ -763,12 +719,7 @@ public final class InputLogic {
mWordComposer.processEvent(inputTransaction.mEvent);
// If it's the first letter, make note of auto-caps state
if (mWordComposer.isSingleLetter()) {
- // We pass 1 to getPreviousWordForSuggestion because we were not composing a word
- // yet, so the word we want is the 1st word before the cursor.
- mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime(
- inputTransaction.mShiftState,
- getPrevWordsInfoFromNthPreviousWordForSuggestion(
- settingsValues.mSpacingAndPunctuations, 1 /* nthPreviousWord */));
+ mWordComposer.setCapitalizedModeAtStartComposingTime(inputTransaction.mShiftState);
}
mConnection.setComposingText(getTextWithUnderline(
mWordComposer.getTypedWord()), 1);
@@ -786,10 +737,6 @@ public final class InputLogic {
mSuggestionStripViewAccessor.dismissAddToDictionaryHint();
}
inputTransaction.setRequiresUpdateSuggestions();
- if (settingsValues.mIsInternal) {
- LatinImeLoggerUtils.onNonSeparator((char)codePoint, inputTransaction.mEvent.mX,
- inputTransaction.mEvent.mY);
- }
}
/**
@@ -818,7 +765,7 @@ public final class InputLogic {
}
// isComposingWord() may have changed since we stored wasComposing
if (mWordComposer.isComposingWord()) {
- if (settingsValues.mCorrectionEnabled) {
+ if (settingsValues.mAutoCorrectionEnabled) {
final String separator = shouldAvoidSendingCode ? LastComposedWord.NOT_A_SEPARATOR
: StringUtils.newSingleCodePointString(codePoint);
commitCurrentAutoCorrection(settingsValues, separator, handler);
@@ -852,9 +799,6 @@ public final class InputLogic {
if (needsPrecedingSpace) {
promotePhantomSpace(settingsValues);
}
- if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- ResearchLogger.latinIME_handleSeparator(codePoint, wasComposingWord);
- }
if (!shouldAvoidSendingCode) {
sendKeyCodePoint(settingsValues, codePoint);
@@ -863,6 +807,7 @@ public final class InputLogic {
if (Constants.CODE_SPACE == codePoint) {
if (maybeDoubleSpacePeriod(inputTransaction)) {
inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW);
+ inputTransaction.setRequiresUpdateSuggestions();
mSpaceState = SpaceState.DOUBLE;
} else if (!mSuggestedWords.isPunctuationSuggestions()) {
mSpaceState = SpaceState.WEAK;
@@ -932,10 +877,6 @@ public final class InputLogic {
}
if (mWordComposer.isComposingWord()) {
if (mWordComposer.isBatchMode()) {
- if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- final String word = mWordComposer.getTypedWord();
- ResearchLogger.latinIME_handleBackspace_batch(word, 1);
- }
final String rejectedSuggestion = mWordComposer.getTypedWord();
mWordComposer.reset();
mWordComposer.setRejectedBatchModeSuggestion(rejectedSuggestion);
@@ -950,9 +891,6 @@ public final class InputLogic {
inputTransaction.setRequiresUpdateSuggestions();
} else {
if (mLastComposedWord.canRevertCommit()) {
- if (inputTransaction.mSettingsValues.mIsInternal) {
- LatinImeLoggerUtils.onAutoCorrectionCancellation();
- }
revertCommit(inputTransaction);
return;
}
@@ -961,9 +899,6 @@ public final class InputLogic {
// This is triggered on backspace after a key that inputs multiple characters,
// like the smiley key or the .com key.
mConnection.deleteSurroundingText(mEnteredText.length(), 0);
- if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- ResearchLogger.latinIME_handleBackspace_cancelTextInput(mEnteredText);
- }
mEnteredText = null;
// If we have mEnteredText, then we know that mHasUncommittedTypedChars == false.
// In addition we know that spaceState is false, and that we should not be
@@ -975,6 +910,9 @@ public final class InputLogic {
if (mConnection.revertDoubleSpacePeriod()) {
// No need to reset mSpaceState, it has already be done (that's why we
// receive it as a parameter)
+ inputTransaction.setRequiresUpdateSuggestions();
+ mWordComposer.setCapitalizedModeAtStartComposingTime(
+ WordComposer.CAPS_MODE_OFF);
return;
}
} else if (SpaceState.SWAP_PUNCTUATION == inputTransaction.mSpaceState) {
@@ -993,10 +931,6 @@ public final class InputLogic {
mConnection.setSelection(mConnection.getExpectedSelectionEnd(),
mConnection.getExpectedSelectionEnd());
mConnection.deleteSurroundingText(numCharsDeleted, 0);
- if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- ResearchLogger.latinIME_handleBackspace(numCharsDeleted,
- false /* shouldUncommitLogUnit */);
- }
} else {
// There is no selection, just delete one character.
if (Constants.NOT_A_CURSOR_POSITION == mConnection.getExpectedSelectionEnd()) {
@@ -1031,10 +965,6 @@ public final class InputLogic {
final int lengthToDelete =
Character.isSupplementaryCodePoint(codePointBeforeCursor) ? 2 : 1;
mConnection.deleteSurroundingText(lengthToDelete, 0);
- if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- ResearchLogger.latinIME_handleBackspace(lengthToDelete,
- true /* shouldUncommitLogUnit */);
- }
if (mDeleteCount > Constants.DELETE_ACCELERATE_AT) {
final int codePointBeforeCursorToDeleteAgain =
mConnection.getCodePointBeforeCursor();
@@ -1042,21 +972,18 @@ public final class InputLogic {
final int lengthToDeleteAgain = Character.isSupplementaryCodePoint(
codePointBeforeCursorToDeleteAgain) ? 2 : 1;
mConnection.deleteSurroundingText(lengthToDeleteAgain, 0);
- if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- ResearchLogger.latinIME_handleBackspace(lengthToDeleteAgain,
- true /* shouldUncommitLogUnit */);
- }
}
}
}
}
- if (inputTransaction.mSettingsValues.isSuggestionStripVisible()
+ if (inputTransaction.mSettingsValues
+ .isCurrentOrientationAllowingSuggestionsPerUserSettings()
&& inputTransaction.mSettingsValues.mSpacingAndPunctuations
.mCurrentLanguageHasSpaces
&& !mConnection.isCursorFollowedByWordCharacter(
inputTransaction.mSettingsValues.mSpacingAndPunctuations)) {
restartSuggestionsOnWordTouchedByCursor(inputTransaction.mSettingsValues,
- true /* includeResumedWordInSuggestions */);
+ true /* shouldIncludeResumedWordInSuggestions */);
}
}
}
@@ -1082,9 +1009,6 @@ public final class InputLogic {
mConnection.deleteSurroundingText(2, 0);
final String text = lastTwo.charAt(1) + " ";
mConnection.commitText(text, 1);
- if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- ResearchLogger.latinIME_swapSwapperAndSpace(lastTwo, text);
- }
inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW);
}
}
@@ -1168,11 +1092,6 @@ public final class InputLogic {
final String textToInsert = inputTransaction.mSettingsValues.mSpacingAndPunctuations
.mSentenceSeparatorAndSpace;
mConnection.commitText(textToInsert, 1);
- if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- ResearchLogger.latinIME_maybeDoubleSpacePeriod(textToInsert,
- false /* isBatchMode */);
- }
- mWordComposer.discardPreviousWordForSuggestion();
return true;
}
return false;
@@ -1209,18 +1128,24 @@ public final class InputLogic {
* @param settingsValues The current settings values.
*/
private void performRecapitalization(final SettingsValues settingsValues) {
- if (!mConnection.hasSelection()) {
- return; // No selection
+ if (!mConnection.hasSelection() || !mRecapitalizeStatus.mIsEnabled()) {
+ return; // No selection or recapitalize is disabled for now
+ }
+ final int selectionStart = mConnection.getExpectedSelectionStart();
+ final int selectionEnd = mConnection.getExpectedSelectionEnd();
+ final int numCharsSelected = selectionEnd - selectionStart;
+ if (numCharsSelected > Constants.MAX_CHARACTERS_FOR_RECAPITALIZATION) {
+ // We bail out if we have too many characters for performance reasons. We don't want
+ // to suck possibly multiple-megabyte data.
+ return;
}
- // If we have a recapitalize in progress, use it; otherwise, create a new one.
- if (!mRecapitalizeStatus.isActive()
- || !mRecapitalizeStatus.isSetAt(mConnection.getExpectedSelectionStart(),
- mConnection.getExpectedSelectionEnd())) {
+ // If we have a recapitalize in progress, use it; otherwise, start a new one.
+ if (!mRecapitalizeStatus.isStarted()
+ || !mRecapitalizeStatus.isSetAt(selectionStart, selectionEnd)) {
final CharSequence selectedText =
mConnection.getSelectedText(0 /* flags, 0 for no styles */);
if (TextUtils.isEmpty(selectedText)) return; // Race condition with the input connection
- mRecapitalizeStatus.initialize(mConnection.getExpectedSelectionStart(),
- mConnection.getExpectedSelectionEnd(), selectedText.toString(),
+ mRecapitalizeStatus.start(selectionStart, selectionEnd, selectedText.toString(),
settingsValues.mLocale,
settingsValues.mSpacingAndPunctuations.mSortedWordSeparators);
// We trim leading and trailing whitespace.
@@ -1228,11 +1153,8 @@ public final class InputLogic {
}
mConnection.finishComposingText();
mRecapitalizeStatus.rotate();
- final int numCharsDeleted = mConnection.getExpectedSelectionEnd()
- - mConnection.getExpectedSelectionStart();
- mConnection.setSelection(mConnection.getExpectedSelectionEnd(),
- mConnection.getExpectedSelectionEnd());
- mConnection.deleteSurroundingText(numCharsDeleted, 0);
+ mConnection.setSelection(selectionEnd, selectionEnd);
+ mConnection.deleteSurroundingText(numCharsSelected, 0);
mConnection.commitText(mRecapitalizeStatus.getRecapitalizedString(), 0);
mConnection.setSelection(mRecapitalizeStatus.getNewCursorStart(),
mRecapitalizeStatus.getNewCursorEnd());
@@ -1243,14 +1165,14 @@ public final class InputLogic {
// 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 (!settingsValues.mCorrectionEnabled) return;
+ if (!settingsValues.mAutoCorrectionEnabled) return;
if (TextUtils.isEmpty(suggestion)) return;
final boolean wasAutoCapitalized =
mWordComposer.wasAutoCapitalized() && !mWordComposer.isMostlyCaps();
final int timeStampInSeconds = (int)TimeUnit.MILLISECONDS.toSeconds(
System.currentTimeMillis());
- mSuggest.mDictionaryFacilitator.addToUserHistory(suggestion, wasAutoCapitalized,
+ mDictionaryFacilitator.addToUserHistory(suggestion, wasAutoCapitalized,
prevWordsInfo, timeStampInSeconds, settingsValues.mBlockPotentiallyOffensive);
}
@@ -1269,7 +1191,7 @@ public final class InputLogic {
return;
}
- final AsyncResultHolder<SuggestedWords> holder = new AsyncResultHolder<SuggestedWords>();
+ final AsyncResultHolder<SuggestedWords> holder = new AsyncResultHolder<>();
mInputLogicHandler.getSuggestedWords(Suggest.SESSION_TYPING,
SuggestedWords.NOT_A_SEQUENCE_NUMBER, new OnGetSuggestedWordsCallback() {
@Override
@@ -1300,12 +1222,12 @@ public final class InputLogic {
* do nothing.
*
* @param settingsValues the current values of the settings.
- * @param includeResumedWordInSuggestions whether to include the word on which we resume
+ * @param shouldIncludeResumedWordInSuggestions whether to include the word on which we resume
* suggestions in the suggestion list.
*/
// TODO: make this private.
public void restartSuggestionsOnWordTouchedByCursor(final SettingsValues settingsValues,
- final boolean includeResumedWordInSuggestions) {
+ final boolean shouldIncludeResumedWordInSuggestions) {
// HACK: We may want to special-case some apps that exhibit bad behavior in case of
// recorrection. This is a temporary, stopgap measure that will be removed later.
// TODO: remove this.
@@ -1329,10 +1251,7 @@ public final class InputLogic {
final int expectedCursorPosition = mConnection.getExpectedSelectionStart();
if (!mConnection.isCursorTouchingWord(settingsValues.mSpacingAndPunctuations)) {
// Show predictions.
- mWordComposer.setCapitalizedModeAndPreviousWordAtStartComposingTime(
- WordComposer.CAPS_MODE_OFF,
- getPrevWordsInfoFromNthPreviousWordForSuggestion(
- settingsValues.mSpacingAndPunctuations, 1));
+ mWordComposer.setCapitalizedModeAtStartComposingTime(WordComposer.CAPS_MODE_OFF);
mLatinIME.mHandler.postUpdateSuggestionStrip();
return;
}
@@ -1349,9 +1268,9 @@ public final class InputLogic {
// we just do not resume because it's safer.
final int numberOfCharsInWordBeforeCursor = range.getNumberOfCharsInWordBeforeCursor();
if (numberOfCharsInWordBeforeCursor > expectedCursorPosition) return;
- final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList();
+ final ArrayList<SuggestedWordInfo> suggestions = new ArrayList<>();
final String typedWord = range.mWord.toString();
- if (includeResumedWordInSuggestions) {
+ if (shouldIncludeResumedWordInSuggestions) {
suggestions.add(new SuggestedWordInfo(typedWord,
SuggestedWords.MAX_SUGGESTIONS + 1,
SuggestedWordInfo.KIND_TYPED, Dictionary.DICTIONARY_USER_TYPED,
@@ -1384,14 +1303,15 @@ public final class InputLogic {
settingsValues.mSpacingAndPunctuations,
0 == numberOfCharsInWordBeforeCursor ? 1 : 2);
mWordComposer.setComposingWord(codePoints,
- mLatinIME.getCoordinatesForCurrentKeyboard(codePoints), prevWordsInfo);
+ mLatinIME.getCoordinatesForCurrentKeyboard(codePoints));
mWordComposer.setCursorPositionWithinWord(
typedWord.codePointCount(0, numberOfCharsInWordBeforeCursor));
mConnection.setComposingRegion(expectedCursorPosition - numberOfCharsInWordBeforeCursor,
expectedCursorPosition + range.getNumberOfCharsInWordAfterCursor());
- if (suggestions.isEmpty()) {
- // We come here if there weren't any suggestion spans on this word. We will try to
- // compute suggestions for it instead.
+ if (suggestions.size() <= (shouldIncludeResumedWordInSuggestions ? 1 : 0)) {
+ // If there weren't any suggestion spans on this word, suggestions#size() will be 1
+ // if shouldIncludeResumedWordInSuggestions is true, 0 otherwise. In this case, we
+ // have no useful suggestions, so we will try to compute some for it instead.
mInputLogicHandler.getSuggestedWords(Suggest.SESSION_TYPING,
SuggestedWords.NOT_A_SEQUENCE_NUMBER, new OnGetSuggestedWordsCallback() {
@Override
@@ -1399,7 +1319,7 @@ public final class InputLogic {
final SuggestedWords suggestedWordsIncludingTypedWord) {
final SuggestedWords suggestedWords;
if (suggestedWordsIncludingTypedWord.size() > 1
- && !includeResumedWordInSuggestions) {
+ && !shouldIncludeResumedWordInSuggestions) {
// We were able to compute new suggestions for this word.
// Remove the typed word, since we don't want to display it in this
// case. The #getSuggestedWordsExcludingTypedWord() method sets
@@ -1462,8 +1382,7 @@ public final class InputLogic {
}
mConnection.deleteSurroundingText(deleteLength, 0);
if (!TextUtils.isEmpty(prevWordsInfo.mPrevWord) && !TextUtils.isEmpty(committedWord)) {
- mSuggest.mDictionaryFacilitator.cancelAddingUserHistory(
- prevWordsInfo, committedWordString);
+ mDictionaryFacilitator.cancelAddingUserHistory(prevWordsInfo, committedWordString);
}
final String stringToCommit = originallyTypedWord + mLastComposedWord.mSeparatorString;
final SpannableString textToCommit = new SpannableString(stringToCommit);
@@ -1473,7 +1392,7 @@ public final class InputLogic {
committedWord.length(), Object.class);
final int lastCharIndex = textToCommit.length() - 1;
// We will collect all suggestions in the following array.
- final ArrayList<String> suggestions = CollectionUtils.newArrayList();
+ final ArrayList<String> suggestions = new ArrayList<>();
// First, add the committed word to the list of suggestions.
suggestions.add(committedWordString);
for (final Object span : spans) {
@@ -1512,20 +1431,10 @@ public final class InputLogic {
// with the typed word, so we need to resume suggestions right away.
final int[] codePoints = StringUtils.toCodePointArray(stringToCommit);
mWordComposer.setComposingWord(codePoints,
- mLatinIME.getCoordinatesForCurrentKeyboard(codePoints), prevWordsInfo);
+ mLatinIME.getCoordinatesForCurrentKeyboard(codePoints));
mConnection.setComposingText(textToCommit, 1);
}
- if (inputTransaction.mSettingsValues.mIsInternal) {
- LatinImeLoggerUtils.onSeparator(mLastComposedWord.mSeparatorString,
- Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE);
- }
- if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- ResearchLogger.latinIME_revertCommit(committedWord.toString(),
- originallyTypedWord.toString(),
- mWordComposer.isBatchMode(), mLastComposedWord.mSeparatorString);
- }
- // Don't restart suggestion yet. We'll restart if the user deletes the
- // separator.
+ // Don't restart suggestion yet. We'll restart if the user deletes the separator.
mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
// We have a separator between the word and the cursor: we should show predictions.
inputTransaction.setRequiresUpdateSuggestions();
@@ -1577,7 +1486,7 @@ public final class InputLogic {
}
public int getCurrentRecapitalizeState() {
- if (!mRecapitalizeStatus.isActive()
+ if (!mRecapitalizeStatus.isStarted()
|| !mRecapitalizeStatus.isSetAt(mConnection.getExpectedSelectionStart(),
mConnection.getExpectedSelectionEnd())) {
// Not recapitalizing at the moment
@@ -1609,8 +1518,9 @@ public final class InputLogic {
return mConnection.getPrevWordsInfoFromNthPreviousWord(
spacingAndPunctuations, nthPreviousWord);
} else {
- return LastComposedWord.NOT_A_COMPOSED_WORD == mLastComposedWord ? new PrevWordsInfo()
- : new PrevWordsInfo(mLastComposedWord.mCommittedWord.toString());
+ return LastComposedWord.NOT_A_COMPOSED_WORD == mLastComposedWord ?
+ PrevWordsInfo.BEGINNING_OF_SENTENCE :
+ new PrevWordsInfo(mLastComposedWord.mCommittedWord.toString());
}
}
@@ -1788,9 +1698,6 @@ public final class InputLogic {
*/
// TODO: replace these two parameters with an InputTransaction
private void sendKeyCodePoint(final SettingsValues settingsValues, final int codePoint) {
- if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- ResearchLogger.latinIME_sendKeyCodePoint(codePoint);
- }
// TODO: Remove this special handling of digit letters.
// For backward compatibility. See {@link InputMethodService#sendKeyChar(char)}.
if (codePoint >= '0' && codePoint <= '9') {
@@ -1822,9 +1729,6 @@ public final class InputLogic {
if (settingsValues.shouldInsertSpacesAutomatically()
&& settingsValues.mSpacingAndPunctuations.mCurrentLanguageHasSpaces
&& !mConnection.textBeforeCursorLooksLikeURL()) {
- if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- ResearchLogger.latinIME_promotePhantomSpace();
- }
sendKeyCodePoint(settingsValues, Constants.CODE_SPACE);
}
}
@@ -1866,9 +1770,6 @@ public final class InputLogic {
mConnection.setComposingText(batchInputText, 1);
}
mConnection.endBatchEdit();
- if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- ResearchLogger.latinIME_onEndBatchInput(batchInputText, 0, suggestedWords);
- }
// Space state must be updated before calling updateShiftState
mSpaceState = SpaceState.PHANTOM;
keyboardSwitcher.requestUpdatingShiftState(getCurrentAutoCapsState(settingsValues),
@@ -1895,9 +1796,6 @@ public final class InputLogic {
if (!mWordComposer.isComposingWord()) return;
final String typedWord = mWordComposer.getTypedWord();
if (typedWord.length() > 0) {
- if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- ResearchLogger.getInstance().onWordFinished(typedWord, mWordComposer.isBatchMode());
- }
commitChosenWord(settingsValues, typedWord,
LastComposedWord.COMMIT_TYPE_USER_TYPED_WORD, separatorString);
}
@@ -1937,15 +1835,6 @@ public final class InputLogic {
throw new RuntimeException("We have an auto-correction but the typed word "
+ "is empty? Impossible! I must commit suicide.");
}
- if (settingsValues.mIsInternal) {
- LatinImeLoggerUtils.onAutoCorrection(
- typedWord, autoCorrection, separator, mWordComposer);
- }
- if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- final SuggestedWords suggestedWords = mSuggestedWords;
- ResearchLogger.latinIme_commitCurrentAutoCorrection(typedWord, autoCorrection,
- separator, mWordComposer.isBatchMode(), suggestedWords);
- }
commitChosenWord(settingsValues, autoCorrection,
LastComposedWord.COMMIT_TYPE_DECIDED_WORD, separator);
if (!typedWord.equals(autoCorrection)) {
@@ -1989,21 +1878,6 @@ public final class InputLogic {
// strings.
mLastComposedWord = mWordComposer.commitWord(commitType,
chosenWordWithSuggestions, separatorString, prevWordsInfo);
- final boolean shouldDiscardPreviousWordForSuggestion;
- if (0 == StringUtils.codePointCount(separatorString)) {
- // Separator is 0-length, we can keep the previous word for suggestion. Either this
- // was a manual pick or the language has no spaces in which case we want to keep the
- // previous word, or it was the keyboard closing or the cursor moving in which case it
- // will be reset anyway.
- shouldDiscardPreviousWordForSuggestion = false;
- } else {
- // Otherwise, we discard if the separator contains any non-whitespace.
- shouldDiscardPreviousWordForSuggestion =
- !StringUtils.containsOnlyWhitespace(separatorString);
- }
- if (shouldDiscardPreviousWordForSuggestion) {
- mWordComposer.discardPreviousWordForSuggestion();
- }
}
/**
@@ -2023,9 +1897,11 @@ public final class InputLogic {
final boolean tryResumeSuggestions, final int remainingTries,
// TODO: remove these arguments
final LatinIME.UIHandler handler) {
+ final boolean shouldFinishComposition = mConnection.hasSelection()
+ || !mConnection.isCursorPositionKnown();
if (!mConnection.resetCachesUponCursorMoveAndReturnSuccess(
mConnection.getExpectedSelectionStart(), mConnection.getExpectedSelectionEnd(),
- false /* shouldFinishComposition */)) {
+ shouldFinishComposition)) {
if (0 < remainingTries) {
handler.postResetCaches(tryResumeSuggestions, remainingTries - 1);
return false;
@@ -2035,7 +1911,9 @@ public final class InputLogic {
}
mConnection.tryFixLyingCursorPosition();
if (tryResumeSuggestions) {
- handler.postResumeSuggestions();
+ // This is triggered when starting input anew, so we want to include the resumed
+ // word in suggestions.
+ handler.postResumeSuggestions(true /* shouldIncludeResumedWordInSuggestions */);
}
return true;
}
diff --git a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
index f5f072b7a..a2ae74b20 100644
--- a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
+++ b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
@@ -192,8 +192,9 @@ public final class FormatSpec {
public static final int VERSION2 = 2;
// Dictionary version used for testing.
public static final int VERSION4_ONLY_FOR_TESTING = 399;
- public static final int VERSION4 = 401;
- public static final int VERSION4_DEV = 402;
+ public static final int VERSION401 = 401;
+ public static final int VERSION4 = 402;
+ public static final int VERSION4_DEV = 403;
static final int MINIMUM_SUPPORTED_VERSION = VERSION2;
static final int MAXIMUM_SUPPORTED_VERSION = VERSION4_DEV;
diff --git a/java/src/com/android/inputmethod/latin/makedict/WordProperty.java b/java/src/com/android/inputmethod/latin/makedict/WordProperty.java
index 853392200..31cb59756 100644
--- a/java/src/com/android/inputmethod/latin/makedict/WordProperty.java
+++ b/java/src/com/android/inputmethod/latin/makedict/WordProperty.java
@@ -18,7 +18,6 @@ package com.android.inputmethod.latin.makedict;
import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.latin.BinaryDictionary;
-import com.android.inputmethod.latin.utils.CollectionUtils;
import com.android.inputmethod.latin.utils.CombinedFormatUtils;
import com.android.inputmethod.latin.utils.StringUtils;
@@ -35,6 +34,8 @@ public final class WordProperty implements Comparable<WordProperty> {
public final ProbabilityInfo mProbabilityInfo;
public final ArrayList<WeightedString> mShortcutTargets;
public final ArrayList<WeightedString> mBigrams;
+ // TODO: Support mIsBeginningOfSentence.
+ public final boolean mIsBeginningOfSentence;
public final boolean mIsNotAWord;
public final boolean mIsBlacklistEntry;
public final boolean mHasShortcuts;
@@ -51,6 +52,7 @@ public final class WordProperty implements Comparable<WordProperty> {
mProbabilityInfo = probabilityInfo;
mShortcutTargets = shortcutTargets;
mBigrams = bigrams;
+ mIsBeginningOfSentence = false;
mIsNotAWord = isNotAWord;
mIsBlacklistEntry = isBlacklistEntry;
mHasBigrams = bigrams != null && !bigrams.isEmpty();
@@ -75,8 +77,9 @@ public final class WordProperty implements Comparable<WordProperty> {
final ArrayList<Integer> shortcutProbabilities) {
mWord = StringUtils.getStringFromNullTerminatedCodePointArray(codePoints);
mProbabilityInfo = createProbabilityInfoFromArray(probabilityInfo);
- mShortcutTargets = CollectionUtils.newArrayList();
- mBigrams = CollectionUtils.newArrayList();
+ mShortcutTargets = new ArrayList<>();
+ mBigrams = new ArrayList<>();
+ mIsBeginningOfSentence = false;
mIsNotAWord = isNotAWord;
mIsBlacklistEntry = isBlacklisted;
mHasShortcuts = hasShortcuts;
diff --git a/java/src/com/android/inputmethod/latin/personalization/AccountUtils.java b/java/src/com/android/inputmethod/latin/personalization/AccountUtils.java
index a446672cb..ab3ef964e 100644
--- a/java/src/com/android/inputmethod/latin/personalization/AccountUtils.java
+++ b/java/src/com/android/inputmethod/latin/personalization/AccountUtils.java
@@ -35,7 +35,7 @@ public class AccountUtils {
}
public static List<String> getDeviceAccountsEmailAddresses(final Context context) {
- final ArrayList<String> retval = new ArrayList<String>();
+ final ArrayList<String> retval = new ArrayList<>();
for (final Account account : getAccounts(context)) {
final String name = account.name;
if (Patterns.EMAIL_ADDRESS.matcher(name).matches()) {
@@ -54,7 +54,7 @@ public class AccountUtils {
*/
public static List<String> getDeviceAccountsWithDomain(
final Context context, final String domain) {
- final ArrayList<String> retval = new ArrayList<String>();
+ final ArrayList<String> retval = new ArrayList<>();
final String atDomain = "@" + domain.toLowerCase(Locale.ROOT);
for (final Account account : getAccounts(context)) {
if (account.name.toLowerCase(Locale.ROOT).endsWith(atDomain)) {
diff --git a/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java b/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java
index 06bdba054..1ba7b366f 100644
--- a/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java
+++ b/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java
@@ -31,7 +31,6 @@ import java.util.Map;
* model.
*/
public abstract class DecayingExpandableBinaryDictionaryBase extends ExpandableBinaryDictionary {
- private static final String TAG = DecayingExpandableBinaryDictionaryBase.class.getSimpleName();
private static final boolean DBG_DUMP_ON_CLOSE = false;
/** Any pair being typed or picked */
@@ -81,4 +80,10 @@ public abstract class DecayingExpandableBinaryDictionaryBase extends ExpandableB
/* package */ void runGCIfRequired() {
runGCIfRequired(false /* mindsBlockByGC */);
}
+
+ @Override
+ public boolean isValidWord(final String word) {
+ // Strings out of this dictionary should not be considered existing words.
+ return false;
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDataChunk.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDataChunk.java
new file mode 100644
index 000000000..9d72de8c5
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDataChunk.java
@@ -0,0 +1,37 @@
+/*
+ * 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.personalization;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+
+public class PersonalizationDataChunk {
+ public final boolean mInputByUser;
+ public final List<String> mTokens;
+ public final int mTimestampInSeconds;
+ public final String mPackageName;
+ public final Locale mlocale = null;
+
+ public PersonalizationDataChunk(boolean inputByUser, final List<String> tokens,
+ final int timestampInSeconds, final String packageName) {
+ mInputByUser = inputByUser;
+ mTokens = Collections.unmodifiableList(tokens);
+ mTimestampInSeconds = timestampInSeconds;
+ mPackageName = packageName;
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java
index 1423fceff..19fa29e5f 100644
--- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java
+++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionary.java
@@ -38,10 +38,4 @@ public class PersonalizationDictionary extends DecayingExpandableBinaryDictionar
final Locale locale, final File dictFile) {
return PersonalizationHelper.getPersonalizationDictionary(context, locale);
}
-
- @Override
- public boolean isValidWord(final String word) {
- // Strings out of this dictionary should not be considered existing words.
- return false;
- }
}
diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionarySessionRegistrar.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionarySessionRegistrar.java
index 9bef7a198..450644032 100644
--- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionarySessionRegistrar.java
+++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationDictionarySessionRegistrar.java
@@ -19,18 +19,15 @@ package com.android.inputmethod.latin.personalization;
import android.content.Context;
import android.content.res.Configuration;
-import com.android.inputmethod.latin.DictionaryFacilitatorForSuggest;
-import com.android.inputmethod.latin.utils.DistracterFilter;
+import com.android.inputmethod.latin.DictionaryFacilitator;
public class PersonalizationDictionarySessionRegistrar {
public static void init(final Context context,
- final DictionaryFacilitatorForSuggest dictionaryFacilitator,
- final DistracterFilter distracterFilter) {
+ final DictionaryFacilitator dictionaryFacilitator) {
}
public static void onConfigurationChanged(final Context context, final Configuration conf,
- final DictionaryFacilitatorForSuggest dictionaryFacilitator,
- final DistracterFilter distracterFilter) {
+ final DictionaryFacilitator dictionaryFacilitator) {
}
public static void onUpdateData(final Context context, final String type) {
diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java
index 6ef505e76..aac40940b 100644
--- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java
+++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java
@@ -16,12 +16,11 @@
package com.android.inputmethod.latin.personalization;
-import com.android.inputmethod.latin.utils.CollectionUtils;
-import com.android.inputmethod.latin.utils.FileUtils;
-
import android.content.Context;
import android.util.Log;
+import com.android.inputmethod.latin.utils.FileUtils;
+
import java.io.File;
import java.io.FilenameFilter;
import java.lang.ref.SoftReference;
@@ -33,9 +32,9 @@ public class PersonalizationHelper {
private static final String TAG = PersonalizationHelper.class.getSimpleName();
private static final boolean DEBUG = false;
private static final ConcurrentHashMap<String, SoftReference<UserHistoryDictionary>>
- sLangUserHistoryDictCache = CollectionUtils.newConcurrentHashMap();
+ sLangUserHistoryDictCache = new ConcurrentHashMap<>();
private static final ConcurrentHashMap<String, SoftReference<PersonalizationDictionary>>
- sLangPersonalizationDictCache = CollectionUtils.newConcurrentHashMap();
+ sLangPersonalizationDictCache = new ConcurrentHashMap<>();
public static UserHistoryDictionary getUserHistoryDictionary(
final Context context, final Locale locale) {
@@ -54,8 +53,7 @@ public class PersonalizationHelper {
}
}
final UserHistoryDictionary dict = new UserHistoryDictionary(context, locale);
- sLangUserHistoryDictCache.put(localeStr,
- new SoftReference<UserHistoryDictionary>(dict));
+ sLangUserHistoryDictCache.put(localeStr, new SoftReference<>(dict));
return dict;
}
}
@@ -108,8 +106,7 @@ public class PersonalizationHelper {
}
}
final PersonalizationDictionary dict = new PersonalizationDictionary(context, locale);
- sLangPersonalizationDictCache.put(
- localeStr, new SoftReference<PersonalizationDictionary>(dict));
+ sLangPersonalizationDictCache.put(localeStr, new SoftReference<>(dict));
return dict;
}
}
diff --git a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java
index f89caf921..ea1035612 100644
--- a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java
@@ -23,6 +23,7 @@ import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.Dictionary;
import com.android.inputmethod.latin.ExpandableBinaryDictionary;
import com.android.inputmethod.latin.PrevWordsInfo;
+import com.android.inputmethod.latin.utils.DistracterFilter;
import java.io.File;
import java.util.Locale;
@@ -46,12 +47,6 @@ public class UserHistoryDictionary extends DecayingExpandableBinaryDictionaryBas
return PersonalizationHelper.getUserHistoryDictionary(context, locale);
}
- @Override
- public boolean isValidWord(final String word) {
- // Strings out of this dictionary should not be considered existing words.
- return false;
- }
-
/**
* Add a word to the user history dictionary.
*
@@ -60,10 +55,11 @@ public class UserHistoryDictionary extends DecayingExpandableBinaryDictionaryBas
* @param word the word the user inputted
* @param isValid whether the word is valid or not
* @param timestamp the timestamp when the word has been inputted
+ * @param distracterFilter the filter to check whether the word is a distracter
*/
public static void addToDictionary(final ExpandableBinaryDictionary userHistoryDictionary,
final PrevWordsInfo prevWordsInfo, final String word, final boolean isValid,
- final int timestamp) {
+ final int timestamp, final DistracterFilter distracterFilter) {
final String prevWord = prevWordsInfo.mPrevWord;
if (word.length() >= Constants.DICTIONARY_MAX_WORD_LENGTH ||
(prevWord != null && prevWord.length() >= Constants.DICTIONARY_MAX_WORD_LENGTH)) {
@@ -71,8 +67,9 @@ public class UserHistoryDictionary extends DecayingExpandableBinaryDictionaryBas
}
final int frequency = isValid ?
FREQUENCY_FOR_WORDS_IN_DICTS : FREQUENCY_FOR_WORDS_NOT_IN_DICTS;
- userHistoryDictionary.addUnigramEntry(word, frequency, null /* shortcutTarget */,
- 0 /* shortcutFreq */, false /* isNotAWord */, false /* isBlacklisted */, timestamp);
+ userHistoryDictionary.addUnigramEntryWithCheckingDistracter(word, frequency,
+ null /* shortcutTarget */, 0 /* shortcutFreq */, false /* isNotAWord */,
+ false /* isBlacklisted */, timestamp, distracterFilter);
// Do not insert a word as a bigram of itself
if (word.equals(prevWord)) {
return;
diff --git a/java/src/com/android/inputmethod/latin/settings/AdditionalSubtypeSettings.java b/java/src/com/android/inputmethod/latin/settings/AdditionalSubtypeSettings.java
index 39977e76f..31fa86774 100644
--- a/java/src/com/android/inputmethod/latin/settings/AdditionalSubtypeSettings.java
+++ b/java/src/com/android/inputmethod/latin/settings/AdditionalSubtypeSettings.java
@@ -47,7 +47,6 @@ import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.RichInputMethodManager;
import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils;
-import com.android.inputmethod.latin.utils.CollectionUtils;
import com.android.inputmethod.latin.utils.DialogUtils;
import com.android.inputmethod.latin.utils.IntentUtils;
import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
@@ -101,7 +100,7 @@ public final class AdditionalSubtypeSettings extends PreferenceFragment {
super(context, android.R.layout.simple_spinner_item);
setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
- final TreeSet<SubtypeLocaleItem> items = CollectionUtils.newTreeSet();
+ final TreeSet<SubtypeLocaleItem> items = new TreeSet<>();
final InputMethodInfo imi = RichInputMethodManager.getInstance()
.getInputMethodInfoOfThisIme();
final int count = imi.getSubtypeCount();
@@ -369,7 +368,6 @@ public final class AdditionalSubtypeSettings extends PreferenceFragment {
mSubtype = (InputMethodSubtype)source.readParcelable(null);
}
- @SuppressWarnings("hiding")
public static final Parcelable.Creator<SavedState> CREATOR =
new Parcelable.Creator<SavedState>() {
@Override
@@ -516,8 +514,7 @@ public final class AdditionalSubtypeSettings extends PreferenceFragment {
localeString, keyboardLayoutSetName);
}
- private AlertDialog createDialog(
- @SuppressWarnings("unused") final SubtypePreference subtypePref) {
+ private AlertDialog createDialog(final SubtypePreference subtypePref) {
final AlertDialog.Builder builder = new AlertDialog.Builder(
DialogUtils.getPlatformDialogThemeContext(getActivity()));
builder.setTitle(R.string.custom_input_styles_title)
@@ -555,7 +552,7 @@ public final class AdditionalSubtypeSettings extends PreferenceFragment {
private InputMethodSubtype[] getSubtypes() {
final PreferenceGroup group = getPreferenceScreen();
- final ArrayList<InputMethodSubtype> subtypes = CollectionUtils.newArrayList();
+ final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>();
final int count = group.getPreferenceCount();
for (int i = 0; i < count; i++) {
final Preference pref = group.getPreference(i);
diff --git a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java
index c4c1234fc..845ddb377 100644
--- a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java
+++ b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java
@@ -25,11 +25,12 @@ import android.preference.CheckBoxPreference;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceFragment;
+import android.preference.PreferenceGroup;
import android.preference.PreferenceScreen;
import com.android.inputmethod.latin.Dictionary;
import com.android.inputmethod.latin.DictionaryDumpBroadcastReceiver;
-import com.android.inputmethod.latin.LatinImeLogger;
+import com.android.inputmethod.latin.DictionaryFacilitator;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.debug.ExternalDictionaryGetterForDebug;
import com.android.inputmethod.latin.utils.ApplicationUtils;
@@ -40,8 +41,6 @@ public final class DebugSettings extends PreferenceFragment
public static final String PREF_DEBUG_MODE = "debug_mode";
public static final String PREF_FORCE_NON_DISTINCT_MULTITOUCH = "force_non_distinct_multitouch";
- public static final String PREF_USABILITY_STUDY_MODE = "usability_study_mode";
- public static final String PREF_STATISTICS_LOGGING = "enable_logging";
public static final String PREF_KEY_PREVIEW_SHOW_UP_START_SCALE =
"pref_key_preview_show_up_start_scale";
public static final String PREF_KEY_PREVIEW_DISMISS_END_SCALE =
@@ -51,18 +50,14 @@ public final class DebugSettings extends PreferenceFragment
public static final String PREF_KEY_PREVIEW_DISMISS_DURATION =
"pref_key_preview_dismiss_duration";
private static final String PREF_READ_EXTERNAL_DICTIONARY = "read_external_dictionary";
- private static final String PREF_DUMP_CONTACTS_DICT = "dump_contacts_dict";
- private static final String PREF_DUMP_USER_DICT = "dump_user_dict";
- private static final String PREF_DUMP_USER_HISTORY_DICT = "dump_user_history_dict";
- private static final String PREF_DUMP_PERSONALIZATION_DICT = "dump_personalization_dict";
+ private static final String PREF_KEY_DUMP_DICTS = "pref_key_dump_dictionaries";
+ private static final String PREF_KEY_DUMP_DICT_PREFIX = "pref_key_dump_dictionaries";
+ private static final String DICT_NAME_KEY_FOR_EXTRAS = "dict_name";
public static final String PREF_SLIDING_KEY_INPUT_PREVIEW = "pref_sliding_key_input_preview";
public static final String PREF_KEY_LONGPRESS_TIMEOUT = "pref_key_longpress_timeout";
- private static final boolean SHOW_STATISTICS_LOGGING = false;
-
private boolean mServiceNeedsRestart = false;
private CheckBoxPreference mDebugMode;
- private CheckBoxPreference mStatisticsLoggingPref;
@Override
public void onCreate(Bundle icicle) {
@@ -71,21 +66,6 @@ public final class DebugSettings extends PreferenceFragment
SharedPreferences prefs = getPreferenceManager().getSharedPreferences();
prefs.registerOnSharedPreferenceChangeListener(this);
- final Preference usabilityStudyPref = findPreference(PREF_USABILITY_STUDY_MODE);
- if (usabilityStudyPref instanceof CheckBoxPreference) {
- final CheckBoxPreference checkbox = (CheckBoxPreference)usabilityStudyPref;
- checkbox.setChecked(prefs.getBoolean(PREF_USABILITY_STUDY_MODE,
- LatinImeLogger.getUsabilityStudyMode(prefs)));
- checkbox.setSummary(R.string.settings_warning_researcher_mode);
- }
- final Preference statisticsLoggingPref = findPreference(PREF_STATISTICS_LOGGING);
- if (statisticsLoggingPref instanceof CheckBoxPreference) {
- mStatisticsLoggingPref = (CheckBoxPreference) statisticsLoggingPref;
- if (!SHOW_STATISTICS_LOGGING) {
- getPreferenceScreen().removePreference(statisticsLoggingPref);
- }
- }
-
final PreferenceScreen readExternalDictionary =
(PreferenceScreen) findPreference(PREF_READ_EXTERNAL_DICTIONARY);
if (null != readExternalDictionary) {
@@ -101,16 +81,18 @@ public final class DebugSettings extends PreferenceFragment
});
}
+ final PreferenceGroup dictDumpPreferenceGroup =
+ (PreferenceGroup)findPreference(PREF_KEY_DUMP_DICTS);
final OnPreferenceClickListener dictDumpPrefClickListener =
new DictDumpPrefClickListener(this);
- findPreference(PREF_DUMP_CONTACTS_DICT).setOnPreferenceClickListener(
- dictDumpPrefClickListener);
- findPreference(PREF_DUMP_USER_DICT).setOnPreferenceClickListener(
- dictDumpPrefClickListener);
- findPreference(PREF_DUMP_USER_HISTORY_DICT).setOnPreferenceClickListener(
- dictDumpPrefClickListener);
- findPreference(PREF_DUMP_PERSONALIZATION_DICT).setOnPreferenceClickListener(
- dictDumpPrefClickListener);
+ for (final String dictName : DictionaryFacilitator.DICT_TYPE_TO_CLASS.keySet()) {
+ final Preference preference = new Preference(getActivity());
+ preference.setKey(PREF_KEY_DUMP_DICT_PREFIX + dictName);
+ preference.setTitle("Dump " + dictName + " dictionary");
+ preference.setOnPreferenceClickListener(dictDumpPrefClickListener);
+ preference.getExtras().putString(DICT_NAME_KEY_FOR_EXTRAS, dictName);
+ dictDumpPreferenceGroup.addPreference(preference);
+ }
final Resources res = getResources();
setupKeyLongpressTimeoutSettings(prefs, res);
setupKeyPreviewAnimationDuration(prefs, res, PREF_KEY_PREVIEW_SHOW_UP_DURATION,
@@ -138,18 +120,7 @@ public final class DebugSettings extends PreferenceFragment
@Override
public boolean onPreferenceClick(final Preference arg0) {
- final String dictName;
- if (arg0.getKey().equals(PREF_DUMP_CONTACTS_DICT)) {
- dictName = Dictionary.TYPE_CONTACTS;
- } else if (arg0.getKey().equals(PREF_DUMP_USER_DICT)) {
- dictName = Dictionary.TYPE_USER;
- } else if (arg0.getKey().equals(PREF_DUMP_USER_HISTORY_DICT)) {
- dictName = Dictionary.TYPE_USER_HISTORY;
- } else if (arg0.getKey().equals(PREF_DUMP_PERSONALIZATION_DICT)) {
- dictName = Dictionary.TYPE_PERSONALIZATION;
- } else {
- dictName = null;
- }
+ final String dictName = arg0.getExtras().getString(DICT_NAME_KEY_FOR_EXTRAS);
if (dictName != null) {
final Intent intent =
new Intent(DictionaryDumpBroadcastReceiver.DICTIONARY_DUMP_INTENT_ACTION);
@@ -163,27 +134,22 @@ public final class DebugSettings extends PreferenceFragment
@Override
public void onStop() {
super.onStop();
- if (mServiceNeedsRestart) Process.killProcess(Process.myPid());
+ if (mServiceNeedsRestart) {
+ Process.killProcess(Process.myPid());
+ }
}
@Override
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
- if (key.equals(PREF_DEBUG_MODE)) {
- if (mDebugMode != null) {
- mDebugMode.setChecked(prefs.getBoolean(PREF_DEBUG_MODE, false));
- final boolean checked = mDebugMode.isChecked();
- if (mStatisticsLoggingPref != null) {
- if (checked) {
- getPreferenceScreen().addPreference(mStatisticsLoggingPref);
- } else {
- getPreferenceScreen().removePreference(mStatisticsLoggingPref);
- }
- }
- updateDebugMode();
- mServiceNeedsRestart = true;
- }
- } else if (key.equals(PREF_FORCE_NON_DISTINCT_MULTITOUCH)) {
+ if (key.equals(PREF_DEBUG_MODE) && mDebugMode != null) {
+ mDebugMode.setChecked(prefs.getBoolean(PREF_DEBUG_MODE, false));
+ updateDebugMode();
mServiceNeedsRestart = true;
+ return;
+ }
+ if (key.equals(PREF_FORCE_NON_DISTINCT_MULTITOUCH)) {
+ mServiceNeedsRestart = true;
+ return;
}
}
diff --git a/java/src/com/android/inputmethod/latin/settings/Settings.java b/java/src/com/android/inputmethod/latin/settings/Settings.java
index d4f6bcd10..235847799 100644
--- a/java/src/com/android/inputmethod/latin/settings/Settings.java
+++ b/java/src/com/android/inputmethod/latin/settings/Settings.java
@@ -20,6 +20,7 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.res.Resources;
+import android.os.Build;
import android.preference.PreferenceManager;
import android.util.Log;
@@ -60,6 +61,10 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
"pref_key_use_double_space_period";
public static final String PREF_BLOCK_POTENTIALLY_OFFENSIVE =
"pref_key_block_potentially_offensive";
+ public static final boolean ENABLE_SHOW_LANGUAGE_SWITCH_KEY_SETTINGS =
+ (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT)
+ || (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT
+ && Build.VERSION.CODENAME.equals("REL"));
public static final String PREF_SHOW_LANGUAGE_SWITCH_KEY =
"pref_show_language_switch_key";
public static final String PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST =
@@ -327,10 +332,6 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang
R.array.keypress_vibration_durations, DEFAULT_KEYPRESS_VIBRATION_DURATION));
}
- public static boolean readUsabilityStudyMode(final SharedPreferences prefs) {
- return prefs.getBoolean(DebugSettings.PREF_USABILITY_STUDY_MODE, true);
- }
-
public static float readKeyPreviewAnimationScale(final SharedPreferences prefs,
final String prefKey, final float defaultValue) {
final float fraction = prefs.getFloat(prefKey, UNDEFINED_PREFERENCE_VALUE_FLOAT);
diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java
index e1d38e7c4..5eb0377c7 100644
--- a/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java
+++ b/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java
@@ -59,7 +59,7 @@ public final class SettingsFragment extends InputMethodSettingsFragment
private static final boolean DBG_USE_INTERNAL_PERSONAL_DICTIONARY_SETTINGS = false;
private static final boolean USE_INTERNAL_PERSONAL_DICTIONARY_SETTIGS =
DBG_USE_INTERNAL_PERSONAL_DICTIONARY_SETTINGS
- || Build.VERSION.SDK_INT <= 18 /* Build.VERSION.JELLY_BEAN_MR2 */;
+ || Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN_MR2;
private void setPreferenceEnabled(final String preferenceKey, final boolean enabled) {
final Preference preference = findPreference(preferenceKey);
@@ -152,10 +152,6 @@ public final class SettingsFragment extends InputMethodSettingsFragment
miscSettings.removePreference(aboutSettings);
}
}
- if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- // The about screen contains items that may be confusing in development-only versions.
- miscSettings.removePreference(aboutSettings);
- }
final boolean showVoiceKeyOption = res.getBoolean(
R.bool.config_enable_show_voice_key_option);
@@ -169,6 +165,13 @@ public final class SettingsFragment extends InputMethodSettingsFragment
removePreference(Settings.PREF_VIBRATE_ON, generalSettings);
removePreference(Settings.PREF_VIBRATION_DURATION_SETTINGS, advancedSettings);
}
+ if (!Settings.ENABLE_SHOW_LANGUAGE_SWITCH_KEY_SETTINGS) {
+ removePreference(
+ Settings.PREF_SHOW_LANGUAGE_SWITCH_KEY, advancedSettings);
+ removePreference(
+ Settings.PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST, advancedSettings);
+ }
+
// TODO: consolidate key preview dismiss delay with the key preview animation parameters.
if (!Settings.readFromBuildConfigIfToShowKeyPreviewPopupOption(res)) {
@@ -199,9 +202,6 @@ public final class SettingsFragment extends InputMethodSettingsFragment
removePreference(Settings.PREF_SHOW_SETUP_WIZARD_ICON, advancedSettings);
}
- setPreferenceEnabled(Settings.PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST,
- Settings.readShowsLanguageSwitchKey(prefs));
-
final PreferenceGroup textCorrectionGroup =
(PreferenceGroup) findPreference(Settings.PREF_CORRECTION_SETTINGS);
final PreferenceScreen dictionaryLink =
@@ -213,6 +213,20 @@ public final class SettingsFragment extends InputMethodSettingsFragment
textCorrectionGroup.removePreference(dictionaryLink);
}
+ if (ProductionFlag.IS_METRICS_LOGGING_SUPPORTED) {
+ final Preference enableMetricsLogging =
+ findPreference(Settings.PREF_ENABLE_METRICS_LOGGING);
+ if (enableMetricsLogging != null) {
+ final int applicationLabelRes = context.getApplicationInfo().labelRes;
+ final String applicationName = res.getString(applicationLabelRes);
+ final String enableMetricsLoggingTitle = res.getString(
+ R.string.enable_metrics_logging, applicationName);
+ enableMetricsLogging.setTitle(enableMetricsLoggingTitle);
+ }
+ } else {
+ removePreference(Settings.PREF_ENABLE_METRICS_LOGGING, textCorrectionGroup);
+ }
+
final Preference editPersonalDictionary =
findPreference(Settings.PREF_EDIT_PERSONAL_DICTIONARY);
final Intent editPersonalDictionaryIntent = editPersonalDictionary.getIntent();
@@ -299,9 +313,6 @@ public final class SettingsFragment extends InputMethodSettingsFragment
if (key.equals(Settings.PREF_POPUP_ON)) {
setPreferenceEnabled(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY,
Settings.readKeyPreviewPopupEnabled(prefs, res));
- } else if (key.equals(Settings.PREF_SHOW_LANGUAGE_SWITCH_KEY)) {
- setPreferenceEnabled(Settings.PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST,
- Settings.readShowsLanguageSwitchKey(prefs));
} else if (key.equals(Settings.PREF_SHOW_SETUP_WIZARD_ICON)) {
LauncherIconVisibilityManager.updateSetupWizardIconVisibility(getActivity());
}
diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
index 16fd05877..389d9a869 100644
--- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
@@ -28,6 +28,7 @@ import com.android.inputmethod.compat.AppWorkaroundsUtils;
import com.android.inputmethod.latin.InputAttributes;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.RichInputMethodManager;
+import com.android.inputmethod.latin.SubtypeSwitcher;
import com.android.inputmethod.latin.utils.AsyncResultHolder;
import com.android.inputmethod.latin.utils.ResourceUtils;
import com.android.inputmethod.latin.utils.TargetPackageInfoGetterTask;
@@ -84,7 +85,7 @@ public final class SettingsValues {
public final int mKeyPreviewPopupDismissDelay;
private final boolean mAutoCorrectEnabled;
public final float mAutoCorrectionThreshold;
- public final boolean mCorrectionEnabled;
+ public final boolean mAutoCorrectionEnabled;
public final int mSuggestionVisibility;
public final int mDisplayOrientation;
private final AsyncResultHolder<AppWorkaroundsUtils> mAppWorkarounds;
@@ -109,7 +110,8 @@ public final class SettingsValues {
// Store the input attributes
if (null == inputAttributes) {
- mInputAttributes = new InputAttributes(null, false /* isFullscreenMode */);
+ mInputAttributes = new InputAttributes(
+ null, false /* isFullscreenMode */, context.getPackageName());
} else {
mInputAttributes = inputAttributes;
}
@@ -121,13 +123,18 @@ public final class SettingsValues {
mKeyPreviewPopupOn = Settings.readKeyPreviewPopupEnabled(prefs, res);
mSlidingKeyInputPreviewEnabled = prefs.getBoolean(
DebugSettings.PREF_SLIDING_KEY_INPUT_PREVIEW, true);
- mShowsVoiceInputKey = needsToShowVoiceInputKey(prefs, res);
+ mShowsVoiceInputKey = needsToShowVoiceInputKey(prefs, res)
+ && !mInputAttributes.mIsPasswordField
+ && !mInputAttributes.hasNoMicrophoneKeyOption()
+ && SubtypeSwitcher.getInstance().isShortcutImeEnabled();
final String autoCorrectionThresholdRawValue = prefs.getString(
Settings.PREF_AUTO_CORRECTION_THRESHOLD,
res.getString(R.string.auto_correction_threshold_mode_index_modest));
- mIncludesOtherImesInLanguageSwitchList = prefs.getBoolean(
- Settings.PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST, false);
- mShowsLanguageSwitchKey = Settings.readShowsLanguageSwitchKey(prefs);
+ mIncludesOtherImesInLanguageSwitchList = Settings.ENABLE_SHOW_LANGUAGE_SWITCH_KEY_SETTINGS
+ ? prefs.getBoolean(Settings.PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST, false)
+ : true /* forcibly */;
+ mShowsLanguageSwitchKey = Settings.ENABLE_SHOW_LANGUAGE_SWITCH_KEY_SETTINGS
+ ? Settings.readShowsLanguageSwitchKey(prefs) : true /* forcibly */;
mUseContactsDict = prefs.getBoolean(Settings.PREF_KEY_USE_CONTACTS_DICT, true);
mUsePersonalizedDicts = prefs.getBoolean(Settings.PREF_KEY_USE_PERSONALIZED_DICTS, true);
mUseDoubleSpacePeriod = prefs.getBoolean(Settings.PREF_KEY_USE_DOUBLE_SPACE_PERIOD, true);
@@ -148,7 +155,7 @@ public final class SettingsValues {
mGestureFloatingPreviewTextEnabled = prefs.getBoolean(
Settings.PREF_GESTURE_FLOATING_PREVIEW_TEXT, true);
mPhraseGestureEnabled = Settings.readPhraseGestureEnabled(prefs, res);
- mCorrectionEnabled = mAutoCorrectEnabled && !mInputAttributes.mInputTypeNoAutoCorrect;
+ mAutoCorrectionEnabled = mAutoCorrectEnabled && !mInputAttributes.mInputTypeNoAutoCorrect;
final String showSuggestionsSetting = prefs.getString(
Settings.PREF_SHOW_SUGGESTIONS_SETTING,
res.getString(R.string.prefs_suggestion_visibility_default_value));
@@ -171,7 +178,7 @@ public final class SettingsValues {
ResourceUtils.getFloatFromFraction(
res, R.fraction.config_key_preview_dismiss_end_scale));
mDisplayOrientation = res.getConfiguration().orientation;
- mAppWorkarounds = new AsyncResultHolder<AppWorkaroundsUtils>();
+ mAppWorkarounds = new AsyncResultHolder<>();
final PackageInfo packageInfo = TargetPackageInfoGetterTask.getCachedPackageInfo(
mInputAttributes.mTargetApplicationPackageName);
if (null != packageInfo) {
@@ -187,11 +194,12 @@ public final class SettingsValues {
}
public boolean isSuggestionsRequested() {
- return mInputAttributes.mIsSettingsSuggestionStripOn
- && (mCorrectionEnabled || isSuggestionStripVisible());
+ return mInputAttributes.mShouldShowSuggestions
+ && (mAutoCorrectionEnabled
+ || isCurrentOrientationAllowingSuggestionsPerUserSettings());
}
- public boolean isSuggestionStripVisible() {
+ public boolean isCurrentOrientationAllowingSuggestionsPerUserSettings() {
return (mSuggestionVisibility == SUGGESTION_VISIBILITY_SHOW_VALUE)
|| (mSuggestionVisibility == SUGGESTION_VISIBILITY_SHOW_ONLY_PORTRAIT_VALUE
&& mDisplayOrientation == Configuration.ORIENTATION_PORTRAIT);
@@ -315,18 +323,18 @@ public final class SettingsValues {
private static boolean needsToShowVoiceInputKey(final SharedPreferences prefs,
final Resources res) {
- if (!prefs.contains(Settings.PREF_VOICE_INPUT_KEY)) {
- // Migrate preference from {@link Settings#PREF_VOICE_MODE_OBSOLETE} to
- // {@link Settings#PREF_VOICE_INPUT_KEY}.
+ // Migrate preference from {@link Settings#PREF_VOICE_MODE_OBSOLETE} to
+ // {@link Settings#PREF_VOICE_INPUT_KEY}.
+ if (prefs.contains(Settings.PREF_VOICE_MODE_OBSOLETE)) {
final String voiceModeMain = res.getString(R.string.voice_mode_main);
final String voiceMode = prefs.getString(
Settings.PREF_VOICE_MODE_OBSOLETE, voiceModeMain);
final boolean shouldShowVoiceInputKey = voiceModeMain.equals(voiceMode);
- prefs.edit().putBoolean(Settings.PREF_VOICE_INPUT_KEY, shouldShowVoiceInputKey).apply();
- }
- // Remove the obsolete preference if exists.
- if (prefs.contains(Settings.PREF_VOICE_MODE_OBSOLETE)) {
- prefs.edit().remove(Settings.PREF_VOICE_MODE_OBSOLETE).apply();
+ prefs.edit()
+ .putBoolean(Settings.PREF_VOICE_INPUT_KEY, shouldShowVoiceInputKey)
+ // Remove the obsolete preference if exists.
+ .remove(Settings.PREF_VOICE_MODE_OBSOLETE)
+ .apply();
}
return prefs.getBoolean(Settings.PREF_VOICE_INPUT_KEY, true);
}
@@ -387,8 +395,8 @@ public final class SettingsValues {
sb.append("" + mAutoCorrectEnabled);
sb.append("\n mAutoCorrectionThreshold = ");
sb.append("" + mAutoCorrectionThreshold);
- sb.append("\n mCorrectionEnabled = ");
- sb.append("" + mCorrectionEnabled);
+ sb.append("\n mAutoCorrectionEnabled = ");
+ sb.append("" + mAutoCorrectionEnabled);
sb.append("\n mSuggestionVisibility = ");
sb.append("" + mSuggestionVisibility);
sb.append("\n mDisplayOrientation = ");
diff --git a/java/src/com/android/inputmethod/latin/setup/SetupStartIndicatorView.java b/java/src/com/android/inputmethod/latin/setup/SetupStartIndicatorView.java
index 974dfddd3..73d25f6aa 100644
--- a/java/src/com/android/inputmethod/latin/setup/SetupStartIndicatorView.java
+++ b/java/src/com/android/inputmethod/latin/setup/SetupStartIndicatorView.java
@@ -21,13 +21,13 @@ import android.content.res.ColorStateList;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
+import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
-import com.android.inputmethod.compat.ViewCompatUtils;
import com.android.inputmethod.latin.R;
public final class SetupStartIndicatorView extends LinearLayout {
@@ -96,13 +96,13 @@ public final class SetupStartIndicatorView extends LinearLayout {
@Override
protected void onDraw(final Canvas canvas) {
super.onDraw(canvas);
- final int layoutDirection = ViewCompatUtils.getLayoutDirection(this);
+ final int layoutDirection = ViewCompat.getLayoutDirection(this);
final int width = getWidth();
final int height = getHeight();
final float halfHeight = height / 2.0f;
final Path path = mIndicatorPath;
path.rewind();
- if (layoutDirection == ViewCompatUtils.LAYOUT_DIRECTION_RTL) {
+ if (layoutDirection == ViewCompat.LAYOUT_DIRECTION_RTL) {
// Left arrow
path.moveTo(width, 0.0f);
path.lineTo(0.0f, halfHeight);
diff --git a/java/src/com/android/inputmethod/latin/setup/SetupStepIndicatorView.java b/java/src/com/android/inputmethod/latin/setup/SetupStepIndicatorView.java
index c909507c6..6734e61b8 100644
--- a/java/src/com/android/inputmethod/latin/setup/SetupStepIndicatorView.java
+++ b/java/src/com/android/inputmethod/latin/setup/SetupStepIndicatorView.java
@@ -20,10 +20,10 @@ import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
+import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.view.View;
-import com.android.inputmethod.compat.ViewCompatUtils;
import com.android.inputmethod.latin.R;
public final class SetupStepIndicatorView extends View {
@@ -38,12 +38,12 @@ public final class SetupStepIndicatorView extends View {
}
public void setIndicatorPosition(final int stepPos, final int totalStepNum) {
- final int layoutDirection = ViewCompatUtils.getLayoutDirection(this);
+ final int layoutDirection = ViewCompat.getLayoutDirection(this);
// The indicator position is the center of the partition that is equally divided into
// the total step number.
final float partionWidth = 1.0f / totalStepNum;
final float pos = stepPos * partionWidth + partionWidth / 2.0f;
- mXRatio = (layoutDirection == ViewCompatUtils.LAYOUT_DIRECTION_RTL) ? 1.0f - pos : pos;
+ mXRatio = (layoutDirection == ViewCompat.LAYOUT_DIRECTION_RTL) ? 1.0f - pos : pos;
invalidate();
}
diff --git a/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java b/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java
index 5072fabd6..bcac05a6a 100644
--- a/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java
+++ b/java/src/com/android/inputmethod/latin/setup/SetupWizardActivity.java
@@ -37,7 +37,6 @@ import com.android.inputmethod.compat.TextViewCompatUtils;
import com.android.inputmethod.compat.ViewCompatUtils;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.settings.SettingsActivity;
-import com.android.inputmethod.latin.utils.CollectionUtils;
import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper;
import java.util.ArrayList;
@@ -482,7 +481,7 @@ public final class SetupWizardActivity extends Activity implements View.OnClickL
static final class SetupStepGroup {
private final SetupStepIndicatorView mIndicatorView;
- private final ArrayList<SetupStep> mGroup = CollectionUtils.newArrayList();
+ private final ArrayList<SetupStep> mGroup = new ArrayList<>();
public SetupStepGroup(final SetupStepIndicatorView indicatorView) {
mIndicatorView = indicatorView;
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
index 65ebcf5f1..8d495646d 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
@@ -27,7 +27,6 @@ import android.view.inputmethod.InputMethodSubtype;
import android.view.textservice.SuggestionsInfo;
import com.android.inputmethod.keyboard.KeyboardLayoutSet;
-import com.android.inputmethod.latin.BinaryDictionary;
import com.android.inputmethod.latin.ContactsBinaryDictionary;
import com.android.inputmethod.latin.Dictionary;
import com.android.inputmethod.latin.DictionaryCollection;
@@ -77,7 +76,7 @@ public final class AndroidSpellCheckerService extends SpellCheckerService
private final Object mUseContactsLock = new Object();
private final HashSet<WeakReference<DictionaryCollection>> mDictionaryCollectionsList =
- CollectionUtils.newHashSet();
+ new HashSet<>();
public static final int SCRIPT_LATIN = 0;
public static final int SCRIPT_CYRILLIC = 1;
@@ -94,7 +93,7 @@ public final class AndroidSpellCheckerService extends SpellCheckerService
// proximity to pass to the dictionary descent algorithm.
// IMPORTANT: this only contains languages - do not write countries in there.
// Only the language is searched from the map.
- mLanguageToScript = CollectionUtils.newTreeMap();
+ mLanguageToScript = new TreeMap<>();
mLanguageToScript.put("cs", SCRIPT_LATIN);
mLanguageToScript.put("da", SCRIPT_LATIN);
mLanguageToScript.put("de", SCRIPT_LATIN);
@@ -255,7 +254,7 @@ public final class AndroidSpellCheckerService extends SpellCheckerService
mOriginalText = originalText;
mRecommendedThreshold = recommendedThreshold;
mMaxLength = maxLength;
- mSuggestions = CollectionUtils.newArrayList(maxLength + 1);
+ mSuggestions = new ArrayList<>(maxLength + 1);
mScores = new int[mMaxLength];
}
@@ -441,8 +440,7 @@ public final class AndroidSpellCheckerService extends SpellCheckerService
}
}
dictionaryCollection.addDictionary(mContactsDictionary);
- mDictionaryCollectionsList.add(
- new WeakReference<DictionaryCollection>(dictionaryCollection));
+ mDictionaryCollectionsList.add(new WeakReference<>(dictionaryCollection));
}
return new DictAndKeyboard(dictionaryCollection, keyboardLayoutSet);
}
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java
index e951f5a89..55274cfe2 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerSession.java
@@ -16,6 +16,7 @@
package com.android.inputmethod.latin.spellcheck;
+import android.content.res.Resources;
import android.os.Binder;
import android.text.TextUtils;
import android.util.Log;
@@ -24,17 +25,20 @@ import android.view.textservice.SuggestionsInfo;
import android.view.textservice.TextInfo;
import com.android.inputmethod.latin.PrevWordsInfo;
-import com.android.inputmethod.latin.utils.CollectionUtils;
import java.util.ArrayList;
+import java.util.Locale;
public final class AndroidSpellCheckerSession extends AndroidWordLevelSpellCheckerSession {
private static final String TAG = AndroidSpellCheckerSession.class.getSimpleName();
private static final boolean DBG = false;
private final static String[] EMPTY_STRING_ARRAY = new String[0];
+ private final Resources mResources;
+ private SentenceLevelAdapter mSentenceLevelAdapter;
public AndroidSpellCheckerSession(AndroidSpellCheckerService service) {
super(service);
+ mResources = service.getResources();
}
private SentenceSuggestionsInfo fixWronglyInvalidatedWordWithSingleQuote(TextInfo ti,
@@ -44,10 +48,9 @@ public final class AndroidSpellCheckerSession extends AndroidWordLevelSpellCheck
return null;
}
final int N = ssi.getSuggestionsCount();
- final ArrayList<Integer> additionalOffsets = CollectionUtils.newArrayList();
- final ArrayList<Integer> additionalLengths = CollectionUtils.newArrayList();
- final ArrayList<SuggestionsInfo> additionalSuggestionsInfos =
- CollectionUtils.newArrayList();
+ final ArrayList<Integer> additionalOffsets = new ArrayList<>();
+ final ArrayList<Integer> additionalLengths = new ArrayList<>();
+ final ArrayList<SuggestionsInfo> additionalSuggestionsInfos = new ArrayList<>();
String currentWord = null;
for (int i = 0; i < N; ++i) {
final SuggestionsInfo si = ssi.getSuggestionsInfoAt(i);
@@ -117,8 +120,7 @@ public final class AndroidSpellCheckerSession extends AndroidWordLevelSpellCheck
@Override
public SentenceSuggestionsInfo[] onGetSentenceSuggestionsMultiple(TextInfo[] textInfos,
int suggestionsLimit) {
- final SentenceSuggestionsInfo[] retval =
- super.onGetSentenceSuggestionsMultiple(textInfos, suggestionsLimit);
+ final SentenceSuggestionsInfo[] retval = splitAndSuggest(textInfos, suggestionsLimit);
if (retval == null || retval.length != textInfos.length) {
return retval;
}
@@ -132,6 +134,58 @@ public final class AndroidSpellCheckerSession extends AndroidWordLevelSpellCheck
return retval;
}
+ /**
+ * Get sentence suggestions for specified texts in an array of TextInfo. This is taken from
+ * SpellCheckerService#onGetSentenceSuggestionsMultiple that we can't use because it's
+ * using private variables.
+ * The default implementation splits the input text to words and returns
+ * {@link SentenceSuggestionsInfo} which contains suggestions for each word.
+ * This function will run on the incoming IPC thread.
+ * So, this is not called on the main thread,
+ * but will be called in series on another thread.
+ * @param textInfos an array of the text metadata
+ * @param suggestionsLimit the maximum number of suggestions to be returned
+ * @return an array of {@link SentenceSuggestionsInfo} returned by
+ * {@link SpellCheckerService.Session#onGetSuggestions(TextInfo, int)}
+ */
+ private SentenceSuggestionsInfo[] splitAndSuggest(TextInfo[] textInfos, int suggestionsLimit) {
+ if (textInfos == null || textInfos.length == 0) {
+ return SentenceLevelAdapter.EMPTY_SENTENCE_SUGGESTIONS_INFOS;
+ }
+ SentenceLevelAdapter sentenceLevelAdapter;
+ synchronized(this) {
+ sentenceLevelAdapter = mSentenceLevelAdapter;
+ if (sentenceLevelAdapter == null) {
+ final String localeStr = getLocale();
+ if (!TextUtils.isEmpty(localeStr)) {
+ sentenceLevelAdapter = new SentenceLevelAdapter(mResources,
+ new Locale(localeStr));
+ mSentenceLevelAdapter = sentenceLevelAdapter;
+ }
+ }
+ }
+ if (sentenceLevelAdapter == null) {
+ return SentenceLevelAdapter.EMPTY_SENTENCE_SUGGESTIONS_INFOS;
+ }
+ final int infosSize = textInfos.length;
+ final SentenceSuggestionsInfo[] retval = new SentenceSuggestionsInfo[infosSize];
+ for (int i = 0; i < infosSize; ++i) {
+ final SentenceLevelAdapter.SentenceTextInfoParams textInfoParams =
+ sentenceLevelAdapter.getSplitWords(textInfos[i]);
+ final ArrayList<SentenceLevelAdapter.SentenceWordItem> mItems =
+ textInfoParams.mItems;
+ final int itemsSize = mItems.size();
+ final TextInfo[] splitTextInfos = new TextInfo[itemsSize];
+ for (int j = 0; j < itemsSize; ++j) {
+ splitTextInfos[j] = mItems.get(j).mTextInfo;
+ }
+ retval[i] = SentenceLevelAdapter.reconstructSuggestions(
+ textInfoParams, onGetSuggestionsMultiple(
+ splitTextInfos, suggestionsLimit, true));
+ }
+ return retval;
+ }
+
@Override
public SuggestionsInfo[] onGetSuggestionsMultiple(TextInfo[] textInfos,
int suggestionsLimit, boolean sequentialWords) {
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
index cf26000d5..0032fcb88 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
@@ -28,7 +28,6 @@ import android.view.textservice.SuggestionsInfo;
import android.view.textservice.TextInfo;
import com.android.inputmethod.compat.SuggestionsInfoCompatUtils;
-import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.Dictionary;
import com.android.inputmethod.latin.PrevWordsInfo;
@@ -69,7 +68,7 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session {
private static final char CHAR_DELIMITER = '\uFFFC';
private static final int MAX_CACHE_SIZE = 50;
private final LruCache<String, SuggestionsParams> mUnigramSuggestionsInfoCache =
- new LruCache<String, SuggestionsParams>(MAX_CACHE_SIZE);
+ new LruCache<>(MAX_CACHE_SIZE);
// TODO: Support n-gram input
private static String generateKey(final String query, final PrevWordsInfo prevWordsInfo) {
@@ -324,7 +323,7 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session {
} else {
coordinates = dictInfo.mKeyboard.getCoordinates(codePoints);
}
- composer.setComposingWord(codePoints, coordinates, null /* previousWord */);
+ composer.setComposingWord(codePoints, coordinates);
// TODO: make a spell checker option to block offensive words or not
final ArrayList<SuggestedWordInfo> suggestions =
dictInfo.mDictionary.getSuggestions(composer, prevWordsInfo,
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/DictAndKeyboard.java b/java/src/com/android/inputmethod/latin/spellcheck/DictAndKeyboard.java
index 1ffe50681..b33739fc1 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/DictAndKeyboard.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/DictAndKeyboard.java
@@ -16,11 +16,11 @@
package com.android.inputmethod.latin.spellcheck;
-import com.android.inputmethod.latin.Dictionary;
import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.KeyboardId;
import com.android.inputmethod.keyboard.KeyboardLayoutSet;
import com.android.inputmethod.keyboard.ProximityInfo;
+import com.android.inputmethod.latin.Dictionary;
/**
* A container for a Dictionary and a Keyboard.
@@ -28,19 +28,15 @@ import com.android.inputmethod.keyboard.ProximityInfo;
public final class DictAndKeyboard {
public final Dictionary mDictionary;
public final Keyboard mKeyboard;
- private final Keyboard mManualShiftedKeyboard;
public DictAndKeyboard(
final Dictionary dictionary, final KeyboardLayoutSet keyboardLayoutSet) {
mDictionary = dictionary;
if (keyboardLayoutSet == null) {
mKeyboard = null;
- mManualShiftedKeyboard = null;
return;
}
mKeyboard = keyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET);
- mManualShiftedKeyboard =
- keyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED);
}
public ProximityInfo getProximityInfo() {
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java
index ba2e0c309..cc52a3e0f 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java
@@ -23,7 +23,6 @@ import com.android.inputmethod.latin.Dictionary;
import com.android.inputmethod.latin.PrevWordsInfo;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.WordComposer;
-import com.android.inputmethod.latin.utils.CollectionUtils;
import java.util.ArrayList;
import java.util.Locale;
@@ -47,7 +46,7 @@ public final class DictionaryPool extends LinkedBlockingQueue<DictAndKeyboard> {
private final Locale mLocale;
private int mSize;
private volatile boolean mClosed;
- final static ArrayList<SuggestedWordInfo> noSuggestions = CollectionUtils.newArrayList();
+ final static ArrayList<SuggestedWordInfo> noSuggestions = new ArrayList<>();
private final static DictAndKeyboard dummyDict = new DictAndKeyboard(
new Dictionary(Dictionary.TYPE_MAIN) {
// TODO: this dummy dictionary should be a singleton in the Dictionary class.
@@ -59,7 +58,7 @@ public final class DictionaryPool extends LinkedBlockingQueue<DictAndKeyboard> {
return noSuggestions;
}
@Override
- public boolean isValidWord(final String word) {
+ public boolean isInDictionary(final String word) {
// This is never called. However if for some strange reason it ever gets
// called, returning true is less destructive (it will not underline the
// word in red).
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SentenceLevelAdapter.java b/java/src/com/android/inputmethod/latin/spellcheck/SentenceLevelAdapter.java
new file mode 100644
index 000000000..13352f39e
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/spellcheck/SentenceLevelAdapter.java
@@ -0,0 +1,185 @@
+/*
+ * 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.spellcheck;
+
+import android.content.res.Resources;
+import android.view.textservice.SentenceSuggestionsInfo;
+import android.view.textservice.SuggestionsInfo;
+import android.view.textservice.TextInfo;
+
+import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
+import com.android.inputmethod.latin.utils.RunInLocale;
+
+import java.util.ArrayList;
+import java.util.Locale;
+
+/**
+ * This code is mostly lifted directly from android.service.textservice.SpellCheckerService in
+ * the framework; maybe that should be protected instead, so that implementers don't have to
+ * rewrite everything for any small change.
+ */
+public class SentenceLevelAdapter {
+ public static final SentenceSuggestionsInfo[] EMPTY_SENTENCE_SUGGESTIONS_INFOS =
+ new SentenceSuggestionsInfo[] {};
+ private static final SuggestionsInfo EMPTY_SUGGESTIONS_INFO = new SuggestionsInfo(0, null);
+ /**
+ * Container for split TextInfo parameters
+ */
+ public static class SentenceWordItem {
+ public final TextInfo mTextInfo;
+ public final int mStart;
+ public final int mLength;
+ public SentenceWordItem(TextInfo ti, int start, int end) {
+ mTextInfo = ti;
+ mStart = start;
+ mLength = end - start;
+ }
+ }
+
+ /**
+ * Container for originally queried TextInfo and parameters
+ */
+ public static class SentenceTextInfoParams {
+ final TextInfo mOriginalTextInfo;
+ final ArrayList<SentenceWordItem> mItems;
+ final int mSize;
+ public SentenceTextInfoParams(TextInfo ti, ArrayList<SentenceWordItem> items) {
+ mOriginalTextInfo = ti;
+ mItems = items;
+ mSize = items.size();
+ }
+ }
+
+ private static class WordIterator {
+ private final SpacingAndPunctuations mSpacingAndPunctuations;
+ public WordIterator(final Resources res, final Locale locale) {
+ final RunInLocale<SpacingAndPunctuations> job
+ = new RunInLocale<SpacingAndPunctuations>() {
+ @Override
+ protected SpacingAndPunctuations job(final Resources res) {
+ return new SpacingAndPunctuations(res);
+ }
+ };
+ mSpacingAndPunctuations = job.runInLocale(res, locale);
+ }
+
+ public int getEndOfWord(final CharSequence sequence, int index) {
+ final int length = sequence.length();
+ index = index < 0 ? 0 : Character.offsetByCodePoints(sequence, index, 1);
+ while (index < length) {
+ final int codePoint = Character.codePointAt(sequence, index);
+ if (mSpacingAndPunctuations.isWordSeparator(codePoint)) {
+ // If it's a period, we want to stop here only if it's followed by another
+ // word separator. In all other cases we stop here.
+ if (Constants.CODE_PERIOD == codePoint) {
+ final int indexOfNextCodePoint =
+ index + Character.charCount(Constants.CODE_PERIOD);
+ if (indexOfNextCodePoint < length
+ && mSpacingAndPunctuations.isWordSeparator(
+ Character.codePointAt(sequence, indexOfNextCodePoint))) {
+ return index;
+ }
+ } else {
+ return index;
+ }
+ }
+ index += Character.charCount(codePoint);
+ }
+ return index;
+ }
+
+ public int getBeginningOfNextWord(final CharSequence sequence, int index) {
+ final int length = sequence.length();
+ if (index >= length) {
+ return -1;
+ }
+ index = index < 0 ? 0 : Character.offsetByCodePoints(sequence, index, 1);
+ while (index < length) {
+ final int codePoint = Character.codePointAt(sequence, index);
+ if (!mSpacingAndPunctuations.isWordSeparator(codePoint)) {
+ return index;
+ }
+ index += Character.charCount(codePoint);
+ }
+ return -1;
+ }
+ }
+
+ private final WordIterator mWordIterator;
+ public SentenceLevelAdapter(final Resources res, final Locale locale) {
+ mWordIterator = new WordIterator(res, locale);
+ }
+
+ public SentenceTextInfoParams getSplitWords(TextInfo originalTextInfo) {
+ final WordIterator wordIterator = mWordIterator;
+ final CharSequence originalText = originalTextInfo.getText();
+ final int cookie = originalTextInfo.getCookie();
+ final int start = -1;
+ final int end = originalText.length();
+ final ArrayList<SentenceWordItem> wordItems = new ArrayList<SentenceWordItem>();
+ int wordStart = wordIterator.getBeginningOfNextWord(originalText, start);
+ int wordEnd = wordIterator.getEndOfWord(originalText, wordStart);
+ while (wordStart <= end && wordEnd != -1 && wordStart != -1) {
+ if (wordEnd >= start && wordEnd > wordStart) {
+ final String query = originalText.subSequence(wordStart, wordEnd).toString();
+ final TextInfo ti = new TextInfo(query, cookie, query.hashCode());
+ wordItems.add(new SentenceWordItem(ti, wordStart, wordEnd));
+ }
+ wordStart = wordIterator.getBeginningOfNextWord(originalText, wordEnd);
+ if (wordStart == -1) {
+ break;
+ }
+ wordEnd = wordIterator.getEndOfWord(originalText, wordStart);
+ }
+ return new SentenceTextInfoParams(originalTextInfo, wordItems);
+ }
+
+ public static SentenceSuggestionsInfo reconstructSuggestions(
+ SentenceTextInfoParams originalTextInfoParams, SuggestionsInfo[] results) {
+ if (results == null || results.length == 0) {
+ return null;
+ }
+ if (originalTextInfoParams == null) {
+ return null;
+ }
+ final int originalCookie = originalTextInfoParams.mOriginalTextInfo.getCookie();
+ final int originalSequence =
+ originalTextInfoParams.mOriginalTextInfo.getSequence();
+
+ final int querySize = originalTextInfoParams.mSize;
+ final int[] offsets = new int[querySize];
+ final int[] lengths = new int[querySize];
+ final SuggestionsInfo[] reconstructedSuggestions = new SuggestionsInfo[querySize];
+ for (int i = 0; i < querySize; ++i) {
+ final SentenceWordItem item = originalTextInfoParams.mItems.get(i);
+ SuggestionsInfo result = null;
+ for (int j = 0; j < results.length; ++j) {
+ final SuggestionsInfo cur = results[j];
+ if (cur != null && cur.getSequence() == item.mTextInfo.getSequence()) {
+ result = cur;
+ result.setCookieAndSequence(originalCookie, originalSequence);
+ break;
+ }
+ }
+ offsets[i] = item.mStart;
+ lengths[i] = item.mLength;
+ reconstructedSuggestions[i] = result != null ? result : EMPTY_SUGGESTIONS_INFO;
+ }
+ return new SentenceSuggestionsInfo(reconstructedSuggestions, offsets, lengths);
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SynchronouslyLoadedContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/spellcheck/SynchronouslyLoadedContactsBinaryDictionary.java
index 75075664f..a6437bac3 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/SynchronouslyLoadedContactsBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/SynchronouslyLoadedContactsBinaryDictionary.java
@@ -47,9 +47,9 @@ public final class SynchronouslyLoadedContactsBinaryDictionary extends ContactsB
}
@Override
- public boolean isValidWord(final String word) {
+ public boolean isInDictionary(final String word) {
synchronized (mLock) {
- return super.isValidWord(word);
+ return super.isInDictionary(word);
}
}
}
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SynchronouslyLoadedUserBinaryDictionary.java b/java/src/com/android/inputmethod/latin/spellcheck/SynchronouslyLoadedUserBinaryDictionary.java
index f2d981a9d..8c9d5d681 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/SynchronouslyLoadedUserBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/SynchronouslyLoadedUserBinaryDictionary.java
@@ -52,9 +52,9 @@ public final class SynchronouslyLoadedUserBinaryDictionary extends UserBinaryDic
}
@Override
- public boolean isValidWord(final String word) {
+ public boolean isInDictionary(final String word) {
synchronized (mLock) {
- return super.isValidWord(word);
+ return super.isInDictionary(word);
}
}
}
diff --git a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
index e90b15ca5..346aea34a 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
@@ -23,23 +23,17 @@ import android.graphics.drawable.Drawable;
import com.android.inputmethod.keyboard.Key;
import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.keyboard.KeyboardActionListener;
import com.android.inputmethod.keyboard.internal.KeyboardBuilder;
import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
import com.android.inputmethod.keyboard.internal.KeyboardParams;
import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.SuggestedWords;
-import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.utils.TypefaceUtils;
public final class MoreSuggestions extends Keyboard {
public final SuggestedWords mSuggestedWords;
- public static abstract class MoreSuggestionsListener extends KeyboardActionListener.Adapter {
- public abstract void onSuggestionSelected(final int index, final SuggestedWordInfo info);
- }
-
MoreSuggestions(final MoreSuggestionsParam params, final SuggestedWords suggestedWords) {
super(params);
mSuggestedWords = suggestedWords;
diff --git a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
index 7fd64c4bf..528d500d2 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
@@ -20,13 +20,16 @@ import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
+import com.android.inputmethod.accessibility.AccessibilityUtils;
+import com.android.inputmethod.accessibility.MoreSuggestionsAccessibilityDelegate;
import com.android.inputmethod.keyboard.Key;
import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.keyboard.KeyboardActionListener;
import com.android.inputmethod.keyboard.MoreKeysKeyboardView;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.SuggestedWords;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.suggestions.MoreSuggestions.MoreSuggestionKey;
-import com.android.inputmethod.latin.suggestions.MoreSuggestions.MoreSuggestionsListener;
/**
* A view that renders a virtual {@link MoreSuggestions}. It handles rendering of keys and detecting
@@ -35,6 +38,10 @@ import com.android.inputmethod.latin.suggestions.MoreSuggestions.MoreSuggestions
public final class MoreSuggestionsView extends MoreKeysKeyboardView {
private static final String TAG = MoreSuggestionsView.class.getSimpleName();
+ public static abstract class MoreSuggestionsListener extends KeyboardActionListener.Adapter {
+ public abstract void onSuggestionSelected(final int index, final SuggestedWordInfo info);
+ }
+
public MoreSuggestionsView(final Context context, final AttributeSet attrs) {
this(context, attrs, R.attr.moreKeysKeyboardViewStyle);
}
@@ -45,6 +52,26 @@ public final class MoreSuggestionsView extends MoreKeysKeyboardView {
}
@Override
+ public void setKeyboard(final Keyboard keyboard) {
+ super.setKeyboard(keyboard);
+ // With accessibility mode off, {@link #mAccessibilityDelegate} is set to null at the
+ // above {@link MoreKeysKeyboardView#setKeyboard(Keyboard)} call.
+ // With accessibility mode on, {@link #mAccessibilityDelegate} is set to a
+ // {@link MoreKeysKeyboardAccessibilityDelegate} object at the above
+ // {@link MoreKeysKeyboardView#setKeyboard(Keyboard)} call. And the object has to be
+ // overwritten by a {@link MoreSuggestionsAccessibilityDelegate} object here.
+ if (AccessibilityUtils.getInstance().isAccessibilityEnabled()) {
+ if (!(mAccessibilityDelegate instanceof MoreSuggestionsAccessibilityDelegate)) {
+ mAccessibilityDelegate = new MoreSuggestionsAccessibilityDelegate(
+ this, mKeyDetector);
+ mAccessibilityDelegate.setOpenAnnounce(R.string.spoken_open_more_suggestions);
+ mAccessibilityDelegate.setCloseAnnounce(R.string.spoken_close_more_suggestions);
+ }
+ mAccessibilityDelegate.setKeyboard(keyboard);
+ }
+ }
+
+ @Override
protected int getDefaultCoordX() {
final MoreSuggestions pane = (MoreSuggestions)getKeyboard();
return pane.mOccupiedWidth / 2;
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
index 810bda758..19b48f081 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
@@ -379,10 +379,9 @@ final class SuggestionStripLayoutHelper {
} else {
wordView.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
}
-
- // Disable this suggestion if the suggestion is null or empty.
- // TODO: Fix disabled {@link TextView}'s content description.
- wordView.setEnabled(!TextUtils.isEmpty(word));
+ // {@link StyleSpan} in a content description may cause an issue of TTS/TalkBack.
+ // Use a simple {@link String} to avoid the issue.
+ wordView.setContentDescription(TextUtils.isEmpty(word) ? null : word.toString());
final CharSequence text = getEllipsizedText(word, width, wordView.getPaint());
final float scaleX = getTextScaleX(word, width, wordView.getPaint());
wordView.setText(text); // TextView.setText() resets text scale x to 1.0.
@@ -461,14 +460,15 @@ final class SuggestionStripLayoutHelper {
}
final TextView wordView = mWordViews.get(positionInStrip);
- wordView.setEnabled(true);
- wordView.setTextColor(mColorAutoCorrect);
+ final String punctuation = punctuationSuggestions.getLabel(positionInStrip);
// {@link TextView#getTag()} is used to get the index in suggestedWords at
// {@link SuggestionStripView#onClick(View)}.
wordView.setTag(positionInStrip);
- wordView.setText(punctuationSuggestions.getLabel(positionInStrip));
+ wordView.setText(punctuation);
+ wordView.setContentDescription(punctuation);
wordView.setTextScaleX(1.0f);
wordView.setCompoundDrawables(null, null, null, null);
+ wordView.setTextColor(mColorAutoCorrect);
stripView.addView(wordView);
setLayoutWeight(wordView, 1.0f, mSuggestionsStripHeight);
}
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
index 619804afa..3be933ff7 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
@@ -42,17 +42,14 @@ import com.android.inputmethod.keyboard.MainKeyboardView;
import com.android.inputmethod.keyboard.MoreKeysPanel;
import com.android.inputmethod.latin.AudioAndHapticFeedbackManager;
import com.android.inputmethod.latin.Constants;
-import com.android.inputmethod.latin.InputAttributes;
import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.SuggestedWords;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
-import com.android.inputmethod.latin.define.ProductionFlag;
import com.android.inputmethod.latin.settings.Settings;
-import com.android.inputmethod.latin.suggestions.MoreSuggestions.MoreSuggestionsListener;
-import com.android.inputmethod.latin.utils.CollectionUtils;
+import com.android.inputmethod.latin.settings.SettingsValues;
+import com.android.inputmethod.latin.suggestions.MoreSuggestionsView.MoreSuggestionsListener;
import com.android.inputmethod.latin.utils.ImportantNoticeUtils;
-import com.android.inputmethod.research.ResearchLogger;
import java.util.ArrayList;
@@ -78,9 +75,9 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
private final MoreSuggestionsView mMoreSuggestionsView;
private final MoreSuggestions.Builder mMoreSuggestionsBuilder;
- private final ArrayList<TextView> mWordViews = CollectionUtils.newArrayList();
- private final ArrayList<TextView> mDebugInfoViews = CollectionUtils.newArrayList();
- private final ArrayList<View> mDividerViews = CollectionUtils.newArrayList();
+ private final ArrayList<TextView> mWordViews = new ArrayList<>();
+ private final ArrayList<TextView> mDebugInfoViews = new ArrayList<>();
+ private final ArrayList<View> mDividerViews = new ArrayList<>();
Listener mListener;
private SuggestedWords mSuggestedWords = SuggestedWords.EMPTY;
@@ -90,43 +87,44 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
private final StripVisibilityGroup mStripVisibilityGroup;
private static class StripVisibilityGroup {
+ private final View mSuggestionStripView;
private final View mSuggestionsStrip;
- private final View mVoiceKey;
private final View mAddToDictionaryStrip;
private final View mImportantNoticeStrip;
- public StripVisibilityGroup(final View suggestionsStrip, final View voiceKey,
- final View addToDictionaryStrip, final View importantNoticeStrip) {
+ public StripVisibilityGroup(final View suggestionStripView,
+ final ViewGroup suggestionsStrip, final ViewGroup addToDictionaryStrip,
+ final View importantNoticeStrip) {
+ mSuggestionStripView = suggestionStripView;
mSuggestionsStrip = suggestionsStrip;
- mVoiceKey = voiceKey;
mAddToDictionaryStrip = addToDictionaryStrip;
mImportantNoticeStrip = importantNoticeStrip;
- showSuggestionsStrip(false /* voiceKeyEnabled */);
+ showSuggestionsStrip();
}
- public void setLayoutDirection(final int layoutDirection) {
+ public void setLayoutDirection(final boolean isRtlLanguage) {
+ final int layoutDirection = isRtlLanguage ? ViewCompat.LAYOUT_DIRECTION_RTL
+ : ViewCompat.LAYOUT_DIRECTION_LTR;
+ ViewCompat.setLayoutDirection(mSuggestionStripView, layoutDirection);
ViewCompat.setLayoutDirection(mSuggestionsStrip, layoutDirection);
ViewCompat.setLayoutDirection(mAddToDictionaryStrip, layoutDirection);
ViewCompat.setLayoutDirection(mImportantNoticeStrip, layoutDirection);
}
- public void showSuggestionsStrip(final boolean enableVoiceKey) {
+ public void showSuggestionsStrip() {
mSuggestionsStrip.setVisibility(VISIBLE);
- mVoiceKey.setVisibility(enableVoiceKey ? VISIBLE : INVISIBLE);
mAddToDictionaryStrip.setVisibility(INVISIBLE);
mImportantNoticeStrip.setVisibility(INVISIBLE);
}
public void showAddToDictionaryStrip() {
mSuggestionsStrip.setVisibility(INVISIBLE);
- mVoiceKey.setVisibility(INVISIBLE);
mAddToDictionaryStrip.setVisibility(VISIBLE);
mImportantNoticeStrip.setVisibility(INVISIBLE);
}
- public void showImportantNoticeStrip(final boolean enableVoiceKey) {
+ public void showImportantNoticeStrip() {
mSuggestionsStrip.setVisibility(INVISIBLE);
- mVoiceKey.setVisibility(enableVoiceKey ? VISIBLE : INVISIBLE);
mAddToDictionaryStrip.setVisibility(INVISIBLE);
mImportantNoticeStrip.setVisibility(VISIBLE);
}
@@ -156,7 +154,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
mVoiceKey = (ImageButton)findViewById(R.id.suggestions_strip_voice_key);
mAddToDictionaryStrip = (ViewGroup)findViewById(R.id.add_to_dictionary_strip);
mImportantNoticeStrip = findViewById(R.id.important_notice_strip);
- mStripVisibilityGroup = new StripVisibilityGroup(mSuggestionsStrip, mVoiceKey,
+ mStripVisibilityGroup = new StripVisibilityGroup(this, mSuggestionsStrip,
mAddToDictionaryStrip, mImportantNoticeStrip);
for (int pos = 0; pos < SuggestedWords.MAX_SUGGESTIONS; pos++) {
@@ -165,7 +163,6 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
word.setOnLongClickListener(this);
mWordViews.add(word);
final View divider = inflater.inflate(R.layout.suggestion_divider, null);
- divider.setOnClickListener(this);
mDividerViews.add(divider);
final TextView info = new TextView(context, null, R.attr.suggestionWordStyle);
info.setTextColor(Color.WHITE);
@@ -204,30 +201,20 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
mMainKeyboardView = (MainKeyboardView)inputView.findViewById(R.id.keyboard_view);
}
- private boolean isVoiceKeyEnabled() {
- if (mMainKeyboardView == null) {
- return false;
- }
- final Keyboard keyboard = mMainKeyboardView.getKeyboard();
- if (keyboard == null) {
- return false;
- }
- return keyboard.mId.mHasShortcutKey;
+ public void updateVisibility(final boolean shouldBeVisible, final boolean isFullscreenMode) {
+ final int visibility = shouldBeVisible ? VISIBLE : (isFullscreenMode ? GONE : INVISIBLE);
+ setVisibility(visibility);
+ final SettingsValues currentSettingsValues = Settings.getInstance().getCurrent();
+ mVoiceKey.setVisibility(currentSettingsValues.mShowsVoiceInputKey ? VISIBLE : INVISIBLE);
}
public void setSuggestions(final SuggestedWords suggestedWords, final boolean isRtlLanguage) {
clear();
- final int layoutDirection = isRtlLanguage ? ViewCompat.LAYOUT_DIRECTION_RTL
- : ViewCompat.LAYOUT_DIRECTION_LTR;
- setLayoutDirection(layoutDirection);
- mStripVisibilityGroup.setLayoutDirection(layoutDirection);
+ mStripVisibilityGroup.setLayoutDirection(isRtlLanguage);
mSuggestedWords = suggestedWords;
mSuggestionsCountInStrip = mLayoutHelper.layoutAndReturnSuggestionCountInStrip(
mSuggestedWords, mSuggestionsStrip, this);
- if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
- ResearchLogger.suggestionStripView_setSuggestions(mSuggestedWords);
- }
- mStripVisibilityGroup.showSuggestionsStrip(isVoiceKeyEnabled());
+ mStripVisibilityGroup.showSuggestionsStrip();
}
public int setMoreSuggestionsHeight(final int remainingHeight) {
@@ -258,8 +245,8 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
// This method checks if we should show the important notice (checks on permanent storage if
// it has been shown once already or not, and if in the setup wizard). If applicable, it shows
// the notice. In all cases, it returns true if it was shown, false otherwise.
- public boolean maybeShowImportantNoticeTitle(final InputAttributes inputAttributes) {
- if (!ImportantNoticeUtils.shouldShowImportantNotice(getContext(), inputAttributes)) {
+ public boolean maybeShowImportantNoticeTitle() {
+ if (!ImportantNoticeUtils.shouldShowImportantNotice(getContext())) {
return false;
}
if (getWidth() <= 0) {
@@ -274,7 +261,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
dismissMoreSuggestionsPanel();
}
mLayoutHelper.layoutImportantNotice(mImportantNoticeStrip, importantNoticeTitle);
- mStripVisibilityGroup.showImportantNoticeStrip(isVoiceKeyEnabled());
+ mStripVisibilityGroup.showImportantNoticeStrip();
mImportantNoticeStrip.setOnClickListener(this);
return true;
}
@@ -282,7 +269,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
public void clear() {
mSuggestionsStrip.removeAllViews();
removeAllDebugInfoViews();
- mStripVisibilityGroup.showSuggestionsStrip(false /* enableVoiceKey */);
+ mStripVisibilityGroup.showSuggestionsStrip();
dismissMoreSuggestionsPanel();
}
@@ -441,6 +428,8 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
@Override
public void onClick(final View view) {
+ AudioAndHapticFeedbackManager.getInstance().performHapticAndAudioFeedback(
+ Constants.CODE_UNSPECIFIED, this);
if (view == mImportantNoticeStrip) {
mListener.showImportantNoticeContents();
return;
@@ -484,7 +473,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
// Called by the framework when the size is known. Show the important notice if applicable.
// This may be overriden by showing suggestions later, if applicable.
if (oldw <= 0 && w > 0) {
- maybeShowImportantNoticeTitle(Settings.getInstance().getCurrent().mInputAttributes);
+ maybeShowImportantNoticeTitle();
}
}
}
diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java
index 21426d1eb..eda81940f 100644
--- a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java
+++ b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java
@@ -167,7 +167,9 @@ public class UserDictionaryAddWordContents {
// should not insert, because either A. the word exists with no shortcut, in which
// case the exact same thing we want to insert is already there, or B. the word
// exists with at least one shortcut, in which case it has priority on our word.
- if (hasWord(newWord, context)) return CODE_ALREADY_PRESENT;
+ if (TextUtils.isEmpty(newShortcut) && hasWord(newWord, context)) {
+ return CODE_ALREADY_PRESENT;
+ }
// Disallow duplicates. If the same word with no shortcut is defined, remove it; if
// the same word with the same shortcut is defined, remove it; but we don't mind if
@@ -256,7 +258,7 @@ public class UserDictionaryAddWordContents {
// The system locale should be inside. We want it at the 2nd spot.
locales.remove(systemLocale); // system locale may not be null
locales.remove(""); // Remove the empty string if it's there
- final ArrayList<LocaleRenderer> localesList = new ArrayList<LocaleRenderer>();
+ final ArrayList<LocaleRenderer> localesList = new ArrayList<>();
// Add the passed locale, then the system locale at the top of the list. Add an
// "all languages" entry at the bottom of the list.
addLocaleDisplayNameToList(activity, localesList, mLocale);
diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordFragment.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordFragment.java
index 4fc132f68..163443036 100644
--- a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordFragment.java
+++ b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordFragment.java
@@ -134,8 +134,8 @@ public class UserDictionaryAddWordFragment extends Fragment
final Spinner localeSpinner =
(Spinner)mRootView.findViewById(R.id.user_dictionary_add_locale);
- final ArrayAdapter<LocaleRenderer> adapter = new ArrayAdapter<LocaleRenderer>(getActivity(),
- android.R.layout.simple_spinner_item, localesList);
+ final ArrayAdapter<LocaleRenderer> adapter = new ArrayAdapter<>(
+ getActivity(), android.R.layout.simple_spinner_item, localesList);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
localeSpinner.setAdapter(adapter);
localeSpinner.setOnItemSelectedListener(this);
diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryList.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryList.java
index 97a924d7b..624783a70 100644
--- a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryList.java
+++ b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryList.java
@@ -56,7 +56,7 @@ public class UserDictionaryList extends PreferenceFragment {
final Cursor cursor = activity.getContentResolver().query(UserDictionary.Words.CONTENT_URI,
new String[] { UserDictionary.Words.LOCALE },
null, null, null);
- final TreeSet<String> localeSet = new TreeSet<String>();
+ final TreeSet<String> localeSet = new TreeSet<>();
if (null == cursor) {
// The user dictionary service is not present or disabled. Return null.
return null;
diff --git a/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java b/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java
index 2bb30a2ba..3ca7c7e1c 100644
--- a/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java
@@ -95,8 +95,7 @@ public final class AdditionalSubtypeUtils {
return EMPTY_SUBTYPE_ARRAY;
}
final String[] prefSubtypeArray = prefSubtypes.split(PREF_SUBTYPE_SEPARATOR);
- final ArrayList<InputMethodSubtype> subtypesList =
- CollectionUtils.newArrayList(prefSubtypeArray.length);
+ final ArrayList<InputMethodSubtype> subtypesList = new ArrayList<>(prefSubtypeArray.length);
for (final String prefSubtype : prefSubtypeArray) {
final String elems[] = prefSubtype.split(LOCALE_AND_LAYOUT_SEPARATOR);
if (elems.length != LENGTH_WITHOUT_EXTRA_VALUE
diff --git a/java/src/com/android/inputmethod/latin/utils/AutoCorrectionUtils.java b/java/src/com/android/inputmethod/latin/utils/AutoCorrectionUtils.java
index 22b9b77d2..34ee2152a 100644
--- a/java/src/com/android/inputmethod/latin/utils/AutoCorrectionUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/AutoCorrectionUtils.java
@@ -16,12 +16,11 @@
package com.android.inputmethod.latin.utils;
-import com.android.inputmethod.latin.BinaryDictionary;
+import android.util.Log;
+
import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
-import android.util.Log;
-
public final class AutoCorrectionUtils {
private static final boolean DBG = LatinImeLogger.sDBG;
private static final String TAG = AutoCorrectionUtils.class.getSimpleName();
@@ -36,7 +35,9 @@ public final class AutoCorrectionUtils {
final float autoCorrectionThreshold) {
if (null != suggestion) {
// Shortlist a whitelisted word
- if (suggestion.mKind == SuggestedWordInfo.KIND_WHITELIST) return true;
+ if (suggestion.isKindOf(SuggestedWordInfo.KIND_WHITELIST)) {
+ return true;
+ }
final int autoCorrectionSuggestionScore = suggestion.mScore;
// TODO: when the normalized score of the first suggestion is nearly equals to
// the normalized score of the second suggestion, behave less aggressive.
diff --git a/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java b/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java
index 702688f93..936219332 100644
--- a/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java
@@ -62,6 +62,22 @@ public final class CapsModeUtils {
}
/**
+ * Helper method to find out if a code point is starting punctuation.
+ *
+ * This include the Unicode START_PUNCTUATION category, but also some other symbols that are
+ * starting, like the inverted question mark or the double quote.
+ *
+ * @param codePoint the code point
+ * @return true if it's starting punctuation, false otherwise.
+ */
+ private static boolean isStartPunctuation(final int codePoint) {
+ return (codePoint == Constants.CODE_DOUBLE_QUOTE || codePoint == Constants.CODE_SINGLE_QUOTE
+ || codePoint == Constants.CODE_INVERTED_QUESTION_MARK
+ || codePoint == Constants.CODE_INVERTED_EXCLAMATION_MARK
+ || Character.getType(codePoint) == Character.START_PUNCTUATION);
+ }
+
+ /**
* Determine what caps mode should be in effect at the current offset in
* the text. Only the mode bits set in <var>reqModes</var> will be
* checked. Note that the caps mode flags here are explicitly defined
@@ -115,8 +131,7 @@ public final class CapsModeUtils {
} else {
for (i = cs.length(); i > 0; i--) {
final char c = cs.charAt(i - 1);
- if (c != Constants.CODE_DOUBLE_QUOTE && c != Constants.CODE_SINGLE_QUOTE
- && Character.getType(c) != Character.START_PUNCTUATION) {
+ if (!isStartPunctuation(c)) {
break;
}
}
@@ -210,11 +225,14 @@ public final class CapsModeUtils {
// We found out that we have a period. We need to determine if this is a full stop or
// otherwise sentence-ending period, or an abbreviation like "e.g.". An abbreviation
- // looks like (\w\.){2,}
+ // looks like (\w\.){2,}. Moreover, in German, you put periods after digits for dates
+ // and some other things, and in German specifically we need to not go into autocaps after
+ // a whitespace-digits-period sequence.
// To find out, we will have a simple state machine with the following states :
- // START, WORD, PERIOD, ABBREVIATION
+ // START, WORD, PERIOD, ABBREVIATION, NUMBER
// On START : (just before the first period)
// letter => WORD
+ // digit => NUMBER if German; end with caps otherwise
// whitespace => end with no caps (it was a stand-alone period)
// otherwise => end with caps (several periods/symbols in a row)
// On WORD : (within the word just before the first period)
@@ -228,6 +246,11 @@ public final class CapsModeUtils {
// letter => LETTER
// period => PERIOD
// otherwise => end with no caps (it was an abbreviation)
+ // On NUMBER : (period immediately preceded by one or more digits)
+ // digit => NUMBER
+ // letter => LETTER (promote to word)
+ // otherwise => end with no caps (it was a whitespace-digits-period sequence,
+ // or a punctuation-digits-period sequence like "11.11.")
// "Not an abbreviation" in the above chart essentially covers cases like "...yes.". This
// should capitalize.
@@ -235,6 +258,7 @@ public final class CapsModeUtils {
final int WORD = 1;
final int PERIOD = 2;
final int LETTER = 3;
+ final int NUMBER = 4;
final int caps = (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS
| TextUtils.CAP_MODE_SENTENCES) & reqModes;
final int noCaps = (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS) & reqModes;
@@ -247,6 +271,8 @@ public final class CapsModeUtils {
state = WORD;
} else if (Character.isWhitespace(c)) {
return noCaps;
+ } else if (Character.isDigit(c) && spacingAndPunctuations.mUsesGermanRules) {
+ state = NUMBER;
} else {
return caps;
}
@@ -275,6 +301,15 @@ public final class CapsModeUtils {
} else {
return noCaps;
}
+ break;
+ case NUMBER:
+ if (Character.isLetter(c)) {
+ state = WORD;
+ } else if (Character.isDigit(c)) {
+ state = NUMBER;
+ } else {
+ return noCaps;
+ }
}
}
// Here we arrived at the start of the line. This should behave exactly like whitespace.
diff --git a/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java b/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java
index bbfa0f091..e3aef29ba 100644
--- a/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java
@@ -16,93 +16,21 @@
package com.android.inputmethod.latin.utils;
-import android.util.SparseArray;
-
-import java.util.ArrayDeque;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
import java.util.Map;
import java.util.TreeMap;
-import java.util.TreeSet;
-import java.util.WeakHashMap;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.CopyOnWriteArrayList;
public final class CollectionUtils {
private CollectionUtils() {
// This utility class is not publicly instantiable.
}
- public static <K,V> HashMap<K,V> newHashMap() {
- return new HashMap<K,V>();
- }
-
- public static <K, V> WeakHashMap<K, V> newWeakHashMap() {
- return new WeakHashMap<K, V>();
- }
-
- public static <K,V> TreeMap<K,V> newTreeMap() {
- return new TreeMap<K,V>();
- }
-
public static <K, V> Map<K,V> newSynchronizedTreeMap() {
- final TreeMap<K,V> treeMap = newTreeMap();
+ final TreeMap<K,V> treeMap = new TreeMap<>();
return Collections.synchronizedMap(treeMap);
}
- public static <K,V> ConcurrentHashMap<K,V> newConcurrentHashMap() {
- return new ConcurrentHashMap<K,V>();
- }
-
- public static <E> HashSet<E> newHashSet() {
- return new HashSet<E>();
- }
-
- public static <E> TreeSet<E> newTreeSet() {
- return new TreeSet<E>();
- }
-
- public static <E> ArrayList<E> newArrayList() {
- return new ArrayList<E>();
- }
-
- public static <E> ArrayList<E> newArrayList(final int initialCapacity) {
- return new ArrayList<E>(initialCapacity);
- }
-
- public static <E> ArrayList<E> newArrayList(final Collection<E> collection) {
- return new ArrayList<E>(collection);
- }
-
- public static <E> LinkedList<E> newLinkedList() {
- return new LinkedList<E>();
- }
-
- public static <E> CopyOnWriteArrayList<E> newCopyOnWriteArrayList() {
- return new CopyOnWriteArrayList<E>();
- }
-
- public static <E> CopyOnWriteArrayList<E> newCopyOnWriteArrayList(
- final Collection<E> collection) {
- return new CopyOnWriteArrayList<E>(collection);
- }
-
- public static <E> CopyOnWriteArrayList<E> newCopyOnWriteArrayList(final E[] array) {
- return new CopyOnWriteArrayList<E>(array);
- }
-
- public static <E> ArrayDeque<E> newArrayDeque() {
- return new ArrayDeque<E>();
- }
-
- public static <E> SparseArray<E> newSparseArray() {
- return new SparseArray<E>();
- }
-
public static <E> ArrayList<E> arrayAsList(final E[] array, final int start, final int end) {
if (array == null) {
throw new NullPointerException();
@@ -111,7 +39,7 @@ public final class CollectionUtils {
throw new IllegalArgumentException();
}
- final ArrayList<E> list = newArrayList(end - start);
+ final ArrayList<E> list = new ArrayList<>(end - start);
for (int i = start; i < end; i++) {
list.add(array[i]);
}
diff --git a/java/src/com/android/inputmethod/latin/utils/CsvUtils.java b/java/src/com/android/inputmethod/latin/utils/CsvUtils.java
index b18a1d83b..a21a1373b 100644
--- a/java/src/com/android/inputmethod/latin/utils/CsvUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/CsvUtils.java
@@ -209,7 +209,7 @@ public final class CsvUtils {
@UsedForTesting
public static String[] split(final int splitFlags, final String line) throws CsvParseException {
final boolean trimSpaces = (splitFlags & SPLIT_FLAGS_TRIM_SPACES) != 0;
- final ArrayList<String> fields = CollectionUtils.newArrayList();
+ final ArrayList<String> fields = new ArrayList<>();
final int length = line.length();
int start = 0;
do {
diff --git a/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java b/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java
index 315913e2f..d76ea10c0 100644
--- a/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java
@@ -336,7 +336,7 @@ public class DictionaryInfoUtils {
public static ArrayList<DictionaryInfo> getCurrentDictionaryFileNameAndVersionInfo(
final Context context) {
- final ArrayList<DictionaryInfo> dictList = CollectionUtils.newArrayList();
+ final ArrayList<DictionaryInfo> dictList = new ArrayList<>();
// Retrieve downloaded dictionaries
final File[] directoryList = getCachedDirectoryList(context);
diff --git a/java/src/com/android/inputmethod/latin/utils/DistracterFilter.java b/java/src/com/android/inputmethod/latin/utils/DistracterFilter.java
index f1057da0b..787e4a59d 100644
--- a/java/src/com/android/inputmethod/latin/utils/DistracterFilter.java
+++ b/java/src/com/android/inputmethod/latin/utils/DistracterFilter.java
@@ -16,129 +16,14 @@
package com.android.inputmethod.latin.utils;
-import java.util.ArrayList;
-import java.util.HashMap;
import java.util.List;
import java.util.Locale;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-import android.content.Context;
-import android.content.res.Resources;
-import android.text.InputType;
-import android.util.Log;
-import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodSubtype;
-import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.keyboard.KeyboardId;
-import com.android.inputmethod.keyboard.KeyboardLayoutSet;
-import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.PrevWordsInfo;
-import com.android.inputmethod.latin.Suggest;
-import com.android.inputmethod.latin.Suggest.OnGetSuggestedWordsCallback;
-import com.android.inputmethod.latin.SuggestedWords;
-import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
-import com.android.inputmethod.latin.WordComposer;
-
-/**
- * This class is used to prevent distracters being added to personalization
- * or user history dictionaries
- */
-public class DistracterFilter {
- private static final String TAG = DistracterFilter.class.getSimpleName();
-
- private static final long TIMEOUT_TO_WAIT_LOADING_DICTIONARIES_IN_SECONDS = 120;
-
- private final Context mContext;
- private final Map<Locale, InputMethodSubtype> mLocaleToSubtypeMap;
- private final Map<Locale, Keyboard> mLocaleToKeyboardMap;
- private final Suggest mSuggest;
- private Keyboard mKeyboard;
-
- // If the score of the top suggestion exceeds this value, the tested word (e.g.,
- // an OOV, a misspelling, or an in-vocabulary word) would be considered as a distracter to
- // words in dictionary. The greater the threshold is, the less likely the tested word would
- // become a distracter, which means the tested word will be more likely to be added to
- // the dictionary.
- private static final float DISTRACTER_WORD_SCORE_THRESHOLD = 2.0f;
-
- // Create empty distracter filter.
- public DistracterFilter() {
- this(null, new ArrayList<InputMethodSubtype>());
- }
-
- /**
- * Create a DistracterFilter instance.
- *
- * @param context the context.
- * @param enabledSubtypes the enabled subtypes.
- */
- public DistracterFilter(final Context context, final List<InputMethodSubtype> enabledSubtypes) {
- mContext = context;
- mLocaleToSubtypeMap = new HashMap<>();
- if (enabledSubtypes != null) {
- for (final InputMethodSubtype subtype : enabledSubtypes) {
- final Locale locale = SubtypeLocaleUtils.getSubtypeLocale(subtype);
- if (mLocaleToSubtypeMap.containsKey(locale)) {
- // Multiple subtypes are enabled for one locale.
- // TODO: Investigate what we should do for this case.
- continue;
- }
- mLocaleToSubtypeMap.put(locale, subtype);
- }
- }
- mLocaleToKeyboardMap = new HashMap<>();
- mSuggest = new Suggest();
- mKeyboard = null;
- }
-
- private static boolean suggestionExceedsDistracterThreshold(
- final SuggestedWordInfo suggestion, final String consideredWord,
- final float distracterThreshold) {
- if (null != suggestion) {
- final int suggestionScore = suggestion.mScore;
- final float normalizedScore = BinaryDictionaryUtils.calcNormalizedScore(
- consideredWord, suggestion.mWord, suggestionScore);
- if (normalizedScore > distracterThreshold) {
- return true;
- }
- }
- return false;
- }
-
- private void loadKeyboardForLocale(final Locale newLocale) {
- final Keyboard cachedKeyboard = mLocaleToKeyboardMap.get(newLocale);
- if (cachedKeyboard != null) {
- mKeyboard = cachedKeyboard;
- return;
- }
- final InputMethodSubtype subtype = mLocaleToSubtypeMap.get(newLocale);
- if (subtype == null) {
- return;
- }
- final EditorInfo editorInfo = new EditorInfo();
- editorInfo.inputType = InputType.TYPE_CLASS_TEXT;
- final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder(
- mContext, editorInfo);
- final Resources res = mContext.getResources();
- final int keyboardWidth = ResourceUtils.getDefaultKeyboardWidth(res);
- final int keyboardHeight = ResourceUtils.getDefaultKeyboardHeight(res);
- builder.setKeyboardGeometry(keyboardWidth, keyboardHeight);
- builder.setSubtype(subtype);
- builder.setIsSpellChecker(false /* isSpellChecker */);
- final KeyboardLayoutSet layoutSet = builder.build();
- mKeyboard = layoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET);
- }
-
- private void loadDictionariesForLocale(final Locale newlocale) throws InterruptedException {
- mSuggest.mDictionaryFacilitator.resetDictionaries(mContext, newlocale,
- false /* useContactsDict */, false /* usePersonalizedDicts */,
- false /* forceReloadMainDictionary */, null /* listener */);
- mSuggest.mDictionaryFacilitator.waitForLoadingMainDictionary(
- TIMEOUT_TO_WAIT_LOADING_DICTIONARIES_IN_SECONDS, TimeUnit.SECONDS);
- }
+public interface DistracterFilter {
/**
* Determine whether a word is a distracter to words in dictionaries.
*
@@ -149,56 +34,25 @@ public class DistracterFilter {
* @return true if testedWord is a distracter, otherwise false.
*/
public boolean isDistracterToWordsInDictionaries(final PrevWordsInfo prevWordsInfo,
- final String testedWord, final Locale locale) {
- if (locale == null) {
- return false;
- }
- if (!locale.equals(mSuggest.mDictionaryFacilitator.getLocale())) {
- if (!mLocaleToSubtypeMap.containsKey(locale)) {
- Log.e(TAG, "Locale " + locale + " is not enabled.");
- // TODO: Investigate what we should do for disabled locales.
- return false;
- }
- loadKeyboardForLocale(locale);
- // Reset dictionaries for the locale.
- try {
- loadDictionariesForLocale(locale);
- } catch (final InterruptedException e) {
- Log.e(TAG, "Interrupted while waiting for loading dicts in DistracterFilter", e);
- return false;
- }
- }
- if (mKeyboard == null) {
+ final String testedWord, final Locale locale);
+
+ public void updateEnabledSubtypes(final List<InputMethodSubtype> enabledSubtypes);
+
+ public void close();
+
+ public static final DistracterFilter EMPTY_DISTRACTER_FILTER = new DistracterFilter() {
+ @Override
+ public boolean isDistracterToWordsInDictionaries(PrevWordsInfo prevWordsInfo,
+ String testedWord, Locale locale) {
return false;
}
- final WordComposer composer = new WordComposer();
- final int[] codePoints = StringUtils.toCodePointArray(testedWord);
- final int[] coordinates = mKeyboard.getCoordinates(codePoints);
- composer.setComposingWord(codePoints, coordinates, prevWordsInfo);
- final int trailingSingleQuotesCount = StringUtils.getTrailingSingleQuotesCount(testedWord);
- final String consideredWord = trailingSingleQuotesCount > 0 ?
- testedWord.substring(0, testedWord.length() - trailingSingleQuotesCount) :
- testedWord;
- final AsyncResultHolder<Boolean> holder = new AsyncResultHolder<Boolean>();
- final OnGetSuggestedWordsCallback callback = new OnGetSuggestedWordsCallback() {
- @Override
- public void onGetSuggestedWords(final SuggestedWords suggestedWords) {
- if (suggestedWords != null && suggestedWords.size() > 1) {
- // The suggestedWordInfo at 0 is the typed word. The 1st suggestion from
- // the decoder is at index 1.
- final SuggestedWordInfo firstSuggestion = suggestedWords.getInfo(1);
- final boolean hasStrongDistractor = suggestionExceedsDistracterThreshold(
- firstSuggestion, consideredWord, DISTRACTER_WORD_SCORE_THRESHOLD);
- holder.set(hasStrongDistractor);
- }
- }
- };
- mSuggest.getSuggestedWords(composer, prevWordsInfo, mKeyboard.getProximityInfo(),
- true /* blockOffensiveWords */, true /* isCorrectionEnbaled */,
- null /* additionalFeaturesOptions */, 0 /* sessionId */,
- SuggestedWords.NOT_A_SEQUENCE_NUMBER, callback);
+ @Override
+ public void close() {
+ }
- return holder.get(false /* defaultValue */, Constants.GET_SUGGESTED_WORDS_TIMEOUT);
- }
+ @Override
+ public void updateEnabledSubtypes(List<InputMethodSubtype> enabledSubtypes) {
+ }
+ };
}
diff --git a/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatches.java b/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatches.java
new file mode 100644
index 000000000..0ee6236b1
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatches.java
@@ -0,0 +1,129 @@
+/*
+ * 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 java.util.List;
+import java.util.Locale;
+import java.util.concurrent.TimeUnit;
+
+import android.content.Context;
+import android.util.Log;
+import android.util.LruCache;
+import android.view.inputmethod.InputMethodSubtype;
+
+import com.android.inputmethod.latin.DictionaryFacilitator;
+import com.android.inputmethod.latin.PrevWordsInfo;
+
+/**
+ * This class is used to prevent distracters being added to personalization
+ * or user history dictionaries
+ */
+public class DistracterFilterCheckingExactMatches implements DistracterFilter {
+ private static final String TAG = DistracterFilterCheckingExactMatches.class.getSimpleName();
+ private static final boolean DEBUG = false;
+
+ private static final long TIMEOUT_TO_WAIT_LOADING_DICTIONARIES_IN_SECONDS = 120;
+ private static final int MAX_DISTRACTERS_CACHE_SIZE = 512;
+
+ private final Context mContext;
+ private final DictionaryFacilitator mDictionaryFacilitator;
+ private final LruCache<String, Boolean> mDistractersCache;
+ private final Object mLock = new Object();
+
+ /**
+ * Create a DistracterFilter instance.
+ *
+ * @param context the context.
+ */
+ public DistracterFilterCheckingExactMatches(final Context context) {
+ mContext = context;
+ mDictionaryFacilitator = new DictionaryFacilitator();
+ mDistractersCache = new LruCache<>(MAX_DISTRACTERS_CACHE_SIZE);
+ }
+
+ @Override
+ public void close() {
+ mDictionaryFacilitator.closeDictionaries();
+ }
+
+ @Override
+ public void updateEnabledSubtypes(final List<InputMethodSubtype> enabledSubtypes) {
+ }
+
+ private void loadDictionariesForLocale(final Locale newlocale) throws InterruptedException {
+ mDictionaryFacilitator.resetDictionaries(mContext, newlocale,
+ false /* useContactsDict */, false /* usePersonalizedDicts */,
+ false /* forceReloadMainDictionary */, null /* listener */);
+ mDictionaryFacilitator.waitForLoadingMainDictionary(
+ TIMEOUT_TO_WAIT_LOADING_DICTIONARIES_IN_SECONDS, TimeUnit.SECONDS);
+ }
+
+ /**
+ * Determine whether a word is a distracter to words in dictionaries.
+ *
+ * @param prevWordsInfo the information of previous words. Not used for now.
+ * @param testedWord the word that will be tested to see whether it is a distracter to words
+ * in dictionaries.
+ * @param locale the locale of word.
+ * @return true if testedWord is a distracter, otherwise false.
+ */
+ @Override
+ public boolean isDistracterToWordsInDictionaries(final PrevWordsInfo prevWordsInfo,
+ final String testedWord, final Locale locale) {
+ if (locale == null) {
+ return false;
+ }
+ if (!locale.equals(mDictionaryFacilitator.getLocale())) {
+ synchronized (mLock) {
+ // Reset dictionaries for the locale.
+ try {
+ mDistractersCache.evictAll();
+ loadDictionariesForLocale(locale);
+ } catch (final InterruptedException e) {
+ Log.e(TAG, "Interrupted while waiting for loading dicts in DistracterFilter",
+ e);
+ return false;
+ }
+ }
+ }
+
+ final Boolean isCachedDistracter = mDistractersCache.get(testedWord);
+ if (isCachedDistracter != null && isCachedDistracter) {
+ if (DEBUG) {
+ Log.d(TAG, "testedWord: " + testedWord);
+ Log.d(TAG, "isDistracter: true (cache hit)");
+ }
+ return true;
+ }
+ // The tested word is a distracter when there is a word that is exact matched to the tested
+ // word and its probability is higher than the tested word's probability.
+ final int perfectMatchFreq = mDictionaryFacilitator.getFrequency(testedWord);
+ final int exactMatchFreq = mDictionaryFacilitator.getMaxFrequencyOfExactMatches(testedWord);
+ final boolean isDistracter = perfectMatchFreq < exactMatchFreq;
+ if (DEBUG) {
+ Log.d(TAG, "testedWord: " + testedWord);
+ Log.d(TAG, "perfectMatchFreq: " + perfectMatchFreq);
+ Log.d(TAG, "exactMatchFreq: " + exactMatchFreq);
+ Log.d(TAG, "isDistracter: " + isDistracter);
+ }
+ if (isDistracter) {
+ // Add the word to the cache.
+ mDistractersCache.put(testedWord, Boolean.TRUE);
+ }
+ return isDistracter;
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingIsInDictionary.java b/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingIsInDictionary.java
new file mode 100644
index 000000000..4ad4ba784
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingIsInDictionary.java
@@ -0,0 +1,59 @@
+/*
+ * 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 java.util.List;
+import java.util.Locale;
+
+import android.view.inputmethod.InputMethodSubtype;
+
+import com.android.inputmethod.latin.Dictionary;
+import com.android.inputmethod.latin.PrevWordsInfo;
+
+public class DistracterFilterCheckingIsInDictionary implements DistracterFilter {
+ private final DistracterFilter mDistracterFilter;
+ private final Dictionary mDictionary;
+
+ public DistracterFilterCheckingIsInDictionary(final DistracterFilter distracterFilter,
+ final Dictionary dictionary) {
+ mDistracterFilter = distracterFilter;
+ mDictionary = dictionary;
+ }
+
+ @Override
+ public boolean isDistracterToWordsInDictionaries(PrevWordsInfo prevWordsInfo,
+ String testedWord, Locale locale) {
+ if (mDictionary.isInDictionary(testedWord)) {
+ // This filter treats entries that are already in the dictionary as non-distracters
+ // because they have passed the filtering in the past.
+ return false;
+ } else {
+ return mDistracterFilter.isDistracterToWordsInDictionaries(
+ prevWordsInfo, testedWord, locale);
+ }
+ }
+
+ @Override
+ public void updateEnabledSubtypes(List<InputMethodSubtype> enabledSubtypes) {
+ // Do nothing.
+ }
+
+ @Override
+ public void close() {
+ // Do nothing.
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/utils/ExecutorUtils.java b/java/src/com/android/inputmethod/latin/utils/ExecutorUtils.java
index ed502ed3d..61da1b789 100644
--- a/java/src/com/android/inputmethod/latin/utils/ExecutorUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/ExecutorUtils.java
@@ -19,22 +19,42 @@ package com.android.inputmethod.latin.utils;
import com.android.inputmethod.annotations.UsedForTesting;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
/**
* Utilities to manage executors.
*/
public class ExecutorUtils {
- private static final ConcurrentHashMap<String, PrioritizedSerialExecutor>
- sExecutorMap = CollectionUtils.newConcurrentHashMap();
+ private static final ConcurrentHashMap<String, ExecutorService> sExecutorMap =
+ new ConcurrentHashMap<>();
+
+ private static class ThreadFactoryWithId implements ThreadFactory {
+ private final String mId;
+
+ public ThreadFactoryWithId(final String id) {
+ mId = id;
+ }
+
+ @Override
+ public Thread newThread(final Runnable r) {
+ return new Thread(r, "Executor - " + mId);
+ }
+ }
+
/**
- * Gets the executor for the given dictionary name.
+ * Gets the executor for the given id.
*/
- public static PrioritizedSerialExecutor getExecutor(final String dictName) {
- PrioritizedSerialExecutor executor = sExecutorMap.get(dictName);
+ public static ExecutorService getExecutor(final String id) {
+ ExecutorService executor = sExecutorMap.get(id);
if (executor == null) {
synchronized(sExecutorMap) {
- executor = new PrioritizedSerialExecutor();
- sExecutorMap.put(dictName, executor);
+ executor = sExecutorMap.get(id);
+ if (executor == null) {
+ executor = Executors.newSingleThreadExecutor(new ThreadFactoryWithId(id));
+ sExecutorMap.put(id, executor);
+ }
}
}
return executor;
@@ -46,7 +66,7 @@ public class ExecutorUtils {
@UsedForTesting
public static void shutdownAllExecutors() {
synchronized(sExecutorMap) {
- for (final PrioritizedSerialExecutor executor : sExecutorMap.values()) {
+ for (final ExecutorService executor : sExecutorMap.values()) {
executor.execute(new Runnable() {
@Override
public void run() {
diff --git a/java/src/com/android/inputmethod/latin/utils/FragmentUtils.java b/java/src/com/android/inputmethod/latin/utils/FragmentUtils.java
index ee2b97b2a..e300bd1d3 100644
--- a/java/src/com/android/inputmethod/latin/utils/FragmentUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/FragmentUtils.java
@@ -26,12 +26,11 @@ import com.android.inputmethod.latin.userdictionary.UserDictionaryAddWordFragmen
import com.android.inputmethod.latin.userdictionary.UserDictionaryList;
import com.android.inputmethod.latin.userdictionary.UserDictionaryLocalePicker;
import com.android.inputmethod.latin.userdictionary.UserDictionarySettings;
-import com.android.inputmethod.research.FeedbackFragment;
import java.util.HashSet;
public class FragmentUtils {
- private static final HashSet<String> sLatinImeFragments = new HashSet<String>();
+ private static final HashSet<String> sLatinImeFragments = new HashSet<>();
static {
sLatinImeFragments.add(DictionarySettingsFragment.class.getName());
sLatinImeFragments.add(AboutPreferences.class.getName());
@@ -43,7 +42,6 @@ public class FragmentUtils {
sLatinImeFragments.add(UserDictionaryList.class.getName());
sLatinImeFragments.add(UserDictionaryLocalePicker.class.getName());
sLatinImeFragments.add(UserDictionarySettings.class.getName());
- sLatinImeFragments.add(FeedbackFragment.class.getName());
}
public static boolean isValidFragment(String fragmentName) {
diff --git a/java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java b/java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java
index 7d937a9d2..8b7077879 100644
--- a/java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java
@@ -23,7 +23,6 @@ import android.provider.Settings.SettingNotFoundException;
import android.text.TextUtils;
import android.util.Log;
-import com.android.inputmethod.latin.InputAttributes;
import com.android.inputmethod.latin.R;
public final class ImportantNoticeUtils {
@@ -78,14 +77,7 @@ public final class ImportantNoticeUtils {
return getCurrentImportantNoticeVersion(context) > lastVersion;
}
- public static boolean shouldShowImportantNotice(final Context context,
- final InputAttributes inputAttributes) {
- if (inputAttributes == null || inputAttributes.mIsPasswordField) {
- return false;
- }
- if (isInSystemSetupWizard(context)) {
- return false;
- }
+ public static boolean shouldShowImportantNotice(final Context context) {
if (!hasNewImportantNotice(context)) {
return false;
}
@@ -93,6 +85,9 @@ public final class ImportantNoticeUtils {
if (TextUtils.isEmpty(importantNoticeTitle)) {
return false;
}
+ if (isInSystemSetupWizard(context)) {
+ return false;
+ }
return true;
}
diff --git a/java/src/com/android/inputmethod/latin/utils/JsonUtils.java b/java/src/com/android/inputmethod/latin/utils/JsonUtils.java
index 764ef72ce..6dd8d9711 100644
--- a/java/src/com/android/inputmethod/latin/utils/JsonUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/JsonUtils.java
@@ -37,7 +37,7 @@ public final class JsonUtils {
private static final String EMPTY_STRING = "";
public static List<Object> jsonStrToList(final String s) {
- final ArrayList<Object> list = CollectionUtils.newArrayList();
+ final ArrayList<Object> list = new ArrayList<>();
final JsonReader reader = new JsonReader(new StringReader(s));
try {
reader.beginArray();
diff --git a/java/src/com/android/inputmethod/latin/utils/LanguageModelParam.java b/java/src/com/android/inputmethod/latin/utils/LanguageModelParam.java
index aaf4a4064..4248bebf6 100644
--- a/java/src/com/android/inputmethod/latin/utils/LanguageModelParam.java
+++ b/java/src/com/android/inputmethod/latin/utils/LanguageModelParam.java
@@ -19,11 +19,12 @@ 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.DictionaryFacilitator;
import com.android.inputmethod.latin.PrevWordsInfo;
import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
import java.util.ArrayList;
+import java.util.List;
import java.util.Locale;
// Note: this class is used as a parameter type of a native method. You should be careful when you
@@ -79,14 +80,13 @@ public final class LanguageModelParam {
// 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 List<String> tokens, final int timestamp,
+ final DictionaryFacilitator dictionaryFacilitator,
final SpacingAndPunctuations spacingAndPunctuations,
final DistracterFilter distracterFilter) {
- final ArrayList<LanguageModelParam> languageModelParams =
- CollectionUtils.newArrayList();
+ final ArrayList<LanguageModelParam> languageModelParams = new ArrayList<>();
final int N = tokens.size();
- PrevWordsInfo prevWordsInfo = new PrevWordsInfo(null);
+ PrevWordsInfo prevWordsInfo = PrevWordsInfo.EMPTY_PREV_WORDS_INFO;
for (int i = 0; i < N; ++i) {
final String tempWord = tokens.get(i);
if (StringUtils.isEmptyStringOrWhiteSpaces(tempWord)) {
@@ -103,7 +103,7 @@ public final class LanguageModelParam {
+ tempWord + "\"");
}
// Sentence terminator found. Split.
- prevWordsInfo = new PrevWordsInfo(null);
+ prevWordsInfo = PrevWordsInfo.EMPTY_PREV_WORDS_INFO;
continue;
}
if (DEBUG_TOKEN) {
@@ -124,43 +124,33 @@ public final class LanguageModelParam {
private static LanguageModelParam detectWhetherVaildWordOrNotAndGetLanguageModelParam(
final PrevWordsInfo prevWordsInfo, final String targetWord, final int timestamp,
- final DictionaryFacilitatorForSuggest dictionaryFacilitator,
+ final DictionaryFacilitator dictionaryFacilitator,
final DistracterFilter distracterFilter) {
final Locale locale = dictionaryFacilitator.getLocale();
if (locale == null) {
return null;
}
- // TODO: Though targetWord is an IV (in-vocabulary) word, we should still apply
- // distracterFilter in the following code. If targetWord is a distracter,
- // it should be filtered out.
if (dictionaryFacilitator.isValidWord(targetWord, false /* ignoreCase */)) {
return createAndGetLanguageModelParamOfWord(prevWordsInfo, targetWord, timestamp,
- true /* isValidWord */, locale);
+ true /* isValidWord */, locale, distracterFilter);
}
final String lowerCaseTargetWord = targetWord.toLowerCase(locale);
if (dictionaryFacilitator.isValidWord(lowerCaseTargetWord, false /* ignoreCase */)) {
// Add the lower-cased word.
return createAndGetLanguageModelParamOfWord(prevWordsInfo, lowerCaseTargetWord,
- timestamp, true /* isValidWord */, locale);
+ timestamp, true /* isValidWord */, locale, distracterFilter);
}
- // Treat the word as an OOV word. The following statement checks whether this OOV
- // is a distracter to words in dictionaries. Being a distracter means the OOV word is
- // too close to a common word in dictionaries (e.g., the OOV "mot" is very close to "not").
- // Adding such a word to dictonaries would interfere with entering in-dictionary words. For
- // example, adding "mot" to dictionaries might interfere with entering "not".
- // This kind of OOV should be filtered out.
- if (distracterFilter.isDistracterToWordsInDictionaries(prevWordsInfo, targetWord, locale)) {
- return null;
- }
+ // Treat the word as an OOV word.
return createAndGetLanguageModelParamOfWord(prevWordsInfo, targetWord, timestamp,
- false /* isValidWord */, locale);
+ false /* isValidWord */, locale, distracterFilter);
}
private static LanguageModelParam createAndGetLanguageModelParamOfWord(
final PrevWordsInfo prevWordsInfo, final String targetWord, final int timestamp,
- final boolean isValidWord, final Locale locale) {
+ final boolean isValidWord, final Locale locale,
+ final DistracterFilter distracterFilter) {
final String word;
if (StringUtils.getCapitalizationType(targetWord) == StringUtils.CAPITALIZE_FIRST
&& prevWordsInfo.mPrevWord == null && !isValidWord) {
@@ -168,6 +158,13 @@ public final class LanguageModelParam {
} else {
word = targetWord;
}
+ // Check whether the word is a distracter to words in the dictionaries.
+ if (distracterFilter.isDistracterToWordsInDictionaries(prevWordsInfo, word, locale)) {
+ if (DEBUG) {
+ Log.d(TAG, "The word (" + word + ") is a distracter. Skip this word.");
+ }
+ return null;
+ }
final int unigramProbability = isValidWord ?
UNIGRAM_PROBABILITY_FOR_VALID_WORD : UNIGRAM_PROBABILITY_FOR_OOV_WORD;
if (prevWordsInfo.mPrevWord == null) {
diff --git a/java/src/com/android/inputmethod/latin/utils/LatinImeLoggerUtils.java b/java/src/com/android/inputmethod/latin/utils/LatinImeLoggerUtils.java
deleted file mode 100644
index d14ba508b..000000000
--- a/java/src/com/android/inputmethod/latin/utils/LatinImeLoggerUtils.java
+++ /dev/null
@@ -1,77 +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.utils;
-
-import android.text.TextUtils;
-
-import com.android.inputmethod.latin.Constants;
-import com.android.inputmethod.latin.LatinImeLogger;
-import com.android.inputmethod.latin.WordComposer;
-
-public final class LatinImeLoggerUtils {
- private LatinImeLoggerUtils() {
- // This utility class is not publicly instantiable.
- }
-
- public static void onNonSeparator(final char code, final int x, final int y) {
- UserLogRingCharBuffer.getInstance().push(code, x, y);
- LatinImeLogger.logOnInputChar();
- }
-
- public static void onSeparator(final int code, final int x, final int y) {
- // Helper method to log a single code point separator
- // TODO: cache this mapping of a code point to a string in a sparse array in StringUtils
- onSeparator(StringUtils.newSingleCodePointString(code), x, y);
- }
-
- public static void onSeparator(final String separator, final int x, final int y) {
- final int length = separator.length();
- for (int i = 0; i < length; i = Character.offsetByCodePoints(separator, i, 1)) {
- int codePoint = Character.codePointAt(separator, i);
- // TODO: accept code points
- UserLogRingCharBuffer.getInstance().push((char)codePoint, x, y);
- }
- LatinImeLogger.logOnInputSeparator();
- }
-
- public static void onAutoCorrection(final String typedWord, final String correctedWord,
- final String separatorString, final WordComposer wordComposer) {
- final boolean isBatchMode = wordComposer.isBatchMode();
- if (!isBatchMode && TextUtils.isEmpty(typedWord)) {
- return;
- }
- // TODO: this fails when the separator is more than 1 code point long, but
- // the backend can't handle it yet. The only case when this happens is with
- // smileys and other multi-character keys.
- final int codePoint = TextUtils.isEmpty(separatorString) ? Constants.NOT_A_CODE
- : separatorString.codePointAt(0);
- if (!isBatchMode) {
- LatinImeLogger.logOnAutoCorrectionForTyping(typedWord, correctedWord, codePoint);
- } else {
- if (!TextUtils.isEmpty(correctedWord)) {
- // We must make sure that InputPointer contains only the relative timestamps,
- // not actual timestamps.
- LatinImeLogger.logOnAutoCorrectionForGeometric(
- "", correctedWord, codePoint, wordComposer.getInputPointers());
- }
- }
- }
-
- public static void onAutoCorrectionCancellation() {
- LatinImeLogger.logOnAutoCorrectionCancelled();
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/LeakGuardHandlerWrapper.java b/java/src/com/android/inputmethod/latin/utils/LeakGuardHandlerWrapper.java
index 8469c87b0..dd6fac671 100644
--- a/java/src/com/android/inputmethod/latin/utils/LeakGuardHandlerWrapper.java
+++ b/java/src/com/android/inputmethod/latin/utils/LeakGuardHandlerWrapper.java
@@ -33,7 +33,7 @@ public class LeakGuardHandlerWrapper<T> extends Handler {
if (ownerInstance == null) {
throw new NullPointerException("ownerInstance is null");
}
- mOwnerInstanceRef = new WeakReference<T>(ownerInstance);
+ mOwnerInstanceRef = new WeakReference<>(ownerInstance);
}
public T getOwnerInstance() {
diff --git a/java/src/com/android/inputmethod/latin/utils/LocaleUtils.java b/java/src/com/android/inputmethod/latin/utils/LocaleUtils.java
index 0c55484b4..c519a0de6 100644
--- a/java/src/com/android/inputmethod/latin/utils/LocaleUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/LocaleUtils.java
@@ -159,7 +159,7 @@ public final class LocaleUtils {
return LOCALE_MATCH <= level;
}
- private static final HashMap<String, Locale> sLocaleCache = CollectionUtils.newHashMap();
+ private static final HashMap<String, Locale> sLocaleCache = new HashMap<>();
/**
* Creates a locale from a string specification.
diff --git a/java/src/com/android/inputmethod/latin/utils/PrioritizedSerialExecutor.java b/java/src/com/android/inputmethod/latin/utils/PrioritizedSerialExecutor.java
deleted file mode 100644
index bf38abc95..000000000
--- a/java/src/com/android/inputmethod/latin/utils/PrioritizedSerialExecutor.java
+++ /dev/null
@@ -1,122 +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.utils;
-
-import com.android.inputmethod.annotations.UsedForTesting;
-
-import java.util.Queue;
-import java.util.concurrent.ArrayBlockingQueue;
-import java.util.concurrent.ConcurrentLinkedQueue;
-import java.util.concurrent.ThreadPoolExecutor;
-import java.util.concurrent.TimeUnit;
-
-/**
- * An object that executes submitted tasks using a thread.
- */
-public class PrioritizedSerialExecutor {
- public static final String TAG = PrioritizedSerialExecutor.class.getSimpleName();
-
- private final Object mLock = new Object();
-
- private final Queue<Runnable> mTasks;
- private final Queue<Runnable> mPrioritizedTasks;
- private boolean mIsShutdown;
- private final ThreadPoolExecutor mThreadPoolExecutor;
-
- // The task which is running now.
- private Runnable mActive;
-
- public PrioritizedSerialExecutor() {
- mTasks = new ConcurrentLinkedQueue<Runnable>();
- mPrioritizedTasks = new ConcurrentLinkedQueue<Runnable>();
- mIsShutdown = false;
- mThreadPoolExecutor = new ThreadPoolExecutor(1 /* corePoolSize */, 1 /* maximumPoolSize */,
- 0 /* keepAliveTime */, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(1));
- }
-
- /**
- * Enqueues the given task into the task queue.
- * @param r the enqueued task
- */
- public void execute(final Runnable r) {
- synchronized(mLock) {
- if (!mIsShutdown) {
- mTasks.offer(new Runnable() {
- @Override
- public void run() {
- try {
- r.run();
- } finally {
- scheduleNext();
- }
- }
- });
- if (mActive == null) {
- scheduleNext();
- }
- }
- }
- }
-
- /**
- * Enqueues the given task into the prioritized task queue.
- * @param r the enqueued task
- */
- @UsedForTesting
- public void executePrioritized(final Runnable r) {
- synchronized(mLock) {
- if (!mIsShutdown) {
- mPrioritizedTasks.offer(new Runnable() {
- @Override
- public void run() {
- try {
- r.run();
- } finally {
- scheduleNext();
- }
- }
- });
- if (mActive == null) {
- scheduleNext();
- }
- }
- }
- }
-
- private boolean fetchNextTasksLocked() {
- mActive = mPrioritizedTasks.poll();
- if (mActive == null) {
- mActive = mTasks.poll();
- }
- return mActive != null;
- }
-
- private void scheduleNext() {
- synchronized(mLock) {
- if (fetchNextTasksLocked()) {
- mThreadPoolExecutor.execute(mActive);
- }
- }
- }
-
- public void shutdown() {
- synchronized(mLock) {
- mIsShutdown = true;
- mThreadPoolExecutor.shutdown();
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/RecapitalizeStatus.java b/java/src/com/android/inputmethod/latin/utils/RecapitalizeStatus.java
index 4521ec531..e3cac97f0 100644
--- a/java/src/com/android/inputmethod/latin/utils/RecapitalizeStatus.java
+++ b/java/src/com/android/inputmethod/latin/utils/RecapitalizeStatus.java
@@ -62,18 +62,22 @@ public class RecapitalizeStatus {
private Locale mLocale;
private int[] mSortedSeparators;
private String mStringAfter;
- private boolean mIsActive;
+ private boolean mIsStarted;
+ private boolean mIsEnabled = true;
private static final int[] EMPTY_STORTED_SEPARATORS = {};
public RecapitalizeStatus() {
// By default, initialize with dummy values that won't match any real recapitalize.
- initialize(-1, -1, "", Locale.getDefault(), EMPTY_STORTED_SEPARATORS);
- deactivate();
+ start(-1, -1, "", Locale.getDefault(), EMPTY_STORTED_SEPARATORS);
+ stop();
}
- public void initialize(final int cursorStart, final int cursorEnd, final String string,
+ public void start(final int cursorStart, final int cursorEnd, final String string,
final Locale locale, final int[] sortedSeparators) {
+ if (!mIsEnabled) {
+ return;
+ }
mCursorStartBefore = cursorStart;
mStringBefore = string;
mCursorStartAfter = cursorStart;
@@ -96,15 +100,27 @@ public class RecapitalizeStatus {
mRotationStyleCurrentIndex = currentMode;
mSkipOriginalMixedCaseMode = true;
}
- mIsActive = true;
+ mIsStarted = true;
+ }
+
+ public void stop() {
+ mIsStarted = false;
+ }
+
+ public boolean isStarted() {
+ return mIsStarted;
+ }
+
+ public void enable() {
+ mIsEnabled = true;
}
- public void deactivate() {
- mIsActive = false;
+ public void disable() {
+ mIsEnabled = false;
}
- public boolean isActive() {
- return mIsActive;
+ public boolean mIsEnabled() {
+ return mIsEnabled;
}
public boolean isSetAt(final int cursorStart, final int cursorEnd) {
diff --git a/java/src/com/android/inputmethod/latin/utils/ResourceUtils.java b/java/src/com/android/inputmethod/latin/utils/ResourceUtils.java
index 49f4929b4..093c5a6c1 100644
--- a/java/src/com/android/inputmethod/latin/utils/ResourceUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/ResourceUtils.java
@@ -41,8 +41,7 @@ public final class ResourceUtils {
// This utility class is not publicly instantiable.
}
- private static final HashMap<String, String> sDeviceOverrideValueMap =
- CollectionUtils.newHashMap();
+ private static final HashMap<String, String> sDeviceOverrideValueMap = new HashMap<>();
private static final String[] BUILD_KEYS_AND_VALUES = {
"HARDWARE", Build.HARDWARE,
@@ -54,8 +53,8 @@ public final class ResourceUtils {
private static final String sBuildKeyValuesDebugString;
static {
- sBuildKeyValues = CollectionUtils.newHashMap();
- final ArrayList<String> keyValuePairs = CollectionUtils.newArrayList();
+ sBuildKeyValues = new HashMap<>();
+ final ArrayList<String> keyValuePairs = new ArrayList<>();
final int keyCount = BUILD_KEYS_AND_VALUES.length / 2;
for (int i = 0; i < keyCount; i++) {
final int index = i * 2;
diff --git a/java/src/com/android/inputmethod/latin/utils/StringUtils.java b/java/src/com/android/inputmethod/latin/utils/StringUtils.java
index 73ac9a573..e4237a7f2 100644
--- a/java/src/com/android/inputmethod/latin/utils/StringUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/StringUtils.java
@@ -28,7 +28,6 @@ import java.util.Arrays;
import java.util.Locale;
public final class StringUtils {
- private static final String TAG = StringUtils.class.getSimpleName();
public static final int CAPITALIZE_NONE = 0; // No caps, or mixed case
public static final int CAPITALIZE_FIRST = 1; // First only
public static final int CAPITALIZE_ALL = 2; // All caps
@@ -110,7 +109,7 @@ public final class StringUtils {
if (!containsInArray(text, elements)) {
return extraValues;
}
- final ArrayList<String> result = CollectionUtils.newArrayList(elements.length - 1);
+ final ArrayList<String> result = new ArrayList<>(elements.length - 1);
for (final String element : elements) {
if (!text.equals(element)) {
result.add(element);
@@ -316,24 +315,6 @@ public final class StringUtils {
return true;
}
- /**
- * Returns true if all code points in text are whitespace, false otherwise. Empty is true.
- */
- // Interestingly enough, U+00A0 NO-BREAK SPACE and U+200B ZERO-WIDTH SPACE are not considered
- // whitespace, while EN SPACE, EM SPACE and IDEOGRAPHIC SPACES are.
- public static boolean containsOnlyWhitespace(final String text) {
- final int length = text.length();
- int i = 0;
- while (i < length) {
- final int codePoint = text.codePointAt(i);
- if (!Character.isWhitespace(codePoint)) {
- return false;
- }
- i += Character.charCount(codePoint);
- }
- return true;
- }
-
public static boolean isIdenticalAfterCapitalizeEachWord(final String text,
final int[] sortedSeparators) {
boolean needsCapsNext = true;
diff --git a/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java b/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java
index 938d27122..351d01400 100644
--- a/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java
@@ -49,17 +49,14 @@ public final class SubtypeLocaleUtils {
private static Resources sResources;
private static String[] sPredefinedKeyboardLayoutSet;
// Keyboard layout to its display name map.
- private static final HashMap<String, String> sKeyboardLayoutToDisplayNameMap =
- CollectionUtils.newHashMap();
+ private static final HashMap<String, String> sKeyboardLayoutToDisplayNameMap = new HashMap<>();
// Keyboard layout to subtype name resource id map.
- private static final HashMap<String, Integer> sKeyboardLayoutToNameIdsMap =
- CollectionUtils.newHashMap();
+ private static final HashMap<String, Integer> sKeyboardLayoutToNameIdsMap = new HashMap<>();
// Exceptional locale to subtype name resource id map.
- private static final HashMap<String, Integer> sExceptionalLocaleToNameIdsMap =
- CollectionUtils.newHashMap();
+ private static final HashMap<String, Integer> sExceptionalLocaleToNameIdsMap = new HashMap<>();
// Exceptional locale to subtype name with layout resource id map.
private static final HashMap<String, Integer> sExceptionalLocaleToWithLayoutNameIdsMap =
- CollectionUtils.newHashMap();
+ new HashMap<>();
private static final String SUBTYPE_NAME_RESOURCE_PREFIX =
"string/subtype_";
private static final String SUBTYPE_NAME_RESOURCE_GENERIC_PREFIX =
@@ -71,7 +68,7 @@ public final class SubtypeLocaleUtils {
// Keyboard layout set name for the subtypes that don't have a keyboardLayoutSet extra value.
// This is for compatibility to keep the same subtype ids as pre-JellyBean.
private static final HashMap<String, String> sLocaleAndExtraValueToKeyboardLayoutSetMap =
- CollectionUtils.newHashMap();
+ new HashMap<>();
private SubtypeLocaleUtils() {
// Intentional empty constructor for utility class.
diff --git a/java/src/com/android/inputmethod/latin/utils/TargetPackageInfoGetterTask.java b/java/src/com/android/inputmethod/latin/utils/TargetPackageInfoGetterTask.java
index 42ea3c959..ab2b00e36 100644
--- a/java/src/com/android/inputmethod/latin/utils/TargetPackageInfoGetterTask.java
+++ b/java/src/com/android/inputmethod/latin/utils/TargetPackageInfoGetterTask.java
@@ -27,8 +27,7 @@ import com.android.inputmethod.compat.AppWorkaroundsUtils;
public final class TargetPackageInfoGetterTask extends
AsyncTask<String, Void, PackageInfo> {
private static final int MAX_CACHE_ENTRIES = 64; // arbitrary
- private static final LruCache<String, PackageInfo> sCache =
- new LruCache<String, PackageInfo>(MAX_CACHE_ENTRIES);
+ private static final LruCache<String, PackageInfo> sCache = new LruCache<>(MAX_CACHE_ENTRIES);
public static PackageInfo getCachedPackageInfo(final String packageName) {
if (null == packageName) return null;
diff --git a/java/src/com/android/inputmethod/latin/utils/TypefaceUtils.java b/java/src/com/android/inputmethod/latin/utils/TypefaceUtils.java
index 087a7f255..fafba79c2 100644
--- a/java/src/com/android/inputmethod/latin/utils/TypefaceUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/TypefaceUtils.java
@@ -30,7 +30,7 @@ public final class TypefaceUtils {
}
// This sparse array caches key label text height in pixel indexed by key label text size.
- private static final SparseArray<Float> sTextHeightCache = CollectionUtils.newSparseArray();
+ private static final SparseArray<Float> sTextHeightCache = new SparseArray<>();
// Working variable for the following method.
private static final Rect sTextHeightBounds = new Rect();
@@ -50,7 +50,7 @@ public final class TypefaceUtils {
}
// This sparse array caches key label text width in pixel indexed by key label text size.
- private static final SparseArray<Float> sTextWidthCache = CollectionUtils.newSparseArray();
+ private static final SparseArray<Float> sTextWidthCache = new SparseArray<>();
// Working variable for the following method.
private static final Rect sTextWidthBounds = new Rect();
diff --git a/java/src/com/android/inputmethod/latin/utils/UsabilityStudyLogUtils.java b/java/src/com/android/inputmethod/latin/utils/UsabilityStudyLogUtils.java
deleted file mode 100644
index 06826dac0..000000000
--- a/java/src/com/android/inputmethod/latin/utils/UsabilityStudyLogUtils.java
+++ /dev/null
@@ -1,293 +0,0 @@
-/*
- * Copyright (C) 2016 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.content.Intent;
-import android.content.pm.PackageManager;
-import android.inputmethodservice.InputMethodService;
-import android.net.Uri;
-import android.os.Environment;
-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;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.nio.channels.FileChannel;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Locale;
-
-public final class UsabilityStudyLogUtils {
- // TODO: remove code duplication with ResearchLog class
- private static final String USABILITY_TAG = UsabilityStudyLogUtils.class.getSimpleName();
- private static final String FILENAME = "log.txt";
- private final Handler mLoggingHandler;
- private File mFile;
- private File mDirectory;
- private InputMethodService mIms;
- private PrintWriter mWriter;
- private final Date mDate;
- private final SimpleDateFormat mDateFormat;
-
- private UsabilityStudyLogUtils() {
- mDate = new Date();
- mDateFormat = new SimpleDateFormat("yyyyMMdd-HHmmss.SSSZ", Locale.US);
-
- HandlerThread handlerThread = new HandlerThread("UsabilityStudyLogUtils logging task",
- Process.THREAD_PRIORITY_BACKGROUND);
- handlerThread.start();
- mLoggingHandler = new Handler(handlerThread.getLooper());
- }
-
- // Initialization-on-demand holder
- private static final class OnDemandInitializationHolder {
- public static final UsabilityStudyLogUtils sInstance = new UsabilityStudyLogUtils();
- }
-
- public static UsabilityStudyLogUtils getInstance() {
- return OnDemandInitializationHolder.sInstance;
- }
-
- public void init(final InputMethodService ims) {
- mIms = ims;
- mDirectory = ims.getFilesDir();
- }
-
- private void createLogFileIfNotExist() {
- if ((mFile == null || !mFile.exists())
- && (mDirectory != null && mDirectory.exists())) {
- try {
- mWriter = getPrintWriter(mDirectory, FILENAME, false);
- } catch (final IOException e) {
- Log.e(USABILITY_TAG, "Can't create log file.");
- }
- }
- }
-
- public static void writeBackSpace(final int x, final int y) {
- UsabilityStudyLogUtils.getInstance().write("<backspace>\t" + x + "\t" + y);
- }
-
- public static void writeChar(final char c, final int x, final int y) {
- String inputChar = String.valueOf(c);
- switch (c) {
- case '\n':
- inputChar = "<enter>";
- break;
- case '\t':
- inputChar = "<tab>";
- break;
- case ' ':
- inputChar = "<space>";
- break;
- }
- UsabilityStudyLogUtils.getInstance().write(inputChar + "\t" + x + "\t" + y);
- 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
- public void run() {
- createLogFileIfNotExist();
- final long currentTime = System.currentTimeMillis();
- mDate.setTime(currentTime);
-
- final String printString = String.format(Locale.US, "%s\t%d\t%s\n",
- mDateFormat.format(mDate), currentTime, log);
- if (LatinImeLogger.sDBG) {
- Log.d(USABILITY_TAG, "Write: " + log);
- }
- mWriter.print(printString);
- }
- });
- }
-
- private synchronized String getBufferedLogs() {
- mWriter.flush();
- final StringBuilder sb = new StringBuilder();
- final BufferedReader br = getBufferedReader();
- String line;
- try {
- while ((line = br.readLine()) != null) {
- sb.append('\n');
- sb.append(line);
- }
- } catch (final IOException e) {
- Log.e(USABILITY_TAG, "Can't read log file.");
- } finally {
- if (LatinImeLogger.sDBG) {
- Log.d(USABILITY_TAG, "Got all buffered logs\n" + sb.toString());
- }
- try {
- br.close();
- } catch (final IOException e) {
- // ignore.
- }
- }
- return sb.toString();
- }
-
- public void emailResearcherLogsAll() {
- mLoggingHandler.post(new Runnable() {
- @Override
- public void run() {
- final Date date = new Date();
- date.setTime(System.currentTimeMillis());
- final String currentDateTimeString =
- new SimpleDateFormat("yyyyMMdd-HHmmssZ", Locale.US).format(date);
- if (mFile == null) {
- Log.w(USABILITY_TAG, "No internal log file found.");
- return;
- }
- if (mIms.checkCallingOrSelfPermission(
- android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
- != PackageManager.PERMISSION_GRANTED) {
- Log.w(USABILITY_TAG, "Doesn't have the permission WRITE_EXTERNAL_STORAGE");
- return;
- }
- mWriter.flush();
- final String destPath = Environment.getExternalStorageDirectory()
- + "/research-" + currentDateTimeString + ".log";
- final File destFile = new File(destPath);
- try {
- final FileInputStream srcStream = new FileInputStream(mFile);
- final FileOutputStream destStream = new FileOutputStream(destFile);
- final FileChannel src = srcStream.getChannel();
- final FileChannel dest = destStream.getChannel();
- src.transferTo(0, src.size(), dest);
- src.close();
- srcStream.close();
- dest.close();
- destStream.close();
- } catch (final FileNotFoundException e1) {
- Log.w(USABILITY_TAG, e1);
- return;
- } catch (final IOException e2) {
- Log.w(USABILITY_TAG, e2);
- return;
- }
- if (!destFile.exists()) {
- Log.w(USABILITY_TAG, "Dest file doesn't exist.");
- return;
- }
- final Intent intent = new Intent(Intent.ACTION_SEND);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- if (LatinImeLogger.sDBG) {
- Log.d(USABILITY_TAG, "Destination file URI is " + destFile.toURI());
- }
- intent.setType("text/plain");
- intent.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://" + destPath));
- intent.putExtra(Intent.EXTRA_SUBJECT,
- "[Research Logs] " + currentDateTimeString);
- mIms.startActivity(intent);
- }
- });
- }
-
- public void printAll() {
- mLoggingHandler.post(new Runnable() {
- @Override
- public void run() {
- mIms.getCurrentInputConnection().commitText(getBufferedLogs(), 0);
- }
- });
- }
-
- public void clearAll() {
- mLoggingHandler.post(new Runnable() {
- @Override
- public void run() {
- if (mFile != null && mFile.exists()) {
- if (LatinImeLogger.sDBG) {
- Log.d(USABILITY_TAG, "Delete log file.");
- }
- mFile.delete();
- mWriter.close();
- }
- }
- });
- }
-
- private BufferedReader getBufferedReader() {
- createLogFileIfNotExist();
- try {
- return new BufferedReader(new FileReader(mFile));
- } catch (final FileNotFoundException e) {
- return null;
- }
- }
-
- private PrintWriter getPrintWriter(final File dir, final String filename,
- final boolean renew) throws IOException {
- mFile = new File(dir, filename);
- if (mFile.exists()) {
- if (renew) {
- mFile.delete();
- }
- }
- return new PrintWriter(new FileOutputStream(mFile), true /* autoFlush */);
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/UserLogRingCharBuffer.java b/java/src/com/android/inputmethod/latin/utils/UserLogRingCharBuffer.java
deleted file mode 100644
index a75d353c9..000000000
--- a/java/src/com/android/inputmethod/latin/utils/UserLogRingCharBuffer.java
+++ /dev/null
@@ -1,137 +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.utils;
-
-import android.inputmethodservice.InputMethodService;
-
-import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.latin.LatinImeLogger;
-import com.android.inputmethod.latin.settings.Settings;
-
-public final class UserLogRingCharBuffer {
- public /* for test */ static final int BUFSIZE = 20;
- public /* for test */ int mLength = 0;
-
- private static UserLogRingCharBuffer sUserLogRingCharBuffer = new UserLogRingCharBuffer();
- private static final char PLACEHOLDER_DELIMITER_CHAR = '\uFFFC';
- private static final int INVALID_COORDINATE = -2;
- private boolean mEnabled = false;
- private int mEnd = 0;
- private char[] mCharBuf = new char[BUFSIZE];
- private int[] mXBuf = new int[BUFSIZE];
- private int[] mYBuf = new int[BUFSIZE];
-
- private UserLogRingCharBuffer() {
- // Intentional empty constructor for singleton.
- }
-
- @UsedForTesting
- public static UserLogRingCharBuffer getInstance() {
- return sUserLogRingCharBuffer;
- }
-
- public static UserLogRingCharBuffer init(final InputMethodService context,
- final boolean enabled, final boolean usabilityStudy) {
- if (!(enabled || usabilityStudy)) {
- return null;
- }
- sUserLogRingCharBuffer.mEnabled = true;
- UsabilityStudyLogUtils.getInstance().init(context);
- return sUserLogRingCharBuffer;
- }
-
- private static int normalize(final int in) {
- int ret = in % BUFSIZE;
- return ret < 0 ? ret + BUFSIZE : ret;
- }
-
- // TODO: accept code points
- @UsedForTesting
- public void push(final char c, final int x, final int y) {
- if (!mEnabled) {
- return;
- }
- if (LatinImeLogger.sUsabilityStudy) {
- UsabilityStudyLogUtils.getInstance().writeChar(c, x, y);
- }
- mCharBuf[mEnd] = c;
- mXBuf[mEnd] = x;
- mYBuf[mEnd] = y;
- mEnd = normalize(mEnd + 1);
- if (mLength < BUFSIZE) {
- ++mLength;
- }
- }
-
- public char pop() {
- if (mLength < 1) {
- return PLACEHOLDER_DELIMITER_CHAR;
- }
- mEnd = normalize(mEnd - 1);
- --mLength;
- return mCharBuf[mEnd];
- }
-
- public char getBackwardNthChar(final int n) {
- if (mLength <= n || n < 0) {
- return PLACEHOLDER_DELIMITER_CHAR;
- }
- return mCharBuf[normalize(mEnd - n - 1)];
- }
-
- public int getPreviousX(final char c, final int back) {
- final int index = normalize(mEnd - 2 - back);
- if (mLength <= back
- || Character.toLowerCase(c) != Character.toLowerCase(mCharBuf[index])) {
- return INVALID_COORDINATE;
- }
- return mXBuf[index];
- }
-
- public int getPreviousY(final char c, final int back) {
- int index = normalize(mEnd - 2 - back);
- if (mLength <= back
- || Character.toLowerCase(c) != Character.toLowerCase(mCharBuf[index])) {
- return INVALID_COORDINATE;
- }
- return mYBuf[index];
- }
-
- public String getLastWord(final int ignoreCharCount) {
- final StringBuilder sb = new StringBuilder();
- int i = ignoreCharCount;
- for (; i < mLength; ++i) {
- final char c = mCharBuf[normalize(mEnd - 1 - i)];
- if (!Settings.getInstance().isWordSeparator(c)) {
- break;
- }
- }
- for (; i < mLength; ++i) {
- char c = mCharBuf[normalize(mEnd - 1 - i)];
- if (!Settings.getInstance().isWordSeparator(c)) {
- sb.append(c);
- } else {
- break;
- }
- }
- return sb.reverse().toString();
- }
-
- public void reset() {
- mLength = 0;
- }
-}