diff options
Diffstat (limited to 'java/src/com/android/inputmethod/latin')
26 files changed, 398 insertions, 204 deletions
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index e66cfca49..6e0cdf2b1 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -27,7 +27,7 @@ import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.JniUtils; import com.android.inputmethod.latin.utils.LanguageModelParam; import com.android.inputmethod.latin.utils.StringUtils; -import com.android.inputmethod.latin.utils.UnigramProperty; +import com.android.inputmethod.latin.utils.WordProperty; import java.io.File; import java.util.ArrayList; @@ -61,18 +61,19 @@ public final class BinaryDictionary extends Dictionary { public static final int NOT_A_VALID_TIMESTAMP = -1; - // Format to get unigram flags from native side via getUnigramPropertyNative(). - private static final int FORMAT_UNIGRAM_PROPERTY_OUTPUT_FLAG_COUNT = 4; - private static final int FORMAT_UNIGRAM_PROPERTY_IS_NOT_A_WORD_INDEX = 0; - private static final int FORMAT_UNIGRAM_PROPERTY_IS_BLACKLISTED_INDEX = 1; - private static final int FORMAT_UNIGRAM_PROPERTY_HAS_BIGRAMS_INDEX = 2; - private static final int FORMAT_UNIGRAM_PROPERTY_HAS_SHORTCUTS_INDEX = 3; + // 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_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; - // Format to get unigram historical info from native side via getUnigramPropertyNative(). - private static final int FORMAT_UNIGRAM_PROPERTY_OUTPUT_HISTORICAL_INFO_COUNT = 3; - private static final int FORMAT_UNIGRAM_PROPERTY_TIMESTAMP_INDEX = 0; - private static final int FORMAT_UNIGRAM_PROPERTY_LEVEL_INDEX = 1; - private static final int FORMAT_UNIGRAM_PROPERTY_COUNT_INDEX = 2; + // Format to get probability and historical info from native side via getWordPropertyNative(). + public static final int FORMAT_WORD_PROPERTY_OUTPUT_PROBABILITY_INFO_COUNT = 4; + public static final int FORMAT_WORD_PROPERTY_PROBABILITY_INDEX = 0; + public static final int FORMAT_WORD_PROPERTY_TIMESTAMP_INDEX = 1; + public static final int FORMAT_WORD_PROPERTY_LEVEL_INDEX = 2; + public static final int FORMAT_WORD_PROPERTY_COUNT_INDEX = 3; private long mNativeDict; private final Locale mLocale; @@ -143,10 +144,10 @@ public final class BinaryDictionary extends Dictionary { 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 void getUnigramPropertyNative(long dict, int[] word, - int[] outCodePoints, boolean[] outFlags, int[] outProbability, - int[] outHistoricalInfo, ArrayList<int[]> outShortcutTargets, - ArrayList<Integer> outShortcutProbabilities); + 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 getSuggestionsNative(long dict, long proximityInfo, long traverseSession, int[] xCoordinates, int[] yCoordinates, int[] times, int[] pointerIds, int[] inputCodePoints, int inputSize, int commitPoint, @@ -306,29 +307,29 @@ public final class BinaryDictionary extends Dictionary { } @UsedForTesting - public UnigramProperty getUnigramProperty(final String word) { + public WordProperty getWordProperty(final String word) { if (TextUtils.isEmpty(word)) { return null; } final int[] codePoints = StringUtils.toCodePointArray(word); final int[] outCodePoints = new int[MAX_WORD_LENGTH]; - final boolean[] outFlags = new boolean[FORMAT_UNIGRAM_PROPERTY_OUTPUT_FLAG_COUNT]; - final int[] outProbability = new int[1]; - final int[] outHistoricalInfo = - new int[FORMAT_UNIGRAM_PROPERTY_OUTPUT_HISTORICAL_INFO_COUNT]; + 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(); - getUnigramPropertyNative(mNativeDict, codePoints, outCodePoints, outFlags, outProbability, - outHistoricalInfo, outShortcutTargets, outShortcutProbabilities); - return new UnigramProperty(codePoints, - outFlags[FORMAT_UNIGRAM_PROPERTY_IS_NOT_A_WORD_INDEX], - outFlags[FORMAT_UNIGRAM_PROPERTY_IS_BLACKLISTED_INDEX], - outFlags[FORMAT_UNIGRAM_PROPERTY_HAS_BIGRAMS_INDEX], - outFlags[FORMAT_UNIGRAM_PROPERTY_HAS_SHORTCUTS_INDEX], outProbability[0], - outHistoricalInfo[FORMAT_UNIGRAM_PROPERTY_TIMESTAMP_INDEX], - outHistoricalInfo[FORMAT_UNIGRAM_PROPERTY_LEVEL_INDEX], - outHistoricalInfo[FORMAT_UNIGRAM_PROPERTY_COUNT_INDEX], - outShortcutTargets, outShortcutProbabilities); + getWordPropertyNative(mNativeDict, codePoints, 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, + outBigramTargets, outBigramProbabilityInfo, outShortcutTargets, + outShortcutProbabilities); } // Add a unigram entry to binary dictionary with unigram attributes in native code. diff --git a/java/src/com/android/inputmethod/latin/Constants.java b/java/src/com/android/inputmethod/latin/Constants.java index 0b396b1de..d6ac71fe2 100644 --- a/java/src/com/android/inputmethod/latin/Constants.java +++ b/java/src/com/android/inputmethod/latin/Constants.java @@ -191,6 +191,8 @@ public final class Constants { public static final int CODE_QUESTION_MARK = '?'; public static final int CODE_EXCLAMATION_MARK = '!'; public static final int CODE_SLASH = '/'; + public static final int CODE_BACKSLASH = '\\'; + public static final int CODE_VERTICAL_BAR = '|'; public static final int CODE_COMMERCIAL_AT = '@'; public static final int CODE_PLUS = '+'; public static final int CODE_PERCENT = '%'; diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java index 8a8a45f35..8b02984e0 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java @@ -505,6 +505,14 @@ public class DictionaryFacilitatorForSuggest { } } + @UsedForTesting + public void clearUserHistoryDictionary() { + if (mUserHistoryDictionary == null) { + return; + } + mUserHistoryDictionary.clearAndFlushDictionary(); + } + // This method gets called only when the IME receives a notification to remove the // personalization dictionary. public void clearPersonalizationDictionary() { diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java index 8e1675b2a..4dee84a7b 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java @@ -267,9 +267,9 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { protected Map<String, String> getHeaderAttributeMap() { HashMap<String, String> attributeMap = new HashMap<String, String>(); - attributeMap.put(FormatSpec.FileHeader.DICTIONARY_ID_ATTRIBUTE, mDictName); - attributeMap.put(FormatSpec.FileHeader.DICTIONARY_LOCALE_ATTRIBUTE, mLocale.toString()); - attributeMap.put(FormatSpec.FileHeader.DICTIONARY_VERSION_ATTRIBUTE, + attributeMap.put(FormatSpec.FileHeader.DICTIONARY_ID_KEY, mDictName); + attributeMap.put(FormatSpec.FileHeader.DICTIONARY_LOCALE_KEY, mLocale.toString()); + attributeMap.put(FormatSpec.FileHeader.DICTIONARY_VERSION_KEY, String.valueOf(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()))); return attributeMap; } diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index e8ea5e39d..208cf22a6 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -42,7 +42,6 @@ import android.preference.PreferenceManager; import android.text.InputType; import android.text.TextUtils; import android.util.Log; -import android.util.Pair; import android.util.PrintWriterPrinter; import android.util.Printer; import android.view.KeyEvent; @@ -154,8 +153,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen private static final int ARG1_NOT_GESTURE_INPUT = 0; 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_WITHOUT_TYPED_WORD = 0; - private static final int ARG2_WITH_TYPED_WORD = 1; + private static final int ARG2_UNUSED = 0; private int mDelayUpdateSuggestions; private int mDelayUpdateShiftState; @@ -190,16 +188,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen break; case MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP: if (msg.arg1 == ARG1_NOT_GESTURE_INPUT) { - if (msg.arg2 == ARG2_WITH_TYPED_WORD) { - final Pair<SuggestedWords, String> p = - (Pair<SuggestedWords, String>) msg.obj; - // [IL]: this is the only place where the second arg is not - // suggestedWords.mTypedWord. - latinIme.showSuggestionStrip(p.first, p.second); - } else { - final SuggestedWords suggestedWords = (SuggestedWords) msg.obj; - latinIme.showSuggestionStrip(suggestedWords, suggestedWords.mTypedWord); - } + final SuggestedWords suggestedWords = (SuggestedWords) msg.obj; + latinIme.showSuggestionStrip(suggestedWords); } else { latinIme.showGesturePreviewAndSuggestionStrip((SuggestedWords) msg.obj, msg.arg1 == ARG1_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT); @@ -292,22 +282,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen ? ARG1_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT : ARG1_SHOW_GESTURE_FLOATING_PREVIEW_TEXT; obtainMessage(MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP, arg1, - ARG2_WITHOUT_TYPED_WORD, suggestedWords).sendToTarget(); + ARG2_UNUSED, suggestedWords).sendToTarget(); } public void showSuggestionStrip(final SuggestedWords suggestedWords) { removeMessages(MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP); obtainMessage(MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP, - ARG1_NOT_GESTURE_INPUT, ARG2_WITHOUT_TYPED_WORD, suggestedWords).sendToTarget(); - } - - // TODO: Remove this method. - public void showSuggestionStripWithTypedWord(final SuggestedWords suggestedWords, - final String typedWord) { - removeMessages(MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP); - obtainMessage(MSG_SHOW_GESTURE_PREVIEW_AND_SUGGESTION_STRIP, ARG1_NOT_GESTURE_INPUT, - ARG2_WITH_TYPED_WORD, - new Pair<SuggestedWords, String>(suggestedWords, typedWord)).sendToTarget(); + ARG1_NOT_GESTURE_INPUT, ARG2_UNUSED, suggestedWords).sendToTarget(); } public void onEndBatchInput(final SuggestedWords suggestedWords) { @@ -1176,6 +1157,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mInputLogic.mSuggest.mDictionaryFacilitator.addWordToUserDictionary(wordToEdit); } + // Callback for the {@link SuggestionStripView}, to call when the important notice strip is + // pressed. + @Override + public void showImportantNoticeContents() { + // TODO: Show dialog to display important notice contents. + } + public void displaySettingsDialog() { if (isShowingOptionDialog()) return; showSubtypeSelectorAndSettings(); @@ -1273,7 +1261,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // This method must run on the UI Thread. private void showGesturePreviewAndSuggestionStrip(final SuggestedWords suggestedWords, final boolean dismissGestureFloatingPreviewText) { - showSuggestionStrip(suggestedWords, suggestedWords.mTypedWord); + showSuggestionStrip(suggestedWords); final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView(); mainKeyboardView.showGestureFloatingPreviewText(suggestedWords); if (dismissGestureFloatingPreviewText) { @@ -1409,8 +1397,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } // TODO[IL]: Define a clean interface for this - public void showSuggestionStrip(final SuggestedWords sourceSuggestedWords, - final String typedWord) { + public void showSuggestionStrip(final SuggestedWords sourceSuggestedWords) { final SuggestedWords suggestedWords = sourceSuggestedWords.isEmpty() ? SuggestedWords.EMPTY : sourceSuggestedWords; final String autoCorrection; @@ -1419,7 +1406,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } else { // We can't use suggestedWords.getWord(SuggestedWords.INDEX_OF_TYPED_WORD) // because it may differ from mWordComposer.mTypedWord. - autoCorrection = typedWord; + autoCorrection = sourceSuggestedWords.mTypedWord; } if (SuggestedWords.EMPTY != suggestedWords) { mInputLogic.mWordComposer.setAutoCorrection(autoCorrection); @@ -1427,7 +1414,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen setSuggestedWords(suggestedWords, isSuggestionsStripVisible()); // Cache the auto-correction in accessibility code so we can speak it if the user // touches a key that will insert it. - AccessibilityUtils.getInstance().setAutoCorrection(suggestedWords, typedWord); + AccessibilityUtils.getInstance().setAutoCorrection(suggestedWords, + sourceSuggestedWords.mTypedWord); } // Called from {@link SuggestionStripView} through the {@link SuggestionStripView#Listener} @@ -1517,8 +1505,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } if (showingAddToDictionaryHint && suggest.mDictionaryFacilitator.isUserDictionaryEnabled()) { - mSuggestionStripView.showAddToDictionaryHint( - suggestion, currentSettings.mHintToSaveText); + mSuggestionStripView.showAddToDictionaryHint(suggestion); } else { // If we're not showing the "Touch again to save", then update the suggestion strip. mHandler.postUpdateSuggestionStrip(); @@ -1538,18 +1525,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } } - public void unsetIsAutoCorrectionIndicatorOnAndCallShowSuggestionStrip( - final SuggestedWords suggestedWords, final String typedWord) { - // Note that it's very important here that suggestedWords.mWillAutoCorrect is false. - // We never want to auto-correct on a resumed suggestion. Please refer to the three places - // above in restartSuggestionsOnWordTouchedByCursor() where suggestedWords is affected. - // We also need to unset mIsAutoCorrectionIndicatorOn to avoid showSuggestionStrip touching - // the text to adapt it. - // TODO: remove mIsAutoCorrectionIndicatorOn (see comment on definition) - mInputLogic.mIsAutoCorrectionIndicatorOn = false; - mHandler.showSuggestionStripWithTypedWord(suggestedWords, typedWord); - } - // TODO: Make this private // Outside LatinIME, only used by the {@link InputTestsBase} test suite. @UsedForTesting diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java index 982a97a5e..dbaf822e4 100644 --- a/java/src/com/android/inputmethod/latin/SuggestedWords.java +++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java @@ -68,6 +68,21 @@ public final class SuggestedWords { final boolean isObsoleteSuggestions, final boolean isPrediction, final int sequenceNumber) { + this(suggestedWordInfoList, + suggestedWordInfoList.isEmpty() ? null + : suggestedWordInfoList.get(INDEX_OF_TYPED_WORD).mWord, + typedWordValid, willAutoCorrect, isPunctuationSuggestions, + isObsoleteSuggestions, isPrediction, sequenceNumber); + } + + public SuggestedWords(final ArrayList<SuggestedWordInfo> suggestedWordInfoList, + final String typedWord, + final boolean typedWordValid, + final boolean willAutoCorrect, + final boolean isPunctuationSuggestions, + final boolean isObsoleteSuggestions, + final boolean isPrediction, + final int sequenceNumber) { mSuggestedWordInfoList = suggestedWordInfoList; mTypedWordValid = typedWordValid; mWillAutoCorrect = willAutoCorrect; @@ -75,7 +90,7 @@ public final class SuggestedWords { mIsObsoleteSuggestions = isObsoleteSuggestions; mIsPrediction = isPrediction; mSequenceNumber = sequenceNumber; - mTypedWord = suggestedWordInfoList.isEmpty() ? null : getWord(INDEX_OF_TYPED_WORD); + mTypedWord = typedWord; } public boolean isEmpty() { @@ -279,17 +294,21 @@ public final class SuggestedWords { // 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(); + String typedWord = null; for (int i = 0; i < mSuggestedWordInfoList.size(); ++i) { final SuggestedWordInfo info = mSuggestedWordInfoList.get(i); if (SuggestedWordInfo.KIND_TYPED != info.mKind) { newSuggestions.add(info); + } else { + assert(null == typedWord); + typedWord = info.mWord; } } // We should never autocorrect, so we say the typed word is valid. Also, in this case, // no auto-correction should take place hence willAutoCorrect = false. - return new SuggestedWords(newSuggestions, true /* typedWordValid */, + return new SuggestedWords(newSuggestions, typedWord, true /* typedWordValid */, false /* willAutoCorrect */, mIsPunctuationSuggestions, mIsObsoleteSuggestions, - mIsPrediction); + mIsPrediction, NOT_A_SEQUENCE_NUMBER); } // Creates a new SuggestedWordInfo from the currently suggested words that removes all but the diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java index ce3ef5374..1bc67b2a0 100644 --- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java +++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java @@ -1120,7 +1120,7 @@ public final class InputLogic { final SuggestedWords suggestedWords = holder.get(null, Constants.GET_SUGGESTED_WORDS_TIMEOUT); if (suggestedWords != null) { - mLatinIME.showSuggestionStrip(suggestedWords, suggestedWords.mTypedWord); + mLatinIME.showSuggestionStrip(suggestedWords); } } @@ -1217,21 +1217,18 @@ public final class InputLogic { // Since there is only one word, willAutoCorrect is false. suggestedWords = suggestedWordsIncludingTypedWord; } - // We need to pass typedWord because mWordComposer.mTypedWord may - // differ from typedWord. - mLatinIME.unsetIsAutoCorrectionIndicatorOnAndCallShowSuggestionStrip( - suggestedWords, typedWord); + mIsAutoCorrectionIndicatorOn = false; + mLatinIME.mHandler.showSuggestionStrip(suggestedWords); }}); } else { // We found suggestion spans in the word. We'll create the SuggestedWords out of // them, and make willAutoCorrect false. - final SuggestedWords suggestedWords = new SuggestedWords(suggestions, + final SuggestedWords suggestedWords = new SuggestedWords(suggestions, typedWord, true /* typedWordValid */, false /* willAutoCorrect */, false /* isPunctuationSuggestions */, false /* isObsoleteSuggestions */, - false /* isPrediction */); - // We need to pass typedWord because mWordComposer.mTypedWord may differ from typedWord. - mLatinIME.unsetIsAutoCorrectionIndicatorOnAndCallShowSuggestionStrip(suggestedWords, - typedWord); + false /* isPrediction */, SuggestedWords.NOT_A_SEQUENCE_NUMBER); + mIsAutoCorrectionIndicatorOn = false; + mLatinIME.mHandler.showSuggestionStrip(suggestedWords); } } @@ -1624,8 +1621,7 @@ public final class InputLogic { mConnection.commitText(batchInputText.substring(0, indexOfLastSpace), 1); final SuggestedWords suggestedWordsForLastWordOfPhraseGesture = suggestedWords.getSuggestedWordsForLastWordOfPhraseGesture(); - mLatinIME.showSuggestionStrip(suggestedWordsForLastWordOfPhraseGesture, - suggestedWordsForLastWordOfPhraseGesture.mTypedWord); + mLatinIME.showSuggestionStrip(suggestedWordsForLastWordOfPhraseGesture); } final String lastWord = batchInputText.substring(indexOfLastSpace); mWordComposer.setBatchInputWord(lastWord); diff --git a/java/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java index f8fa68f45..1a9118147 100644 --- a/java/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java +++ b/java/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java @@ -48,7 +48,7 @@ public abstract class AbstractDictDecoder implements DictDecoder { throw new UnsupportedFormatException("Unsupported version : " + version); } // TODO: Remove this field. - final int optionsFlags = HeaderReader.readOptionFlags(headerBuffer); + HeaderReader.readOptionFlags(headerBuffer); final int headerSize = HeaderReader.readHeaderSize(headerBuffer); if (headerSize < 0) { throw new UnsupportedFormatException("header size can't be negative."); @@ -59,8 +59,8 @@ public abstract class AbstractDictDecoder implements DictDecoder { final FileHeader header = new FileHeader(headerSize, new FusionDictionary.DictionaryOptions(attributes), - new FormatOptions(version, - 0 != (optionsFlags & FormatSpec.CONTAINS_TIMESTAMP_FLAG))); + new FormatOptions(version, FileHeader.ATTRIBUTE_VALUE_TRUE.equals( + attributes.get(FileHeader.HAS_HISTORICAL_INFO_KEY)))); return header; } diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java index 9a24c47af..31747155e 100644 --- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java @@ -499,7 +499,6 @@ public final class BinaryDictDecoderUtils { final int nodeArrayOriginPos = dictDecoder.getPosition(); do { // Scan the linked-list node. - final int nodeArrayHeadPos = dictDecoder.getPosition(); final int count = dictDecoder.readPtNodeCount(); int groupPos = dictDecoder.getPosition(); for (int i = count; i > 0; --i) { // Scan the array of PtNode. diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java index bb40e0dd5..eff8fc375 100644 --- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java @@ -756,14 +756,6 @@ public class BinaryDictEncoderUtils { } /** - * Makes the 2-byte value for options flags. Unused at the moment, and always 0. - */ - private static final int makeOptionsValue(final FormatOptions formatOptions) { - // TODO: why doesn't this handle CONTAINS_TIMESTAMP_FLAG? - return 0; - } - - /** * Makes the flag value for a shortcut. * * @param more whether there are more attributes after this one. @@ -949,7 +941,8 @@ public class BinaryDictEncoderUtils { headerBuffer.write((byte) (0xFF & version)); // Options flags - final int options = makeOptionsValue(formatOptions); + // TODO: Remove this field. + final int options = 0; headerBuffer.write((byte) (0xFF & (options >> 8))); headerBuffer.write((byte) (0xFF & options)); final int headerSizeOffset = headerBuffer.size(); diff --git a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java index 437fa942b..74e305976 100644 --- a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java +++ b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java @@ -192,10 +192,6 @@ public final class FormatSpec { static final int MINIMUM_SUPPORTED_VERSION = VERSION2; static final int MAXIMUM_SUPPORTED_VERSION = VERSION4; - // These options need to be the same numeric values as the one in the native reading code. - // TODO: Make the native reading code read this variable. - static final int CONTAINS_TIMESTAMP_FLAG = 0x10; - // TODO: Make this value adaptative to content data, store it in the header, and // use it in the reading code. static final int MAX_WORD_LENGTH = Constants.DICTIONARY_MAX_WORD_LENGTH; @@ -249,26 +245,26 @@ public final class FormatSpec { static final String TRIE_FILE_EXTENSION = ".trie"; public static final String HEADER_FILE_EXTENSION = ".header"; static final String FREQ_FILE_EXTENSION = ".freq"; - static final String UNIGRAM_TIMESTAMP_FILE_EXTENSION = ".timestamp"; // tat = Terminal Address Table static final String TERMINAL_ADDRESS_TABLE_FILE_EXTENSION = ".tat"; static final String BIGRAM_FILE_EXTENSION = ".bigram"; static final String SHORTCUT_FILE_EXTENSION = ".shortcut"; static final String LOOKUP_TABLE_FILE_SUFFIX = "_lookup"; static final String CONTENT_TABLE_FILE_SUFFIX = "_index"; + static final int FLAGS_IN_FREQ_FILE_SIZE = 1; static final int FREQUENCY_AND_FLAGS_SIZE = 2; static final int TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE = 3; static final int UNIGRAM_TIMESTAMP_SIZE = 4; + static final int UNIGRAM_COUNTER_SIZE = 1; + static final int UNIGRAM_LEVEL_SIZE = 1; // With the English main dictionary as of October 2013, the size of bigram address table is // is 345KB with the block size being 16. // This is 54% of that of full address table. static final int BIGRAM_ADDRESS_TABLE_BLOCK_SIZE = 16; - static final int BIGRAM_CONTENT_COUNT = 2; + static final int BIGRAM_CONTENT_COUNT = 1; static final int BIGRAM_FREQ_CONTENT_INDEX = 0; - static final int BIGRAM_TIMESTAMP_CONTENT_INDEX = 1; static final String BIGRAM_FREQ_CONTENT_ID = "_freq"; - static final String BIGRAM_TIMESTAMP_CONTENT_ID = "_timestamp"; static final int BIGRAM_TIMESTAMP_SIZE = 4; static final int BIGRAM_COUNTER_SIZE = 1; static final int BIGRAM_LEVEL_SIZE = 1; @@ -340,16 +336,19 @@ public final class FormatSpec { public final int mBodyOffset; public final DictionaryOptions mDictionaryOptions; public final FormatOptions mFormatOptions; + // Note that these are corresponding definitions in native code in latinime::HeaderPolicy // and latinime::HeaderReadWriteUtils. - public static final String USES_FORGETTING_CURVE_ATTRIBUTE = "USES_FORGETTING_CURVE"; - public static final String HAS_HISTORICAL_INFO_ATTRIBUTE = "HAS_HISTORICAL_INFO"; + // TODO: Standardize the key names and bump up the format version, taking care not to + // break format version 2 dictionaries. + public static final String DICTIONARY_VERSION_KEY = "version"; + public static final String DICTIONARY_LOCALE_KEY = "locale"; + public static final String DICTIONARY_ID_KEY = "dictionary"; + public static final String DICTIONARY_DESCRIPTION_KEY = "description"; + public static final String DICTIONARY_DATE_KEY = "date"; + public static final String HAS_HISTORICAL_INFO_KEY = "HAS_HISTORICAL_INFO"; + public static final String USES_FORGETTING_CURVE_KEY = "USES_FORGETTING_CURVE"; public static final String ATTRIBUTE_VALUE_TRUE = "1"; - - public static final String DICTIONARY_VERSION_ATTRIBUTE = "version"; - public static final String DICTIONARY_LOCALE_ATTRIBUTE = "locale"; - public static final String DICTIONARY_ID_ATTRIBUTE = "dictionary"; - private static final String DICTIONARY_DESCRIPTION_ATTRIBUTE = "description"; public FileHeader(final int headerSize, final DictionaryOptions dictionaryOptions, final FormatOptions formatOptions) throws UnsupportedFormatException { mDictionaryOptions = dictionaryOptions; @@ -369,24 +368,24 @@ public final class FormatSpec { // Helper method to get the locale as a String public String getLocaleString() { - return mDictionaryOptions.mAttributes.get(FileHeader.DICTIONARY_LOCALE_ATTRIBUTE); + return mDictionaryOptions.mAttributes.get(FileHeader.DICTIONARY_LOCALE_KEY); } // Helper method to get the version String public String getVersion() { - return mDictionaryOptions.mAttributes.get(FileHeader.DICTIONARY_VERSION_ATTRIBUTE); + return mDictionaryOptions.mAttributes.get(FileHeader.DICTIONARY_VERSION_KEY); } // Helper method to get the dictionary ID as a String public String getId() { - return mDictionaryOptions.mAttributes.get(FileHeader.DICTIONARY_ID_ATTRIBUTE); + return mDictionaryOptions.mAttributes.get(FileHeader.DICTIONARY_ID_KEY); } // Helper method to get the description public String getDescription() { // TODO: Right now each dictionary file comes with a description in its own language. // It will display as is no matter the device's locale. It should be internationalized. - return mDictionaryOptions.mAttributes.get(FileHeader.DICTIONARY_DESCRIPTION_ATTRIBUTE); + return mDictionaryOptions.mAttributes.get(FileHeader.DICTIONARY_DESCRIPTION_KEY); } } diff --git a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java index fdf2ae7b5..5b0e8399a 100644 --- a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java +++ b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java @@ -61,6 +61,7 @@ public final class FusionDictionary implements Iterable<Word> { mData = new ArrayList<PtNode>(); } public PtNodeArray(ArrayList<PtNode> data) { + Collections.sort(data, PTNODE_COMPARATOR); mData = data; } } diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java index 7071893d2..e459e4861 100644 --- a/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java +++ b/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java @@ -143,7 +143,7 @@ public class Ver4DictDecoder extends AbstractDictDecoder { mTerminalAddressTableBuffer = mBufferFactory.getDictionaryBuffer( getFile(FILETYPE_TERMINAL_ADDRESS_TABLE)); mBigramReader = new BigramContentReader(mDictDirectory.getName(), - mDictDirectory, mBufferFactory, false); + mDictDirectory, mBufferFactory); mBigramReader.openBuffers(); mShortcutReader = new ShortcutContentReader(mDictDirectory.getName(), mDictDirectory, mBufferFactory); @@ -184,39 +184,24 @@ public class Ver4DictDecoder extends AbstractDictDecoder { */ protected static class BigramContentReader extends SparseTableContentReader { public BigramContentReader(final String name, final File baseDir, - final DictionaryBufferFactory factory, final boolean hasTimestamp) { + final DictionaryBufferFactory factory) { super(name + FormatSpec.BIGRAM_FILE_EXTENSION, FormatSpec.BIGRAM_ADDRESS_TABLE_BLOCK_SIZE, baseDir, - getContentFilenames(name, hasTimestamp), getContentIds(hasTimestamp), factory); + getContentFilenames(name), getContentIds(), factory); } // TODO: Consolidate this method and BigramContentWriter.getContentFilenames. - protected static String[] getContentFilenames(final String name, - final boolean hasTimestamp) { - final String[] contentFilenames; - if (hasTimestamp) { - contentFilenames = new String[] { name + FormatSpec.BIGRAM_FILE_EXTENSION, - name + FormatSpec.BIGRAM_FILE_EXTENSION }; - } else { - contentFilenames = new String[] { name + FormatSpec.BIGRAM_FILE_EXTENSION }; - } - return contentFilenames; + protected static String[] getContentFilenames(final String name) { + return new String[] { name + FormatSpec.BIGRAM_FILE_EXTENSION }; } // TODO: Consolidate this method and BigramContentWriter.getContentIds. - protected static String[] getContentIds(final boolean hasTimestamp) { - final String[] contentIds; - if (hasTimestamp) { - contentIds = new String[] { FormatSpec.BIGRAM_FREQ_CONTENT_ID, - FormatSpec.BIGRAM_TIMESTAMP_CONTENT_ID }; - } else { - contentIds = new String[] { FormatSpec.BIGRAM_FREQ_CONTENT_ID }; - } - return contentIds; + protected static String[] getContentIds() { + return new String[] { FormatSpec.BIGRAM_FREQ_CONTENT_ID }; } public ArrayList<PendingAttribute> readTargetsAndFrequencies(final int terminalId, - final DictBuffer terminalAddressTableBuffer) { + final DictBuffer terminalAddressTableBuffer, final FormatOptions options) { final ArrayList<PendingAttribute> bigrams = CollectionUtils.newArrayList(); read(FormatSpec.BIGRAM_FREQ_CONTENT_INDEX, terminalId, new SparseTableContentReaderInterface() { @@ -226,14 +211,26 @@ public class Ver4DictDecoder extends AbstractDictDecoder { // If bigrams.size() reaches FormatSpec.MAX_BIGRAMS_IN_A_PTNODE, // remaining bigram entries are ignored. final int bigramFlags = buffer.readUnsignedByte(); + final int probability; + + if (options.mHasTimestamp) { + probability = buffer.readUnsignedByte(); + // Skip timestamp + buffer.readInt(); + // Skip level + buffer.readUnsignedByte(); + // Skip count + buffer.readUnsignedByte(); + } else { + probability = bigramFlags + & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY; + } final int targetTerminalId = buffer.readUnsignedInt24(); terminalAddressTableBuffer.position(targetTerminalId * FormatSpec.TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE); final int targetAddress = terminalAddressTableBuffer.readUnsignedInt24(); - bigrams.add(new PendingAttribute(bigramFlags - & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY, - targetAddress)); + bigrams.add(new PendingAttribute(probability, targetAddress)); if (0 == (bigramFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT)) { break; @@ -286,8 +283,19 @@ public class Ver4DictDecoder extends AbstractDictDecoder { } protected static class PtNodeReader extends AbstractDictDecoder.PtNodeReader { - protected static int readFrequency(final DictBuffer frequencyBuffer, final int terminalId) { - frequencyBuffer.position(terminalId * FormatSpec.FREQUENCY_AND_FLAGS_SIZE + 1); + protected static int readFrequency(final DictBuffer frequencyBuffer, final int terminalId, + final FormatOptions formatOptions) { + final int readingPos; + if (formatOptions.mHasTimestamp) { + final int entrySize = FormatSpec.FREQUENCY_AND_FLAGS_SIZE + + FormatSpec.UNIGRAM_TIMESTAMP_SIZE + FormatSpec.UNIGRAM_LEVEL_SIZE + + FormatSpec.UNIGRAM_COUNTER_SIZE; + readingPos = terminalId * entrySize + FormatSpec.FLAGS_IN_FREQ_FILE_SIZE; + } else { + readingPos = terminalId * FormatSpec.FREQUENCY_AND_FLAGS_SIZE + + FormatSpec.FLAGS_IN_FREQ_FILE_SIZE; + } + frequencyBuffer.position(readingPos); return frequencyBuffer.readUnsignedByte(); } @@ -354,12 +362,12 @@ public class Ver4DictDecoder extends AbstractDictDecoder { } @Override - public PtNodeInfo readPtNode(int ptNodePos, FormatOptions options) { + public PtNodeInfo readPtNode(final int ptNodePos, final FormatOptions options) { final Ver4PtNodeInfo nodeInfo = readVer4PtNodeInfo(ptNodePos, options); final int frequency; if (0 != (FormatSpec.FLAG_IS_TERMINAL & nodeInfo.mFlags)) { - frequency = PtNodeReader.readFrequency(mFrequencyBuffer, nodeInfo.mTerminalId); + frequency = PtNodeReader.readFrequency(mFrequencyBuffer, nodeInfo.mTerminalId, options); } else { frequency = PtNode.NOT_A_TERMINAL; } @@ -367,7 +375,7 @@ public class Ver4DictDecoder extends AbstractDictDecoder { final ArrayList<WeightedString> shortcutTargets = mShortcutReader.readShortcuts( nodeInfo.mTerminalId); final ArrayList<PendingAttribute> bigrams = mBigramReader.readTargetsAndFrequencies( - nodeInfo.mTerminalId, mTerminalAddressTableBuffer); + nodeInfo.mTerminalId, mTerminalAddressTableBuffer, options); return new PtNodeInfo(ptNodePos, ptNodePos + nodeInfo.mNodeSize, nodeInfo.mFlags, nodeInfo.mCharacters, frequency, nodeInfo.mParentPos, nodeInfo.mChildrenPos, diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java index d34aa171e..b12f79b07 100644 --- a/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java +++ b/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java @@ -62,7 +62,7 @@ public class Ver4DictEncoder implements DictEncoder { final BinaryDictionary binaryDict = new BinaryDictionary(mDictPlacedDir.getAbsolutePath(), 0l, mDictPlacedDir.length(), true /* useFullEditDistance */, LocaleUtils.constructLocaleFromString(dict.mOptions.mAttributes.get( - FormatSpec.FileHeader.DICTIONARY_LOCALE_ATTRIBUTE)), + FormatSpec.FileHeader.DICTIONARY_LOCALE_KEY)), Dictionary.TYPE_USER /* Dictionary type. Does not matter for us */, true /* isUpdatable */); if (!binaryDict.isValidDictionary()) { diff --git a/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java b/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java index 4a610e6f9..d636a253a 100644 --- a/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java +++ b/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java @@ -95,13 +95,13 @@ public abstract class DecayingExpandableBinaryDictionaryBase extends ExpandableB @Override protected Map<String, String> getHeaderAttributeMap() { HashMap<String, String> attributeMap = new HashMap<String, String>(); - attributeMap.put(FormatSpec.FileHeader.USES_FORGETTING_CURVE_ATTRIBUTE, + attributeMap.put(FormatSpec.FileHeader.USES_FORGETTING_CURVE_KEY, FormatSpec.FileHeader.ATTRIBUTE_VALUE_TRUE); - attributeMap.put(FormatSpec.FileHeader.HAS_HISTORICAL_INFO_ATTRIBUTE, + attributeMap.put(FormatSpec.FileHeader.HAS_HISTORICAL_INFO_KEY, FormatSpec.FileHeader.ATTRIBUTE_VALUE_TRUE); - attributeMap.put(FormatSpec.FileHeader.DICTIONARY_ID_ATTRIBUTE, mDictName); - attributeMap.put(FormatSpec.FileHeader.DICTIONARY_LOCALE_ATTRIBUTE, mLocale.toString()); - attributeMap.put(FormatSpec.FileHeader.DICTIONARY_VERSION_ATTRIBUTE, + attributeMap.put(FormatSpec.FileHeader.DICTIONARY_ID_KEY, mDictName); + attributeMap.put(FormatSpec.FileHeader.DICTIONARY_LOCALE_KEY, mLocale.toString()); + attributeMap.put(FormatSpec.FileHeader.DICTIONARY_VERSION_KEY, String.valueOf(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()))); return attributeMap; } diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java index e4ae64fdc..2979544ae 100644 --- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java +++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java @@ -49,7 +49,6 @@ public final class SettingsValues { // From resources: public final SpacingAndPunctuations mSpacingAndPunctuations; public final int mDelayUpdateOldSuggestions; - public final CharSequence mHintToSaveText; // From preferences, in the same order as xml/prefs.xml: public final boolean mAutoCap; @@ -101,7 +100,6 @@ public final class SettingsValues { // Get the resources mDelayUpdateOldSuggestions = res.getInteger(R.integer.config_delay_update_old_suggestions); mSpacingAndPunctuations = new SpacingAndPunctuations(res); - mHintToSaveText = res.getText(R.string.hint_add_to_dictionary); // Store the input attributes if (null == inputAttributes) { diff --git a/java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java b/java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java index 8ba32ff76..70cb2b285 100644 --- a/java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java +++ b/java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java @@ -19,6 +19,7 @@ package com.android.inputmethod.latin.settings; import android.content.res.Resources; import com.android.inputmethod.keyboard.internal.KeySpecParser; +import com.android.inputmethod.keyboard.internal.MoreKeySpec; import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.Dictionary; import com.android.inputmethod.latin.R; @@ -55,7 +56,7 @@ public final class SpacingAndPunctuations { res.getString(R.string.symbols_word_connectors)); mSortedWordSeparators = StringUtils.toSortedCodePointArray( res.getString(R.string.symbols_word_separators)); - final String[] suggestPuncsSpec = KeySpecParser.splitKeySpecs(res.getString( + final String[] suggestPuncsSpec = MoreKeySpec.splitKeySpecs(res.getString( R.string.suggested_punctuations)); mSuggestPuncList = createSuggestPuncList(suggestPuncsSpec); mSentenceSeparator = res.getInteger(R.integer.sentence_separator); diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java index 3cd9d9555..da084e1e9 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java @@ -28,7 +28,6 @@ import android.graphics.Rect; import android.graphics.Typeface; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; -import android.support.v4.view.GravityCompat; import android.support.v4.view.ViewCompat; import android.text.Spannable; import android.text.SpannableString; @@ -45,11 +44,13 @@ import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.TextView; +import com.android.inputmethod.compat.TextViewCompatUtils; import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SuggestedWords; import com.android.inputmethod.latin.utils.AutoCorrectionUtils; import com.android.inputmethod.latin.utils.ResourceUtils; +import com.android.inputmethod.latin.utils.SubtypeLocaleUtils; import com.android.inputmethod.latin.utils.ViewLayoutUtils; import java.util.ArrayList; @@ -459,7 +460,7 @@ final class SuggestionStripLayoutHelper { } public void layoutAddToDictionaryHint(final String word, final ViewGroup addToDictionaryStrip, - final int stripWidth, final CharSequence hintText) { + final int stripWidth) { final int width = stripWidth - mDividerWidth - mPadding * 2; final TextView wordView = (TextView)addToDictionaryStrip.findViewById(R.id.word_to_save); @@ -473,13 +474,17 @@ final class SuggestionStripLayoutHelper { final TextView hintView = (TextView)addToDictionaryStrip.findViewById( R.id.hint_add_to_dictionary); - hintView.setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL | GravityCompat.START); hintView.setTextColor(mColorAutoCorrect); final boolean isRtlLanguage = (ViewCompat.getLayoutDirection(addToDictionaryStrip) == ViewCompat.LAYOUT_DIRECTION_RTL); - final String hintWithArrow = (isRtlLanguage ? RIGHTWARDS_ARROW : LEFTWARDS_ARROW) - + hintText; + final String arrow = isRtlLanguage ? RIGHTWARDS_ARROW : LEFTWARDS_ARROW; + final Resources res = addToDictionaryStrip.getResources(); + final boolean isRtlSystem = SubtypeLocaleUtils.isRtlLanguage(res.getConfiguration().locale); + final CharSequence hintText = res.getText(R.string.hint_add_to_dictionary); + final String hintWithArrow = (isRtlLanguage == isRtlSystem) + ? (arrow + hintText) : (hintText + arrow); final int hintWidth = width - wordWidth; + hintView.setTextScaleX(1.0f); // Reset textScaleX. final float hintScaleX = getTextScaleX(hintWithArrow, hintWidth, hintView.getPaint()); hintView.setText(hintWithArrow); hintView.setTextScaleX(hintScaleX); @@ -487,7 +492,24 @@ final class SuggestionStripLayoutHelper { hintView, 1.0f - mCenterSuggestionWeight, ViewGroup.LayoutParams.MATCH_PARENT); } - private static void setLayoutWeight(final View v, final float weight, final int height) { + public void layoutImportantNotice(final View importantNoticeStrip, final int stripWidth) { + final Resources res = importantNoticeStrip.getResources(); + final Drawable infoIcon = res.getDrawable(R.drawable.sym_keyboard_info_holo_dark); + final Drawable moreIcon = res.getDrawable(R.drawable.sym_keyboard_more_holo_dark); + final int width = stripWidth - infoIcon.getIntrinsicWidth() - moreIcon.getIntrinsicWidth(); + final TextView titleView = (TextView)importantNoticeStrip.findViewById( + R.id.important_notice_title); + titleView.setTextColor(mColorAutoCorrect); + TextViewCompatUtils.setCompoundDrawablesRelativeWithIntrinsicBounds( + titleView, infoIcon, null, moreIcon, null); + final CharSequence importantNoticeTitle = res.getText(R.string.important_notice_title); + titleView.setTextScaleX(1.0f); // Reset textScaleX. + final float titleScaleX = getTextScaleX(importantNoticeTitle, width, titleView.getPaint()); + titleView.setText(importantNoticeTitle); + titleView.setTextScaleX(titleScaleX); + } + + static void setLayoutWeight(final View v, final float weight, final int height) { final ViewGroup.LayoutParams lp = v.getLayoutParams(); if (lp instanceof LinearLayout.LayoutParams) { final LinearLayout.LayoutParams llp = (LinearLayout.LayoutParams)lp; diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java index 0ebf5cba5..fe95d6781 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java @@ -18,8 +18,10 @@ package com.android.inputmethod.latin.suggestions; import android.content.Context; import android.content.res.Resources; +import android.graphics.Color; import android.support.v4.view.ViewCompat; import android.util.AttributeSet; +import android.util.TypedValue; import android.view.GestureDetector; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -52,13 +54,16 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick OnLongClickListener { public interface Listener { public void addWordToUserDictionary(String word); + public void showImportantNoticeContents(); public void pickSuggestionManually(int index, SuggestedWordInfo word); } static final boolean DBG = LatinImeLogger.sDBG; + private static final float DEBUG_INFO_TEXT_SIZE_IN_DIP = 6.0f; private final ViewGroup mSuggestionsStrip; private final ViewGroup mAddToDictionaryStrip; + private final View mImportantNoticeStrip; MainKeyboardView mMainKeyboardView; private final View mMoreSuggestionsContainer; @@ -78,10 +83,13 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick private static class StripVisibilityGroup { private final View mSuggestionsStrip; private final View mAddToDictionaryStrip; + private final View mImportantNoticeStrip; - public StripVisibilityGroup(final View suggestionsStrip, final View addToDictionaryStrip) { + public StripVisibilityGroup(final View suggestionsStrip, final View addToDictionaryStrip, + final View importantNoticeStrip) { mSuggestionsStrip = suggestionsStrip; mAddToDictionaryStrip = addToDictionaryStrip; + mImportantNoticeStrip = importantNoticeStrip; showSuggestionsStrip(); } @@ -90,16 +98,25 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick : ViewCompat.LAYOUT_DIRECTION_LTR; ViewCompat.setLayoutDirection(mSuggestionsStrip, layoutDirection); ViewCompat.setLayoutDirection(mAddToDictionaryStrip, layoutDirection); + ViewCompat.setLayoutDirection(mImportantNoticeStrip, layoutDirection); } public void showSuggestionsStrip() { mSuggestionsStrip.setVisibility(VISIBLE); mAddToDictionaryStrip.setVisibility(INVISIBLE); + mImportantNoticeStrip.setVisibility(INVISIBLE); } public void showAddToDictionaryStrip() { mSuggestionsStrip.setVisibility(INVISIBLE); mAddToDictionaryStrip.setVisibility(VISIBLE); + mImportantNoticeStrip.setVisibility(INVISIBLE); + } + + public void showImportantNoticeStrip() { + mSuggestionsStrip.setVisibility(INVISIBLE); + mAddToDictionaryStrip.setVisibility(INVISIBLE); + mImportantNoticeStrip.setVisibility(VISIBLE); } public boolean isShowingAddToDictionaryStrip() { @@ -125,17 +142,22 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick mSuggestionsStrip = (ViewGroup)findViewById(R.id.suggestions_strip); mAddToDictionaryStrip = (ViewGroup)findViewById(R.id.add_to_dictionary_strip); - mStripVisibilityGroup = new StripVisibilityGroup(mSuggestionsStrip, mAddToDictionaryStrip); + mImportantNoticeStrip = findViewById(R.id.important_notice_strip); + mStripVisibilityGroup = new StripVisibilityGroup(mSuggestionsStrip, mAddToDictionaryStrip, + mImportantNoticeStrip); for (int pos = 0; pos < SuggestedWords.MAX_SUGGESTIONS; pos++) { - final TextView word = (TextView)inflater.inflate(R.layout.suggestion_word, null); + final TextView word = new TextView(context, null, R.attr.suggestionWordStyle); word.setOnClickListener(this); word.setOnLongClickListener(this); mWordViews.add(word); final View divider = inflater.inflate(R.layout.suggestion_divider, null); divider.setOnClickListener(this); mDividerViews.add(divider); - mDebugInfoViews.add((TextView)inflater.inflate(R.layout.suggestion_info, null)); + final TextView info = new TextView(context, null, R.attr.suggestionWordStyle); + info.setTextColor(Color.WHITE); + info.setTextSize(TypedValue.COMPLEX_UNIT_DIP, DEBUG_INFO_TEXT_SIZE_IN_DIP); + mDebugInfoViews.add(info); } mLayoutHelper = new SuggestionStripLayoutHelper( @@ -170,6 +192,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { ResearchLogger.suggestionStripView_setSuggestions(mSuggestedWords); } + mStripVisibilityGroup.showSuggestionsStrip(); } public int setMoreSuggestionsHeight(final int remainingHeight) { @@ -180,8 +203,8 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick return mStripVisibilityGroup.isShowingAddToDictionaryStrip(); } - public void showAddToDictionaryHint(final String word, final CharSequence hintText) { - mLayoutHelper.layoutAddToDictionaryHint(word, mAddToDictionaryStrip, getWidth(), hintText); + public void showAddToDictionaryHint(final String word) { + mLayoutHelper.layoutAddToDictionaryHint(word, mAddToDictionaryStrip, getWidth()); // {@link TextView#setTag()} is used to hold the word to be added to dictionary. The word // will be extracted at {@link #onClick(View)}. mAddToDictionaryStrip.setTag(word); @@ -197,6 +220,12 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick return false; } + public void showImportantNoticeTitle() { + mLayoutHelper.layoutImportantNotice(mImportantNoticeStrip, getWidth()); + mStripVisibilityGroup.showImportantNoticeStrip(); + mImportantNoticeStrip.setOnClickListener(this); + } + public void clear() { mSuggestionsStrip.removeAllViews(); removeAllDebugInfoViews(); @@ -354,6 +383,10 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick @Override public void onClick(final View view) { + if (view == mImportantNoticeStrip) { + mListener.showImportantNoticeContents(); + return; + } final Object tag = view.getTag(); // {@link String} tag is set at {@link #showAddToDictionaryHint(String,CharSequence)}. if (tag instanceof String) { diff --git a/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java b/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java index cc25102ce..bbfa0f091 100644 --- a/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java @@ -102,4 +102,19 @@ public final class CollectionUtils { 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(); + } + if (start < 0 || start > end || end > array.length) { + throw new IllegalArgumentException(); + } + + final ArrayList<E> list = newArrayList(end - start); + for (int i = start; i < end; i++) { + list.add(array[i]); + } + return list; + } } diff --git a/java/src/com/android/inputmethod/latin/utils/CsvUtils.java b/java/src/com/android/inputmethod/latin/utils/CsvUtils.java index 36b927eea..b18a1d83b 100644 --- a/java/src/com/android/inputmethod/latin/utils/CsvUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/CsvUtils.java @@ -17,6 +17,7 @@ package com.android.inputmethod.latin.utils; import com.android.inputmethod.annotations.UsedForTesting; +import com.android.inputmethod.latin.Constants; import java.util.ArrayList; @@ -57,9 +58,9 @@ public final class CsvUtils { // Note that none of these characters match high or low surrogate characters, so we need not // take care of matching by code point. - private static final char COMMA = ','; - private static final char SPACE = ' '; - private static final char QUOTE = '"'; + private static final char COMMA = Constants.CODE_COMMA; + private static final char SPACE = Constants.CODE_SPACE; + private static final char QUOTE = Constants.CODE_DOUBLE_QUOTE; @SuppressWarnings("serial") public static class CsvParseException extends RuntimeException { diff --git a/java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java b/java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java new file mode 100644 index 000000000..4a0823155 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin.utils; + +import android.content.Context; +import android.content.SharedPreferences; +import android.provider.Settings; +import android.provider.Settings.SettingNotFoundException; +import android.util.Log; + +import com.android.inputmethod.latin.R; + +public final class ImportantNoticeUtils { + private static final String TAG = ImportantNoticeUtils.class.getSimpleName(); + + // {@link SharedPreferences} name to save the last important notice version that has been + // displayed to users. + private static final String PREFERENCE_NAME = "important_notice"; + private static final String KEY_IMPORTANT_NOTICE_VERSION = "important_notice_version"; + + // Copy of the hidden {@link Settings.Secure#USER_SETUP_COMPLETE} settings key. + // The value is zero until each multiuser completes system setup wizard. + // Caveat: This is a hidden API. + private static final String Settings_Secure_USER_SETUP_COMPLETE = "user_setup_complete"; + private static final int USER_SETUP_IS_NOT_COMPLETE = 0; + + private ImportantNoticeUtils() { + // This utility class is not publicly instantiable. + } + + public static boolean isInSystemSetupWizard(final Context context) { + try { + final int userSetupComplete = Settings.Secure.getInt( + context.getContentResolver(), Settings_Secure_USER_SETUP_COMPLETE); + return userSetupComplete == USER_SETUP_IS_NOT_COMPLETE; + } catch (final SettingNotFoundException e) { + Log.w(TAG, "Can't find settings in Settings.Secure: key=" + + Settings_Secure_USER_SETUP_COMPLETE); + return false; + } + } + + private static SharedPreferences getImportantNoticePreferences(final Context context) { + return context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); + } + + private static int getCurrentImportantNoticeVersion(final Context context) { + return context.getResources().getInteger(R.integer.config_important_notice_version); + } + + public static boolean hasNewImportantNotice(final Context context) { + final SharedPreferences prefs = getImportantNoticePreferences(context); + final int lastVersion = prefs.getInt(KEY_IMPORTANT_NOTICE_VERSION, 0); + return getCurrentImportantNoticeVersion(context) > lastVersion; + } + + public static void updateLastImportantNoticeVersion(final Context context) { + final SharedPreferences prefs = getImportantNoticePreferences(context); + prefs.edit() + .putInt(KEY_IMPORTANT_NOTICE_VERSION, getCurrentImportantNoticeVersion(context)) + .apply(); + } +} diff --git a/java/src/com/android/inputmethod/latin/utils/StringUtils.java b/java/src/com/android/inputmethod/latin/utils/StringUtils.java index b154623ae..c632a71a9 100644 --- a/java/src/com/android/inputmethod/latin/utils/StringUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/StringUtils.java @@ -16,6 +16,8 @@ package com.android.inputmethod.latin.utils; +import static com.android.inputmethod.latin.Constants.CODE_UNSPECIFIED; + import android.text.TextUtils; import com.android.inputmethod.annotations.UsedForTesting; @@ -471,4 +473,20 @@ public final class StringUtils { } return bytes; } + + public static String toUpperCaseOfStringForLocale(final String text, + final boolean needsToUpperCase, final Locale locale) { + if (text == null || !needsToUpperCase) return text; + return text.toUpperCase(locale); + } + + public static int toUpperCaseOfCodeForLocale(final int code, final boolean needsToUpperCase, + final Locale locale) { + if (!Constants.isLetterCode(code) || !needsToUpperCase) return code; + final String text = newSingleCodePointString(code); + final String casedText = toUpperCaseOfStringForLocale( + text, needsToUpperCase, locale); + return codePointCount(casedText) == 1 + ? casedText.codePointAt(0) : CODE_UNSPECIFIED; + } } diff --git a/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java b/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java index 0d0288923..b3871bfb4 100644 --- a/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java @@ -347,9 +347,12 @@ public final class SubtypeLocaleUtils { Arrays.sort(SORTED_RTL_LANGUAGES); } - public static boolean isRtlLanguage(final InputMethodSubtype subtype) { - final Locale locale = getSubtypeLocale(subtype); + public static boolean isRtlLanguage(final Locale locale) { final String language = locale.getLanguage(); return Arrays.binarySearch(SORTED_RTL_LANGUAGES, language) >= 0; } + + public static boolean isRtlLanguage(final InputMethodSubtype subtype) { + return isRtlLanguage(getSubtypeLocale(subtype)); + } } diff --git a/java/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtils.java b/java/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtils.java index db628fe18..7af03da59 100644 --- a/java/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtils.java @@ -22,6 +22,7 @@ import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.makedict.BinaryDictIOUtils; import com.android.inputmethod.latin.makedict.DictDecoder; import com.android.inputmethod.latin.makedict.DictEncoder; +import com.android.inputmethod.latin.makedict.FormatSpec; import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions; import com.android.inputmethod.latin.makedict.FusionDictionary; import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray; @@ -44,9 +45,6 @@ import java.util.concurrent.TimeUnit; public final class UserHistoryDictIOUtils { private static final String TAG = UserHistoryDictIOUtils.class.getSimpleName(); private static final boolean DEBUG = false; - private static final String USES_FORGETTING_CURVE_KEY = "USES_FORGETTING_CURVE"; - private static final String USES_FORGETTING_CURVE_VALUE = "1"; - private static final String LAST_UPDATED_TIME_KEY = "date"; public interface OnAddWordListener { /** @@ -75,8 +73,9 @@ public final class UserHistoryDictIOUtils { final BigramDictionaryInterface dict, final UserHistoryDictionaryBigramList bigrams, final FormatOptions formatOptions, final HashMap<String, String> options) { final FusionDictionary fusionDict = constructFusionDictionary(dict, bigrams, options); - fusionDict.addOptionAttribute(USES_FORGETTING_CURVE_KEY, USES_FORGETTING_CURVE_VALUE); - fusionDict.addOptionAttribute(LAST_UPDATED_TIME_KEY, + fusionDict.addOptionAttribute(FormatSpec.FileHeader.USES_FORGETTING_CURVE_KEY, + FormatSpec.FileHeader.ATTRIBUTE_VALUE_TRUE); + fusionDict.addOptionAttribute(FormatSpec.FileHeader.DICTIONARY_DATE_KEY, String.valueOf(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()))); try { dictEncoder.writeDictionary(fusionDict, formatOptions); diff --git a/java/src/com/android/inputmethod/latin/utils/UnigramProperty.java b/java/src/com/android/inputmethod/latin/utils/WordProperty.java index 4feee4393..ba9b114b0 100644 --- a/java/src/com/android/inputmethod/latin/utils/UnigramProperty.java +++ b/java/src/com/android/inputmethod/latin/utils/WordProperty.java @@ -26,21 +26,36 @@ import java.util.ArrayList; // This has information that belong to a unigram. This class has some detailed attributes such as // historical information but they have to be checked only for testing purpose. @UsedForTesting -public class UnigramProperty { +public class WordProperty { public final String mCodePoints; public final boolean mIsNotAWord; public final boolean mIsBlacklisted; public final boolean mHasBigrams; public final boolean mHasShortcuts; - public final int mProbability; - // mTimestamp, mLevel and mCount are historical info. These values are depend on the - // implementation in native code; thus, we must not use them and have any assumptions about - // them except for tests. - public final int mTimestamp; - public final int mLevel; - public final int mCount; + public final ProbabilityInfo mProbabilityInfo; + public final ArrayList<WeightedString> mBigramTargets = CollectionUtils.newArrayList(); + public final ArrayList<ProbabilityInfo> mBigramProbabilityInfo = CollectionUtils.newArrayList(); public final ArrayList<WeightedString> mShortcutTargets = CollectionUtils.newArrayList(); + // TODO: Use this kind of Probability class for dictionary read/write code under the makedict + // package. + public static final class ProbabilityInfo { + public final int mProbability; + // wTimestamp, mLevel and mCount are historical info. These values are depend on the + // implementation in native code; thus, we must not use them and have any assumptions about + // them except for tests. + public final int mTimestamp; + public final int mLevel; + public final int mCount; + + public ProbabilityInfo(final int[] probabilityInfo) { + mProbability = probabilityInfo[BinaryDictionary.FORMAT_WORD_PROPERTY_PROBABILITY_INDEX]; + mTimestamp = probabilityInfo[BinaryDictionary.FORMAT_WORD_PROPERTY_TIMESTAMP_INDEX]; + mLevel = probabilityInfo[BinaryDictionary.FORMAT_WORD_PROPERTY_LEVEL_INDEX]; + mCount = probabilityInfo[BinaryDictionary.FORMAT_WORD_PROPERTY_COUNT_INDEX]; + } + } + private static int getCodePointCount(final int[] codePoints) { for (int i = 0; i < codePoints.length; i++) { if (codePoints[i] == 0) { @@ -50,21 +65,32 @@ public class UnigramProperty { return codePoints.length; } - // This represents invalid unigram when the probability is BinaryDictionary.NOT_A_PROBABILITY. - public UnigramProperty(final int[] codePoints, final boolean isNotAWord, + // 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 probability, final int timestamp, - final int level, final int count, final ArrayList<int[]> shortcutTargets, + final boolean hasShortcuts, final int[] probabilityInfo, + final ArrayList<int[]> bigramTargets, final ArrayList<int[]> bigramProbabilityInfo, + final ArrayList<int[]> shortcutTargets, final ArrayList<Integer> shortcutProbabilities) { mCodePoints = new String(codePoints, 0 /* offset */, getCodePointCount(codePoints)); mIsNotAWord = isNotAWord; mIsBlacklisted = isBlacklisted; mHasBigrams = hasBigram; mHasShortcuts = hasShortcuts; - mProbability = probability; - mTimestamp = timestamp; - mLevel = level; - mCount = count; + mProbabilityInfo = new ProbabilityInfo(probabilityInfo); + + final int bigramTargetCount = bigramTargets.size(); + for (int i = 0; i < bigramTargetCount; i++) { + final int[] bigramTargetCodePointArray = bigramTargets.get(i); + final String bigramTargetString = new String(bigramTargetCodePointArray, + 0 /* offset */, getCodePointCount(bigramTargetCodePointArray)); + final ProbabilityInfo bigramProbability = + new ProbabilityInfo(bigramProbabilityInfo.get(i)); + mBigramTargets.add( + new WeightedString(bigramTargetString, bigramProbability.mProbability)); + mBigramProbabilityInfo.add(bigramProbability); + } + final int shortcutTargetCount = shortcutTargets.size(); for (int i = 0; i < shortcutTargetCount; i++) { final int[] shortcutTargetCodePointArray = shortcutTargets.get(i); @@ -77,6 +103,6 @@ public class UnigramProperty { @UsedForTesting public boolean isValid() { - return mProbability != BinaryDictionary.NOT_A_PROBABILITY; + return mProbabilityInfo.mProbability != BinaryDictionary.NOT_A_PROBABILITY; } }
\ No newline at end of file |