diff options
16 files changed, 322 insertions, 93 deletions
diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java index a638b238c..9ba46202a 100644 --- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java @@ -34,7 +34,6 @@ import android.preference.PreferenceManager; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.util.Log; -import android.util.SparseArray; import android.util.TypedValue; import android.view.LayoutInflater; import android.view.MotionEvent; @@ -71,6 +70,9 @@ import com.android.inputmethod.latin.utils.UsabilityStudyLogUtils; import com.android.inputmethod.latin.utils.ViewLayoutUtils; import com.android.inputmethod.research.ResearchLogger; +import java.util.ArrayDeque; +import java.util.HashMap; +import java.util.HashSet; import java.util.WeakHashMap; /** @@ -158,7 +160,10 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack private final int mKeyPreviewLayoutId; private final int mKeyPreviewOffset; private final int mKeyPreviewHeight; - private final SparseArray<TextView> mKeyPreviewTexts = CollectionUtils.newSparseArray(); + // Free {@link TextView} pool that can be used for key preview. + private final ArrayDeque<TextView> mFreeKeyPreviewTextViews = CollectionUtils.newArrayDeque(); + // Map from {@link Key} to {@link TextView} that is currently being displayed as key preview. + private final HashMap<Key,TextView> mShowingKeyPreviewTextViews = CollectionUtils.newHashMap(); private final KeyPreviewDrawParams mKeyPreviewDrawParams = new KeyPreviewDrawParams(); private boolean mShowKeyPreviewPopup = true; private int mKeyPreviewLingerTimeout; @@ -381,13 +386,18 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack public void handleMessage(final Message msg) { final MainKeyboardView mainKeyboardView = getOuterInstance(); if (mainKeyboardView == null) return; - final PointerTracker tracker = (PointerTracker) msg.obj; switch (msg.what) { case MSG_DISMISS_KEY_PREVIEW: - final TextView previewText = mainKeyboardView.mKeyPreviewTexts.get( - tracker.mPointerId); - if (previewText != null) { - previewText.setVisibility(INVISIBLE); + final Key key = (Key)msg.obj; + if (key != null) { + final TextView previewTextView = + mainKeyboardView.mShowingKeyPreviewTextViews.remove(key); + if (previewTextView != null) { + previewTextView.setVisibility(INVISIBLE); + mainKeyboardView.mFreeKeyPreviewTextViews.add(previewTextView); + } + // To redraw key top letter. + mainKeyboardView.invalidateKey(key); } break; case MSG_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT: @@ -396,12 +406,8 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack } } - public void dismissKeyPreview(final long delay, final PointerTracker tracker) { - sendMessageDelayed(obtainMessage(MSG_DISMISS_KEY_PREVIEW, tracker), delay); - } - - public void cancelDismissKeyPreview(final PointerTracker tracker) { - removeMessages(MSG_DISMISS_KEY_PREVIEW, tracker); + public void dismissKeyPreview(final long delay, final Key key) { + sendMessageDelayed(obtainMessage(MSG_DISMISS_KEY_PREVIEW, key), delay); } private void cancelAllDismissKeyPreviews() { @@ -681,33 +687,34 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack return mShowKeyPreviewPopup; } - private void addKeyPreview(final TextView keyPreview) { - locatePreviewPlacerView(); - mPreviewPlacerView.addView( - keyPreview, ViewLayoutUtils.newLayoutParam(mPreviewPlacerView, 0, 0)); - } - - private TextView getKeyPreviewText(final int pointerId) { - TextView previewText = mKeyPreviewTexts.get(pointerId); - if (previewText != null) { - return previewText; + private TextView getKeyPreviewTextView(final Key key) { + TextView previewTextView = mShowingKeyPreviewTextViews.remove(key); + if (previewTextView != null) { + return previewTextView; + } + previewTextView = mFreeKeyPreviewTextViews.poll(); + if (previewTextView != null) { + return previewTextView; } final Context context = getContext(); if (mKeyPreviewLayoutId != 0) { - previewText = (TextView)LayoutInflater.from(context).inflate(mKeyPreviewLayoutId, null); + previewTextView = (TextView)LayoutInflater.from(context) + .inflate(mKeyPreviewLayoutId, null); } else { - previewText = new TextView(context); + previewTextView = new TextView(context); } - mKeyPreviewTexts.put(pointerId, previewText); - return previewText; + locatePreviewPlacerView(); + mPreviewPlacerView.addView( + previewTextView, ViewLayoutUtils.newLayoutParam(mPreviewPlacerView, 0, 0)); + return previewTextView; } private void dismissAllKeyPreviews() { - final int pointerCount = mKeyPreviewTexts.size(); - for (int id = 0; id < pointerCount; id++) { - final TextView previewText = mKeyPreviewTexts.get(id); - if (previewText != null) { - previewText.setVisibility(INVISIBLE); + for (final Key key : new HashSet<Key>(mShowingKeyPreviewTextViews.keySet())) { + final TextView previewTextView = mShowingKeyPreviewTextViews.remove(key); + if (previewTextView != null) { + previewTextView.setVisibility(INVISIBLE); + mFreeKeyPreviewTextViews.add(previewTextView); } } PointerTracker.setReleasedKeyGraphicsToAllKeys(); @@ -735,23 +742,7 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack private static final int STATE_HAS_MOREKEYS = 1; @Override - public void showKeyPreview(final PointerTracker tracker) { - final KeyPreviewDrawParams previewParams = mKeyPreviewDrawParams; - final Keyboard keyboard = getKeyboard(); - if (!mShowKeyPreviewPopup) { - previewParams.mPreviewVisibleOffset = -keyboard.mVerticalGap; - return; - } - - final TextView previewText = getKeyPreviewText(tracker.mPointerId); - // If the key preview has no parent view yet, add it to the ViewGroup which can place - // key preview absolutely in SoftInputWindow. - if (previewText.getParent() == null) { - addKeyPreview(previewText); - } - - mDrawingHandler.cancelDismissKeyPreview(tracker); - final Key key = tracker.getKey(); + public void showKeyPreview(final Key key) { // If key is invalid or IME is already closed, we must not show key preview. // Trying to show key preview while root window is closed causes // WindowManager.BadTokenException. @@ -759,38 +750,47 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack return; } + final KeyPreviewDrawParams previewParams = mKeyPreviewDrawParams; + final Keyboard keyboard = getKeyboard(); + if (!mShowKeyPreviewPopup) { + previewParams.mPreviewVisibleOffset = -keyboard.mVerticalGap; + return; + } + + final TextView previewTextView = getKeyPreviewTextView(key); final KeyDrawParams drawParams = mKeyDrawParams; - previewText.setTextColor(drawParams.mPreviewTextColor); - final Drawable background = previewText.getBackground(); + previewTextView.setTextColor(drawParams.mPreviewTextColor); + final Drawable background = previewTextView.getBackground(); final String label = key.getPreviewLabel(); // What we show as preview should match what we show on a key top in onDraw(). if (label != null) { // TODO Should take care of temporaryShiftLabel here. - previewText.setCompoundDrawables(null, null, null, null); - previewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, + previewTextView.setCompoundDrawables(null, null, null, null); + previewTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, key.selectPreviewTextSize(drawParams)); - previewText.setTypeface(key.selectPreviewTypeface(drawParams)); - previewText.setText(label); + previewTextView.setTypeface(key.selectPreviewTypeface(drawParams)); + previewTextView.setText(label); } else { - previewText.setCompoundDrawables(null, null, null, + previewTextView.setCompoundDrawables(null, null, null, key.getPreviewIcon(keyboard.mIconsSet)); - previewText.setText(null); + previewTextView.setText(null); } - previewText.measure( + previewTextView.measure( ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); final int keyDrawWidth = key.getDrawWidth(); - final int previewWidth = previewText.getMeasuredWidth(); + final int previewWidth = previewTextView.getMeasuredWidth(); final int previewHeight = mKeyPreviewHeight; // The width and height of visible part of the key preview background. The content marker // of the background 9-patch have to cover the visible part of the background. - previewParams.mPreviewVisibleWidth = previewWidth - previewText.getPaddingLeft() - - previewText.getPaddingRight(); - previewParams.mPreviewVisibleHeight = previewHeight - previewText.getPaddingTop() - - previewText.getPaddingBottom(); + previewParams.mPreviewVisibleWidth = previewWidth - previewTextView.getPaddingLeft() + - previewTextView.getPaddingRight(); + previewParams.mPreviewVisibleHeight = previewHeight - previewTextView.getPaddingTop() + - previewTextView.getPaddingBottom(); // The distance between the top edge of the parent key and the bottom of the visible part // of the key preview background. - previewParams.mPreviewVisibleOffset = mKeyPreviewOffset - previewText.getPaddingBottom(); + previewParams.mPreviewVisibleOffset = + mKeyPreviewOffset - previewTextView.getPaddingBottom(); getLocationInWindow(mOriginCoords); // The key preview is horizontally aligned with the center of the visible part of the // parent key. If it doesn't fit in this {@link KeyboardView}, it is moved inward to fit and @@ -817,13 +817,14 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack background.setState(KEY_PREVIEW_BACKGROUND_STATE_TABLE[statePosition][hasMoreKeys]); } ViewLayoutUtils.placeViewAt( - previewText, previewX, previewY, previewWidth, previewHeight); - previewText.setVisibility(VISIBLE); + previewTextView, previewX, previewY, previewWidth, previewHeight); + previewTextView.setVisibility(VISIBLE); + mShowingKeyPreviewTextViews.put(key, previewTextView); } @Override - public void dismissKeyPreview(final PointerTracker tracker) { - mDrawingHandler.dismissKeyPreview(mKeyPreviewLingerTimeout, tracker); + public void dismissKeyPreview(final Key key) { + mDrawingHandler.dismissKeyPreview(mKeyPreviewLingerTimeout, key); } public void setSlidingKeyInputPreviewEnabled(final boolean enabled) { @@ -1175,6 +1176,12 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack if (key.altCodeWhileTyping() && key.isEnabled()) { params.mAnimAlpha = mAltCodeKeyWhileTypingAnimAlpha; } + // Don't draw key top letter when key preview is showing. + if (mShowingKeyPreviewTextViews.containsKey(key)) { + // TODO: Fade out animation for the key top letter, and fade in animation for the key + // background color when the user presses the key. + return; + } final int code = key.getCode(); if (code == Constants.CODE_SPACE) { drawSpacebar(key, canvas, paint); diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java index 385123998..fca727b29 100644 --- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java +++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java @@ -223,7 +223,7 @@ public final class MoreKeysKeyboard extends Keyboard { } public int getDefaultKeyCoordX() { - return mLeftKeys * mColumnWidth; + return mLeftKeys * mColumnWidth + mLeftPadding; } public int getX(final int n, final int row) { diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java index 973128d36..8492d9385 100644 --- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java +++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java @@ -81,11 +81,13 @@ public class MoreKeysKeyboardView extends KeyboardView implements MoreKeysPanel mListener = listener; final View container = getContainerView(); // The coordinates of panel's left-top corner in parentView's coordinate system. - final int x = pointX - getDefaultCoordX() - container.getPaddingLeft(); - final int y = pointY - container.getMeasuredHeight() + container.getPaddingBottom(); + // We need to consider background drawable paddings. + final int x = pointX - getDefaultCoordX() - container.getPaddingLeft() - getPaddingLeft(); + final int y = pointY - container.getMeasuredHeight() + container.getPaddingBottom() + + getPaddingBottom(); parentView.getLocationInWindow(mCoordinates); - // Ensure the horizontal position of the panel does not extend past the screen edges. + // Ensure the horizontal position of the panel does not extend past the parentView edges. final int maxX = parentView.getMeasuredWidth() - container.getMeasuredWidth(); final int panelX = Math.max(0, Math.min(maxX, x)) + CoordinateUtils.x(mCoordinates); final int panelY = y + CoordinateUtils.y(mCoordinates); diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java index 52f190e77..8860ed32a 100644 --- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java +++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java @@ -84,8 +84,8 @@ public final class PointerTracker implements PointerTrackerQueue.Element { public interface DrawingProxy { public void invalidateKey(Key key); - public void showKeyPreview(PointerTracker tracker); - public void dismissKeyPreview(PointerTracker tracker); + public void showKeyPreview(Key key); + public void dismissKeyPreview(Key key); public void showSlidingKeyInputPreview(PointerTracker tracker); public void dismissSlidingKeyInputPreview(); public void showGestureTrail(PointerTracker tracker, boolean showsFloatingPreviewText); @@ -637,7 +637,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { } private void setReleasedKeyGraphics(final Key key) { - mDrawingProxy.dismissKeyPreview(this); + mDrawingProxy.dismissKeyPreview(key); if (key == null) { return; } @@ -685,7 +685,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element { } if (!key.noKeyPreview() && !sInGesture && !needsToSuppressKeyPreviewPopup(eventTime)) { - mDrawingProxy.showKeyPreview(this); + mDrawingProxy.showKeyPreview(key); } updatePressKeyGraphics(key); diff --git a/native/jni/Android.mk b/native/jni/Android.mk index e770d9866..2c5401ba5 100644 --- a/native/jni/Android.mk +++ b/native/jni/Android.mk @@ -97,7 +97,8 @@ LATIN_IME_CORE_SRC_FILES := \ ver4_patricia_trie_node_reader.cpp \ ver4_patricia_trie_node_writer.cpp \ ver4_patricia_trie_policy.cpp \ - ver4_patricia_trie_reading_utils.cpp ) \ + ver4_patricia_trie_reading_utils.cpp \ + ver4_patricia_trie_writing_helper.cpp) \ $(addprefix suggest/policyimpl/dictionary/utils/, \ buffer_with_extendable_buffer.cpp \ byte_array_utils.cpp \ 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 063b84cbf..903d55307 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 @@ -17,13 +17,13 @@ #include "suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.h" #include <stdint.h> -#include <string> #include "defines.h" #include "suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h" #include "suggest/policyimpl/dictionary/structure/v3/dynamic_patricia_trie_policy.h" #include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h" #include "suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.h" +#include "suggest/policyimpl/dictionary/utils/file_utils.h" #include "suggest/policyimpl/dictionary/utils/format_utils.h" #include "suggest/policyimpl/dictionary/utils/mmapped_buffer.h" @@ -49,17 +49,15 @@ namespace latinime { return DictionaryStructureWithBufferPolicy::StructurePoilcyPtr( new DynamicPatriciaTriePolicy(mmappedBuffer)); case FormatUtils::VERSION_4: { - std::string dictDirPath(path); - const std::string::size_type pos = - dictDirPath.rfind(Ver4DictConstants::TRIE_FILE_EXTENSION); - if (pos == std::string::npos) { + const int dictDirPathBufSize = strlen(path) + 1 /* terminator */; + char dictDirPath[dictDirPathBufSize]; + if (!FileUtils::getFilePathWithoutSuffix(path, Ver4DictConstants::TRIE_FILE_EXTENSION, + dictDirPathBufSize, dictDirPath)) { // Dictionary file name is not valid as a version 4 dictionary. return DictionaryStructureWithBufferPolicy::StructurePoilcyPtr(0); } - // Removing extension to get the base path. - dictDirPath.erase(pos); const Ver4DictBuffers::Ver4DictBuffersPtr dictBuffers = - Ver4DictBuffers::openVer4DictBuffers(dictDirPath.c_str(), mmappedBuffer); + Ver4DictBuffers::openVer4DictBuffers(dictDirPath, mmappedBuffer); if (!dictBuffers.get()->isValid()) { AKLOGE("DICT: The dictionary doesn't satisfy ver4 format requirements."); ASSERT(false); diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.h index eaf18b56a..873b2406c 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.h @@ -67,8 +67,28 @@ class TerminalPositionLookupTable : public SingleDictContent { return mSize; } - bool flushToFile(const char *const dictDirPath) const { - return flush(dictDirPath, Ver4DictConstants::TERMINAL_ADDRESS_TABLE_FILE_EXTENSION); + bool flushToFile(const char *const dictDirPath, const int newHeaderRegionSize) const { + const int headerRegionSizeDiff = newHeaderRegionSize - mHeaderRegionSize; + // If header region size has been changed, terminal PtNode positions have to be adjusted + // depending on the new header region size. + if (headerRegionSizeDiff != 0) { + TerminalPositionLookupTable lookupTableToWrite; + for (int i = 0; i < mSize; ++i) { + const int terminalPtNodePosition = getTerminalPtNodePosition(i) + + headerRegionSizeDiff; + if (!lookupTableToWrite.setTerminalPtNodePosition(i, terminalPtNodePosition)) { + AKLOGE("Cannot set terminal position to lookupTableToWrite." + " terminalId: %d, position: %d", i, terminalPtNodePosition); + return false; + } + } + return lookupTableToWrite.flush(dictDirPath, + Ver4DictConstants::TERMINAL_ADDRESS_TABLE_FILE_EXTENSION); + } else { + // We can simply use this lookup table because the header region size has not been + // changed. + return flush(dictDirPath, Ver4DictConstants::TERMINAL_ADDRESS_TABLE_FILE_EXTENSION); + } } private: diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.cpp index e17c5eab4..d31253153 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.cpp @@ -25,7 +25,8 @@ namespace latinime { -bool Ver4DictBuffers::flush(const char *const dictDirPath) const { +bool Ver4DictBuffers::flushHeaderAndDictBuffers(const char *const dictDirPath, + const BufferWithExtendableBuffer *const headerBuffer) const { // Create temporary directory. const int tmpDirPathBufSize = FileUtils::getFilePathWithSuffixBufSize(dictDirPath, DictFileWritingUtils::TEMP_FILE_SUFFIX_FOR_WRITING_DICT_FILE); @@ -38,8 +39,7 @@ bool Ver4DictBuffers::flush(const char *const dictDirPath) const { return false; } // Write trie file. - const BufferWithExtendableBuffer *buffers[] = - {&mExpandableHeaderBuffer, &mExpandableTrieBuffer}; + const BufferWithExtendableBuffer *buffers[] = {headerBuffer, &mExpandableTrieBuffer}; if (!DictFileWritingUtils::flushBuffersToFileInDir(tmpDirPath, Ver4DictConstants::TRIE_FILE_EXTENSION, buffers, 2 /* bufferCount */)) { AKLOGE("Dictionary trie file %s/%s cannot be written.", tmpDirPath, @@ -47,7 +47,7 @@ bool Ver4DictBuffers::flush(const char *const dictDirPath) const { return false; } // Write dictionary contents. - if (!mTerminalPositionLookupTable.flushToFile(tmpDirPath)) { + if (!mTerminalPositionLookupTable.flushToFile(tmpDirPath, headerBuffer->getTailPosition())) { AKLOGE("Terminal position lookup table cannot be written. %s", tmpDirPath); return false; } diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h index 0684bdd0c..bfd0bbdfe 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h @@ -57,6 +57,10 @@ class Ver4DictBuffers { return &mExpandableTrieBuffer; } + AK_FORCE_INLINE const BufferWithExtendableBuffer *getTrieBuffer() const { + return &mExpandableTrieBuffer; + } + AK_FORCE_INLINE TerminalPositionLookupTable *getUpdatableTerminalPositionLookupTable() { return &mTerminalPositionLookupTable; } @@ -89,7 +93,12 @@ class Ver4DictBuffers { return mIsUpdatable; } - bool flush(const char *const dictDirPath) const; + bool flush(const char *const dictDirPath) const { + return flushHeaderAndDictBuffers(dictDirPath, &mExpandableHeaderBuffer); + } + + bool flushHeaderAndDictBuffers(const char *const dictDirPath, + const BufferWithExtendableBuffer *const headerBuffer) const; private: DISALLOW_COPY_AND_ASSIGN(Ver4DictBuffers); diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp index 698483a79..8ee15e0ef 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp @@ -216,7 +216,11 @@ bool Ver4PatriciaTriePolicy::removeBigramWords(const int *const word0, const int } void Ver4PatriciaTriePolicy::flush(const char *const filePath) { - // TODO: Implement. + if (!mBuffers.get()->isUpdatable()) { + AKLOGI("Warning: flush() is called for non-updatable dictionary. filePath: %s", filePath); + return; + } + mWritingHelper.writeToDictFile(filePath, &mHeaderPolicy, mUnigramCount, mBigramCount); } void Ver4PatriciaTriePolicy::flushWithGC(const char *const filePath) { diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.h index e8fdf5513..605de96a7 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.h +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.h @@ -26,6 +26,7 @@ #include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h" #include "suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.h" #include "suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.h" +#include "suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.h" #include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" namespace latinime { @@ -50,6 +51,7 @@ class Ver4PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy { &mShortcutPolicy), mUpdatingHelper(mDictBuffer, &mNodeReader, &mNodeWriter, mHeaderPolicy.isDecayingDict()), + mWritingHelper(mBuffers.get()), mUnigramCount(mHeaderPolicy.getUnigramCount()), mBigramCount(mHeaderPolicy.getBigramCount()) {}; @@ -120,6 +122,7 @@ class Ver4PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy { Ver4PatriciaTrieNodeReader mNodeReader; Ver4PatriciaTrieNodeWriter mNodeWriter; DynamicPatriciaTrieUpdatingHelper mUpdatingHelper; + Ver4PatriciaTrieWritingHelper mWritingHelper; int mUnigramCount; int mBigramCount; }; diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.cpp new file mode 100644 index 000000000..c85a632d3 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.cpp @@ -0,0 +1,50 @@ +/* + * 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/structure/v4/ver4_patricia_trie_writing_helper.h" + +#include <cstring> + +#include "suggest/policyimpl/dictionary/header/header_policy.h" +#include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h" +#include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h" +#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h" +#include "suggest/policyimpl/dictionary/utils/file_utils.h" + +namespace latinime { + +void Ver4PatriciaTrieWritingHelper::writeToDictFile(const char *const trieFilePath, + const HeaderPolicy *const headerPolicy, const int unigramCount, + const int bigramCount) const { + const int dirPathBufSize = strlen(trieFilePath) + 1 /* terminator */; + char dirPath[dirPathBufSize]; + FileUtils::getDirPath(trieFilePath, dirPathBufSize, dirPath); + BufferWithExtendableBuffer headerBuffer( + BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE); + const int extendedRegionSize = headerPolicy->getExtendedRegionSize() + + mBuffers->getTrieBuffer()->getUsedAdditionalBufferSize(); + if (!headerPolicy->writeHeaderToBuffer(&headerBuffer, false /* updatesLastUpdatedTime */, + false /* updatesLastDecayedTime */, unigramCount, bigramCount, extendedRegionSize)) { + AKLOGE("Cannot write header structure to buffer. updatesLastUpdatedTime: %d, " + "updatesLastDecayedTime: %d, unigramCount: %d, bigramCount: %d, " + "extendedRegionSize: %d", false, false, unigramCount, bigramCount, + extendedRegionSize); + return; + } + mBuffers->flushHeaderAndDictBuffers(dirPath, &headerBuffer); +} + +} // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.h new file mode 100644 index 000000000..80d631527 --- /dev/null +++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.h @@ -0,0 +1,42 @@ +/* + * 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_VER4_PATRICIA_TRIE_WRITING_HELPER_H +#define LATINIME_VER4_PATRICIA_TRIE_WRITING_HELPER_H + +#include "defines.h" + +namespace latinime { + +class HeaderPolicy; +class Ver4DictBuffers; + +class Ver4PatriciaTrieWritingHelper { + public: + Ver4PatriciaTrieWritingHelper(Ver4DictBuffers *const buffers) + : mBuffers(buffers) {} + + void writeToDictFile(const char *const trieFilePath, const HeaderPolicy *const headerPolicy, + const int unigramCount, const int bigramCount) const; + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(Ver4PatriciaTrieWritingHelper); + + Ver4DictBuffers *const mBuffers; +}; +} // namespace latinime + +#endif /* LATINIME_VER4_PATRICIA_TRIE_WRITING_HELPER_H */ diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/file_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/file_utils.cpp index 1748d5a49..dedcd7a99 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/utils/file_utils.cpp +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/file_utils.cpp @@ -88,4 +88,43 @@ namespace latinime { snprintf(outFilePath, filePathBufSize, "%s/%s", dirPath, fileName); } +/* static */ bool FileUtils::getFilePathWithoutSuffix(const char *const filePath, + const char *const suffix, const int outDirPathBufSize, char *const outDirPath) { + const int filePathLength = strlen(filePath); + const int suffixLength = strlen(suffix); + if (filePathLength <= suffixLength) { + AKLOGE("File path length (%s:%d) is shorter that suffix length (%s:%d).", + filePath, filePathLength, suffix, suffixLength); + return false; + } + const int resultFilePathLength = filePathLength - suffixLength; + if (outDirPathBufSize <= resultFilePathLength) { + AKLOGE("outDirPathBufSize is too small. filePath: %s, suffix: %s, outDirPathBufSize: %d", + filePath, suffix, outDirPathBufSize); + return false; + } + if (strncmp(filePath + resultFilePathLength, suffix, suffixLength) != 0) { + AKLOGE("File Path %s does not have %s as a suffix", filePath, suffix); + return false; + } + snprintf(outDirPath, resultFilePathLength + 1 /* terminator */, "%s", filePath); + return true; +} + +/* static */ void FileUtils::getDirPath(const char *const filePath, const int outDirPathBufSize, + char *const outDirPath) { + for (int i = strlen(filePath) - 1; i >= 0; --i) { + if (filePath[i] == '/') { + if (i >= outDirPathBufSize) { + AKLOGE("outDirPathBufSize is too small. filePath: %s, outDirPathBufSize: %d", + filePath, outDirPathBufSize); + ASSERT(false); + return; + } + snprintf(outDirPath, i + 1 /* terminator */, "%s", filePath); + return; + } + } +} + } // namespace latinime diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/file_utils.h b/native/jni/src/suggest/policyimpl/dictionary/utils/file_utils.h index fc27aeecb..7dcdef85f 100644 --- a/native/jni/src/suggest/policyimpl/dictionary/utils/file_utils.h +++ b/native/jni/src/suggest/policyimpl/dictionary/utils/file_utils.h @@ -39,6 +39,13 @@ class FileUtils { static void getFilePath(const char *const dirPath, const char *const fileName, const int filePathBufSize, char *const outFilePath); + // Returns whether the filePath have the suffix. + static bool getFilePathWithoutSuffix(const char *const filePath, const char *const suffix, + const int dirPathBufSize, char *const outDirPath); + + static void getDirPath(const char *const filePath, const int dirPathBufSize, + char *const outDirPath); + private: DISALLOW_IMPLICIT_CONSTRUCTORS(FileUtils); }; diff --git a/tests/src/com/android/inputmethod/latin/Ver4BinaryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/Ver4BinaryDictionaryTests.java index 15d990c6d..b51a86b1c 100644 --- a/tests/src/com/android/inputmethod/latin/Ver4BinaryDictionaryTests.java +++ b/tests/src/com/android/inputmethod/latin/Ver4BinaryDictionaryTests.java @@ -250,4 +250,51 @@ public class Ver4BinaryDictionaryTests extends AndroidTestCase { binaryDictionary.removeBigramWords("bcc", "aaa"); } + public void testFlushDictionary() { + final String dictVersion = Long.toString(System.currentTimeMillis()); + File trieFile = null; + try { + trieFile = createEmptyDictionaryAndGetTrieFile(dictVersion); + } catch (IOException e) { + fail("IOException while writing an initial dictionary : " + e); + } + BinaryDictionary binaryDictionary = new BinaryDictionary(trieFile.getAbsolutePath(), + 0 /* offset */, trieFile.length(), true /* useFullEditDistance */, + Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + + final int probability = 100; + binaryDictionary.addUnigramWord("aaa", probability); + binaryDictionary.addUnigramWord("abcd", probability); + // Close without flushing. + binaryDictionary.close(); + + binaryDictionary = new BinaryDictionary(trieFile.getAbsolutePath(), + 0 /* offset */, trieFile.length(), true /* useFullEditDistance */, + Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + + assertEquals(Dictionary.NOT_A_PROBABILITY, binaryDictionary.getFrequency("aaa")); + assertEquals(Dictionary.NOT_A_PROBABILITY, binaryDictionary.getFrequency("abcd")); + + binaryDictionary.addUnigramWord("aaa", probability); + binaryDictionary.addUnigramWord("abcd", probability); + binaryDictionary.flush(); + binaryDictionary.close(); + + binaryDictionary = new BinaryDictionary(trieFile.getAbsolutePath(), + 0 /* offset */, trieFile.length(), true /* useFullEditDistance */, + Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + + assertEquals(probability, binaryDictionary.getFrequency("aaa")); + assertEquals(probability, binaryDictionary.getFrequency("abcd")); + binaryDictionary.addUnigramWord("bcde", probability); + binaryDictionary.flush(); + binaryDictionary.close(); + + binaryDictionary = new BinaryDictionary(trieFile.getAbsolutePath(), + 0 /* offset */, trieFile.length(), true /* useFullEditDistance */, + Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */); + assertEquals(probability, binaryDictionary.getFrequency("bcde")); + binaryDictionary.close(); + } + } |