aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--java/res/layout/emoji_palettes_view.xml4
-rw-r--r--java/res/layout/input_view.xml35
-rw-r--r--java/res/layout/main_keyboard_frame.xml45
-rw-r--r--java/res/values/donottranslate-text-decorator.xml127
-rw-r--r--java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java2
-rw-r--r--java/src/com/android/inputmethod/keyboard/Key.java24
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java7
-rw-r--r--java/src/com/android/inputmethod/keyboard/TextDecorator.java444
-rw-r--r--java/src/com/android/inputmethod/keyboard/TextDecoratorUi.java259
-rw-r--r--java/src/com/android/inputmethod/keyboard/TextDecoratorUiOperator.java55
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java8
-rw-r--r--java/src/com/android/inputmethod/latin/InputPointers.java6
-rw-r--r--java/src/com/android/inputmethod/latin/InputView.java4
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java179
-rw-r--r--java/src/com/android/inputmethod/latin/PunctuationSuggestions.java1
-rw-r--r--java/src/com/android/inputmethod/latin/RichInputConnection.java31
-rw-r--r--java/src/com/android/inputmethod/latin/Suggest.java12
-rw-r--r--java/src/com/android/inputmethod/latin/SuggestedWords.java28
-rw-r--r--java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java261
-rw-r--r--java/src/com/android/inputmethod/latin/settings/SettingsValues.java22
-rw-r--r--java/src/com/android/inputmethod/latin/utils/ViewLayoutUtils.java39
-rw-r--r--native/jni/NativeFileList.mk5
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/structure/v4/bigram/ver4_bigram_list_policy.cpp282
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/structure/v4/bigram/ver4_bigram_list_policy.h72
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.cpp219
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.h128
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_entry.h99
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.cpp21
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.h78
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.cpp10
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h11
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp20
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h10
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.cpp27
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.h7
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp91
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.h8
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.cpp136
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.h43
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/utils/trie_map.h2
-rw-r--r--native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_test.cpp20
-rw-r--r--native/jni/tests/utils/time_keeper_test.cpp38
-rw-r--r--tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java9
-rw-r--r--tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java19
-rw-r--r--tests/src/com/android/inputmethod/latin/settings/SpacingAndPunctuationsTests.java2
45 files changed, 1638 insertions, 1312 deletions
diff --git a/java/res/layout/emoji_palettes_view.xml b/java/res/layout/emoji_palettes_view.xml
index a6ea38ba4..26cc042ab 100644
--- a/java/res/layout/emoji_palettes_view.xml
+++ b/java/res/layout/emoji_palettes_view.xml
@@ -20,10 +20,10 @@
<com.android.inputmethod.keyboard.emoji.EmojiPalettesView
xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/emoji_keyboard_view"
- android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:layout_gravity="bottom"
+ android:orientation="vertical"
style="?attr/emojiPalettesViewStyle"
>
<LinearLayout
diff --git a/java/res/layout/input_view.xml b/java/res/layout/input_view.xml
index a4bcdcc8a..46551f63f 100644
--- a/java/res/layout/input_view.xml
+++ b/java/res/layout/input_view.xml
@@ -21,38 +21,11 @@
<com.android.inputmethod.latin.InputView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="bottom|center_horizontal"
- android:orientation="vertical" >
- <!-- The height of key_preview_backing view will automatically be determined by code. -->
- <FrameLayout
- android:id="@+id/key_preview_backing"
- android:layout_width="match_parent"
- android:layout_height="0dp" />
- <LinearLayout
+ android:layout_height="wrap_content">
+ <include
android:id="@+id/main_keyboard_frame"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="vertical" >
-
- <!-- To ensure that key preview popup is correctly placed when the current system locale is
- one of RTL locales, layoutDirection="ltr" is needed in the SDK version 17+. -->
- <com.android.inputmethod.latin.suggestions.SuggestionStripView
- android:id="@+id/suggestion_strip_view"
- android:layoutDirection="ltr"
- android:layout_width="match_parent"
- android:layout_height="@dimen/config_suggestions_strip_height"
- android:gravity="center_vertical"
- style="?attr/suggestionStripViewStyle" />
-
- <!-- To ensure that key preview popup is correctly placed when the current system locale is
- one of RTL locales, layoutDirection="ltr" is needed in the SDK version 17+. -->
- <com.android.inputmethod.keyboard.MainKeyboardView
- android:id="@+id/keyboard_view"
- android:layoutDirection="ltr"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
- </LinearLayout>
+ layout="@layout/main_keyboard_frame" />
<include
+ android:id="@+id/emoji_palettes_view"
layout="@layout/emoji_palettes_view" />
</com.android.inputmethod.latin.InputView>
diff --git a/java/res/layout/main_keyboard_frame.xml b/java/res/layout/main_keyboard_frame.xml
new file mode 100644
index 000000000..ebf746679
--- /dev/null
+++ b/java/res/layout/main_keyboard_frame.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom"
+ android:orientation="vertical" >
+
+ <!-- To ensure that key preview popup is correctly placed when the current system locale is
+ one of RTL locales, layoutDirection="ltr" is needed in the SDK version 17+. -->
+ <com.android.inputmethod.latin.suggestions.SuggestionStripView
+ android:id="@+id/suggestion_strip_view"
+ android:layoutDirection="ltr"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/config_suggestions_strip_height"
+ android:gravity="center_vertical"
+ style="?attr/suggestionStripViewStyle" />
+
+ <!-- To ensure that key preview popup is correctly placed when the current system locale is
+ one of RTL locales, layoutDirection="ltr" is needed in the SDK version 17+. -->
+ <com.android.inputmethod.keyboard.MainKeyboardView
+ android:id="@+id/keyboard_view"
+ android:layoutDirection="ltr"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+</LinearLayout>
diff --git a/java/res/values/donottranslate-text-decorator.xml b/java/res/values/donottranslate-text-decorator.xml
new file mode 100644
index 000000000..9c39a4689
--- /dev/null
+++ b/java/res/values/donottranslate-text-decorator.xml
@@ -0,0 +1,127 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+
+<resources>
+ <!-- The delay time in milliseconds from to show the commit indicator -->
+ <integer name="text_decorator_delay_in_milliseconds_to_show_commit_indicator">
+ 500
+ </integer>
+
+ <!-- The extra margin in dp around the hit area of the commit/add-to-dictionary indicator -->
+ <integer name="text_decorator_hit_area_margin_in_dp">
+ 4
+ </integer>
+
+ <!-- If true, the commit/add-to-text indicator will be suppressed when the word isn't going to
+ trigger auto-correction. -->
+ <bool name="text_decorator_only_for_auto_correction">false</bool>
+
+ <!-- If true, the commit/add-to-text indicator will be suppressed when the word is already in
+ the dictionary. -->
+ <bool name="text_decorator_only_for_out_of_vocabulary">false</bool>
+
+ <!-- Background color to be used to highlight the target text when the commit indicator is
+ visible. -->
+ <color name="text_decorator_commit_indicator_text_highlight_color">
+ #B6E2DE
+ </color>
+
+ <!-- Background color of the commit indicator. -->
+ <color name="text_decorator_commit_indicator_background_color">
+ #48B6AC
+ </color>
+
+ <!-- Foreground color of the commit indicator. -->
+ <color name="text_decorator_commit_indicator_foreground_color">
+ #FFFFFF
+ </color>
+
+ <!-- Viewport size of "text_decorator_commit_indicator_path". -->
+ <integer name="text_decorator_commit_indicator_path_size">
+ 480
+ </integer>
+
+ <!-- Coordinates of the closed path to be used to render the commit indicator.
+ The format is: X[0], Y[0], X[1], Y[1], ..., X[N-1], Y[N-1] -->
+ <integer-array name="text_decorator_commit_indicator_path">
+ <item>180</item>
+ <item>323</item>
+ <item>97</item>
+ <item>240</item>
+ <item>68</item>
+ <item>268</item>
+ <item>180</item>
+ <item>380</item>
+ <item>420</item>
+ <item>140</item>
+ <item>392</item>
+ <item>112</item>
+ </integer-array>
+
+ <!-- Background color to be used to highlight the target text when the add-to-dictionary
+ indicator is visible. -->
+ <color name="text_decorator_add_to_dictionary_indicator_text_highlight_color">
+ #D1E7B7
+ </color>
+
+ <!-- Foreground color of the commit indicator. -->
+ <color name="text_decorator_add_to_dictionary_indicator_background_color">
+ #4EB848
+ </color>
+
+ <!-- Foreground color of the add-to-dictionary indicator. -->
+ <color name="text_decorator_add_to_dictionary_indicator_foreground_color">
+ #FFFFFF
+ </color>
+
+ <!-- Viewport size of "text_decorator_add_to_dictionary_indicator_path". -->
+ <integer name="text_decorator_add_to_dictionary_indicator_path_size">
+ 480
+ </integer>
+
+ <!-- Coordinates of the closed path to be used to render the add-to-dictionary indicator.
+ The format is: X[0], Y[0], X[1], Y[1], ..., X[N-1], Y[N-1] -->
+ <integer-array name="text_decorator_add_to_dictionary_indicator_path">
+ <item>380</item>
+ <item>260</item>
+ <item>260</item>
+ <item>260</item>
+ <item>260</item>
+ <item>380</item>
+ <item>220</item>
+ <item>380</item>
+ <item>220</item>
+ <item>260</item>
+ <item>100</item>
+ <item>260</item>
+ <item>100</item>
+ <item>220</item>
+ <item>220</item>
+ <item>220</item>
+ <item>220</item>
+ <item>100</item>
+ <item>260</item>
+ <item>100</item>
+ <item>260</item>
+ <item>220</item>
+ <item>380</item>
+ <item>220</item>
+ </integer-array>
+</resources>
diff --git a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
index c07997bc9..c33c01552 100644
--- a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
+++ b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
@@ -68,7 +68,7 @@ public final class SuggestionSpanUtils {
public static CharSequence getTextWithSuggestionSpan(final Context context,
final String pickedWord, final SuggestedWords suggestedWords) {
if (TextUtils.isEmpty(pickedWord) || suggestedWords.isEmpty()
- || suggestedWords.mIsPrediction || suggestedWords.isPunctuationSuggestions()) {
+ || suggestedWords.isPrediction() || suggestedWords.isPunctuationSuggestions()) {
return pickedWord;
}
diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java
index f00889ed7..3743d26e6 100644
--- a/java/src/com/android/inputmethod/keyboard/Key.java
+++ b/java/src/com/android/inputmethod/keyboard/Key.java
@@ -93,13 +93,13 @@ public class Key implements Comparable<Key> {
/** Icon to display instead of a label. Icon takes precedence over a label */
private final int mIconId;
- /** Width of the key, not including the gap */
+ /** Width of the key, excluding the gap */
private final int mWidth;
- /** Height of the key, not including the gap */
+ /** Height of the key, excluding the gap */
private final int mHeight;
- /** X coordinate of the key in the keyboard layout */
+ /** X coordinate of the top-left corner of the key in the keyboard layout, excluding the gap. */
private final int mX;
- /** Y coordinate of the key in the keyboard layout */
+ /** Y coordinate of the top-left corner of the key in the keyboard layout, excluding the gap. */
private final int mY;
/** Hit bounding box of the key */
private final Rect mHitBox = new Rect();
@@ -736,18 +736,34 @@ public class Key implements Comparable<Key> {
return iconSet.getIconDrawable(getIconId());
}
+ /**
+ * Gets the width of the key in pixels, excluding the gap.
+ * @return The width of the key in pixels, excluding the gap.
+ */
public int getWidth() {
return mWidth;
}
+ /**
+ * Gets the height of the key in pixels, excluding the gap.
+ * @return The height of the key in pixels, excluding the gap.
+ */
public int getHeight() {
return mHeight;
}
+ /**
+ * Gets the x-coordinate of the top-left corner of the key in pixels, excluding the gap.
+ * @return The x-coordinate of the top-left corner of the key in pixels, excluding the gap.
+ */
public int getX() {
return mX;
}
+ /**
+ * Gets the y-coordinate of the top-left corner of the key in pixels, excluding the gap.
+ * @return The y-coordinate of the top-left corner of the key in pixels, excluding the gap.
+ */
public int getY() {
return mY;
}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index 77cdf49fb..30bd1dfda 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -28,6 +28,7 @@ import android.view.View;
import android.view.inputmethod.EditorInfo;
import com.android.inputmethod.compat.InputMethodServiceCompatUtils;
+import com.android.inputmethod.event.Event;
import com.android.inputmethod.keyboard.KeyboardLayoutSet.KeyboardLayoutSetException;
import com.android.inputmethod.keyboard.emoji.EmojiPalettesView;
import com.android.inputmethod.keyboard.internal.KeyboardState;
@@ -311,9 +312,9 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
/**
* Updates state machine to figure out when to automatically switch back to the previous mode.
*/
- public void onCodeInput(final int code, final int currentAutoCapsState,
+ public void onEvent(final Event event, final int currentAutoCapsState,
final int currentRecapitalizeState) {
- mState.onCodeInput(code, currentAutoCapsState, currentRecapitalizeState);
+ mState.onEvent(event, currentAutoCapsState, currentRecapitalizeState);
}
public boolean isShowingEmojiPalettes() {
@@ -359,7 +360,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
R.layout.input_view, null);
mMainKeyboardFrame = mCurrentInputView.findViewById(R.id.main_keyboard_frame);
mEmojiPalettesView = (EmojiPalettesView)mCurrentInputView.findViewById(
- R.id.emoji_keyboard_view);
+ R.id.emoji_palettes_view);
mKeyboardView = (MainKeyboardView) mCurrentInputView.findViewById(R.id.keyboard_view);
mKeyboardView.setHardwareAcceleratedDrawingEnabled(isHardwareAcceleratedDrawingEnabled);
diff --git a/java/src/com/android/inputmethod/keyboard/TextDecorator.java b/java/src/com/android/inputmethod/keyboard/TextDecorator.java
new file mode 100644
index 000000000..178516134
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/TextDecorator.java
@@ -0,0 +1,444 @@
+/*
+ * 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.keyboard;
+
+import android.graphics.Matrix;
+import android.graphics.PointF;
+import android.graphics.RectF;
+import android.inputmethodservice.InputMethodService;
+import android.os.Message;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+import android.view.inputmethod.CursorAnchorInfo;
+
+import com.android.inputmethod.annotations.UsedForTesting;
+import com.android.inputmethod.compat.CursorAnchorInfoCompatWrapper;
+import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper;
+
+import javax.annotation.Nonnull;
+
+/**
+ * A controller class of commit/add-to-dictionary indicator (a.k.a. TextDecorator). This class
+ * is designed to be independent of UI subsystems such as {@link View}. All the UI related
+ * operations are delegated to {@link TextDecoratorUi} via {@link TextDecoratorUiOperator}.
+ */
+public class TextDecorator {
+ private static final String TAG = TextDecorator.class.getSimpleName();
+ private static final boolean DEBUG = false;
+
+ private static final int MODE_NONE = 0;
+ private static final int MODE_COMMIT = 1;
+ private static final int MODE_ADD_TO_DICTIONARY = 2;
+
+ private int mMode = MODE_NONE;
+
+ private final PointF mLocalOrigin = new PointF();
+ private final RectF mRelativeIndicatorBounds = new RectF();
+ private final RectF mRelativeComposingTextBounds = new RectF();
+
+ private boolean mIsFullScreenMode = false;
+ private SuggestedWordInfo mWaitingWord = null;
+ private CursorAnchorInfoCompatWrapper mCursorAnchorInfoWrapper = null;
+
+ @Nonnull
+ private final Listener mListener;
+
+ @Nonnull
+ private TextDecoratorUiOperator mUiOperator = EMPTY_UI_OPERATOR;
+
+ public interface Listener {
+ /**
+ * Called when the user clicks the composing text to commit.
+ * @param wordInfo the suggested word which the user clicked on.
+ */
+ void onClickComposingTextToCommit(final SuggestedWordInfo wordInfo);
+
+ /**
+ * Called when the user clicks the composing text to add the word into the dictionary.
+ * @param wordInfo the suggested word which the user clicked on.
+ */
+ void onClickComposingTextToAddToDictionary(final SuggestedWordInfo wordInfo);
+ }
+
+ public TextDecorator(final Listener listener) {
+ mListener = (listener != null) ? listener : EMPTY_LISTENER;
+ }
+
+ /**
+ * Sets the UI operator for {@link TextDecorator}. Any user visible operations will be
+ * delegated to the associated UI operator.
+ * @param uiOperator the UI operator to be associated.
+ */
+ public void setUiOperator(final TextDecoratorUiOperator uiOperator) {
+ mUiOperator.disposeUi();
+ mUiOperator = uiOperator;
+ mUiOperator.setOnClickListener(getOnClickHandler());
+ }
+
+ private final Runnable mDefaultOnClickHandler = new Runnable() {
+ @Override
+ public void run() {
+ onClickIndicator();
+ }
+ };
+
+ @UsedForTesting
+ final Runnable getOnClickHandler() {
+ return mDefaultOnClickHandler;
+ }
+
+ /**
+ * Shows the "Commit" indicator and associates it with the given suggested word.
+ *
+ * <p>The effect of {@link #showCommitIndicator(SuggestedWordInfo)} and
+ * {@link #showAddToDictionaryIndicator(SuggestedWordInfo)} are exclusive to each other. Call
+ * {@link #reset()} to hide the indicator.</p>
+ *
+ * @param wordInfo the suggested word which should be associated with the indicator. This object
+ * will be passed back in {@link Listener#onClickComposingTextToCommit(SuggestedWordInfo)}
+ */
+ public void showCommitIndicator(final SuggestedWordInfo wordInfo) {
+ if (mMode == MODE_COMMIT && wordInfo != null &&
+ TextUtils.equals(mWaitingWord.mWord, wordInfo.mWord)) {
+ // Skip layout for better performance.
+ return;
+ }
+ mWaitingWord = wordInfo;
+ mMode = MODE_COMMIT;
+ layoutLater();
+ }
+
+ /**
+ * Shows the "Add to dictionary" indicator and associates it with associating the given
+ * suggested word.
+ *
+ * <p>The effect of {@link #showCommitIndicator(SuggestedWordInfo)} and
+ * {@link #showAddToDictionaryIndicator(SuggestedWordInfo)} are exclusive to each other. Call
+ * {@link #reset()} to hide the indicator.</p>
+ *
+ * @param wordInfo the suggested word which should be associated with the indicator. This object
+ * will be passed back in
+ * {@link Listener#onClickComposingTextToAddToDictionary(SuggestedWordInfo)}.
+ */
+ public void showAddToDictionaryIndicator(final SuggestedWordInfo wordInfo) {
+ if (mMode == MODE_ADD_TO_DICTIONARY && wordInfo != null &&
+ TextUtils.equals(mWaitingWord.mWord, wordInfo.mWord)) {
+ // Skip layout for better performance.
+ return;
+ }
+ mWaitingWord = wordInfo;
+ mMode = MODE_ADD_TO_DICTIONARY;
+ layoutLater();
+ return;
+ }
+
+ /**
+ * Must be called when the input method is about changing to for from the full screen mode.
+ * @param fullScreenMode {@code true} if the input method is entering the full screen mode.
+ * {@code false} is the input method is finishing the full screen mode.
+ */
+ public void notifyFullScreenMode(final boolean fullScreenMode) {
+ final boolean currentFullScreenMode = mIsFullScreenMode;
+ if (!currentFullScreenMode && fullScreenMode) {
+ // Currently full screen mode is not supported.
+ // TODO: Support full screen mode.
+ mUiOperator.hideUi();
+ }
+ mIsFullScreenMode = fullScreenMode;
+ }
+
+ /**
+ * Resets previous requests and makes indicator invisible.
+ */
+ public void reset() {
+ mWaitingWord = null;
+ mMode = MODE_NONE;
+ mLocalOrigin.set(0.0f, 0.0f);
+ mRelativeIndicatorBounds.set(0.0f, 0.0f, 0.0f, 0.0f);
+ mRelativeComposingTextBounds.set(0.0f, 0.0f, 0.0f, 0.0f);
+ cancelLayoutInternalExpectedly("Resetting internal state.");
+ }
+
+ /**
+ * Must be called when the {@link InputMethodService#onUpdateCursorAnchorInfo()} is called.
+ *
+ * <p>CAVEAT: Currently the input method author is responsible for ignoring
+ * {@link InputMethodService#onUpdateCursorAnchorInfo()} called in full screen mode.</p>
+ * @param info the compatibility wrapper object for the received {@link CursorAnchorInfo}.
+ */
+ public void onUpdateCursorAnchorInfo(final CursorAnchorInfoCompatWrapper info) {
+ if (mIsFullScreenMode) {
+ // TODO: Consider to call InputConnection#requestCursorAnchorInfo to disable the
+ // event callback to suppress unnecessary event callbacks.
+ return;
+ }
+ mCursorAnchorInfoWrapper = info;
+ // Do not use layoutLater() to minimize the latency.
+ layoutImmediately();
+ }
+
+ /**
+ * Hides indicator if the new composing text doesn't match the expected one.
+ *
+ * <p>Calling this method is optional but recommended whenever the new composition is passed to
+ * the application. The motivation of this method is to reduce the UI latency. With this method,
+ * we can hide the indicator without waiting the arrival of the
+ * {@link InputMethodService#onUpdateCursorAnchorInfo(CursorAnchorInfo)} callback, assuming that
+ * the application accepts the new composing text without any modification. Even if this
+ * assumption is false, the indicator will be shown again when
+ * {@link InputMethodService#onUpdateCursorAnchorInfo(CursorAnchorInfo)} is actually received.
+ * </p>
+ *
+ * @param newComposingText the new composing text that is being passed to the application.
+ */
+ public void hideIndicatorIfNecessary(final CharSequence newComposingText) {
+ if (mMode != MODE_COMMIT && mMode != MODE_ADD_TO_DICTIONARY) {
+ return;
+ }
+ if (!TextUtils.equals(newComposingText, mWaitingWord.mWord)) {
+ mUiOperator.hideUi();
+ }
+ }
+
+ private void cancelLayoutInternalUnexpectedly(final String message) {
+ mUiOperator.hideUi();
+ Log.d(TAG, message);
+ }
+
+ private void cancelLayoutInternalExpectedly(final String message) {
+ mUiOperator.hideUi();
+ if (DEBUG) {
+ Log.d(TAG, message);
+ }
+ }
+
+ private void layoutLater() {
+ mLayoutInvalidator.invalidateLayout();
+ }
+
+
+ private void layoutImmediately() {
+ // Clear pending layout requests.
+ mLayoutInvalidator.cancelInvalidateLayout();
+ layoutMain();
+ }
+
+ private void layoutMain() {
+ if (mIsFullScreenMode) {
+ cancelLayoutInternalUnexpectedly("Full screen mode isn't yet supported.");
+ return;
+ }
+
+ if (mMode != MODE_COMMIT && mMode != MODE_ADD_TO_DICTIONARY) {
+ if (mMode == MODE_NONE) {
+ cancelLayoutInternalExpectedly("Not ready for layouting.");
+ } else {
+ cancelLayoutInternalUnexpectedly("Unknown mMode=" + mMode);
+ }
+ return;
+ }
+
+ final CursorAnchorInfoCompatWrapper info = mCursorAnchorInfoWrapper;
+
+ if (info == null) {
+ cancelLayoutInternalExpectedly("CursorAnchorInfo isn't available.");
+ return;
+ }
+
+ final Matrix matrix = info.getMatrix();
+ if (matrix == null) {
+ cancelLayoutInternalUnexpectedly("Matrix is null");
+ }
+
+ final CharSequence composingText = info.getComposingText();
+ if (mMode == MODE_COMMIT) {
+ if (composingText == null) {
+ cancelLayoutInternalExpectedly("composingText is null.");
+ return;
+ }
+ final int composingTextStart = info.getComposingTextStart();
+ final int lastCharRectIndex = composingTextStart + composingText.length() - 1;
+ final RectF lastCharRect = info.getCharacterRect(lastCharRectIndex);
+ final int lastCharRectFlag = info.getCharacterRectFlags(lastCharRectIndex);
+ final int lastCharRectType =
+ lastCharRectFlag & CursorAnchorInfoCompatWrapper.CHARACTER_RECT_TYPE_MASK;
+ if (lastCharRect == null || matrix == null || lastCharRectType !=
+ CursorAnchorInfoCompatWrapper.CHARACTER_RECT_TYPE_FULLY_VISIBLE) {
+ mUiOperator.hideUi();
+ return;
+ }
+ final RectF segmentStartCharRect = new RectF(lastCharRect);
+ for (int i = composingText.length() - 2; i >= 0; --i) {
+ final RectF charRect = info.getCharacterRect(composingTextStart + i);
+ if (charRect == null) {
+ break;
+ }
+ if (charRect.top != segmentStartCharRect.top) {
+ break;
+ }
+ if (charRect.bottom != segmentStartCharRect.bottom) {
+ break;
+ }
+ segmentStartCharRect.set(charRect);
+ }
+
+ mLocalOrigin.set(lastCharRect.right, lastCharRect.top);
+ mRelativeIndicatorBounds.set(lastCharRect.right, lastCharRect.top,
+ lastCharRect.right + lastCharRect.height(), lastCharRect.bottom);
+ mRelativeIndicatorBounds.offset(-mLocalOrigin.x, -mLocalOrigin.y);
+
+ mRelativeIndicatorBounds.set(lastCharRect.right, lastCharRect.top,
+ lastCharRect.right + lastCharRect.height(), lastCharRect.bottom);
+ mRelativeIndicatorBounds.offset(-mLocalOrigin.x, -mLocalOrigin.y);
+
+ mRelativeComposingTextBounds.set(segmentStartCharRect.left, segmentStartCharRect.top,
+ segmentStartCharRect.right, segmentStartCharRect.bottom);
+ mRelativeComposingTextBounds.offset(-mLocalOrigin.x, -mLocalOrigin.y);
+
+ if (mWaitingWord == null) {
+ cancelLayoutInternalExpectedly("mWaitingText is null.");
+ return;
+ }
+ if (TextUtils.isEmpty(mWaitingWord.mWord)) {
+ cancelLayoutInternalExpectedly("mWaitingText.mWord is empty.");
+ return;
+ }
+ if (!TextUtils.equals(composingText, mWaitingWord.mWord)) {
+ // This is indeed an expected situation because of the asynchronous nature of
+ // input method framework in Android. Note that composingText is notified from the
+ // application, while mWaitingWord.mWord is obtained directly from the InputLogic.
+ cancelLayoutInternalExpectedly(
+ "Composing text doesn't match the one we are waiting for.");
+ return;
+ }
+ } else {
+ if (!TextUtils.isEmpty(composingText)) {
+ // This is an unexpected case.
+ // TODO: Document this.
+ mUiOperator.hideUi();
+ return;
+ }
+ // In MODE_ADD_TO_DICTIONARY, we cannot retrieve the character position at all because
+ // of the lack of composing text. We will use the insertion marker position instead.
+ if (info.isInsertionMarkerClipped()) {
+ mUiOperator.hideUi();
+ return;
+ }
+ final float insertionMarkerHolizontal = info.getInsertionMarkerHorizontal();
+ final float insertionMarkerTop = info.getInsertionMarkerTop();
+ mLocalOrigin.set(insertionMarkerHolizontal, insertionMarkerTop);
+ }
+
+ final RectF indicatorBounds = new RectF(mRelativeIndicatorBounds);
+ final RectF composingTextBounds = new RectF(mRelativeComposingTextBounds);
+ indicatorBounds.offset(mLocalOrigin.x, mLocalOrigin.y);
+ composingTextBounds.offset(mLocalOrigin.x, mLocalOrigin.y);
+ mUiOperator.layoutUi(mMode == MODE_COMMIT, matrix, indicatorBounds, composingTextBounds);
+ }
+
+ private void onClickIndicator() {
+ if (mWaitingWord == null || TextUtils.isEmpty(mWaitingWord.mWord)) {
+ return;
+ }
+ switch (mMode) {
+ case MODE_COMMIT:
+ mListener.onClickComposingTextToCommit(mWaitingWord);
+ break;
+ case MODE_ADD_TO_DICTIONARY:
+ mListener.onClickComposingTextToAddToDictionary(mWaitingWord);
+ break;
+ }
+ }
+
+ private final LayoutInvalidator mLayoutInvalidator = new LayoutInvalidator(this);
+
+ /**
+ * Used for managing pending layout tasks for {@link TextDecorator#layoutLater()}.
+ */
+ private static final class LayoutInvalidator {
+ private final HandlerImpl mHandler;
+ public LayoutInvalidator(final TextDecorator ownerInstance) {
+ mHandler = new HandlerImpl(ownerInstance);
+ }
+
+ private static final int MSG_LAYOUT = 0;
+
+ private static final class HandlerImpl
+ extends LeakGuardHandlerWrapper<TextDecorator> {
+ public HandlerImpl(final TextDecorator ownerInstance) {
+ super(ownerInstance);
+ }
+
+ @Override
+ public void handleMessage(final Message msg) {
+ final TextDecorator owner = getOwnerInstance();
+ if (owner == null) {
+ return;
+ }
+ switch (msg.what) {
+ case MSG_LAYOUT:
+ owner.layoutMain();
+ break;
+ }
+ }
+ }
+
+ /**
+ * Puts a layout task into the scheduler. Does nothing if one or more layout tasks are
+ * already scheduled.
+ */
+ public void invalidateLayout() {
+ if (!mHandler.hasMessages(MSG_LAYOUT)) {
+ mHandler.obtainMessage(MSG_LAYOUT).sendToTarget();
+ }
+ }
+
+ /**
+ * Clears the pending layout tasks.
+ */
+ public void cancelInvalidateLayout() {
+ mHandler.removeMessages(MSG_LAYOUT);
+ }
+ }
+
+ private final static Listener EMPTY_LISTENER = new Listener() {
+ @Override
+ public void onClickComposingTextToCommit(SuggestedWordInfo wordInfo) {
+ }
+ @Override
+ public void onClickComposingTextToAddToDictionary(SuggestedWordInfo wordInfo) {
+ }
+ };
+
+ private final static TextDecoratorUiOperator EMPTY_UI_OPERATOR = new TextDecoratorUiOperator() {
+ @Override
+ public void disposeUi() {
+ }
+ @Override
+ public void hideUi() {
+ }
+ @Override
+ public void setOnClickListener(Runnable listener) {
+ }
+ @Override
+ public void layoutUi(boolean isCommitMode, Matrix matrix, RectF indicatorBounds,
+ RectF composingTextBounds) {
+ }
+ };
+}
diff --git a/java/src/com/android/inputmethod/keyboard/TextDecoratorUi.java b/java/src/com/android/inputmethod/keyboard/TextDecoratorUi.java
new file mode 100644
index 000000000..6e215a9ca
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/TextDecoratorUi.java
@@ -0,0 +1,259 @@
+/*
+ * 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.keyboard;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.RectF;
+import android.graphics.drawable.ColorDrawable;
+import android.inputmethodservice.InputMethodService;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.view.ViewGroup.LayoutParams;
+import android.view.ViewParent;
+import android.widget.PopupWindow;
+import android.widget.RelativeLayout;
+
+import com.android.inputmethod.latin.R;
+
+/**
+ * Used as the UI component of {@link TextDecorator}.
+ */
+public final class TextDecoratorUi implements TextDecoratorUiOperator {
+ private static final boolean VISUAL_DEBUG = false;
+ private static final int VISUAL_DEBUG_HIT_AREA_COLOR = 0x80ff8000;
+
+ private final RelativeLayout mLocalRootView;
+ private final CommitIndicatorView mCommitIndicatorView;
+ private final AddToDictionaryIndicatorView mAddToDictionaryIndicatorView;
+ private final PopupWindow mTouchEventWindow;
+ private final View mTouchEventWindowClickListenerView;
+ private final float mHitAreaMarginInPixels;
+
+ /**
+ * This constructor is designed to be called from {@link InputMethodService#setInputView(View)}.
+ * Other usages are not supported.
+ *
+ * @param context the context of the input method.
+ * @param inputView the view that is passed to {@link InputMethodService#setInputView(View)}.
+ */
+ public TextDecoratorUi(final Context context, final View inputView) {
+ final Resources resources = context.getResources();
+ final int hitAreaMarginInDP = resources.getInteger(
+ R.integer.text_decorator_hit_area_margin_in_dp);
+ mHitAreaMarginInPixels = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ hitAreaMarginInDP, resources.getDisplayMetrics());
+
+ mLocalRootView = new RelativeLayout(context);
+ mLocalRootView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
+ LayoutParams.MATCH_PARENT));
+ // TODO: Use #setBackground(null) for API Level >= 16.
+ mLocalRootView.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
+
+ final ViewGroup contentView = getContentView(inputView);
+ mCommitIndicatorView = new CommitIndicatorView(context);
+ mAddToDictionaryIndicatorView = new AddToDictionaryIndicatorView(context);
+ mLocalRootView.addView(mCommitIndicatorView);
+ mLocalRootView.addView(mAddToDictionaryIndicatorView);
+ if (contentView != null) {
+ contentView.addView(mLocalRootView);
+ }
+
+ // This popup window is used to avoid the limitation that the input method is not able to
+ // observe the touch events happening outside of InputMethodService.Insets#touchableRegion.
+ // We don't use this popup window for rendering the UI for performance reasons though.
+ mTouchEventWindow = new PopupWindow(context);
+ if (VISUAL_DEBUG) {
+ mTouchEventWindow.setBackgroundDrawable(new ColorDrawable(VISUAL_DEBUG_HIT_AREA_COLOR));
+ } else {
+ mTouchEventWindow.setBackgroundDrawable(null);
+ }
+ mTouchEventWindowClickListenerView = new View(context);
+ mTouchEventWindow.setContentView(mTouchEventWindowClickListenerView);
+ }
+
+ @Override
+ public void disposeUi() {
+ if (mLocalRootView != null) {
+ final ViewParent parent = mLocalRootView.getParent();
+ if (parent != null && parent instanceof ViewGroup) {
+ ((ViewGroup) parent).removeView(mLocalRootView);
+ }
+ mLocalRootView.removeAllViews();
+ }
+ if (mTouchEventWindow != null) {
+ mTouchEventWindow.dismiss();
+ }
+ }
+
+ @Override
+ public void hideUi() {
+ mCommitIndicatorView.setVisibility(View.GONE);
+ mAddToDictionaryIndicatorView.setVisibility(View.GONE);
+ mTouchEventWindow.dismiss();
+ }
+
+ @Override
+ public void layoutUi(final boolean isCommitMode, final Matrix matrix,
+ final RectF indicatorBounds, final RectF composingTextBounds) {
+ final RectF indicatorBoundsInScreenCoordinates = new RectF();
+ matrix.mapRect(indicatorBoundsInScreenCoordinates, indicatorBounds);
+ mCommitIndicatorView.setBounds(indicatorBoundsInScreenCoordinates);
+ mAddToDictionaryIndicatorView.setBounds(indicatorBoundsInScreenCoordinates);
+
+ final RectF hitAreaBounds = new RectF(composingTextBounds);
+ hitAreaBounds.union(indicatorBounds);
+ final RectF hitAreaBoundsInScreenCoordinates = new RectF();
+ matrix.mapRect(hitAreaBoundsInScreenCoordinates, hitAreaBounds);
+ hitAreaBoundsInScreenCoordinates.inset(-mHitAreaMarginInPixels, -mHitAreaMarginInPixels);
+
+ final int[] originScreen = new int[2];
+ mLocalRootView.getLocationOnScreen(originScreen);
+ final int viewOriginX = originScreen[0];
+ final int viewOriginY = originScreen[1];
+
+ final View toBeShown;
+ final View toBeHidden;
+ if (isCommitMode) {
+ toBeShown = mCommitIndicatorView;
+ toBeHidden = mAddToDictionaryIndicatorView;
+ } else {
+ toBeShown = mAddToDictionaryIndicatorView;
+ toBeHidden = mCommitIndicatorView;
+ }
+ toBeShown.setX(indicatorBoundsInScreenCoordinates.left - viewOriginX);
+ toBeShown.setY(indicatorBoundsInScreenCoordinates.top - viewOriginY);
+ toBeShown.setVisibility(View.VISIBLE);
+ toBeHidden.setVisibility(View.GONE);
+
+ if (mTouchEventWindow.isShowing()) {
+ mTouchEventWindow.update((int)hitAreaBoundsInScreenCoordinates.left - viewOriginX,
+ (int)hitAreaBoundsInScreenCoordinates.top - viewOriginY,
+ (int)hitAreaBoundsInScreenCoordinates.width(),
+ (int)hitAreaBoundsInScreenCoordinates.height());
+ } else {
+ mTouchEventWindow.setWidth((int)hitAreaBoundsInScreenCoordinates.width());
+ mTouchEventWindow.setHeight((int)hitAreaBoundsInScreenCoordinates.height());
+ mTouchEventWindow.showAtLocation(mLocalRootView, Gravity.NO_GRAVITY,
+ (int)hitAreaBoundsInScreenCoordinates.left - viewOriginX,
+ (int)hitAreaBoundsInScreenCoordinates.top - viewOriginY);
+ }
+ }
+
+ @Override
+ public void setOnClickListener(final Runnable listener) {
+ mTouchEventWindowClickListenerView.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(final View arg0) {
+ listener.run();
+ }
+ });
+ }
+
+ private static class IndicatorView extends View {
+ private final Path mPath;
+ private final Path mTmpPath = new Path();
+ private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
+ private final Matrix mMatrix = new Matrix();
+ private final int mBackgroundColor;
+ private final int mForegroundColor;
+ private final RectF mBounds = new RectF();
+ public IndicatorView(Context context, final int pathResourceId,
+ final int sizeResourceId, final int backgroundColorResourceId,
+ final int foregroundColroResourceId) {
+ super(context);
+ final Resources resources = context.getResources();
+ mPath = createPath(resources, pathResourceId, sizeResourceId);
+ mBackgroundColor = resources.getColor(backgroundColorResourceId);
+ mForegroundColor = resources.getColor(foregroundColroResourceId);
+ }
+
+ public void setBounds(final RectF rect) {
+ mBounds.set(rect);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ mPaint.setColor(mBackgroundColor);
+ mPaint.setStyle(Paint.Style.FILL);
+ canvas.drawRect(0.0f, 0.0f, mBounds.width(), mBounds.height(), mPaint);
+
+ mMatrix.reset();
+ mMatrix.postScale(mBounds.width(), mBounds.height());
+ mPath.transform(mMatrix, mTmpPath);
+ mPaint.setColor(mForegroundColor);
+ canvas.drawPath(mTmpPath, mPaint);
+ }
+
+ private static Path createPath(final Resources resources, final int pathResourceId,
+ final int sizeResourceId) {
+ final int size = resources.getInteger(sizeResourceId);
+ final float normalizationFactor = 1.0f / size;
+ final int[] array = resources.getIntArray(pathResourceId);
+
+ final Path path = new Path();
+ for (int i = 0; i < array.length; i += 2) {
+ if (i == 0) {
+ path.moveTo(array[i] * normalizationFactor, array[i + 1] * normalizationFactor);
+ } else {
+ path.lineTo(array[i] * normalizationFactor, array[i + 1] * normalizationFactor);
+ }
+ }
+ path.close();
+ return path;
+ }
+ }
+
+ private static ViewGroup getContentView(final View view) {
+ final View rootView = view.getRootView();
+ if (rootView == null) {
+ return null;
+ }
+
+ final ViewGroup windowContentView = (ViewGroup)rootView.findViewById(android.R.id.content);
+ if (windowContentView == null) {
+ return null;
+ }
+ return windowContentView;
+ }
+
+ private static final class CommitIndicatorView extends TextDecoratorUi.IndicatorView {
+ public CommitIndicatorView(final Context context) {
+ super(context, R.array.text_decorator_commit_indicator_path,
+ R.integer.text_decorator_commit_indicator_path_size,
+ R.color.text_decorator_commit_indicator_background_color,
+ R.color.text_decorator_commit_indicator_foreground_color);
+ }
+ }
+
+ private static final class AddToDictionaryIndicatorView extends TextDecoratorUi.IndicatorView {
+ public AddToDictionaryIndicatorView(final Context context) {
+ super(context, R.array.text_decorator_add_to_dictionary_indicator_path,
+ R.integer.text_decorator_add_to_dictionary_indicator_path_size,
+ R.color.text_decorator_add_to_dictionary_indicator_background_color,
+ R.color.text_decorator_add_to_dictionary_indicator_foreground_color);
+ }
+ }
+} \ No newline at end of file
diff --git a/java/src/com/android/inputmethod/keyboard/TextDecoratorUiOperator.java b/java/src/com/android/inputmethod/keyboard/TextDecoratorUiOperator.java
new file mode 100644
index 000000000..f84e12d8c
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/TextDecoratorUiOperator.java
@@ -0,0 +1,55 @@
+/*
+ * 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.keyboard;
+
+import android.graphics.Matrix;
+import android.graphics.PointF;
+import android.graphics.RectF;
+
+/**
+ * This interface defines how UI operations required for {@link TextDecorator} are delegated to
+ * the actual UI implementation class.
+ */
+public interface TextDecoratorUiOperator {
+ /**
+ * Called to notify that the UI is ready to be disposed.
+ */
+ void disposeUi();
+
+ /**
+ * Called when the UI should become invisible.
+ */
+ void hideUi();
+
+ /**
+ * Called to set the new click handler.
+ * @param onClickListener the callback object whose {@link Runnable#run()} should be called when
+ * the indicator is clicked.
+ */
+ void setOnClickListener(final Runnable onClickListener);
+
+ /**
+ * Called when the layout should be updated.
+ * @param isCommitMode {@code true} if the commit indicator should be shown. Show the
+ * add-to-dictionary indicator otherwise.
+ * @param matrix The matrix that transforms the local coordinates into the screen coordinates.
+ * @param indicatorBounds The bounding box of the indicator, in local coordinates.
+ * @param composingTextBounds The bounding box of the composing text, in local coordinates.
+ */
+ void layoutUi(final boolean isCommitMode, final Matrix matrix, final RectF indicatorBounds,
+ final RectF composingTextBounds);
+}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java
index b98ced97c..5f4d55bdb 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java
@@ -19,6 +19,7 @@ package com.android.inputmethod.keyboard.internal;
import android.text.TextUtils;
import android.util.Log;
+import com.android.inputmethod.event.Event;
import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.utils.RecapitalizeStatus;
@@ -29,7 +30,7 @@ import com.android.inputmethod.latin.utils.RecapitalizeStatus;
*
* The input events are {@link #onLoadKeyboard(int, int)}, {@link #onSaveKeyboardState()},
* {@link #onPressKey(int,boolean,int,int)}, {@link #onReleaseKey(int,boolean,int,int)},
- * {@link #onCodeInput(int,int,int)}, {@link #onFinishSlidingInput(int,int)},
+ * {@link #onEvent(Event,int,int)}, {@link #onFinishSlidingInput(int,int)},
* {@link #onUpdateShiftState(int,int)}, {@link #onResetKeyboardStateToAlphabet(int,int)}.
*
* The actions are {@link SwitchActions}'s methods.
@@ -610,10 +611,11 @@ public final class KeyboardState {
return c == Constants.CODE_SPACE || c == Constants.CODE_ENTER;
}
- public void onCodeInput(final int code, final int currentAutoCapsState,
+ public void onEvent(final Event event, final int currentAutoCapsState,
final int currentRecapitalizeState) {
+ final int code = event.isFunctionalKeyEvent() ? event.mKeyCode : event.mCodePoint;
if (DEBUG_EVENT) {
- Log.d(TAG, "onCodeInput: code=" + Constants.printableCode(code)
+ Log.d(TAG, "onEvent: code=" + Constants.printableCode(code)
+ " autoCaps=" + currentAutoCapsState + " " + this);
}
diff --git a/java/src/com/android/inputmethod/latin/InputPointers.java b/java/src/com/android/inputmethod/latin/InputPointers.java
index 790e0d830..d57a881c0 100644
--- a/java/src/com/android/inputmethod/latin/InputPointers.java
+++ b/java/src/com/android/inputmethod/latin/InputPointers.java
@@ -145,6 +145,12 @@ public final class InputPointers {
return mPointerIds.getPrimitiveArray();
}
+ /**
+ * Gets the time each point was registered, in milliseconds, relative to the first event in the
+ * sequence.
+ * @return The time each point was registered, in milliseconds, relative to the first event in
+ * the sequence.
+ */
public int[] getTimes() {
if (DebugFlags.DEBUG_ENABLED || DEBUG_TIME) {
if (!isValidTimeStamps()) {
diff --git a/java/src/com/android/inputmethod/latin/InputView.java b/java/src/com/android/inputmethod/latin/InputView.java
index e9e12f09f..7fa935413 100644
--- a/java/src/com/android/inputmethod/latin/InputView.java
+++ b/java/src/com/android/inputmethod/latin/InputView.java
@@ -21,14 +21,14 @@ import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
-import android.widget.LinearLayout;
+import android.widget.FrameLayout;
import com.android.inputmethod.accessibility.AccessibilityUtils;
import com.android.inputmethod.keyboard.MainKeyboardView;
import com.android.inputmethod.latin.suggestions.MoreSuggestionsView;
import com.android.inputmethod.latin.suggestions.SuggestionStripView;
-public final class InputView extends LinearLayout {
+public final class InputView extends FrameLayout {
private final Rect mInputViewRect = new Rect();
private MainKeyboardView mMainKeyboardView;
private KeyboardTopPaddingForwarder mKeyboardTopPaddingForwarder;
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 87b34a99c..ee0ff5c0a 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -29,7 +29,6 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
import android.content.res.Resources;
-import android.graphics.Rect;
import android.inputmethodservice.InputMethodService;
import android.media.AudioManager;
import android.net.ConnectivityManager;
@@ -43,6 +42,7 @@ import android.util.Log;
import android.util.PrintWriterPrinter;
import android.util.Printer;
import android.util.SparseArray;
+import android.view.Gravity;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
@@ -57,7 +57,6 @@ import android.view.inputmethod.InputMethodSubtype;
import com.android.inputmethod.accessibility.AccessibilityUtils;
import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.compat.CursorAnchorInfoCompatWrapper;
-import com.android.inputmethod.compat.InputConnectionCompatUtils;
import com.android.inputmethod.compat.InputMethodServiceCompatUtils;
import com.android.inputmethod.dictionarypack.DictionaryPackConstants;
import com.android.inputmethod.event.Event;
@@ -69,6 +68,7 @@ import com.android.inputmethod.keyboard.KeyboardActionListener;
import com.android.inputmethod.keyboard.KeyboardId;
import com.android.inputmethod.keyboard.KeyboardSwitcher;
import com.android.inputmethod.keyboard.MainKeyboardView;
+import com.android.inputmethod.keyboard.TextDecoratorUi;
import com.android.inputmethod.latin.Suggest.OnGetSuggestedWordsCallback;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.define.DebugFlags;
@@ -94,6 +94,7 @@ import com.android.inputmethod.latin.utils.JniUtils;
import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper;
import com.android.inputmethod.latin.utils.StatsUtils;
import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
+import com.android.inputmethod.latin.utils.ViewLayoutUtils;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -150,8 +151,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// TODO: Move these {@link View}s to {@link KeyboardSwitcher}.
private View mInputView;
- private View mExtractArea;
- private View mKeyPreviewBackingView;
private SuggestionStripView mSuggestionStripView;
private RichInputMethodManager mRichImm;
@@ -183,8 +182,9 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private static final int MSG_UPDATE_TAIL_BATCH_INPUT_COMPLETED = 6;
private static final int MSG_RESET_CACHES = 7;
private static final int MSG_WAIT_FOR_DICTIONARY_LOAD = 8;
+ private static final int MSG_SHOW_COMMIT_INDICATOR = 9;
// Update this when adding new messages
- private static final int MSG_LAST = MSG_WAIT_FOR_DICTIONARY_LOAD;
+ private static final int MSG_LAST = MSG_SHOW_COMMIT_INDICATOR;
private static final int ARG1_NOT_GESTURE_INPUT = 0;
private static final int ARG1_DISMISS_GESTURE_FLOATING_PREVIEW_TEXT = 1;
@@ -195,6 +195,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private int mDelayInMillisecondsToUpdateSuggestions;
private int mDelayInMillisecondsToUpdateShiftState;
+ private int mDelayInMillisecondsToShowCommitIndicator;
public UIHandler(final LatinIME ownerInstance) {
super(ownerInstance);
@@ -206,10 +207,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
return;
}
final Resources res = latinIme.getResources();
- mDelayInMillisecondsToUpdateSuggestions =
- res.getInteger(R.integer.config_delay_in_milliseconds_to_update_suggestions);
- mDelayInMillisecondsToUpdateShiftState =
- res.getInteger(R.integer.config_delay_in_milliseconds_to_update_shift_state);
+ mDelayInMillisecondsToUpdateSuggestions = res.getInteger(
+ R.integer.config_delay_in_milliseconds_to_update_suggestions);
+ mDelayInMillisecondsToUpdateShiftState = res.getInteger(
+ R.integer.config_delay_in_milliseconds_to_update_shift_state);
+ mDelayInMillisecondsToShowCommitIndicator = res.getInteger(
+ R.integer.text_decorator_delay_in_milliseconds_to_show_commit_indicator);
}
@Override
@@ -258,7 +261,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
case MSG_RESET_CACHES:
final SettingsValues settingsValues = latinIme.mSettings.getCurrent();
if (latinIme.mInputLogic.retryResetCachesAndReturnSuccess(
- msg.arg1 == 1 /* tryResumeSuggestions */,
+ msg.arg1 == ARG1_TRUE /* tryResumeSuggestions */,
msg.arg2 /* remainingTries */, this /* handler */)) {
// If we were able to reset the caches, then we can reload the keyboard.
// Otherwise, we'll do it when we can.
@@ -267,6 +270,14 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
latinIme.getCurrentRecapitalizeState());
}
break;
+ case MSG_SHOW_COMMIT_INDICATOR:
+ // Protocol of MSG_SET_COMMIT_INDICATOR_ENABLED:
+ // - what: MSG_SHOW_COMMIT_INDICATOR
+ // - arg1: not used.
+ // - arg2: not used.
+ // - obj: the Runnable object to be called back.
+ ((Runnable) msg.obj).run();
+ break;
case MSG_WAIT_FOR_DICTIONARY_LOAD:
Log.i(TAG, "Timeout waiting for dictionary load");
break;
@@ -367,6 +378,19 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
obtainMessage(MSG_UPDATE_TAIL_BATCH_INPUT_COMPLETED, suggestedWords).sendToTarget();
}
+ /**
+ * Posts a delayed task to show the commit indicator.
+ *
+ * <p>Only one task can exist in the queue. When this method is called, any prior task that
+ * has not yet fired will be canceled.</p>
+ * @param task the runnable object that will be fired when the delayed task is dispatched.
+ */
+ public void postShowCommitIndicatorTask(final Runnable task) {
+ removeMessages(MSG_SHOW_COMMIT_INDICATOR);
+ sendMessageDelayed(obtainMessage(MSG_SHOW_COMMIT_INDICATOR, task),
+ mDelayInMillisecondsToShowCommitIndicator);
+ }
+
// Working variables for the following methods.
private boolean mIsOrientationChanging;
private boolean mPendingSuccessiveImsCallback;
@@ -710,13 +734,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
public void setInputView(final View view) {
super.setInputView(view);
mInputView = view;
- mExtractArea = getWindow().getWindow().getDecorView()
- .findViewById(android.R.id.extractArea);
- mKeyPreviewBackingView = view.findViewById(R.id.key_preview_backing);
mSuggestionStripView = (SuggestionStripView)view.findViewById(R.id.suggestion_strip_view);
if (hasSuggestionStripView()) {
mSuggestionStripView.setListener(this, view);
}
+ mInputLogic.setTextDecoratorUi(new TextDecoratorUi(this, view));
}
@Override
@@ -751,20 +773,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// is not guaranteed. It may even be called at the same time on a different thread.
final RichInputMethodSubtype richSubtype = new RichInputMethodSubtype(subtype);
mSubtypeSwitcher.onSubtypeChanged(richSubtype);
- mInputLogic.onSubtypeChanged(SubtypeLocaleUtils.getCombiningRulesExtraValue(subtype));
+ mInputLogic.onSubtypeChanged(SubtypeLocaleUtils.getCombiningRulesExtraValue(subtype),
+ mSettings.getCurrent());
loadKeyboard();
}
private void onStartInputInternal(final EditorInfo editorInfo, final boolean restarting) {
super.onStartInput(editorInfo, restarting);
- if (ProductionFlags.ENABLE_CURSOR_ANCHOR_INFO_CALLBACK) {
- // AcceptTypedWord feature relies on CursorAnchorInfo.
- if (mSettings.getCurrent().mShouldShowUiToAcceptTypedWord) {
- InputConnectionCompatUtils.requestUpdateCursorAnchorInfo(
- getCurrentInputConnection(), true /* enableMonitor */,
- true /* requestImmediateCallback */);
- }
- }
}
@SuppressWarnings("deprecation")
@@ -833,7 +848,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// span, so we should reset our state unconditionally, even if restarting is true.
// We also tell the input logic about the combining rules for the current subtype, so
// it can adjust its combiners if needed.
- mInputLogic.startInput(mSubtypeSwitcher.getCombiningRulesExtraValueOfCurrentSubtype());
+ mInputLogic.startInput(mSubtypeSwitcher.getCombiningRulesExtraValueOfCurrentSubtype(),
+ currentSettingsValues);
// Note: the following does a round-trip IPC on the main thread: be careful
final Locale currentLocale = mSubtypeSwitcher.getCurrentSubtypeLocale();
@@ -973,9 +989,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// @Override
public void onUpdateCursorAnchorInfo(final CursorAnchorInfo info) {
if (ProductionFlags.ENABLE_CURSOR_ANCHOR_INFO_CALLBACK) {
- final CursorAnchorInfoCompatWrapper wrapper =
- CursorAnchorInfoCompatWrapper.fromObject(info);
- // TODO: Implement here
+ mInputLogic.onUpdateCursorAnchorInfo(CursorAnchorInfoCompatWrapper.fromObject(info));
}
}
@@ -1052,42 +1066,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
applicationSpecifiedCompletions);
final SuggestedWords suggestedWords = new SuggestedWords(applicationSuggestedWords,
null /* rawSuggestions */, false /* typedWordValid */, false /* willAutoCorrect */,
- false /* isObsoleteSuggestions */, false /* isPrediction */,
+ false /* isObsoleteSuggestions */,
SuggestedWords.INPUT_STYLE_APPLICATION_SPECIFIED /* inputStyle */);
// When in fullscreen mode, show completions generated by the application forcibly
setSuggestedWords(suggestedWords);
}
- private int getAdjustedBackingViewHeight() {
- final int currentHeight = mKeyPreviewBackingView.getHeight();
- if (currentHeight > 0) {
- return currentHeight;
- }
-
- final View visibleKeyboardView = mKeyboardSwitcher.getVisibleKeyboardView();
- if (visibleKeyboardView == null) {
- return 0;
- }
- // TODO: !!!!!!!!!!!!!!!!!!!! Handle different backing view heights between the main !!!
- // keyboard and the emoji keyboard. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
- final int keyboardHeight = visibleKeyboardView.getHeight();
- final int suggestionsHeight = mSuggestionStripView.getHeight();
- final int displayHeight = getResources().getDisplayMetrics().heightPixels;
- final Rect rect = new Rect();
- mKeyPreviewBackingView.getWindowVisibleDisplayFrame(rect);
- final int notificationBarHeight = rect.top;
- final int remainingHeight = displayHeight - notificationBarHeight - suggestionsHeight
- - keyboardHeight;
-
- final LayoutParams params = mKeyPreviewBackingView.getLayoutParams();
- mSuggestionStripView.setMoreSuggestionsHeight(remainingHeight);
-
- // Let the backing cover the remaining region entirely.
- params.height = remainingHeight;
- mKeyPreviewBackingView.setLayoutParams(params);
- return params.height;
- }
-
@Override
public void onComputeInsets(final InputMethodService.Insets outInsets) {
super.onComputeInsets(outInsets);
@@ -1095,40 +1079,30 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (visibleKeyboardView == null || !hasSuggestionStripView()) {
return;
}
+ final int inputHeight = mInputView.getHeight();
final boolean hasHardwareKeyboard = mKeyboardSwitcher.hasHardwareKeyboard();
if (hasHardwareKeyboard && visibleKeyboardView.getVisibility() == View.GONE) {
// If there is a hardware keyboard and a visible software keyboard view has been hidden,
// no visual element will be shown on the screen.
- outInsets.touchableInsets = mInputView.getHeight();
- outInsets.visibleTopInsets = mInputView.getHeight();
+ outInsets.touchableInsets = inputHeight;
+ outInsets.visibleTopInsets = inputHeight;
return;
}
- final int adjustedBackingHeight = getAdjustedBackingViewHeight();
- final boolean backingGone = (mKeyPreviewBackingView.getVisibility() == View.GONE);
- final int backingHeight = backingGone ? 0 : adjustedBackingHeight;
- // In fullscreen mode, the height of the extract area managed by InputMethodService should
- // be considered.
- // See {@link android.inputmethodservice.InputMethodService#onComputeInsets}.
- final int extractHeight = isFullscreenMode() ? mExtractArea.getHeight() : 0;
- final int suggestionsHeight = (mSuggestionStripView.getVisibility() == View.GONE) ? 0
- : mSuggestionStripView.getHeight();
- final int extraHeight = extractHeight + backingHeight + suggestionsHeight;
- int visibleTopY = extraHeight;
- // Need to set touchable region only if input view is being shown
+ final int suggestionsHeight = (!mKeyboardSwitcher.isShowingEmojiPalettes()
+ && mSuggestionStripView.getVisibility() == View.VISIBLE)
+ ? mSuggestionStripView.getHeight() : 0;
+ final int visibleTopY = inputHeight - visibleKeyboardView.getHeight() - suggestionsHeight;
+ mSuggestionStripView.setMoreSuggestionsHeight(visibleTopY);
+ // Need to set touchable region only if a keyboard view is being shown.
if (visibleKeyboardView.isShown()) {
- // Note that the height of Emoji layout is the same as the height of the main keyboard
- // and the suggestion strip
- if (mKeyboardSwitcher.isShowingEmojiPalettes()
- || mSuggestionStripView.getVisibility() == View.VISIBLE) {
- visibleTopY -= suggestionsHeight;
- }
- final int touchY = mKeyboardSwitcher.isShowingMoreKeysPanel() ? 0 : visibleTopY;
- final int touchWidth = visibleKeyboardView.getWidth();
- final int touchHeight = visibleKeyboardView.getHeight() + extraHeight
+ final int touchLeft = 0;
+ final int touchTop = mKeyboardSwitcher.isShowingMoreKeysPanel() ? 0 : visibleTopY;
+ final int touchRight = visibleKeyboardView.getWidth();
+ final int touchBottom = inputHeight
// Extend touchable region below the keyboard.
+ EXTENDED_TOUCHABLE_REGION_HEIGHT;
outInsets.touchableInsets = InputMethodService.Insets.TOUCHABLE_INSETS_REGION;
- outInsets.touchableRegion.set(0, touchY, touchWidth, touchHeight);
+ outInsets.touchableRegion.set(touchLeft, touchTop, touchRight, touchBottom);
}
outInsets.contentTopInsets = visibleTopY;
outInsets.visibleTopInsets = visibleTopY;
@@ -1173,12 +1147,28 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
@Override
public void updateFullscreenMode() {
+ // Override layout parameters to expand {@link SoftInputWindow} to the entire screen.
+ // See {@link InputMethodService#setinputView(View) and
+ // {@link SoftInputWindow#updateWidthHeight(WindowManager.LayoutParams)}.
+ final Window window = getWindow().getWindow();
+ ViewLayoutUtils.updateLayoutHeightOf(window, LayoutParams.MATCH_PARENT);
+ // This method may be called before {@link #setInputView(View)}.
+ if (mInputView != null) {
+ // In non-fullscreen mode, {@link InputView} and its parent inputArea should expand to
+ // the entire screen and be placed at the bottom of {@link SoftInputWindow}.
+ // In fullscreen mode, these shouldn't expand to the entire screen and should be
+ // coexistent with {@link #mExtractedArea} above.
+ // See {@link InputMethodService#setInputView(View) and
+ // com.android.internal.R.layout.input_method.xml.
+ final int layoutHeight = isFullscreenMode()
+ ? LayoutParams.WRAP_CONTENT : LayoutParams.MATCH_PARENT;
+ final View inputArea = window.findViewById(android.R.id.inputArea);
+ ViewLayoutUtils.updateLayoutHeightOf(inputArea, layoutHeight);
+ ViewLayoutUtils.updateLayoutGravityOf(inputArea, Gravity.BOTTOM);
+ ViewLayoutUtils.updateLayoutHeightOf(mInputView, layoutHeight);
+ }
super.updateFullscreenMode();
-
- if (mKeyPreviewBackingView == null) return;
- // In fullscreen mode, no need to have extra space to show the key preview.
- // If not, we should have extra space above the keyboard to show the key preview.
- mKeyPreviewBackingView.setVisibility(isFullscreenMode() ? View.GONE : View.VISIBLE);
+ mInputLogic.onUpdateFullscreenMode(isFullscreenMode());
}
private int getCurrentAutoCapsState() {
@@ -1216,6 +1206,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
return;
}
mDictionaryFacilitator.addWordToUserDictionary(this /* context */, word);
+ mInputLogic.onAddWordToUserDictionary();
}
// Callback for the {@link SuggestionStripView}, to call when the important notice strip is
@@ -1297,7 +1288,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// code) needs the coordinates in the keyboard frame.
// TODO: We should reconsider which coordinate system should be used to represent
// keyboard event. Also we should pull this up -- LatinIME has no business doing
- // this transformation, it should be done already before calling onCodeInput.
+ // this transformation, it should be done already before calling onEvent.
final int keyX = mainKeyboardView.getKeyX(x);
final int keyY = mainKeyboardView.getKeyY(y);
final Event event = createSoftwareKeypressEvent(getCodePointForKeyboard(codePoint),
@@ -1308,7 +1299,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// This method is public for testability of LatinIME, but also in the future it should
// completely replace #onCodeInput.
public void onEvent(final Event event) {
- if (Constants.CODE_SHORTCUT == event.mCodePoint) {
+ if (Constants.CODE_SHORTCUT == event.mKeyCode) {
mSubtypeSwitcher.switchToShortcutIME(this);
}
final InputTransaction completeInputTransaction =
@@ -1316,8 +1307,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mKeyboardSwitcher.getKeyboardShiftMode(),
mKeyboardSwitcher.getCurrentKeyboardScriptId(), mHandler);
updateStateAfterInputTransaction(completeInputTransaction);
- mKeyboardSwitcher.onCodeInput(event.mCodePoint, getCurrentAutoCapsState(),
- getCurrentRecapitalizeState());
+ mKeyboardSwitcher.onEvent(event, getCurrentAutoCapsState(), getCurrentRecapitalizeState());
}
// A helper method to split the code point and the key code. Ultimately, they should not be
@@ -1341,13 +1331,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
@Override
public void onTextInput(final String rawText) {
// TODO: have the keyboard pass the correct key code when we need it.
- final Event event = Event.createSoftwareTextEvent(rawText, Event.NOT_A_KEY_CODE);
+ final Event event = Event.createSoftwareTextEvent(rawText, Constants.CODE_OUTPUT_TEXT);
final InputTransaction completeInputTransaction =
mInputLogic.onTextInput(mSettings.getCurrent(), event,
mKeyboardSwitcher.getKeyboardShiftMode(), mHandler);
updateStateAfterInputTransaction(completeInputTransaction);
- mKeyboardSwitcher.onCodeInput(Constants.CODE_OUTPUT_TEXT, getCurrentAutoCapsState(),
- getCurrentRecapitalizeState());
+ mKeyboardSwitcher.onEvent(event, getCurrentAutoCapsState(), getCurrentRecapitalizeState());
}
@Override
@@ -1414,7 +1403,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
private void setSuggestedWords(final SuggestedWords suggestedWords) {
- mInputLogic.setSuggestedWords(suggestedWords);
+ final SettingsValues currentSettingsValues = mSettings.getCurrent();
+ mInputLogic.setSuggestedWords(suggestedWords, currentSettingsValues, mHandler);
// TODO: Modify this when we support suggestions with hard keyboard
if (!hasSuggestionStripView()) {
return;
@@ -1423,7 +1413,6 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
return;
}
- final SettingsValues currentSettingsValues = mSettings.getCurrent();
final boolean shouldShowImportantNotice =
ImportantNoticeUtils.shouldShowImportantNotice(this);
final boolean shouldShowSuggestionCandidates =
diff --git a/java/src/com/android/inputmethod/latin/PunctuationSuggestions.java b/java/src/com/android/inputmethod/latin/PunctuationSuggestions.java
index 6b0205c0f..56014cbad 100644
--- a/java/src/com/android/inputmethod/latin/PunctuationSuggestions.java
+++ b/java/src/com/android/inputmethod/latin/PunctuationSuggestions.java
@@ -35,7 +35,6 @@ public final class PunctuationSuggestions extends SuggestedWords {
false /* typedWordValid */,
false /* hasAutoCorrectionCandidate */,
false /* isObsoleteSuggestions */,
- false /* isPrediction */,
INPUT_STYLE_NONE /* inputStyle */);
}
diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java
index 035557610..497823aeb 100644
--- a/java/src/com/android/inputmethod/latin/RichInputConnection.java
+++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java
@@ -30,7 +30,9 @@ import android.view.inputmethod.CorrectionInfo;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputMethodManager;
+import com.android.inputmethod.compat.InputConnectionCompatUtils;
import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
import com.android.inputmethod.latin.utils.CapsModeUtils;
import com.android.inputmethod.latin.utils.DebugLogUtils;
@@ -906,4 +908,33 @@ public final class RichInputConnection {
mIC.setSelection(mExpectedSelStart, mExpectedSelEnd);
}
}
+
+ private boolean mCursorAnchorInfoMonitorEnabled = false;
+
+ /**
+ * Requests the editor to call back {@link InputMethodManager#updateCursorAnchorInfo}.
+ * @param enableMonitor {@code true} to request the editor to call back the method whenever the
+ * cursor/anchor position is changed.
+ * @param requestImmediateCallback {@code true} to request the editor to call back the method
+ * as soon as possible to notify the current cursor/anchor position to the input method.
+ * @return {@code true} if the request is accepted. Returns {@code false} otherwise, which
+ * includes "not implemented" or "rejected" or "temporarily unavailable" or whatever which
+ * prevents the application from fulfilling the request. (TODO: Improve the API when it turns
+ * out that we actually need more detailed error codes)
+ */
+ public boolean requestUpdateCursorAnchorInfo(final boolean enableMonitor,
+ final boolean requestImmediateCallback) {
+ final boolean scheduled = InputConnectionCompatUtils.requestUpdateCursorAnchorInfo(mIC,
+ enableMonitor, requestImmediateCallback);
+ mCursorAnchorInfoMonitorEnabled = (scheduled && enableMonitor);
+ return scheduled;
+ }
+
+ /**
+ * @return {@code true} if the application reported that the monitor mode of
+ * {@link InputMethodService#onUpdateCursorAnchorInfo(CursorAnchorInfo)} is currently enabled.
+ */
+ public boolean isCursorAnchorInfoMonitorEnabled() {
+ return mCursorAnchorInfoMonitorEnabled;
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index ab852f8dd..6779351fd 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -120,9 +120,9 @@ public final class Suggest {
// and calls the callback function with the suggestions.
private void getSuggestedWordsForNonBatchInput(final WordComposer wordComposer,
final PrevWordsInfo prevWordsInfo, final ProximityInfo proximityInfo,
- final SettingsValuesForSuggestion settingsValuesForSuggestion, final int inputStyle,
- final boolean isCorrectionEnabled, final int sequenceNumber,
- final OnGetSuggestedWordsCallback callback) {
+ final SettingsValuesForSuggestion settingsValuesForSuggestion,
+ final int inputStyleIfNotPrediction, final boolean isCorrectionEnabled,
+ final int sequenceNumber, final OnGetSuggestedWordsCallback callback) {
final String typedWord = wordComposer.getTypedWord();
final int trailingSingleQuotesCount = StringUtils.getTrailingSingleQuotesCount(typedWord);
final String consideredWord = trailingSingleQuotesCount > 0
@@ -186,6 +186,8 @@ public final class Suggest {
suggestionsList = suggestionsContainer;
}
+ final int inputStyle = resultsArePredictions ? SuggestedWords.INPUT_STYLE_PREDICTION :
+ inputStyleIfNotPrediction;
callback.onGetSuggestedWords(new SuggestedWords(suggestionsList,
suggestionResults.mRawSuggestions,
// TODO: this first argument is lying. If this is a whitelisted word which is an
@@ -193,8 +195,7 @@ public final class Suggest {
// rename the attribute or change the value.
!resultsArePredictions && !allowsToBeAutoCorrected /* typedWordValid */,
hasAutoCorrection /* willAutoCorrect */,
- false /* isObsoleteSuggestions */, resultsArePredictions,
- inputStyle, sequenceNumber));
+ false /* isObsoleteSuggestions */, inputStyle, sequenceNumber));
}
// Retrieves suggestions for the batch input
@@ -244,7 +245,6 @@ public final class Suggest {
true /* typedWordValid */,
false /* willAutoCorrect */,
false /* isObsoleteSuggestions */,
- false /* isPrediction */,
inputStyle, sequenceNumber));
}
diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java
index 38fcb683d..1eebabece 100644
--- a/java/src/com/android/inputmethod/latin/SuggestedWords.java
+++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java
@@ -38,14 +38,15 @@ public class SuggestedWords {
public static final int INPUT_STYLE_TAIL_BATCH = 3;
public static final int INPUT_STYLE_APPLICATION_SPECIFIED = 4;
public static final int INPUT_STYLE_RECORRECTION = 5;
+ public static final int INPUT_STYLE_PREDICTION = 6;
// The maximum number of suggestions available.
public static final int MAX_SUGGESTIONS = 18;
private static final ArrayList<SuggestedWordInfo> EMPTY_WORD_INFO_LIST = new ArrayList<>(0);
public static final SuggestedWords EMPTY = new SuggestedWords(
- EMPTY_WORD_INFO_LIST, null /* rawSuggestions */, false, false, false, false,
- INPUT_STYLE_NONE);
+ EMPTY_WORD_INFO_LIST, null /* rawSuggestions */, false /* typedWordValid */,
+ false /* willAutoCorrect */, false /* isObsoleteSuggestions */, INPUT_STYLE_NONE);
public final String mTypedWord;
public final boolean mTypedWordValid;
@@ -54,7 +55,6 @@ public class SuggestedWords {
// whether this exactly matches the user entry or not.
public final boolean mWillAutoCorrect;
public final boolean mIsObsoleteSuggestions;
- public final boolean mIsPrediction;
// How the input for these suggested words was done by the user. Must be one of the
// INPUT_STYLE_* constants above.
public final int mInputStyle;
@@ -67,10 +67,9 @@ public class SuggestedWords {
final boolean typedWordValid,
final boolean willAutoCorrect,
final boolean isObsoleteSuggestions,
- final boolean isPrediction,
final int inputStyle) {
this(suggestedWordInfoList, rawSuggestions, typedWordValid, willAutoCorrect,
- isObsoleteSuggestions, isPrediction, inputStyle, NOT_A_SEQUENCE_NUMBER);
+ isObsoleteSuggestions, inputStyle, NOT_A_SEQUENCE_NUMBER);
}
public SuggestedWords(final ArrayList<SuggestedWordInfo> suggestedWordInfoList,
@@ -78,13 +77,12 @@ public class SuggestedWords {
final boolean typedWordValid,
final boolean willAutoCorrect,
final boolean isObsoleteSuggestions,
- final boolean isPrediction,
final int inputStyle,
final int sequenceNumber) {
this(suggestedWordInfoList, rawSuggestions,
- (suggestedWordInfoList.isEmpty() || isPrediction) ? null
+ (suggestedWordInfoList.isEmpty() || INPUT_STYLE_PREDICTION == inputStyle) ? null
: suggestedWordInfoList.get(INDEX_OF_TYPED_WORD).mWord,
- typedWordValid, willAutoCorrect, isObsoleteSuggestions, isPrediction, inputStyle,
+ typedWordValid, willAutoCorrect, isObsoleteSuggestions, inputStyle,
sequenceNumber);
}
@@ -94,7 +92,6 @@ public class SuggestedWords {
final boolean typedWordValid,
final boolean willAutoCorrect,
final boolean isObsoleteSuggestions,
- final boolean isPrediction,
final int inputStyle,
final int sequenceNumber) {
mSuggestedWordInfoList = suggestedWordInfoList;
@@ -102,7 +99,6 @@ public class SuggestedWords {
mTypedWordValid = typedWordValid;
mWillAutoCorrect = willAutoCorrect;
mIsObsoleteSuggestions = isObsoleteSuggestions;
- mIsPrediction = isPrediction;
mInputStyle = inputStyle;
mSequenceNumber = sequenceNumber;
mTypedWord = typedWord;
@@ -381,9 +377,14 @@ public class SuggestedWords {
}
}
+ public boolean isPrediction() {
+ return INPUT_STYLE_PREDICTION == mInputStyle;
+ }
+
// SuggestedWords is an immutable object, as much as possible. We must not just remove
// words from the member ArrayList as some other parties may expect the object to never change.
- public SuggestedWords getSuggestedWordsExcludingTypedWord(final int inputStyle) {
+ // This is only ever called by recorrection at the moment, hence the ForRecorrection moniker.
+ public SuggestedWords getSuggestedWordsExcludingTypedWordForRecorrection() {
final ArrayList<SuggestedWordInfo> newSuggestions = new ArrayList<>();
String typedWord = null;
for (int i = 0; i < mSuggestedWordInfoList.size(); ++i) {
@@ -399,7 +400,7 @@ public class SuggestedWords {
// no auto-correction should take place hence willAutoCorrect = false.
return new SuggestedWords(newSuggestions, null /* rawSuggestions */, typedWord,
true /* typedWordValid */, false /* willAutoCorrect */, mIsObsoleteSuggestions,
- mIsPrediction, inputStyle, NOT_A_SEQUENCE_NUMBER);
+ SuggestedWords.INPUT_STYLE_RECORRECTION, NOT_A_SEQUENCE_NUMBER);
}
// Creates a new SuggestedWordInfo from the currently suggested words that removes all but the
@@ -418,8 +419,7 @@ public class SuggestedWords {
SuggestedWordInfo.NOT_A_CONFIDENCE));
}
return new SuggestedWords(newSuggestions, null /* rawSuggestions */, mTypedWordValid,
- mWillAutoCorrect, mIsObsoleteSuggestions, mIsPrediction,
- INPUT_STYLE_TAIL_BATCH);
+ mWillAutoCorrect, mIsObsoleteSuggestions, INPUT_STYLE_TAIL_BATCH);
}
/**
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
index 616828efe..4b5edb076 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
@@ -17,9 +17,12 @@
package com.android.inputmethod.latin.inputlogic;
import android.graphics.Color;
+import android.inputmethodservice.InputMethodService;
import android.os.SystemClock;
import android.text.SpannableString;
+import android.text.Spanned;
import android.text.TextUtils;
+import android.text.style.BackgroundColorSpan;
import android.text.style.SuggestionSpan;
import android.util.Log;
import android.view.KeyCharacterMap;
@@ -27,11 +30,14 @@ import android.view.KeyEvent;
import android.view.inputmethod.CorrectionInfo;
import android.view.inputmethod.EditorInfo;
+import com.android.inputmethod.compat.CursorAnchorInfoCompatWrapper;
import com.android.inputmethod.compat.SuggestionSpanUtils;
import com.android.inputmethod.event.Event;
import com.android.inputmethod.event.InputTransaction;
import com.android.inputmethod.keyboard.KeyboardSwitcher;
import com.android.inputmethod.keyboard.ProximityInfo;
+import com.android.inputmethod.keyboard.TextDecorator;
+import com.android.inputmethod.keyboard.TextDecoratorUiOperator;
import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.Dictionary;
import com.android.inputmethod.latin.DictionaryFacilitator;
@@ -46,6 +52,7 @@ import com.android.inputmethod.latin.SuggestedWords;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.WordComposer;
import com.android.inputmethod.latin.define.DebugFlags;
+import com.android.inputmethod.latin.define.ProductionFlags;
import com.android.inputmethod.latin.settings.SettingsValues;
import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion;
import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
@@ -81,6 +88,18 @@ public final class InputLogic {
public final Suggest mSuggest;
private final DictionaryFacilitator mDictionaryFacilitator;
+ private final TextDecorator mTextDecorator = new TextDecorator(new TextDecorator.Listener() {
+ @Override
+ public void onClickComposingTextToCommit(SuggestedWordInfo wordInfo) {
+ mLatinIME.pickSuggestionManually(wordInfo);
+ }
+ @Override
+ public void onClickComposingTextToAddToDictionary(SuggestedWordInfo wordInfo) {
+ mLatinIME.addWordToUserDictionary(wordInfo.mWord);
+ mLatinIME.dismissAddToDictionaryHint();
+ }
+ });
+
public LastComposedWord mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
// This has package visibility so it can be accessed from InputLogicHandler.
/* package */ final WordComposer mWordComposer;
@@ -124,8 +143,9 @@ public final class InputLogic {
* Call this when input starts or restarts in some editor (typically, in onStartInputView).
*
* @param combiningSpec the combining spec string for this subtype
+ * @param settingsValues the current settings values
*/
- public void startInput(final String combiningSpec) {
+ public void startInput(final String combiningSpec, final SettingsValues settingsValues) {
mEnteredText = null;
mWordComposer.restartCombining(combiningSpec);
resetComposingState(true /* alsoResetLastComposedWord */);
@@ -143,15 +163,24 @@ public final class InputLogic {
} else {
mInputLogicHandler.reset();
}
+
+ if (ProductionFlags.ENABLE_CURSOR_ANCHOR_INFO_CALLBACK) {
+ // AcceptTypedWord feature relies on CursorAnchorInfo.
+ if (settingsValues.mShouldShowUiToAcceptTypedWord) {
+ mConnection.requestUpdateCursorAnchorInfo(true /* enableMonitor */,
+ true /* requestImmediateCallback */);
+ }
+ }
}
/**
* Call this when the subtype changes.
* @param combiningSpec the spec string for the combining rules
+ * @param settingsValues the current settings values
*/
- public void onSubtypeChanged(final String combiningSpec) {
+ public void onSubtypeChanged(final String combiningSpec, final SettingsValues settingsValues) {
finishInput();
- startInput(combiningSpec);
+ startInput(combiningSpec, settingsValues);
}
/**
@@ -303,8 +332,18 @@ public final class InputLogic {
return inputTransaction;
}
- commitChosenWord(settingsValues, suggestion,
- LastComposedWord.COMMIT_TYPE_MANUAL_PICK, LastComposedWord.NOT_A_SEPARATOR);
+ final boolean shouldShowAddToDictionaryHint = shouldShowAddToDictionaryHint(suggestionInfo);
+ final boolean shouldShowAddToDictionaryIndicator =
+ shouldShowAddToDictionaryHint && settingsValues.mShouldShowUiToAcceptTypedWord;
+ final int backgroundColor;
+ if (shouldShowAddToDictionaryIndicator) {
+ backgroundColor = settingsValues.mTextHighlightColorForAddToDictionaryIndicator;
+ } else {
+ backgroundColor = Color.TRANSPARENT;
+ }
+ commitChosenWordWithBackgroundColor(settingsValues, suggestion,
+ LastComposedWord.COMMIT_TYPE_MANUAL_PICK, LastComposedWord.NOT_A_SEPARATOR,
+ backgroundColor);
mConnection.endBatchEdit();
// Don't allow cancellation of manual pick
mLastComposedWord.deactivate();
@@ -312,13 +351,16 @@ public final class InputLogic {
mSpaceState = SpaceState.PHANTOM;
inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW);
- if (shouldShowAddToDictionaryHint(suggestionInfo)) {
+ if (shouldShowAddToDictionaryHint) {
mSuggestionStripViewAccessor.showAddToDictionaryHint(suggestion);
} else {
// If we're not showing the "Touch again to save", then update the suggestion strip.
// That's going to be predictions (or punctuation suggestions), so INPUT_STYLE_NONE.
handler.postUpdateSuggestionStrip(SuggestedWords.INPUT_STYLE_NONE);
}
+ if (shouldShowAddToDictionaryIndicator) {
+ mTextDecorator.showAddToDictionaryIndicator(suggestionInfo);
+ }
return inputTransaction;
}
@@ -386,6 +428,8 @@ public final class InputLogic {
// The cursor has been moved : we now accept to perform recapitalization
mRecapitalizeStatus.enable();
+ // We moved the cursor and need to invalidate the indicator right now.
+ mTextDecorator.reset();
// We moved the cursor. If we are touching a word, we need to resume suggestion.
mLatinIME.mHandler.postResumeSuggestions(false /* shouldIncludeResumedWordInSuggestions */,
true /* shouldDelay */);
@@ -561,7 +605,8 @@ public final class InputLogic {
// TODO: on the long term, this method should become private, but it will be difficult.
// Especially, how do we deal with InputMethodService.onDisplayCompletions?
- public void setSuggestedWords(final SuggestedWords suggestedWords) {
+ public void setSuggestedWords(final SuggestedWords suggestedWords,
+ final SettingsValues settingsValues, final LatinIME.UIHandler handler) {
if (SuggestedWords.EMPTY != suggestedWords) {
final String autoCorrection;
if (suggestedWords.mWillAutoCorrect) {
@@ -575,6 +620,43 @@ public final class InputLogic {
}
mSuggestedWords = suggestedWords;
final boolean newAutoCorrectionIndicator = suggestedWords.mWillAutoCorrect;
+ if (shouldShowCommitIndicator(suggestedWords, settingsValues)) {
+ // typedWordInfo is never null here.
+ final int textBackgroundColor = settingsValues.mTextHighlightColorForCommitIndicator;
+ final SuggestedWordInfo typedWordInfo = suggestedWords.getTypedWordInfoOrNull();
+ handler.postShowCommitIndicatorTask(new Runnable() {
+ @Override
+ public void run() {
+ // TODO: This needs to be refactored to ensure that mWordComposer is accessed
+ // only from the UI thread.
+ if (!mWordComposer.isComposingWord()) {
+ mTextDecorator.reset();
+ return;
+ }
+ final SuggestedWordInfo currentTypedWordInfo =
+ mSuggestedWords.getTypedWordInfoOrNull();
+ if (currentTypedWordInfo == null) {
+ mTextDecorator.reset();
+ return;
+ }
+ if (!currentTypedWordInfo.equals(typedWordInfo)) {
+ // Suggested word has been changed. This task is obsolete.
+ mTextDecorator.reset();
+ return;
+ }
+ // TODO: As with the above TODO comment, this operation must be performed only
+ // on the UI thread too. Needs to be refactored.
+ setComposingTextInternalWithBackgroundColor(typedWordInfo.mWord,
+ 1 /* newCursorPosition */, textBackgroundColor);
+ mTextDecorator.showCommitIndicator(typedWordInfo);
+ }
+ });
+ } else {
+ // Note: It is OK to not cancel previous postShowCommitIndicatorTask() here. Having a
+ // cancellation mechanism could improve performance a bit though.
+ mTextDecorator.reset();
+ }
+
// Put a blue underline to a word in TextView which will be auto-corrected.
if (mIsAutoCorrectionIndicatorOn != newAutoCorrectionIndicator
&& mWordComposer.isComposingWord()) {
@@ -585,7 +667,7 @@ public final class InputLogic {
// message, this is called outside any batch edit. Potentially, this may result in some
// janky flickering of the screen, although the display speed makes it unlikely in
// the practice.
- mConnection.setComposingText(textWithUnderline, 1);
+ setComposingTextInternal(textWithUnderline, 1);
}
}
@@ -608,7 +690,7 @@ public final class InputLogic {
inputTransaction.setDidAffectContents();
}
if (mWordComposer.isComposingWord()) {
- mConnection.setComposingText(mWordComposer.getTypedWord(), 1);
+ setComposingTextInternal(mWordComposer.getTypedWord(), 1);
inputTransaction.setDidAffectContents();
inputTransaction.setRequiresUpdateSuggestions();
}
@@ -638,13 +720,13 @@ public final class InputLogic {
case Constants.CODE_SHIFT:
performRecapitalization(inputTransaction.mSettingsValues);
inputTransaction.requireShiftUpdate(InputTransaction.SHIFT_UPDATE_NOW);
- if (mSuggestedWords.mIsPrediction) {
+ if (mSuggestedWords.isPrediction()) {
inputTransaction.setRequiresUpdateSuggestions();
}
break;
case Constants.CODE_CAPSLOCK:
// Note: Changing keyboard to shift lock state is handled in
- // {@link KeyboardSwitcher#onCodeInput(int)}.
+ // {@link KeyboardSwitcher#onEvent(Event)}.
break;
case Constants.CODE_SYMBOL_SHIFT:
// Note: Calling back to the keyboard on the symbol Shift key is handled in
@@ -672,11 +754,11 @@ public final class InputLogic {
break;
case Constants.CODE_EMOJI:
// Note: Switching emoji keyboard is being handled in
- // {@link KeyboardState#onCodeInput(int,int)}.
+ // {@link KeyboardState#onEvent(Event,int)}.
break;
case Constants.CODE_ALPHA_FROM_EMOJI:
// Note: Switching back from Emoji keyboard to the main keyboard is being
- // handled in {@link KeyboardState#onCodeInput(int,int)}.
+ // handled in {@link KeyboardState#onEvent(Event,int)}.
break;
case Constants.CODE_SHIFT_ENTER:
// TODO: remove this object
@@ -756,6 +838,8 @@ public final class InputLogic {
if (!mWordComposer.isComposingWord() &&
mSuggestionStripViewAccessor.isShowingAddToDictionaryHint()) {
mSuggestionStripViewAccessor.dismissAddToDictionaryHint();
+ mConnection.removeBackgroundColorFromHighlightedTextIfNecessary();
+ mTextDecorator.reset();
}
final int codePoint = event.mCodePoint;
@@ -842,8 +926,7 @@ public final class InputLogic {
if (mWordComposer.isSingleLetter()) {
mWordComposer.setCapitalizedModeAtStartComposingTime(inputTransaction.mShiftState);
}
- mConnection.setComposingText(getTextWithUnderline(
- mWordComposer.getTypedWord()), 1);
+ setComposingTextInternal(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
} else {
final boolean swapWeakSpace = tryStripSpaceAndReturnWhetherShouldSwapInstead(event,
inputTransaction);
@@ -1006,7 +1089,7 @@ public final class InputLogic {
mWordComposer.applyProcessedEvent(event);
}
if (mWordComposer.isComposingWord()) {
- mConnection.setComposingText(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
+ setComposingTextInternal(getTextWithUnderline(mWordComposer.getTypedWord()), 1);
} else {
mConnection.commitText("", 1);
}
@@ -1466,11 +1549,10 @@ public final class InputLogic {
&& !shouldIncludeResumedWordInSuggestions) {
// We were able to compute new suggestions for this word.
// Remove the typed word, since we don't want to display it in this
- // case. The #getSuggestedWordsExcludingTypedWord() method sets
- // willAutoCorrect to false.
+ // case. The #getSuggestedWordsExcludingTypedWordForRecorrection()
+ // method sets willAutoCorrect to false.
suggestedWords = suggestedWordsIncludingTypedWord
- .getSuggestedWordsExcludingTypedWord(SuggestedWords
- .INPUT_STYLE_RECORRECTION);
+ .getSuggestedWordsExcludingTypedWordForRecorrection();
} else {
// No saved suggestions, and we were unable to compute any good one
// either. Rather than displaying an empty suggestion strip, we'll
@@ -1487,11 +1569,9 @@ public final class InputLogic {
// color of the word in the suggestion strip changes according to this parameter,
// and false gives the correct color.
final SuggestedWords suggestedWords = new SuggestedWords(suggestions,
- null /* rawSuggestions */, typedWord,
- false /* typedWordValid */, false /* willAutoCorrect */,
- false /* isObsoleteSuggestions */, false /* isPrediction */,
- SuggestedWords.INPUT_STYLE_RECORRECTION,
- SuggestedWords.NOT_A_SEQUENCE_NUMBER);
+ null /* rawSuggestions */, typedWord, false /* typedWordValid */,
+ false /* willAutoCorrect */, false /* isObsoleteSuggestions */,
+ SuggestedWords.INPUT_STYLE_RECORRECTION, SuggestedWords.NOT_A_SEQUENCE_NUMBER);
mIsAutoCorrectionIndicatorOn = false;
mLatinIME.mHandler.showSuggestionStrip(suggestedWords);
}
@@ -1577,7 +1657,7 @@ public final class InputLogic {
final int[] codePoints = StringUtils.toCodePointArray(stringToCommit);
mWordComposer.setComposingWord(codePoints,
mLatinIME.getCoordinatesForCurrentKeyboard(codePoints));
- mConnection.setComposingText(textToCommit, 1);
+ setComposingTextInternal(textToCommit, 1);
}
// Don't restart suggestion yet. We'll restart if the user deletes the separator.
mLastComposedWord = LastComposedWord.NOT_A_COMPOSED_WORD;
@@ -1787,8 +1867,7 @@ public final class InputLogic {
SuggestedWords.getTypedWordAndPreviousSuggestions(typedWord, oldSuggestedWords);
return new SuggestedWords(typedWordAndPreviousSuggestions, null /* rawSuggestions */,
false /* typedWordValid */, false /* hasAutoCorrectionCandidate */,
- true /* isObsoleteSuggestions */, false /* isPrediction */,
- oldSuggestedWords.mInputStyle);
+ true /* isObsoleteSuggestions */, oldSuggestedWords.mInputStyle);
}
/**
@@ -1911,10 +1990,10 @@ public final class InputLogic {
}
final String lastWord = batchInputText.substring(indexOfLastSpace);
mWordComposer.setBatchInputWord(lastWord);
- mConnection.setComposingText(lastWord, 1);
+ setComposingTextInternal(lastWord, 1);
} else {
mWordComposer.setBatchInputWord(batchInputText);
- mConnection.setComposingText(batchInputText, 1);
+ setComposingTextInternal(batchInputText, 1);
}
mConnection.endBatchEdit();
// Space state must be updated before calling updateShiftState
@@ -2112,4 +2191,126 @@ public final class InputLogic {
settingsValues.mAutoCorrectionEnabledPerUserSettings,
inputStyle, sequenceNumber, callback);
}
+
+ /**
+ * Used as an injection point for each call of
+ * {@link RichInputConnection#setComposingText(CharSequence, int)}.
+ *
+ * <p>Currently using this method is optional and you can still directly call
+ * {@link RichInputConnection#setComposingText(CharSequence, int)}, but it is recommended to
+ * use this method whenever possible to optimize the behavior of {@link TextDecorator}.<p>
+ * <p>TODO: Should we move this mechanism to {@link RichInputConnection}?</p>
+ *
+ * @param newComposingText the composing text to be set
+ * @param newCursorPosition the new cursor position
+ */
+ private void setComposingTextInternal(final CharSequence newComposingText,
+ final int newCursorPosition) {
+ setComposingTextInternalWithBackgroundColor(newComposingText, newCursorPosition,
+ Color.TRANSPARENT);
+ }
+
+ /**
+ * Equivalent to {@link #setComposingTextInternal(CharSequence, int)} except that this method
+ * allows to set {@link BackgroundColorSpan} to the composing text with the given color.
+ *
+ * <p>TODO: Currently the background color is exclusive with the black underline, which is
+ * automatically added by the framework. We need to change the framework if we need to have both
+ * of them at the same time.</p>
+ * <p>TODO: Should we move this method to {@link RichInputConnection}?</p>
+ *
+ * @param newComposingText the composing text to be set
+ * @param newCursorPosition the new cursor position
+ * @param backgroundColor the background color to be set to the composing text. Set
+ * {@link Color#TRANSPARENT} to disable the background color.
+ */
+ private void setComposingTextInternalWithBackgroundColor(final CharSequence newComposingText,
+ final int newCursorPosition, final int backgroundColor) {
+ final CharSequence composingTextToBeSet;
+ if (backgroundColor == Color.TRANSPARENT) {
+ composingTextToBeSet = newComposingText;
+ } else {
+ final SpannableString spannable = new SpannableString(newComposingText);
+ final BackgroundColorSpan backgroundColorSpan =
+ new BackgroundColorSpan(backgroundColor);
+ spannable.setSpan(backgroundColorSpan, 0, spannable.length(),
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_COMPOSING);
+ composingTextToBeSet = spannable;
+ }
+ mConnection.setComposingText(composingTextToBeSet, newCursorPosition);
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////
+ // Following methods are tentatively placed in this class for the integration with
+ // TextDecorator.
+ // TODO: Decouple things that are not related to the input logic.
+ //////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Sets the UI operator for {@link TextDecorator}.
+ * @param uiOperator the UI operator which should be associated with {@link TextDecorator}.
+ */
+ public void setTextDecoratorUi(final TextDecoratorUiOperator uiOperator) {
+ mTextDecorator.setUiOperator(uiOperator);
+ }
+
+ /**
+ * Must be called from {@link InputMethodService#onUpdateCursorAnchorInfo} is called.
+ * @param info The wrapper object with which we can access cursor/anchor info.
+ */
+ public void onUpdateCursorAnchorInfo(final CursorAnchorInfoCompatWrapper info) {
+ mTextDecorator.onUpdateCursorAnchorInfo(info);
+ }
+
+ /**
+ * Must be called when {@link InputMethodService#updateFullscreenMode} is called.
+ * @param isFullscreen {@code true} if the input method is in full-screen mode.
+ */
+ public void onUpdateFullscreenMode(final boolean isFullscreen) {
+ mTextDecorator.notifyFullScreenMode(isFullscreen);
+ }
+
+ /**
+ * Must be called from {@link LatinIME#addWordToUserDictionary(String)}.
+ */
+ public void onAddWordToUserDictionary() {
+ mConnection.removeBackgroundColorFromHighlightedTextIfNecessary();
+ mTextDecorator.reset();
+ }
+
+ /**
+ * Returns whether the commit indicator should be shown or not.
+ * @param suggestedWords the suggested word that is being displayed.
+ * @param settingsValues the current settings value.
+ * @return {@code true} if the commit indicator should be shown.
+ */
+ private boolean shouldShowCommitIndicator(final SuggestedWords suggestedWords,
+ final SettingsValues settingsValues) {
+ if (!mConnection.isCursorAnchorInfoMonitorEnabled()) {
+ // We cannot help in this case because we are heavily relying on this new API.
+ return false;
+ }
+ if (!settingsValues.mShouldShowUiToAcceptTypedWord) {
+ return false;
+ }
+ final SuggestedWordInfo typedWordInfo = suggestedWords.getTypedWordInfoOrNull();
+ if (typedWordInfo == null) {
+ return false;
+ }
+ if (suggestedWords.mInputStyle != SuggestedWords.INPUT_STYLE_TYPING){
+ return false;
+ }
+ if (settingsValues.mShowCommitIndicatorOnlyForAutoCorrection
+ && !suggestedWords.mWillAutoCorrect) {
+ return false;
+ }
+ // TODO: Calling shouldShowAddToDictionaryHint(typedWordInfo) multiple times should be fine
+ // in terms of performance, but we can do better. One idea is to make SuggestedWords include
+ // a boolean that tells whether the word is a dictionary word or not.
+ if (settingsValues.mShowCommitIndicatorOnlyForOutOfVocabulary
+ && !shouldShowAddToDictionaryHint(typedWordInfo)) {
+ return false;
+ }
+ return true;
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
index a9789d888..91a2b6776 100644
--- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
@@ -96,6 +96,12 @@ public class SettingsValues {
public final int[] mAdditionalFeaturesSettingValues =
new int[AdditionalFeaturesSettingUtils.ADDITIONAL_FEATURES_SETTINGS_SIZE];
+ // TextDecorator
+ public final int mTextHighlightColorForCommitIndicator;
+ public final int mTextHighlightColorForAddToDictionaryIndicator;
+ public final boolean mShowCommitIndicatorOnlyForAutoCorrection;
+ public final boolean mShowCommitIndicatorOnlyForOutOfVocabulary;
+
// Debug settings
public final boolean mIsInternal;
public final int mKeyPreviewShowUpDuration;
@@ -164,6 +170,14 @@ public class SettingsValues {
mSuggestionsEnabledPerUserSettings = readSuggestionsEnabled(prefs);
AdditionalFeaturesSettingUtils.readAdditionalFeaturesPreferencesIntoArray(
prefs, mAdditionalFeaturesSettingValues);
+ mShowCommitIndicatorOnlyForAutoCorrection = res.getBoolean(
+ R.bool.text_decorator_only_for_auto_correction);
+ mShowCommitIndicatorOnlyForOutOfVocabulary = res.getBoolean(
+ R.bool.text_decorator_only_for_out_of_vocabulary);
+ mTextHighlightColorForCommitIndicator = res.getColor(
+ R.color.text_decorator_commit_indicator_text_highlight_color);
+ mTextHighlightColorForAddToDictionaryIndicator = res.getColor(
+ R.color.text_decorator_add_to_dictionary_indicator_text_highlight_color);
mIsInternal = Settings.isInternal(prefs);
mKeyPreviewShowUpDuration = Settings.readKeyPreviewAnimationDuration(
prefs, DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_DURATION,
@@ -401,6 +415,14 @@ public class SettingsValues {
sb.append("" + (null == awu ? "null" : awu.toString()));
sb.append("\n mAdditionalFeaturesSettingValues = ");
sb.append("" + Arrays.toString(mAdditionalFeaturesSettingValues));
+ sb.append("\n mShowCommitIndicatorOnlyForAutoCorrection = ");
+ sb.append("" + mShowCommitIndicatorOnlyForAutoCorrection);
+ sb.append("\n mShowCommitIndicatorOnlyForOutOfVocabulary = ");
+ sb.append("" + mShowCommitIndicatorOnlyForOutOfVocabulary);
+ sb.append("\n mTextHighlightColorForCommitIndicator = ");
+ sb.append("" + mTextHighlightColorForCommitIndicator);
+ sb.append("\n mTextHighlightColorForAddToDictionaryIndicator = ");
+ sb.append("" + mTextHighlightColorForAddToDictionaryIndicator);
sb.append("\n mIsInternal = ");
sb.append("" + mIsInternal);
sb.append("\n mKeyPreviewShowUpDuration = ");
diff --git a/java/src/com/android/inputmethod/latin/utils/ViewLayoutUtils.java b/java/src/com/android/inputmethod/latin/utils/ViewLayoutUtils.java
index f9d853493..dd122b634 100644
--- a/java/src/com/android/inputmethod/latin/utils/ViewLayoutUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/ViewLayoutUtils.java
@@ -19,7 +19,10 @@ package com.android.inputmethod.latin.utils;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.MarginLayoutParams;
+import android.view.Window;
+import android.view.WindowManager;
import android.widget.FrameLayout;
+import android.widget.LinearLayout;
import android.widget.RelativeLayout;
public final class ViewLayoutUtils {
@@ -51,4 +54,40 @@ public final class ViewLayoutUtils {
marginLayoutParams.setMargins(x, y, 0, 0);
}
}
+
+ public static void updateLayoutHeightOf(final Window window, final int layoutHeight) {
+ final WindowManager.LayoutParams params = window.getAttributes();
+ if (params.height != layoutHeight) {
+ params.height = layoutHeight;
+ window.setAttributes(params);
+ }
+ }
+
+ public static void updateLayoutHeightOf(final View view, final int layoutHeight) {
+ final ViewGroup.LayoutParams params = view.getLayoutParams();
+ if (params.height != layoutHeight) {
+ params.height = layoutHeight;
+ view.setLayoutParams(params);
+ }
+ }
+
+ public static void updateLayoutGravityOf(final View view, final int layoutGravity) {
+ final ViewGroup.LayoutParams lp = view.getLayoutParams();
+ if (lp instanceof LinearLayout.LayoutParams) {
+ final LinearLayout.LayoutParams params = (LinearLayout.LayoutParams)lp;
+ if (params.gravity != layoutGravity) {
+ params.gravity = layoutGravity;
+ view.setLayoutParams(params);
+ }
+ } else if (lp instanceof FrameLayout.LayoutParams) {
+ final FrameLayout.LayoutParams params = (FrameLayout.LayoutParams)lp;
+ if (params.gravity != layoutGravity) {
+ params.gravity = layoutGravity;
+ view.setLayoutParams(params);
+ }
+ } else {
+ throw new IllegalArgumentException("Layout parameter doesn't have gravity: "
+ + lp.getClass().getName());
+ }
+ }
}
diff --git a/native/jni/NativeFileList.mk b/native/jni/NativeFileList.mk
index 5e662543e..018a34d18 100644
--- a/native/jni/NativeFileList.mk
+++ b/native/jni/NativeFileList.mk
@@ -61,7 +61,6 @@ LATIN_IME_CORE_SRC_FILES := \
ver2_patricia_trie_node_reader.cpp \
ver2_pt_node_array_reader.cpp) \
$(addprefix suggest/policyimpl/dictionary/structure/v4/, \
- bigram/ver4_bigram_list_policy.cpp \
ver4_dict_buffers.cpp \
ver4_dict_constants.cpp \
ver4_patricia_trie_node_reader.cpp \
@@ -71,7 +70,6 @@ LATIN_IME_CORE_SRC_FILES := \
ver4_patricia_trie_writing_helper.cpp \
ver4_pt_node_array_reader.cpp) \
$(addprefix suggest/policyimpl/dictionary/structure/v4/content/, \
- bigram_dict_content.cpp \
language_model_dict_content.cpp \
shortcut_dict_content.cpp \
sparse_table_dict_content.cpp \
@@ -132,4 +130,5 @@ LATIN_IME_CORE_TEST_FILES := \
suggest/policyimpl/dictionary/utils/sparse_table_test.cpp \
suggest/policyimpl/dictionary/utils/trie_map_test.cpp \
utils/autocorrection_threshold_utils_test.cpp \
- utils/int_array_view_test.cpp
+ utils/int_array_view_test.cpp \
+ utils/time_keeper_test.cpp
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/bigram/ver4_bigram_list_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/bigram/ver4_bigram_list_policy.cpp
deleted file mode 100644
index 08dc107ab..000000000
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/bigram/ver4_bigram_list_policy.cpp
+++ /dev/null
@@ -1,282 +0,0 @@
-/*
- * 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/bigram/ver4_bigram_list_policy.h"
-
-#include "suggest/core/dictionary/property/bigram_property.h"
-#include "suggest/policyimpl/dictionary/header/header_policy.h"
-#include "suggest/policyimpl/dictionary/structure/pt_common/bigram/bigram_list_read_write_utils.h"
-#include "suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.h"
-#include "suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.h"
-#include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h"
-#include "suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h"
-
-namespace latinime {
-
-void Ver4BigramListPolicy::getNextBigram(int *const outBigramPos, int *const outProbability,
- bool *const outHasNext, int *const bigramEntryPos) const {
- const BigramEntry bigramEntry =
- mBigramDictContent->getBigramEntryAndAdvancePosition(bigramEntryPos);
- if (outBigramPos) {
- // Lookup target PtNode position.
- *outBigramPos = mTerminalPositionLookupTable->getTerminalPtNodePosition(
- bigramEntry.getTargetTerminalId());
- }
- if (outProbability) {
- if (bigramEntry.hasHistoricalInfo()) {
- *outProbability =
- ForgettingCurveUtils::decodeProbability(bigramEntry.getHistoricalInfo(),
- mHeaderPolicy);
- } else {
- *outProbability = bigramEntry.getProbability();
- }
- }
- if (outHasNext) {
- *outHasNext = bigramEntry.hasNext();
- }
-}
-
-bool Ver4BigramListPolicy::addNewEntry(const int terminalId, const int newTargetTerminalId,
- const BigramProperty *const bigramProperty, bool *const outAddedNewEntry) {
- // 1. The word has no bigrams yet.
- // 2. The word has bigrams, and there is the target in the list.
- // 3. The word has bigrams, and there is an invalid entry that can be reclaimed.
- // 4. The word has bigrams. We have to append new bigram entry to the list.
- // 5. Same as 4, but the list is the last entry of the content file.
- if (outAddedNewEntry) {
- *outAddedNewEntry = false;
- }
- const int bigramListPos = mBigramDictContent->getBigramListHeadPos(terminalId);
- if (bigramListPos == NOT_A_DICT_POS) {
- // Case 1. PtNode that doesn't have a bigram list.
- // Create new bigram list.
- if (!mBigramDictContent->createNewBigramList(terminalId)) {
- return false;
- }
- const BigramEntry newBigramEntry(false /* hasNext */, NOT_A_PROBABILITY,
- newTargetTerminalId);
- const BigramEntry bigramEntryToWrite = createUpdatedBigramEntryFrom(&newBigramEntry,
- bigramProperty);
- // Write an entry.
- int writingPos = mBigramDictContent->getBigramListHeadPos(terminalId);
- if (!mBigramDictContent->writeBigramEntryAndAdvancePosition(&bigramEntryToWrite,
- &writingPos)) {
- AKLOGE("Cannot write bigram entry. pos: %d.", writingPos);
- return false;
- }
- if (!mBigramDictContent->writeTerminator(writingPos)) {
- AKLOGE("Cannot write bigram list terminator. pos: %d.", writingPos);
- return false;
- }
- if (outAddedNewEntry) {
- *outAddedNewEntry = true;
- }
- return true;
- }
-
- int tailEntryPos = NOT_A_DICT_POS;
- const int entryPosToUpdate = getEntryPosToUpdate(newTargetTerminalId, bigramListPos,
- &tailEntryPos);
- if (entryPosToUpdate == NOT_A_DICT_POS) {
- // Case 4, 5. Add new entry to the bigram list.
- const int contentTailPos = mBigramDictContent->getContentTailPos();
- // If the tail entry is at the tail of content buffer, the new entry can be written without
- // link (Case 5).
- const bool canAppendEntry =
- contentTailPos == tailEntryPos + mBigramDictContent->getBigramEntrySize();
- const int newEntryPos = canAppendEntry ? tailEntryPos : contentTailPos;
- int writingPos = newEntryPos;
- // Write new entry at the tail position of the bigram content.
- const BigramEntry newBigramEntry(false /* hasNext */, NOT_A_PROBABILITY,
- newTargetTerminalId);
- const BigramEntry bigramEntryToWrite = createUpdatedBigramEntryFrom(
- &newBigramEntry, bigramProperty);
- if (!mBigramDictContent->writeBigramEntryAndAdvancePosition(&bigramEntryToWrite,
- &writingPos)) {
- AKLOGE("Cannot write bigram entry. pos: %d.", writingPos);
- return false;
- }
- if (!mBigramDictContent->writeTerminator(writingPos)) {
- AKLOGE("Cannot write bigram list terminator. pos: %d.", writingPos);
- return false;
- }
- if (!canAppendEntry) {
- // Update link of the current tail entry.
- if (!mBigramDictContent->writeLink(newEntryPos, tailEntryPos)) {
- AKLOGE("Cannot update bigram entry link. pos: %d, linked entry pos: %d.",
- tailEntryPos, newEntryPos);
- return false;
- }
- }
- if (outAddedNewEntry) {
- *outAddedNewEntry = true;
- }
- return true;
- }
-
- // Case 2. Overwrite the existing entry. Case 3. Reclaim and reuse the existing invalid entry.
- const BigramEntry originalBigramEntry = mBigramDictContent->getBigramEntry(entryPosToUpdate);
- if (!originalBigramEntry.isValid()) {
- // Case 3. Reuse the existing invalid entry. outAddedNewEntry is false when an existing
- // entry is updated.
- if (outAddedNewEntry) {
- *outAddedNewEntry = true;
- }
- }
- const BigramEntry updatedBigramEntry =
- originalBigramEntry.updateTargetTerminalIdAndGetEntry(newTargetTerminalId);
- const BigramEntry bigramEntryToWrite = createUpdatedBigramEntryFrom(
- &updatedBigramEntry, bigramProperty);
- return mBigramDictContent->writeBigramEntry(&bigramEntryToWrite, entryPosToUpdate);
-}
-
-bool Ver4BigramListPolicy::removeEntry(const int terminalId, const int targetTerminalId) {
- const int bigramListPos = mBigramDictContent->getBigramListHeadPos(terminalId);
- if (bigramListPos == NOT_A_DICT_POS) {
- // Bigram list doesn't exist.
- return false;
- }
- const int entryPosToUpdate = getEntryPosToUpdate(targetTerminalId, bigramListPos,
- nullptr /* outTailEntryPos */);
- if (entryPosToUpdate == NOT_A_DICT_POS) {
- // Bigram entry doesn't exist.
- return false;
- }
- const BigramEntry bigramEntry = mBigramDictContent->getBigramEntry(entryPosToUpdate);
- if (targetTerminalId != bigramEntry.getTargetTerminalId()) {
- // Bigram entry doesn't exist.
- return false;
- }
- // Remove bigram entry by marking it as invalid entry and overwriting the original entry.
- const BigramEntry updatedBigramEntry = bigramEntry.getInvalidatedEntry();
- return mBigramDictContent->writeBigramEntry(&updatedBigramEntry, entryPosToUpdate);
-}
-
-bool Ver4BigramListPolicy::updateAllBigramEntriesAndDeleteUselessEntries(const int terminalId,
- int *const outBigramCount) {
- const int bigramListPos = mBigramDictContent->getBigramListHeadPos(terminalId);
- if (bigramListPos == NOT_A_DICT_POS) {
- // Bigram list doesn't exist.
- return true;
- }
- bool hasNext = true;
- int readingPos = bigramListPos;
- while (hasNext) {
- const BigramEntry bigramEntry =
- mBigramDictContent->getBigramEntryAndAdvancePosition(&readingPos);
- const int entryPos = readingPos - mBigramDictContent->getBigramEntrySize();
- hasNext = bigramEntry.hasNext();
- if (!bigramEntry.isValid()) {
- continue;
- }
- const int targetPtNodePos = mTerminalPositionLookupTable->getTerminalPtNodePosition(
- bigramEntry.getTargetTerminalId());
- if (targetPtNodePos == NOT_A_DICT_POS) {
- // Invalidate bigram entry.
- const BigramEntry updatedBigramEntry = bigramEntry.getInvalidatedEntry();
- if (!mBigramDictContent->writeBigramEntry(&updatedBigramEntry, entryPos)) {
- return false;
- }
- } else if (bigramEntry.hasHistoricalInfo()) {
- const HistoricalInfo historicalInfo = ForgettingCurveUtils::createHistoricalInfoToSave(
- bigramEntry.getHistoricalInfo(), mHeaderPolicy);
- if (ForgettingCurveUtils::needsToKeep(&historicalInfo, mHeaderPolicy)) {
- const BigramEntry updatedBigramEntry =
- bigramEntry.updateHistoricalInfoAndGetEntry(&historicalInfo);
- if (!mBigramDictContent->writeBigramEntry(&updatedBigramEntry, entryPos)) {
- return false;
- }
- *outBigramCount += 1;
- } else {
- // Remove entry.
- const BigramEntry updatedBigramEntry = bigramEntry.getInvalidatedEntry();
- if (!mBigramDictContent->writeBigramEntry(&updatedBigramEntry, entryPos)) {
- return false;
- }
- }
- } else {
- *outBigramCount += 1;
- }
- }
- return true;
-}
-
-int Ver4BigramListPolicy::getBigramEntryConut(const int terminalId) {
- const int bigramListPos = mBigramDictContent->getBigramListHeadPos(terminalId);
- if (bigramListPos == NOT_A_DICT_POS) {
- // Bigram list doesn't exist.
- return 0;
- }
- int bigramCount = 0;
- bool hasNext = true;
- int readingPos = bigramListPos;
- while (hasNext) {
- const BigramEntry bigramEntry =
- mBigramDictContent->getBigramEntryAndAdvancePosition(&readingPos);
- hasNext = bigramEntry.hasNext();
- if (bigramEntry.isValid()) {
- bigramCount++;
- }
- }
- return bigramCount;
-}
-
-int Ver4BigramListPolicy::getEntryPosToUpdate(const int targetTerminalIdToFind,
- const int bigramListPos, int *const outTailEntryPos) const {
- if (outTailEntryPos) {
- *outTailEntryPos = NOT_A_DICT_POS;
- }
- int invalidEntryPos = NOT_A_DICT_POS;
- int readingPos = bigramListPos;
- while (true) {
- const BigramEntry bigramEntry =
- mBigramDictContent->getBigramEntryAndAdvancePosition(&readingPos);
- const int entryPos = readingPos - mBigramDictContent->getBigramEntrySize();
- if (!bigramEntry.hasNext()) {
- if (outTailEntryPos) {
- *outTailEntryPos = entryPos;
- }
- break;
- }
- if (bigramEntry.getTargetTerminalId() == targetTerminalIdToFind) {
- // Entry with same target is found.
- return entryPos;
- } else if (!bigramEntry.isValid()) {
- // Invalid entry that can be reused is found.
- invalidEntryPos = entryPos;
- }
- }
- return invalidEntryPos;
-}
-
-const BigramEntry Ver4BigramListPolicy::createUpdatedBigramEntryFrom(
- const BigramEntry *const originalBigramEntry,
- const BigramProperty *const bigramProperty) const {
- // TODO: Consolidate historical info and probability.
- if (mHeaderPolicy->hasHistoricalInfoOfWords()) {
- const HistoricalInfo historicalInfoForUpdate(bigramProperty->getTimestamp(),
- bigramProperty->getLevel(), bigramProperty->getCount());
- const HistoricalInfo updatedHistoricalInfo =
- ForgettingCurveUtils::createUpdatedHistoricalInfo(
- originalBigramEntry->getHistoricalInfo(), bigramProperty->getProbability(),
- &historicalInfoForUpdate, mHeaderPolicy);
- return originalBigramEntry->updateHistoricalInfoAndGetEntry(&updatedHistoricalInfo);
- } else {
- return originalBigramEntry->updateProbabilityAndGetEntry(bigramProperty->getProbability());
- }
-}
-
-} // namespace latinime
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/bigram/ver4_bigram_list_policy.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/bigram/ver4_bigram_list_policy.h
deleted file mode 100644
index 4b3bb3725..000000000
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/bigram/ver4_bigram_list_policy.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * 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_BIGRAM_LIST_POLICY_H
-#define LATINIME_VER4_BIGRAM_LIST_POLICY_H
-
-#include "defines.h"
-#include "suggest/core/policy/dictionary_bigrams_structure_policy.h"
-#include "suggest/policyimpl/dictionary/structure/v4/content/bigram_entry.h"
-
-namespace latinime {
-
-class BigramDictContent;
-class BigramProperty;
-class HeaderPolicy;
-class TerminalPositionLookupTable;
-
-class Ver4BigramListPolicy : public DictionaryBigramsStructurePolicy {
- public:
- Ver4BigramListPolicy(BigramDictContent *const bigramDictContent,
- const TerminalPositionLookupTable *const terminalPositionLookupTable,
- const HeaderPolicy *const headerPolicy)
- : mBigramDictContent(bigramDictContent),
- mTerminalPositionLookupTable(terminalPositionLookupTable),
- mHeaderPolicy(headerPolicy) {}
-
- void getNextBigram(int *const outBigramPos, int *const outProbability,
- bool *const outHasNext, int *const bigramEntryPos) const;
-
- bool skipAllBigrams(int *const pos) const {
- // Do nothing because we don't need to skip bigram lists in ver4 dictionaries.
- return true;
- }
-
- bool addNewEntry(const int terminalId, const int newTargetTerminalId,
- const BigramProperty *const bigramProperty, bool *const outAddedNewEntry);
-
- bool removeEntry(const int terminalId, const int targetTerminalId);
-
- bool updateAllBigramEntriesAndDeleteUselessEntries(const int terminalId,
- int *const outBigramCount);
-
- int getBigramEntryConut(const int terminalId);
-
- private:
- DISALLOW_IMPLICIT_CONSTRUCTORS(Ver4BigramListPolicy);
-
- int getEntryPosToUpdate(const int targetTerminalIdToFind, const int bigramListPos,
- int *const outTailEntryPos) const;
-
- const BigramEntry createUpdatedBigramEntryFrom(const BigramEntry *const originalBigramEntry,
- const BigramProperty *const bigramProperty) const;
-
- BigramDictContent *const mBigramDictContent;
- const TerminalPositionLookupTable *const mTerminalPositionLookupTable;
- const HeaderPolicy *const mHeaderPolicy;
-};
-} // namespace latinime
-#endif /* LATINIME_VER4_BIGRAM_LIST_POLICY_H */
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.cpp
deleted file mode 100644
index d7e1952b5..000000000
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.cpp
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- * 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/content/bigram_dict_content.h"
-
-#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h"
-
-namespace latinime {
-
-const int BigramDictContent::INVALID_LINKED_ENTRY_POS = Ver4DictConstants::NOT_A_TERMINAL_ID;
-
-const BigramEntry BigramDictContent::getBigramEntryAndAdvancePosition(
- int *const bigramEntryPos) const {
- const BufferWithExtendableBuffer *const bigramListBuffer = getContentBuffer();
- const int bigramEntryTailPos = (*bigramEntryPos) + getBigramEntrySize();
- if (*bigramEntryPos < 0 || bigramEntryTailPos > bigramListBuffer->getTailPosition()) {
- AKLOGE("Invalid bigram entry position. bigramEntryPos: %d, bigramEntryTailPos: %d, "
- "bufSize: %d", *bigramEntryPos, bigramEntryTailPos,
- bigramListBuffer->getTailPosition());
- ASSERT(false);
- return BigramEntry(false /* hasNext */, NOT_A_PROBABILITY,
- Ver4DictConstants::NOT_A_TERMINAL_ID);
- }
- const int bigramFlags = bigramListBuffer->readUintAndAdvancePosition(
- Ver4DictConstants::BIGRAM_FLAGS_FIELD_SIZE, bigramEntryPos);
- const bool isLink = (bigramFlags & Ver4DictConstants::BIGRAM_IS_LINK_MASK) != 0;
- int probability = NOT_A_PROBABILITY;
- int timestamp = NOT_A_TIMESTAMP;
- int level = 0;
- int count = 0;
- if (mHasHistoricalInfo) {
- timestamp = bigramListBuffer->readUintAndAdvancePosition(
- Ver4DictConstants::TIME_STAMP_FIELD_SIZE, bigramEntryPos);
- level = bigramListBuffer->readUintAndAdvancePosition(
- Ver4DictConstants::WORD_LEVEL_FIELD_SIZE, bigramEntryPos);
- count = bigramListBuffer->readUintAndAdvancePosition(
- Ver4DictConstants::WORD_COUNT_FIELD_SIZE, bigramEntryPos);
- } else {
- probability = bigramListBuffer->readUintAndAdvancePosition(
- Ver4DictConstants::PROBABILITY_SIZE, bigramEntryPos);
- }
- const int encodedTargetTerminalId = bigramListBuffer->readUintAndAdvancePosition(
- Ver4DictConstants::BIGRAM_TARGET_TERMINAL_ID_FIELD_SIZE, bigramEntryPos);
- const int targetTerminalId =
- (encodedTargetTerminalId == Ver4DictConstants::INVALID_BIGRAM_TARGET_TERMINAL_ID) ?
- Ver4DictConstants::NOT_A_TERMINAL_ID : encodedTargetTerminalId;
- if (isLink) {
- const int linkedEntryPos = targetTerminalId;
- if (linkedEntryPos == INVALID_LINKED_ENTRY_POS) {
- // Bigram list terminator is found.
- return BigramEntry(false /* hasNext */, NOT_A_PROBABILITY,
- Ver4DictConstants::NOT_A_TERMINAL_ID);
- }
- *bigramEntryPos = linkedEntryPos;
- return getBigramEntryAndAdvancePosition(bigramEntryPos);
- }
- // hasNext is always true because we should continue to read the next entry until the terminator
- // is found.
- if (mHasHistoricalInfo) {
- const HistoricalInfo historicalInfo(timestamp, level, count);
- return BigramEntry(true /* hasNext */, probability, &historicalInfo, targetTerminalId);
- } else {
- return BigramEntry(true /* hasNext */, probability, targetTerminalId);
- }
-}
-
-bool BigramDictContent::writeBigramEntryAndAdvancePosition(
- const BigramEntry *const bigramEntryToWrite, int *const entryWritingPos) {
- return writeBigramEntryAttributesAndAdvancePosition(false /* isLink */,
- bigramEntryToWrite->getProbability(), bigramEntryToWrite->getTargetTerminalId(),
- bigramEntryToWrite->getHistoricalInfo()->getTimeStamp(),
- bigramEntryToWrite->getHistoricalInfo()->getLevel(),
- bigramEntryToWrite->getHistoricalInfo()->getCount(),
- entryWritingPos);
-}
-
-bool BigramDictContent::writeBigramEntryAttributesAndAdvancePosition(
- const bool isLink, const int probability, const int targetTerminalId,
- const int timestamp, const int level, const int count, int *const entryWritingPos) {
- BufferWithExtendableBuffer *const bigramListBuffer = getWritableContentBuffer();
- const int bigramFlags = isLink ? Ver4DictConstants::BIGRAM_IS_LINK_MASK : 0;
- if (!bigramListBuffer->writeUintAndAdvancePosition(bigramFlags,
- Ver4DictConstants::BIGRAM_FLAGS_FIELD_SIZE, entryWritingPos)) {
- AKLOGE("Cannot write bigram flags. pos: %d, flags: %x", *entryWritingPos, bigramFlags);
- return false;
- }
- if (mHasHistoricalInfo) {
- if (!bigramListBuffer->writeUintAndAdvancePosition(timestamp,
- Ver4DictConstants::TIME_STAMP_FIELD_SIZE, entryWritingPos)) {
- AKLOGE("Cannot write bigram timestamps. pos: %d, timestamp: %d", *entryWritingPos,
- timestamp);
- return false;
- }
- if (!bigramListBuffer->writeUintAndAdvancePosition(level,
- Ver4DictConstants::WORD_LEVEL_FIELD_SIZE, entryWritingPos)) {
- AKLOGE("Cannot write bigram level. pos: %d, level: %d", *entryWritingPos,
- level);
- return false;
- }
- if (!bigramListBuffer->writeUintAndAdvancePosition(count,
- Ver4DictConstants::WORD_COUNT_FIELD_SIZE, entryWritingPos)) {
- AKLOGE("Cannot write bigram count. pos: %d, count: %d", *entryWritingPos,
- count);
- return false;
- }
- } else {
- if (!bigramListBuffer->writeUintAndAdvancePosition(probability,
- Ver4DictConstants::PROBABILITY_SIZE, entryWritingPos)) {
- AKLOGE("Cannot write bigram probability. pos: %d, probability: %d", *entryWritingPos,
- probability);
- return false;
- }
- }
- const int targetTerminalIdToWrite = (targetTerminalId == Ver4DictConstants::NOT_A_TERMINAL_ID) ?
- Ver4DictConstants::INVALID_BIGRAM_TARGET_TERMINAL_ID : targetTerminalId;
- if (!bigramListBuffer->writeUintAndAdvancePosition(targetTerminalIdToWrite,
- Ver4DictConstants::BIGRAM_TARGET_TERMINAL_ID_FIELD_SIZE, entryWritingPos)) {
- AKLOGE("Cannot write bigram target terminal id. pos: %d, target terminal id: %d",
- *entryWritingPos, targetTerminalId);
- return false;
- }
- return true;
-}
-
-bool BigramDictContent::writeLink(const int linkedEntryPos, const int writingPos) {
- const int targetTerminalId = linkedEntryPos;
- int pos = writingPos;
- return writeBigramEntryAttributesAndAdvancePosition(true /* isLink */,
- NOT_A_PROBABILITY /* probability */, targetTerminalId, NOT_A_TIMESTAMP, 0 /* level */,
- 0 /* count */, &pos);
-}
-
-bool BigramDictContent::runGC(const TerminalPositionLookupTable::TerminalIdMap *const terminalIdMap,
- const BigramDictContent *const originalBigramDictContent,
- int *const outBigramEntryCount) {
- for (TerminalPositionLookupTable::TerminalIdMap::const_iterator it = terminalIdMap->begin();
- it != terminalIdMap->end(); ++it) {
- const int originalBigramListPos =
- originalBigramDictContent->getBigramListHeadPos(it->first);
- if (originalBigramListPos == NOT_A_DICT_POS) {
- // This terminal does not have a bigram list.
- continue;
- }
- const int bigramListPos = getContentBuffer()->getTailPosition();
- int bigramEntryCount = 0;
- // Copy bigram list with GC from original content.
- if (!runGCBigramList(originalBigramListPos, originalBigramDictContent, bigramListPos,
- terminalIdMap, &bigramEntryCount)) {
- AKLOGE("Cannot complete GC for the bigram list. original pos: %d, pos: %d",
- originalBigramListPos, bigramListPos);
- return false;
- }
- if (bigramEntryCount == 0) {
- // All bigram entries are useless. This terminal does not have a bigram list.
- continue;
- }
- *outBigramEntryCount += bigramEntryCount;
- // Set bigram list position to the lookup table.
- if (!getUpdatableAddressLookupTable()->set(it->second, bigramListPos)) {
- AKLOGE("Cannot set bigram list position. terminal id: %d, pos: %d",
- it->second, bigramListPos);
- return false;
- }
- }
- return true;
-}
-
-// Returns whether GC for the bigram list was succeeded or not.
-bool BigramDictContent::runGCBigramList(const int bigramListPos,
- const BigramDictContent *const sourceBigramDictContent, const int toPos,
- const TerminalPositionLookupTable::TerminalIdMap *const terminalIdMap,
- int *const outEntryCount) {
- bool hasNext = true;
- int readingPos = bigramListPos;
- int writingPos = toPos;
- while (hasNext) {
- const BigramEntry originalBigramEntry =
- sourceBigramDictContent->getBigramEntryAndAdvancePosition(&readingPos);
- hasNext = originalBigramEntry.hasNext();
- if (!originalBigramEntry.isValid()) {
- continue;
- }
- TerminalPositionLookupTable::TerminalIdMap::const_iterator it =
- terminalIdMap->find(originalBigramEntry.getTargetTerminalId());
- if (it == terminalIdMap->end()) {
- // Target word has been removed.
- continue;
- }
- const BigramEntry updatedBigramEntry =
- originalBigramEntry.updateTargetTerminalIdAndGetEntry(it->second);
- if (!writeBigramEntryAndAdvancePosition(&updatedBigramEntry, &writingPos)) {
- AKLOGE("Cannot write bigram entry to run GC. pos: %d", writingPos);
- return false;
- }
- *outEntryCount += 1;
- }
- if (*outEntryCount > 0) {
- if (!writeTerminator(writingPos)) {
- AKLOGE("Cannot write terminator to run GC. pos: %d", writingPos);
- return false;
- }
- }
- return true;
-}
-
-} // namespace latinime
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.h
deleted file mode 100644
index 20bae5943..000000000
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.h
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * 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_BIGRAM_DICT_CONTENT_H
-#define LATINIME_BIGRAM_DICT_CONTENT_H
-
-#include <cstdio>
-
-#include "defines.h"
-#include "suggest/policyimpl/dictionary/structure/v4/content/bigram_entry.h"
-#include "suggest/policyimpl/dictionary/structure/v4/content/sparse_table_dict_content.h"
-#include "suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.h"
-#include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h"
-
-namespace latinime {
-
-class ReadWriteByteArrayView;
-
-class BigramDictContent : public SparseTableDictContent {
- public:
- BigramDictContent(const ReadWriteByteArrayView *const buffers, const bool hasHistoricalInfo)
- : SparseTableDictContent(buffers, Ver4DictConstants::BIGRAM_ADDRESS_TABLE_BLOCK_SIZE,
- Ver4DictConstants::BIGRAM_ADDRESS_TABLE_DATA_SIZE),
- mHasHistoricalInfo(hasHistoricalInfo) {}
-
- BigramDictContent(const bool hasHistoricalInfo)
- : SparseTableDictContent(Ver4DictConstants::BIGRAM_ADDRESS_TABLE_BLOCK_SIZE,
- Ver4DictConstants::BIGRAM_ADDRESS_TABLE_DATA_SIZE),
- mHasHistoricalInfo(hasHistoricalInfo) {}
-
- int getContentTailPos() const {
- return getContentBuffer()->getTailPosition();
- }
-
- const BigramEntry getBigramEntry(const int bigramEntryPos) const {
- int readingPos = bigramEntryPos;
- return getBigramEntryAndAdvancePosition(&readingPos);
- }
-
- const BigramEntry getBigramEntryAndAdvancePosition(int *const bigramEntryPos) const;
-
- // Returns head position of bigram list for a PtNode specified by terminalId.
- int getBigramListHeadPos(const int terminalId) const {
- const SparseTable *const addressLookupTable = getAddressLookupTable();
- if (!addressLookupTable->contains(terminalId)) {
- return NOT_A_DICT_POS;
- }
- return addressLookupTable->get(terminalId);
- }
-
- bool writeBigramEntryAtTail(const BigramEntry *const bigramEntryToWrite) {
- int writingPos = getContentBuffer()->getTailPosition();
- return writeBigramEntryAndAdvancePosition(bigramEntryToWrite, &writingPos);
- }
-
- bool writeBigramEntry(const BigramEntry *const bigramEntryToWrite, const int entryWritingPos) {
- int writingPos = entryWritingPos;
- return writeBigramEntryAndAdvancePosition(bigramEntryToWrite, &writingPos);
- }
-
- bool writeBigramEntryAndAdvancePosition(const BigramEntry *const bigramEntryToWrite,
- int *const entryWritingPos);
-
- bool writeTerminator(const int writingPos) {
- // Terminator is a link to the invalid position.
- return writeLink(INVALID_LINKED_ENTRY_POS, writingPos);
- }
-
- bool writeLink(const int linkedPos, const int writingPos);
-
- bool createNewBigramList(const int terminalId) {
- const int bigramListPos = getContentBuffer()->getTailPosition();
- return getUpdatableAddressLookupTable()->set(terminalId, bigramListPos);
- }
-
- bool flushToFile(FILE *const file) const {
- return flush(file);
- }
-
- bool runGC(const TerminalPositionLookupTable::TerminalIdMap *const terminalIdMap,
- const BigramDictContent *const originalBigramDictContent,
- int *const outBigramEntryCount);
-
- int getBigramEntrySize() const {
- if (mHasHistoricalInfo) {
- return Ver4DictConstants::BIGRAM_FLAGS_FIELD_SIZE
- + Ver4DictConstants::TIME_STAMP_FIELD_SIZE
- + Ver4DictConstants::WORD_LEVEL_FIELD_SIZE
- + Ver4DictConstants::WORD_COUNT_FIELD_SIZE
- + Ver4DictConstants::BIGRAM_TARGET_TERMINAL_ID_FIELD_SIZE;
- } else {
- return Ver4DictConstants::BIGRAM_FLAGS_FIELD_SIZE
- + Ver4DictConstants::PROBABILITY_SIZE
- + Ver4DictConstants::BIGRAM_TARGET_TERMINAL_ID_FIELD_SIZE;
- }
- }
-
- private:
- DISALLOW_COPY_AND_ASSIGN(BigramDictContent);
-
- static const int INVALID_LINKED_ENTRY_POS;
-
- bool writeBigramEntryAttributesAndAdvancePosition(
- const bool isLink, const int probability, const int targetTerminalId,
- const int timestamp, const int level, const int count, int *const entryWritingPos);
-
- bool runGCBigramList(const int bigramListPos,
- const BigramDictContent *const sourceBigramDictContent, const int toPos,
- const TerminalPositionLookupTable::TerminalIdMap *const terminalIdMap,
- int *const outEntryCount);
-
- bool mHasHistoricalInfo;
-};
-} // namespace latinime
-#endif /* LATINIME_BIGRAM_DICT_CONTENT_H */
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_entry.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_entry.h
deleted file mode 100644
index 2b0cbd93b..000000000
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_entry.h
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * 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_BIGRAM_ENTRY_H
-#define LATINIME_BIGRAM_ENTRY_H
-
-#include "defines.h"
-#include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h"
-#include "suggest/policyimpl/dictionary/utils/historical_info.h"
-
-namespace latinime {
-
-class BigramEntry {
- public:
- BigramEntry(const BigramEntry& bigramEntry)
- : mHasNext(bigramEntry.mHasNext), mProbability(bigramEntry.mProbability),
- mHistoricalInfo(), mTargetTerminalId(bigramEntry.mTargetTerminalId) {}
-
- // Entry with historical information.
- BigramEntry(const bool hasNext, const int probability, const int targetTerminalId)
- : mHasNext(hasNext), mProbability(probability), mHistoricalInfo(),
- mTargetTerminalId(targetTerminalId) {}
-
- // Entry with historical information.
- BigramEntry(const bool hasNext, const int probability,
- const HistoricalInfo *const historicalInfo, const int targetTerminalId)
- : mHasNext(hasNext), mProbability(probability), mHistoricalInfo(*historicalInfo),
- mTargetTerminalId(targetTerminalId) {}
-
- const BigramEntry getInvalidatedEntry() const {
- return updateTargetTerminalIdAndGetEntry(Ver4DictConstants::NOT_A_TERMINAL_ID);
- }
-
- const BigramEntry updateHasNextAndGetEntry(const bool hasNext) const {
- return BigramEntry(hasNext, mProbability, &mHistoricalInfo, mTargetTerminalId);
- }
-
- const BigramEntry updateTargetTerminalIdAndGetEntry(const int newTargetTerminalId) const {
- return BigramEntry(mHasNext, mProbability, &mHistoricalInfo, newTargetTerminalId);
- }
-
- const BigramEntry updateProbabilityAndGetEntry(const int probability) const {
- return BigramEntry(mHasNext, probability, &mHistoricalInfo, mTargetTerminalId);
- }
-
- const BigramEntry updateHistoricalInfoAndGetEntry(
- const HistoricalInfo *const historicalInfo) const {
- return BigramEntry(mHasNext, mProbability, historicalInfo, mTargetTerminalId);
- }
-
- bool isValid() const {
- return mTargetTerminalId != Ver4DictConstants::NOT_A_TERMINAL_ID;
- }
-
- bool hasNext() const {
- return mHasNext;
- }
-
- int getProbability() const {
- return mProbability;
- }
-
- bool hasHistoricalInfo() const {
- return mHistoricalInfo.isValid();
- }
-
- const HistoricalInfo *getHistoricalInfo() const {
- return &mHistoricalInfo;
- }
-
- int getTargetTerminalId() const {
- return mTargetTerminalId;
- }
-
- private:
- // Copy constructor is public to use this class as a type of return value.
- DISALLOW_DEFAULT_CONSTRUCTOR(BigramEntry);
- DISALLOW_ASSIGNMENT_OPERATOR(BigramEntry);
-
- const bool mHasNext;
- const int mProbability;
- const HistoricalInfo mHistoricalInfo;
- const int mTargetTerminalId;
-};
-} // namespace latinime
-#endif /* LATINIME_BIGRAM_ENTRY_H */
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.cpp
index ea2d24e67..d5749e9eb 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.cpp
@@ -23,6 +23,9 @@
namespace latinime {
+const int LanguageModelDictContent::UNIGRAM_COUNT_INDEX_IN_ENTRY_COUNT_TABLE = 0;
+const int LanguageModelDictContent::BIGRAM_COUNT_INDEX_IN_ENTRY_COUNT_TABLE = 1;
+
bool LanguageModelDictContent::save(FILE *const file) const {
return mTrieMap.save(file);
}
@@ -71,13 +74,22 @@ bool LanguageModelDictContent::removeNgramProbabilityEntry(const WordIdArrayView
return mTrieMap.remove(wordId, bitmapEntryIndex);
}
+LanguageModelDictContent::EntryRange LanguageModelDictContent::getProbabilityEntries(
+ const WordIdArrayView prevWordIds) const {
+ const int bitmapEntryIndex = getBitmapEntryIndex(prevWordIds);
+ return EntryRange(mTrieMap.getEntriesInSpecifiedLevel(bitmapEntryIndex), mHasHistoricalInfo);
+}
+
bool LanguageModelDictContent::truncateEntries(const int *const entryCounts,
- const int *const maxEntryCounts, const HeaderPolicy *const headerPolicy) {
+ const int *const maxEntryCounts, const HeaderPolicy *const headerPolicy,
+ int *const outEntryCounts) {
for (int i = 0; i <= MAX_PREV_WORD_COUNT_FOR_N_GRAM; ++i) {
if (entryCounts[i] <= maxEntryCounts[i]) {
+ outEntryCounts[i] = entryCounts[i];
continue;
}
- if (!turncateEntriesInSpecifiedLevel(headerPolicy, maxEntryCounts[i], i)) {
+ if (!turncateEntriesInSpecifiedLevel(headerPolicy, maxEntryCounts[i], i,
+ &outEntryCounts[i])) {
return false;
}
}
@@ -179,7 +191,8 @@ bool LanguageModelDictContent::updateAllProbabilityEntriesInner(const int bitmap
}
bool LanguageModelDictContent::turncateEntriesInSpecifiedLevel(
- const HeaderPolicy *const headerPolicy, const int maxEntryCount, const int targetLevel) {
+ const HeaderPolicy *const headerPolicy, const int maxEntryCount, const int targetLevel,
+ int *const outEntryCount) {
std::vector<int> prevWordIds;
std::vector<EntryInfoToTurncate> entryInfoVector;
if (!getEntryInfo(headerPolicy, targetLevel, mTrieMap.getRootBitmapEntryIndex(),
@@ -187,8 +200,10 @@ bool LanguageModelDictContent::turncateEntriesInSpecifiedLevel(
return false;
}
if (static_cast<int>(entryInfoVector.size()) <= maxEntryCount) {
+ *outEntryCount = static_cast<int>(entryInfoVector.size());
return true;
}
+ *outEntryCount = maxEntryCount;
const int entryCountToRemove = static_cast<int>(entryInfoVector.size()) - maxEntryCount;
std::partial_sort(entryInfoVector.begin(), entryInfoVector.begin() + entryCountToRemove,
entryInfoVector.end(),
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.h
index 43b2aab66..aa612e35a 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.h
@@ -39,6 +39,78 @@ class HeaderPolicy;
*/
class LanguageModelDictContent {
public:
+ static const int UNIGRAM_COUNT_INDEX_IN_ENTRY_COUNT_TABLE;
+ static const int BIGRAM_COUNT_INDEX_IN_ENTRY_COUNT_TABLE;
+
+ // Pair of word id and probability entry used for iteration.
+ class WordIdAndProbabilityEntry {
+ public:
+ WordIdAndProbabilityEntry(const int wordId, const ProbabilityEntry &probabilityEntry)
+ : mWordId(wordId), mProbabilityEntry(probabilityEntry) {}
+
+ int getWordId() const { return mWordId; }
+ const ProbabilityEntry getProbabilityEntry() const { return mProbabilityEntry; }
+
+ private:
+ DISALLOW_DEFAULT_CONSTRUCTOR(WordIdAndProbabilityEntry);
+ DISALLOW_ASSIGNMENT_OPERATOR(WordIdAndProbabilityEntry);
+
+ const int mWordId;
+ const ProbabilityEntry mProbabilityEntry;
+ };
+
+ // Iterator.
+ class EntryIterator {
+ public:
+ EntryIterator(const TrieMap::TrieMapIterator &trieMapIterator,
+ const bool hasHistoricalInfo)
+ : mTrieMapIterator(trieMapIterator), mHasHistoricalInfo(hasHistoricalInfo) {}
+
+ const WordIdAndProbabilityEntry operator*() const {
+ const TrieMap::TrieMapIterator::IterationResult &result = *mTrieMapIterator;
+ return WordIdAndProbabilityEntry(
+ result.key(), ProbabilityEntry::decode(result.value(), mHasHistoricalInfo));
+ }
+
+ bool operator!=(const EntryIterator &other) const {
+ return mTrieMapIterator != other.mTrieMapIterator;
+ }
+
+ const EntryIterator &operator++() {
+ ++mTrieMapIterator;
+ return *this;
+ }
+
+ private:
+ DISALLOW_DEFAULT_CONSTRUCTOR(EntryIterator);
+ DISALLOW_ASSIGNMENT_OPERATOR(EntryIterator);
+
+ TrieMap::TrieMapIterator mTrieMapIterator;
+ const bool mHasHistoricalInfo;
+ };
+
+ // Class represents range to use range base for loops.
+ class EntryRange {
+ public:
+ EntryRange(const TrieMap::TrieMapRange trieMapRange, const bool hasHistoricalInfo)
+ : mTrieMapRange(trieMapRange), mHasHistoricalInfo(hasHistoricalInfo) {}
+
+ EntryIterator begin() const {
+ return EntryIterator(mTrieMapRange.begin(), mHasHistoricalInfo);
+ }
+
+ EntryIterator end() const {
+ return EntryIterator(mTrieMapRange.end(), mHasHistoricalInfo);
+ }
+
+ private:
+ DISALLOW_DEFAULT_CONSTRUCTOR(EntryRange);
+ DISALLOW_ASSIGNMENT_OPERATOR(EntryRange);
+
+ const TrieMap::TrieMapRange mTrieMapRange;
+ const bool mHasHistoricalInfo;
+ };
+
LanguageModelDictContent(const ReadWriteByteArrayView trieMapBuffer,
const bool hasHistoricalInfo)
: mTrieMap(trieMapBuffer), mHasHistoricalInfo(hasHistoricalInfo) {}
@@ -76,6 +148,8 @@ class LanguageModelDictContent {
bool removeNgramProbabilityEntry(const WordIdArrayView prevWordIds, const int wordId);
+ EntryRange getProbabilityEntries(const WordIdArrayView prevWordIds) const;
+
bool updateAllProbabilityEntries(const HeaderPolicy *const headerPolicy,
int *const outEntryCounts) {
for (int i = 0; i <= MAX_PREV_WORD_COUNT_FOR_N_GRAM; ++i) {
@@ -87,7 +161,7 @@ class LanguageModelDictContent {
// entryCounts should be created by updateAllProbabilityEntries.
bool truncateEntries(const int *const entryCounts, const int *const maxEntryCounts,
- const HeaderPolicy *const headerPolicy);
+ const HeaderPolicy *const headerPolicy, int *const outEntryCounts);
private:
DISALLOW_COPY_AND_ASSIGN(LanguageModelDictContent);
@@ -126,7 +200,7 @@ class LanguageModelDictContent {
bool updateAllProbabilityEntriesInner(const int bitmapEntryIndex, const int level,
const HeaderPolicy *const headerPolicy, int *const outEntryCounts);
bool turncateEntriesInSpecifiedLevel(const HeaderPolicy *const headerPolicy,
- const int maxEntryCount, const int targetLevel);
+ const int maxEntryCount, const int targetLevel, int *const outEntryCount);
bool getEntryInfo(const HeaderPolicy *const headerPolicy, const int targetLevel,
const int bitmapEntryIndex, std::vector<int> *const prevWordIds,
std::vector<EntryInfoToTurncate> *const outEntryInfo) const;
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 1f40e3dd2..45f88e9b2 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
@@ -159,11 +159,6 @@ bool Ver4DictBuffers::flushDictBuffers(FILE *const file) const {
AKLOGE("Language model dict content cannot be written.");
return false;
}
- // Write bigram dict content.
- if (!mBigramDictContent.flushToFile(file)) {
- AKLOGE("Bigram dict content cannot be written.");
- return false;
- }
// Write shortcut dict content.
if (!mShortcutDictContent.flushToFile(file)) {
AKLOGE("Shortcut dict content cannot be written.");
@@ -186,8 +181,6 @@ Ver4DictBuffers::Ver4DictBuffers(MmappedBuffer::MmappedBufferPtr &&headerBuffer,
contentBuffers[Ver4DictConstants::TERMINAL_ADDRESS_LOOKUP_TABLE_BUFFER_INDEX]),
mLanguageModelDictContent(contentBuffers[Ver4DictConstants::LANGUAGE_MODEL_BUFFER_INDEX],
mHeaderPolicy.hasHistoricalInfoOfWords()),
- mBigramDictContent(&contentBuffers[Ver4DictConstants::BIGRAM_BUFFERS_INDEX],
- mHeaderPolicy.hasHistoricalInfoOfWords()),
mShortcutDictContent(&contentBuffers[Ver4DictConstants::SHORTCUT_BUFFERS_INDEX]),
mIsUpdatable(mDictBuffer->isUpdatable()) {}
@@ -196,7 +189,6 @@ Ver4DictBuffers::Ver4DictBuffers(const HeaderPolicy *const headerPolicy, const i
mExpandableHeaderBuffer(Ver4DictConstants::MAX_DICTIONARY_SIZE),
mExpandableTrieBuffer(maxTrieSize), mTerminalPositionLookupTable(),
mLanguageModelDictContent(headerPolicy->hasHistoricalInfoOfWords()),
- mBigramDictContent(headerPolicy->hasHistoricalInfoOfWords()), mShortcutDictContent(),
- mIsUpdatable(true) {}
+ mShortcutDictContent(), mIsUpdatable(true) {}
} // namespace latinime
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 70a7983f1..5407525af 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
@@ -22,7 +22,6 @@
#include "defines.h"
#include "suggest/policyimpl/dictionary/header/header_policy.h"
-#include "suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.h"
#include "suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content.h"
#include "suggest/policyimpl/dictionary/structure/v4/content/shortcut_dict_content.h"
#include "suggest/policyimpl/dictionary/structure/v4/content/terminal_position_lookup_table.h"
@@ -53,7 +52,6 @@ class Ver4DictBuffers {
return mExpandableTrieBuffer.isNearSizeLimit()
|| mTerminalPositionLookupTable.isNearSizeLimit()
|| mLanguageModelDictContent.isNearSizeLimit()
- || mBigramDictContent.isNearSizeLimit()
|| mShortcutDictContent.isNearSizeLimit();
}
@@ -89,14 +87,6 @@ class Ver4DictBuffers {
return &mLanguageModelDictContent;
}
- AK_FORCE_INLINE BigramDictContent *getMutableBigramDictContent() {
- return &mBigramDictContent;
- }
-
- AK_FORCE_INLINE const BigramDictContent *getBigramDictContent() const {
- return &mBigramDictContent;
- }
-
AK_FORCE_INLINE ShortcutDictContent *getMutableShortcutDictContent() {
return &mShortcutDictContent;
}
@@ -135,7 +125,6 @@ class Ver4DictBuffers {
BufferWithExtendableBuffer mExpandableTrieBuffer;
TerminalPositionLookupTable mTerminalPositionLookupTable;
LanguageModelDictContent mLanguageModelDictContent;
- BigramDictContent mBigramDictContent;
ShortcutDictContent mShortcutDictContent;
const int mIsUpdatable;
};
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp
index b085a6661..9acf2d44f 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp
@@ -29,20 +29,18 @@ const int Ver4DictConstants::MAX_DICT_EXTENDED_REGION_SIZE = 1 * 1024 * 1024;
// NUM_OF_BUFFERS_FOR_SINGLE_DICT_CONTENT for Trie and TerminalAddressLookupTable.
// NUM_OF_BUFFERS_FOR_LANGUAGE_MODEL_DICT_CONTENT for language model.
-// NUM_OF_BUFFERS_FOR_SPARSE_TABLE_DICT_CONTENT for bigram and shortcut.
+// NUM_OF_BUFFERS_FOR_SPARSE_TABLE_DICT_CONTENT for shortcut.
const size_t Ver4DictConstants::NUM_OF_CONTENT_BUFFERS_IN_BODY_FILE =
NUM_OF_BUFFERS_FOR_SINGLE_DICT_CONTENT * 2
+ NUM_OF_BUFFERS_FOR_LANGUAGE_MODEL_DICT_CONTENT
- + NUM_OF_BUFFERS_FOR_SPARSE_TABLE_DICT_CONTENT * 2;
+ + NUM_OF_BUFFERS_FOR_SPARSE_TABLE_DICT_CONTENT;
const int Ver4DictConstants::TRIE_BUFFER_INDEX = 0;
const int Ver4DictConstants::TERMINAL_ADDRESS_LOOKUP_TABLE_BUFFER_INDEX =
TRIE_BUFFER_INDEX + NUM_OF_BUFFERS_FOR_SINGLE_DICT_CONTENT;
const int Ver4DictConstants::LANGUAGE_MODEL_BUFFER_INDEX =
TERMINAL_ADDRESS_LOOKUP_TABLE_BUFFER_INDEX + NUM_OF_BUFFERS_FOR_SINGLE_DICT_CONTENT;
-const int Ver4DictConstants::BIGRAM_BUFFERS_INDEX =
- LANGUAGE_MODEL_BUFFER_INDEX + NUM_OF_BUFFERS_FOR_LANGUAGE_MODEL_DICT_CONTENT;
const int Ver4DictConstants::SHORTCUT_BUFFERS_INDEX =
- BIGRAM_BUFFERS_INDEX + NUM_OF_BUFFERS_FOR_SPARSE_TABLE_DICT_CONTENT;
+ LANGUAGE_MODEL_BUFFER_INDEX + NUM_OF_BUFFERS_FOR_LANGUAGE_MODEL_DICT_CONTENT;
const int Ver4DictConstants::NOT_A_TERMINAL_ID = -1;
const int Ver4DictConstants::PROBABILITY_SIZE = 1;
@@ -56,21 +54,9 @@ const int Ver4DictConstants::WORD_COUNT_FIELD_SIZE = 1;
const uint8_t Ver4DictConstants::FLAG_REPRESENTS_BEGINNING_OF_SENTENCE = 0x1;
-const int Ver4DictConstants::BIGRAM_ADDRESS_TABLE_BLOCK_SIZE = 16;
-const int Ver4DictConstants::BIGRAM_ADDRESS_TABLE_DATA_SIZE = 4;
const int Ver4DictConstants::SHORTCUT_ADDRESS_TABLE_BLOCK_SIZE = 64;
const int Ver4DictConstants::SHORTCUT_ADDRESS_TABLE_DATA_SIZE = 4;
-const int Ver4DictConstants::BIGRAM_TARGET_TERMINAL_ID_FIELD_SIZE = 3;
-// Unsigned int max value of BIGRAM_TARGET_TERMINAL_ID_FIELD_SIZE-byte is used for representing
-// invalid terminal ID in bigram lists.
-const int Ver4DictConstants::INVALID_BIGRAM_TARGET_TERMINAL_ID =
- (1 << (BIGRAM_TARGET_TERMINAL_ID_FIELD_SIZE * 8)) - 1;
-const int Ver4DictConstants::BIGRAM_FLAGS_FIELD_SIZE = 1;
-const int Ver4DictConstants::BIGRAM_PROBABILITY_MASK = 0x0F;
-const int Ver4DictConstants::BIGRAM_IS_LINK_MASK = 0x80;
-const int Ver4DictConstants::BIGRAM_LARGE_PROBABILITY_FIELD_SIZE = 1;
-
const int Ver4DictConstants::SHORTCUT_FLAGS_FIELD_SIZE = 1;
const int Ver4DictConstants::SHORTCUT_PROBABILITY_MASK = 0x0F;
const int Ver4DictConstants::SHORTCUT_HAS_NEXT_MASK = 0x80;
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h
index 230b3052d..97035311e 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h
@@ -52,19 +52,9 @@ class Ver4DictConstants {
// Flags in probability entry.
static const uint8_t FLAG_REPRESENTS_BEGINNING_OF_SENTENCE;
- static const int BIGRAM_ADDRESS_TABLE_BLOCK_SIZE;
- static const int BIGRAM_ADDRESS_TABLE_DATA_SIZE;
static const int SHORTCUT_ADDRESS_TABLE_BLOCK_SIZE;
static const int SHORTCUT_ADDRESS_TABLE_DATA_SIZE;
- static const int BIGRAM_FLAGS_FIELD_SIZE;
- static const int BIGRAM_TARGET_TERMINAL_ID_FIELD_SIZE;
- static const int INVALID_BIGRAM_TARGET_TERMINAL_ID;
- static const int BIGRAM_IS_LINK_MASK;
- static const int BIGRAM_PROBABILITY_MASK;
- // Used when bigram list has time stamp.
- static const int BIGRAM_LARGE_PROBABILITY_FIELD_SIZE;
-
static const int SHORTCUT_FLAGS_FIELD_SIZE;
static const int SHORTCUT_PROBABILITY_MASK;
static const int SHORTCUT_HAS_NEXT_MASK;
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.cpp
index b7c31bf75..9ca712470 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.cpp
@@ -21,7 +21,6 @@
#include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_utils.h"
#include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_writing_utils.h"
#include "suggest/policyimpl/dictionary/structure/pt_common/patricia_trie_reading_utils.h"
-#include "suggest/policyimpl/dictionary/structure/v4/bigram/ver4_bigram_list_policy.h"
#include "suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h"
#include "suggest/policyimpl/dictionary/structure/v4/shortcut/ver4_shortcut_list_policy.h"
#include "suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.h"
@@ -230,12 +229,6 @@ bool Ver4PatriciaTrieNodeWriter::addNgramEntry(const WordIdArrayView prevWordIds
if (!probabilityEntry.isValid() && outAddedNewBigram) {
*outAddedNewBigram = true;
}
- // TODO: Remove.
- if (!mBigramPolicy->addNewEntry(prevWordIds[0], wordId, bigramProperty, outAddedNewBigram)) {
- AKLOGE("Cannot add new bigram entry. prevWordId: %d, wordId: %d",
- prevWordIds[0], wordId);
- return false;
- }
return true;
}
@@ -244,19 +237,15 @@ bool Ver4PatriciaTrieNodeWriter::removeNgramEntry(const WordIdArrayView prevWord
// TODO: Support n-gram.
LanguageModelDictContent *const languageModelDictContent =
mBuffers->getMutableLanguageModelDictContent();
- if (!languageModelDictContent->removeNgramProbabilityEntry(prevWordIds.limit(1 /* maxSize */),
- wordId)) {
- // TODO: Uncomment.
- // return false;
- }
- // TODO: Remove.
- return mBigramPolicy->removeEntry(prevWordIds[0], wordId);
+ return languageModelDictContent->removeNgramProbabilityEntry(prevWordIds.limit(1 /* maxSize */),
+ wordId);
}
+// TODO: Remove when we stop supporting v402 format.
bool Ver4PatriciaTrieNodeWriter::updateAllBigramEntriesAndDeleteUselessEntries(
const PtNodeParams *const sourcePtNodeParams, int *const outBigramEntryCount) {
- return mBigramPolicy->updateAllBigramEntriesAndDeleteUselessEntries(
- sourcePtNodeParams->getTerminalId(), outBigramEntryCount);
+ // Do nothing.
+ return true;
}
bool Ver4PatriciaTrieNodeWriter::updateAllPositionFields(
@@ -291,12 +280,6 @@ bool Ver4PatriciaTrieNodeWriter::updateAllPositionFields(
if (!updateChildrenPosition(toBeUpdatedPtNodeParams, childrenPos)) {
return false;
}
-
- // Counts bigram entries.
- if (outBigramEntryCount) {
- *outBigramEntryCount = mBigramPolicy->getBigramEntryConut(
- toBeUpdatedPtNodeParams->getTerminalId());
- }
return true;
}
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.h
index 5d73b6ea3..08b7d3825 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_writer.h
@@ -27,7 +27,6 @@ namespace latinime {
class BufferWithExtendableBuffer;
class HeaderPolicy;
-class Ver4BigramListPolicy;
class Ver4DictBuffers;
class Ver4PatriciaTrieNodeReader;
class Ver4PtNodeArrayReader;
@@ -42,10 +41,9 @@ class Ver4PatriciaTrieNodeWriter : public PtNodeWriter {
Ver4DictBuffers *const buffers, const HeaderPolicy *const headerPolicy,
const PtNodeReader *const ptNodeReader,
const PtNodeArrayReader *const ptNodeArrayReader,
- Ver4BigramListPolicy *const bigramPolicy, Ver4ShortcutListPolicy *const shortcutPolicy)
+ Ver4ShortcutListPolicy *const shortcutPolicy)
: mTrieBuffer(trieBuffer), mBuffers(buffers), mHeaderPolicy(headerPolicy),
- mReadingHelper(ptNodeReader, ptNodeArrayReader), mBigramPolicy(bigramPolicy),
- mShortcutPolicy(shortcutPolicy) {}
+ mReadingHelper(ptNodeReader, ptNodeArrayReader), mShortcutPolicy(shortcutPolicy) {}
virtual ~Ver4PatriciaTrieNodeWriter() {}
@@ -114,7 +112,6 @@ class Ver4PatriciaTrieNodeWriter : public PtNodeWriter {
Ver4DictBuffers *const mBuffers;
const HeaderPolicy *const mHeaderPolicy;
DynamicPtReadingHelper mReadingHelper;
- Ver4BigramListPolicy *const mBigramPolicy;
Ver4ShortcutListPolicy *const mShortcutPolicy;
};
} // namespace latinime
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 5eb2d3fe8..ae3208cfe 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
@@ -159,11 +159,21 @@ void Ver4PatriciaTriePolicy::iterateNgramEntries(const int *const prevWordsPtNod
if (!prevWordsPtNodePos) {
return;
}
- const int bigramsPosition = getBigramsPositionOfPtNode(prevWordsPtNodePos[0]);
- BinaryDictionaryBigramsIterator bigramsIt(&mBigramPolicy, bigramsPosition);
- while (bigramsIt.hasNext()) {
- bigramsIt.next();
- listener->onVisitEntry(bigramsIt.getProbability(), bigramsIt.getBigramPos());
+ // TODO: Support n-gram.
+ const PtNodeParams ptNodeParams =
+ mNodeReader.fetchPtNodeParamsInBufferFromPtNodePos(prevWordsPtNodePos[0]);
+ const int prevWordId = ptNodeParams.getTerminalId();
+ const WordIdArrayView prevWordIds = WordIdArrayView::fromObject(&prevWordId);
+ const auto languageModelDictContent = mBuffers->getLanguageModelDictContent();
+ for (const auto entry : languageModelDictContent->getProbabilityEntries(prevWordIds)) {
+ const ProbabilityEntry &probabilityEntry = entry.getProbabilityEntry();
+ const int probability = probabilityEntry.hasHistoricalInfo() ?
+ ForgettingCurveUtils::decodeProbability(
+ probabilityEntry.getHistoricalInfo(), mHeaderPolicy) :
+ probabilityEntry.getProbability();
+ const int ptNodePos = mBuffers->getTerminalPositionLookupTable()->getTerminalPtNodePosition(
+ entry.getWordId());
+ listener->onVisitEntry(probability, ptNodePos);
}
}
@@ -179,18 +189,6 @@ int Ver4PatriciaTriePolicy::getShortcutPositionOfPtNode(const int ptNodePos) con
ptNodeParams.getTerminalId());
}
-int Ver4PatriciaTriePolicy::getBigramsPositionOfPtNode(const int ptNodePos) const {
- if (ptNodePos == NOT_A_DICT_POS) {
- return NOT_A_DICT_POS;
- }
- const PtNodeParams ptNodeParams(mNodeReader.fetchPtNodeParamsInBufferFromPtNodePos(ptNodePos));
- if (ptNodeParams.isDeleted()) {
- return NOT_A_DICT_POS;
- }
- return mBuffers->getBigramDictContent()->getBigramListHeadPos(
- ptNodeParams.getTerminalId());
-}
-
bool Ver4PatriciaTriePolicy::addUnigramEntry(const int *const word, const int length,
const UnigramProperty *const unigramProperty) {
if (!mBuffers->isUpdatable()) {
@@ -471,41 +469,32 @@ const WordProperty Ver4PatriciaTriePolicy::getWordProperty(const int *const code
ptNodeParams.getTerminalId());
const HistoricalInfo *const historicalInfo = probabilityEntry.getHistoricalInfo();
// Fetch bigram information.
+ // TODO: Support n-gram.
std::vector<BigramProperty> bigrams;
- const int bigramListPos = getBigramsPositionOfPtNode(ptNodePos);
- if (bigramListPos != NOT_A_DICT_POS) {
- int bigramWord1CodePoints[MAX_WORD_LENGTH];
- const BigramDictContent *const bigramDictContent = mBuffers->getBigramDictContent();
- const TerminalPositionLookupTable *const terminalPositionLookupTable =
- mBuffers->getTerminalPositionLookupTable();
- bool hasNext = true;
- int readingPos = bigramListPos;
- while (hasNext) {
- const BigramEntry bigramEntry =
- bigramDictContent->getBigramEntryAndAdvancePosition(&readingPos);
- hasNext = bigramEntry.hasNext();
- const int word1TerminalId = bigramEntry.getTargetTerminalId();
- const int word1TerminalPtNodePos =
- terminalPositionLookupTable->getTerminalPtNodePosition(word1TerminalId);
- if (word1TerminalPtNodePos == NOT_A_DICT_POS) {
- continue;
- }
- // Word (unigram) probability
- int word1Probability = NOT_A_PROBABILITY;
- const int codePointCount = getCodePointsAndProbabilityAndReturnCodePointCount(
- word1TerminalPtNodePos, MAX_WORD_LENGTH, bigramWord1CodePoints,
- &word1Probability);
- const std::vector<int> word1(bigramWord1CodePoints,
- bigramWord1CodePoints + codePointCount);
- const HistoricalInfo *const historicalInfo = bigramEntry.getHistoricalInfo();
- const int probability = bigramEntry.hasHistoricalInfo() ?
- ForgettingCurveUtils::decodeProbability(
- bigramEntry.getHistoricalInfo(), mHeaderPolicy) :
- bigramEntry.getProbability();
- bigrams.emplace_back(&word1, probability,
- historicalInfo->getTimeStamp(), historicalInfo->getLevel(),
- historicalInfo->getCount());
- }
+ const int wordId = ptNodeParams.getTerminalId();
+ const WordIdArrayView prevWordIds = WordIdArrayView::fromObject(&wordId);
+ const TerminalPositionLookupTable *const terminalPositionLookupTable =
+ mBuffers->getTerminalPositionLookupTable();
+ int bigramWord1CodePoints[MAX_WORD_LENGTH];
+ for (const auto entry : mBuffers->getLanguageModelDictContent()->getProbabilityEntries(
+ prevWordIds)) {
+ const int word1TerminalPtNodePos =
+ terminalPositionLookupTable->getTerminalPtNodePosition(entry.getWordId());
+ // Word (unigram) probability
+ int word1Probability = NOT_A_PROBABILITY;
+ const int codePointCount = getCodePointsAndProbabilityAndReturnCodePointCount(
+ word1TerminalPtNodePos, MAX_WORD_LENGTH, bigramWord1CodePoints,
+ &word1Probability);
+ const std::vector<int> word1(bigramWord1CodePoints,
+ bigramWord1CodePoints + codePointCount);
+ const ProbabilityEntry probabilityEntry = entry.getProbabilityEntry();
+ const HistoricalInfo *const historicalInfo = probabilityEntry.getHistoricalInfo();
+ const int probability = probabilityEntry.hasHistoricalInfo() ?
+ ForgettingCurveUtils::decodeProbability(historicalInfo, mHeaderPolicy) :
+ probabilityEntry.getProbability();
+ bigrams.emplace_back(&word1, probability,
+ historicalInfo->getTimeStamp(), historicalInfo->getLevel(),
+ historicalInfo->getCount());
}
// Fetch shortcut information.
std::vector<UnigramProperty::ShortcutProperty> shortcuts;
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 e46803ffe..90e06c7f9 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
@@ -23,7 +23,6 @@
#include "suggest/core/policy/dictionary_structure_with_buffer_policy.h"
#include "suggest/policyimpl/dictionary/header/header_policy.h"
#include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_updating_helper.h"
-#include "suggest/policyimpl/dictionary/structure/v4/bigram/ver4_bigram_list_policy.h"
#include "suggest/policyimpl/dictionary/structure/v4/shortcut/ver4_shortcut_list_policy.h"
#include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h"
#include "suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.h"
@@ -43,14 +42,12 @@ class Ver4PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy {
Ver4PatriciaTriePolicy(Ver4DictBuffers::Ver4DictBuffersPtr buffers)
: mBuffers(std::move(buffers)), mHeaderPolicy(mBuffers->getHeaderPolicy()),
mDictBuffer(mBuffers->getWritableTrieBuffer()),
- mBigramPolicy(mBuffers->getMutableBigramDictContent(),
- mBuffers->getTerminalPositionLookupTable(), mHeaderPolicy),
mShortcutPolicy(mBuffers->getMutableShortcutDictContent(),
mBuffers->getTerminalPositionLookupTable()),
mNodeReader(mDictBuffer, mBuffers->getLanguageModelDictContent(), mHeaderPolicy),
mPtNodeArrayReader(mDictBuffer),
mNodeWriter(mDictBuffer, mBuffers.get(), mHeaderPolicy, &mNodeReader,
- &mPtNodeArrayReader, &mBigramPolicy, &mShortcutPolicy),
+ &mPtNodeArrayReader, &mShortcutPolicy),
mUpdatingHelper(mDictBuffer, &mNodeReader, &mNodeWriter),
mWritingHelper(mBuffers.get()),
mUnigramCount(mHeaderPolicy->getUnigramCount()),
@@ -133,7 +130,6 @@ class Ver4PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy {
const Ver4DictBuffers::Ver4DictBuffersPtr mBuffers;
const HeaderPolicy *const mHeaderPolicy;
BufferWithExtendableBuffer *const mDictBuffer;
- Ver4BigramListPolicy mBigramPolicy;
Ver4ShortcutListPolicy mShortcutPolicy;
Ver4PatriciaTrieNodeReader mNodeReader;
Ver4PtNodeArrayReader mPtNodeArrayReader;
@@ -144,8 +140,6 @@ class Ver4PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy {
int mBigramCount;
std::vector<int> mTerminalPtNodePositionsForIteratingWords;
mutable bool mIsCorrupted;
-
- int getBigramsPositionOfPtNode(const int ptNodePos) const;
};
} // namespace latinime
#endif // LATINIME_VER4_PATRICIA_TRIE_POLICY_H
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
index d53575aa7..63e43a544 100644
--- 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
@@ -20,7 +20,6 @@
#include <queue>
#include "suggest/policyimpl/dictionary/header/header_policy.h"
-#include "suggest/policyimpl/dictionary/structure/v4/bigram/ver4_bigram_list_policy.h"
#include "suggest/policyimpl/dictionary/structure/v4/shortcut/ver4_shortcut_list_policy.h"
#include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h"
#include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h"
@@ -77,13 +76,10 @@ bool Ver4PatriciaTrieWritingHelper::runGC(const int rootPtNodeArrayPos,
Ver4PatriciaTrieNodeReader ptNodeReader(mBuffers->getTrieBuffer(),
mBuffers->getLanguageModelDictContent(), headerPolicy);
Ver4PtNodeArrayReader ptNodeArrayReader(mBuffers->getTrieBuffer());
- Ver4BigramListPolicy bigramPolicy(mBuffers->getMutableBigramDictContent(),
- mBuffers->getTerminalPositionLookupTable(), headerPolicy);
Ver4ShortcutListPolicy shortcutPolicy(mBuffers->getMutableShortcutDictContent(),
mBuffers->getTerminalPositionLookupTable());
Ver4PatriciaTrieNodeWriter ptNodeWriter(mBuffers->getWritableTrieBuffer(),
- mBuffers, headerPolicy, &ptNodeReader, &ptNodeArrayReader, &bigramPolicy,
- &shortcutPolicy);
+ mBuffers, headerPolicy, &ptNodeReader, &ptNodeArrayReader, &shortcutPolicy);
int entryCountTable[MAX_PREV_WORD_COUNT_FOR_N_GRAM + 1];
if (!mBuffers->getMutableLanguageModelDictContent()->updateAllProbabilityEntries(headerPolicy,
@@ -93,14 +89,16 @@ bool Ver4PatriciaTrieWritingHelper::runGC(const int rootPtNodeArrayPos,
}
if (headerPolicy->isDecayingDict()) {
int maxEntryCountTable[MAX_PREV_WORD_COUNT_FOR_N_GRAM + 1];
- maxEntryCountTable[0] = headerPolicy->getMaxUnigramCount();
- maxEntryCountTable[1] = headerPolicy->getMaxBigramCount();
+ maxEntryCountTable[LanguageModelDictContent::UNIGRAM_COUNT_INDEX_IN_ENTRY_COUNT_TABLE] =
+ headerPolicy->getMaxUnigramCount();
+ maxEntryCountTable[LanguageModelDictContent::BIGRAM_COUNT_INDEX_IN_ENTRY_COUNT_TABLE] =
+ headerPolicy->getMaxBigramCount();
for (size_t i = 2; i < NELEMS(maxEntryCountTable); ++i) {
// TODO: Have max n-gram count.
maxEntryCountTable[i] = headerPolicy->getMaxBigramCount();
}
if (!mBuffers->getMutableLanguageModelDictContent()->truncateEntries(entryCountTable,
- maxEntryCountTable, headerPolicy)) {
+ maxEntryCountTable, headerPolicy, entryCountTable)) {
AKLOGE("Failed to truncate entries in language model dict content.");
return false;
}
@@ -116,16 +114,6 @@ bool Ver4PatriciaTrieWritingHelper::runGC(const int rootPtNodeArrayPos,
&traversePolicyToUpdateUnigramProbabilityAndMarkUselessPtNodesAsDeleted)) {
return false;
}
- const int unigramCount = traversePolicyToUpdateUnigramProbabilityAndMarkUselessPtNodesAsDeleted
- .getValidUnigramCount();
- const int maxUnigramCount = headerPolicy->getMaxUnigramCount();
- if (headerPolicy->isDecayingDict() && unigramCount > maxUnigramCount) {
- if (!truncateUnigrams(&ptNodeReader, &ptNodeWriter, maxUnigramCount)) {
- AKLOGE("Cannot remove unigrams. current: %d, max: %d", unigramCount,
- maxUnigramCount);
- return false;
- }
- }
readingHelper.initWithPtNodeArrayPos(rootPtNodeArrayPos);
DynamicPtGcEventListeners::TraversePolicyToUpdateBigramProbability
@@ -134,21 +122,12 @@ bool Ver4PatriciaTrieWritingHelper::runGC(const int rootPtNodeArrayPos,
&traversePolicyToUpdateBigramProbability)) {
return false;
}
- const int bigramCount = traversePolicyToUpdateBigramProbability.getValidBigramEntryCount();
- const int maxBigramCount = headerPolicy->getMaxBigramCount();
- if (headerPolicy->isDecayingDict() && bigramCount > maxBigramCount) {
- if (!truncateBigrams(maxBigramCount)) {
- AKLOGE("Cannot remove bigrams. current: %d, max: %d", bigramCount, maxBigramCount);
- return false;
- }
- }
// Mapping from positions in mBuffer to positions in bufferToWrite.
PtNodeWriter::DictPositionRelocationMap dictPositionRelocationMap;
readingHelper.initWithPtNodeArrayPos(rootPtNodeArrayPos);
Ver4PatriciaTrieNodeWriter ptNodeWriterForNewBuffers(buffersToWrite->getWritableTrieBuffer(),
- buffersToWrite, headerPolicy, &ptNodeReader, &ptNodeArrayReader, &bigramPolicy,
- &shortcutPolicy);
+ buffersToWrite, headerPolicy, &ptNodeReader, &ptNodeArrayReader, &shortcutPolicy);
DynamicPtGcEventListeners::TraversePolicyToPlaceAndWriteValidPtNodesToBuffer
traversePolicyToPlaceAndWriteValidPtNodesToBuffer(&ptNodeWriterForNewBuffers,
buffersToWrite->getWritableTrieBuffer(), &dictPositionRelocationMap);
@@ -161,12 +140,10 @@ bool Ver4PatriciaTrieWritingHelper::runGC(const int rootPtNodeArrayPos,
Ver4PatriciaTrieNodeReader newPtNodeReader(buffersToWrite->getTrieBuffer(),
buffersToWrite->getLanguageModelDictContent(), headerPolicy);
Ver4PtNodeArrayReader newPtNodeArrayreader(buffersToWrite->getTrieBuffer());
- Ver4BigramListPolicy newBigramPolicy(buffersToWrite->getMutableBigramDictContent(),
- buffersToWrite->getTerminalPositionLookupTable(), headerPolicy);
Ver4ShortcutListPolicy newShortcutPolicy(buffersToWrite->getMutableShortcutDictContent(),
buffersToWrite->getTerminalPositionLookupTable());
Ver4PatriciaTrieNodeWriter newPtNodeWriter(buffersToWrite->getWritableTrieBuffer(),
- buffersToWrite, headerPolicy, &newPtNodeReader, &newPtNodeArrayreader, &newBigramPolicy,
+ buffersToWrite, headerPolicy, &newPtNodeReader, &newPtNodeArrayreader,
&newShortcutPolicy);
// Re-assign terminal IDs for valid terminal PtNodes.
TerminalPositionLookupTable::TerminalIdMap terminalIdMap;
@@ -179,11 +156,6 @@ bool Ver4PatriciaTrieWritingHelper::runGC(const int rootPtNodeArrayPos,
mBuffers->getLanguageModelDictContent(), nullptr /* outNgramCount */)) {
return false;
}
- // Run GC for bigram dict content.
- if(!buffersToWrite->getMutableBigramDictContent()->runGC(&terminalIdMap,
- mBuffers->getBigramDictContent(), outBigramCount)) {
- return false;
- }
// Run GC for shortcut dict content.
if(!buffersToWrite->getMutableShortcutDictContent()->runGC(&terminalIdMap,
mBuffers->getShortcutDictContent())) {
@@ -204,94 +176,10 @@ bool Ver4PatriciaTrieWritingHelper::runGC(const int rootPtNodeArrayPos,
&traversePolicyToUpdateAllPtNodeFlagsAndTerminalIds)) {
return false;
}
- *outUnigramCount = traversePolicyToUpdateAllPositionFields.getUnigramCount();
- return true;
-}
-
-// TODO: Remove.
-bool Ver4PatriciaTrieWritingHelper::truncateUnigrams(
- const Ver4PatriciaTrieNodeReader *const ptNodeReader,
- Ver4PatriciaTrieNodeWriter *const ptNodeWriter, const int maxUnigramCount) {
- const TerminalPositionLookupTable *const terminalPosLookupTable =
- mBuffers->getTerminalPositionLookupTable();
- const int nextTerminalId = terminalPosLookupTable->getNextTerminalId();
- std::priority_queue<DictProbability, std::vector<DictProbability>, DictProbabilityComparator>
- priorityQueue;
- for (int i = 0; i < nextTerminalId; ++i) {
- const int terminalPos = terminalPosLookupTable->getTerminalPtNodePosition(i);
- if (terminalPos == NOT_A_DICT_POS) {
- continue;
- }
- const ProbabilityEntry probabilityEntry =
- mBuffers->getLanguageModelDictContent()->getProbabilityEntry(i);
- const int probability = probabilityEntry.hasHistoricalInfo() ?
- ForgettingCurveUtils::decodeProbability(
- probabilityEntry.getHistoricalInfo(), mBuffers->getHeaderPolicy()) :
- probabilityEntry.getProbability();
- priorityQueue.push(DictProbability(terminalPos, probability,
- probabilityEntry.getHistoricalInfo()->getTimeStamp()));
- }
-
- // Delete unigrams.
- while (static_cast<int>(priorityQueue.size()) > maxUnigramCount) {
- const int ptNodePos = priorityQueue.top().getDictPos();
- priorityQueue.pop();
- const PtNodeParams ptNodeParams =
- ptNodeReader->fetchPtNodeParamsInBufferFromPtNodePos(ptNodePos);
- if (ptNodeParams.representsNonWordInfo()) {
- continue;
- }
- if (!ptNodeWriter->markPtNodeAsWillBecomeNonTerminal(&ptNodeParams)) {
- AKLOGE("Cannot mark PtNode as willBecomeNonterminal. PtNode pos: %d", ptNodePos);
- return false;
- }
- }
- return true;
-}
-
-// TODO: Remove.
-bool Ver4PatriciaTrieWritingHelper::truncateBigrams(const int maxBigramCount) {
- const TerminalPositionLookupTable *const terminalPosLookupTable =
- mBuffers->getTerminalPositionLookupTable();
- const int nextTerminalId = terminalPosLookupTable->getNextTerminalId();
- std::priority_queue<DictProbability, std::vector<DictProbability>, DictProbabilityComparator>
- priorityQueue;
- BigramDictContent *const bigramDictContent = mBuffers->getMutableBigramDictContent();
- for (int i = 0; i < nextTerminalId; ++i) {
- const int bigramListPos = bigramDictContent->getBigramListHeadPos(i);
- if (bigramListPos == NOT_A_DICT_POS) {
- continue;
- }
- bool hasNext = true;
- int readingPos = bigramListPos;
- while (hasNext) {
- const BigramEntry bigramEntry =
- bigramDictContent->getBigramEntryAndAdvancePosition(&readingPos);
- const int entryPos = readingPos - bigramDictContent->getBigramEntrySize();
- hasNext = bigramEntry.hasNext();
- if (!bigramEntry.isValid()) {
- continue;
- }
- const int probability = bigramEntry.hasHistoricalInfo() ?
- ForgettingCurveUtils::decodeProbability(
- bigramEntry.getHistoricalInfo(), mBuffers->getHeaderPolicy()) :
- bigramEntry.getProbability();
- priorityQueue.push(DictProbability(entryPos, probability,
- bigramEntry.getHistoricalInfo()->getTimeStamp()));
- }
- }
-
- // Delete bigrams.
- while (static_cast<int>(priorityQueue.size()) > maxBigramCount) {
- const int entryPos = priorityQueue.top().getDictPos();
- const BigramEntry bigramEntry = bigramDictContent->getBigramEntry(entryPos);
- const BigramEntry invalidatedBigramEntry = bigramEntry.getInvalidatedEntry();
- if (!bigramDictContent->writeBigramEntry(&invalidatedBigramEntry, entryPos)) {
- AKLOGE("Cannot write bigram entry to remove. pos: %d", entryPos);
- return false;
- }
- priorityQueue.pop();
- }
+ *outUnigramCount =
+ entryCountTable[LanguageModelDictContent::UNIGRAM_COUNT_INDEX_IN_ENTRY_COUNT_TABLE];
+ *outBigramCount =
+ entryCountTable[LanguageModelDictContent::BIGRAM_COUNT_INDEX_IN_ENTRY_COUNT_TABLE];
return true;
}
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
index bb464ad28..b6278c4cb 100644
--- 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
@@ -66,49 +66,6 @@ class Ver4PatriciaTrieWritingHelper {
const TerminalPositionLookupTable::TerminalIdMap *const mTerminalIdMap;
};
- // For truncateUnigrams() and truncateBigrams().
- class DictProbability {
- public:
- DictProbability(const int dictPos, const int probability, const int timestamp)
- : mDictPos(dictPos), mProbability(probability), mTimestamp(timestamp) {}
-
- int getDictPos() const {
- return mDictPos;
- }
-
- int getProbability() const {
- return mProbability;
- }
-
- int getTimestamp() const {
- return mTimestamp;
- }
-
- private:
- DISALLOW_DEFAULT_CONSTRUCTOR(DictProbability);
-
- int mDictPos;
- int mProbability;
- int mTimestamp;
- };
-
- // For truncateUnigrams() and truncateBigrams().
- class DictProbabilityComparator {
- public:
- bool operator()(const DictProbability &left, const DictProbability &right) {
- if (left.getProbability() != right.getProbability()) {
- return left.getProbability() > right.getProbability();
- }
- if (left.getTimestamp() != right.getTimestamp()) {
- return left.getTimestamp() < right.getTimestamp();
- }
- return left.getDictPos() > right.getDictPos();
- }
-
- private:
- DISALLOW_ASSIGNMENT_OPERATOR(DictProbabilityComparator);
- };
-
bool runGC(const int rootPtNodeArrayPos, const HeaderPolicy *const headerPolicy,
Ver4DictBuffers *const buffersToWrite, int *const outUnigramCount,
int *const outBigramCount);
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/trie_map.h b/native/jni/src/suggest/policyimpl/dictionary/utils/trie_map.h
index c2aeac211..00765888b 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/utils/trie_map.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/trie_map.h
@@ -98,7 +98,7 @@ class TrieMap {
TrieMapIterator(const TrieMap *const trieMap, const int bitmapEntryIndex)
: mTrieMap(trieMap), mStateStack(), mBaseBitmapEntryIndex(bitmapEntryIndex),
mKey(0), mValue(0), mIsValid(false), mNextLevelBitmapEntryIndex(INVALID_INDEX) {
- if (!trieMap) {
+ if (!trieMap || mBaseBitmapEntryIndex == INVALID_INDEX) {
return;
}
const Entry bitmapEntry = mTrieMap->readEntry(mBaseBitmapEntryIndex);
diff --git a/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_test.cpp b/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_test.cpp
index 3cacba1c3..ca8d56f27 100644
--- a/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_test.cpp
+++ b/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_test.cpp
@@ -18,6 +18,8 @@
#include <gtest/gtest.h>
+#include <unordered_set>
+
#include "utils/int_array_view.h"
namespace latinime {
@@ -69,5 +71,23 @@ TEST(LanguageModelDictContentTest, TestUnigramProbabilityWithHistoricalInfo) {
EXPECT_TRUE(LanguageModelDictContent.removeProbabilityEntry(wordId));
}
+TEST(LanguageModelDictContentTest, TestIterateProbabilityEntry) {
+ LanguageModelDictContent languageModelDictContent(false /* useHistoricalInfo */);
+
+ const ProbabilityEntry originalEntry(0xFC, 100);
+
+ const int wordIds[] = { 1, 2, 3, 4, 5 };
+ for (const int wordId : wordIds) {
+ languageModelDictContent.setProbabilityEntry(wordId, &originalEntry);
+ }
+ std::unordered_set<int> wordIdSet(std::begin(wordIds), std::end(wordIds));
+ for (const auto entry : languageModelDictContent.getProbabilityEntries(WordIdArrayView())) {
+ EXPECT_EQ(originalEntry.getFlags(), entry.getProbabilityEntry().getFlags());
+ EXPECT_EQ(originalEntry.getProbability(), entry.getProbabilityEntry().getProbability());
+ wordIdSet.erase(entry.getWordId());
+ }
+ EXPECT_TRUE(wordIdSet.empty());
+}
+
} // namespace
} // namespace latinime
diff --git a/native/jni/tests/utils/time_keeper_test.cpp b/native/jni/tests/utils/time_keeper_test.cpp
new file mode 100644
index 000000000..3f54b91f1
--- /dev/null
+++ b/native/jni/tests/utils/time_keeper_test.cpp
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+#include "utils/time_keeper.h"
+
+#include <gtest/gtest.h>
+
+namespace latinime {
+namespace {
+
+TEST(TimeKeeperTest, TestTestMode) {
+ TimeKeeper::setCurrentTime();
+ const int startTime = TimeKeeper::peekCurrentTime();
+ static const int TEST_CURRENT_TIME = 100;
+ TimeKeeper::startTestModeWithForceCurrentTime(TEST_CURRENT_TIME);
+ EXPECT_EQ(TEST_CURRENT_TIME, TimeKeeper::peekCurrentTime());
+ TimeKeeper::setCurrentTime();
+ EXPECT_EQ(TEST_CURRENT_TIME, TimeKeeper::peekCurrentTime());
+ TimeKeeper::stopTestMode();
+ TimeKeeper::setCurrentTime();
+ EXPECT_LE(startTime, TimeKeeper::peekCurrentTime());
+}
+
+} // namespace
+} // namespace latinime
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java b/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java
index a353e5a35..986a233c1 100644
--- a/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java
+++ b/tests/src/com/android/inputmethod/keyboard/internal/MockKeyboardSwitcher.java
@@ -18,6 +18,7 @@ package com.android.inputmethod.keyboard.internal;
import android.text.TextUtils;
+import com.android.inputmethod.event.Event;
import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.utils.RecapitalizeStatus;
@@ -26,7 +27,7 @@ public class MockKeyboardSwitcher implements KeyboardState.SwitchActions {
// Argument for {@link KeyboardState#onPressKey} and {@link KeyboardState#onReleaseKey}.
public static final boolean NOT_SLIDING = false;
public static final boolean SLIDING = true;
- // Argument for {@link KeyboardState#onCodeInput}.
+ // Argument for {@link KeyboardState#onEvent}.
public static final boolean SINGLE = true;
public static final boolean MULTI = false;
public static final int CAP_MODE_OFF = Constants.TextUtils.CAP_MODE_OFF;
@@ -183,7 +184,11 @@ public class MockKeyboardSwitcher implements KeyboardState.SwitchActions {
} else {
mAutoCapsState = mAutoCapsMode;
}
- mState.onCodeInput(code, mAutoCapsState, RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE);
+ final Event event =
+ Event.createSoftwareKeypressEvent(code /* codePoint */, code /* keyCode */,
+ Constants.NOT_A_COORDINATE, Constants.NOT_A_COORDINATE,
+ false /* isKeyRepeat */);
+ mState.onEvent(event, mAutoCapsState, RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE);
}
public void onFinishSlidingInput() {
diff --git a/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java b/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java
index 2785dec43..c28d08cdb 100644
--- a/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java
+++ b/tests/src/com/android/inputmethod/latin/SuggestedWordsTests.java
@@ -62,6 +62,7 @@ public class SuggestedWordsTests extends AndroidTestCase {
public void testGetSuggestedWordsExcludingTypedWord() {
final String TYPED_WORD = "typed";
final int NUMBER_OF_ADDED_SUGGESTIONS = 5;
+ final int KIND_OF_SECOND_CORRECTION = SuggestedWordInfo.KIND_CORRECTION;
final ArrayList<SuggestedWordInfo> list = new ArrayList<>();
list.add(createTypedWordInfo(TYPED_WORD));
for (int i = 0; i < NUMBER_OF_ADDED_SUGGESTIONS; ++i) {
@@ -73,21 +74,23 @@ public class SuggestedWordsTests extends AndroidTestCase {
false /* typedWordValid */,
false /* willAutoCorrect */,
false /* isObsoleteSuggestions */,
- false /* isPrediction*/,
SuggestedWords.INPUT_STYLE_NONE);
assertEquals(NUMBER_OF_ADDED_SUGGESTIONS + 1, words.size());
assertEquals("typed", words.getWord(0));
assertTrue(words.getInfo(0).isKindOf(SuggestedWordInfo.KIND_TYPED));
assertEquals("0", words.getWord(1));
- assertTrue(words.getInfo(1).isKindOf(SuggestedWordInfo.KIND_CORRECTION));
+ assertTrue(words.getInfo(1).isKindOf(KIND_OF_SECOND_CORRECTION));
assertEquals("4", words.getWord(5));
- assertTrue(words.getInfo(5).isKindOf(SuggestedWordInfo.KIND_CORRECTION));
+ assertTrue(words.getInfo(5).isKindOf(KIND_OF_SECOND_CORRECTION));
- final SuggestedWords wordsWithoutTyped = words.getSuggestedWordsExcludingTypedWord(
- SuggestedWords.INPUT_STYLE_NONE);
+ final SuggestedWords wordsWithoutTyped =
+ words.getSuggestedWordsExcludingTypedWordForRecorrection();
+ // Make sure that the typed word has indeed been excluded, by testing the size of the
+ // suggested words, the string and the kind of the top suggestion, which should match
+ // the string and kind of what we inserted after the typed word.
assertEquals(words.size() - 1, wordsWithoutTyped.size());
assertEquals("0", wordsWithoutTyped.getWord(0));
- assertTrue(wordsWithoutTyped.getInfo(0).isKindOf(SuggestedWordInfo.KIND_CORRECTION));
+ assertTrue(wordsWithoutTyped.getInfo(0).isKindOf(KIND_OF_SECOND_CORRECTION));
}
// Helper for testGetTransformedWordInfo
@@ -133,7 +136,6 @@ public class SuggestedWordsTests extends AndroidTestCase {
false /* typedWordValid */,
false /* willAutoCorrect */,
false /* isObsoleteSuggestions */,
- false /* isPrediction*/,
SuggestedWords.INPUT_STYLE_NONE);
final SuggestedWordInfo typedWord = wordsWithTypedWord.getTypedWordInfoOrNull();
assertNotNull(typedWord);
@@ -141,8 +143,7 @@ public class SuggestedWordsTests extends AndroidTestCase {
// Make sure getTypedWordInfoOrNull() returns null.
final SuggestedWords wordsWithoutTypedWord =
- wordsWithTypedWord.getSuggestedWordsExcludingTypedWord(
- SuggestedWords.INPUT_STYLE_NONE);
+ wordsWithTypedWord.getSuggestedWordsExcludingTypedWordForRecorrection();
assertNull(wordsWithoutTypedWord.getTypedWordInfoOrNull());
// Make sure getTypedWordInfoOrNull() returns null.
diff --git a/tests/src/com/android/inputmethod/latin/settings/SpacingAndPunctuationsTests.java b/tests/src/com/android/inputmethod/latin/settings/SpacingAndPunctuationsTests.java
index 2cc22fae4..eb76032b1 100644
--- a/tests/src/com/android/inputmethod/latin/settings/SpacingAndPunctuationsTests.java
+++ b/tests/src/com/android/inputmethod/latin/settings/SpacingAndPunctuationsTests.java
@@ -429,7 +429,7 @@ public class SpacingAndPunctuationsTests extends AndroidTestCase {
assertFalse("willAutoCorrect", suggestedWords.mWillAutoCorrect);
assertTrue("isPunctuationSuggestions", suggestedWords.isPunctuationSuggestions());
assertFalse("isObsoleteSuggestions", suggestedWords.mIsObsoleteSuggestions);
- assertFalse("isPrediction", suggestedWords.mIsPrediction);
+ assertFalse("isPrediction", suggestedWords.isPrediction());
assertEquals("size", punctuationLabels.length, suggestedWords.size());
for (int index = 0; index < suggestedWords.size(); index++) {
assertEquals("punctuation label at " + index,