diff options
Diffstat (limited to 'java/src/com/android/inputmethod/latin')
17 files changed, 333 insertions, 274 deletions
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index 543f74fc4..335e52fef 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -28,7 +28,6 @@ import com.android.inputmethod.latin.makedict.FormatSpec; import com.android.inputmethod.latin.makedict.FormatSpec.DictionaryOptions; 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.FileUtils; import com.android.inputmethod.latin.utils.JniUtils; @@ -49,10 +48,6 @@ import java.util.Map; public final class BinaryDictionary extends Dictionary { private static final String TAG = BinaryDictionary.class.getSimpleName(); - // Must be equal to MAX_WORD_LENGTH in native/jni/src/defines.h - private static final int MAX_WORD_LENGTH = Constants.DICTIONARY_MAX_WORD_LENGTH; - // Must be equal to MAX_RESULTS in native/jni/src/defines.h - private static final int MAX_RESULTS = 18; // The cutoff returned by native for auto-commit confidence. // Must be equal to CONFIDENCE_TO_AUTO_COMMIT in native/jni/src/defines.h private static final int CONFIDENCE_TO_AUTO_COMMIT = 1000000; @@ -69,11 +64,12 @@ public final class BinaryDictionary extends Dictionary { public static final int NOT_A_VALID_TIMESTAMP = -1; // Format to get unigram flags from native side via getWordPropertyNative(). - private static final int FORMAT_WORD_PROPERTY_OUTPUT_FLAG_COUNT = 4; + private static final int FORMAT_WORD_PROPERTY_OUTPUT_FLAG_COUNT = 5; private static final int FORMAT_WORD_PROPERTY_IS_NOT_A_WORD_INDEX = 0; private static final int FORMAT_WORD_PROPERTY_IS_BLACKLISTED_INDEX = 1; private static final int FORMAT_WORD_PROPERTY_HAS_BIGRAMS_INDEX = 2; private static final int FORMAT_WORD_PROPERTY_HAS_SHORTCUTS_INDEX = 3; + private static final int FORMAT_WORD_PROPERTY_IS_BEGINNING_OF_SENTENCE_INDEX = 4; // Format to get probability and historical info from native side via getWordPropertyNative(). public static final int FORMAT_WORD_PROPERTY_OUTPUT_PROBABILITY_INFO_COUNT = 4; @@ -88,21 +84,10 @@ public final class BinaryDictionary extends Dictionary { private final Locale mLocale; private final long mDictSize; private final String mDictFilePath; + private final boolean mUseFullEditDistance; private final boolean mIsUpdatable; private boolean mHasUpdated; - private final int[] mInputCodePoints = new int[MAX_WORD_LENGTH]; - private final int[] mOutputSuggestionCount = new int[1]; - private final int[] mOutputCodePoints = new int[MAX_WORD_LENGTH * MAX_RESULTS]; - private final int[] mSpaceIndices = new int[MAX_RESULTS]; - private final int[] mOutputScores = new int[MAX_RESULTS]; - private final int[] mOutputTypes = new int[MAX_RESULTS]; - // Only one result is ever used - private final int[] mOutputAutoCommitFirstWordConfidence = new int[1]; - private final float[] mInputOutputLanguageWeight = new float[1]; - - private final NativeSuggestOptions mNativeSuggestOptions = new NativeSuggestOptions(); - private final SparseArray<DicTraverseSession> mDicTraverseSessions = new SparseArray<>(); // TODO: There should be a way to remove used DicTraverseSession objects from @@ -136,7 +121,7 @@ public final class BinaryDictionary extends Dictionary { mDictFilePath = filename; mIsUpdatable = isUpdatable; mHasUpdated = false; - mNativeSuggestOptions.setUseFullEditDistance(useFullEditDistance); + mUseFullEditDistance = useFullEditDistance; loadDictionary(filename, offset, length, isUpdatable); } @@ -148,7 +133,6 @@ public final class BinaryDictionary extends Dictionary { * @param formatVersion the format version of the dictionary * @param attributeMap the attributes of the dictionary */ - @UsedForTesting public BinaryDictionary(final String filename, final boolean useFullEditDistance, final Locale locale, final String dictType, final long formatVersion, final Map<String, String> attributeMap) { @@ -159,7 +143,7 @@ public final class BinaryDictionary extends Dictionary { // On memory dictionary is always updatable. mIsUpdatable = true; mHasUpdated = false; - mNativeSuggestOptions.setUseFullEditDistance(useFullEditDistance); + mUseFullEditDistance = useFullEditDistance; final String[] keyArray = new String[attributeMap.size()]; final String[] valueArray = new String[attributeMap.size()]; int index = 0; @@ -193,10 +177,12 @@ public final class BinaryDictionary extends Dictionary { 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, - ArrayList<int[]> outShortcutTargets, ArrayList<Integer> outShortcutProbabilities); - private static native int getNextWordNative(long dict, int token, int[] outCodePoints); + boolean isBeginningOfSentence, int[] outCodePoints, boolean[] outFlags, + int[] outProbabilityInfo, ArrayList<int[]> outBigramTargets, + ArrayList<int[]> outBigramProbabilityInfo, ArrayList<int[]> outShortcutTargets, + ArrayList<Integer> outShortcutProbabilities); + private static native int getNextWordNative(long dict, int token, int[] outCodePoints, + boolean[] outIsBeginningOfSentence); 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, @@ -274,8 +260,8 @@ public final class BinaryDictionary extends Dictionary { if (!isValidDictionary()) { return null; } - - Arrays.fill(mInputCodePoints, Constants.NOT_A_CODE); + final DicTraverseSession session = getTraverseSession(sessionId); + Arrays.fill(session.mInputCodePoints, Constants.NOT_A_CODE); // TODO: toLowerCase in the native code final int[] prevWordCodePointArray = (null == prevWordsInfo.mPrevWord) ? null : StringUtils.toCodePointArray(prevWordsInfo.mPrevWord); @@ -284,47 +270,50 @@ public final class BinaryDictionary extends Dictionary { final int inputSize; if (!isGesture) { inputSize = composer.copyCodePointsExceptTrailingSingleQuotesAndReturnCodePointCount( - mInputCodePoints); + session.mInputCodePoints); if (inputSize < 0) { return null; } } else { inputSize = inputPointers.getPointerSize(); } - - mNativeSuggestOptions.setIsGesture(isGesture); - mNativeSuggestOptions.setBlockOffensiveWords(blockOffensiveWords); - mNativeSuggestOptions.setAdditionalFeaturesOptions(additionalFeaturesOptions); + session.mNativeSuggestOptions.setUseFullEditDistance(mUseFullEditDistance); + session.mNativeSuggestOptions.setIsGesture(isGesture); + session.mNativeSuggestOptions.setBlockOffensiveWords(blockOffensiveWords); + session.mNativeSuggestOptions.setAdditionalFeaturesOptions(additionalFeaturesOptions); if (inOutLanguageWeight != null) { - mInputOutputLanguageWeight[0] = inOutLanguageWeight[0]; + session.mInputOutputLanguageWeight[0] = inOutLanguageWeight[0]; } else { - mInputOutputLanguageWeight[0] = Dictionary.NOT_A_LANGUAGE_WEIGHT; + session.mInputOutputLanguageWeight[0] = Dictionary.NOT_A_LANGUAGE_WEIGHT; } // proximityInfo and/or prevWordForBigrams may not be null. getSuggestionsNative(mNativeDict, proximityInfo.getNativeProximityInfo(), getTraverseSession(sessionId).getSession(), inputPointers.getXCoordinates(), inputPointers.getYCoordinates(), inputPointers.getTimes(), - inputPointers.getPointerIds(), mInputCodePoints, inputSize, - mNativeSuggestOptions.getOptions(), prevWordCodePointArray, - prevWordsInfo.mIsBeginningOfSentence, mOutputSuggestionCount, - mOutputCodePoints, mOutputScores, mSpaceIndices, mOutputTypes, - mOutputAutoCommitFirstWordConfidence, mInputOutputLanguageWeight); + inputPointers.getPointerIds(), session.mInputCodePoints, inputSize, + session.mNativeSuggestOptions.getOptions(), prevWordCodePointArray, + prevWordsInfo.mIsBeginningOfSentence, session.mOutputSuggestionCount, + session.mOutputCodePoints, session.mOutputScores, session.mSpaceIndices, + session.mOutputTypes, session.mOutputAutoCommitFirstWordConfidence, + session.mInputOutputLanguageWeight); if (inOutLanguageWeight != null) { - inOutLanguageWeight[0] = mInputOutputLanguageWeight[0]; + inOutLanguageWeight[0] = session.mInputOutputLanguageWeight[0]; } - final int count = mOutputSuggestionCount[0]; + final int count = session.mOutputSuggestionCount[0]; final ArrayList<SuggestedWordInfo> suggestions = new ArrayList<>(); for (int j = 0; j < count; ++j) { - final int start = j * MAX_WORD_LENGTH; + final int start = j * Constants.DICTIONARY_MAX_WORD_LENGTH; int len = 0; - while (len < MAX_WORD_LENGTH && mOutputCodePoints[start + len] != 0) { + while (len < Constants.DICTIONARY_MAX_WORD_LENGTH + && session.mOutputCodePoints[start + len] != 0) { ++len; } if (len > 0) { - suggestions.add(new SuggestedWordInfo(new String(mOutputCodePoints, start, len), - mOutputScores[j], mOutputTypes[j], this /* sourceDict */, - mSpaceIndices[j] /* indexOfTouchPointOfSecondWord */, - mOutputAutoCommitFirstWordConfidence[0])); + suggestions.add(new SuggestedWordInfo( + new String(session.mOutputCodePoints, start, len), + session.mOutputScores[j], session.mOutputTypes[j], this /* sourceDict */, + session.mSpaceIndices[j] /* indexOfTouchPointOfSecondWord */, + session.mOutputAutoCommitFirstWordConfidence[0])); } } return suggestions; @@ -372,12 +361,12 @@ public final class BinaryDictionary extends Dictionary { prevWordsInfo.mIsBeginningOfSentence, codePoints1); } - public WordProperty getWordProperty(final String word) { - if (TextUtils.isEmpty(word)) { + public WordProperty getWordProperty(final String word, final boolean isBeginningOfSentence) { + if (word == null) { return null; } final int[] codePoints = StringUtils.toCodePointArray(word); - final int[] outCodePoints = new int[MAX_WORD_LENGTH]; + final int[] outCodePoints = new int[Constants.DICTIONARY_MAX_WORD_LENGTH]; final boolean[] outFlags = new boolean[FORMAT_WORD_PROPERTY_OUTPUT_FLAG_COUNT]; final int[] outProbabilityInfo = new int[FORMAT_WORD_PROPERTY_OUTPUT_PROBABILITY_INFO_COUNT]; @@ -385,14 +374,15 @@ public final class BinaryDictionary extends Dictionary { 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); + getWordPropertyNative(mNativeDict, codePoints, isBeginningOfSentence, outCodePoints, + outFlags, outProbabilityInfo, outBigramTargets, outBigramProbabilityInfo, + outShortcutTargets, outShortcutProbabilities); return new WordProperty(codePoints, outFlags[FORMAT_WORD_PROPERTY_IS_NOT_A_WORD_INDEX], outFlags[FORMAT_WORD_PROPERTY_IS_BLACKLISTED_INDEX], outFlags[FORMAT_WORD_PROPERTY_HAS_BIGRAMS_INDEX], - outFlags[FORMAT_WORD_PROPERTY_HAS_SHORTCUTS_INDEX], outProbabilityInfo, + outFlags[FORMAT_WORD_PROPERTY_HAS_SHORTCUTS_INDEX], + outFlags[FORMAT_WORD_PROPERTY_IS_BEGINNING_OF_SENTENCE_INDEX], outProbabilityInfo, outBigramTargets, outBigramProbabilityInfo, outShortcutTargets, outShortcutProbabilities); } @@ -412,10 +402,13 @@ public final class BinaryDictionary extends Dictionary { * If token is 0, this method newly starts iterating the dictionary. */ public GetNextWordPropertyResult getNextWordProperty(final int token) { - final int[] codePoints = new int[MAX_WORD_LENGTH]; - final int nextToken = getNextWordNative(mNativeDict, token, codePoints); + final int[] codePoints = new int[Constants.DICTIONARY_MAX_WORD_LENGTH]; + final boolean[] isBeginningOfSentence = new boolean[1]; + final int nextToken = getNextWordNative(mNativeDict, token, codePoints, + isBeginningOfSentence); final String word = StringUtils.getStringFromNullTerminatedCodePointArray(codePoints); - return new GetNextWordPropertyResult(getWordProperty(word), nextToken); + return new GetNextWordPropertyResult( + getWordProperty(word, isBeginningOfSentence[0]), nextToken); } // Add a unigram entry to binary dictionary with unigram attributes in native code. diff --git a/java/src/com/android/inputmethod/latin/DicTraverseSession.java b/java/src/com/android/inputmethod/latin/DicTraverseSession.java index 8d295adee..8bbf426e5 100644 --- a/java/src/com/android/inputmethod/latin/DicTraverseSession.java +++ b/java/src/com/android/inputmethod/latin/DicTraverseSession.java @@ -16,6 +16,7 @@ package com.android.inputmethod.latin; +import com.android.inputmethod.latin.settings.NativeSuggestOptions; import com.android.inputmethod.latin.utils.JniUtils; import java.util.Locale; @@ -24,6 +25,20 @@ public final class DicTraverseSession { static { JniUtils.loadNativeLibrary(); } + // Must be equal to MAX_RESULTS in native/jni/src/defines.h + private static final int MAX_RESULTS = 18; + public final int[] mInputCodePoints = new int[Constants.DICTIONARY_MAX_WORD_LENGTH]; + public final int[] mOutputSuggestionCount = new int[1]; + public final int[] mOutputCodePoints = + new int[Constants.DICTIONARY_MAX_WORD_LENGTH * MAX_RESULTS]; + public final int[] mSpaceIndices = new int[MAX_RESULTS]; + public final int[] mOutputScores = new int[MAX_RESULTS]; + public final int[] mOutputTypes = new int[MAX_RESULTS]; + // Only one result is ever used + public final int[] mOutputAutoCommitFirstWordConfidence = new int[1]; + public final float[] mInputOutputLanguageWeight = new float[1]; + + public final NativeSuggestOptions mNativeSuggestOptions = new NativeSuggestOptions(); private static native long setDicTraverseSessionNative(String locale, long dictSize); private static native void initDicTraverseSessionNative(long nativeDicTraverseSession, diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java index 4a28a242a..304c450ab 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java @@ -468,20 +468,24 @@ public class DictionaryFacilitator { isValid, timeStampInSeconds, mDistracterFilter); } - public void cancelAddingUserHistory(final PrevWordsInfo prevWordsInfo, - final String committedWord) { - final ExpandableBinaryDictionary userHistoryDictionary = - mDictionaries.getSubDict(Dictionary.TYPE_USER_HISTORY); - if (userHistoryDictionary != null) { - userHistoryDictionary.removeNgramDynamically(prevWordsInfo, committedWord); + private void removeWord(final String dictName, final String word) { + final ExpandableBinaryDictionary dictionary = mDictionaries.getSubDict(dictName); + if (dictionary != null) { + dictionary.removeUnigramEntryDynamically(word); } } + public void removeWordFromPersonalizedDicts(final String word) { + removeWord(Dictionary.TYPE_USER_HISTORY, word); + removeWord(Dictionary.TYPE_PERSONALIZATION, word); + removeWord(Dictionary.TYPE_CONTEXTUAL, word); + } + // TODO: Revise the way to fusion suggestion results. public SuggestionResults getSuggestionResults(final WordComposer composer, final PrevWordsInfo prevWordsInfo, final ProximityInfo proximityInfo, final boolean blockOffensiveWords, final int[] additionalFeaturesOptions, - final int sessionId, final ArrayList<SuggestedWordInfo> rawSuggestions) { + final int sessionId) { final Dictionaries dictionaries = mDictionaries; final SuggestionResults suggestionResults = new SuggestionResults(dictionaries.mLocale, SuggestedWords.MAX_SUGGESTIONS); @@ -495,21 +499,13 @@ public class DictionaryFacilitator { languageWeight); if (null == dictionarySuggestions) continue; suggestionResults.addAll(dictionarySuggestions); - if (null != rawSuggestions) { - rawSuggestions.addAll(dictionarySuggestions); + if (null != suggestionResults.mRawSuggestions) { + suggestionResults.mRawSuggestions.addAll(dictionarySuggestions); } } return suggestionResults; } - public boolean isValidMainDictWord(final String word) { - final Dictionary mainDict = mDictionaries.getDict(Dictionary.TYPE_MAIN); - if (TextUtils.isEmpty(word) || mainDict == null) { - return false; - } - return mainDict.isValidWord(word); - } - public boolean isValidWord(final String word, final boolean ignoreCase) { if (TextUtils.isEmpty(word)) { return false; diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java index b1966bffc..37879cf68 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java @@ -122,12 +122,6 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { return mBinaryDictionary.isValidDictionary(); } - // TODO: Remove and always enable beginning of sentence prediction. Currently, this is enabled - // only for ContextualDictionary. - protected boolean enableBeginningOfSentencePrediction() { - return false; - } - /** * Creates a new expandable binary dictionary. * @@ -311,6 +305,27 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { } /** + * Dynamically remove the unigram entry from the dictionary. + */ + public void removeUnigramEntryDynamically(final String word) { + reloadDictionaryIfRequired(); + asyncExecuteTaskWithWriteLock(new Runnable() { + @Override + public void run() { + if (mBinaryDictionary == null) { + return; + } + runGCIfRequiredLocked(true /* mindsBlockByGC */); + if (!mBinaryDictionary.removeUnigramEntry(word)) { + if (DEBUG) { + Log.i(TAG, "Cannot remove unigram entry: " + word); + } + } + } + }); + } + + /** * Adds n-gram information of a word to the dictionary. May overwrite an existing entry. */ public void addNgramEntry(final PrevWordsInfo prevWordsInfo, final String word, @@ -341,6 +356,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { /** * Dynamically remove the n-gram entry in the dictionary. */ + @UsedForTesting public void removeNgramDynamically(final PrevWordsInfo prevWordsInfo, final String word) { reloadDictionaryIfRequired(); asyncExecuteTaskWithWriteLock(new Runnable() { @@ -404,10 +420,6 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { if (mBinaryDictionary == null) { return null; } - if (composer.size() == 0 && prevWordsInfo.mIsBeginningOfSentence - && !enableBeginningOfSentencePrediction()) { - return null; - } final ArrayList<SuggestedWordInfo> suggestions = mBinaryDictionary.getSuggestions(composer, prevWordsInfo, proximityInfo, blockOffensiveWords, additionalFeaturesOptions, sessionId, diff --git a/java/src/com/android/inputmethod/latin/InputAttributes.java b/java/src/com/android/inputmethod/latin/InputAttributes.java index e1ae3dfe3..ebe436128 100644 --- a/java/src/com/android/inputmethod/latin/InputAttributes.java +++ b/java/src/com/android/inputmethod/latin/InputAttributes.java @@ -41,6 +41,7 @@ public final class InputAttributes { final public boolean mShouldShowSuggestions; final public boolean mApplicationSpecifiedCompletionOn; final public boolean mShouldInsertSpacesAutomatically; + final public boolean mShouldShowVoiceInputKey; final private int mInputType; final private EditorInfo mEditorInfo; final private String mPackageNameForPrivateImeOptions; @@ -74,6 +75,7 @@ public final class InputAttributes { mInputTypeNoAutoCorrect = false; mApplicationSpecifiedCompletionOn = false; mShouldInsertSpacesAutomatically = false; + mShouldShowVoiceInputKey = false; return; } // inputClass == InputType.TYPE_CLASS_TEXT @@ -99,6 +101,12 @@ public final class InputAttributes { mShouldInsertSpacesAutomatically = InputTypeUtils.isAutoSpaceFriendlyType(inputType); + final boolean noMicrophone = mIsPasswordField + || InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS == variation + || InputType.TYPE_TEXT_VARIATION_URI == variation + || hasNoMicrophoneKeyOption(); + mShouldShowVoiceInputKey = !noMicrophone; + // If it's a browser edit field and auto correct is not ON explicitly, then // disable auto correction, but keep suggestions on. // If NO_SUGGESTIONS is set, don't do prediction. @@ -119,7 +127,7 @@ public final class InputAttributes { return editorInfo.inputType == mInputType; } - public boolean hasNoMicrophoneKeyOption() { + private boolean hasNoMicrophoneKeyOption() { @SuppressWarnings("deprecation") final boolean deprecatedNoMicrophone = InputAttributes.inPrivateImeOptions( null, NO_MICROPHONE_COMPAT, mEditorInfo); diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index 670d856e3..9d03e8a43 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -100,30 +100,43 @@ public final class Suggest { ? typedWord.substring(0, typedWord.length() - trailingSingleQuotesCount) : typedWord; - final ArrayList<SuggestedWordInfo> rawSuggestions; - if (ProductionFlag.INCLUDE_RAW_SUGGESTIONS) { - rawSuggestions = new ArrayList<>(); - } else { - rawSuggestions = null; - } final SuggestionResults suggestionResults = mDictionaryFacilitator.getSuggestionResults( wordComposer, prevWordsInfo, proximityInfo, blockOffensiveWords, - additionalFeaturesOptions, SESSION_TYPING, rawSuggestions); + additionalFeaturesOptions, SESSION_TYPING); + final boolean isPrediction = !wordComposer.isComposingWord(); + final boolean shouldMakeSuggestionsAllUpperCase = wordComposer.isAllUpperCase() + && !wordComposer.isResumed(); final boolean isOnlyFirstCharCapitalized = wordComposer.isOrWillBeOnlyFirstCharCapitalized(); + + final ArrayList<SuggestedWordInfo> suggestionsContainer = + new ArrayList<>(suggestionResults); + final int suggestionsCount = suggestionsContainer.size(); + if (isOnlyFirstCharCapitalized || shouldMakeSuggestionsAllUpperCase + || 0 != trailingSingleQuotesCount) { + for (int i = 0; i < suggestionsCount; ++i) { + final SuggestedWordInfo wordInfo = suggestionsContainer.get(i); + final SuggestedWordInfo transformedWordInfo = getTransformedSuggestedWordInfo( + wordInfo, suggestionResults.mLocale, shouldMakeSuggestionsAllUpperCase, + isOnlyFirstCharCapitalized, trailingSingleQuotesCount); + suggestionsContainer.set(i, transformedWordInfo); + } + } + SuggestedWordInfo.removeDups(typedWord, suggestionsContainer); + // 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. - final boolean isAllUpperCase = wordComposer.isAllUpperCase() && !wordComposer.isResumed(); final String firstSuggestion; final String whitelistedWord; if (suggestionResults.isEmpty()) { whitelistedWord = firstSuggestion = null; } else { final SuggestedWordInfo firstSuggestedWordInfo = getTransformedSuggestedWordInfo( - suggestionResults.first(), suggestionResults.mLocale, isAllUpperCase, - isOnlyFirstCharCapitalized, trailingSingleQuotesCount); + suggestionResults.first(), suggestionResults.mLocale, + shouldMakeSuggestionsAllUpperCase, isOnlyFirstCharCapitalized, + trailingSingleQuotesCount); firstSuggestion = firstSuggestedWordInfo.mWord; if (!firstSuggestedWordInfo.isKindOf(SuggestedWordInfo.KIND_WHITELIST)) { whitelistedWord = null; @@ -132,8 +145,6 @@ public final class Suggest { } } - final boolean isPrediction = !wordComposer.isComposingWord(); - // We allow auto-correction if we have a whitelisted word, or if the word is not a valid // word of more than 1 char, except if the first suggestion is the same as the typed string // because in this case if it's strong enough to auto-correct that will mistakenly designate @@ -171,19 +182,6 @@ public final class Suggest { suggestionResults.first(), consideredWord, mAutoCorrectionThreshold); } - final ArrayList<SuggestedWordInfo> suggestionsContainer = - new ArrayList<>(suggestionResults); - final int suggestionsCount = suggestionsContainer.size(); - 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, - isOnlyFirstCharCapitalized, trailingSingleQuotesCount); - suggestionsContainer.set(i, transformedWordInfo); - } - } - if (!TextUtils.isEmpty(typedWord)) { suggestionsContainer.add(0, new SuggestedWordInfo(typedWord, SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_TYPED, @@ -191,7 +189,6 @@ public final class Suggest { SuggestedWordInfo.NOT_AN_INDEX /* indexOfTouchPointOfSecondWord */, SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */)); } - SuggestedWordInfo.removeDups(suggestionsContainer); final ArrayList<SuggestedWordInfo> suggestionsList; if (DBG && !suggestionsContainer.isEmpty()) { @@ -200,12 +197,13 @@ public final class Suggest { suggestionsList = suggestionsContainer; } - callback.onGetSuggestedWords(new SuggestedWords(suggestionsList, rawSuggestions, + callback.onGetSuggestedWords(new SuggestedWords(suggestionsList, + suggestionResults.mRawSuggestions, // TODO: this first argument is lying. If this is a whitelisted word which is an // actual word, it says typedWordValid = false, which looks wrong. We should either // rename the attribute or change the value. !isPrediction && !allowsToBeAutoCorrected /* typedWordValid */, - hasAutoCorrection, /* willAutoCorrect */ + hasAutoCorrection /* willAutoCorrect */, false /* isObsoleteSuggestions */, isPrediction, sequenceNumber)); } @@ -216,15 +214,9 @@ public final class Suggest { final boolean blockOffensiveWords, final int[] additionalFeaturesOptions, final int sessionId, final int sequenceNumber, final OnGetSuggestedWordsCallback callback) { - final ArrayList<SuggestedWordInfo> rawSuggestions; - if (ProductionFlag.INCLUDE_RAW_SUGGESTIONS) { - rawSuggestions = new ArrayList<>(); - } else { - rawSuggestions = null; - } final SuggestionResults suggestionResults = mDictionaryFacilitator.getSuggestionResults( wordComposer, prevWordsInfo, proximityInfo, blockOffensiveWords, - additionalFeaturesOptions, sessionId, rawSuggestions); + additionalFeaturesOptions, sessionId); final ArrayList<SuggestedWordInfo> suggestionsContainer = new ArrayList<>(suggestionResults); final int suggestionsCount = suggestionsContainer.size(); @@ -245,7 +237,7 @@ public final class Suggest { final SuggestedWordInfo rejected = suggestionsContainer.remove(0); suggestionsContainer.add(1, rejected); } - SuggestedWordInfo.removeDups(suggestionsContainer); + SuggestedWordInfo.removeDups(null /* typedWord */, suggestionsContainer); // For some reason some suggestions with MIN_VALUE are making their way here. // TODO: Find a more robust way to detect distractors. @@ -257,7 +249,8 @@ public final class Suggest { // In the batch input mode, the most relevant suggested word should act as a "typed word" // (typedWordValid=true), not as an "auto correct word" (willAutoCorrect=false). - callback.onGetSuggestedWords(new SuggestedWords(suggestionsContainer, rawSuggestions, + callback.onGetSuggestedWords(new SuggestedWords(suggestionsContainer, + suggestionResults.mRawSuggestions, true /* typedWordValid */, false /* willAutoCorrect */, false /* isObsoleteSuggestions */, diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java index 72461e17a..f22af7991 100644 --- a/java/src/com/android/inputmethod/latin/SuggestedWords.java +++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java @@ -316,10 +316,6 @@ public class SuggestedWords { return mDebugString; } - public int codePointCount() { - return mCodePointCount; - } - public int codePointAt(int i) { return mWord.codePointAt(i); } @@ -333,23 +329,29 @@ public class SuggestedWords { } } - // TODO: Consolidate this method and StringUtils.removeDupes() in the future. - public static void removeDups(ArrayList<SuggestedWordInfo> candidates) { - if (candidates.size() <= 1) { + // This will always remove the higher index if a duplicate is found. + public static void removeDups(final String typedWord, + ArrayList<SuggestedWordInfo> candidates) { + if (candidates.isEmpty()) { return; } - int i = 1; - while (i < candidates.size()) { - final SuggestedWordInfo cur = candidates.get(i); - for (int j = 0; j < i; ++j) { - final SuggestedWordInfo previous = candidates.get(j); - if (cur.mWord.equals(previous.mWord)) { - candidates.remove(cur.mScore < previous.mScore ? i : j); - --i; - break; - } + if (!TextUtils.isEmpty(typedWord)) { + removeSuggestedWordInfoFrom(typedWord, candidates, -1 /* startIndexExclusive */); + } + for (int i = 0; i < candidates.size(); ++i) { + removeSuggestedWordInfoFrom(candidates.get(i).mWord, candidates, + i /* startIndexExclusive */); + } + } + + private static void removeSuggestedWordInfoFrom(final String word, + final ArrayList<SuggestedWordInfo> candidates, final int startIndexExclusive) { + for (int i = startIndexExclusive + 1; i < candidates.size(); ++i) { + final SuggestedWordInfo previous = candidates.get(i); + if (word.equals(previous.mWord)) { + candidates.remove(i); + --i; } - ++i; } } } diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java index de95b9787..4d3f5b50b 100644 --- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java +++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java @@ -728,14 +728,13 @@ public final class InputLogic { mConnection.setComposingText(getTextWithUnderline( mWordComposer.getTypedWord()), 1); } else { - final boolean swapWeakSpace = maybeStripSpace(inputTransaction, - inputTransaction.mEvent.isSuggestionStripPress()); + final boolean swapWeakSpace = tryStripSpaceAndReturnWhetherShouldSwapInstead( + inputTransaction, inputTransaction.mEvent.isSuggestionStripPress()); - sendKeyCodePoint(settingsValues, codePoint); - - if (swapWeakSpace) { - swapSwapperAndSpace(inputTransaction); + if (swapWeakSpace && trySwapSwapperAndSpace(inputTransaction)) { mSpaceState = SpaceState.WEAK; + } else { + sendKeyCodePoint(settingsValues, codePoint); } // In case the "add to dictionary" hint was still displayed. mSuggestionStripViewAccessor.dismissAddToDictionaryHint(); @@ -780,7 +779,8 @@ public final class InputLogic { } } - final boolean swapWeakSpace = maybeStripSpace(inputTransaction, isFromSuggestionStrip); + final boolean swapWeakSpace = tryStripSpaceAndReturnWhetherShouldSwapInstead( + inputTransaction, isFromSuggestionStrip); final boolean isInsideDoubleQuoteOrAfterDigit = Constants.CODE_DOUBLE_QUOTE == codePoint && mConnection.isInsideDoubleQuoteOrAfterDigit(); @@ -804,16 +804,14 @@ public final class InputLogic { promotePhantomSpace(settingsValues); } - if (!shouldAvoidSendingCode) { - sendKeyCodePoint(settingsValues, codePoint); - } - - if (Constants.CODE_SPACE == codePoint) { - if (maybeDoubleSpacePeriod(inputTransaction)) { - inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW); - inputTransaction.setRequiresUpdateSuggestions(); - mSpaceState = SpaceState.DOUBLE; - } else if (!mSuggestedWords.isPunctuationSuggestions()) { + if (tryPerformDoubleSpacePeriod(inputTransaction)) { + mSpaceState = SpaceState.DOUBLE; + inputTransaction.setRequiresUpdateSuggestions(); + } else if (swapWeakSpace && trySwapSwapperAndSpace(inputTransaction)) { + mSpaceState = SpaceState.SWAP_PUNCTUATION; + mSuggestionStripViewAccessor.setNeutralSuggestionStrip(); + } else if (Constants.CODE_SPACE == codePoint) { + if (!mSuggestedWords.isPunctuationSuggestions()) { mSpaceState = SpaceState.WEAK; } @@ -821,11 +819,12 @@ public final class InputLogic { if (wasComposingWord || mSuggestedWords.isEmpty()) { inputTransaction.setRequiresUpdateSuggestions(); } + + if (!shouldAvoidSendingCode) { + sendKeyCodePoint(settingsValues, codePoint); + } } else { - if (swapWeakSpace) { - swapSwapperAndSpace(inputTransaction); - mSpaceState = SpaceState.SWAP_PUNCTUATION; - } else if ((SpaceState.PHANTOM == inputTransaction.mSpaceState + if ((SpaceState.PHANTOM == inputTransaction.mSpaceState && settingsValues.isUsuallyFollowedBySpace(codePoint)) || (Constants.CODE_DOUBLE_QUOTE == codePoint && isInsideDoubleQuoteOrAfterDigit)) { @@ -843,6 +842,8 @@ public final class InputLogic { mSpaceState = SpaceState.PHANTOM; } + sendKeyCodePoint(settingsValues, codePoint); + // Set punctuation right away. onUpdateSelection will fire but tests whether it is // already displayed or not, so it's okay. mSuggestionStripViewAccessor.setNeutralSuggestionStrip(); @@ -884,6 +885,9 @@ public final class InputLogic { final String rejectedSuggestion = mWordComposer.getTypedWord(); mWordComposer.reset(); mWordComposer.setRejectedBatchModeSuggestion(rejectedSuggestion); + if (!TextUtils.isEmpty(rejectedSuggestion)) { + mDictionaryFacilitator.removeWordFromPersonalizedDicts(rejectedSuggestion); + } } else { mWordComposer.processEvent(inputTransaction.mEvent); } @@ -1005,16 +1009,18 @@ public final class InputLogic { * This method will check that there are two characters before the cursor and that the first * one is a space before it does the actual swapping. * @param inputTransaction The transaction in progress. + * @return true if the swap has been performed, false if it was prevented by preliminary checks. */ - private void swapSwapperAndSpace(final InputTransaction inputTransaction) { - final CharSequence lastTwo = mConnection.getTextBeforeCursor(2, 0); - // It is guaranteed lastTwo.charAt(1) is a swapper - else this method is not called. - if (lastTwo != null && lastTwo.length() == 2 && lastTwo.charAt(0) == Constants.CODE_SPACE) { - mConnection.deleteSurroundingText(2, 0); - final String text = lastTwo.charAt(1) + " "; - mConnection.commitText(text, 1); - inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW); + private boolean trySwapSwapperAndSpace(final InputTransaction inputTransaction) { + final int codePointBeforeCursor = mConnection.getCodePointBeforeCursor(); + if (Constants.CODE_SPACE != codePointBeforeCursor) { + return false; } + mConnection.deleteSurroundingText(1, 0); + final String text = inputTransaction.mEvent.getTextToCommit() + " "; + mConnection.commitText(text, 1); + inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW); + return true; } /* @@ -1023,8 +1029,8 @@ public final class InputLogic { * @param isFromSuggestionStrip Whether this code point is coming from the suggestion strip. * @return whether we should swap the space instead of removing it. */ - private boolean maybeStripSpace(final InputTransaction inputTransaction, - final boolean isFromSuggestionStrip) { + private boolean tryStripSpaceAndReturnWhetherShouldSwapInstead( + final InputTransaction inputTransaction, final boolean isFromSuggestionStrip) { final int codePoint = inputTransaction.mEvent.mCodePoint; if (Constants.CODE_ENTER == codePoint && SpaceState.SWAP_PUNCTUATION == inputTransaction.mSpaceState) { @@ -1065,37 +1071,42 @@ public final class InputLogic { * period-space sequence of characters. This typically happens when the user presses space * twice in a row quickly. * This method will check that the double-space-to-period is active in settings, that the - * two spaces have been input close enough together, and that the previous character allows - * for the transformation to take place. If all of these conditions are fulfilled, this - * method applies the transformation and returns true. Otherwise, it does nothing and - * returns false. + * two spaces have been input close enough together, that the typed character is a space + * and that the previous character allows for the transformation to take place. If all of + * these conditions are fulfilled, this method applies the transformation and returns true. + * Otherwise, it does nothing and returns false. * * @param inputTransaction The transaction in progress. * @return true if we applied the double-space-to-period transformation, false otherwise. */ - private boolean maybeDoubleSpacePeriod(final InputTransaction inputTransaction) { - if (!inputTransaction.mSettingsValues.mUseDoubleSpacePeriod) return false; - if (!isDoubleSpacePeriodCountdownActive(inputTransaction)) return false; - // We only do this when we see two spaces and an accepted code point before the cursor. - // The code point may be a surrogate pair but the two spaces may not, so we need 4 chars. - final CharSequence lastThree = mConnection.getTextBeforeCursor(4, 0); - if (null == lastThree) return false; - final int length = lastThree.length(); - if (length < 3) return false; - if (lastThree.charAt(length - 1) != Constants.CODE_SPACE) return false; - if (lastThree.charAt(length - 2) != Constants.CODE_SPACE) return false; - // We know there are spaces in pos -1 and -2, and we have at least three chars. - // If we have only three chars, isSurrogatePairs can't return true as charAt(1) is a space, - // so this is fine. + private boolean tryPerformDoubleSpacePeriod(final InputTransaction inputTransaction) { + // Check the setting, the typed character and the countdown. If any of the conditions is + // not fulfilled, return false. + if (!inputTransaction.mSettingsValues.mUseDoubleSpacePeriod + || Constants.CODE_SPACE != inputTransaction.mEvent.mCodePoint + || !isDoubleSpacePeriodCountdownActive(inputTransaction)) { + return false; + } + // We only do this when we see one space and an accepted code point before the cursor. + // The code point may be a surrogate pair but the space may not, so we need 3 chars. + final CharSequence lastTwo = mConnection.getTextBeforeCursor(3, 0); + if (null == lastTwo) return false; + final int length = lastTwo.length(); + if (length < 2) return false; + if (lastTwo.charAt(length - 1) != Constants.CODE_SPACE) return false; + // We know there is a space in pos -1, and we have at least two chars. If we have only two + // chars, isSurrogatePairs can't return true as charAt(1) is a space, so this is fine. final int firstCodePoint = - Character.isSurrogatePair(lastThree.charAt(0), lastThree.charAt(1)) ? - Character.codePointAt(lastThree, 0) : lastThree.charAt(length - 3); + Character.isSurrogatePair(lastTwo.charAt(0), lastTwo.charAt(1)) ? + Character.codePointAt(lastTwo, length - 3) : lastTwo.charAt(length - 2); if (canBeFollowedByDoubleSpacePeriod(firstCodePoint)) { cancelDoubleSpacePeriodCountdown(); - mConnection.deleteSurroundingText(2, 0); + mConnection.deleteSurroundingText(1, 0); final String textToInsert = inputTransaction.mSettingsValues.mSpacingAndPunctuations .mSentenceSeparatorAndSpace; mConnection.commitText(textToInsert, 1); + inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW); + inputTransaction.setRequiresUpdateSuggestions(); return true; } return false; @@ -1187,6 +1198,8 @@ public final class InputLogic { Log.w(TAG, "Called updateSuggestionsOrPredictions but suggestions were not " + "requested!"); } + // Clear the suggestions strip. + mSuggestionStripViewAccessor.showSuggestionStrip(SuggestedWords.EMPTY); return; } @@ -1363,7 +1376,6 @@ public final class InputLogic { * @param inputTransaction The transaction in progress. */ private void revertCommit(final InputTransaction inputTransaction) { - final PrevWordsInfo prevWordsInfo = mLastComposedWord.mPrevWordsInfo; final CharSequence originallyTypedWord = mLastComposedWord.mTypedWord; final CharSequence committedWord = mLastComposedWord.mCommittedWord; final String committedWordString = committedWord.toString(); @@ -1385,8 +1397,8 @@ public final class InputLogic { } } mConnection.deleteSurroundingText(deleteLength, 0); - if (!TextUtils.isEmpty(prevWordsInfo.mPrevWord) && !TextUtils.isEmpty(committedWord)) { - mDictionaryFacilitator.cancelAddingUserHistory(prevWordsInfo, committedWordString); + if (!TextUtils.isEmpty(committedWord)) { + mDictionaryFacilitator.removeWordFromPersonalizedDicts(committedWordString); } final String stringToCommit = originallyTypedWord + mLastComposedWord.mSeparatorString; final SpannableString textToCommit = new SpannableString(stringToCommit); diff --git a/java/src/com/android/inputmethod/latin/makedict/WordProperty.java b/java/src/com/android/inputmethod/latin/makedict/WordProperty.java index 31cb59756..cd78e2235 100644 --- a/java/src/com/android/inputmethod/latin/makedict/WordProperty.java +++ b/java/src/com/android/inputmethod/latin/makedict/WordProperty.java @@ -70,8 +70,8 @@ public final class WordProperty implements Comparable<WordProperty> { // Construct word property using information from native code. // This represents invalid word when the probability is BinaryDictionary.NOT_A_PROBABILITY. public WordProperty(final int[] codePoints, final boolean isNotAWord, - final boolean isBlacklisted, final boolean hasBigram, - final boolean hasShortcuts, final int[] probabilityInfo, + final boolean isBlacklisted, final boolean hasBigram, final boolean hasShortcuts, + final boolean isBeginningOfSentence, final int[] probabilityInfo, final ArrayList<int[]> bigramTargets, final ArrayList<int[]> bigramProbabilityInfo, final ArrayList<int[]> shortcutTargets, final ArrayList<Integer> shortcutProbabilities) { @@ -79,7 +79,7 @@ public final class WordProperty implements Comparable<WordProperty> { mProbabilityInfo = createProbabilityInfoFromArray(probabilityInfo); mShortcutTargets = new ArrayList<>(); mBigrams = new ArrayList<>(); - mIsBeginningOfSentence = false; + mIsBeginningOfSentence = isBeginningOfSentence; mIsNotAWord = isNotAWord; mIsBlacklistEntry = isBlacklisted; mHasShortcuts = hasShortcuts; diff --git a/java/src/com/android/inputmethod/latin/personalization/ContextualDictionary.java b/java/src/com/android/inputmethod/latin/personalization/ContextualDictionary.java index a96018fe9..ac55b9333 100644 --- a/java/src/com/android/inputmethod/latin/personalization/ContextualDictionary.java +++ b/java/src/com/android/inputmethod/latin/personalization/ContextualDictionary.java @@ -43,11 +43,6 @@ public class ContextualDictionary extends ExpandableBinaryDictionary { } @Override - protected boolean enableBeginningOfSentencePrediction() { - return true; - } - - @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/settings/DebugSettings.java b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java index 845ddb377..c17e86892 100644 --- a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java +++ b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java @@ -21,14 +21,13 @@ import android.content.SharedPreferences; import android.content.res.Resources; import android.os.Bundle; import android.os.Process; -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 android.preference.TwoStatePreference; -import com.android.inputmethod.latin.Dictionary; import com.android.inputmethod.latin.DictionaryDumpBroadcastReceiver; import com.android.inputmethod.latin.DictionaryFacilitator; import com.android.inputmethod.latin.R; @@ -57,7 +56,7 @@ public final class DebugSettings extends PreferenceFragment public static final String PREF_KEY_LONGPRESS_TIMEOUT = "pref_key_longpress_timeout"; private boolean mServiceNeedsRestart = false; - private CheckBoxPreference mDebugMode; + private TwoStatePreference mDebugMode; @Override public void onCreate(Bundle icicle) { @@ -107,7 +106,7 @@ public final class DebugSettings extends PreferenceFragment res, R.fraction.config_key_preview_dismiss_end_scale)); mServiceNeedsRestart = false; - mDebugMode = (CheckBoxPreference) findPreference(PREF_DEBUG_MODE); + mDebugMode = (TwoStatePreference) findPreference(PREF_DEBUG_MODE); updateDebugMode(); } diff --git a/java/src/com/android/inputmethod/latin/settings/Settings.java b/java/src/com/android/inputmethod/latin/settings/Settings.java index 235847799..fb1a210bb 100644 --- a/java/src/com/android/inputmethod/latin/settings/Settings.java +++ b/java/src/com/android/inputmethod/latin/settings/Settings.java @@ -39,8 +39,14 @@ import java.util.concurrent.locks.ReentrantLock; public final class Settings implements SharedPreferences.OnSharedPreferenceChangeListener { private static final String TAG = Settings.class.getSimpleName(); + // Settings screens + public static final String SCREEN_INPUT = "screen_input"; + public static final String SCREEN_MULTI_LINGUAL = "screen_multi_lingual"; + public static final String SCREEN_GESTURE = "screen_gesture"; + public static final String SCREEN_CORRECTION = "screen_correction"; + public static final String SCREEN_ADVANCED = "screen_advanced"; + public static final String SCREEN_DEBUG = "screen_debug"; // In the same order as xml/prefs.xml - public static final String PREF_GENERAL_SETTINGS = "general_settings"; public static final String PREF_AUTO_CAP = "auto_cap"; public static final String PREF_VIBRATE_ON = "vibrate_on"; public static final String PREF_SOUND_ON = "sound_on"; @@ -48,13 +54,10 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang // PREF_VOICE_MODE_OBSOLETE is obsolete. Use PREF_VOICE_INPUT_KEY instead. public static final String PREF_VOICE_MODE_OBSOLETE = "voice_mode"; public static final String PREF_VOICE_INPUT_KEY = "pref_voice_input_key"; - public static final String PREF_CORRECTION_SETTINGS = "correction_settings"; public static final String PREF_EDIT_PERSONAL_DICTIONARY = "edit_personal_dictionary"; public static final String PREF_CONFIGURE_DICTIONARIES_KEY = "configure_dictionaries_key"; public static final String PREF_AUTO_CORRECTION_THRESHOLD = "auto_correction_threshold"; public static final String PREF_SHOW_SUGGESTIONS_SETTING = "show_suggestions_setting"; - public static final String PREF_MISC_SETTINGS = "misc_settings"; - public static final String PREF_ADVANCED_SETTINGS = "pref_advanced_settings"; public static final String PREF_KEY_USE_CONTACTS_DICT = "pref_key_use_contacts_dict"; public static final String PREF_KEY_USE_PERSONALIZED_DICTS = "pref_key_use_personalized_dicts"; public static final String PREF_KEY_USE_DOUBLE_SPACE_PERIOD = @@ -75,7 +78,6 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang public static final String PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY = "pref_key_preview_popup_dismiss_delay"; public static final String PREF_BIGRAM_PREDICTIONS = "next_word_prediction"; - public static final String PREF_GESTURE_SETTINGS = "gesture_typing_settings"; public static final String PREF_GESTURE_INPUT = "gesture_input"; public static final String PREF_VIBRATION_DURATION_SETTINGS = "pref_vibration_duration_settings"; @@ -89,7 +91,6 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang public static final String PREF_INPUT_LANGUAGE = "input_language"; public static final String PREF_SELECTED_LANGUAGES = "selected_languages"; - public static final String PREF_DEBUG_SETTINGS = "debug_settings"; public static final String PREF_KEY_IS_INTERNAL = "pref_key_is_internal"; public static final String PREF_ENABLE_METRICS_LOGGING = "pref_enable_metrics_logging"; @@ -105,8 +106,6 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang "pref_last_used_personalization_dict_wiped_time"; private static final String PREF_CORPUS_HANDLES_FOR_PERSONALIZATION = "pref_corpus_handles_for_personalization"; - public static final String PREF_SEND_FEEDBACK = "send_feedback"; - public static final String PREF_ABOUT_KEYBOARD = "about_keyboard"; // Emoji public static final String PREF_EMOJI_RECENT_KEYS = "emoji_recent_keys"; diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java index 5eb0377c7..689f878be 100644 --- a/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java +++ b/java/src/com/android/inputmethod/latin/settings/SettingsFragment.java @@ -27,13 +27,15 @@ import android.content.res.Resources; import android.media.AudioManager; import android.os.Build; import android.os.Bundle; -import android.preference.CheckBoxPreference; import android.preference.ListPreference; import android.preference.Preference; -import android.preference.Preference.OnPreferenceClickListener; import android.preference.PreferenceGroup; import android.preference.PreferenceScreen; +import android.preference.TwoStatePreference; import android.util.Log; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; import android.view.inputmethod.InputMethodSubtype; import com.android.inputmethod.dictionarypack.DictionarySettingsActivity; @@ -61,6 +63,10 @@ public final class SettingsFragment extends InputMethodSettingsFragment DBG_USE_INTERNAL_PERSONAL_DICTIONARY_SETTINGS || Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN_MR2; + private static final int NO_MENU_GROUP = Menu.NONE; // We don't care about menu grouping. + private static final int MENU_FEEDBACK = Menu.FIRST; // The first menu item id and order. + private static final int MENU_ABOUT = Menu.FIRST + 1; // The second menu item id and order. + private void setPreferenceEnabled(final String preferenceKey, final boolean enabled) { final Preference preference = findPreference(preferenceKey); if (preference != null) { @@ -93,6 +99,7 @@ public final class SettingsFragment extends InputMethodSettingsFragment @Override public void onCreate(final Bundle icicle) { super.onCreate(icicle); + setHasOptionsMenu(true); setInputMethodSettingsCategoryTitle(R.string.language_selection_title); setSubtypeEnablerTitle(R.string.select_language); addPreferencesFromResource(R.xml.prefs); @@ -117,66 +124,48 @@ public final class SettingsFragment extends InputMethodSettingsFragment ensureConsistencyOfAutoCorrectionSettings(); - final PreferenceGroup generalSettings = - (PreferenceGroup) findPreference(Settings.PREF_GENERAL_SETTINGS); - final PreferenceGroup miscSettings = - (PreferenceGroup) findPreference(Settings.PREF_MISC_SETTINGS); - - final Preference debugSettings = findPreference(Settings.PREF_DEBUG_SETTINGS); - if (debugSettings != null) { - if (Settings.isInternal(prefs)) { - final Intent debugSettingsIntent = new Intent(Intent.ACTION_MAIN); - debugSettingsIntent.setClassName( - context.getPackageName(), DebugSettingsActivity.class.getName()); - debugSettings.setIntent(debugSettingsIntent); - } else { - miscSettings.removePreference(debugSettings); - } - } - - final Preference feedbackSettings = findPreference(Settings.PREF_SEND_FEEDBACK); - final Preference aboutSettings = findPreference(Settings.PREF_ABOUT_KEYBOARD); - if (feedbackSettings != null) { - if (FeedbackUtils.isFeedbackFormSupported()) { - feedbackSettings.setOnPreferenceClickListener(new OnPreferenceClickListener() { - @Override - public boolean onPreferenceClick(final Preference pref) { - FeedbackUtils.showFeedbackForm(getActivity()); - return true; - } - }); - aboutSettings.setTitle(FeedbackUtils.getAboutKeyboardTitleResId()); - aboutSettings.setIntent(FeedbackUtils.getAboutKeyboardIntent(getActivity())); - } else { - miscSettings.removePreference(feedbackSettings); - miscSettings.removePreference(aboutSettings); - } + final PreferenceScreen inputScreen = + (PreferenceScreen) findPreference(Settings.SCREEN_INPUT); + final PreferenceScreen multiLingualScreen = + (PreferenceScreen) findPreference(Settings.SCREEN_MULTI_LINGUAL); + final PreferenceScreen gestureScreen = + (PreferenceScreen) findPreference(Settings.SCREEN_GESTURE); + final PreferenceScreen correctionScreen = + (PreferenceScreen) findPreference(Settings.SCREEN_CORRECTION); + final PreferenceScreen advancedScreen = + (PreferenceScreen) findPreference(Settings.SCREEN_ADVANCED); + final PreferenceScreen debugScreen = + (PreferenceScreen) findPreference(Settings.SCREEN_DEBUG); + + if (Settings.isInternal(prefs)) { + final Intent debugSettingsIntent = new Intent(Intent.ACTION_MAIN); + debugSettingsIntent.setClassName( + context.getPackageName(), DebugSettingsActivity.class.getName()); + debugScreen.setIntent(debugSettingsIntent); + } else { + advancedScreen.removePreference(debugScreen); } final boolean showVoiceKeyOption = res.getBoolean( R.bool.config_enable_show_voice_key_option); if (!showVoiceKeyOption) { - removePreference(Settings.PREF_VOICE_INPUT_KEY, generalSettings); + removePreference(Settings.PREF_VOICE_INPUT_KEY, inputScreen); } - final PreferenceGroup advancedSettings = - (PreferenceGroup) findPreference(Settings.PREF_ADVANCED_SETTINGS); if (!AudioAndHapticFeedbackManager.getInstance().hasVibrator()) { - removePreference(Settings.PREF_VIBRATE_ON, generalSettings); - removePreference(Settings.PREF_VIBRATION_DURATION_SETTINGS, advancedSettings); + removePreference(Settings.PREF_VIBRATE_ON, inputScreen); + removePreference(Settings.PREF_VIBRATION_DURATION_SETTINGS, advancedScreen); } if (!Settings.ENABLE_SHOW_LANGUAGE_SWITCH_KEY_SETTINGS) { + removePreference(Settings.PREF_SHOW_LANGUAGE_SWITCH_KEY, multiLingualScreen); removePreference( - Settings.PREF_SHOW_LANGUAGE_SWITCH_KEY, advancedSettings); - removePreference( - Settings.PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST, advancedSettings); + Settings.PREF_INCLUDE_OTHER_IMES_IN_LANGUAGE_SWITCH_LIST, multiLingualScreen); } - // TODO: consolidate key preview dismiss delay with the key preview animation parameters. if (!Settings.readFromBuildConfigIfToShowKeyPreviewPopupOption(res)) { - removePreference(Settings.PREF_POPUP_ON, generalSettings); - removePreference(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY, advancedSettings); + removePreference(Settings.PREF_POPUP_ON, inputScreen); + removePreference(Settings.PREF_KEY_PREVIEW_POPUP_DISMISS_DELAY, advancedScreen); } else { // TODO: Cleanup this setup. final ListPreference keyPreviewPopupDismissDelay = @@ -199,18 +188,16 @@ public final class SettingsFragment extends InputMethodSettingsFragment } if (!res.getBoolean(R.bool.config_setup_wizard_available)) { - removePreference(Settings.PREF_SHOW_SETUP_WIZARD_ICON, advancedSettings); + removePreference(Settings.PREF_SHOW_SETUP_WIZARD_ICON, advancedScreen); } - final PreferenceGroup textCorrectionGroup = - (PreferenceGroup) findPreference(Settings.PREF_CORRECTION_SETTINGS); final PreferenceScreen dictionaryLink = (PreferenceScreen) findPreference(Settings.PREF_CONFIGURE_DICTIONARIES_KEY); final Intent intent = dictionaryLink.getIntent(); intent.setClassName(context.getPackageName(), DictionarySettingsActivity.class.getName()); final int number = context.getPackageManager().queryIntentActivities(intent, 0).size(); if (0 >= number) { - textCorrectionGroup.removePreference(dictionaryLink); + correctionScreen.removePreference(dictionaryLink); } if (ProductionFlag.IS_METRICS_LOGGING_SUPPORTED) { @@ -224,7 +211,7 @@ public final class SettingsFragment extends InputMethodSettingsFragment enableMetricsLogging.setTitle(enableMetricsLoggingTitle); } } else { - removePreference(Settings.PREF_ENABLE_METRICS_LOGGING, textCorrectionGroup); + removePreference(Settings.PREF_ENABLE_METRICS_LOGGING, advancedScreen); } final Preference editPersonalDictionary = @@ -238,7 +225,7 @@ public final class SettingsFragment extends InputMethodSettingsFragment } if (!Settings.readFromBuildConfigIfGestureInputEnabled(res)) { - removePreference(Settings.PREF_GESTURE_SETTINGS, getPreferenceScreen()); + getPreferenceScreen().removePreference(gestureScreen); } AdditionalFeaturesSettingUtils.addAdditionalFeaturesPreferences(context, this); @@ -261,8 +248,8 @@ public final class SettingsFragment extends InputMethodSettingsFragment voiceInputKeyOption.setSummary(isShortcutImeEnabled ? null : res.getText(R.string.voice_input_disabled_summary)); } - final CheckBoxPreference showSetupWizardIcon = - (CheckBoxPreference)findPreference(Settings.PREF_SHOW_SETUP_WIZARD_ICON); + final TwoStatePreference showSetupWizardIcon = + (TwoStatePreference)findPreference(Settings.PREF_SHOW_SETUP_WIZARD_ICON); if (showSetupWizardIcon != null) { showSetupWizardIcon.setChecked(Settings.readShowSetupWizardIcon(prefs, getActivity())); } @@ -476,4 +463,33 @@ public final class SettingsFragment extends InputMethodSettingsFragment userDictionaryPreference.setFragment(UserDictionaryList.class.getName()); } } + + @Override + public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) { + if (FeedbackUtils.isFeedbackFormSupported()) { + menu.add(NO_MENU_GROUP, MENU_FEEDBACK /* itemId */, MENU_FEEDBACK /* order */, + R.string.send_feedback); + } + final int aboutResId = FeedbackUtils.getAboutKeyboardTitleResId(); + if (aboutResId != 0) { + menu.add(NO_MENU_GROUP, MENU_ABOUT /* itemId */, MENU_ABOUT /* order */, aboutResId); + } + } + + @Override + public boolean onOptionsItemSelected(final MenuItem item) { + final int itemId = item.getItemId(); + if (itemId == MENU_FEEDBACK) { + FeedbackUtils.showFeedbackForm(getActivity()); + return true; + } + if (itemId == MENU_ABOUT) { + final Intent aboutIntent = FeedbackUtils.getAboutKeyboardIntent(getActivity()); + if (aboutIntent != null) { + startActivity(aboutIntent); + return true; + } + } + return super.onOptionsItemSelected(item); + } } diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java index 44104019b..8de5fed07 100644 --- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java +++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java @@ -125,8 +125,7 @@ public final class SettingsValues { mSlidingKeyInputPreviewEnabled = prefs.getBoolean( DebugSettings.PREF_SLIDING_KEY_INPUT_PREVIEW, true); mShowsVoiceInputKey = needsToShowVoiceInputKey(prefs, res) - && !mInputAttributes.mIsPasswordField - && !mInputAttributes.hasNoMicrophoneKeyOption() + && mInputAttributes.mShouldShowVoiceInputKey && SubtypeSwitcher.getInstance().isShortcutImeEnabled(); final String autoCorrectionThresholdRawValue = prefs.getString( Settings.PREF_AUTO_CORRECTION_THRESHOLD, diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java index 19b48f081..ad5aad747 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java @@ -44,10 +44,12 @@ import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.TextView; +import com.android.inputmethod.accessibility.AccessibilityUtils; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.PunctuationSuggestions; 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.AutoCorrectionUtils; import com.android.inputmethod.latin.utils.ResourceUtils; import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; @@ -249,8 +251,8 @@ final class SuggestionStripLayoutHelper { final int positionInStrip = getPositionInSuggestionStrip(indexInSuggestedWords, suggestedWords); // Use identity for strings, not #equals : it's the typed word if it's the same object - final boolean isTypedWord = - suggestedWords.getWord(indexInSuggestedWords) == suggestedWords.mTypedWord; + final boolean isTypedWord = suggestedWords.getInfo(indexInSuggestedWords).isKindOf( + SuggestedWordInfo.KIND_TYPED); final int color; if (positionInStrip == mCenterPositionInStrip && suggestedWords.mWillAutoCorrect) { @@ -386,6 +388,12 @@ final class SuggestionStripLayoutHelper { final float scaleX = getTextScaleX(word, width, wordView.getPaint()); wordView.setText(text); // TextView.setText() resets text scale x to 1.0. wordView.setTextScaleX(Math.max(scaleX, MIN_TEXT_XSCALE)); + // A <code>wordView</code> should be disabled when <code>word</code> is empty in order to + // make it unclickable. + // With accessibility touch exploration on, <code>wordView</code> should be enabled even + // when it is empty to avoid announcing as "disabled". + wordView.setEnabled(!TextUtils.isEmpty(word) + || AccessibilityUtils.getInstance().isTouchExplorationEnabled()); return wordView; } diff --git a/java/src/com/android/inputmethod/latin/utils/CombinedFormatUtils.java b/java/src/com/android/inputmethod/latin/utils/CombinedFormatUtils.java index c66007537..34f59e8bc 100644 --- a/java/src/com/android/inputmethod/latin/utils/CombinedFormatUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/CombinedFormatUtils.java @@ -31,6 +31,7 @@ public class CombinedFormatUtils { public static final String HISTORICAL_INFO_TAG = "historicalInfo"; public static final String HISTORICAL_INFO_SEPARATOR = ":"; public static final String WORD_TAG = "word"; + public static final String BEGINNING_OF_SENTENCE_TAG = "beginning_of_sentence"; public static final String NOT_A_WORD_TAG = "not_a_word"; public static final String BLACKLISTED_TAG = "blacklisted"; @@ -56,6 +57,9 @@ public class CombinedFormatUtils { builder.append(" " + WORD_TAG + "=" + wordProperty.mWord); builder.append(","); builder.append(formatProbabilityInfo(wordProperty.mProbabilityInfo)); + if (wordProperty.mIsBeginningOfSentence) { + builder.append("," + BEGINNING_OF_SENTENCE_TAG + "=true"); + } if (wordProperty.mIsNotAWord) { builder.append("," + NOT_A_WORD_TAG + "=true"); } diff --git a/java/src/com/android/inputmethod/latin/utils/SuggestionResults.java b/java/src/com/android/inputmethod/latin/utils/SuggestionResults.java index 0b362c48a..5c109a68c 100644 --- a/java/src/com/android/inputmethod/latin/utils/SuggestionResults.java +++ b/java/src/com/android/inputmethod/latin/utils/SuggestionResults.java @@ -17,7 +17,9 @@ package com.android.inputmethod.latin.utils; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; +import com.android.inputmethod.latin.define.ProductionFlag; +import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.Locale; @@ -29,6 +31,7 @@ import java.util.TreeSet; */ public final class SuggestionResults extends TreeSet<SuggestedWordInfo> { public final Locale mLocale; + public final ArrayList<SuggestedWordInfo> mRawSuggestions; private final int mCapacity; public SuggestionResults(final Locale locale, final int capacity) { @@ -40,6 +43,11 @@ public final class SuggestionResults extends TreeSet<SuggestedWordInfo> { super(comparator); mLocale = locale; mCapacity = capacity; + if (ProductionFlag.INCLUDE_RAW_SUGGESTIONS) { + mRawSuggestions = new ArrayList<>(); + } else { + mRawSuggestions = null; + } } @Override |