aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--java/res/drawable-hdpi/sym_keyboard_info_holo_dark.pngbin0 -> 1577 bytes
-rw-r--r--java/res/drawable-hdpi/sym_keyboard_more_holo_dark.pngbin0 -> 1065 bytes
-rw-r--r--java/res/drawable-mdpi/sym_keyboard_info_holo_dark.pngbin0 -> 1054 bytes
-rw-r--r--java/res/drawable-mdpi/sym_keyboard_more_holo_dark.pngbin0 -> 797 bytes
-rw-r--r--java/res/drawable-xhdpi/sym_keyboard_info_holo_dark.pngbin0 -> 2270 bytes
-rw-r--r--java/res/drawable-xhdpi/sym_keyboard_more_holo_dark.pngbin0 -> 1458 bytes
-rw-r--r--java/res/drawable-xxhdpi/sym_keyboard_info_holo_dark.pngbin0 -> 2971 bytes
-rw-r--r--java/res/drawable-xxhdpi/sym_keyboard_more_holo_dark.pngbin0 -> 1935 bytes
-rw-r--r--java/res/layout/suggestions_strip.xml13
-rw-r--r--java/res/values/strings-config-important-notice.xml27
-rw-r--r--java/src/com/android/inputmethod/keyboard/Key.java22
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/CodesArrayParser.java22
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/EmojiPageKeyboardView.java52
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java426
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyStyle.java6
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java2
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java1
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java67
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java215
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionary.java95
-rw-r--r--java/src/com/android/inputmethod/latin/Constants.java2
-rw-r--r--java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java6
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java37
-rw-r--r--java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java2
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java2
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/FormatSpec.java25
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java2
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java10
-rw-r--r--java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java3
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java27
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java32
-rw-r--r--java/src/com/android/inputmethod/latin/utils/CollectionUtils.java15
-rw-r--r--java/src/com/android/inputmethod/latin/utils/CsvUtils.java7
-rw-r--r--java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java14
-rw-r--r--java/src/com/android/inputmethod/latin/utils/StringUtils.java18
-rw-r--r--java/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtils.java9
-rw-r--r--java/src/com/android/inputmethod/latin/utils/WordProperty.java (renamed from java/src/com/android/inputmethod/latin/utils/UnigramProperty.java)60
-rw-r--r--native/jni/NativeFileList.mk2
-rw-r--r--native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp48
-rw-r--r--native/jni/src/suggest/core/dictionary/dictionary.cpp10
-rw-r--r--native/jni/src/suggest/core/dictionary/dictionary.h11
-rw-r--r--native/jni/src/suggest/core/dictionary/unigram_property.cpp52
-rw-r--r--native/jni/src/suggest/core/dictionary/unigram_property.h87
-rw-r--r--native/jni/src/suggest/core/dictionary/word_property.cpp76
-rw-r--r--native/jni/src/suggest/core/dictionary/word_property.h121
-rw-r--r--native/jni/src/suggest/core/policy/dictionary_structure_with_buffer_policy.h9
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/header/header_policy.cpp24
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h26
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h11
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp64
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.h4
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.cpp14
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.cpp4
-rw-r--r--tests/src/com/android/inputmethod/keyboard/internal/MoreKeySpecSplitTests.java (renamed from tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserSplitTests.java)12
-rw-r--r--tests/src/com/android/inputmethod/keyboard/internal/MoreKeySpecTests.java (renamed from tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTests.java)37
-rw-r--r--tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java10
-rw-r--r--tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java114
-rw-r--r--tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java13
-rw-r--r--tests/src/com/android/inputmethod/latin/makedict/BinaryDictUtils.java10
-rw-r--r--tests/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtilsTests.java6
-rw-r--r--tools/dicttool/Android.mk2
-rw-r--r--tools/dicttool/tests/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtilsTests.java12
-rw-r--r--tools/make-keyboard-text/res/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.tmpl67
63 files changed, 1289 insertions, 776 deletions
diff --git a/java/res/drawable-hdpi/sym_keyboard_info_holo_dark.png b/java/res/drawable-hdpi/sym_keyboard_info_holo_dark.png
new file mode 100644
index 000000000..f52f5af7a
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_info_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_more_holo_dark.png b/java/res/drawable-hdpi/sym_keyboard_more_holo_dark.png
new file mode 100644
index 000000000..72e2b297d
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_more_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_info_holo_dark.png b/java/res/drawable-mdpi/sym_keyboard_info_holo_dark.png
new file mode 100644
index 000000000..fa4e2390f
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_info_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_more_holo_dark.png b/java/res/drawable-mdpi/sym_keyboard_more_holo_dark.png
new file mode 100644
index 000000000..58e27a2df
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_more_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_info_holo_dark.png b/java/res/drawable-xhdpi/sym_keyboard_info_holo_dark.png
new file mode 100644
index 000000000..513892fb9
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_info_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_more_holo_dark.png b/java/res/drawable-xhdpi/sym_keyboard_more_holo_dark.png
new file mode 100644
index 000000000..07e9bee38
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_more_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_info_holo_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_info_holo_dark.png
new file mode 100644
index 000000000..b214d0eca
--- /dev/null
+++ b/java/res/drawable-xxhdpi/sym_keyboard_info_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_more_holo_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_more_holo_dark.png
new file mode 100644
index 000000000..702da2720
--- /dev/null
+++ b/java/res/drawable-xxhdpi/sym_keyboard_more_holo_dark.png
Binary files differ
diff --git a/java/res/layout/suggestions_strip.xml b/java/res/layout/suggestions_strip.xml
index 9c83542a6..5e1019bcd 100644
--- a/java/res/layout/suggestions_strip.xml
+++ b/java/res/layout/suggestions_strip.xml
@@ -45,4 +45,17 @@
android:textAlignment="viewStart"
style="?attr/suggestionWordStyle" />
</LinearLayout>
+ <LinearLayout
+ android:id="@+id/important_notice_strip"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <TextView
+ android:id="@+id/important_notice_title"
+ android:text="@string/important_notice_title"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:textSize="14sp"
+ style="?attr/suggestionWordStyle" />
+ </LinearLayout>
</merge>
diff --git a/java/res/values/strings-config-important-notice.xml b/java/res/values/strings-config-important-notice.xml
new file mode 100644
index 000000000..da206a37c
--- /dev/null
+++ b/java/res/values/strings-config-important-notice.xml
@@ -0,0 +1,27 @@
+<?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>
+ <integer name="config_important_notice_version">0</integer>
+ <!-- The title of the important notice displayed on the suggestion strip. -->
+ <string name="important_notice_title"></string>
+ <!-- The contents of the important notice. -->
+ <string name="important_notice_contents"></string>
+</resources>
diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java
index c8c4d30ef..f7eec6b43 100644
--- a/java/src/com/android/inputmethod/keyboard/Key.java
+++ b/java/src/com/android/inputmethod/keyboard/Key.java
@@ -284,19 +284,19 @@ public class Key implements Comparable<Key> {
int moreKeysColumn = style.getInt(keyAttr,
R.styleable.Keyboard_Key_maxMoreKeysColumn, params.mMaxMoreKeysKeyboardColumn);
int value;
- if ((value = KeySpecParser.getIntValue(moreKeys, MORE_KEYS_AUTO_COLUMN_ORDER, -1)) > 0) {
+ if ((value = MoreKeySpec.getIntValue(moreKeys, MORE_KEYS_AUTO_COLUMN_ORDER, -1)) > 0) {
moreKeysColumn = value & MORE_KEYS_COLUMN_MASK;
}
- if ((value = KeySpecParser.getIntValue(moreKeys, MORE_KEYS_FIXED_COLUMN_ORDER, -1)) > 0) {
+ if ((value = MoreKeySpec.getIntValue(moreKeys, MORE_KEYS_FIXED_COLUMN_ORDER, -1)) > 0) {
moreKeysColumn = MORE_KEYS_FLAGS_FIXED_COLUMN_ORDER | (value & MORE_KEYS_COLUMN_MASK);
}
- if (KeySpecParser.getBooleanValue(moreKeys, MORE_KEYS_HAS_LABELS)) {
+ if (MoreKeySpec.getBooleanValue(moreKeys, MORE_KEYS_HAS_LABELS)) {
moreKeysColumn |= MORE_KEYS_FLAGS_HAS_LABELS;
}
- if (KeySpecParser.getBooleanValue(moreKeys, MORE_KEYS_NEEDS_DIVIDERS)) {
+ if (MoreKeySpec.getBooleanValue(moreKeys, MORE_KEYS_NEEDS_DIVIDERS)) {
moreKeysColumn |= MORE_KEYS_FLAGS_NEEDS_DIVIDERS;
}
- if (KeySpecParser.getBooleanValue(moreKeys, MORE_KEYS_NO_PANEL_AUTO_MORE_KEY)) {
+ if (MoreKeySpec.getBooleanValue(moreKeys, MORE_KEYS_NO_PANEL_AUTO_MORE_KEY)) {
moreKeysColumn |= MORE_KEYS_FLAGS_NO_PANEL_AUTO_MORE_KEY;
}
mMoreKeysColumnAndFlags = moreKeysColumn;
@@ -308,7 +308,7 @@ public class Key implements Comparable<Key> {
additionalMoreKeys = style.getStringArray(keyAttr,
R.styleable.Keyboard_Key_additionalMoreKeys);
}
- moreKeys = KeySpecParser.insertAdditionalMoreKeys(moreKeys, additionalMoreKeys);
+ moreKeys = MoreKeySpec.insertAdditionalMoreKeys(moreKeys, additionalMoreKeys);
if (moreKeys != null) {
actionFlags |= ACTION_FLAGS_ENABLE_LONG_PRESS;
mMoreKeys = new MoreKeySpec[moreKeys.length];
@@ -331,16 +331,16 @@ public class Key implements Comparable<Key> {
// code point nor as a surrogate pair.
mLabel = new StringBuilder().appendCodePoint(code).toString();
} else {
- mLabel = KeySpecParser.toUpperCaseOfStringForLocale(style.getString(keyAttr,
+ mLabel = StringUtils.toUpperCaseOfStringForLocale(style.getString(keyAttr,
R.styleable.Keyboard_Key_keyLabel), needsToUpperCase, locale);
}
if ((mLabelFlags & LABEL_FLAGS_DISABLE_HINT_LABEL) != 0) {
mHintLabel = null;
} else {
- mHintLabel = KeySpecParser.toUpperCaseOfStringForLocale(style.getString(keyAttr,
+ mHintLabel = StringUtils.toUpperCaseOfStringForLocale(style.getString(keyAttr,
R.styleable.Keyboard_Key_keyHintLabel), needsToUpperCase, locale);
}
- String outputText = KeySpecParser.toUpperCaseOfStringForLocale(style.getString(keyAttr,
+ String outputText = StringUtils.toUpperCaseOfStringForLocale(style.getString(keyAttr,
R.styleable.Keyboard_Key_keyOutputText), needsToUpperCase, locale);
// Choose the first letter of the label as primary code if not specified.
if (code == CODE_UNSPECIFIED && TextUtils.isEmpty(outputText)
@@ -367,9 +367,9 @@ public class Key implements Comparable<Key> {
mCode = CODE_OUTPUT_TEXT;
}
} else {
- mCode = KeySpecParser.toUpperCaseOfCodeForLocale(code, needsToUpperCase, locale);
+ mCode = StringUtils.toUpperCaseOfCodeForLocale(code, needsToUpperCase, locale);
}
- final int altCode = KeySpecParser.toUpperCaseOfCodeForLocale(
+ final int altCode = StringUtils.toUpperCaseOfCodeForLocale(
KeySpecParser.parseCode(style.getString(keyAttr,
R.styleable.Keyboard_Key_altCode), params.mCodesSet, CODE_UNSPECIFIED),
needsToUpperCase, locale);
diff --git a/java/src/com/android/inputmethod/keyboard/internal/CodesArrayParser.java b/java/src/com/android/inputmethod/keyboard/internal/CodesArrayParser.java
index 4ccecb2f0..dce7fc57e 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/CodesArrayParser.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/CodesArrayParser.java
@@ -17,6 +17,7 @@
package com.android.inputmethod.keyboard.internal;
import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.latin.utils.StringUtils;
import android.text.TextUtils;
@@ -29,15 +30,16 @@ import android.text.TextUtils;
* marker. An output text may consist of multiple code points separated by comma.
* The format of the codesArray element should be:
* <pre>
- * codePointInHex[,codePoint2InHex]*(|outputTextCodePointInHex[,outputTextCodePoint2InHex]*)?
+ * label1[,label2]*(|outputText1[,outputText2]*(|minSupportSdkVersion)?)?
* </pre>
*/
// TODO: Write unit tests for this class.
public final class CodesArrayParser {
// Constants for parsing.
- private static final char COMMA = ',';
- private static final String VERTICAL_BAR_STRING = "\\|";
- private static final String COMMA_STRING = ",";
+ private static final char COMMA = Constants.CODE_COMMA;
+ private static final String COMMA_REGEX = StringUtils.newSingleCodePointString(COMMA);
+ private static final String VERTICAL_BAR_REGEX = // "\\|"
+ new String(new char[] { Constants.CODE_BACKSLASH, Constants.CODE_VERTICAL_BAR });
private static final int BASE_HEX = 16;
private CodesArrayParser() {
@@ -45,7 +47,7 @@ public final class CodesArrayParser {
}
private static String getLabelSpec(final String codesArraySpec) {
- final String[] strs = codesArraySpec.split(VERTICAL_BAR_STRING, -1);
+ final String[] strs = codesArraySpec.split(VERTICAL_BAR_REGEX, -1);
if (strs.length <= 1) {
return codesArraySpec;
}
@@ -55,7 +57,7 @@ public final class CodesArrayParser {
public static String parseLabel(final String codesArraySpec) {
final String labelSpec = getLabelSpec(codesArraySpec);
final StringBuilder sb = new StringBuilder();
- for (final String codeInHex : labelSpec.split(COMMA_STRING)) {
+ for (final String codeInHex : labelSpec.split(COMMA_REGEX)) {
final int codePoint = Integer.parseInt(codeInHex, BASE_HEX);
sb.appendCodePoint(codePoint);
}
@@ -63,17 +65,15 @@ public final class CodesArrayParser {
}
private static String getCodeSpec(final String codesArraySpec) {
- final String[] strs = codesArraySpec.split(VERTICAL_BAR_STRING, -1);
+ final String[] strs = codesArraySpec.split(VERTICAL_BAR_REGEX, -1);
if (strs.length <= 1) {
return codesArraySpec;
}
return TextUtils.isEmpty(strs[1]) ? strs[0] : strs[1];
}
- // codesArraySpec consists of:
- // <label>|<code0>,<code1>,...|<minSupportSdkVersion>
public static int getMinSupportSdkVersion(final String codesArraySpec) {
- final String[] strs = codesArraySpec.split(VERTICAL_BAR_STRING, -1);
+ final String[] strs = codesArraySpec.split(VERTICAL_BAR_REGEX, -1);
if (strs.length <= 2) {
return 0;
}
@@ -98,7 +98,7 @@ public final class CodesArrayParser {
return null;
}
final StringBuilder sb = new StringBuilder();
- for (final String codeInHex : codeSpec.split(COMMA_STRING)) {
+ for (final String codeInHex : codeSpec.split(COMMA_REGEX)) {
final int codePoint = Integer.parseInt(codeInHex, BASE_HEX);
sb.appendCodePoint(codePoint);
}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/EmojiPageKeyboardView.java b/java/src/com/android/inputmethod/keyboard/internal/EmojiPageKeyboardView.java
index 5c7c6e39d..be7396520 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/EmojiPageKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/EmojiPageKeyboardView.java
@@ -17,6 +17,7 @@
package com.android.inputmethod.keyboard.internal;
import android.content.Context;
+import android.os.Handler;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
@@ -35,16 +36,19 @@ import com.android.inputmethod.latin.R;
// TODO: Implement key popup preview.
public final class EmojiPageKeyboardView extends KeyboardView implements
GestureDetector.OnGestureListener {
+ private static final long KEY_PRESS_DELAY_TIME = 250; // msec
+ private static final long KEY_RELEASE_DELAY_TIME = 30; // msec
+
public interface OnKeyEventListener {
public void onPressKey(Key key);
public void onReleaseKey(Key key);
}
private static final OnKeyEventListener EMPTY_LISTENER = new OnKeyEventListener() {
- @Override
- public void onPressKey(final Key key) {}
- @Override
- public void onReleaseKey(final Key key) {}
+ @Override
+ public void onPressKey(final Key key) {}
+ @Override
+ public void onReleaseKey(final Key key) {}
};
private OnKeyEventListener mListener = EMPTY_LISTENER;
@@ -60,6 +64,7 @@ public final class EmojiPageKeyboardView extends KeyboardView implements
super(context, attrs, defStyle);
mGestureDetector = new GestureDetector(context, this);
mGestureDetector.setIsLongpressEnabled(false /* isLongpressEnabled */);
+ mHandler = new Handler();
}
public void setOnKeyEventListener(final OnKeyEventListener listener) {
@@ -92,6 +97,8 @@ public final class EmojiPageKeyboardView extends KeyboardView implements
// {@link GestureEnabler#OnGestureListener} methods.
private Key mCurrentKey;
+ private Runnable mPendingKeyDown;
+ private final Handler mHandler;
private Key getKey(final MotionEvent e) {
final int index = e.getActionIndex();
@@ -101,6 +108,8 @@ public final class EmojiPageKeyboardView extends KeyboardView implements
}
public void releaseCurrentKey() {
+ mHandler.removeCallbacks(mPendingKeyDown);
+ mPendingKeyDown = null;
final Key currentKey = mCurrentKey;
if (currentKey == null) {
return;
@@ -118,9 +127,17 @@ public final class EmojiPageKeyboardView extends KeyboardView implements
if (key == null) {
return false;
}
- key.onPressed();
- invalidateKey(key);
- mListener.onPressKey(key);
+ // Do not trigger key-down effect right now in case this is actually a fling action.
+ mPendingKeyDown = new Runnable() {
+ @Override
+ public void run() {
+ mPendingKeyDown = null;
+ key.onPressed();
+ invalidateKey(key);
+ mListener.onPressKey(key);
+ }
+ };
+ mHandler.postDelayed(mPendingKeyDown, KEY_PRESS_DELAY_TIME);
return false;
}
@@ -132,13 +149,28 @@ public final class EmojiPageKeyboardView extends KeyboardView implements
@Override
public boolean onSingleTapUp(final MotionEvent e) {
final Key key = getKey(e);
+ final Runnable pendingKeyDown = mPendingKeyDown;
+ final Key currentKey = mCurrentKey;
releaseCurrentKey();
if (key == null) {
return false;
}
- key.onReleased();
- invalidateKey(key);
- mListener.onReleaseKey(key);
+ if (key == currentKey && pendingKeyDown != null) {
+ pendingKeyDown.run();
+ // Trigger key-release event a little later so that a user can see visual feedback.
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ key.onReleased();
+ invalidateKey(key);
+ mListener.onReleaseKey(key);
+ }
+ }, KEY_RELEASE_DELAY_TIME);
+ } else {
+ key.onReleased();
+ invalidateKey(key);
+ mListener.onReleaseKey(key);
+ }
return true;
}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java
index accfaedcb..c51941d32 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java
@@ -19,114 +19,47 @@ package com.android.inputmethod.keyboard.internal;
import static com.android.inputmethod.latin.Constants.CODE_OUTPUT_TEXT;
import static com.android.inputmethod.latin.Constants.CODE_UNSPECIFIED;
-import android.text.TextUtils;
-
import com.android.inputmethod.latin.Constants;
-import com.android.inputmethod.latin.LatinImeLogger;
-import com.android.inputmethod.latin.utils.CollectionUtils;
import com.android.inputmethod.latin.utils.StringUtils;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Locale;
-
/**
- * The string parser of more keys specification.
- * The specification is comma separated texts each of which represents one "more key".
- * The specification might have label or string resource reference in it. These references are
- * expanded before parsing comma.
- * - Label reference should be a string representation of label (!text/label_name)
- * - String resource reference should be a string representation of resource (!text/resource_name)
- * Each "more key" specification is one of the following:
- * - Label optionally followed by keyOutputText or code (keyLabel|keyOutputText).
- * - Icon followed by keyOutputText or code (!icon/icon_name|!code/code_name)
- * - Icon should be a string representation of icon (!icon/icon_name).
- * - Code should be a code point presented by hexadecimal string prefixed with "0x", or a string
- * representation of code (!code/code_name).
+ * The string parser of the key specification.
+ *
+ * Each key specification is one of the following:
+ * - Label optionally followed by keyOutputText (keyLabel|keyOutputText).
+ * - Label optionally followed by code point (keyLabel|!code/code_name).
+ * - Icon followed by keyOutputText (!icon/icon_name|keyOutputText).
+ * - Icon followed by code point (!icon/icon_name|!code/code_name).
+ * Label and keyOutputText are one of the following:
+ * - Literal string.
+ * - Label reference represented by (!text/label_name), see {@link KeyboardTextsSet}.
+ * - String resource reference represented by (!text/resource_name), see {@link KeyboardTextsSet}.
+ * Icon is represented by (!icon/icon_name), see {@link KeyboardIconsSet}.
+ * Code is one of the following:
+ * - Code point presented by hexadecimal string prefixed with "0x"
+ * - Code reference represented by (!code/code_name), see {@link KeyboardCodesSet}.
* Special character, comma ',' backslash '\', and bar '|' can be escaped by '\' character.
- * Note that the '\' is also parsed by XML parser and CSV parser as well.
- * See {@link KeyboardIconsSet} about icon_name.
+ * Note that the '\' is also parsed by XML parser and {@link MoreKeySpec#splitKeySpecs(String)}
+ * as well.
*/
public final class KeySpecParser {
- private static final boolean DEBUG = LatinImeLogger.sDBG;
-
- private static final int MAX_STRING_REFERENCE_INDIRECTION = 10;
-
// Constants for parsing.
- private static final char COMMA = ',';
- private static final char BACKSLASH = '\\';
- private static final char VERTICAL_BAR = '|';
- private static final String PREFIX_TEXT = "!text/";
- static final String PREFIX_ICON = "!icon/";
- private static final String PREFIX_CODE = "!code/";
+ private static final char BACKSLASH = Constants.CODE_BACKSLASH;
+ private static final char VERTICAL_BAR = Constants.CODE_VERTICAL_BAR;
private static final String PREFIX_HEX = "0x";
- private static final String ADDITIONAL_MORE_KEY_MARKER = "%";
private KeySpecParser() {
// Intentional empty constructor for utility class.
}
- /**
- * Split the text containing multiple key specifications separated by commas into an array of
- * key specifications.
- * A key specification can contain a character escaped by the backslash character, including a
- * comma character.
- * Note that an empty key specification will be eliminated from the result array.
- *
- * @param text the text containing multiple key specifications.
- * @return an array of key specification text. Null if the specified <code>text</code> is empty
- * or has no key specifications.
- */
- public static String[] splitKeySpecs(final String text) {
- if (TextUtils.isEmpty(text)) {
- return null;
- }
- final int size = text.length();
- // Optimization for one-letter key specification.
- if (size == 1) {
- return text.charAt(0) == COMMA ? null : new String[] { text };
- }
-
- ArrayList<String> list = null;
- int start = 0;
- // The characters in question in this loop are COMMA and BACKSLASH. These characters never
- // match any high or low surrogate character. So it is OK to iterate through with char
- // index.
- for (int pos = 0; pos < size; pos++) {
- final char c = text.charAt(pos);
- if (c == COMMA) {
- // Skip empty entry.
- if (pos - start > 0) {
- if (list == null) {
- list = CollectionUtils.newArrayList();
- }
- list.add(text.substring(start, pos));
- }
- // Skip comma
- start = pos + 1;
- } else if (c == BACKSLASH) {
- // Skip escape character and escaped character.
- pos++;
- }
- }
- final String remain = (size - start > 0) ? text.substring(start) : null;
- if (list == null) {
- return remain != null ? new String[] { remain } : null;
- }
- if (remain != null) {
- list.add(remain);
- }
- return list.toArray(new String[list.size()]);
+ private static boolean hasIcon(final String keySpec) {
+ return keySpec.startsWith(KeyboardIconsSet.PREFIX_ICON);
}
- private static boolean hasIcon(final String moreKeySpec) {
- return moreKeySpec.startsWith(PREFIX_ICON);
- }
-
- private static boolean hasCode(final String moreKeySpec) {
- final int end = indexOfLabelEnd(moreKeySpec, 0);
- if (end > 0 && end + 1 < moreKeySpec.length() && moreKeySpec.startsWith(
- PREFIX_CODE, end + 1)) {
+ private static boolean hasCode(final String keySpec) {
+ final int end = indexOfLabelEnd(keySpec, 0);
+ if (end > 0 && end + 1 < keySpec.length() && keySpec.startsWith(
+ KeyboardCodesSet.PREFIX_CODE, end + 1)) {
return true;
}
return false;
@@ -151,17 +84,17 @@ public final class KeySpecParser {
return sb.toString();
}
- private static int indexOfLabelEnd(final String moreKeySpec, final int start) {
- if (moreKeySpec.indexOf(BACKSLASH, start) < 0) {
- final int end = moreKeySpec.indexOf(VERTICAL_BAR, start);
+ private static int indexOfLabelEnd(final String keySpec, final int start) {
+ if (keySpec.indexOf(BACKSLASH, start) < 0) {
+ final int end = keySpec.indexOf(VERTICAL_BAR, start);
if (end == 0) {
- throw new KeySpecParserError(VERTICAL_BAR + " at " + start + ": " + moreKeySpec);
+ throw new KeySpecParserError(VERTICAL_BAR + " at " + start + ": " + keySpec);
}
return end;
}
- final int length = moreKeySpec.length();
+ final int length = keySpec.length();
for (int pos = start; pos < length; pos++) {
- final char c = moreKeySpec.charAt(pos);
+ final char c = keySpec.charAt(pos);
if (c == BACKSLASH && pos + 1 < length) {
// Skip escape char
pos++;
@@ -172,63 +105,63 @@ public final class KeySpecParser {
return -1;
}
- public static String getLabel(final String moreKeySpec) {
- if (hasIcon(moreKeySpec)) {
+ public static String getLabel(final String keySpec) {
+ if (hasIcon(keySpec)) {
return null;
}
- final int end = indexOfLabelEnd(moreKeySpec, 0);
- final String label = (end > 0) ? parseEscape(moreKeySpec.substring(0, end))
- : parseEscape(moreKeySpec);
- if (TextUtils.isEmpty(label)) {
- throw new KeySpecParserError("Empty label: " + moreKeySpec);
+ final int end = indexOfLabelEnd(keySpec, 0);
+ final String label = (end > 0) ? parseEscape(keySpec.substring(0, end))
+ : parseEscape(keySpec);
+ if (label.isEmpty()) {
+ throw new KeySpecParserError("Empty label: " + keySpec);
}
return label;
}
- private static String getOutputTextInternal(final String moreKeySpec) {
- final int end = indexOfLabelEnd(moreKeySpec, 0);
+ private static String getOutputTextInternal(final String keySpec) {
+ final int end = indexOfLabelEnd(keySpec, 0);
if (end <= 0) {
return null;
}
- if (indexOfLabelEnd(moreKeySpec, end + 1) >= 0) {
- throw new KeySpecParserError("Multiple " + VERTICAL_BAR + ": " + moreKeySpec);
+ if (indexOfLabelEnd(keySpec, end + 1) >= 0) {
+ throw new KeySpecParserError("Multiple " + VERTICAL_BAR + ": " + keySpec);
}
- return parseEscape(moreKeySpec.substring(end + /* VERTICAL_BAR */1));
+ return parseEscape(keySpec.substring(end + /* VERTICAL_BAR */1));
}
- static String getOutputText(final String moreKeySpec) {
- if (hasCode(moreKeySpec)) {
+ public static String getOutputText(final String keySpec) {
+ if (hasCode(keySpec)) {
return null;
}
- final String outputText = getOutputTextInternal(moreKeySpec);
+ final String outputText = getOutputTextInternal(keySpec);
if (outputText != null) {
if (StringUtils.codePointCount(outputText) == 1) {
// If output text is one code point, it should be treated as a code.
// See {@link #getCode(Resources, String)}.
return null;
}
- if (!TextUtils.isEmpty(outputText)) {
- return outputText;
+ if (outputText.isEmpty()) {
+ throw new KeySpecParserError("Empty outputText: " + keySpec);
}
- throw new KeySpecParserError("Empty outputText: " + moreKeySpec);
+ return outputText;
}
- final String label = getLabel(moreKeySpec);
+ final String label = getLabel(keySpec);
if (label == null) {
- throw new KeySpecParserError("Empty label: " + moreKeySpec);
+ throw new KeySpecParserError("Empty label: " + keySpec);
}
// Code is automatically generated for one letter label. See {@link getCode()}.
return (StringUtils.codePointCount(label) == 1) ? null : label;
}
- static int getCode(final String moreKeySpec, final KeyboardCodesSet codesSet) {
- if (hasCode(moreKeySpec)) {
- final int end = indexOfLabelEnd(moreKeySpec, 0);
- if (indexOfLabelEnd(moreKeySpec, end + 1) >= 0) {
- throw new KeySpecParserError("Multiple " + VERTICAL_BAR + ": " + moreKeySpec);
+ public static int getCode(final String keySpec, final KeyboardCodesSet codesSet) {
+ if (hasCode(keySpec)) {
+ final int end = indexOfLabelEnd(keySpec, 0);
+ if (indexOfLabelEnd(keySpec, end + 1) >= 0) {
+ throw new KeySpecParserError("Multiple " + VERTICAL_BAR + ": " + keySpec);
}
- return parseCode(moreKeySpec.substring(end + 1), codesSet, CODE_UNSPECIFIED);
+ return parseCode(keySpec.substring(end + 1), codesSet, CODE_UNSPECIFIED);
}
- final String outputText = getOutputTextInternal(moreKeySpec);
+ final String outputText = getOutputTextInternal(keySpec);
if (outputText != null) {
// If output text is one code point, it should be treated as a code.
// See {@link #getOutputText(String)}.
@@ -237,7 +170,7 @@ public final class KeySpecParser {
}
return CODE_OUTPUT_TEXT;
}
- final String label = getLabel(moreKeySpec);
+ final String label = getLabel(keySpec);
// Code is automatically generated for one letter label.
if (StringUtils.codePointCount(label) == 1) {
return label.codePointAt(0);
@@ -248,8 +181,8 @@ public final class KeySpecParser {
public static int parseCode(final String text, final KeyboardCodesSet codesSet,
final int defCode) {
if (text == null) return defCode;
- if (text.startsWith(PREFIX_CODE)) {
- return codesSet.getCode(text.substring(PREFIX_CODE.length()));
+ if (text.startsWith(KeyboardCodesSet.PREFIX_CODE)) {
+ return codesSet.getCode(text.substring(KeyboardCodesSet.PREFIX_CODE.length()));
} else if (text.startsWith(PREFIX_HEX)) {
return Integer.parseInt(text.substring(PREFIX_HEX.length()), 16);
} else {
@@ -257,245 +190,22 @@ public final class KeySpecParser {
}
}
- public static int getIconId(final String moreKeySpec) {
- if (moreKeySpec != null && hasIcon(moreKeySpec)) {
- final int end = moreKeySpec.indexOf(VERTICAL_BAR, PREFIX_ICON.length());
- final String name = (end < 0) ? moreKeySpec.substring(PREFIX_ICON.length())
- : moreKeySpec.substring(PREFIX_ICON.length(), end);
+ public static int getIconId(final String keySpec) {
+ if (keySpec != null && hasIcon(keySpec)) {
+ final int end = keySpec.indexOf(
+ VERTICAL_BAR, KeyboardIconsSet.PREFIX_ICON.length());
+ final String name = (end < 0)
+ ? keySpec.substring(KeyboardIconsSet.PREFIX_ICON.length())
+ : keySpec.substring(KeyboardIconsSet.PREFIX_ICON.length(), end);
return KeyboardIconsSet.getIconId(name);
}
return KeyboardIconsSet.ICON_UNDEFINED;
}
- private static <T> ArrayList<T> arrayAsList(final T[] array, final int start, final int end) {
- if (array == null) {
- throw new NullPointerException();
- }
- if (start < 0 || start > end || end > array.length) {
- throw new IllegalArgumentException();
- }
-
- final ArrayList<T> list = CollectionUtils.newArrayList(end - start);
- for (int i = start; i < end; i++) {
- list.add(array[i]);
- }
- return list;
- }
-
- private static final String[] EMPTY_STRING_ARRAY = new String[0];
-
- private static String[] filterOutEmptyString(final String[] array) {
- if (array == null) {
- return EMPTY_STRING_ARRAY;
- }
- ArrayList<String> out = null;
- for (int i = 0; i < array.length; i++) {
- final String entry = array[i];
- if (TextUtils.isEmpty(entry)) {
- if (out == null) {
- out = arrayAsList(array, 0, i);
- }
- } else if (out != null) {
- out.add(entry);
- }
- }
- if (out == null) {
- return array;
- }
- return out.toArray(new String[out.size()]);
- }
-
- public static String[] insertAdditionalMoreKeys(final String[] moreKeySpecs,
- final String[] additionalMoreKeySpecs) {
- final String[] moreKeys = filterOutEmptyString(moreKeySpecs);
- final String[] additionalMoreKeys = filterOutEmptyString(additionalMoreKeySpecs);
- final int moreKeysCount = moreKeys.length;
- final int additionalCount = additionalMoreKeys.length;
- ArrayList<String> out = null;
- int additionalIndex = 0;
- for (int moreKeyIndex = 0; moreKeyIndex < moreKeysCount; moreKeyIndex++) {
- final String moreKeySpec = moreKeys[moreKeyIndex];
- if (moreKeySpec.equals(ADDITIONAL_MORE_KEY_MARKER)) {
- if (additionalIndex < additionalCount) {
- // Replace '%' marker with additional more key specification.
- final String additionalMoreKey = additionalMoreKeys[additionalIndex];
- if (out != null) {
- out.add(additionalMoreKey);
- } else {
- moreKeys[moreKeyIndex] = additionalMoreKey;
- }
- additionalIndex++;
- } else {
- // Filter out excessive '%' marker.
- if (out == null) {
- out = arrayAsList(moreKeys, 0, moreKeyIndex);
- }
- }
- } else {
- if (out != null) {
- out.add(moreKeySpec);
- }
- }
- }
- if (additionalCount > 0 && additionalIndex == 0) {
- // No '%' marker is found in more keys.
- // Insert all additional more keys to the head of more keys.
- if (DEBUG && out != null) {
- throw new RuntimeException("Internal logic error:"
- + " moreKeys=" + Arrays.toString(moreKeys)
- + " additionalMoreKeys=" + Arrays.toString(additionalMoreKeys));
- }
- out = arrayAsList(additionalMoreKeys, additionalIndex, additionalCount);
- for (int i = 0; i < moreKeysCount; i++) {
- out.add(moreKeys[i]);
- }
- } else if (additionalIndex < additionalCount) {
- // The number of '%' markers are less than additional more keys.
- // Append remained additional more keys to the tail of more keys.
- if (DEBUG && out != null) {
- throw new RuntimeException("Internal logic error:"
- + " moreKeys=" + Arrays.toString(moreKeys)
- + " additionalMoreKeys=" + Arrays.toString(additionalMoreKeys));
- }
- out = arrayAsList(moreKeys, 0, moreKeysCount);
- for (int i = additionalIndex; i < additionalCount; i++) {
- out.add(additionalMoreKeys[additionalIndex]);
- }
- }
- if (out == null && moreKeysCount > 0) {
- return moreKeys;
- } else if (out != null && out.size() > 0) {
- return out.toArray(new String[out.size()]);
- } else {
- return null;
- }
- }
-
@SuppressWarnings("serial")
public static final class KeySpecParserError extends RuntimeException {
public KeySpecParserError(final String message) {
super(message);
}
}
-
- public static String resolveTextReference(final String rawText,
- final KeyboardTextsSet textsSet) {
- if (TextUtils.isEmpty(rawText)) {
- return null;
- }
- int level = 0;
- String text = rawText;
- StringBuilder sb;
- do {
- level++;
- if (level >= MAX_STRING_REFERENCE_INDIRECTION) {
- throw new RuntimeException("too many @string/resource indirection: " + text);
- }
-
- final int prefixLen = PREFIX_TEXT.length();
- final int size = text.length();
- if (size < prefixLen) {
- return TextUtils.isEmpty(text) ? null : text;
- }
-
- sb = null;
- for (int pos = 0; pos < size; pos++) {
- final char c = text.charAt(pos);
- if (text.startsWith(PREFIX_TEXT, pos) && textsSet != null) {
- if (sb == null) {
- sb = new StringBuilder(text.substring(0, pos));
- }
- final int end = searchTextNameEnd(text, pos + prefixLen);
- final String name = text.substring(pos + prefixLen, end);
- sb.append(textsSet.getText(name));
- pos = end - 1;
- } else if (c == BACKSLASH) {
- if (sb != null) {
- // Append both escape character and escaped character.
- sb.append(text.substring(pos, Math.min(pos + 2, size)));
- }
- pos++;
- } else if (sb != null) {
- sb.append(c);
- }
- }
-
- if (sb != null) {
- text = sb.toString();
- }
- } while (sb != null);
- return TextUtils.isEmpty(text) ? null : text;
- }
-
- private static int searchTextNameEnd(final String text, final int start) {
- final int size = text.length();
- for (int pos = start; pos < size; pos++) {
- final char c = text.charAt(pos);
- // Label name should be consisted of [a-zA-Z_0-9].
- if ((c >= 'a' && c <= 'z') || c == '_' || (c >= '0' && c <= '9')) {
- continue;
- }
- return pos;
- }
- return size;
- }
-
- public static int getIntValue(final String[] moreKeys, final String key,
- final int defaultValue) {
- if (moreKeys == null) {
- return defaultValue;
- }
- final int keyLen = key.length();
- boolean foundValue = false;
- int value = defaultValue;
- for (int i = 0; i < moreKeys.length; i++) {
- final String moreKeySpec = moreKeys[i];
- if (moreKeySpec == null || !moreKeySpec.startsWith(key)) {
- continue;
- }
- moreKeys[i] = null;
- try {
- if (!foundValue) {
- value = Integer.parseInt(moreKeySpec.substring(keyLen));
- foundValue = true;
- }
- } catch (NumberFormatException e) {
- throw new RuntimeException(
- "integer should follow after " + key + ": " + moreKeySpec);
- }
- }
- return value;
- }
-
- public static boolean getBooleanValue(final String[] moreKeys, final String key) {
- if (moreKeys == null) {
- return false;
- }
- boolean value = false;
- for (int i = 0; i < moreKeys.length; i++) {
- final String moreKeySpec = moreKeys[i];
- if (moreKeySpec == null || !moreKeySpec.equals(key)) {
- continue;
- }
- moreKeys[i] = null;
- value = true;
- }
- return value;
- }
-
- public static int toUpperCaseOfCodeForLocale(final int code, final boolean needsToUpperCase,
- final Locale locale) {
- if (!Constants.isLetterCode(code) || !needsToUpperCase) return code;
- final String text = StringUtils.newSingleCodePointString(code);
- final String casedText = KeySpecParser.toUpperCaseOfStringForLocale(
- text, needsToUpperCase, locale);
- return StringUtils.codePointCount(casedText) == 1
- ? casedText.codePointAt(0) : CODE_UNSPECIFIED;
- }
-
- public static String toUpperCaseOfStringForLocale(final String text,
- final boolean needsToUpperCase, final Locale locale) {
- if (text == null || !needsToUpperCase) return text;
- return text.toUpperCase(locale);
- }
}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyStyle.java b/java/src/com/android/inputmethod/keyboard/internal/KeyStyle.java
index e6a674334..7941ddd41 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyStyle.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyStyle.java
@@ -32,15 +32,15 @@ public abstract class KeyStyle {
protected String parseString(final TypedArray a, final int index) {
if (a.hasValue(index)) {
- return KeySpecParser.resolveTextReference(a.getString(index), mTextsSet);
+ return mTextsSet.resolveTextReference(a.getString(index));
}
return null;
}
protected String[] parseStringArray(final TypedArray a, final int index) {
if (a.hasValue(index)) {
- final String text = KeySpecParser.resolveTextReference(a.getString(index), mTextsSet);
- return KeySpecParser.splitKeySpecs(text);
+ final String text = mTextsSet.resolveTextReference(a.getString(index));
+ return MoreKeySpec.splitKeySpecs(text);
}
return null;
}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java
index 78809d53b..aeb4db557 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardCodesSet.java
@@ -22,6 +22,8 @@ import com.android.inputmethod.latin.utils.CollectionUtils;
import java.util.HashMap;
public final class KeyboardCodesSet {
+ public static final String PREFIX_CODE = "!code/";
+
private static final HashMap<String, int[]> sLanguageToCodesMap = CollectionUtils.newHashMap();
private static final HashMap<String, Integer> sNameToIdMap = CollectionUtils.newHashMap();
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java
index 0ee935f60..da8bf7d69 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardIconsSet.java
@@ -30,6 +30,7 @@ import java.util.HashMap;
public final class KeyboardIconsSet {
private static final String TAG = KeyboardIconsSet.class.getSimpleName();
+ public static final String PREFIX_ICON = "!icon/";
public static final int ICON_UNDEFINED = 0;
private static final int ATTR_UNDEFINED = 0;
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
index 4e8eb3ea7..f8c098893 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
@@ -18,8 +18,10 @@ package com.android.inputmethod.keyboard.internal;
import android.content.Context;
import android.content.res.Resources;
+import android.text.TextUtils;
import com.android.inputmethod.annotations.UsedForTesting;
+import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.utils.CollectionUtils;
import java.util.HashMap;
@@ -45,6 +47,10 @@ import java.util.HashMap;
* KeyboardTextsSet.java
*/
public final class KeyboardTextsSet {
+ public static final String PREFIX_TEXT = "!text/";
+ private static final char BACKSLASH = Constants.CODE_BACKSLASH;
+ private static final int MAX_STRING_REFERENCE_INDIRECTION = 10;
+
// Language to texts map.
private static final HashMap<String, String[]> sLocaleToTextsMap = CollectionUtils.newHashMap();
private static final HashMap<String, Integer> sNameToIdsMap = CollectionUtils.newHashMap();
@@ -87,6 +93,67 @@ public final class KeyboardTextsSet {
return (text == null) ? LANGUAGE_DEFAULT[id] : text;
}
+ private static int searchTextNameEnd(final String text, final int start) {
+ final int size = text.length();
+ for (int pos = start; pos < size; pos++) {
+ final char c = text.charAt(pos);
+ // Label name should be consisted of [a-zA-Z_0-9].
+ if ((c >= 'a' && c <= 'z') || c == '_' || (c >= '0' && c <= '9')) {
+ continue;
+ }
+ return pos;
+ }
+ return size;
+ }
+
+ public String resolveTextReference(final String rawText) {
+ if (TextUtils.isEmpty(rawText)) {
+ return null;
+ }
+ int level = 0;
+ String text = rawText;
+ StringBuilder sb;
+ do {
+ level++;
+ if (level >= MAX_STRING_REFERENCE_INDIRECTION) {
+ throw new RuntimeException("too many @string/resource indirection: " + text);
+ }
+
+ final int prefixLen = PREFIX_TEXT.length();
+ final int size = text.length();
+ if (size < prefixLen) {
+ return TextUtils.isEmpty(text) ? null : text;
+ }
+
+ sb = null;
+ for (int pos = 0; pos < size; pos++) {
+ final char c = text.charAt(pos);
+ if (text.startsWith(PREFIX_TEXT, pos)) {
+ if (sb == null) {
+ sb = new StringBuilder(text.substring(0, pos));
+ }
+ final int end = searchTextNameEnd(text, pos + prefixLen);
+ final String name = text.substring(pos + prefixLen, end);
+ sb.append(getText(name));
+ pos = end - 1;
+ } else if (c == BACKSLASH) {
+ if (sb != null) {
+ // Append both escape character and escaped character.
+ sb.append(text.substring(pos, Math.min(pos + 2, size)));
+ }
+ pos++;
+ } else if (sb != null) {
+ sb.append(c);
+ }
+ }
+
+ if (sb != null) {
+ text = sb.toString();
+ }
+ } while (sb != null);
+ return TextUtils.isEmpty(text) ? null : text;
+ }
+
// These texts' name should be aligned with the @string/<name> in
// values*/strings-action-keys.xml.
private static final String[] RESOURCE_NAMES = {
diff --git a/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java b/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java
index 110936f8f..d3bc0c2b2 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java
@@ -19,10 +19,25 @@ package com.android.inputmethod.keyboard.internal;
import android.text.TextUtils;
import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.latin.LatinImeLogger;
+import com.android.inputmethod.latin.utils.CollectionUtils;
import com.android.inputmethod.latin.utils.StringUtils;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Locale;
+/**
+ * The more key specification object. The more keys are an array of {@link MoreKeySpec}.
+ *
+ * The more keys specification is comma separated "key specification" each of which represents one
+ * "more key".
+ * The key specification might have label or string resource reference in it. These references are
+ * expanded before parsing comma.
+ * Special character, comma ',' backslash '\' can be escaped by '\' character.
+ * Note that the '\' is also parsed by XML parser and {@link MoreKeySpec#splitKeySpecs(String)}
+ * as well.
+ */
public final class MoreKeySpec {
public final int mCode;
public final String mLabel;
@@ -31,9 +46,9 @@ public final class MoreKeySpec {
public MoreKeySpec(final String moreKeySpec, boolean needsToUpperCase, final Locale locale,
final KeyboardCodesSet codesSet) {
- mLabel = KeySpecParser.toUpperCaseOfStringForLocale(
+ mLabel = StringUtils.toUpperCaseOfStringForLocale(
KeySpecParser.getLabel(moreKeySpec), needsToUpperCase, locale);
- final int code = KeySpecParser.toUpperCaseOfCodeForLocale(
+ final int code = StringUtils.toUpperCaseOfCodeForLocale(
KeySpecParser.getCode(moreKeySpec, codesSet), needsToUpperCase, locale);
if (code == Constants.CODE_UNSPECIFIED) {
// Some letter, for example German Eszett (U+00DF: "ß"), has multiple characters
@@ -42,7 +57,7 @@ public final class MoreKeySpec {
mOutputText = mLabel;
} else {
mCode = code;
- mOutputText = KeySpecParser.toUpperCaseOfStringForLocale(
+ mOutputText = StringUtils.toUpperCaseOfStringForLocale(
KeySpecParser.getOutputText(moreKeySpec), needsToUpperCase, locale);
}
mIconId = KeySpecParser.getIconId(moreKeySpec);
@@ -74,7 +89,7 @@ public final class MoreKeySpec {
@Override
public String toString() {
final String label = (mIconId == KeyboardIconsSet.ICON_UNDEFINED ? mLabel
- : KeySpecParser.PREFIX_ICON + KeyboardIconsSet.getIconName(mIconId));
+ : KeyboardIconsSet.PREFIX_ICON + KeyboardIconsSet.getIconName(mIconId));
final String output = (mCode == Constants.CODE_OUTPUT_TEXT ? mOutputText
: Constants.printableCode(mCode));
if (StringUtils.codePointCount(label) == 1 && label.codePointAt(0) == mCode) {
@@ -83,4 +98,196 @@ public final class MoreKeySpec {
return label + "|" + output;
}
}
+
+ private static final boolean DEBUG = LatinImeLogger.sDBG;
+ // Constants for parsing.
+ private static final char COMMA = Constants.CODE_COMMA;
+ private static final char BACKSLASH = Constants.CODE_BACKSLASH;
+ private static final String ADDITIONAL_MORE_KEY_MARKER =
+ StringUtils.newSingleCodePointString(Constants.CODE_PERCENT);
+
+ /**
+ * Split the text containing multiple key specifications separated by commas into an array of
+ * key specifications.
+ * A key specification can contain a character escaped by the backslash character, including a
+ * comma character.
+ * Note that an empty key specification will be eliminated from the result array.
+ *
+ * @param text the text containing multiple key specifications.
+ * @return an array of key specification text. Null if the specified <code>text</code> is empty
+ * or has no key specifications.
+ */
+ public static String[] splitKeySpecs(final String text) {
+ if (TextUtils.isEmpty(text)) {
+ return null;
+ }
+ final int size = text.length();
+ // Optimization for one-letter key specification.
+ if (size == 1) {
+ return text.charAt(0) == COMMA ? null : new String[] { text };
+ }
+
+ ArrayList<String> list = null;
+ int start = 0;
+ // The characters in question in this loop are COMMA and BACKSLASH. These characters never
+ // match any high or low surrogate character. So it is OK to iterate through with char
+ // index.
+ for (int pos = 0; pos < size; pos++) {
+ final char c = text.charAt(pos);
+ if (c == COMMA) {
+ // Skip empty entry.
+ if (pos - start > 0) {
+ if (list == null) {
+ list = CollectionUtils.newArrayList();
+ }
+ list.add(text.substring(start, pos));
+ }
+ // Skip comma
+ start = pos + 1;
+ } else if (c == BACKSLASH) {
+ // Skip escape character and escaped character.
+ pos++;
+ }
+ }
+ final String remain = (size - start > 0) ? text.substring(start) : null;
+ if (list == null) {
+ return remain != null ? new String[] { remain } : null;
+ }
+ if (remain != null) {
+ list.add(remain);
+ }
+ return list.toArray(new String[list.size()]);
+ }
+
+ private static final String[] EMPTY_STRING_ARRAY = new String[0];
+
+ private static String[] filterOutEmptyString(final String[] array) {
+ if (array == null) {
+ return EMPTY_STRING_ARRAY;
+ }
+ ArrayList<String> out = null;
+ for (int i = 0; i < array.length; i++) {
+ final String entry = array[i];
+ if (TextUtils.isEmpty(entry)) {
+ if (out == null) {
+ out = CollectionUtils.arrayAsList(array, 0, i);
+ }
+ } else if (out != null) {
+ out.add(entry);
+ }
+ }
+ if (out == null) {
+ return array;
+ }
+ return out.toArray(new String[out.size()]);
+ }
+
+ public static String[] insertAdditionalMoreKeys(final String[] moreKeySpecs,
+ final String[] additionalMoreKeySpecs) {
+ final String[] moreKeys = filterOutEmptyString(moreKeySpecs);
+ final String[] additionalMoreKeys = filterOutEmptyString(additionalMoreKeySpecs);
+ final int moreKeysCount = moreKeys.length;
+ final int additionalCount = additionalMoreKeys.length;
+ ArrayList<String> out = null;
+ int additionalIndex = 0;
+ for (int moreKeyIndex = 0; moreKeyIndex < moreKeysCount; moreKeyIndex++) {
+ final String moreKeySpec = moreKeys[moreKeyIndex];
+ if (moreKeySpec.equals(ADDITIONAL_MORE_KEY_MARKER)) {
+ if (additionalIndex < additionalCount) {
+ // Replace '%' marker with additional more key specification.
+ final String additionalMoreKey = additionalMoreKeys[additionalIndex];
+ if (out != null) {
+ out.add(additionalMoreKey);
+ } else {
+ moreKeys[moreKeyIndex] = additionalMoreKey;
+ }
+ additionalIndex++;
+ } else {
+ // Filter out excessive '%' marker.
+ if (out == null) {
+ out = CollectionUtils.arrayAsList(moreKeys, 0, moreKeyIndex);
+ }
+ }
+ } else {
+ if (out != null) {
+ out.add(moreKeySpec);
+ }
+ }
+ }
+ if (additionalCount > 0 && additionalIndex == 0) {
+ // No '%' marker is found in more keys.
+ // Insert all additional more keys to the head of more keys.
+ if (DEBUG && out != null) {
+ throw new RuntimeException("Internal logic error:"
+ + " moreKeys=" + Arrays.toString(moreKeys)
+ + " additionalMoreKeys=" + Arrays.toString(additionalMoreKeys));
+ }
+ out = CollectionUtils.arrayAsList(additionalMoreKeys, additionalIndex, additionalCount);
+ for (int i = 0; i < moreKeysCount; i++) {
+ out.add(moreKeys[i]);
+ }
+ } else if (additionalIndex < additionalCount) {
+ // The number of '%' markers are less than additional more keys.
+ // Append remained additional more keys to the tail of more keys.
+ if (DEBUG && out != null) {
+ throw new RuntimeException("Internal logic error:"
+ + " moreKeys=" + Arrays.toString(moreKeys)
+ + " additionalMoreKeys=" + Arrays.toString(additionalMoreKeys));
+ }
+ out = CollectionUtils.arrayAsList(moreKeys, 0, moreKeysCount);
+ for (int i = additionalIndex; i < additionalCount; i++) {
+ out.add(additionalMoreKeys[additionalIndex]);
+ }
+ }
+ if (out == null && moreKeysCount > 0) {
+ return moreKeys;
+ } else if (out != null && out.size() > 0) {
+ return out.toArray(new String[out.size()]);
+ } else {
+ return null;
+ }
+ }
+
+ public static int getIntValue(final String[] moreKeys, final String key,
+ final int defaultValue) {
+ if (moreKeys == null) {
+ return defaultValue;
+ }
+ final int keyLen = key.length();
+ boolean foundValue = false;
+ int value = defaultValue;
+ for (int i = 0; i < moreKeys.length; i++) {
+ final String moreKeySpec = moreKeys[i];
+ if (moreKeySpec == null || !moreKeySpec.startsWith(key)) {
+ continue;
+ }
+ moreKeys[i] = null;
+ try {
+ if (!foundValue) {
+ value = Integer.parseInt(moreKeySpec.substring(keyLen));
+ foundValue = true;
+ }
+ } catch (NumberFormatException e) {
+ throw new RuntimeException(
+ "integer should follow after " + key + ": " + moreKeySpec);
+ }
+ }
+ return value;
+ }
+
+ public static boolean getBooleanValue(final String[] moreKeys, final String key) {
+ if (moreKeys == null) {
+ return false;
+ }
+ boolean value = false;
+ for (int i = 0; i < moreKeys.length; i++) {
+ final String moreKeySpec = moreKeys[i];
+ if (moreKeySpec == null || !moreKeySpec.equals(key)) {
+ continue;
+ }
+ moreKeys[i] = null;
+ value = true;
+ }
+ return value;
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index e66cfca49..00eb57c9f 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -22,12 +22,13 @@ import android.util.SparseArray;
import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.keyboard.ProximityInfo;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+import com.android.inputmethod.latin.makedict.Word;
import com.android.inputmethod.latin.settings.NativeSuggestOptions;
import com.android.inputmethod.latin.utils.CollectionUtils;
import com.android.inputmethod.latin.utils.JniUtils;
import com.android.inputmethod.latin.utils.LanguageModelParam;
import com.android.inputmethod.latin.utils.StringUtils;
-import com.android.inputmethod.latin.utils.UnigramProperty;
+import com.android.inputmethod.latin.utils.WordProperty;
import java.io.File;
import java.util.ArrayList;
@@ -61,18 +62,19 @@ public final class BinaryDictionary extends Dictionary {
public static final int NOT_A_VALID_TIMESTAMP = -1;
- // Format to get unigram flags from native side via getUnigramPropertyNative().
- private static final int FORMAT_UNIGRAM_PROPERTY_OUTPUT_FLAG_COUNT = 4;
- private static final int FORMAT_UNIGRAM_PROPERTY_IS_NOT_A_WORD_INDEX = 0;
- private static final int FORMAT_UNIGRAM_PROPERTY_IS_BLACKLISTED_INDEX = 1;
- private static final int FORMAT_UNIGRAM_PROPERTY_HAS_BIGRAMS_INDEX = 2;
- private static final int FORMAT_UNIGRAM_PROPERTY_HAS_SHORTCUTS_INDEX = 3;
+ // Format to get unigram flags from native side via getWordPropertyNative().
+ private static final int FORMAT_WORD_PROPERTY_OUTPUT_FLAG_COUNT = 4;
+ private static final int FORMAT_WORD_PROPERTY_IS_NOT_A_WORD_INDEX = 0;
+ private static final int FORMAT_WORD_PROPERTY_IS_BLACKLISTED_INDEX = 1;
+ private static final int FORMAT_WORD_PROPERTY_HAS_BIGRAMS_INDEX = 2;
+ private static final int FORMAT_WORD_PROPERTY_HAS_SHORTCUTS_INDEX = 3;
- // Format to get unigram historical info from native side via getUnigramPropertyNative().
- private static final int FORMAT_UNIGRAM_PROPERTY_OUTPUT_HISTORICAL_INFO_COUNT = 3;
- private static final int FORMAT_UNIGRAM_PROPERTY_TIMESTAMP_INDEX = 0;
- private static final int FORMAT_UNIGRAM_PROPERTY_LEVEL_INDEX = 1;
- private static final int FORMAT_UNIGRAM_PROPERTY_COUNT_INDEX = 2;
+ // Format to get probability and historical info from native side via getWordPropertyNative().
+ public static final int FORMAT_WORD_PROPERTY_OUTPUT_PROBABILITY_INFO_COUNT = 4;
+ public static final int FORMAT_WORD_PROPERTY_PROBABILITY_INDEX = 0;
+ public static final int FORMAT_WORD_PROPERTY_TIMESTAMP_INDEX = 1;
+ public static final int FORMAT_WORD_PROPERTY_LEVEL_INDEX = 2;
+ public static final int FORMAT_WORD_PROPERTY_COUNT_INDEX = 3;
private long mNativeDict;
private final Locale mLocale;
@@ -143,10 +145,11 @@ public final class BinaryDictionary extends Dictionary {
private static native int getFormatVersionNative(long dict);
private static native int getProbabilityNative(long dict, int[] word);
private static native int getBigramProbabilityNative(long dict, int[] word0, int[] word1);
- private static native void getUnigramPropertyNative(long dict, int[] word,
- int[] outCodePoints, boolean[] outFlags, int[] outProbability,
- int[] outHistoricalInfo, ArrayList<int[]> outShortcutTargets,
- ArrayList<Integer> outShortcutProbabilities);
+ private static native void getWordPropertyNative(long dict, int[] word,
+ int[] outCodePoints, boolean[] outFlags, int[] outProbabilityInfo,
+ ArrayList<int[]> outBigramTargets, ArrayList<int[]> outBigramProbabilityInfo,
+ ArrayList<int[]> outShortcutTargets, ArrayList<Integer> outShortcutProbabilities);
+ private static native int getNextWordNative(long dict, int token, int[] outCodePoints);
private static native int getSuggestionsNative(long dict, long proximityInfo,
long traverseSession, int[] xCoordinates, int[] yCoordinates, int[] times,
int[] pointerIds, int[] inputCodePoints, int inputSize, int commitPoint,
@@ -306,29 +309,56 @@ public final class BinaryDictionary extends Dictionary {
}
@UsedForTesting
- public UnigramProperty getUnigramProperty(final String word) {
+ public WordProperty getWordProperty(final String word) {
if (TextUtils.isEmpty(word)) {
return null;
}
final int[] codePoints = StringUtils.toCodePointArray(word);
final int[] outCodePoints = new int[MAX_WORD_LENGTH];
- final boolean[] outFlags = new boolean[FORMAT_UNIGRAM_PROPERTY_OUTPUT_FLAG_COUNT];
- final int[] outProbability = new int[1];
- final int[] outHistoricalInfo =
- new int[FORMAT_UNIGRAM_PROPERTY_OUTPUT_HISTORICAL_INFO_COUNT];
+ final boolean[] outFlags = new boolean[FORMAT_WORD_PROPERTY_OUTPUT_FLAG_COUNT];
+ final int[] outProbabilityInfo =
+ new int[FORMAT_WORD_PROPERTY_OUTPUT_PROBABILITY_INFO_COUNT];
+ final ArrayList<int[]> outBigramTargets = CollectionUtils.newArrayList();
+ final ArrayList<int[]> outBigramProbabilityInfo = CollectionUtils.newArrayList();
final ArrayList<int[]> outShortcutTargets = CollectionUtils.newArrayList();
final ArrayList<Integer> outShortcutProbabilities = CollectionUtils.newArrayList();
- getUnigramPropertyNative(mNativeDict, codePoints, outCodePoints, outFlags, outProbability,
- outHistoricalInfo, outShortcutTargets, outShortcutProbabilities);
- return new UnigramProperty(codePoints,
- outFlags[FORMAT_UNIGRAM_PROPERTY_IS_NOT_A_WORD_INDEX],
- outFlags[FORMAT_UNIGRAM_PROPERTY_IS_BLACKLISTED_INDEX],
- outFlags[FORMAT_UNIGRAM_PROPERTY_HAS_BIGRAMS_INDEX],
- outFlags[FORMAT_UNIGRAM_PROPERTY_HAS_SHORTCUTS_INDEX], outProbability[0],
- outHistoricalInfo[FORMAT_UNIGRAM_PROPERTY_TIMESTAMP_INDEX],
- outHistoricalInfo[FORMAT_UNIGRAM_PROPERTY_LEVEL_INDEX],
- outHistoricalInfo[FORMAT_UNIGRAM_PROPERTY_COUNT_INDEX],
- outShortcutTargets, outShortcutProbabilities);
+ getWordPropertyNative(mNativeDict, codePoints, outCodePoints, outFlags, outProbabilityInfo,
+ outBigramTargets, outBigramProbabilityInfo, outShortcutTargets,
+ outShortcutProbabilities);
+ return new WordProperty(codePoints,
+ outFlags[FORMAT_WORD_PROPERTY_IS_NOT_A_WORD_INDEX],
+ outFlags[FORMAT_WORD_PROPERTY_IS_BLACKLISTED_INDEX],
+ outFlags[FORMAT_WORD_PROPERTY_HAS_BIGRAMS_INDEX],
+ outFlags[FORMAT_WORD_PROPERTY_HAS_SHORTCUTS_INDEX], outProbabilityInfo,
+ outBigramTargets, outBigramProbabilityInfo, outShortcutTargets,
+ outShortcutProbabilities);
+ }
+
+ public static class GetNextWordPropertyResult {
+ public WordProperty mWordProperty;
+ public int mNextToken;
+
+ public GetNextWordPropertyResult(final WordProperty wordPreperty, final int nextToken) {
+ mWordProperty = wordPreperty;
+ mNextToken = nextToken;
+ }
+ }
+
+ /**
+ * Method to iterate all words in the dictionary for makedict.
+ * If token is 0, this method newly starts iterating the dictionary.
+ */
+ @UsedForTesting
+ public GetNextWordPropertyResult getNextWordProperty(final int token) {
+ final int[] codePoints = new int[MAX_WORD_LENGTH];
+ final int nextToken = getNextWordNative(mNativeDict, token, codePoints);
+ int len = 0;
+ // codePoints is null-terminated if its length is shorter than the array length.
+ while (len < MAX_WORD_LENGTH && codePoints[len] != 0) {
+ ++len;
+ }
+ final String word = new String(mOutputCodePoints, 0, len);
+ return new GetNextWordPropertyResult(getWordProperty(word), nextToken);
}
// Add a unigram entry to binary dictionary with unigram attributes in native code.
@@ -379,7 +409,6 @@ public final class BinaryDictionary extends Dictionary {
return;
}
}
-
}
private void reopen() {
diff --git a/java/src/com/android/inputmethod/latin/Constants.java b/java/src/com/android/inputmethod/latin/Constants.java
index 0b396b1de..d6ac71fe2 100644
--- a/java/src/com/android/inputmethod/latin/Constants.java
+++ b/java/src/com/android/inputmethod/latin/Constants.java
@@ -191,6 +191,8 @@ public final class Constants {
public static final int CODE_QUESTION_MARK = '?';
public static final int CODE_EXCLAMATION_MARK = '!';
public static final int CODE_SLASH = '/';
+ public static final int CODE_BACKSLASH = '\\';
+ public static final int CODE_VERTICAL_BAR = '|';
public static final int CODE_COMMERCIAL_AT = '@';
public static final int CODE_PLUS = '+';
public static final int CODE_PERCENT = '%';
diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
index 8e1675b2a..4dee84a7b 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
@@ -267,9 +267,9 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
protected Map<String, String> getHeaderAttributeMap() {
HashMap<String, String> attributeMap = new HashMap<String, String>();
- attributeMap.put(FormatSpec.FileHeader.DICTIONARY_ID_ATTRIBUTE, mDictName);
- attributeMap.put(FormatSpec.FileHeader.DICTIONARY_LOCALE_ATTRIBUTE, mLocale.toString());
- attributeMap.put(FormatSpec.FileHeader.DICTIONARY_VERSION_ATTRIBUTE,
+ attributeMap.put(FormatSpec.FileHeader.DICTIONARY_ID_KEY, mDictName);
+ attributeMap.put(FormatSpec.FileHeader.DICTIONARY_LOCALE_KEY, mLocale.toString());
+ attributeMap.put(FormatSpec.FileHeader.DICTIONARY_VERSION_KEY,
String.valueOf(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())));
return attributeMap;
}
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 8f4b2d67e..6517ef29d 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -79,6 +79,7 @@ import com.android.inputmethod.latin.utils.ApplicationUtils;
import com.android.inputmethod.latin.utils.CapsModeUtils;
import com.android.inputmethod.latin.utils.CompletionInfoUtils;
import com.android.inputmethod.latin.utils.CoordinateUtils;
+import com.android.inputmethod.latin.utils.ImportantNoticeUtils;
import com.android.inputmethod.latin.utils.IntentUtils;
import com.android.inputmethod.latin.utils.JniUtils;
import com.android.inputmethod.latin.utils.LatinImeLoggerUtils;
@@ -1157,6 +1158,27 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
mInputLogic.mSuggest.mDictionaryFacilitator.addWordToUserDictionary(wordToEdit);
}
+ // TODO: Move this method out of {@link LatinIME}.
+ // Callback for the {@link SuggestionStripView}, to call when the important notice strip is
+ // pressed.
+ @Override
+ public void showImportantNoticeContents() {
+ final Context context = this;
+ final DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(final DialogInterface di, final int position) {
+ di.dismiss();
+ ImportantNoticeUtils.updateLastImportantNoticeVersion(context);
+ setNeutralSuggestionStrip();
+ }
+ };
+ final AlertDialog.Builder builder =
+ new AlertDialog.Builder(context, AlertDialog.THEME_HOLO_DARK);
+ builder.setMessage(R.string.important_notice_contents)
+ .setPositiveButton(android.R.string.ok, listener);
+ showOptionDialog(builder.create(), false /* cancelable */);
+ }
+
public void displaySettingsDialog() {
if (isShowingOptionDialog()) return;
showSubtypeSelectorAndSettings();
@@ -1401,10 +1423,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// because it may differ from mWordComposer.mTypedWord.
autoCorrection = sourceSuggestedWords.mTypedWord;
}
- if (SuggestedWords.EMPTY != suggestedWords) {
+ if (SuggestedWords.EMPTY == suggestedWords) {
+ setNeutralSuggestionStrip();
+ } else {
mInputLogic.mWordComposer.setAutoCorrection(autoCorrection);
+ setSuggestedWords(suggestedWords, isSuggestionsStripVisible());
}
- setSuggestedWords(suggestedWords, isSuggestionsStripVisible());
// Cache the auto-correction in accessibility code so we can speak it if the user
// touches a key that will insert it.
AccessibilityUtils.getInstance().setAutoCorrection(suggestedWords,
@@ -1685,17 +1709,18 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
};
final AlertDialog.Builder builder =
new AlertDialog.Builder(this).setItems(items, listener).setTitle(title);
- showOptionDialog(builder.create());
+ showOptionDialog(builder.create(), true /*cancelable */);
}
- public void showOptionDialog(final AlertDialog dialog) {
+ // TODO: Move this method out of {@link LatinIME}.
+ private void showOptionDialog(final AlertDialog dialog, final boolean cancelable) {
final IBinder windowToken = mKeyboardSwitcher.getMainKeyboardView().getWindowToken();
if (windowToken == null) {
return;
}
- dialog.setCancelable(true);
- dialog.setCanceledOnTouchOutside(true);
+ dialog.setCancelable(cancelable);
+ dialog.setCanceledOnTouchOutside(cancelable);
final Window window = dialog.getWindow();
final WindowManager.LayoutParams lp = window.getAttributes();
diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
index 1bc67b2a0..f53183f37 100644
--- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
+++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java
@@ -1142,6 +1142,8 @@ public final class InputLogic {
// Recorrection is not supported in languages without spaces because we don't know
// how to segment them yet.
if (!settingsValues.mSpacingAndPunctuations.mCurrentLanguageHasSpaces) return;
+ // If no suggestions are requested, don't try restarting suggestions.
+ if (!settingsValues.isSuggestionsRequested()) return;
// If the cursor is not touching a word, or if there is a selection, return right away.
if (mConnection.hasSelection()) return;
// If we don't know the cursor location, return.
diff --git a/java/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java
index 370782b33..1a9118147 100644
--- a/java/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java
+++ b/java/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java
@@ -60,7 +60,7 @@ public abstract class AbstractDictDecoder implements DictDecoder {
final FileHeader header = new FileHeader(headerSize,
new FusionDictionary.DictionaryOptions(attributes),
new FormatOptions(version, FileHeader.ATTRIBUTE_VALUE_TRUE.equals(
- attributes.get(FileHeader.HAS_HISTORICAL_INFO_ATTRIBUTE))));
+ attributes.get(FileHeader.HAS_HISTORICAL_INFO_KEY))));
return header;
}
diff --git a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
index 61c17fc46..74e305976 100644
--- a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
+++ b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
@@ -336,16 +336,19 @@ public final class FormatSpec {
public final int mBodyOffset;
public final DictionaryOptions mDictionaryOptions;
public final FormatOptions mFormatOptions;
+
// Note that these are corresponding definitions in native code in latinime::HeaderPolicy
// and latinime::HeaderReadWriteUtils.
- public static final String USES_FORGETTING_CURVE_ATTRIBUTE = "USES_FORGETTING_CURVE";
- public static final String HAS_HISTORICAL_INFO_ATTRIBUTE = "HAS_HISTORICAL_INFO";
+ // TODO: Standardize the key names and bump up the format version, taking care not to
+ // break format version 2 dictionaries.
+ public static final String DICTIONARY_VERSION_KEY = "version";
+ public static final String DICTIONARY_LOCALE_KEY = "locale";
+ public static final String DICTIONARY_ID_KEY = "dictionary";
+ public static final String DICTIONARY_DESCRIPTION_KEY = "description";
+ public static final String DICTIONARY_DATE_KEY = "date";
+ public static final String HAS_HISTORICAL_INFO_KEY = "HAS_HISTORICAL_INFO";
+ public static final String USES_FORGETTING_CURVE_KEY = "USES_FORGETTING_CURVE";
public static final String ATTRIBUTE_VALUE_TRUE = "1";
-
- public static final String DICTIONARY_VERSION_ATTRIBUTE = "version";
- public static final String DICTIONARY_LOCALE_ATTRIBUTE = "locale";
- public static final String DICTIONARY_ID_ATTRIBUTE = "dictionary";
- private static final String DICTIONARY_DESCRIPTION_ATTRIBUTE = "description";
public FileHeader(final int headerSize, final DictionaryOptions dictionaryOptions,
final FormatOptions formatOptions) throws UnsupportedFormatException {
mDictionaryOptions = dictionaryOptions;
@@ -365,24 +368,24 @@ public final class FormatSpec {
// Helper method to get the locale as a String
public String getLocaleString() {
- return mDictionaryOptions.mAttributes.get(FileHeader.DICTIONARY_LOCALE_ATTRIBUTE);
+ return mDictionaryOptions.mAttributes.get(FileHeader.DICTIONARY_LOCALE_KEY);
}
// Helper method to get the version String
public String getVersion() {
- return mDictionaryOptions.mAttributes.get(FileHeader.DICTIONARY_VERSION_ATTRIBUTE);
+ return mDictionaryOptions.mAttributes.get(FileHeader.DICTIONARY_VERSION_KEY);
}
// Helper method to get the dictionary ID as a String
public String getId() {
- return mDictionaryOptions.mAttributes.get(FileHeader.DICTIONARY_ID_ATTRIBUTE);
+ return mDictionaryOptions.mAttributes.get(FileHeader.DICTIONARY_ID_KEY);
}
// Helper method to get the description
public String getDescription() {
// TODO: Right now each dictionary file comes with a description in its own language.
// It will display as is no matter the device's locale. It should be internationalized.
- return mDictionaryOptions.mAttributes.get(FileHeader.DICTIONARY_DESCRIPTION_ATTRIBUTE);
+ return mDictionaryOptions.mAttributes.get(FileHeader.DICTIONARY_DESCRIPTION_KEY);
}
}
diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java
index d34aa171e..b12f79b07 100644
--- a/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java
+++ b/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java
@@ -62,7 +62,7 @@ public class Ver4DictEncoder implements DictEncoder {
final BinaryDictionary binaryDict = new BinaryDictionary(mDictPlacedDir.getAbsolutePath(),
0l, mDictPlacedDir.length(), true /* useFullEditDistance */,
LocaleUtils.constructLocaleFromString(dict.mOptions.mAttributes.get(
- FormatSpec.FileHeader.DICTIONARY_LOCALE_ATTRIBUTE)),
+ FormatSpec.FileHeader.DICTIONARY_LOCALE_KEY)),
Dictionary.TYPE_USER /* Dictionary type. Does not matter for us */,
true /* isUpdatable */);
if (!binaryDict.isValidDictionary()) {
diff --git a/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java b/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java
index 4a610e6f9..d636a253a 100644
--- a/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java
+++ b/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java
@@ -95,13 +95,13 @@ public abstract class DecayingExpandableBinaryDictionaryBase extends ExpandableB
@Override
protected Map<String, String> getHeaderAttributeMap() {
HashMap<String, String> attributeMap = new HashMap<String, String>();
- attributeMap.put(FormatSpec.FileHeader.USES_FORGETTING_CURVE_ATTRIBUTE,
+ attributeMap.put(FormatSpec.FileHeader.USES_FORGETTING_CURVE_KEY,
FormatSpec.FileHeader.ATTRIBUTE_VALUE_TRUE);
- attributeMap.put(FormatSpec.FileHeader.HAS_HISTORICAL_INFO_ATTRIBUTE,
+ attributeMap.put(FormatSpec.FileHeader.HAS_HISTORICAL_INFO_KEY,
FormatSpec.FileHeader.ATTRIBUTE_VALUE_TRUE);
- attributeMap.put(FormatSpec.FileHeader.DICTIONARY_ID_ATTRIBUTE, mDictName);
- attributeMap.put(FormatSpec.FileHeader.DICTIONARY_LOCALE_ATTRIBUTE, mLocale.toString());
- attributeMap.put(FormatSpec.FileHeader.DICTIONARY_VERSION_ATTRIBUTE,
+ attributeMap.put(FormatSpec.FileHeader.DICTIONARY_ID_KEY, mDictName);
+ attributeMap.put(FormatSpec.FileHeader.DICTIONARY_LOCALE_KEY, mLocale.toString());
+ attributeMap.put(FormatSpec.FileHeader.DICTIONARY_VERSION_KEY,
String.valueOf(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())));
return attributeMap;
}
diff --git a/java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java b/java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java
index 8ba32ff76..70cb2b285 100644
--- a/java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java
+++ b/java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java
@@ -19,6 +19,7 @@ package com.android.inputmethod.latin.settings;
import android.content.res.Resources;
import com.android.inputmethod.keyboard.internal.KeySpecParser;
+import com.android.inputmethod.keyboard.internal.MoreKeySpec;
import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.Dictionary;
import com.android.inputmethod.latin.R;
@@ -55,7 +56,7 @@ public final class SpacingAndPunctuations {
res.getString(R.string.symbols_word_connectors));
mSortedWordSeparators = StringUtils.toSortedCodePointArray(
res.getString(R.string.symbols_word_separators));
- final String[] suggestPuncsSpec = KeySpecParser.splitKeySpecs(res.getString(
+ final String[] suggestPuncsSpec = MoreKeySpec.splitKeySpecs(res.getString(
R.string.suggested_punctuations));
mSuggestPuncList = createSuggestPuncList(suggestPuncsSpec);
mSentenceSeparator = res.getInteger(R.integer.sentence_separator);
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
index 5ed42ab00..a89f70e1f 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
@@ -28,7 +28,6 @@ import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
-import android.support.v4.view.GravityCompat;
import android.support.v4.view.ViewCompat;
import android.text.Spannable;
import android.text.SpannableString;
@@ -45,6 +44,7 @@ import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
+import com.android.inputmethod.compat.TextViewCompatUtils;
import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.SuggestedWords;
@@ -492,7 +492,30 @@ final class SuggestionStripLayoutHelper {
hintView, 1.0f - mCenterSuggestionWeight, ViewGroup.LayoutParams.MATCH_PARENT);
}
- private static void setLayoutWeight(final View v, final float weight, final int height) {
+ public void layoutImportantNotice(final View importantNoticeStrip, final int stripWidth) {
+ final Resources res = importantNoticeStrip.getResources();
+ final Drawable infoIcon = res.getDrawable(R.drawable.sym_keyboard_info_holo_dark);
+ final Drawable moreIcon = res.getDrawable(R.drawable.sym_keyboard_more_holo_dark);
+ final int width = stripWidth - infoIcon.getIntrinsicWidth() - moreIcon.getIntrinsicWidth();
+ final TextView titleView = (TextView)importantNoticeStrip.findViewById(
+ R.id.important_notice_title);
+ titleView.setTextColor(mColorAutoCorrect);
+ TextViewCompatUtils.setCompoundDrawablesRelativeWithIntrinsicBounds(
+ titleView, infoIcon, null, moreIcon, null);
+ final CharSequence importantNoticeTitle = titleView.getText();
+ titleView.setTextScaleX(1.0f); // Reset textScaleX.
+ // When the suggestions strip is displayed first time, stripWidth may be zero.
+ // Then importantNoticeTitle will be displayed as is without auto text scaleX.
+ // TODO: Fix the logic to always have a correct value of stripWidth.
+ if (width > 0) {
+ // Auto text scaleX to show entire important notice title should be on the strip.
+ final float titleScaleX = getTextScaleX(
+ importantNoticeTitle, width, titleView.getPaint());
+ titleView.setTextScaleX(titleScaleX);
+ }
+ }
+
+ static void setLayoutWeight(final View v, final float weight, final int height) {
final ViewGroup.LayoutParams lp = v.getLayoutParams();
if (lp instanceof LinearLayout.LayoutParams) {
final LinearLayout.LayoutParams llp = (LinearLayout.LayoutParams)lp;
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
index 22804fce1..b04a2cb5b 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
@@ -54,6 +54,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
OnLongClickListener {
public interface Listener {
public void addWordToUserDictionary(String word);
+ public void showImportantNoticeContents();
public void pickSuggestionManually(int index, SuggestedWordInfo word);
}
@@ -62,6 +63,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
private final ViewGroup mSuggestionsStrip;
private final ViewGroup mAddToDictionaryStrip;
+ private final View mImportantNoticeStrip;
MainKeyboardView mMainKeyboardView;
private final View mMoreSuggestionsContainer;
@@ -81,10 +83,13 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
private static class StripVisibilityGroup {
private final View mSuggestionsStrip;
private final View mAddToDictionaryStrip;
+ private final View mImportantNoticeStrip;
- public StripVisibilityGroup(final View suggestionsStrip, final View addToDictionaryStrip) {
+ public StripVisibilityGroup(final View suggestionsStrip, final View addToDictionaryStrip,
+ final View importantNoticeStrip) {
mSuggestionsStrip = suggestionsStrip;
mAddToDictionaryStrip = addToDictionaryStrip;
+ mImportantNoticeStrip = importantNoticeStrip;
showSuggestionsStrip();
}
@@ -93,16 +98,25 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
: ViewCompat.LAYOUT_DIRECTION_LTR;
ViewCompat.setLayoutDirection(mSuggestionsStrip, layoutDirection);
ViewCompat.setLayoutDirection(mAddToDictionaryStrip, layoutDirection);
+ ViewCompat.setLayoutDirection(mImportantNoticeStrip, layoutDirection);
}
public void showSuggestionsStrip() {
mSuggestionsStrip.setVisibility(VISIBLE);
mAddToDictionaryStrip.setVisibility(INVISIBLE);
+ mImportantNoticeStrip.setVisibility(INVISIBLE);
}
public void showAddToDictionaryStrip() {
mSuggestionsStrip.setVisibility(INVISIBLE);
mAddToDictionaryStrip.setVisibility(VISIBLE);
+ mImportantNoticeStrip.setVisibility(INVISIBLE);
+ }
+
+ public void showImportantNoticeStrip() {
+ mSuggestionsStrip.setVisibility(INVISIBLE);
+ mAddToDictionaryStrip.setVisibility(INVISIBLE);
+ mImportantNoticeStrip.setVisibility(VISIBLE);
}
public boolean isShowingAddToDictionaryStrip() {
@@ -128,7 +142,9 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
mSuggestionsStrip = (ViewGroup)findViewById(R.id.suggestions_strip);
mAddToDictionaryStrip = (ViewGroup)findViewById(R.id.add_to_dictionary_strip);
- mStripVisibilityGroup = new StripVisibilityGroup(mSuggestionsStrip, mAddToDictionaryStrip);
+ mImportantNoticeStrip = findViewById(R.id.important_notice_strip);
+ mStripVisibilityGroup = new StripVisibilityGroup(mSuggestionsStrip, mAddToDictionaryStrip,
+ mImportantNoticeStrip);
for (int pos = 0; pos < SuggestedWords.MAX_SUGGESTIONS; pos++) {
final TextView word = new TextView(context, null, R.attr.suggestionWordStyle);
@@ -176,6 +192,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
ResearchLogger.suggestionStripView_setSuggestions(mSuggestedWords);
}
+ mStripVisibilityGroup.showSuggestionsStrip();
}
public int setMoreSuggestionsHeight(final int remainingHeight) {
@@ -203,6 +220,13 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
return false;
}
+ // TODO: This method should be called after this View has been attached and displayed.
+ public void showImportantNoticeTitle() {
+ mLayoutHelper.layoutImportantNotice(mImportantNoticeStrip, getWidth());
+ mStripVisibilityGroup.showImportantNoticeStrip();
+ mImportantNoticeStrip.setOnClickListener(this);
+ }
+
public void clear() {
mSuggestionsStrip.removeAllViews();
removeAllDebugInfoViews();
@@ -360,6 +384,10 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
@Override
public void onClick(final View view) {
+ if (view == mImportantNoticeStrip) {
+ mListener.showImportantNoticeContents();
+ return;
+ }
final Object tag = view.getTag();
// {@link String} tag is set at {@link #showAddToDictionaryHint(String,CharSequence)}.
if (tag instanceof String) {
diff --git a/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java b/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java
index cc25102ce..bbfa0f091 100644
--- a/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java
@@ -102,4 +102,19 @@ public final class CollectionUtils {
public static <E> SparseArray<E> newSparseArray() {
return new SparseArray<E>();
}
+
+ public static <E> ArrayList<E> arrayAsList(final E[] array, final int start, final int end) {
+ if (array == null) {
+ throw new NullPointerException();
+ }
+ if (start < 0 || start > end || end > array.length) {
+ throw new IllegalArgumentException();
+ }
+
+ final ArrayList<E> list = newArrayList(end - start);
+ for (int i = start; i < end; i++) {
+ list.add(array[i]);
+ }
+ return list;
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/utils/CsvUtils.java b/java/src/com/android/inputmethod/latin/utils/CsvUtils.java
index 36b927eea..b18a1d83b 100644
--- a/java/src/com/android/inputmethod/latin/utils/CsvUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/CsvUtils.java
@@ -17,6 +17,7 @@
package com.android.inputmethod.latin.utils;
import com.android.inputmethod.annotations.UsedForTesting;
+import com.android.inputmethod.latin.Constants;
import java.util.ArrayList;
@@ -57,9 +58,9 @@ public final class CsvUtils {
// Note that none of these characters match high or low surrogate characters, so we need not
// take care of matching by code point.
- private static final char COMMA = ',';
- private static final char SPACE = ' ';
- private static final char QUOTE = '"';
+ private static final char COMMA = Constants.CODE_COMMA;
+ private static final char SPACE = Constants.CODE_SPACE;
+ private static final char QUOTE = Constants.CODE_DOUBLE_QUOTE;
@SuppressWarnings("serial")
public static class CsvParseException extends RuntimeException {
diff --git a/java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java b/java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java
index 8eff2e6e7..4a0823155 100644
--- a/java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/ImportantNoticeUtils.java
@@ -22,13 +22,11 @@ import android.provider.Settings;
import android.provider.Settings.SettingNotFoundException;
import android.util.Log;
+import com.android.inputmethod.latin.R;
+
public final class ImportantNoticeUtils {
private static final String TAG = ImportantNoticeUtils.class.getSimpleName();
- // The current version number of an important notice.
- // This number must be incremented whenever users should see a new important notice.
- private static final int CURRENT_IMPORTANT_NOTICE_VERSION = 0;
-
// {@link SharedPreferences} name to save the last important notice version that has been
// displayed to users.
private static final String PREFERENCE_NAME = "important_notice";
@@ -60,16 +58,20 @@ public final class ImportantNoticeUtils {
return context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
}
+ private static int getCurrentImportantNoticeVersion(final Context context) {
+ return context.getResources().getInteger(R.integer.config_important_notice_version);
+ }
+
public static boolean hasNewImportantNotice(final Context context) {
final SharedPreferences prefs = getImportantNoticePreferences(context);
final int lastVersion = prefs.getInt(KEY_IMPORTANT_NOTICE_VERSION, 0);
- return CURRENT_IMPORTANT_NOTICE_VERSION > lastVersion;
+ return getCurrentImportantNoticeVersion(context) > lastVersion;
}
public static void updateLastImportantNoticeVersion(final Context context) {
final SharedPreferences prefs = getImportantNoticePreferences(context);
prefs.edit()
- .putInt(KEY_IMPORTANT_NOTICE_VERSION, CURRENT_IMPORTANT_NOTICE_VERSION)
+ .putInt(KEY_IMPORTANT_NOTICE_VERSION, getCurrentImportantNoticeVersion(context))
.apply();
}
}
diff --git a/java/src/com/android/inputmethod/latin/utils/StringUtils.java b/java/src/com/android/inputmethod/latin/utils/StringUtils.java
index b154623ae..c632a71a9 100644
--- a/java/src/com/android/inputmethod/latin/utils/StringUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/StringUtils.java
@@ -16,6 +16,8 @@
package com.android.inputmethod.latin.utils;
+import static com.android.inputmethod.latin.Constants.CODE_UNSPECIFIED;
+
import android.text.TextUtils;
import com.android.inputmethod.annotations.UsedForTesting;
@@ -471,4 +473,20 @@ public final class StringUtils {
}
return bytes;
}
+
+ public static String toUpperCaseOfStringForLocale(final String text,
+ final boolean needsToUpperCase, final Locale locale) {
+ if (text == null || !needsToUpperCase) return text;
+ return text.toUpperCase(locale);
+ }
+
+ public static int toUpperCaseOfCodeForLocale(final int code, final boolean needsToUpperCase,
+ final Locale locale) {
+ if (!Constants.isLetterCode(code) || !needsToUpperCase) return code;
+ final String text = newSingleCodePointString(code);
+ final String casedText = toUpperCaseOfStringForLocale(
+ text, needsToUpperCase, locale);
+ return codePointCount(casedText) == 1
+ ? casedText.codePointAt(0) : CODE_UNSPECIFIED;
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtils.java b/java/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtils.java
index db628fe18..7af03da59 100644
--- a/java/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtils.java
@@ -22,6 +22,7 @@ import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.latin.makedict.BinaryDictIOUtils;
import com.android.inputmethod.latin.makedict.DictDecoder;
import com.android.inputmethod.latin.makedict.DictEncoder;
+import com.android.inputmethod.latin.makedict.FormatSpec;
import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
import com.android.inputmethod.latin.makedict.FusionDictionary;
import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray;
@@ -44,9 +45,6 @@ import java.util.concurrent.TimeUnit;
public final class UserHistoryDictIOUtils {
private static final String TAG = UserHistoryDictIOUtils.class.getSimpleName();
private static final boolean DEBUG = false;
- private static final String USES_FORGETTING_CURVE_KEY = "USES_FORGETTING_CURVE";
- private static final String USES_FORGETTING_CURVE_VALUE = "1";
- private static final String LAST_UPDATED_TIME_KEY = "date";
public interface OnAddWordListener {
/**
@@ -75,8 +73,9 @@ public final class UserHistoryDictIOUtils {
final BigramDictionaryInterface dict, final UserHistoryDictionaryBigramList bigrams,
final FormatOptions formatOptions, final HashMap<String, String> options) {
final FusionDictionary fusionDict = constructFusionDictionary(dict, bigrams, options);
- fusionDict.addOptionAttribute(USES_FORGETTING_CURVE_KEY, USES_FORGETTING_CURVE_VALUE);
- fusionDict.addOptionAttribute(LAST_UPDATED_TIME_KEY,
+ fusionDict.addOptionAttribute(FormatSpec.FileHeader.USES_FORGETTING_CURVE_KEY,
+ FormatSpec.FileHeader.ATTRIBUTE_VALUE_TRUE);
+ fusionDict.addOptionAttribute(FormatSpec.FileHeader.DICTIONARY_DATE_KEY,
String.valueOf(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())));
try {
dictEncoder.writeDictionary(fusionDict, formatOptions);
diff --git a/java/src/com/android/inputmethod/latin/utils/UnigramProperty.java b/java/src/com/android/inputmethod/latin/utils/WordProperty.java
index 4feee4393..ba9b114b0 100644
--- a/java/src/com/android/inputmethod/latin/utils/UnigramProperty.java
+++ b/java/src/com/android/inputmethod/latin/utils/WordProperty.java
@@ -26,21 +26,36 @@ import java.util.ArrayList;
// This has information that belong to a unigram. This class has some detailed attributes such as
// historical information but they have to be checked only for testing purpose.
@UsedForTesting
-public class UnigramProperty {
+public class WordProperty {
public final String mCodePoints;
public final boolean mIsNotAWord;
public final boolean mIsBlacklisted;
public final boolean mHasBigrams;
public final boolean mHasShortcuts;
- public final int mProbability;
- // mTimestamp, mLevel and mCount are historical info. These values are depend on the
- // implementation in native code; thus, we must not use them and have any assumptions about
- // them except for tests.
- public final int mTimestamp;
- public final int mLevel;
- public final int mCount;
+ public final ProbabilityInfo mProbabilityInfo;
+ public final ArrayList<WeightedString> mBigramTargets = CollectionUtils.newArrayList();
+ public final ArrayList<ProbabilityInfo> mBigramProbabilityInfo = CollectionUtils.newArrayList();
public final ArrayList<WeightedString> mShortcutTargets = CollectionUtils.newArrayList();
+ // TODO: Use this kind of Probability class for dictionary read/write code under the makedict
+ // package.
+ public static final class ProbabilityInfo {
+ public final int mProbability;
+ // wTimestamp, mLevel and mCount are historical info. These values are depend on the
+ // implementation in native code; thus, we must not use them and have any assumptions about
+ // them except for tests.
+ public final int mTimestamp;
+ public final int mLevel;
+ public final int mCount;
+
+ public ProbabilityInfo(final int[] probabilityInfo) {
+ mProbability = probabilityInfo[BinaryDictionary.FORMAT_WORD_PROPERTY_PROBABILITY_INDEX];
+ mTimestamp = probabilityInfo[BinaryDictionary.FORMAT_WORD_PROPERTY_TIMESTAMP_INDEX];
+ mLevel = probabilityInfo[BinaryDictionary.FORMAT_WORD_PROPERTY_LEVEL_INDEX];
+ mCount = probabilityInfo[BinaryDictionary.FORMAT_WORD_PROPERTY_COUNT_INDEX];
+ }
+ }
+
private static int getCodePointCount(final int[] codePoints) {
for (int i = 0; i < codePoints.length; i++) {
if (codePoints[i] == 0) {
@@ -50,21 +65,32 @@ public class UnigramProperty {
return codePoints.length;
}
- // This represents invalid unigram when the probability is BinaryDictionary.NOT_A_PROBABILITY.
- public UnigramProperty(final int[] codePoints, final boolean isNotAWord,
+ // This represents invalid word when the probability is BinaryDictionary.NOT_A_PROBABILITY.
+ public WordProperty(final int[] codePoints, final boolean isNotAWord,
final boolean isBlacklisted, final boolean hasBigram,
- final boolean hasShortcuts, final int probability, final int timestamp,
- final int level, final int count, final ArrayList<int[]> shortcutTargets,
+ final boolean hasShortcuts, final int[] probabilityInfo,
+ final ArrayList<int[]> bigramTargets, final ArrayList<int[]> bigramProbabilityInfo,
+ final ArrayList<int[]> shortcutTargets,
final ArrayList<Integer> shortcutProbabilities) {
mCodePoints = new String(codePoints, 0 /* offset */, getCodePointCount(codePoints));
mIsNotAWord = isNotAWord;
mIsBlacklisted = isBlacklisted;
mHasBigrams = hasBigram;
mHasShortcuts = hasShortcuts;
- mProbability = probability;
- mTimestamp = timestamp;
- mLevel = level;
- mCount = count;
+ mProbabilityInfo = new ProbabilityInfo(probabilityInfo);
+
+ final int bigramTargetCount = bigramTargets.size();
+ for (int i = 0; i < bigramTargetCount; i++) {
+ final int[] bigramTargetCodePointArray = bigramTargets.get(i);
+ final String bigramTargetString = new String(bigramTargetCodePointArray,
+ 0 /* offset */, getCodePointCount(bigramTargetCodePointArray));
+ final ProbabilityInfo bigramProbability =
+ new ProbabilityInfo(bigramProbabilityInfo.get(i));
+ mBigramTargets.add(
+ new WeightedString(bigramTargetString, bigramProbability.mProbability));
+ mBigramProbabilityInfo.add(bigramProbability);
+ }
+
final int shortcutTargetCount = shortcutTargets.size();
for (int i = 0; i < shortcutTargetCount; i++) {
final int[] shortcutTargetCodePointArray = shortcutTargets.get(i);
@@ -77,6 +103,6 @@ public class UnigramProperty {
@UsedForTesting
public boolean isValid() {
- return mProbability != BinaryDictionary.NOT_A_PROBABILITY;
+ return mProbabilityInfo.mProbability != BinaryDictionary.NOT_A_PROBABILITY;
}
} \ No newline at end of file
diff --git a/native/jni/NativeFileList.mk b/native/jni/NativeFileList.mk
index d2f22598e..c7061c8c3 100644
--- a/native/jni/NativeFileList.mk
+++ b/native/jni/NativeFileList.mk
@@ -32,7 +32,7 @@ LATIN_IME_CORE_SRC_FILES := \
error_type_utils.cpp \
multi_bigram_map.cpp \
suggestions_output_utils.cpp \
- unigram_property.cpp) \
+ word_property.cpp) \
$(addprefix suggest/core/layout/, \
additional_proximity_chars.cpp \
proximity_info.cpp \
diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
index 586a306ec..c919ebd91 100644
--- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
+++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
@@ -24,7 +24,7 @@
#include "jni.h"
#include "jni_common.h"
#include "suggest/core/dictionary/dictionary.h"
-#include "suggest/core/dictionary/unigram_property.h"
+#include "suggest/core/dictionary/word_property.h"
#include "suggest/core/suggest_options.h"
#include "suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.h"
#include "suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h"
@@ -260,19 +260,39 @@ static jint latinime_BinaryDictionary_getBigramProbability(JNIEnv *env, jclass c
word1Length);
}
-static void latinime_BinaryDictionary_getUnigramProperty(JNIEnv *env, jclass clazz,
+// Method to iterate all words in the dictionary for makedict.
+// If token is 0, this method newly starts iterating the dictionary. This method returns 0 when
+// the dictionary does not have a next word.
+static jint latinime_BinaryDictionary_getNextWord(JNIEnv *env, jclass clazz,
+ jlong dict, jint token, jintArray outCodePoints) {
+ Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict);
+ if (!dictionary) return 0;
+ const jsize outCodePointsLength = env->GetArrayLength(outCodePoints);
+ if (outCodePointsLength != MAX_WORD_LENGTH) {
+ AKLOGE("Invalid outCodePointsLength: %d", outCodePointsLength);
+ ASSERT(false);
+ return 0;
+ }
+ int wordCodePoints[outCodePointsLength];
+ memset(wordCodePoints, 0, sizeof(wordCodePoints));
+ const int nextToken = dictionary->getNextWordAndNextToken(token, wordCodePoints);
+ env->SetIntArrayRegion(outCodePoints, 0, outCodePointsLength, wordCodePoints);
+ return nextToken;
+}
+
+static void latinime_BinaryDictionary_getWordProperty(JNIEnv *env, jclass clazz,
jlong dict, jintArray word, jintArray outCodePoints, jbooleanArray outFlags,
- jintArray outProbability, jintArray outHistoricalInfo, jobject outShortcutTargets,
- jobject outShortcutProbabilities) {
+ jintArray outProbabilityInfo, jobject outBigramTargets, jobject outBigramProbabilityInfo,
+ jobject outShortcutTargets, jobject outShortcutProbabilities) {
Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict);
if (!dictionary) return;
const jsize wordLength = env->GetArrayLength(word);
int wordCodePoints[wordLength];
env->GetIntArrayRegion(word, 0, wordLength, wordCodePoints);
- const UnigramProperty unigramProperty = dictionary->getUnigramProperty(
- wordCodePoints, wordLength);
- unigramProperty.outputProperties(env, outCodePoints, outFlags, outProbability,
- outHistoricalInfo, outShortcutTargets, outShortcutProbabilities);
+ const WordProperty wordProperty = dictionary->getWordProperty(wordCodePoints, wordLength);
+ wordProperty.outputProperties(env, outCodePoints, outFlags, outProbabilityInfo,
+ outBigramTargets, outBigramProbabilityInfo, outShortcutTargets,
+ outShortcutProbabilities);
}
static jfloat latinime_BinaryDictionary_calcNormalizedScore(JNIEnv *env, jclass clazz,
@@ -521,9 +541,15 @@ static const JNINativeMethod sMethods[] = {
reinterpret_cast<void *>(latinime_BinaryDictionary_getBigramProbability)
},
{
- const_cast<char *>("getUnigramPropertyNative"),
- const_cast<char *>("(J[I[I[Z[I[ILjava/util/ArrayList;Ljava/util/ArrayList;)V"),
- reinterpret_cast<void *>(latinime_BinaryDictionary_getUnigramProperty)
+ const_cast<char *>("getWordPropertyNative"),
+ const_cast<char *>("(J[I[I[Z[ILjava/util/ArrayList;Ljava/util/ArrayList;"
+ "Ljava/util/ArrayList;Ljava/util/ArrayList;)V"),
+ reinterpret_cast<void *>(latinime_BinaryDictionary_getWordProperty)
+ },
+ {
+ const_cast<char *>("getNextWordNative"),
+ const_cast<char *>("(JI[I)I"),
+ reinterpret_cast<void *>(latinime_BinaryDictionary_getNextWord)
},
{
const_cast<char *>("calcNormalizedScoreNative"),
diff --git a/native/jni/src/suggest/core/dictionary/dictionary.cpp b/native/jni/src/suggest/core/dictionary/dictionary.cpp
index e68c0a6d8..9b71eff7a 100644
--- a/native/jni/src/suggest/core/dictionary/dictionary.cpp
+++ b/native/jni/src/suggest/core/dictionary/dictionary.cpp
@@ -143,13 +143,19 @@ void Dictionary::getProperty(const char *const query, const int queryLength, cha
maxResultLength);
}
-const UnigramProperty Dictionary::getUnigramProperty(const int *const codePoints,
+const WordProperty Dictionary::getWordProperty(const int *const codePoints,
const int codePointCount) {
TimeKeeper::setCurrentTime();
- return mDictionaryStructureWithBufferPolicy.get()->getUnigramProperty(
+ return mDictionaryStructureWithBufferPolicy.get()->getWordProperty(
codePoints, codePointCount);
}
+int Dictionary::getNextWordAndNextToken(const int token, int *const outCodePoints) {
+ TimeKeeper::setCurrentTime();
+ return mDictionaryStructureWithBufferPolicy.get()->getNextWordAndNextToken(
+ token, outCodePoints);
+}
+
void Dictionary::logDictionaryInfo(JNIEnv *const env) const {
int dictionaryIdCodePointBuffer[HEADER_ATTRIBUTE_BUFFER_SIZE];
int versionStringCodePointBuffer[HEADER_ATTRIBUTE_BUFFER_SIZE];
diff --git a/native/jni/src/suggest/core/dictionary/dictionary.h b/native/jni/src/suggest/core/dictionary/dictionary.h
index b37b4aa18..0a413cb52 100644
--- a/native/jni/src/suggest/core/dictionary/dictionary.h
+++ b/native/jni/src/suggest/core/dictionary/dictionary.h
@@ -22,7 +22,7 @@
#include "defines.h"
#include "jni.h"
#include "suggest/core/dictionary/bigram_dictionary.h"
-#include "suggest/core/dictionary/unigram_property.h"
+#include "suggest/core/dictionary/word_property.h"
#include "suggest/core/policy/dictionary_header_structure_policy.h"
#include "suggest/core/policy/dictionary_structure_with_buffer_policy.h"
#include "suggest/core/suggest_interface.h"
@@ -34,7 +34,7 @@ class DictionaryStructureWithBufferPolicy;
class DicTraverseSession;
class ProximityInfo;
class SuggestOptions;
-class UnigramProperty;
+class WordProperty;
class Dictionary {
public:
@@ -94,7 +94,12 @@ class Dictionary {
void getProperty(const char *const query, const int queryLength, char *const outResult,
const int maxResultLength);
- const UnigramProperty getUnigramProperty(const int *const codePoints, const int codePointCount);
+ const WordProperty getWordProperty(const int *const codePoints, const int codePointCount);
+
+ // Method to iterate all words in the dictionary.
+ // The returned token has to be used to get the next word. If token is 0, this method newly
+ // starts iterating the dictionary.
+ int getNextWordAndNextToken(const int token, int *const outCodePoints);
const DictionaryStructureWithBufferPolicy *getDictionaryStructurePolicy() const {
return mDictionaryStructureWithBufferPolicy.get();
diff --git a/native/jni/src/suggest/core/dictionary/unigram_property.cpp b/native/jni/src/suggest/core/dictionary/unigram_property.cpp
deleted file mode 100644
index 16bbb69d8..000000000
--- a/native/jni/src/suggest/core/dictionary/unigram_property.cpp
+++ /dev/null
@@ -1,52 +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/core/dictionary/unigram_property.h"
-
-namespace latinime {
-
-void UnigramProperty::outputProperties(JNIEnv *const env, jintArray outCodePoints,
- jbooleanArray outFlags, jintArray outProbability, jintArray outHistoricalInfo,
- jobject outShortcutTargets, jobject outShortcutProbabilities) const {
- env->SetIntArrayRegion(outCodePoints, 0 /* start */, mCodePointCount, mCodePoints);
- jboolean flags[] = {mIsNotAWord, mIsBlacklisted, mHasBigrams, mHasShortcuts};
- env->SetBooleanArrayRegion(outFlags, 0 /* start */, NELEMS(flags), flags);
- env->SetIntArrayRegion(outProbability, 0 /* start */, 1 /* len */, &mProbability);
- int historicalInfo[] = {mTimestamp, mLevel, mCount};
- env->SetIntArrayRegion(outHistoricalInfo, 0 /* start */, NELEMS(historicalInfo),
- historicalInfo);
-
- jclass integerClass = env->FindClass("java/lang/Integer");
- jmethodID intToIntegerConstructorId = env->GetMethodID(integerClass, "<init>", "(I)V");
- jclass arrayListClass = env->FindClass("java/util/ArrayList");
- jmethodID addMethodId = env->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z");
- const int shortcutTargetCount = mShortcutTargets.size();
- for (int i = 0; i < shortcutTargetCount; ++i) {
- jintArray shortcutTargetCodePointArray = env->NewIntArray(mShortcutTargets[i].size());
- env->SetIntArrayRegion(shortcutTargetCodePointArray, 0 /* start */,
- mShortcutTargets[i].size(), &mShortcutTargets[i][0]);
- env->CallVoidMethod(outShortcutTargets, addMethodId, shortcutTargetCodePointArray);
- env->DeleteLocalRef(shortcutTargetCodePointArray);
- jobject integerProbability = env->NewObject(integerClass, intToIntegerConstructorId,
- mShortcutProbabilities[i]);
- env->CallVoidMethod(outShortcutProbabilities, addMethodId, integerProbability);
- env->DeleteLocalRef(integerProbability);
- }
- env->DeleteLocalRef(integerClass);
- env->DeleteLocalRef(arrayListClass);
-}
-
-} // namespace latinime
diff --git a/native/jni/src/suggest/core/dictionary/unigram_property.h b/native/jni/src/suggest/core/dictionary/unigram_property.h
deleted file mode 100644
index c4ebb86ab..000000000
--- a/native/jni/src/suggest/core/dictionary/unigram_property.h
+++ /dev/null
@@ -1,87 +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_UNIGRAM_PROPERTY_H
-#define LATINIME_UNIGRAM_PROPERTY_H
-
-#include <cstring>
-#include <vector>
-
-#include "defines.h"
-#include "jni.h"
-
-namespace latinime {
-
-// This class is used for returning information belonging to a unigram to java side.
-class UnigramProperty {
- public:
- // Invalid unigram.
- UnigramProperty()
- : mCodePoints(), mCodePointCount(0), mIsNotAWord(false), mIsBlacklisted(false),
- mHasBigrams(false), mHasShortcuts(false), mProbability(NOT_A_PROBABILITY),
- mTimestamp(0), mLevel(0), mCount(0), mShortcutTargets(), mShortcutProbabilities() {}
-
- UnigramProperty(const UnigramProperty &unigramProperty)
- : mCodePoints(), mCodePointCount(unigramProperty.mCodePointCount),
- mIsNotAWord(unigramProperty.mIsNotAWord),
- mIsBlacklisted(unigramProperty.mIsBlacklisted),
- mHasBigrams(unigramProperty.mHasBigrams),
- mHasShortcuts(unigramProperty.mHasShortcuts),
- mProbability(unigramProperty.mProbability),
- mTimestamp(unigramProperty.mTimestamp), mLevel(unigramProperty.mLevel),
- mCount(unigramProperty.mCount), mShortcutTargets(unigramProperty.mShortcutTargets),
- mShortcutProbabilities(unigramProperty.mShortcutProbabilities) {
- memcpy(mCodePoints, unigramProperty.mCodePoints, sizeof(mCodePoints));
- }
-
- UnigramProperty(const int *const codePoints, const int codePointCount,
- const bool isNotAWord, const bool isBlacklisted, const bool hasBigrams,
- const bool hasShortcuts, const int probability, const int timestamp,
- const int level, const int count,
- const std::vector<std::vector<int> > *const shortcutTargets,
- const std::vector<int> *const shortcutProbabilities)
- : mCodePoints(), mCodePointCount(codePointCount),
- mIsNotAWord(isNotAWord), mIsBlacklisted(isBlacklisted), mHasBigrams(hasBigrams),
- mHasShortcuts(hasShortcuts), mProbability(probability), mTimestamp(timestamp),
- mLevel(level), mCount(count), mShortcutTargets(*shortcutTargets),
- mShortcutProbabilities(*shortcutProbabilities) {
- memcpy(mCodePoints, codePoints, sizeof(mCodePoints));
- }
-
- void outputProperties(JNIEnv *const env, jintArray outCodePoints, jbooleanArray outFlags,
- jintArray outProbability, jintArray outHistoricalInfo, jobject outShortcutTargets,
- jobject outShortcutProbabilities) const;
-
- private:
- DISALLOW_ASSIGNMENT_OPERATOR(UnigramProperty);
-
- int mCodePoints[MAX_WORD_LENGTH];
- int mCodePointCount;
- bool mIsNotAWord;
- bool mIsBlacklisted;
- bool mHasBigrams;
- bool mHasShortcuts;
- int mProbability;
- // Historical information
- int mTimestamp;
- int mLevel;
- int mCount;
- // Shortcut
- std::vector<std::vector<int> > mShortcutTargets;
- std::vector<int> mShortcutProbabilities;
-};
-} // namespace latinime
-#endif // LATINIME_UNIGRAM_PROPERTY_H
diff --git a/native/jni/src/suggest/core/dictionary/word_property.cpp b/native/jni/src/suggest/core/dictionary/word_property.cpp
new file mode 100644
index 000000000..288e6b05e
--- /dev/null
+++ b/native/jni/src/suggest/core/dictionary/word_property.cpp
@@ -0,0 +1,76 @@
+/*
+ * 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/core/dictionary/word_property.h"
+
+namespace latinime {
+
+void WordProperty::outputProperties(JNIEnv *const env, jintArray outCodePoints,
+ jbooleanArray outFlags, jintArray outProbabilityInfo, jobject outBigramTargets,
+ jobject outBigramProbabilities, jobject outShortcutTargets,
+ jobject outShortcutProbabilities) const {
+ env->SetIntArrayRegion(outCodePoints, 0 /* start */, mCodePoints.size(), &mCodePoints[0]);
+ jboolean flags[] = {mIsNotAWord, mIsBlacklisted, mHasBigrams, mHasShortcuts};
+ env->SetBooleanArrayRegion(outFlags, 0 /* start */, NELEMS(flags), flags);
+ int probabilityInfo[] = {mProbability, mTimestamp, mLevel, mCount};
+ env->SetIntArrayRegion(outProbabilityInfo, 0 /* start */, NELEMS(probabilityInfo),
+ probabilityInfo);
+
+ jclass integerClass = env->FindClass("java/lang/Integer");
+ jmethodID intToIntegerConstructorId = env->GetMethodID(integerClass, "<init>", "(I)V");
+ jclass arrayListClass = env->FindClass("java/util/ArrayList");
+ jmethodID addMethodId = env->GetMethodID(arrayListClass, "add", "(Ljava/lang/Object;)Z");
+
+ // Output bigrams.
+ const int bigramCount = mBigrams.size();
+ for (int i = 0; i < bigramCount; ++i) {
+ const BigramProperty *const bigramProperty = &mBigrams[i];
+ const std::vector<int> *const word1CodePoints = bigramProperty->getTargetCodePoints();
+ jintArray bigramWord1CodePointArray = env->NewIntArray(word1CodePoints->size());
+ env->SetIntArrayRegion(bigramWord1CodePointArray, 0 /* start */,
+ word1CodePoints->size(), &word1CodePoints->at(0));
+ env->CallVoidMethod(outBigramTargets, addMethodId, bigramWord1CodePointArray);
+ env->DeleteLocalRef(bigramWord1CodePointArray);
+
+ int bigramProbabilityInfo[] = {bigramProperty->getProbability(),
+ bigramProperty->getTimestamp(), bigramProperty->getLevel(),
+ bigramProperty->getCount()};
+ jintArray bigramProbabilityInfoArray = env->NewIntArray(NELEMS(bigramProbabilityInfo));
+ env->SetIntArrayRegion(bigramProbabilityInfoArray, 0 /* start */,
+ NELEMS(bigramProbabilityInfo), bigramProbabilityInfo);
+ env->CallVoidMethod(outBigramProbabilities, addMethodId, bigramProbabilityInfoArray);
+ env->DeleteLocalRef(bigramProbabilityInfoArray);
+ }
+
+ // Output shortcuts.
+ const int shortcutTargetCount = mShortcuts.size();
+ for (int i = 0; i < shortcutTargetCount; ++i) {
+ const std::vector<int> *const targetCodePoints = mShortcuts[i].getTargetCodePoints();
+ jintArray shortcutTargetCodePointArray = env->NewIntArray(targetCodePoints->size());
+ env->SetIntArrayRegion(shortcutTargetCodePointArray, 0 /* start */,
+ targetCodePoints->size(), &targetCodePoints->at(0));
+ env->CallVoidMethod(outShortcutTargets, addMethodId, shortcutTargetCodePointArray);
+ env->DeleteLocalRef(shortcutTargetCodePointArray);
+ jobject integerProbability = env->NewObject(integerClass, intToIntegerConstructorId,
+ mShortcuts[i].getProbability());
+ env->CallVoidMethod(outShortcutProbabilities, addMethodId, integerProbability);
+ env->DeleteLocalRef(integerProbability);
+ }
+ env->DeleteLocalRef(integerClass);
+ env->DeleteLocalRef(arrayListClass);
+}
+
+} // namespace latinime
diff --git a/native/jni/src/suggest/core/dictionary/word_property.h b/native/jni/src/suggest/core/dictionary/word_property.h
new file mode 100644
index 000000000..40b1a91a4
--- /dev/null
+++ b/native/jni/src/suggest/core/dictionary/word_property.h
@@ -0,0 +1,121 @@
+/*
+ * 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_WORD_PROPERTY_H
+#define LATINIME_WORD_PROPERTY_H
+
+#include <cstring>
+#include <vector>
+
+#include "defines.h"
+#include "jni.h"
+
+namespace latinime {
+
+// This class is used for returning information belonging to a word to java side.
+class WordProperty {
+ public:
+ class BigramProperty {
+ public:
+ BigramProperty(const std::vector<int> *const targetCodePoints,
+ const int probability, const int timestamp, const int level, const int count)
+ : mTargetCodePoints(*targetCodePoints), mProbability(probability),
+ mTimestamp(timestamp), mLevel(level), mCount(count) {}
+
+ const std::vector<int> *getTargetCodePoints() const {
+ return &mTargetCodePoints;
+ }
+
+ int getProbability() const {
+ return mProbability;
+ }
+
+ int getTimestamp() const {
+ return mTimestamp;
+ }
+
+ int getLevel() const {
+ return mLevel;
+ }
+
+ int getCount() const {
+ return mCount;
+ }
+
+ private:
+ std::vector<int> mTargetCodePoints;
+ int mProbability;
+ int mTimestamp;
+ int mLevel;
+ int mCount;
+ };
+
+ class ShortcutProperty {
+ public:
+ ShortcutProperty(const std::vector<int> *const targetCodePoints, const int probability)
+ : mTargetCodePoints(*targetCodePoints), mProbability(probability) {}
+
+ const std::vector<int> *getTargetCodePoints() const {
+ return &mTargetCodePoints;
+ }
+
+ int getProbability() const {
+ return mProbability;
+ }
+
+ private:
+ std::vector<int> mTargetCodePoints;
+ int mProbability;
+ };
+
+ // Invalid word.
+ WordProperty()
+ : mCodePoints(), mIsNotAWord(false), mIsBlacklisted(false),
+ mHasBigrams(false), mHasShortcuts(false), mProbability(NOT_A_PROBABILITY),
+ mTimestamp(0), mLevel(0), mCount(0), mBigrams(), mShortcuts() {}
+
+ WordProperty(const std::vector<int> *const codePoints,
+ const bool isNotAWord, const bool isBlacklisted, const bool hasBigrams,
+ const bool hasShortcuts, const int probability, const int timestamp,
+ const int level, const int count, const std::vector<BigramProperty> *const bigrams,
+ const std::vector<ShortcutProperty> *const shortcuts)
+ : mCodePoints(*codePoints), mIsNotAWord(isNotAWord), mIsBlacklisted(isBlacklisted),
+ mHasBigrams(hasBigrams), mHasShortcuts(hasShortcuts), mProbability(probability),
+ mTimestamp(timestamp), mLevel(level), mCount(count), mBigrams(*bigrams),
+ mShortcuts(*shortcuts) {}
+
+ void outputProperties(JNIEnv *const env, jintArray outCodePoints, jbooleanArray outFlags,
+ jintArray outProbabilityInfo, jobject outBigramTargets, jobject outBigramProbabilities,
+ jobject outShortcutTargets, jobject outShortcutProbabilities) const;
+
+ private:
+ DISALLOW_ASSIGNMENT_OPERATOR(WordProperty);
+
+ std::vector<int> mCodePoints;
+ bool mIsNotAWord;
+ bool mIsBlacklisted;
+ bool mHasBigrams;
+ bool mHasShortcuts;
+ int mProbability;
+ // Historical information
+ int mTimestamp;
+ int mLevel;
+ int mCount;
+ std::vector<BigramProperty> mBigrams;
+ std::vector<ShortcutProperty> mShortcuts;
+};
+} // namespace latinime
+#endif // LATINIME_WORD_PROPERTY_H
diff --git a/native/jni/src/suggest/core/policy/dictionary_structure_with_buffer_policy.h b/native/jni/src/suggest/core/policy/dictionary_structure_with_buffer_policy.h
index c74a4ebbe..784419586 100644
--- a/native/jni/src/suggest/core/policy/dictionary_structure_with_buffer_policy.h
+++ b/native/jni/src/suggest/core/policy/dictionary_structure_with_buffer_policy.h
@@ -18,7 +18,7 @@
#define LATINIME_DICTIONARY_STRUCTURE_POLICY_H
#include "defines.h"
-#include "suggest/core/dictionary/unigram_property.h"
+#include "suggest/core/dictionary/word_property.h"
#include "utils/exclusive_ownership_pointer.h"
namespace latinime {
@@ -92,9 +92,14 @@ class DictionaryStructureWithBufferPolicy {
const int maxResultLength) = 0;
// Used for testing.
- virtual const UnigramProperty getUnigramProperty(const int *const codePonts,
+ virtual const WordProperty getWordProperty(const int *const codePonts,
const int codePointCount) const = 0;
+ // Method to iterate all words in the dictionary.
+ // The returned token has to be used to get the next word. If token is 0, this method newly
+ // starts iterating the dictionary.
+ virtual int getNextWordAndNextToken(const int token, int *const outCodePoints) = 0;
+
protected:
DictionaryStructureWithBufferPolicy() {}
diff --git a/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.cpp
index 37a5b3fe4..7504524f0 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.cpp
@@ -24,7 +24,7 @@ const char *const HeaderPolicy::REQUIRES_GERMAN_UMLAUT_PROCESSING_KEY =
"REQUIRES_GERMAN_UMLAUT_PROCESSING";
// TODO: Change attribute string to "IS_DECAYING_DICT".
const char *const HeaderPolicy::IS_DECAYING_DICT_KEY = "USES_FORGETTING_CURVE";
-const char *const HeaderPolicy::LAST_UPDATED_TIME_KEY = "date";
+const char *const HeaderPolicy::DATE_KEY = "date";
const char *const HeaderPolicy::LAST_DECAYED_TIME_KEY = "LAST_DECAYED_TIME";
const char *const HeaderPolicy::UNIGRAM_COUNT_KEY = "UNIGRAM_COUNT";
const char *const HeaderPolicy::BIGRAM_COUNT_KEY = "BIGRAM_COUNT";
@@ -73,13 +73,13 @@ bool HeaderPolicy::readRequiresGermanUmlautProcessing() const {
REQUIRES_GERMAN_UMLAUT_PROCESSING_KEY, false);
}
-bool HeaderPolicy::fillInAndWriteHeaderToBuffer(const bool updatesLastUpdatedTime,
- const bool updatesLastDecayedTime, const int unigramCount, const int bigramCount,
+bool HeaderPolicy::fillInAndWriteHeaderToBuffer(const bool updatesLastDecayedTime,
+ const int unigramCount, const int bigramCount,
const int extendedRegionSize, BufferWithExtendableBuffer *const outBuffer) const {
int writingPos = 0;
HeaderReadWriteUtils::AttributeMap attributeMapToWrite(mAttributeMap);
- fillInHeader(updatesLastDecayedTime, updatesLastDecayedTime,
- unigramCount, bigramCount, extendedRegionSize, &attributeMapToWrite);
+ fillInHeader(updatesLastDecayedTime, unigramCount, bigramCount,
+ extendedRegionSize, &attributeMapToWrite);
if (!HeaderReadWriteUtils::writeDictionaryVersion(outBuffer, mDictFormatVersion,
&writingPos)) {
return false;
@@ -106,18 +106,16 @@ bool HeaderPolicy::fillInAndWriteHeaderToBuffer(const bool updatesLastUpdatedTim
return true;
}
-void HeaderPolicy::fillInHeader(const bool updatesLastUpdatedTime,
- const bool updatesLastDecayedTime, const int unigramCount, const int bigramCount,
- const int extendedRegionSize, HeaderReadWriteUtils::AttributeMap *outAttributeMap) const {
+void HeaderPolicy::fillInHeader(const bool updatesLastDecayedTime, const int unigramCount,
+ const int bigramCount, const int extendedRegionSize,
+ HeaderReadWriteUtils::AttributeMap *outAttributeMap) const {
HeaderReadWriteUtils::setIntAttribute(outAttributeMap, UNIGRAM_COUNT_KEY, unigramCount);
HeaderReadWriteUtils::setIntAttribute(outAttributeMap, BIGRAM_COUNT_KEY, bigramCount);
HeaderReadWriteUtils::setIntAttribute(outAttributeMap, EXTENDED_REGION_SIZE_KEY,
extendedRegionSize);
- if (updatesLastUpdatedTime) {
- // Set current time as the last updated time.
- HeaderReadWriteUtils::setIntAttribute(outAttributeMap, LAST_UPDATED_TIME_KEY,
- TimeKeeper::peekCurrentTime());
- }
+ // Set the current time as the generation time.
+ HeaderReadWriteUtils::setIntAttribute(outAttributeMap, DATE_KEY,
+ TimeKeeper::peekCurrentTime());
if (updatesLastDecayedTime) {
// Set current time as the last updated time.
HeaderReadWriteUtils::setIntAttribute(outAttributeMap, LAST_DECAYED_TIME_KEY,
diff --git a/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h
index d65315212..a44f9f0fc 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h
@@ -39,8 +39,8 @@ class HeaderPolicy : public DictionaryHeaderStructurePolicy {
mRequiresGermanUmlautProcessing(readRequiresGermanUmlautProcessing()),
mIsDecayingDict(HeaderReadWriteUtils::readBoolAttributeValue(&mAttributeMap,
IS_DECAYING_DICT_KEY, false /* defaultValue */)),
- mLastUpdatedTime(HeaderReadWriteUtils::readIntAttributeValue(&mAttributeMap,
- LAST_UPDATED_TIME_KEY, TimeKeeper::peekCurrentTime() /* defaultValue */)),
+ mDate(HeaderReadWriteUtils::readIntAttributeValue(&mAttributeMap,
+ DATE_KEY, TimeKeeper::peekCurrentTime() /* defaultValue */)),
mLastDecayedTime(HeaderReadWriteUtils::readIntAttributeValue(&mAttributeMap,
LAST_DECAYED_TIME_KEY, TimeKeeper::peekCurrentTime() /* defaultValue */)),
mUnigramCount(HeaderReadWriteUtils::readIntAttributeValue(&mAttributeMap,
@@ -62,10 +62,10 @@ class HeaderPolicy : public DictionaryHeaderStructurePolicy {
mRequiresGermanUmlautProcessing(readRequiresGermanUmlautProcessing()),
mIsDecayingDict(HeaderReadWriteUtils::readBoolAttributeValue(&mAttributeMap,
IS_DECAYING_DICT_KEY, false /* defaultValue */)),
- mLastUpdatedTime(HeaderReadWriteUtils::readIntAttributeValue(&mAttributeMap,
- LAST_UPDATED_TIME_KEY, TimeKeeper::peekCurrentTime() /* defaultValue */)),
+ mDate(HeaderReadWriteUtils::readIntAttributeValue(&mAttributeMap,
+ DATE_KEY, TimeKeeper::peekCurrentTime() /* defaultValue */)),
mLastDecayedTime(HeaderReadWriteUtils::readIntAttributeValue(&mAttributeMap,
- LAST_UPDATED_TIME_KEY, TimeKeeper::peekCurrentTime() /* defaultValue */)),
+ DATE_KEY, TimeKeeper::peekCurrentTime() /* defaultValue */)),
mUnigramCount(0), mBigramCount(0), mExtendedRegionSize(0),
mHasHistoricalInfoOfWords(HeaderReadWriteUtils::readBoolAttributeValue(
&mAttributeMap, HAS_HISTORICAL_INFO_KEY, false /* defaultValue */)) {}
@@ -75,7 +75,7 @@ class HeaderPolicy : public DictionaryHeaderStructurePolicy {
: mDictFormatVersion(FormatUtils::UNKNOWN_VERSION), mDictionaryFlags(0), mSize(0),
mAttributeMap(), mMultiWordCostMultiplier(0.0f),
mRequiresGermanUmlautProcessing(false), mIsDecayingDict(false),
- mLastUpdatedTime(0), mLastDecayedTime(0), mUnigramCount(0), mBigramCount(0),
+ mDate(0), mLastDecayedTime(0), mUnigramCount(0), mBigramCount(0),
mExtendedRegionSize(0), mHasHistoricalInfoOfWords(false) {}
~HeaderPolicy() {}
@@ -122,8 +122,8 @@ class HeaderPolicy : public DictionaryHeaderStructurePolicy {
return mRequiresGermanUmlautProcessing;
}
- AK_FORCE_INLINE int getLastUpdatedTime() const {
- return mLastUpdatedTime;
+ AK_FORCE_INLINE int getDate() const {
+ return mDate;
}
AK_FORCE_INLINE int getLastDecayedTime() const {
@@ -149,11 +149,11 @@ class HeaderPolicy : public DictionaryHeaderStructurePolicy {
void readHeaderValueOrQuestionMark(const char *const key,
int *outValue, int outValueSize) const;
- bool fillInAndWriteHeaderToBuffer(const bool updatesLastUpdatedTime,
- const bool updatesLastDecayedTime, const int unigramCount, const int bigramCount,
+ bool fillInAndWriteHeaderToBuffer(const bool updatesLastDecayedTime,
+ const int unigramCount, const int bigramCount,
const int extendedRegionSize, BufferWithExtendableBuffer *const outBuffer) const;
- void fillInHeader(const bool updatesLastUpdatedTime, const bool updatesLastDecayedTime,
+ void fillInHeader(const bool updatesLastDecayedTime,
const int unigramCount, const int bigramCount, const int extendedRegionSize,
HeaderReadWriteUtils::AttributeMap *outAttributeMap) const;
@@ -163,7 +163,7 @@ class HeaderPolicy : public DictionaryHeaderStructurePolicy {
static const char *const MULTIPLE_WORDS_DEMOTION_RATE_KEY;
static const char *const REQUIRES_GERMAN_UMLAUT_PROCESSING_KEY;
static const char *const IS_DECAYING_DICT_KEY;
- static const char *const LAST_UPDATED_TIME_KEY;
+ static const char *const DATE_KEY;
static const char *const LAST_DECAYED_TIME_KEY;
static const char *const UNIGRAM_COUNT_KEY;
static const char *const BIGRAM_COUNT_KEY;
@@ -179,7 +179,7 @@ class HeaderPolicy : public DictionaryHeaderStructurePolicy {
const float mMultiWordCostMultiplier;
const bool mRequiresGermanUmlautProcessing;
const bool mIsDecayingDict;
- const int mLastUpdatedTime;
+ const int mDate;
const int mLastDecayedTime;
const int mUnigramCount;
const int mBigramCount;
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h
index 2adafd22b..319c81569 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h
@@ -123,10 +123,15 @@ class PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy {
}
}
- const UnigramProperty getUnigramProperty(const int *const codePoints,
+ const WordProperty getWordProperty(const int *const codePoints,
const int codePointCount) const {
- // getUnigramProperty is not supported.
- return UnigramProperty();
+ // getWordProperty is not supported.
+ return WordProperty();
+ }
+
+ int getNextWordAndNextToken(const int token, int *const outCodePoints) {
+ // getNextWordAndNextToken is not supported.
+ return 0;
}
private:
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 b4730fe68..1c420e070 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
@@ -20,7 +20,7 @@
#include "suggest/core/dicnode/dic_node.h"
#include "suggest/core/dicnode/dic_node_vector.h"
-#include "suggest/core/dictionary/unigram_property.h"
+#include "suggest/core/dictionary/word_property.h"
#include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_reading_helper.h"
#include "suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_node_reader.h"
#include "suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h"
@@ -317,22 +317,59 @@ void Ver4PatriciaTriePolicy::getProperty(const char *const query, const int quer
}
}
-const UnigramProperty Ver4PatriciaTriePolicy::getUnigramProperty(const int *const codePoints,
+const WordProperty Ver4PatriciaTriePolicy::getWordProperty(const int *const codePoints,
const int codePointCount) const {
const int ptNodePos = getTerminalPtNodePositionOfWord(codePoints, codePointCount,
false /* forceLowerCaseSearch */);
if (ptNodePos == NOT_A_DICT_POS) {
- AKLOGE("fetchUnigramProperty is called for invalid word.");
- return UnigramProperty();
+ AKLOGE("getWordProperty is called for invalid word.");
+ return WordProperty();
}
const PtNodeParams ptNodeParams = mNodeReader.fetchNodeInfoInBufferFromPtNodePos(ptNodePos);
+ std::vector<int> codePointVector(ptNodeParams.getCodePoints(),
+ ptNodeParams.getCodePoints() + ptNodeParams.getCodePointCount());
const ProbabilityEntry probabilityEntry =
mBuffers.get()->getProbabilityDictContent()->getProbabilityEntry(
ptNodeParams.getTerminalId());
const HistoricalInfo *const historicalInfo = probabilityEntry.getHistoricalInfo();
+ // Fetch bigram information.
+ std::vector<WordProperty::BigramProperty> bigrams;
+ const int bigramListPos = getBigramsPositionOfPtNode(ptNodePos);
+ if (bigramListPos != NOT_A_DICT_POS) {
+ int bigramWord1CodePoints[MAX_WORD_LENGTH];
+ const BigramDictContent *const bigramDictContent = mBuffers.get()->getBigramDictContent();
+ const TerminalPositionLookupTable *const terminalPositionLookupTable =
+ mBuffers.get()->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);
+ std::vector<int> word1(bigramWord1CodePoints,
+ bigramWord1CodePoints + codePointCount);
+ const HistoricalInfo *const historicalInfo = bigramEntry.getHistoricalInfo();
+ const int probability = bigramEntry.hasHistoricalInfo() ?
+ ForgettingCurveUtils::decodeProbability(bigramEntry.getHistoricalInfo()) :
+ bigramEntry.getProbability();
+ bigrams.push_back(WordProperty::BigramProperty(&word1, probability,
+ historicalInfo->getTimeStamp(), historicalInfo->getLevel(),
+ historicalInfo->getCount()));
+ }
+ }
// Fetch shortcut information.
- std::vector<std::vector<int> > shortcutTargets;
- std::vector<int> shortcutProbabilities;
+ std::vector<WordProperty::ShortcutProperty> shortcuts;
int shortcutPos = getShortcutPositionOfPtNode(ptNodePos);
if (shortcutPos != NOT_A_DICT_POS) {
int shortcutTarget[MAX_WORD_LENGTH];
@@ -345,15 +382,20 @@ const UnigramProperty Ver4PatriciaTriePolicy::getUnigramProperty(const int *cons
shortcutDictContent->getShortcutEntryAndAdvancePosition(MAX_WORD_LENGTH, shortcutTarget,
&shortcutTargetLength, &shortcutProbability, &hasNext, &shortcutPos);
std::vector<int> target(shortcutTarget, shortcutTarget + shortcutTargetLength);
- shortcutTargets.push_back(target);
- shortcutProbabilities.push_back(shortcutProbability);
+ shortcuts.push_back(WordProperty::ShortcutProperty(&target, shortcutProbability));
}
}
- return UnigramProperty(ptNodeParams.getCodePoints(), ptNodeParams.getCodePointCount(),
- ptNodeParams.isNotAWord(), ptNodeParams.isBlacklisted(), ptNodeParams.hasBigrams(),
+ return WordProperty(&codePointVector, ptNodeParams.isNotAWord(),
+ ptNodeParams.isBlacklisted(), ptNodeParams.hasBigrams(),
ptNodeParams.hasShortcutTargets(), ptNodeParams.getProbability(),
historicalInfo->getTimeStamp(), historicalInfo->getLevel(),
- historicalInfo->getCount(), &shortcutTargets, &shortcutProbabilities);
+ historicalInfo->getCount(), &bigrams, &shortcuts);
+}
+
+int Ver4PatriciaTriePolicy::getNextWordAndNextToken(const int token,
+ int *const outCodePoints) {
+ // TODO: Implement.
+ return 0;
}
} // namespace latinime
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 81aed20a3..1bcd4ceea 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
@@ -106,9 +106,11 @@ class Ver4PatriciaTriePolicy : public DictionaryStructureWithBufferPolicy {
void getProperty(const char *const query, const int queryLength, char *const outResult,
const int maxResultLength);
- const UnigramProperty getUnigramProperty(const int *const codePoints,
+ const WordProperty getWordProperty(const int *const codePoints,
const int codePointCount) const;
+ int getNextWordAndNextToken(const int token, int *const outCodePoints);
+
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(Ver4PatriciaTriePolicy);
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 b6f813c75..672097455 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
@@ -39,12 +39,11 @@ void Ver4PatriciaTrieWritingHelper::writeToDictFile(const char *const dictDirPat
BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE);
const int extendedRegionSize = headerPolicy->getExtendedRegionSize()
+ mBuffers->getTrieBuffer()->getUsedAdditionalBufferSize();
- if (!headerPolicy->fillInAndWriteHeaderToBuffer(false /* updatesLastUpdatedTime */,
- false /* updatesLastDecayedTime */, unigramCount, bigramCount, extendedRegionSize,
- &headerBuffer)) {
- AKLOGE("Cannot write header structure to buffer. updatesLastUpdatedTime: %d, "
+ if (!headerPolicy->fillInAndWriteHeaderToBuffer(false /* updatesLastDecayedTime */,
+ unigramCount, bigramCount, extendedRegionSize, &headerBuffer)) {
+ AKLOGE("Cannot write header structure to buffer. "
"updatesLastDecayedTime: %d, unigramCount: %d, bigramCount: %d, "
- "extendedRegionSize: %d", false, false, unigramCount, bigramCount,
+ "extendedRegionSize: %d", false, unigramCount, bigramCount,
extendedRegionSize);
return;
}
@@ -63,9 +62,8 @@ void Ver4PatriciaTrieWritingHelper::writeToDictFileWithGC(const int rootPtNodeAr
}
BufferWithExtendableBuffer headerBuffer(
BufferWithExtendableBuffer::DEFAULT_MAX_ADDITIONAL_BUFFER_SIZE);
- if (!headerPolicy->fillInAndWriteHeaderToBuffer(true /* updatesLastUpdatedTime */,
- true /* updatesLastDecayedTime */, unigramCount, bigramCount,
- 0 /* extendedRegionSize */, &headerBuffer)) {
+ if (!headerPolicy->fillInAndWriteHeaderToBuffer(true /* updatesLastDecayedTime */,
+ unigramCount, bigramCount, 0 /* extendedRegionSize */, &headerBuffer)) {
return;
}
dictBuffers.get()->flushHeaderAndDictBuffers(dictDirPath, &headerBuffer);
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.cpp
index 1e57a0b5d..84403c807 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.cpp
@@ -48,8 +48,8 @@ const char *const DictFileWritingUtils::TEMP_FILE_SUFFIX_FOR_WRITING_DICT_FILE =
HeaderPolicy headerPolicy(FormatUtils::VERSION_4, attributeMap);
Ver4DictBuffers::Ver4DictBuffersPtr dictBuffers =
Ver4DictBuffers::createVer4DictBuffers(&headerPolicy);
- headerPolicy.fillInAndWriteHeaderToBuffer(true /* updatesLastUpdatedTime */,
- true /* updatesLastDecayedTime */, 0 /* unigramCount */, 0 /* bigramCount */,
+ headerPolicy.fillInAndWriteHeaderToBuffer(true /* updatesLastDecayedTime */,
+ 0 /* unigramCount */, 0 /* bigramCount */,
0 /* extendedRegionSize */, dictBuffers.get()->getWritableHeaderBuffer());
if (!DynamicPtWritingUtils::writeEmptyDictionary(
dictBuffers.get()->getWritableTrieBuffer(), 0 /* rootPos */)) {
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserSplitTests.java b/tests/src/com/android/inputmethod/keyboard/internal/MoreKeySpecSplitTests.java
index cbe2d5960..5f301a839 100644
--- a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserSplitTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/internal/MoreKeySpecSplitTests.java
@@ -31,7 +31,7 @@ import java.util.Arrays;
import java.util.Locale;
@MediumTest
-public class KeySpecParserSplitTests extends InstrumentationTestCase {
+public class MoreKeySpecSplitTests extends InstrumentationTestCase {
private static final Locale TEST_LOCALE = Locale.ENGLISH;
final KeyboardTextsSet mTextsSet = new KeyboardTextsSet();
@@ -92,8 +92,8 @@ public class KeySpecParserSplitTests extends InstrumentationTestCase {
private void assertTextArray(final String message, final String value,
final String ... expectedArray) {
- final String resolvedActual = KeySpecParser.resolveTextReference(value, mTextsSet);
- final String[] actual = KeySpecParser.splitKeySpecs(resolvedActual);
+ final String resolvedActual = mTextsSet.resolveTextReference(value);
+ final String[] actual = MoreKeySpec.splitKeySpecs(resolvedActual);
final String[] expected = (expectedArray.length == 0) ? null : expectedArray;
assertArrayEquals(message, expected, actual);
}
@@ -117,13 +117,11 @@ public class KeySpecParserSplitTests extends InstrumentationTestCase {
private static final String SURROGATE2 = PAIR1 + PAIR2 + PAIR3;
public void testResolveNullText() {
- assertNull("resolve null", KeySpecParser.resolveTextReference(
- null, mTextsSet));
+ assertNull("resolve null", mTextsSet.resolveTextReference(null));
}
public void testResolveEmptyText() {
- assertNull("resolve empty text", KeySpecParser.resolveTextReference(
- "!text/empty_string", mTextsSet));
+ assertNull("resolve empty text", mTextsSet.resolveTextReference("!text/empty_string"));
}
public void testSplitZero() {
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTests.java b/tests/src/com/android/inputmethod/keyboard/internal/MoreKeySpecTests.java
index afb2b0343..538ba2ccf 100644
--- a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/internal/MoreKeySpecTests.java
@@ -32,7 +32,7 @@ import java.util.Arrays;
import java.util.Locale;
@SmallTest
-public class KeySpecParserTests extends AndroidTestCase {
+public class MoreKeySpecTests extends AndroidTestCase {
private final static Locale TEST_LOCALE = Locale.ENGLISH;
final KeyboardCodesSet mCodesSet = new KeyboardCodesSet();
final KeyboardTextsSet mTextsSet = new KeyboardTextsSet();
@@ -71,9 +71,10 @@ public class KeySpecParserTests extends AndroidTestCase {
mSettingsIconId = KeySpecParser.getIconId(ICON_SETTINGS);
}
- private void assertParser(String message, String moreKeySpec, String expectedLabel,
- String expectedOutputText, int expectedIcon, int expectedCode) {
- final String labelResolved = KeySpecParser.resolveTextReference(moreKeySpec, mTextsSet);
+ private void assertParser(final String message, final String moreKeySpec,
+ final String expectedLabel, final String expectedOutputText, final int expectedIcon,
+ final int expectedCode) {
+ final String labelResolved = mTextsSet.resolveTextReference(moreKeySpec);
final MoreKeySpec spec = new MoreKeySpec(labelResolved, false /* needsToUpperCase */,
Locale.US, mCodesSet);
assertEquals(message + " [label]", expectedLabel, spec.mLabel);
@@ -86,8 +87,9 @@ public class KeySpecParserTests extends AndroidTestCase {
Constants.printableCode(spec.mCode));
}
- private void assertParserError(String message, String moreKeySpec, String expectedLabel,
- String expectedOutputText, int expectedIcon, int expectedCode) {
+ private void assertParserError(final String message, final String moreKeySpec,
+ final String expectedLabel, final String expectedOutputText, final int expectedIcon,
+ final int expectedCode) {
try {
assertParser(message, moreKeySpec, expectedLabel, expectedOutputText, expectedIcon,
expectedCode);
@@ -339,7 +341,8 @@ public class KeySpecParserTests extends AndroidTestCase {
null, null, mSettingsIconId, mCodeSettings);
}
- private static void assertArrayEquals(String message, Object[] expected, Object[] actual) {
+ private static void assertArrayEquals(final String message, final Object[] expected,
+ final Object[] actual) {
if (expected == actual) {
return;
}
@@ -357,10 +360,9 @@ public class KeySpecParserTests extends AndroidTestCase {
}
}
- private static void assertInsertAdditionalMoreKeys(String message, String[] moreKeys,
- String[] additionalMoreKeys, String[] expected) {
- final String[] actual =
- KeySpecParser.insertAdditionalMoreKeys( moreKeys, additionalMoreKeys);
+ private static void assertInsertAdditionalMoreKeys(final String message,
+ final String[] moreKeys, final String[] additionalMoreKeys, final String[] expected) {
+ final String[] actual = MoreKeySpec.insertAdditionalMoreKeys(moreKeys, additionalMoreKeys);
assertArrayEquals(message, expected, actual);
}
@@ -584,10 +586,10 @@ public class KeySpecParserTests extends AndroidTestCase {
private static final String AUTO_COLUMN_ORDER = "!autoColumnOrder!";
private static final String FIXED_COLUMN_ORDER = "!fixedColumnOrder!";
- private static void assertGetBooleanValue(String message, String key, String[] moreKeys,
- String[] expected, boolean expectedValue) {
+ private static void assertGetBooleanValue(final String message, final String key,
+ final String[] moreKeys, final String[] expected, final boolean expectedValue) {
final String[] actual = Arrays.copyOf(moreKeys, moreKeys.length);
- final boolean actualValue = KeySpecParser.getBooleanValue(actual, key);
+ final boolean actualValue = MoreKeySpec.getBooleanValue(actual, key);
assertEquals(message + " [value]", expectedValue, actualValue);
assertArrayEquals(message, expected, actual);
}
@@ -622,10 +624,11 @@ public class KeySpecParserTests extends AndroidTestCase {
"a", null, "b", NEEDS_DIVIDER, "!HASLABEL!", "d" }, true);
}
- private static void assertGetIntValue(String message, String key, int defaultValue,
- String[] moreKeys, String[] expected, int expectedValue) {
+ private static void assertGetIntValue(final String message, final String key,
+ final int defaultValue, final String[] moreKeys, final String[] expected,
+ final int expectedValue) {
final String[] actual = Arrays.copyOf(moreKeys, moreKeys.length);
- final int actualValue = KeySpecParser.getIntValue(actual, key, defaultValue);
+ final int actualValue = MoreKeySpec.getIntValue(actual, key, defaultValue);
assertEquals(message + " [value]", expectedValue, actualValue);
assertArrayEquals(message, expected, actual);
}
diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
index c42765633..343ab420c 100644
--- a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
+++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
@@ -102,13 +102,13 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase {
getContext().getCacheDir());
FileUtils.deleteRecursively(file);
Map<String, String> attributeMap = new HashMap<String, String>();
- attributeMap.put(FormatSpec.FileHeader.DICTIONARY_ID_ATTRIBUTE, dictId);
- attributeMap.put(FormatSpec.FileHeader.DICTIONARY_LOCALE_ATTRIBUTE, dictId);
- attributeMap.put(FormatSpec.FileHeader.DICTIONARY_VERSION_ATTRIBUTE,
+ attributeMap.put(FormatSpec.FileHeader.DICTIONARY_ID_KEY, dictId);
+ attributeMap.put(FormatSpec.FileHeader.DICTIONARY_LOCALE_KEY, dictId);
+ attributeMap.put(FormatSpec.FileHeader.DICTIONARY_VERSION_KEY,
String.valueOf(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())));
- attributeMap.put(FormatSpec.FileHeader.USES_FORGETTING_CURVE_ATTRIBUTE,
+ attributeMap.put(FormatSpec.FileHeader.USES_FORGETTING_CURVE_KEY,
FormatSpec.FileHeader.ATTRIBUTE_VALUE_TRUE);
- attributeMap.put(FormatSpec.FileHeader.HAS_HISTORICAL_INFO_ATTRIBUTE,
+ attributeMap.put(FormatSpec.FileHeader.HAS_HISTORICAL_INFO_KEY,
FormatSpec.FileHeader.ATTRIBUTE_VALUE_TRUE);
if (BinaryDictionary.createEmptyDictFile(file.getAbsolutePath(),
FormatSpec.VERSION4, attributeMap)) {
diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java
index 73b6fdc3b..e39b46f94 100644
--- a/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java
+++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java
@@ -26,7 +26,7 @@ import com.android.inputmethod.latin.makedict.FormatSpec;
import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
import com.android.inputmethod.latin.utils.FileUtils;
import com.android.inputmethod.latin.utils.LanguageModelParam;
-import com.android.inputmethod.latin.utils.UnigramProperty;
+import com.android.inputmethod.latin.utils.WordProperty;
import java.io.File;
import java.io.IOException;
@@ -871,14 +871,15 @@ public class BinaryDictionaryTests extends AndroidTestCase {
}
}
- public void testGetUnigramProperties() {
- testGetUnigramProperties(FormatSpec.VERSION4);
+ public void testGetWordProperties() {
+ testGetWordProperties(FormatSpec.VERSION4);
}
- private void testGetUnigramProperties(final int formatVersion) {
+ private void testGetWordProperties(final int formatVersion) {
final long seed = System.currentTimeMillis();
final Random random = new Random(seed);
- final int ITERATION_COUNT = 1000;
+ final int UNIGRAM_COUNT = 1000;
+ final int BIGRAM_COUNT = 1000;
final int codePointSetSize = 20;
final int[] codePointSet = CodePointUtils.generateCodePointSet(codePointSetSize, random);
@@ -892,11 +893,16 @@ public class BinaryDictionaryTests extends AndroidTestCase {
0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
- final UnigramProperty invalidUnigramProperty =
- binaryDictionary.getUnigramProperty("dummyWord");
- assertFalse(invalidUnigramProperty.isValid());
+ final WordProperty invalidWordProperty = binaryDictionary.getWordProperty("dummyWord");
+ assertFalse(invalidWordProperty.isValid());
+
+ final ArrayList<String> words = new ArrayList<String>();
+ final HashMap<String, Integer> wordProbabilities = new HashMap<String, Integer>();
+ final HashMap<String, HashSet<String>> bigrams = new HashMap<String, HashSet<String>>();
+ final HashMap<Pair<String, String>, Integer> bigramProbabilities =
+ new HashMap<Pair<String, String>, Integer>();
- for (int i = 0; i < ITERATION_COUNT; i++) {
+ for (int i = 0; i < UNIGRAM_COUNT; i++) {
final String word = CodePointUtils.generateWord(random, codePointSet);
final int unigramProbability = random.nextInt(0xFF);
final boolean isNotAWord = random.nextBoolean();
@@ -905,17 +911,64 @@ public class BinaryDictionaryTests extends AndroidTestCase {
binaryDictionary.addUnigramWord(word, unigramProbability,
null /* shortcutTarget */, BinaryDictionary.NOT_A_PROBABILITY,
isNotAWord, isBlacklisted, BinaryDictionary.NOT_A_VALID_TIMESTAMP);
- final UnigramProperty unigramProperty =
- binaryDictionary.getUnigramProperty(word);
+ if (binaryDictionary.needsToRunGC(false /* mindsBlockByGC */)) {
+ binaryDictionary.flushWithGC();
+ }
+ words.add(word);
+ wordProbabilities.put(word, unigramProbability);
+ final WordProperty unigramProperty = binaryDictionary.getWordProperty(word);
assertEquals(word, unigramProperty.mCodePoints);
assertTrue(unigramProperty.isValid());
assertEquals(isNotAWord, unigramProperty.mIsNotAWord);
assertEquals(isBlacklisted, unigramProperty.mIsBlacklisted);
assertEquals(false, unigramProperty.mHasBigrams);
assertEquals(false, unigramProperty.mHasShortcuts);
- assertEquals(unigramProbability, unigramProperty.mProbability);
+ assertEquals(unigramProbability, unigramProperty.mProbabilityInfo.mProbability);
assertTrue(unigramProperty.mShortcutTargets.isEmpty());
}
+
+ for (int i = 0; i < BIGRAM_COUNT; i++) {
+ final int word0Index = random.nextInt(wordProbabilities.size());
+ final int word1Index = random.nextInt(wordProbabilities.size());
+ if (word0Index == word1Index) {
+ continue;
+ }
+ final String word0 = words.get(word0Index);
+ final String word1 = words.get(word1Index);
+ final int bigramProbability = random.nextInt(0xF);
+ binaryDictionary.addBigramWords(word0, word1, bigramProbability,
+ BinaryDictionary.NOT_A_VALID_TIMESTAMP);
+ if (binaryDictionary.needsToRunGC(false /* mindsBlockByGC */)) {
+ binaryDictionary.flushWithGC();
+ }
+ if (!bigrams.containsKey(word0)) {
+ final HashSet<String> bigramWord1s = new HashSet<String>();
+ bigrams.put(word0, bigramWord1s);
+ }
+ bigrams.get(word0).add(word1);
+ bigramProbabilities.put(new Pair<String, String>(word0, word1), bigramProbability);
+ }
+
+ for (int i = 0; i < words.size(); i++) {
+ final String word0 = words.get(i);
+ if (!bigrams.containsKey(word0)) {
+ continue;
+ }
+ final HashSet<String> bigramWord1s = bigrams.get(word0);
+ final WordProperty unigramProperty = binaryDictionary.getWordProperty(word0);
+ assertEquals(bigramWord1s.size(), unigramProperty.mBigramTargets.size());
+ assertEquals(unigramProperty.mBigramTargets.size(),
+ unigramProperty.mBigramProbabilityInfo.size());
+ for (int j = 0; j < unigramProperty.mBigramTargets.size(); j++) {
+ final String word1 = unigramProperty.mBigramTargets.get(j).mWord;
+ assertTrue(bigramWord1s.contains(word1));
+ final int probability = unigramProperty.mBigramTargets.get(j).mFrequency;
+ assertEquals((int)bigramProbabilities.get(new Pair<String, String>(word0, word1)),
+ probability);
+ assertEquals(unigramProperty.mBigramProbabilityInfo.get(j).mProbability,
+ probability);
+ }
+ }
}
public void testAddShortcuts() {
@@ -938,28 +991,28 @@ public class BinaryDictionaryTests extends AndroidTestCase {
binaryDictionary.addUnigramWord("aaa", unigramProbability, "zzz",
shortcutProbability, false /* isNotAWord */, false /* isBlacklisted */,
0 /* timestamp */);
- UnigramProperty unigramProperty = binaryDictionary.getUnigramProperty("aaa");
- assertEquals(1, unigramProperty.mShortcutTargets.size());
- assertEquals("zzz", unigramProperty.mShortcutTargets.get(0).mWord);
- assertEquals(shortcutProbability, unigramProperty.mShortcutTargets.get(0).mFrequency);
+ WordProperty wordProperty = binaryDictionary.getWordProperty("aaa");
+ assertEquals(1, wordProperty.mShortcutTargets.size());
+ assertEquals("zzz", wordProperty.mShortcutTargets.get(0).mWord);
+ assertEquals(shortcutProbability, wordProperty.mShortcutTargets.get(0).mFrequency);
final int updatedShortcutProbability = 2;
binaryDictionary.addUnigramWord("aaa", unigramProbability, "zzz",
updatedShortcutProbability, false /* isNotAWord */, false /* isBlacklisted */,
0 /* timestamp */);
- unigramProperty = binaryDictionary.getUnigramProperty("aaa");
- assertEquals(1, unigramProperty.mShortcutTargets.size());
- assertEquals("zzz", unigramProperty.mShortcutTargets.get(0).mWord);
+ wordProperty = binaryDictionary.getWordProperty("aaa");
+ assertEquals(1, wordProperty.mShortcutTargets.size());
+ assertEquals("zzz", wordProperty.mShortcutTargets.get(0).mWord);
assertEquals(updatedShortcutProbability,
- unigramProperty.mShortcutTargets.get(0).mFrequency);
+ wordProperty.mShortcutTargets.get(0).mFrequency);
binaryDictionary.addUnigramWord("aaa", unigramProbability, "yyy",
shortcutProbability, false /* isNotAWord */, false /* isBlacklisted */,
0 /* timestamp */);
final HashMap<String, Integer> shortcutTargets = new HashMap<String, Integer>();
shortcutTargets.put("zzz", updatedShortcutProbability);
shortcutTargets.put("yyy", shortcutProbability);
- unigramProperty = binaryDictionary.getUnigramProperty("aaa");
- assertEquals(2, unigramProperty.mShortcutTargets.size());
- for (WeightedString shortcutTarget : unigramProperty.mShortcutTargets) {
+ wordProperty = binaryDictionary.getWordProperty("aaa");
+ assertEquals(2, wordProperty.mShortcutTargets.size());
+ for (WeightedString shortcutTarget : wordProperty.mShortcutTargets) {
assertTrue(shortcutTargets.containsKey(shortcutTarget.mWord));
assertEquals((int)shortcutTargets.get(shortcutTarget.mWord), shortcutTarget.mFrequency);
shortcutTargets.remove(shortcutTarget.mWord);
@@ -967,9 +1020,9 @@ public class BinaryDictionaryTests extends AndroidTestCase {
shortcutTargets.put("zzz", updatedShortcutProbability);
shortcutTargets.put("yyy", shortcutProbability);
binaryDictionary.flushWithGC();
- unigramProperty = binaryDictionary.getUnigramProperty("aaa");
- assertEquals(2, unigramProperty.mShortcutTargets.size());
- for (WeightedString shortcutTarget : unigramProperty.mShortcutTargets) {
+ wordProperty = binaryDictionary.getWordProperty("aaa");
+ assertEquals(2, wordProperty.mShortcutTargets.size());
+ for (WeightedString shortcutTarget : wordProperty.mShortcutTargets) {
assertTrue(shortcutTargets.containsKey(shortcutTarget.mWord));
assertEquals((int)shortcutTargets.get(shortcutTarget.mWord), shortcutTarget.mFrequency);
shortcutTargets.remove(shortcutTarget.mWord);
@@ -1036,14 +1089,15 @@ public class BinaryDictionaryTests extends AndroidTestCase {
}
for (final String word : words) {
- final UnigramProperty unigramProperty = binaryDictionary.getUnigramProperty(word);
- assertEquals((int)unigramProbabilities.get(word), unigramProperty.mProbability);
+ final WordProperty wordProperty = binaryDictionary.getWordProperty(word);
+ assertEquals((int)unigramProbabilities.get(word),
+ wordProperty.mProbabilityInfo.mProbability);
if (!shortcutTargets.containsKey(word)) {
// The word does not have shortcut targets.
continue;
}
- assertEquals(shortcutTargets.get(word).size(), unigramProperty.mShortcutTargets.size());
- for (final WeightedString shortcutTarget : unigramProperty.mShortcutTargets) {
+ assertEquals(shortcutTargets.get(word).size(), wordProperty.mShortcutTargets.size());
+ for (final WeightedString shortcutTarget : wordProperty.mShortcutTargets) {
final String targetCodePonts = shortcutTarget.mWord;
assertEquals((int)shortcutTargets.get(word).get(targetCodePonts),
shortcutTarget.mFrequency);
diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java
index 8a1ac5233..715db2f9b 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java
@@ -108,6 +108,19 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase {
}
}
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ BinaryDictionary.setCurrentTimeForTest(0);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ // Quit test mode.
+ BinaryDictionary.setCurrentTimeForTest(-1);
+ }
+
private void generateWords(final int number, final Random random) {
final int[] codePointSet = CodePointUtils.generateCodePointSet(DEFAULT_CODE_POINT_SET_SIZE,
random);
diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictUtils.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictUtils.java
index f17596865..20cf9a562 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictUtils.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictUtils.java
@@ -39,13 +39,13 @@ public class BinaryDictUtils {
public static DictionaryOptions makeDictionaryOptions(final String id, final String version,
final FormatSpec.FormatOptions formatOptions) {
final DictionaryOptions options = new DictionaryOptions(new HashMap<String, String>());
- options.mAttributes.put(FileHeader.DICTIONARY_LOCALE_ATTRIBUTE, "en_US");
- options.mAttributes.put(FileHeader.DICTIONARY_ID_ATTRIBUTE, id);
- options.mAttributes.put(FileHeader.DICTIONARY_VERSION_ATTRIBUTE, version);
+ options.mAttributes.put(FileHeader.DICTIONARY_LOCALE_KEY, "en_US");
+ options.mAttributes.put(FileHeader.DICTIONARY_ID_KEY, id);
+ options.mAttributes.put(FileHeader.DICTIONARY_VERSION_KEY, version);
if (formatOptions.mHasTimestamp) {
- options.mAttributes.put(FileHeader.HAS_HISTORICAL_INFO_ATTRIBUTE,
+ options.mAttributes.put(FileHeader.HAS_HISTORICAL_INFO_KEY,
FileHeader.ATTRIBUTE_VALUE_TRUE);
- options.mAttributes.put(FileHeader.USES_FORGETTING_CURVE_ATTRIBUTE,
+ options.mAttributes.put(FileHeader.USES_FORGETTING_CURVE_KEY,
FileHeader.ATTRIBUTE_VALUE_TRUE);
}
return options;
diff --git a/tests/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtilsTests.java
index ae08b49d7..93731b3cd 100644
--- a/tests/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtilsTests.java
@@ -56,9 +56,9 @@ public class UserHistoryDictIOUtilsTests extends AndroidTestCase
private static final String TEST_DICT_FILE_EXTENSION = ".testDict";
private static final HashMap<String, String> HEADER_OPTIONS = new HashMap<String, String>();
static {
- HEADER_OPTIONS.put(FileHeader.DICTIONARY_LOCALE_ATTRIBUTE, "en_US");
- HEADER_OPTIONS.put(FileHeader.DICTIONARY_ID_ATTRIBUTE, "test");
- HEADER_OPTIONS.put(FileHeader.DICTIONARY_VERSION_ATTRIBUTE, "1000");
+ HEADER_OPTIONS.put(FileHeader.DICTIONARY_LOCALE_KEY, "en_US");
+ HEADER_OPTIONS.put(FileHeader.DICTIONARY_ID_KEY, "test");
+ HEADER_OPTIONS.put(FileHeader.DICTIONARY_VERSION_KEY, "1000");
}
/**
diff --git a/tools/dicttool/Android.mk b/tools/dicttool/Android.mk
index b7b1fe6cd..adfb920dd 100644
--- a/tools/dicttool/Android.mk
+++ b/tools/dicttool/Android.mk
@@ -49,7 +49,7 @@ USED_TARGETTED_UTILS := \
$(LATINIME_CORE_SOURCE_DIRECTORY)/utils/LocaleUtils.java \
$(LATINIME_CORE_SOURCE_DIRECTORY)/utils/ResizableIntArray.java \
$(LATINIME_CORE_SOURCE_DIRECTORY)/utils/StringUtils.java \
- $(LATINIME_CORE_SOURCE_DIRECTORY)/utils/UnigramProperty.java
+ $(LATINIME_CORE_SOURCE_DIRECTORY)/utils/WordProperty.java
DICTTOOL_ONDEVICE_TESTS_DIRECTORY := \
$(LATINIME_LOCAL_DIR)/tests/src/com/android/inputmethod/latin/makedict/
diff --git a/tools/dicttool/tests/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtilsTests.java b/tools/dicttool/tests/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtilsTests.java
index 0c11f868e..d8059e428 100644
--- a/tools/dicttool/tests/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtilsTests.java
+++ b/tools/dicttool/tests/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtilsTests.java
@@ -48,9 +48,9 @@ public class BinaryDictOffdeviceUtilsTests extends TestCase {
// Create a thrice-compressed dictionary file.
final DictionaryOptions testOptions = new DictionaryOptions(new HashMap<String, String>());
- testOptions.mAttributes.put(FormatSpec.FileHeader.DICTIONARY_VERSION_ATTRIBUTE, VERSION);
- testOptions.mAttributes.put(FormatSpec.FileHeader.DICTIONARY_LOCALE_ATTRIBUTE, LOCALE);
- testOptions.mAttributes.put(FormatSpec.FileHeader.DICTIONARY_ID_ATTRIBUTE, ID);
+ testOptions.mAttributes.put(FormatSpec.FileHeader.DICTIONARY_VERSION_KEY, VERSION);
+ testOptions.mAttributes.put(FormatSpec.FileHeader.DICTIONARY_LOCALE_KEY, LOCALE);
+ testOptions.mAttributes.put(FormatSpec.FileHeader.DICTIONARY_ID_KEY, ID);
final FusionDictionary dict = new FusionDictionary(new PtNodeArray(), testOptions);
dict.add("foo", TEST_FREQ, null, false /* isNotAWord */);
dict.add("fta", 1, null, false /* isNotAWord */);
@@ -80,11 +80,11 @@ public class BinaryDictOffdeviceUtilsTests extends TestCase {
null /* dict : an optional dictionary to add words to, or null */,
false /* deleteDictIfBroken */);
assertEquals("Wrong version attribute", VERSION, resultDict.mOptions.mAttributes.get(
- FormatSpec.FileHeader.DICTIONARY_VERSION_ATTRIBUTE));
+ FormatSpec.FileHeader.DICTIONARY_VERSION_KEY));
assertEquals("Wrong locale attribute", LOCALE, resultDict.mOptions.mAttributes.get(
- FormatSpec.FileHeader.DICTIONARY_LOCALE_ATTRIBUTE));
+ FormatSpec.FileHeader.DICTIONARY_LOCALE_KEY));
assertEquals("Wrong id attribute", ID, resultDict.mOptions.mAttributes.get(
- FormatSpec.FileHeader.DICTIONARY_ID_ATTRIBUTE));
+ FormatSpec.FileHeader.DICTIONARY_ID_KEY));
assertEquals("Dictionary can't be read back correctly",
FusionDictionary.findWordInTree(resultDict.mRootNodeArray, "foo").getFrequency(),
TEST_FREQ);
diff --git a/tools/make-keyboard-text/res/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.tmpl b/tools/make-keyboard-text/res/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.tmpl
index a8ac981e0..db1dde39f 100644
--- a/tools/make-keyboard-text/res/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.tmpl
+++ b/tools/make-keyboard-text/res/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.tmpl
@@ -18,8 +18,10 @@ package com.android.inputmethod.keyboard.internal;
import android.content.Context;
import android.content.res.Resources;
+import android.text.TextUtils;
import com.android.inputmethod.annotations.UsedForTesting;
+import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.utils.CollectionUtils;
import java.util.HashMap;
@@ -45,6 +47,10 @@ import java.util.HashMap;
* KeyboardTextsSet.java
*/
public final class KeyboardTextsSet {
+ public static final String PREFIX_TEXT = "!text/";
+ private static final char BACKSLASH = Constants.CODE_BACKSLASH;
+ private static final int MAX_STRING_REFERENCE_INDIRECTION = 10;
+
// Language to texts map.
private static final HashMap<String, String[]> sLocaleToTextsMap = CollectionUtils.newHashMap();
private static final HashMap<String, Integer> sNameToIdsMap = CollectionUtils.newHashMap();
@@ -87,6 +93,67 @@ public final class KeyboardTextsSet {
return (text == null) ? LANGUAGE_DEFAULT[id] : text;
}
+ private static int searchTextNameEnd(final String text, final int start) {
+ final int size = text.length();
+ for (int pos = start; pos < size; pos++) {
+ final char c = text.charAt(pos);
+ // Label name should be consisted of [a-zA-Z_0-9].
+ if ((c >= 'a' && c <= 'z') || c == '_' || (c >= '0' && c <= '9')) {
+ continue;
+ }
+ return pos;
+ }
+ return size;
+ }
+
+ public String resolveTextReference(final String rawText) {
+ if (TextUtils.isEmpty(rawText)) {
+ return null;
+ }
+ int level = 0;
+ String text = rawText;
+ StringBuilder sb;
+ do {
+ level++;
+ if (level >= MAX_STRING_REFERENCE_INDIRECTION) {
+ throw new RuntimeException("too many @string/resource indirection: " + text);
+ }
+
+ final int prefixLen = PREFIX_TEXT.length();
+ final int size = text.length();
+ if (size < prefixLen) {
+ return TextUtils.isEmpty(text) ? null : text;
+ }
+
+ sb = null;
+ for (int pos = 0; pos < size; pos++) {
+ final char c = text.charAt(pos);
+ if (text.startsWith(PREFIX_TEXT, pos)) {
+ if (sb == null) {
+ sb = new StringBuilder(text.substring(0, pos));
+ }
+ final int end = searchTextNameEnd(text, pos + prefixLen);
+ final String name = text.substring(pos + prefixLen, end);
+ sb.append(getText(name));
+ pos = end - 1;
+ } else if (c == BACKSLASH) {
+ if (sb != null) {
+ // Append both escape character and escaped character.
+ sb.append(text.substring(pos, Math.min(pos + 2, size)));
+ }
+ pos++;
+ } else if (sb != null) {
+ sb.append(c);
+ }
+ }
+
+ if (sb != null) {
+ text = sb.toString();
+ }
+ } while (sb != null);
+ return TextUtils.isEmpty(text) ? null : text;
+ }
+
// These texts' name should be aligned with the @string/<name> in
// values*/strings-action-keys.xml.
private static final String[] RESOURCE_NAMES = {