diff options
6 files changed, 297 insertions, 46 deletions
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 6be9ded5c..a8a29a1d4 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -1668,15 +1668,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen return didAutoCorrect; } - // Called from PointerTracker through the KeyboardActionListener interface - @Override - public void onTextInput(final String rawText) { - mConnection.beginBatchEdit(); - if (mWordComposer.isComposingWord()) { - commitCurrentAutoCorrection(rawText); - } else { - resetComposingState(true /* alsoResetLastComposedWord */); - } + // Called from the end of onTextInput + private void completeOnTextInput(final String rawText) { mHandler.postUpdateSuggestionStrip(); if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS && ResearchLogger.RESEARCH_KEY_OUTPUT_TEXT.equals(rawText)) { @@ -1699,12 +1692,44 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mEnteredText = text; } + // Called from PointerTracker through the KeyboardActionListener interface + @Override + public void onTextInput(final String rawText) { + mConnection.beginBatchEdit(); + boolean isReturningAsynchronously = false; + if (mWordComposer.isComposingWord()) { + commitCurrentAutoCorrection(rawText, new Runnable() { + @Override + public void run() { + completeOnTextInput(rawText); + } + }); + isReturningAsynchronously = true; + } else { + resetComposingState(true /* alsoResetLastComposedWord */); + } + if (!isReturningAsynchronously) { + completeOnTextInput(rawText); + } + } + + private void completeOnStartBatchInput(final SettingsValues settingsValues) { + final int codePointBeforeCursor = mConnection.getCodePointBeforeCursor(); + if (Character.isLetterOrDigit(codePointBeforeCursor) + || settingsValues.isUsuallyFollowedBySpace(codePointBeforeCursor)) { + mSpaceState = SPACE_STATE_PHANTOM; + } + mConnection.endBatchEdit(); + mWordComposer.setCapitalizedModeAtStartComposingTime(getActualCapsMode()); + } + @Override public void onStartBatchInput() { mInputUpdater.onStartBatchInput(); mHandler.cancelUpdateSuggestionStrip(); mConnection.beginBatchEdit(); final SettingsValues settingsValues = mSettings.getCurrent(); + boolean isReturningAsynchronously = false; if (mWordComposer.isComposingWord()) { if (settingsValues.mIsInternal) { if (mWordComposer.isBatchMode()) { @@ -1726,19 +1751,21 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen // tapping probably is that the word you intend to type is not in the dictionary, // so we do not attempt to correct, on the assumption that if that was a dictionary // word, the user would probably have gestured instead. - commitCurrentAutoCorrection(LastComposedWord.NOT_A_SEPARATOR); + commitCurrentAutoCorrection(LastComposedWord.NOT_A_SEPARATOR, new Runnable() { + @Override + public void run() { + completeOnStartBatchInput(settingsValues); + } + }); + isReturningAsynchronously = true; } else { commitTyped(LastComposedWord.NOT_A_SEPARATOR); } mExpectingUpdateSelection = true; } - final int codePointBeforeCursor = mConnection.getCodePointBeforeCursor(); - if (Character.isLetterOrDigit(codePointBeforeCursor) - || settingsValues.isUsuallyFollowedBySpace(codePointBeforeCursor)) { - mSpaceState = SPACE_STATE_PHANTOM; + if (!isReturningAsynchronously) { + completeOnStartBatchInput(settingsValues); } - mConnection.endBatchEdit(); - mWordComposer.setCapitalizedModeAtStartComposingTime(getActualCapsMode()); } private static final class InputUpdater implements Handler.Callback { @@ -2218,30 +2245,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen mKeyboardSwitcher.updateShiftState(); } - // Returns true if we did an autocorrection, false otherwise. - private boolean handleSeparator(final int primaryCode, final int x, final int y, - final int spaceState) { - boolean didAutoCorrect = false; - final SettingsValues currentSettings = mSettings.getCurrent(); - // We avoid sending spaces in languages without spaces if we were composing. - final boolean shouldAvoidSendingCode = Constants.CODE_SPACE == primaryCode - && !currentSettings.mCurrentLanguageHasSpaces && mWordComposer.isComposingWord(); - if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) { - // If we are in the middle of a recorrection, we need to commit the recorrection - // first so that we can insert the separator at the current cursor position. - resetEntireInputState(mLastSelectionStart); - } - if (mWordComposer.isComposingWord()) { // May have changed since we stored wasComposing - if (currentSettings.mCorrectionEnabled) { - final String separator = shouldAvoidSendingCode ? LastComposedWord.NOT_A_SEPARATOR - : new String(new int[] { primaryCode }, 0, 1); - commitCurrentAutoCorrection(separator); - didAutoCorrect = true; - } else { - commitTyped(new String(new int[]{primaryCode}, 0, 1)); - } - } - + private void completeHandleSeparator(final int primaryCode, final int x, final int y, + final int spaceState, final SettingsValues currentSettings, + final boolean shouldAvoidSendingCode) { final boolean swapWeakSpace = maybeStripSpace(primaryCode, spaceState, Constants.SUGGESTION_STRIP_COORDINATE == x); @@ -2296,7 +2302,44 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } mKeyboardSwitcher.updateShiftState(); - return didAutoCorrect; + } + + // Returns true if we do an autocorrection, false otherwise. + private boolean handleSeparator(final int primaryCode, final int x, final int y, + final int spaceState) { + boolean doesAutoCorrect = false; + final SettingsValues currentSettings = mSettings.getCurrent(); + // We avoid sending spaces in languages without spaces if we were composing. + final boolean shouldAvoidSendingCode = Constants.CODE_SPACE == primaryCode + && !currentSettings.mCurrentLanguageHasSpaces && mWordComposer.isComposingWord(); + if (mWordComposer.isCursorFrontOrMiddleOfComposingWord()) { + // If we are in the middle of a recorrection, we need to commit the recorrection + // first so that we can insert the separator at the current cursor position. + resetEntireInputState(mLastSelectionStart); + } + boolean isReturningAsynchronously = false; + if (mWordComposer.isComposingWord()) { // May have changed since we stored wasComposing + if (currentSettings.mCorrectionEnabled) { + final String separator = shouldAvoidSendingCode ? LastComposedWord.NOT_A_SEPARATOR + : new String(new int[] { primaryCode }, 0, 1); + commitCurrentAutoCorrection(separator, new Runnable() { + @Override + public void run() { + completeHandleSeparator(primaryCode, x, y, spaceState, currentSettings, + shouldAvoidSendingCode); + } + }); + doesAutoCorrect = true; + isReturningAsynchronously = true; + } else { + commitTyped(new String(new int[]{primaryCode}, 0, 1)); + } + } + if (!isReturningAsynchronously) { + completeHandleSeparator(primaryCode, x, y, spaceState, currentSettings, + shouldAvoidSendingCode); + } + return doesAutoCorrect; } private CharSequence getTextWithUnderline(final String text) { @@ -2507,7 +2550,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen setSuggestionStripShown(isSuggestionsStripVisible()); } - private void commitCurrentAutoCorrection(final String separatorString) { + private void commitCurrentAutoCorrection(final String separator, final Runnable callback) { // Complete any pending suggestions query first if (mHandler.hasPendingUpdateSuggestions()) { updateSuggestionStrip(); @@ -2523,16 +2566,16 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen } if (mSettings.isInternal()) { LatinImeLoggerUtils.onAutoCorrection( - typedWord, autoCorrection, separatorString, mWordComposer); + typedWord, autoCorrection, separator, mWordComposer); } if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) { final SuggestedWords suggestedWords = mSuggestedWords; ResearchLogger.latinIme_commitCurrentAutoCorrection(typedWord, autoCorrection, - separatorString, mWordComposer.isBatchMode(), suggestedWords); + separator, mWordComposer.isBatchMode(), suggestedWords); } mExpectingUpdateSelection = true; commitChosenWord(autoCorrection, LastComposedWord.COMMIT_TYPE_DECIDED_WORD, - separatorString); + separator); if (!typedWord.equals(autoCorrection)) { // This will make the correction flash for a short while as a visual clue // to the user that auto-correction happened. It has no other effect; in particular @@ -2545,6 +2588,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen typedWord, autoCorrection)); } } + if (callback != null) { + callback.run(); + } } // Called from {@link SuggestionStripView} through the {@link SuggestionStripView#Listener} diff --git a/native/jni/Android.mk b/native/jni/Android.mk index 0e36f3df9..d83bdadfa 100644 --- a/native/jni/Android.mk +++ b/native/jni/Android.mk @@ -78,6 +78,7 @@ LATIN_IME_CORE_SRC_FILES := \ dynamic_patricia_trie_reading_helper.cpp \ dynamic_patricia_trie_reading_utils.cpp \ dynamic_patricia_trie_writing_helper.cpp \ + dynamic_patricia_trie_writing_utils.cpp \ patricia_trie_policy.cpp \ patricia_trie_reading_utils.cpp) \ $(addprefix suggest/policyimpl/dictionary/utils/, \ diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.cpp b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.cpp index 2042fcbd2..a2712c34c 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.cpp @@ -72,7 +72,7 @@ void DynamicPatriciaTrieReadingHelper::followForwardLink() { } if (DynamicPatriciaTrieReadingUtils::isValidForwardLinkPosition(forwardLinkPosition)) { // Follow the forward link. - mPos = forwardLinkPosition; + mPos += forwardLinkPosition; nextNodeArray(); } else { // All node arrays have been read. diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.cpp b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.cpp index 128d69d88..a4793e167 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.cpp @@ -19,6 +19,7 @@ #include "suggest/policyimpl/dictionary/bigram/dynamic_bigram_list_policy.h" #include "suggest/policyimpl/dictionary/dynamic_patricia_trie_node_reader.h" #include "suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_helper.h" +#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.h" #include "suggest/policyimpl/dictionary/shortcut/dynamic_shortcut_list_policy.h" namespace latinime { diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.cpp new file mode 100644 index 000000000..4187504b4 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.cpp @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.h" + +#include <cstddef> +#include <cstdlib> +#include <stdint.h> + +#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" + +namespace latinime { + +const size_t DynamicPatriciaTrieWritingUtils::MAX_PTNODE_ARRAY_SIZE_TO_USE_SMALL_SIZE_FIELD = 0x7F; +const size_t DynamicPatriciaTrieWritingUtils::MAX_PTNODE_ARRAY_SIZE = 0x7FFF; +const int DynamicPatriciaTrieWritingUtils::SMALL_PTNODE_ARRAY_SIZE_FIELD_SIZE = 1; +const int DynamicPatriciaTrieWritingUtils::LARGE_PTNODE_ARRAY_SIZE_FIELD_SIZE = 2; +const int DynamicPatriciaTrieWritingUtils::LARGE_PTNODE_ARRAY_SIZE_FIELD_SIZE_FLAG = 0x8000; +const int DynamicPatriciaTrieWritingUtils::DICT_OFFSET_FIELD_SIZE = 3; +const int DynamicPatriciaTrieWritingUtils::MAX_DICT_OFFSET_VALUE = 0x7FFFFF; +const int DynamicPatriciaTrieWritingUtils::MIN_DICT_OFFSET_VALUE = -0x7FFFFF; +const int DynamicPatriciaTrieWritingUtils::DICT_OFFSET_NEGATIVE_FLAG = 0x800000; +const int DynamicPatriciaTrieWritingUtils::PROBABILITY_FIELD_SIZE = 1; +const int DynamicPatriciaTrieWritingUtils::NODE_FLAG_FIELD_SIZE = 1; + +/* static */ bool DynamicPatriciaTrieWritingUtils::writeForwardLinkPositionAndAdvancePosition( + BufferWithExtendableBuffer *const buffer, const int forwardLinkPos, + int *const forwardLinkFieldPos) { + const int offset = (forwardLinkPos != NOT_A_DICT_POS) ? + forwardLinkPos - (*forwardLinkFieldPos) : 0; + return writeDictOffset(buffer, offset, forwardLinkFieldPos); +} + +/* static */ bool DynamicPatriciaTrieWritingUtils::writePtNodeArraySizeAndAdvancePosition( + BufferWithExtendableBuffer *const buffer, const size_t arraySize, + int *const arraySizeFieldPos) { + if (arraySize <= MAX_PTNODE_ARRAY_SIZE_TO_USE_SMALL_SIZE_FIELD) { + return buffer->writeUintAndAdvancePosition(arraySize, SMALL_PTNODE_ARRAY_SIZE_FIELD_SIZE, + arraySizeFieldPos); + } else if (arraySize <= MAX_PTNODE_ARRAY_SIZE) { + uint32_t data = arraySize | LARGE_PTNODE_ARRAY_SIZE_FIELD_SIZE_FLAG; + return buffer->writeUintAndAdvancePosition(data, LARGE_PTNODE_ARRAY_SIZE_FIELD_SIZE, + arraySizeFieldPos); + } else { + AKLOGI("PtNode array size cannot be written because arraySize is too large: %zd", + arraySize); + ASSERT(false); + return false; + } +} + +/* static */ bool DynamicPatriciaTrieWritingUtils::writeFlagsAndAdvancePosition( + BufferWithExtendableBuffer *const buffer, + const DynamicPatriciaTrieReadingUtils::NodeFlags nodeFlags, int *const nodeFlagsFieldPos) { + return buffer->writeUintAndAdvancePosition(nodeFlags, NODE_FLAG_FIELD_SIZE, nodeFlagsFieldPos); +} + +/* static */ bool DynamicPatriciaTrieWritingUtils::writeParentPositionAndAdvancePosition( + BufferWithExtendableBuffer *const buffer, const int parentPosition, + int *const parentPosFieldPos) { + // Note that parentPosition is offset from node's head position. + int offset = (parentPosition != NOT_A_DICT_POS) ? parentPosition : 0; + return writeDictOffset(buffer, offset, parentPosFieldPos); +} + +/* static */ bool DynamicPatriciaTrieWritingUtils::writeCodePointsAndAdvancePosition( + BufferWithExtendableBuffer *const buffer, const int *const codePoints, + const int codePointCount, int *const codePointFieldPos) { + if (codePointCount <= 0) { + AKLOGI("code points cannot be written because codePointCount is invalid: %d", + codePointCount); + ASSERT(false); + return false; + } + const bool hasMultipleCodePoints = codePointCount > 1; + return buffer->writeCodePointsAndAdvancePosition(codePoints, codePointCount, + hasMultipleCodePoints, codePointFieldPos); +} + +/* static */ bool DynamicPatriciaTrieWritingUtils::writeProbabilityAndAdvancePosition( + BufferWithExtendableBuffer *const buffer, const int probability, + int *const probabilityFieldPos) { + if (probability < 0 || probability > MAX_PROBABILITY) { + AKLOGI("probability cannot be written because the probability is invalid: %d", + probability); + ASSERT(false); + return false; + } + return buffer->writeUintAndAdvancePosition(probability, PROBABILITY_FIELD_SIZE, + probabilityFieldPos); +} + +/* static */ bool DynamicPatriciaTrieWritingUtils::writeChildrenPositionAndAdvancePosition( + BufferWithExtendableBuffer *const buffer, const int childrenPosition, + int *const childrenPositionFieldPos) { + int offset = (childrenPosition != NOT_A_DICT_POS) ? + childrenPosition - (*childrenPositionFieldPos) : 0; + return writeDictOffset(buffer, offset, childrenPositionFieldPos); +} + +/* static */ bool DynamicPatriciaTrieWritingUtils::writeDictOffset( + BufferWithExtendableBuffer *const buffer, const int offset, int *const offsetFieldPos) { + if (offset > MAX_DICT_OFFSET_VALUE || offset < MIN_DICT_OFFSET_VALUE) { + AKLOGI("offset cannot be written because the offset is too large or too small: %d", + offset); + ASSERT(false); + return false; + } + uint32_t data = 0; + if (offset >= 0) { + data = offset; + } else { + data = abs(offset) | DICT_OFFSET_NEGATIVE_FLAG; + } + return buffer->writeUintAndAdvancePosition(data, DICT_OFFSET_FIELD_SIZE, offsetFieldPos); +} +} diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.h b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.h new file mode 100644 index 000000000..801042ddf --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_utils.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2013, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LATINIME_DYNAMIC_PATRICIA_TRIE_WRITING_UTILS_H +#define LATINIME_DYNAMIC_PATRICIA_TRIE_WRITING_UTILS_H + +#include <cstddef> + +#include "defines.h" +#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h" + +namespace latinime { + +class BufferWithExtendableBuffer; + +class DynamicPatriciaTrieWritingUtils { + public: + static bool writeForwardLinkPositionAndAdvancePosition( + BufferWithExtendableBuffer *const buffer, const int forwardLinkPos, + int *const forwardLinkFieldPos); + + static bool writePtNodeArraySizeAndAdvancePosition(BufferWithExtendableBuffer *const buffer, + const size_t arraySize, int *const arraySizeFieldPos); + + static bool writeFlagsAndAdvancePosition(BufferWithExtendableBuffer *const buffer, + const DynamicPatriciaTrieReadingUtils::NodeFlags nodeFlags, + int *const nodeFlagsFieldPos); + + static bool writeParentPositionAndAdvancePosition(BufferWithExtendableBuffer *const buffer, + const int parentPosition, int *const parentPosFieldPos); + + static bool writeCodePointsAndAdvancePosition(BufferWithExtendableBuffer *const buffer, + const int *const codePoints, const int codePointCount, int *const codePointFieldPos); + + static bool writeProbabilityAndAdvancePosition(BufferWithExtendableBuffer *const buffer, + const int probability, int *const probabilityFieldPos); + + static bool writeChildrenPositionAndAdvancePosition(BufferWithExtendableBuffer *const buffer, + const int childrenPosition, int *const childrenPositionFieldPos); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(DynamicPatriciaTrieWritingUtils); + + static const size_t MAX_PTNODE_ARRAY_SIZE_TO_USE_SMALL_SIZE_FIELD; + static const size_t MAX_PTNODE_ARRAY_SIZE; + static const int SMALL_PTNODE_ARRAY_SIZE_FIELD_SIZE; + static const int LARGE_PTNODE_ARRAY_SIZE_FIELD_SIZE; + static const int LARGE_PTNODE_ARRAY_SIZE_FIELD_SIZE_FLAG; + static const int DICT_OFFSET_FIELD_SIZE; + static const int MAX_DICT_OFFSET_VALUE; + static const int MIN_DICT_OFFSET_VALUE; + static const int DICT_OFFSET_NEGATIVE_FLAG; + static const int NODE_FLAG_FIELD_SIZE; + static const int PROBABILITY_FIELD_SIZE; + + static bool writeDictOffset(BufferWithExtendableBuffer *const buffer, const int offset, + int *const offsetFieldPos); +}; +} // namespace latinime +#endif /* LATINIME_DYNAMIC_PATRICIA_TRIE_WRITING_UTILS_H */ |