diff options
20 files changed, 297 insertions, 128 deletions
diff --git a/java/src/com/android/inputmethod/event/CombinerChain.java b/java/src/com/android/inputmethod/event/CombinerChain.java index 990f7deea..9e7f04d4f 100644 --- a/java/src/com/android/inputmethod/event/CombinerChain.java +++ b/java/src/com/android/inputmethod/event/CombinerChain.java @@ -56,18 +56,20 @@ public class CombinerChain { * * The combiner chain takes events as inputs and outputs code points and combining state. * For example, if the input language is Japanese, the combining chain will typically perform - * kana conversion. + * kana conversion. This takes a string for initial text, taken to be present before the + * cursor: we'll start after this. * + * @param initialText The text that has already been combined so far. * @param combinerList A list of combiners to be applied in order. */ - public CombinerChain(final Combiner... combinerList) { + public CombinerChain(final String initialText, final Combiner... combinerList) { mCombiners = CollectionUtils.newArrayList(); // The dead key combiner is always active, and always first mCombiners.add(new DeadKeyCombiner()); for (final Combiner combiner : combinerList) { mCombiners.add(combiner); } - mCombinedText = new StringBuilder(); + mCombinedText = new StringBuilder(initialText); mStateFeedback = new SpannableStringBuilder(); } diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 8a2ed1088..3751a3cdb 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -84,6 +84,7 @@ import com.android.inputmethod.latin.utils.CapsModeUtils; import com.android.inputmethod.latin.utils.CoordinateUtils; import com.android.inputmethod.latin.utils.DialogUtils; import com.android.inputmethod.latin.utils.DistracterFilter; +import com.android.inputmethod.latin.utils.DistracterFilterUtils; import com.android.inputmethod.latin.utils.ImportantNoticeUtils; import com.android.inputmethod.latin.utils.IntentUtils; import com.android.inputmethod.latin.utils.JniUtils; @@ -1745,7 +1746,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen @UsedForTesting /* package for test */ DistracterFilter createDistracterFilter() { - return DistracterFilter.createDistracterFilter(mInputLogic.mSuggest, mKeyboardSwitcher); + return DistracterFilterUtils.createDistracterFilter( + mInputLogic.mSuggest, mKeyboardSwitcher); } public void dumpDictionaryForDebug(final String dictName) { diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java index cdee496a8..ac6972928 100644 --- a/java/src/com/android/inputmethod/latin/WordComposer.java +++ b/java/src/com/android/inputmethod/latin/WordComposer.java @@ -80,7 +80,7 @@ public final class WordComposer { private boolean mIsFirstCharCapitalized; public WordComposer() { - mCombinerChain = new CombinerChain(); + mCombinerChain = new CombinerChain(""); mEvents = CollectionUtils.newArrayList(); mAutoCorrection = null; mIsResumed = false; @@ -92,18 +92,17 @@ public final class WordComposer { } /** - * Restart input with a new combining spec. + * Restart the combiners, possibly with a new spec. * @param combiningSpec The spec string for combining. This is found in the extra value. */ - public void restart(final String combiningSpec) { + public void restartCombining(final String combiningSpec) { final String nonNullCombiningSpec = null == combiningSpec ? "" : combiningSpec; - if (nonNullCombiningSpec.equals(mCombiningSpec)) { - mCombinerChain.reset(); - } else { - mCombinerChain = new CombinerChain(CombinerChain.createCombiners(nonNullCombiningSpec)); + if (!nonNullCombiningSpec.equals(mCombiningSpec)) { + mCombinerChain = new CombinerChain( + mCombinerChain.getComposingWordWithCombiningFeedback().toString(), + CombinerChain.createCombiners(nonNullCombiningSpec)); mCombiningSpec = nonNullCombiningSpec; } - reset(); } /** diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java index 1156c7737..ea58abc14 100644 --- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java +++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java @@ -127,7 +127,7 @@ public final class InputLogic { public void startInput(final boolean restarting, final EditorInfo editorInfo, final String combiningSpec) { mEnteredText = null; - mWordComposer.restart(combiningSpec); + mWordComposer.restartCombining(combiningSpec); resetComposingState(true /* alsoResetLastComposedWord */); mDeleteCount = 0; mSpaceState = SpaceState.NONE; @@ -150,7 +150,7 @@ public final class InputLogic { * @param combiningSpec the spec string for the combining rules */ public void onSubtypeChanged(final String combiningSpec) { - mWordComposer.restart(combiningSpec); + mWordComposer.restartCombining(combiningSpec); } /** diff --git a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java index 613ff2ba4..f5f072b7a 100644 --- a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java +++ b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java @@ -193,8 +193,9 @@ public final class FormatSpec { // Dictionary version used for testing. public static final int VERSION4_ONLY_FOR_TESTING = 399; public static final int VERSION4 = 401; + public static final int VERSION4_DEV = 402; static final int MINIMUM_SUPPORTED_VERSION = VERSION2; - static final int MAXIMUM_SUPPORTED_VERSION = VERSION4; + static final int MAXIMUM_SUPPORTED_VERSION = VERSION4_DEV; // TODO: Make this value adaptative to content data, store it in the header, and // use it in the reading code. diff --git a/java/src/com/android/inputmethod/latin/utils/DistracterFilter.java b/java/src/com/android/inputmethod/latin/utils/DistracterFilter.java index 55cbf79b3..0a0379982 100644 --- a/java/src/com/android/inputmethod/latin/utils/DistracterFilter.java +++ b/java/src/com/android/inputmethod/latin/utils/DistracterFilter.java @@ -17,8 +17,6 @@ package com.android.inputmethod.latin.utils; import com.android.inputmethod.keyboard.Keyboard; -import com.android.inputmethod.keyboard.KeyboardSwitcher; -import com.android.inputmethod.keyboard.MainKeyboardView; import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.Suggest; import com.android.inputmethod.latin.Suggest.OnGetSuggestedWordsCallback; @@ -54,18 +52,6 @@ public class DistracterFilter { mKeyboard = keyboard; } - public static DistracterFilter createDistracterFilter(final Suggest suggest, - final KeyboardSwitcher keyboardSwitcher) { - final MainKeyboardView mainKeyboardView = keyboardSwitcher.getMainKeyboardView(); - // TODO: Create Keyboard when mainKeyboardView is null. - // TODO: Figure out the most reasonable keyboard for the filter. Refer to the - // spellchecker's logic. - final Keyboard keyboard = (mainKeyboardView != null) ? - mainKeyboardView.getKeyboard() : null; - final DistracterFilter distracterFilter = new DistracterFilter(suggest, keyboard); - return distracterFilter; - } - private static boolean suggestionExceedsDistracterThreshold( final SuggestedWordInfo suggestion, final String consideredWord, final float distracterThreshold) { @@ -90,19 +76,14 @@ public class DistracterFilter { */ public boolean isDistracterToWordsInDictionaries(final String prevWord, final String testedWord) { - if (mSuggest == null) { + if (mSuggest == null || mKeyboard == null) { return false; } final WordComposer composer = new WordComposer(); final int[] codePoints = StringUtils.toCodePointArray(testedWord); final int[] coordinates; - if (null == mKeyboard) { - coordinates = CoordinateUtils.newCoordinateArray(codePoints.length, - Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE); - } else { - coordinates = mKeyboard.getCoordinates(codePoints); - } + coordinates = mKeyboard.getCoordinates(codePoints); composer.setComposingWord(codePoints, coordinates, prevWord); final int trailingSingleQuotesCount = composer.trailingSingleQuotesCount(); diff --git a/java/src/com/android/inputmethod/latin/utils/DistracterFilterUtils.java b/java/src/com/android/inputmethod/latin/utils/DistracterFilterUtils.java new file mode 100644 index 000000000..df07f976c --- /dev/null +++ b/java/src/com/android/inputmethod/latin/utils/DistracterFilterUtils.java @@ -0,0 +1,40 @@ +/* + * 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 com.android.inputmethod.keyboard.Keyboard; +import com.android.inputmethod.keyboard.KeyboardSwitcher; +import com.android.inputmethod.keyboard.MainKeyboardView; +import com.android.inputmethod.latin.Suggest; + +public class DistracterFilterUtils { + private DistracterFilterUtils() { + // This utility class is not publicly instantiable. + } + + public static final DistracterFilter createDistracterFilter(final Suggest suggest, + final KeyboardSwitcher keyboardSwitcher) { + final MainKeyboardView mainKeyboardView = keyboardSwitcher.getMainKeyboardView(); + // TODO: Create Keyboard when mainKeyboardView is null. + // TODO: Figure out the most reasonable keyboard for the filter. Refer to the + // spellchecker's logic. + final Keyboard keyboard = (mainKeyboardView != null) ? + mainKeyboardView.getKeyboard() : null; + final DistracterFilter distracterFilter = new DistracterFilter(suggest, keyboard); + return distracterFilter; + } +} diff --git a/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h index da24302c2..479d15164 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h +++ b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h @@ -143,6 +143,8 @@ class HeaderPolicy : public DictionaryHeaderStructurePolicy { return FormatUtils::VERSION_4_ONLY_FOR_TESTING; case FormatUtils::VERSION_4: return FormatUtils::VERSION_4; + case FormatUtils::VERSION_4_DEV: + return FormatUtils::VERSION_4_DEV; default: return FormatUtils::UNKNOWN_VERSION; } diff --git a/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.cpp index 2a9028a9e..a8f8f284b 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.cpp @@ -100,6 +100,7 @@ typedef DictionaryHeaderStructurePolicy::AttributeMap AttributeMap; return false; case FormatUtils::VERSION_4_ONLY_FOR_TESTING: case FormatUtils::VERSION_4: + case FormatUtils::VERSION_4_DEV: return buffer->writeUintAndAdvancePosition(version /* data */, HEADER_DICTIONARY_VERSION_SIZE, writingPos); default: diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.cpp index c4d18608c..59f1f29e9 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.cpp @@ -19,6 +19,9 @@ #include <climits> #include "defines.h" +#include "suggest/policyimpl/dictionary/structure/backward/v401/ver4_dict_buffers.h" +#include "suggest/policyimpl/dictionary/structure/backward/v401/ver4_dict_constants.h" +#include "suggest/policyimpl/dictionary/structure/backward/v401/ver4_patricia_trie_policy.h" #include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_writing_utils.h" #include "suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h" #include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h" @@ -42,7 +45,7 @@ namespace latinime { if (isUpdatable) { AKLOGE("One file dictionaries don't support updating. path: %s", path); ASSERT(false); - return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(nullptr); + return nullptr; } return newPolicyForFileDict(path, bufOffset, size); } @@ -54,26 +57,43 @@ namespace latinime { const DictionaryHeaderStructurePolicy::AttributeMap *const attributeMap) { FormatUtils::FORMAT_VERSION dictFormatVersion = FormatUtils::getFormatVersion(formatVersion); switch (dictFormatVersion) { - case FormatUtils::VERSION_4_ONLY_FOR_TESTING: case FormatUtils::VERSION_4: { - HeaderPolicy headerPolicy(dictFormatVersion, locale, attributeMap); - Ver4DictBuffers::Ver4DictBuffersPtr dictBuffers = - Ver4DictBuffers::createVer4DictBuffers(&headerPolicy, - Ver4DictConstants::MAX_DICT_EXTENDED_REGION_SIZE); - if (!DynamicPtWritingUtils::writeEmptyDictionary( - dictBuffers->getWritableTrieBuffer(), 0 /* rootPos */)) { - AKLOGE("Empty ver4 dictionary structure cannot be created on memory."); - return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(nullptr); - } - return DictionaryStructureWithBufferPolicy::StructurePolicyPtr( - new Ver4PatriciaTriePolicy(std::move(dictBuffers))); + return newPolicyForOnMemoryV4Dict<backward::v401::Ver4DictConstants, + backward::v401::Ver4DictBuffers, + backward::v401::Ver4DictBuffers::Ver4DictBuffersPtr, + backward::v401::Ver4PatriciaTriePolicy>( + dictFormatVersion, locale, attributeMap); + } + case FormatUtils::VERSION_4_ONLY_FOR_TESTING: + case FormatUtils::VERSION_4_DEV: { + return newPolicyForOnMemoryV4Dict<Ver4DictConstants, Ver4DictBuffers, + Ver4DictBuffers::Ver4DictBuffersPtr, Ver4PatriciaTriePolicy>( + dictFormatVersion, locale, attributeMap); } default: AKLOGE("DICT: dictionary format %d is not supported for on memory dictionary", formatVersion); break; } - return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(nullptr); + return nullptr; +} + +template<class DictConstants, class DictBuffers, class DictBuffersPtr, class StructurePolicy> +/* static */ DictionaryStructureWithBufferPolicy::StructurePolicyPtr + DictionaryStructureWithBufferPolicyFactory::newPolicyForOnMemoryV4Dict( + const FormatUtils::FORMAT_VERSION formatVersion, + const std::vector<int> &locale, + const DictionaryHeaderStructurePolicy::AttributeMap *const attributeMap) { + HeaderPolicy headerPolicy(formatVersion, locale, attributeMap); + DictBuffersPtr dictBuffers = DictBuffers::createVer4DictBuffers(&headerPolicy, + DictConstants::MAX_DICT_EXTENDED_REGION_SIZE); + if (!DynamicPtWritingUtils::writeEmptyDictionary( + dictBuffers->getWritableTrieBuffer(), 0 /* rootPos */)) { + AKLOGE("Empty ver4 dictionary structure cannot be created on memory."); + return nullptr; + } + return DictionaryStructureWithBufferPolicy::StructurePolicyPtr( + new StructurePolicy(std::move(dictBuffers))); } /* static */ DictionaryStructureWithBufferPolicy::StructurePolicyPtr @@ -84,10 +104,10 @@ namespace latinime { getHeaderFilePathInDictDir(path, headerFilePathBufSize, headerFilePath); // Allocated buffer in MmapedBuffer::openBuffer() will be freed in the destructor of // MmappedBufferPtr if the instance has the responsibility. - MmappedBuffer::MmappedBufferPtr mmappedBuffer( - MmappedBuffer::openBuffer(headerFilePath, isUpdatable)); + MmappedBuffer::MmappedBufferPtr mmappedBuffer = + MmappedBuffer::openBuffer(headerFilePath, isUpdatable); if (!mmappedBuffer) { - return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(nullptr); + return nullptr; } const FormatUtils::FORMAT_VERSION formatVersion = FormatUtils::detectFormatVersion( mmappedBuffer->getBuffer(), mmappedBuffer->getBufferSize()); @@ -95,34 +115,50 @@ namespace latinime { case FormatUtils::VERSION_2: AKLOGE("Given path is a directory but the format is version 2. path: %s", path); break; - case FormatUtils::VERSION_4_ONLY_FOR_TESTING: case FormatUtils::VERSION_4: { - const int dictDirPathBufSize = strlen(headerFilePath) + 1 /* terminator */; - char dictPath[dictDirPathBufSize]; - if (!FileUtils::getFilePathWithoutSuffix(headerFilePath, - Ver4DictConstants::HEADER_FILE_EXTENSION, dictDirPathBufSize, dictPath)) { - AKLOGE("Dictionary file name is not valid as a ver4 dictionary. path: %s", path); - ASSERT(false); - return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(nullptr); - } - Ver4DictBuffers::Ver4DictBuffersPtr dictBuffers( - Ver4DictBuffers::openVer4DictBuffers(dictPath, std::move(mmappedBuffer), - formatVersion)); - if (!dictBuffers || !dictBuffers->isValid()) { - AKLOGE("DICT: The dictionary doesn't satisfy ver4 format requirements. path: %s", - path); - ASSERT(false); - return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(nullptr); - } - return DictionaryStructureWithBufferPolicy::StructurePolicyPtr( - new Ver4PatriciaTriePolicy(std::move(dictBuffers))); + return newPolicyForV4Dict<backward::v401::Ver4DictConstants, + backward::v401::Ver4DictBuffers, + backward::v401::Ver4DictBuffers::Ver4DictBuffersPtr, + backward::v401::Ver4PatriciaTriePolicy>( + headerFilePath, formatVersion, std::move(mmappedBuffer)); + } + case FormatUtils::VERSION_4_ONLY_FOR_TESTING: + case FormatUtils::VERSION_4_DEV: { + return newPolicyForV4Dict<Ver4DictConstants, Ver4DictBuffers, + Ver4DictBuffers::Ver4DictBuffersPtr, Ver4PatriciaTriePolicy>( + headerFilePath, formatVersion, std::move(mmappedBuffer)); } default: AKLOGE("DICT: dictionary format is unknown, bad magic number. path: %s", path); break; } ASSERT(false); - return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(nullptr); + return nullptr; +} + +template<class DictConstants, class DictBuffers, class DictBuffersPtr, class StructurePolicy> +/* static */ DictionaryStructureWithBufferPolicy::StructurePolicyPtr + DictionaryStructureWithBufferPolicyFactory::newPolicyForV4Dict( + const char *const headerFilePath, const FormatUtils::FORMAT_VERSION formatVersion, + MmappedBuffer::MmappedBufferPtr &&mmappedBuffer) { + const int dictDirPathBufSize = strlen(headerFilePath) + 1 /* terminator */; + char dictPath[dictDirPathBufSize]; + if (!FileUtils::getFilePathWithoutSuffix(headerFilePath, + DictConstants::HEADER_FILE_EXTENSION, dictDirPathBufSize, dictPath)) { + AKLOGE("Dictionary file name is not valid as a ver4 dictionary. path: %s", path); + ASSERT(false); + return nullptr; + } + DictBuffersPtr dictBuffers = + DictBuffers::openVer4DictBuffers(dictPath, std::move(mmappedBuffer), formatVersion); + if (!dictBuffers || !dictBuffers->isValid()) { + AKLOGE("DICT: The dictionary doesn't satisfy ver4 format requirements. path: %s", + path); + ASSERT(false); + return nullptr; + } + return DictionaryStructureWithBufferPolicy::StructurePolicyPtr( + new StructurePolicy(std::move(dictBuffers))); } /* static */ DictionaryStructureWithBufferPolicy::StructurePolicyPtr @@ -133,7 +169,7 @@ namespace latinime { MmappedBuffer::MmappedBufferPtr mmappedBuffer( MmappedBuffer::openBuffer(path, bufOffset, size, false /* isUpdatable */)); if (!mmappedBuffer) { - return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(nullptr); + return nullptr; } switch (FormatUtils::detectFormatVersion(mmappedBuffer->getBuffer(), mmappedBuffer->getBufferSize())) { @@ -142,6 +178,7 @@ namespace latinime { new PatriciaTriePolicy(std::move(mmappedBuffer))); case FormatUtils::VERSION_4_ONLY_FOR_TESTING: case FormatUtils::VERSION_4: + case FormatUtils::VERSION_4_DEV: AKLOGE("Given path is a file but the format is version 4. path: %s", path); break; default: @@ -149,7 +186,7 @@ namespace latinime { break; } ASSERT(false); - return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(nullptr); + return nullptr; } /* static */ void DictionaryStructureWithBufferPolicyFactory::getHeaderFilePathInDictDir( diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.h b/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.h index f71447e23..768454d8d 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.h @@ -22,6 +22,8 @@ #include "defines.h" #include "suggest/core/policy/dictionary_header_structure_policy.h" #include "suggest/core/policy/dictionary_structure_with_buffer_policy.h" +#include "suggest/policyimpl/dictionary/utils/format_utils.h" +#include "suggest/policyimpl/dictionary/utils/mmapped_buffer.h" namespace latinime { @@ -32,16 +34,26 @@ class DictionaryStructureWithBufferPolicyFactory { const int size, const bool isUpdatable); static DictionaryStructureWithBufferPolicy::StructurePolicyPtr - newPolicyForOnMemoryDict(const int formatVersion, - const std::vector<int> &locale, + newPolicyForOnMemoryDict(const int formatVersion, const std::vector<int> &locale, const DictionaryHeaderStructurePolicy::AttributeMap *const attributeMap); private: DISALLOW_IMPLICIT_CONSTRUCTORS(DictionaryStructureWithBufferPolicyFactory); + template<class DictConstants, class DictBuffers, class DictBuffersPtr, class StructurePolicy> + static DictionaryStructureWithBufferPolicy::StructurePolicyPtr + newPolicyForOnMemoryV4Dict(const FormatUtils::FORMAT_VERSION formatVersion, + const std::vector<int> &locale, + const DictionaryHeaderStructurePolicy::AttributeMap *const attributeMap); + static DictionaryStructureWithBufferPolicy::StructurePolicyPtr newPolicyForDirectoryDict(const char *const path, const bool isUpdatable); + template<class DictConstants, class DictBuffers, class DictBuffersPtr, class StructurePolicy> + static DictionaryStructureWithBufferPolicy::StructurePolicyPtr newPolicyForV4Dict( + const char *const headerFilePath, const FormatUtils::FORMAT_VERSION formatVersion, + MmappedBuffer::MmappedBufferPtr &&mmappedBuffer); + static DictionaryStructureWithBufferPolicy::StructurePolicyPtr newPolicyForFileDict(const char *const path, const int bufOffset, const int size); diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.cpp index 80970c7f8..105363db5 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.cpp @@ -23,6 +23,7 @@ #include <sys/types.h> #include "suggest/policyimpl/dictionary/header/header_policy.h" +#include "suggest/policyimpl/dictionary/structure/backward/v401/ver4_dict_buffers.h" #include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_writing_utils.h" #include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h" #include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" @@ -40,10 +41,16 @@ const char *const DictFileWritingUtils::TEMP_FILE_SUFFIX_FOR_WRITING_DICT_FILE = TimeKeeper::setCurrentTime(); const FormatUtils::FORMAT_VERSION formatVersion = FormatUtils::getFormatVersion(dictVersion); switch (formatVersion) { - case FormatUtils::VERSION_4_ONLY_FOR_TESTING: case FormatUtils::VERSION_4: - return createEmptyV4DictFile(filePath, localeAsCodePointVector, attributeMap, - formatVersion); + return createEmptyV4DictFile<backward::v401::Ver4DictConstants, + backward::v401::Ver4DictBuffers, + backward::v401::Ver4DictBuffers::Ver4DictBuffersPtr>( + filePath, localeAsCodePointVector, attributeMap, formatVersion); + case FormatUtils::VERSION_4_ONLY_FOR_TESTING: + case FormatUtils::VERSION_4_DEV: + return createEmptyV4DictFile<Ver4DictConstants, Ver4DictBuffers, + Ver4DictBuffers::Ver4DictBuffersPtr>( + filePath, localeAsCodePointVector, attributeMap, formatVersion); default: AKLOGE("Cannot create dictionary %s because format version %d is not supported.", filePath, dictVersion); @@ -51,14 +58,14 @@ const char *const DictFileWritingUtils::TEMP_FILE_SUFFIX_FOR_WRITING_DICT_FILE = } } +template<class DictConstants, class DictBuffers, class DictBuffersPtr> /* static */ bool DictFileWritingUtils::createEmptyV4DictFile(const char *const dirPath, const std::vector<int> localeAsCodePointVector, const DictionaryHeaderStructurePolicy::AttributeMap *const attributeMap, const FormatUtils::FORMAT_VERSION formatVersion) { HeaderPolicy headerPolicy(formatVersion, localeAsCodePointVector, attributeMap); - Ver4DictBuffers::Ver4DictBuffersPtr dictBuffers( - Ver4DictBuffers::createVer4DictBuffers(&headerPolicy, - Ver4DictConstants::MAX_DICT_EXTENDED_REGION_SIZE)); + DictBuffersPtr dictBuffers = DictBuffers::createVer4DictBuffers(&headerPolicy, + DictConstants::MAX_DICT_EXTENDED_REGION_SIZE); headerPolicy.fillInAndWriteHeaderToBuffer(true /* updatesLastDecayedTime */, 0 /* unigramCount */, 0 /* bigramCount */, 0 /* extendedRegionSize */, dictBuffers->getWritableHeaderBuffer()); diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h b/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h index a822989db..5df5856d2 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h @@ -45,6 +45,12 @@ class DictFileWritingUtils { private: DISALLOW_IMPLICIT_CONSTRUCTORS(DictFileWritingUtils); + static bool createEmptyV401DictFile(const char *const filePath, + const std::vector<int> localeAsCodePointVector, + const DictionaryHeaderStructurePolicy::AttributeMap *const attributeMap, + const FormatUtils::FORMAT_VERSION formatVersion); + + template<class DictConstants, class DictBuffers, class DictBuffersPtr> static bool createEmptyV4DictFile(const char *const filePath, const std::vector<int> localeAsCodePointVector, const DictionaryHeaderStructurePolicy::AttributeMap *const attributeMap, diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.cpp index a8518cdca..ba405b07e 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.cpp @@ -33,6 +33,8 @@ const int FormatUtils::DICTIONARY_MINIMUM_SIZE = 12; return VERSION_4_ONLY_FOR_TESTING; case VERSION_4: return VERSION_4; + case VERSION_4_DEV: + return VERSION_4_DEV; default: return UNKNOWN_VERSION; } @@ -62,6 +64,8 @@ const int FormatUtils::DICTIONARY_MINIMUM_SIZE = 12; return VERSION_4_ONLY_FOR_TESTING; } else if (ByteArrayUtils::readUint16(dict, 4) == VERSION_4) { return VERSION_4; + } else if (ByteArrayUtils::readUint16(dict, 4) == VERSION_4_DEV) { + return VERSION_4_DEV; } else { return UNKNOWN_VERSION; } diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.h b/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.h index 20dfb9d8c..c47f30ca4 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.h +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.h @@ -33,6 +33,7 @@ class FormatUtils { VERSION_2 = 2, VERSION_4_ONLY_FOR_TESTING = 399, VERSION_4 = 401, + VERSION_4_DEV = 402, UNKNOWN_VERSION = -1 }; diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/mmapped_buffer.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/mmapped_buffer.cpp index d3e0c237f..4a126ff85 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/utils/mmapped_buffer.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/mmapped_buffer.cpp @@ -33,7 +33,7 @@ namespace latinime { const int mmapFd = open(path, O_RDONLY); if (mmapFd < 0) { AKLOGE("DICT: Can't open the source. path=%s errno=%d", path, errno); - return MmappedBufferPtr(nullptr); + return nullptr; } const int pagesize = sysconf(_SC_PAGESIZE); const int offset = bufferOffset % pagesize; @@ -45,13 +45,13 @@ namespace latinime { if (mmappedBuffer == MAP_FAILED) { AKLOGE("DICT: Can't mmap dictionary. errno=%d", errno); close(mmapFd); - return MmappedBufferPtr(nullptr); + return nullptr; } uint8_t *const buffer = static_cast<uint8_t *>(mmappedBuffer) + offset; if (!buffer) { AKLOGE("DICT: buffer is null"); close(mmapFd); - return MmappedBufferPtr(nullptr); + return nullptr; } return MmappedBufferPtr(new MmappedBuffer(buffer, bufferSize, mmappedBuffer, alignedSize, mmapFd, isUpdatable)); @@ -61,7 +61,7 @@ namespace latinime { const char *const path, const bool isUpdatable) { const int fileSize = FileUtils::getFileSize(path); if (fileSize == -1) { - return MmappedBufferPtr(nullptr); + return nullptr; } else if (fileSize == 0) { return MmappedBufferPtr(new MmappedBuffer(isUpdatable)); } else { @@ -76,7 +76,7 @@ namespace latinime { const int filePathLength = snprintf(filePath, filePathBufferSize, "%s%s", dirPath, fileName); if (filePathLength >= filePathBufferSize) { - return MmappedBufferPtr(nullptr); + return nullptr; } return openBuffer(filePath, isUpdatable); } diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java index 35d9a4e18..90b90ffb5 100644 --- a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java +++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java @@ -46,6 +46,8 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { private static final String TEST_DICT_FILE_EXTENSION = ".testDict"; private static final String TEST_LOCALE = "test"; private static final int DUMMY_PROBABILITY = 0; + private static final int[] DICT_FORMAT_VERSIONS = + new int[] { FormatSpec.VERSION4, FormatSpec.VERSION4_DEV }; private int mCurrentTime = 0; @@ -94,7 +96,8 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { private File createEmptyDictionaryAndGetFile(final String dictId, final int formatVersion) throws IOException { if (formatVersion == FormatSpec.VERSION4 - || formatVersion == FormatSpec.VERSION4_ONLY_FOR_TESTING) { + || formatVersion == FormatSpec.VERSION4_ONLY_FOR_TESTING + || formatVersion == FormatSpec.VERSION4_DEV) { return createEmptyVer4DictionaryAndGetFile(dictId, formatVersion); } else { throw new IOException("Dictionary format version " + formatVersion @@ -120,7 +123,7 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { return file; } else { throw new IOException("Empty dictionary " + file.getAbsolutePath() - + " cannot be created."); + + " cannot be created. Foramt version: " + formatVersion); } } @@ -133,7 +136,9 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { } public void testReadDictInJavaSide() { - testReadDictInJavaSide(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testReadDictInJavaSide(formatVersion); + } } private void testReadDictInJavaSide(final int formatVersion) { @@ -178,10 +183,6 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { } public void testControlCurrentTime() { - testControlCurrentTime(FormatSpec.VERSION4); - } - - private void testControlCurrentTime(final int formatVersion) { final int TEST_COUNT = 1000; final long seed = System.currentTimeMillis(); final Random random = new Random(seed); @@ -197,7 +198,9 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { } public void testAddValidAndInvalidWords() { - testAddValidAndInvalidWords(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testAddValidAndInvalidWords(formatVersion); + } } private void testAddValidAndInvalidWords(final int formatVersion) { @@ -240,7 +243,9 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { } public void testDecayingProbability() { - testDecayingProbability(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testDecayingProbability(formatVersion); + } } private void testDecayingProbability(final int formatVersion) { @@ -295,7 +300,9 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { } public void testAddManyUnigramsToDecayingDict() { - testAddManyUnigramsToDecayingDict(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testAddManyUnigramsToDecayingDict(formatVersion); + } } private void testAddManyUnigramsToDecayingDict(final int formatVersion) { @@ -354,7 +361,9 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { } public void testOverflowUnigrams() { - testOverflowUnigrams(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testOverflowUnigrams(formatVersion); + } } private void testOverflowUnigrams(final int formatVersion) { @@ -413,7 +422,9 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { } public void testAddManyBigramsToDecayingDict() { - testAddManyBigramsToDecayingDict(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testAddManyBigramsToDecayingDict(formatVersion); + } } private void testAddManyBigramsToDecayingDict(final int formatVersion) { @@ -487,7 +498,9 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { } public void testOverflowBigrams() { - testOverflowBigrams(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testOverflowBigrams(formatVersion); + } } private void testOverflowBigrams(final int formatVersion) { @@ -566,7 +579,9 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase { } public void testDictMigration() { - testDictMigration(FormatSpec.VERSION4_ONLY_FOR_TESTING, FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testDictMigration(FormatSpec.VERSION4_ONLY_FOR_TESTING, formatVersion); + } } private void testDictMigration(final int fromFormatVersion, final int toFormatVersion) { diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java index 770e76e5f..cfeed0aa4 100644 --- a/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java +++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java @@ -43,11 +43,14 @@ import java.util.Random; public class BinaryDictionaryTests extends AndroidTestCase { private static final String TEST_DICT_FILE_EXTENSION = ".testDict"; private static final String TEST_LOCALE = "test"; + private static final int[] DICT_FORMAT_VERSIONS = + new int[] { FormatSpec.VERSION4, FormatSpec.VERSION4_DEV }; private File createEmptyDictionaryAndGetFile(final String dictId, final int formatVersion) throws IOException { if (formatVersion == FormatSpec.VERSION4 - || formatVersion == FormatSpec.VERSION4_ONLY_FOR_TESTING) { + || formatVersion == FormatSpec.VERSION4_ONLY_FOR_TESTING + || formatVersion == FormatSpec.VERSION4_DEV) { return createEmptyVer4DictionaryAndGetFile(dictId, formatVersion); } else { throw new IOException("Dictionary format version " + formatVersion @@ -67,12 +70,14 @@ public class BinaryDictionaryTests extends AndroidTestCase { return file; } else { throw new IOException("Empty dictionary " + file.getAbsolutePath() - + " cannot be created."); + + " cannot be created. Format version: " + formatVersion); } } public void testIsValidDictionary() { - testIsValidDictionary(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testIsValidDictionary(formatVersion); + } } private void testIsValidDictionary(final int formatVersion) { @@ -100,7 +105,9 @@ public class BinaryDictionaryTests extends AndroidTestCase { } public void testConstructingDictionaryOnMemory() { - testConstructingDictionaryOnMemory(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testConstructingDictionaryOnMemory(formatVersion); + } } private void testConstructingDictionaryOnMemory(final int formatVersion) { @@ -131,7 +138,9 @@ public class BinaryDictionaryTests extends AndroidTestCase { } public void testAddTooLongWord() { - testAddTooLongWord(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testAddTooLongWord(formatVersion); + } } private void testAddTooLongWord(final int formatVersion) { @@ -190,7 +199,9 @@ public class BinaryDictionaryTests extends AndroidTestCase { } public void testAddUnigramWord() { - testAddUnigramWord(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testAddUnigramWord(formatVersion); + } } private void testAddUnigramWord(final int formatVersion) { @@ -232,7 +243,9 @@ public class BinaryDictionaryTests extends AndroidTestCase { } public void testRandomlyAddUnigramWord() { - testRandomlyAddUnigramWord(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testRandomlyAddUnigramWord(formatVersion); + } } private void testRandomlyAddUnigramWord(final int formatVersion) { @@ -268,7 +281,9 @@ public class BinaryDictionaryTests extends AndroidTestCase { } public void testAddBigramWords() { - testAddBigramWords(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testAddBigramWords(formatVersion); + } } private void testAddBigramWords(final int formatVersion) { @@ -336,7 +351,9 @@ public class BinaryDictionaryTests extends AndroidTestCase { } public void testRandomlyAddBigramWords() { - testRandomlyAddBigramWords(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testRandomlyAddBigramWords(formatVersion); + } } private void testRandomlyAddBigramWords(final int formatVersion) { @@ -397,7 +414,9 @@ public class BinaryDictionaryTests extends AndroidTestCase { } public void testRemoveBigramWords() { - testRemoveBigramWords(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testRemoveBigramWords(formatVersion); + } } private void testRemoveBigramWords(final int formatVersion) { @@ -447,7 +466,9 @@ public class BinaryDictionaryTests extends AndroidTestCase { } public void testFlushDictionary() { - testFlushDictionary(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testFlushDictionary(formatVersion); + } } private void testFlushDictionary(final int formatVersion) { @@ -499,7 +520,9 @@ public class BinaryDictionaryTests extends AndroidTestCase { } public void testFlushWithGCDictionary() { - testFlushWithGCDictionary(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testFlushWithGCDictionary(formatVersion); + } } private void testFlushWithGCDictionary(final int formatVersion) { @@ -547,7 +570,9 @@ public class BinaryDictionaryTests extends AndroidTestCase { } public void testAddBigramWordsAndFlashWithGC() { - testAddBigramWordsAndFlashWithGC(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testAddBigramWordsAndFlashWithGC(formatVersion); + } } // TODO: Evaluate performance of GC @@ -616,7 +641,9 @@ public class BinaryDictionaryTests extends AndroidTestCase { } public void testRandomOperationsAndFlashWithGC() { - testRandomOperationsAndFlashWithGC(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testRandomOperationsAndFlashWithGC(formatVersion); + } } private void testRandomOperationsAndFlashWithGC(final int formatVersion) { @@ -727,7 +754,9 @@ public class BinaryDictionaryTests extends AndroidTestCase { } public void testAddManyUnigramsAndFlushWithGC() { - testAddManyUnigramsAndFlushWithGC(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testAddManyUnigramsAndFlushWithGC(formatVersion); + } } private void testAddManyUnigramsAndFlushWithGC(final int formatVersion) { @@ -775,7 +804,9 @@ public class BinaryDictionaryTests extends AndroidTestCase { } public void testUnigramAndBigramCount() { - testUnigramAndBigramCount(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testUnigramAndBigramCount(formatVersion); + } } private void testUnigramAndBigramCount(final int formatVersion) { @@ -834,7 +865,9 @@ public class BinaryDictionaryTests extends AndroidTestCase { } public void testAddMultipleDictionaryEntries() { - testAddMultipleDictionaryEntries(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testAddMultipleDictionaryEntries(formatVersion); + } } private void testAddMultipleDictionaryEntries(final int formatVersion) { @@ -896,7 +929,9 @@ public class BinaryDictionaryTests extends AndroidTestCase { } public void testGetWordProperties() { - testGetWordProperties(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testGetWordProperties(formatVersion); + } } private void testGetWordProperties(final int formatVersion) { @@ -995,7 +1030,9 @@ public class BinaryDictionaryTests extends AndroidTestCase { } public void testIterateAllWords() { - testIterateAllWords(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testIterateAllWords(formatVersion); + } } private void testIterateAllWords(final int formatVersion) { @@ -1091,7 +1128,9 @@ public class BinaryDictionaryTests extends AndroidTestCase { } public void testAddShortcuts() { - testAddShortcuts(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testAddShortcuts(formatVersion); + } } private void testAddShortcuts(final int formatVersion) { @@ -1151,7 +1190,9 @@ public class BinaryDictionaryTests extends AndroidTestCase { } public void testAddManyShortcuts() { - testAddManyShortcuts(FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testAddManyShortcuts(formatVersion); + } } private void testAddManyShortcuts(final int formatVersion) { @@ -1227,7 +1268,9 @@ public class BinaryDictionaryTests extends AndroidTestCase { } public void testDictMigration() { - testDictMigration(FormatSpec.VERSION4_ONLY_FOR_TESTING, FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testDictMigration(FormatSpec.VERSION4_ONLY_FOR_TESTING, formatVersion); + } } private void testDictMigration(final int fromFormatVersion, final int toFormatVersion) { @@ -1271,7 +1314,9 @@ public class BinaryDictionaryTests extends AndroidTestCase { } public void testLargeDictMigration() { - testLargeDictMigration(FormatSpec.VERSION4_ONLY_FOR_TESTING, FormatSpec.VERSION4); + for (final int formatVersion : DICT_FORMAT_VERSIONS) { + testLargeDictMigration(FormatSpec.VERSION4_ONLY_FOR_TESTING, formatVersion); + } } private void testLargeDictMigration(final int fromFormatVersion, final int toFormatVersion) { diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTests.java b/tests/src/com/android/inputmethod/latin/InputLogicTests.java index 29423e8e3..a9444160f 100644 --- a/tests/src/com/android/inputmethod/latin/InputLogicTests.java +++ b/tests/src/com/android/inputmethod/latin/InputLogicTests.java @@ -600,4 +600,16 @@ public class InputLogicTests extends InputTestsBase { assertEquals("type words letter by letter", EXPECTED_RESULT, mEditText.getText().toString()); } + + public void testSwitchLanguages() { + final String WORD_TO_TYPE_FIRST_PART = "com"; + final String WORD_TO_TYPE_SECOND_PART = "md "; + final String EXPECTED_RESULT = "comme "; + changeLanguage("en"); + type(WORD_TO_TYPE_FIRST_PART); + changeLanguage("fr"); + type(WORD_TO_TYPE_SECOND_PART); + assertEquals("Composing continues after switching languages", EXPECTED_RESULT, + mEditText.getText().toString()); + } } diff --git a/tools/dicttool/compat/com/android/inputmethod/event/CombinerChain.java b/tools/dicttool/compat/com/android/inputmethod/event/CombinerChain.java index 47f781e62..538d759c8 100644 --- a/tools/dicttool/compat/com/android/inputmethod/event/CombinerChain.java +++ b/tools/dicttool/compat/com/android/inputmethod/event/CombinerChain.java @@ -28,8 +28,10 @@ import java.util.ArrayList; // TODO: there should not be a dependency to this in dicttool, so there // should be a sensible way to separate them cleanly. public class CombinerChain { - private StringBuilder mComposingWord = new StringBuilder(); - public CombinerChain(final Combiner... combinerList) {} + private StringBuilder mComposingWord; + public CombinerChain(final String initialText, final Combiner... combinerList) { + mComposingWord = new StringBuilder(initialText); + } public void processEvent(final ArrayList<Event> previousEvents, final Event newEvent) { mComposingWord.append(newEvent.getTextToCommit()); |