aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--java/proguard.flags69
-rw-r--r--java/res/layout/suggestion_word.xml3
-rw-r--r--java/res/values/styles.xml4
-rw-r--r--java/res/xml/key_styles_currency.xml8
-rw-r--r--java/res/xml/method.xml8
-rw-r--r--java/res/xml/rows_number_normal.xml2
-rw-r--r--java/res/xml/rows_phone.xml2
-rw-r--r--java/res/xml/rows_phone_symbols.xml2
-rw-r--r--java/src/com/android/inputmethod/annotations/ExternallyReferenced.java24
-rw-r--r--java/src/com/android/inputmethod/annotations/UsedForTesting.java24
-rw-r--r--java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java23
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java10
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java10
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java12
-rw-r--r--java/src/com/android/inputmethod/keyboard/MainKeyboardView.java6
-rw-r--r--java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java3
-rw-r--r--java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java32
-rw-r--r--java/src/com/android/inputmethod/keyboard/PointerTracker.java6
-rw-r--r--java/src/com/android/inputmethod/keyboard/ProximityInfo.java21
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java3
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java3
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/TouchPositionCorrection.java3
-rw-r--r--java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java22
-rw-r--r--java/src/com/android/inputmethod/latin/AutoCorrection.java23
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionary.java43
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java8
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java9
-rw-r--r--java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java15
-rw-r--r--java/src/com/android/inputmethod/latin/Dictionary.java10
-rw-r--r--java/src/com/android/inputmethod/latin/DictionaryCollection.java6
-rw-r--r--java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java14
-rw-r--r--java/src/com/android/inputmethod/latin/ExpandableDictionary.java72
-rw-r--r--java/src/com/android/inputmethod/latin/InputPointers.java8
-rw-r--r--java/src/com/android/inputmethod/latin/LastComposedWord.java4
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java97
-rw-r--r--java/src/com/android/inputmethod/latin/RichInputConnection.java30
-rw-r--r--java/src/com/android/inputmethod/latin/StringUtils.java104
-rw-r--r--java/src/com/android/inputmethod/latin/Suggest.java43
-rw-r--r--java/src/com/android/inputmethod/latin/SuggestedWords.java29
-rw-r--r--java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java4
-rw-r--r--java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java4
-rw-r--r--java/src/com/android/inputmethod/latin/UserBinaryDictionary.java2
-rw-r--r--java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java7
-rw-r--r--java/src/com/android/inputmethod/latin/UserHistoryDictionary.java28
-rw-r--r--java/src/com/android/inputmethod/latin/WordComposer.java19
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java727
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java147
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/FormatSpec.java16
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java2
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java4
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java2
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java4
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java3
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java11
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java28
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java166
-rw-r--r--native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp6
-rw-r--r--native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp12
-rw-r--r--native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp4
-rw-r--r--native/jni/src/char_utils.cpp7
-rw-r--r--native/jni/src/correction.cpp2
-rw-r--r--native/jni/src/correction.h1
-rw-r--r--native/jni/src/defines.h10
-rw-r--r--native/jni/src/dictionary.cpp7
-rw-r--r--native/jni/src/dictionary.h4
-rw-r--r--native/jni/src/geometry_utils.h19
-rw-r--r--native/jni/src/proximity_info.cpp7
-rw-r--r--native/jni/src/proximity_info.h2
-rw-r--r--native/jni/src/proximity_info_state.cpp656
-rw-r--r--native/jni/src/proximity_info_state.h42
-rw-r--r--native/jni/src/unigram_dictionary.cpp24
-rw-r--r--native/jni/src/unigram_dictionary.h5
-rw-r--r--tests/src/com/android/inputmethod/latin/BlueUnderlineTests.java6
-rw-r--r--tests/src/com/android/inputmethod/latin/InputLogicTests.java18
-rw-r--r--tests/src/com/android/inputmethod/latin/InputLogicTestsNonEnglish.java39
-rw-r--r--tests/src/com/android/inputmethod/latin/InputTestsBase.java9
-rw-r--r--tests/src/com/android/inputmethod/latin/UserHistoryDictIOUtilsTests.java2
-rw-r--r--tests/src/com/android/inputmethod/latin/UserHistoryDictionaryTests.java65
-rw-r--r--tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOTests.java9
-rw-r--r--tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java397
-rw-r--r--tools/dicttool/Android.mk6
-rw-r--r--tools/dicttool/src/com/android/inputmethod/latin/dicttool/AdditionalCommandList.java (renamed from tools/dicttool/src/android/inputmethod/latin/dicttool/AdditionalCommandList.java)0
-rw-r--r--tools/dicttool/src/com/android/inputmethod/latin/dicttool/CommandList.java (renamed from tools/dicttool/src/android/inputmethod/latin/dicttool/CommandList.java)0
-rw-r--r--tools/dicttool/src/com/android/inputmethod/latin/dicttool/Compress.java (renamed from tools/dicttool/src/android/inputmethod/latin/dicttool/Compress.java)0
-rw-r--r--tools/dicttool/src/com/android/inputmethod/latin/dicttool/DictionaryMaker.java (renamed from tools/dicttool/src/android/inputmethod/latin/dicttool/DictionaryMaker.java)4
-rw-r--r--tools/dicttool/src/com/android/inputmethod/latin/dicttool/Dicttool.java (renamed from tools/dicttool/src/android/inputmethod/latin/dicttool/Dicttool.java)0
-rw-r--r--tools/dicttool/src/com/android/inputmethod/latin/dicttool/Info.java (renamed from tools/dicttool/src/android/inputmethod/latin/dicttool/Info.java)0
-rw-r--r--tools/dicttool/src/com/android/inputmethod/latin/dicttool/Makedict.java (renamed from tools/dicttool/src/android/inputmethod/latin/dicttool/Makedict.java)0
-rw-r--r--tools/dicttool/src/com/android/inputmethod/latin/dicttool/XmlDictInputOutput.java (renamed from tools/dicttool/src/android/inputmethod/latin/dicttool/XmlDictInputOutput.java)0
-rw-r--r--tools/dicttool/src/com/android/inputmethod/latin/makedict/MakedictLog.java (renamed from tools/dicttool/src/android/inputmethod/latin/dicttool/MakedictLog.java)0
-rw-r--r--tools/dicttool/tests/com/android/inputmethod/latin/makedict/BinaryDictInputOutputTest.java14
-rwxr-xr-xtools/dicttool/tests/etc/test-dicttool.sh2
-rw-r--r--tools/maketext/res/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.tmpl3
93 files changed, 2513 insertions, 858 deletions
diff --git a/java/proguard.flags b/java/proguard.flags
index ac5b7df16..d65924f7c 100644
--- a/java/proguard.flags
+++ b/java/proguard.flags
@@ -1,64 +1,11 @@
--keep class com.android.inputmethod.latin.BinaryDictionary {
- int mDictLength;
- <init>(...);
+# Keep classes and methods that have the @UsedForTesting annotation
+-keep @com.android.inputmethod.annotations.UsedForTesting class *
+-keepclassmembers class * {
+@com.android.inputmethod.annotations.UsedForTesting *;
}
--keep class com.android.inputmethod.keyboard.ProximityInfo {
- <init>(com.android.inputmethod.keyboard.ProximityInfo);
+# Keep classes and methods that have the @ExternallyReferenced annotation
+-keep @com.android.inputmethod.annotations.ExternallyReferenced class *
+-keepclassmembers class * {
+@com.android.inputmethod.annotations.ExternallyReferenced *;
}
-
--keep class com.android.inputmethod.latin.Suggest {
- <init>(...);
- com.android.inputmethod.latin.SuggestedWords getSuggestions(...);
-}
-
--keep class com.android.inputmethod.latin.AutoCorrection {
- java.lang.CharSequence getAutoCorrectionWord();
-}
-
--keep class com.android.inputmethod.latin.Utils {
- boolean equalsIgnoreCase(...);
-}
-
--keep class com.android.inputmethod.latin.InputPointers {
- *;
-}
-
--keep class com.android.inputmethod.latin.ResizableIntArray {
- *;
-}
-
--keep class com.android.inputmethod.latin.spellcheck.SpellCheckerSettingsFragment {
- *;
-}
-
--keep class com.android.inputmethod.keyboard.MainKeyboardView {
- # Keep getter/setter methods for ObjectAnimator
- int getLanguageOnSpacebarAnimAlpha();
- void setLanguageOnSpacebarAnimAlpha(int);
- int getAltCodeKeyWhileTypingAnimAlpha();
- void setAltCodeKeyWhileTypingAnimAlpha(int);
-}
-
--keep class com.android.inputmethod.keyboard.MoreKeysKeyboard$Builder$MoreKeysKeyboardParams {
- <init>(...);
-}
-
--keepclasseswithmembernames class * {
- native <methods>;
-}
-
--keep class com.android.inputmethod.research.ResearchLogger {
- void flush();
- void publishCurrentLogUnit(...);
-}
-
--keep class com.android.inputmethod.keyboard.KeyboardLayoutSet$Builder {
- void setTouchPositionCorrectionEnabled(...);
-}
-
-# The support library contains references to newer platform versions.
-# Don't warn about those in case this app is linking against an older
-# platform version. We know about them, and they are safe.
--dontwarn android.support.v4.**
--dontwarn android.support.v13.**
diff --git a/java/res/layout/suggestion_word.xml b/java/res/layout/suggestion_word.xml
index d64cacf04..fa00e041e 100644
--- a/java/res/layout/suggestion_word.xml
+++ b/java/res/layout/suggestion_word.xml
@@ -18,6 +18,8 @@
*/
-->
+<!-- Provide a haptic feedback by ourselves based on the keyboard settings.
+ We just need to ignore the system's haptic feedback settings. -->
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
@@ -29,6 +31,7 @@
android:paddingTop="0dp"
android:paddingRight="@dimen/suggestion_padding"
android:paddingBottom="0dp"
+ android:hapticFeedbackEnabled="false"
android:focusable="false"
android:clickable="false"
android:singleLine="true"
diff --git a/java/res/values/styles.xml b/java/res/values/styles.xml
index dbb56ab4d..4766a2295 100644
--- a/java/res/values/styles.xml
+++ b/java/res/values/styles.xml
@@ -124,6 +124,7 @@
<item name="keyboardTopPadding">0dp</item>
<item name="keyboardBottomPadding">0dp</item>
<item name="horizontalGap">0dp</item>
+ <item name="touchPositionCorrectionData">@null</item>
</style>
<style
name="MoreKeysKeyboardView"
@@ -231,6 +232,7 @@
<item name="keyboardTopPadding">0dp</item>
<item name="keyboardBottomPadding">0dp</item>
<item name="horizontalGap">0dp</item>
+ <item name="touchPositionCorrectionData">@null</item>
</style>
<style
name="MoreKeysKeyboardView.Stone"
@@ -300,6 +302,7 @@
<item name="keyboardTopPadding">0dp</item>
<item name="keyboardBottomPadding">0dp</item>
<item name="horizontalGap">0dp</item>
+ <item name="touchPositionCorrectionData">@null</item>
</style>
<style
name="MoreKeysKeyboardView.Gingerbread"
@@ -355,6 +358,7 @@
<item name="keyboardTopPadding">0dp</item>
<item name="keyboardBottomPadding">0dp</item>
<item name="horizontalGap">0dp</item>
+ <item name="touchPositionCorrectionData">@null</item>
</style>
<style
name="MoreKeysKeyboardView.IceCreamSandwich"
diff --git a/java/res/xml/key_styles_currency.xml b/java/res/xml/key_styles_currency.xml
index 6dea16f89..1dadbe033 100644
--- a/java/res/xml/key_styles_currency.xml
+++ b/java/res/xml/key_styles_currency.xml
@@ -53,13 +53,13 @@
22. Spain (es_ES, ca_ES)
23. Vatican City (it_VA)
-->
- <!-- Though Denmark and Turkey don't using Euro as their currencies, but having Euro sign on
- the symbol keyboard might be useful. Especially Danish krone (kr) and Turkish lira
- (TL) can be represented by usual alphabet letters. -->
+ <!-- Though Denmark, Sweden and Turkey don't use Euro as their currency, having the Euro
+ sign on the symbol keyboard might be useful. Especially Danish krone (kr), Swedish
+ krona (kr) and Turkish lira (TL) can be represented by usual alphabet letters. -->
<!-- Note: Some locales may not have country code, and it it supposed to indicate the
country where the language originally/mainly spoken. -->
<case
- latin:localeCode="da|de|es|el|fi|fr|it|nl|sk|sl|pt_PT|tr"
+ latin:localeCode="da|de|es|el|fi|fr|it|nl|sk|sl|sv|pt_PT|tr"
>
<include
latin:keyboardLayout="@xml/key_styles_currency_euro" />
diff --git a/java/res/xml/method.xml b/java/res/xml/method.xml
index 3d360a8f0..ff8998ecf 100644
--- a/java/res/xml/method.xml
+++ b/java/res/xml/method.xml
@@ -36,6 +36,7 @@
en_GB: English Great Britain/qwerty
eo: Esperanto/spanish
es: Spanish/spanish
+ es_419: Spanish Latin America/qwerty
et: Estonian/nordic
fa: Persian/arabic
fi: Finnish/nordic
@@ -184,6 +185,13 @@
/>
<subtype android:icon="@drawable/ic_subtype_keyboard"
android:label="@string/subtype_generic"
+ android:subtypeId="1648333446"
+ android:imeSubtypeLocale="es_419"
+ android:imeSubtypeMode="keyboard"
+ android:imeSubtypeExtraValue="KeyboardLayoutSet=qwerty,AsciiCapable"
+ />
+ <subtype android:icon="@drawable/ic_subtype_keyboard"
+ android:label="@string/subtype_generic"
android:subtypeId="-332580523"
android:imeSubtypeLocale="et"
android:imeSubtypeMode="keyboard"
diff --git a/java/res/xml/rows_number_normal.xml b/java/res/xml/rows_number_normal.xml
index c59e26247..b77544bc3 100644
--- a/java/res/xml/rows_number_normal.xml
+++ b/java/res/xml/rows_number_normal.xml
@@ -33,6 +33,8 @@
latin:keyStyle="numKeyStyle" />
<Key
latin:keyLabel="-"
+ latin:moreKeys="+"
+ latin:keyLabelFlags="hasPopupHint"
latin:keyStyle="numFunctionalKeyStyle"
latin:keyWidth="fillRight" />
</Row>
diff --git a/java/res/xml/rows_phone.xml b/java/res/xml/rows_phone.xml
index 630b24ea4..9299c2aa5 100644
--- a/java/res/xml/rows_phone.xml
+++ b/java/res/xml/rows_phone.xml
@@ -34,6 +34,8 @@
latin:keyStyle="num3KeyStyle" />
<Key
latin:keyLabel="-"
+ latin:moreKeys="+"
+ latin:keyLabelFlags="hasPopupHint"
latin:keyStyle="numFunctionalKeyStyle"
latin:keyWidth="fillRight" />
</Row>
diff --git a/java/res/xml/rows_phone_symbols.xml b/java/res/xml/rows_phone_symbols.xml
index 7841c56e5..c13018e7c 100644
--- a/java/res/xml/rows_phone_symbols.xml
+++ b/java/res/xml/rows_phone_symbols.xml
@@ -37,6 +37,8 @@
latin:keyStyle="numKeyStyle" />
<Key
latin:keyLabel="-"
+ latin:moreKeys="+"
+ latin:keyLabelFlags="hasPopupHint"
latin:keyStyle="numFunctionalKeyStyle"
latin:keyWidth="fillRight" />
</Row>
diff --git a/java/src/com/android/inputmethod/annotations/ExternallyReferenced.java b/java/src/com/android/inputmethod/annotations/ExternallyReferenced.java
new file mode 100644
index 000000000..ea5f12ce2
--- /dev/null
+++ b/java/src/com/android/inputmethod/annotations/ExternallyReferenced.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.annotations;
+
+/**
+ * Denotes that the class, method or field should not be eliminated by ProGuard,
+ * because it is externally referenced. (See proguard.flags)
+ */
+public @interface ExternallyReferenced {
+}
diff --git a/java/src/com/android/inputmethod/annotations/UsedForTesting.java b/java/src/com/android/inputmethod/annotations/UsedForTesting.java
new file mode 100644
index 000000000..2ada091e4
--- /dev/null
+++ b/java/src/com/android/inputmethod/annotations/UsedForTesting.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.annotations;
+
+/**
+ * Denotes that the class, method or field should not be eliminated by ProGuard,
+ * so that unit tests can access it. (See proguard.flags)
+ */
+public @interface UsedForTesting {
+}
diff --git a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
index 159f43650..c1609ea28 100644
--- a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
+++ b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
@@ -83,14 +83,13 @@ public final class SuggestionSpanUtils {
}
public static CharSequence getTextWithAutoCorrectionIndicatorUnderline(
- Context context, CharSequence text) {
+ final Context context, final String text) {
if (TextUtils.isEmpty(text) || CONSTRUCTOR_SuggestionSpan == null
|| OBJ_FLAG_AUTO_CORRECTION == null || OBJ_SUGGESTIONS_MAX_SIZE == null
|| OBJ_FLAG_MISSPELLED == null || OBJ_FLAG_EASY_CORRECT == null) {
return text;
}
- final Spannable spannable = text instanceof Spannable
- ? (Spannable) text : new SpannableString(text);
+ final Spannable spannable = new SpannableString(text);
final Object[] args =
{ context, null, new String[] {}, (int)OBJ_FLAG_AUTO_CORRECTION,
(Class<?>) SuggestionSpanPickedNotificationReceiver.class };
@@ -104,28 +103,24 @@ public final class SuggestionSpanUtils {
return spannable;
}
- public static CharSequence getTextWithSuggestionSpan(Context context,
- CharSequence pickedWord, SuggestedWords suggestedWords, boolean dictionaryAvailable) {
+ public static CharSequence getTextWithSuggestionSpan(final Context context,
+ final String pickedWord, final SuggestedWords suggestedWords,
+ final boolean dictionaryAvailable) {
if (!dictionaryAvailable || TextUtils.isEmpty(pickedWord)
|| CONSTRUCTOR_SuggestionSpan == null
- || suggestedWords == null || suggestedWords.size() == 0
- || suggestedWords.mIsPrediction || suggestedWords.mIsPunctuationSuggestions
+ || suggestedWords.isEmpty() || suggestedWords.mIsPrediction
+ || suggestedWords.mIsPunctuationSuggestions
|| OBJ_SUGGESTIONS_MAX_SIZE == null) {
return pickedWord;
}
- final Spannable spannable;
- if (pickedWord instanceof Spannable) {
- spannable = (Spannable) pickedWord;
- } else {
- spannable = new SpannableString(pickedWord);
- }
+ final Spannable spannable = new SpannableString(pickedWord);
final ArrayList<String> suggestionsList = CollectionUtils.newArrayList();
for (int i = 0; i < suggestedWords.size(); ++i) {
if (suggestionsList.size() >= OBJ_SUGGESTIONS_MAX_SIZE) {
break;
}
- final CharSequence word = suggestedWords.getWord(i);
+ final String word = suggestedWords.getWord(i);
if (!TextUtils.equals(pickedWord, word)) {
suggestionsList.add(word.toString());
}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java
index 5c8f78f5e..e7a4e82a2 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardActionListener.java
@@ -56,11 +56,11 @@ public interface KeyboardActionListener {
public void onCodeInput(int primaryCode, int x, int y);
/**
- * Sends a sequence of characters to the listener.
+ * Sends a string of characters to the listener.
*
- * @param text the sequence of characters to be displayed.
+ * @param text the string of characters to be registered.
*/
- public void onTextInput(CharSequence text);
+ public void onTextInput(String text);
/**
* Called when user started batch input.
@@ -99,7 +99,7 @@ public interface KeyboardActionListener {
@Override
public void onCodeInput(int primaryCode, int x, int y) {}
@Override
- public void onTextInput(CharSequence text) {}
+ public void onTextInput(String text) {}
@Override
public void onStartBatchInput() {}
@Override
@@ -114,7 +114,7 @@ public interface KeyboardActionListener {
}
// TODO: Remove this method when the vertical correction is removed.
- public static boolean isInvalidCoordinate(int coordinate) {
+ public static boolean isInvalidCoordinate(final int coordinate) {
// Detect {@link Constants#NOT_A_COORDINATE},
// {@link Constants#SUGGESTION_STRIP_COORDINATE}, and
// {@link Constants#SPELL_CHECKER_COORDINATE}.
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
index c7813ab02..4d5d7e14e 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
@@ -34,6 +34,7 @@ import android.util.Xml;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodSubtype;
+import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.compat.EditorInfoCompatUtils;
import com.android.inputmethod.keyboard.internal.KeyboardBuilder;
import com.android.inputmethod.keyboard.internal.KeyboardParams;
@@ -194,12 +195,11 @@ public final class KeyboardLayoutSet {
final Params params = mParams;
final boolean isSymbols = (keyboardLayoutSetElementId == KeyboardId.ELEMENT_SYMBOLS
|| keyboardLayoutSetElementId == KeyboardId.ELEMENT_SYMBOLS_SHIFTED);
- final boolean noLanguage = SubtypeLocale.isNoLanguage(params.mSubtype);
- final boolean voiceKeyEnabled = params.mVoiceKeyEnabled && !noLanguage;
- final boolean hasShortcutKey = voiceKeyEnabled && (isSymbols != params.mVoiceKeyOnMain);
+ final boolean hasShortcutKey = params.mVoiceKeyEnabled
+ && (isSymbols != params.mVoiceKeyOnMain);
return new KeyboardId(keyboardLayoutSetElementId, params.mSubtype, params.mDeviceFormFactor,
params.mOrientation, params.mWidth, params.mMode, params.mEditorInfo,
- params.mNoSettingsKey, voiceKeyEnabled, hasShortcutKey,
+ params.mNoSettingsKey, params.mVoiceKeyEnabled, hasShortcutKey,
params.mLanguageSwitchKeyEnabled);
}
@@ -266,7 +266,7 @@ public final class KeyboardLayoutSet {
return this;
}
- // For test only
+ @UsedForTesting
public void disableTouchPositionCorrectionDataForTest() {
mParams.mDisableTouchPositionCorrectionDataForTest = true;
}
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index 38025e8e4..7e0ea1699 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -29,6 +29,7 @@ import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
import com.android.inputmethod.keyboard.KeyboardLayoutSet.KeyboardLayoutSetException;
import com.android.inputmethod.keyboard.PointerTracker.TimerProxy;
import com.android.inputmethod.keyboard.internal.KeyboardState;
+import com.android.inputmethod.latin.AudioAndHapticFeedbackManager;
import com.android.inputmethod.latin.DebugSettings;
import com.android.inputmethod.latin.ImfUtils;
import com.android.inputmethod.latin.InputView;
@@ -65,6 +66,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
new KeyboardTheme(5, R.style.KeyboardTheme_IceCreamSandwich),
};
+ private AudioAndHapticFeedbackManager mFeedbackManager;
private SubtypeSwitcher mSubtypeSwitcher;
private SharedPreferences mPrefs;
private boolean mForceNonDistinctMultitouch;
@@ -102,6 +104,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
private void initInternal(LatinIME latinIme, SharedPreferences prefs) {
mLatinIME = latinIme;
mResources = latinIme.getResources();
+ mFeedbackManager = new AudioAndHapticFeedbackManager(latinIme);
mPrefs = prefs;
mSubtypeSwitcher = SubtypeSwitcher.getInstance();
mState = new KeyboardState(this);
@@ -147,6 +150,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
mKeyboardLayoutSet = builder.build();
try {
mState.onLoadKeyboard(mResources.getString(R.string.layout_switch_back_symbols));
+ mFeedbackManager.onSettingsChanged(settingsValues);
} catch (KeyboardLayoutSetException e) {
Log.w(TAG, "loading keyboard failed: " + e.mKeyboardId, e.getCause());
LatinImeLogger.logOnException(e.mKeyboardId.toString(), e.getCause());
@@ -154,6 +158,10 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
}
}
+ public void onRingerModeChanged() {
+ mFeedbackManager.onRingerModeChanged();
+ }
+
public void saveKeyboardState() {
if (getKeyboard() != null) {
mState.onSaveKeyboardState();
@@ -202,7 +210,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
public void onPressKey(int code) {
if (isVibrateAndSoundFeedbackRequired()) {
- mLatinIME.hapticAndAudioFeedback(code);
+ mFeedbackManager.hapticAndAudioFeedback(code, mKeyboardView);
}
mState.onPressKey(code, isSinglePointer(), mLatinIME.getCurrentAutoCapsState());
}
@@ -314,7 +322,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
// Implements {@link KeyboardState.SwitchActions}.
@Override
public void hapticAndAudioFeedback(int code) {
- mLatinIME.hapticAndAudioFeedback(code);
+ mFeedbackManager.hapticAndAudioFeedback(code, mKeyboardView);
}
public void onLongPressTimeout(int code) {
diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
index 3e6f92c2a..5b6820fa6 100644
--- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
@@ -41,6 +41,7 @@ import android.widget.PopupWindow;
import com.android.inputmethod.accessibility.AccessibilityUtils;
import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
+import com.android.inputmethod.annotations.ExternallyReferenced;
import com.android.inputmethod.keyboard.PointerTracker.DrawingProxy;
import com.android.inputmethod.keyboard.PointerTracker.TimerProxy;
import com.android.inputmethod.keyboard.internal.KeyDrawParams;
@@ -417,20 +418,23 @@ public final class MainKeyboardView extends KeyboardView implements PointerTrack
return animator;
}
- // Getter/setter methods for {@link ObjectAnimator}.
+ @ExternallyReferenced
public int getLanguageOnSpacebarAnimAlpha() {
return mLanguageOnSpacebarAnimAlpha;
}
+ @ExternallyReferenced
public void setLanguageOnSpacebarAnimAlpha(final int alpha) {
mLanguageOnSpacebarAnimAlpha = alpha;
invalidateKey(mSpaceKey);
}
+ @ExternallyReferenced
public int getAltCodeKeyWhileTypingAnimAlpha() {
return mAltCodeKeyWhileTypingAnimAlpha;
}
+ @ExternallyReferenced
public void setAltCodeKeyWhileTypingAnimAlpha(final int alpha) {
mAltCodeKeyWhileTypingAnimAlpha = alpha;
updateAltCodeKeyWhileTyping();
diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java
index d7d4be40b..3826a39a4 100644
--- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboard.java
@@ -20,6 +20,7 @@ import android.graphics.Paint;
import android.graphics.drawable.Drawable;
import android.view.View;
+import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.keyboard.internal.KeyboardBuilder;
import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
import com.android.inputmethod.keyboard.internal.KeyboardParams;
@@ -39,7 +40,7 @@ public final class MoreKeysKeyboard extends Keyboard {
return mDefaultKeyCoordX;
}
- /* package for test */
+ @UsedForTesting
static class MoreKeysKeyboardParams extends KeyboardParams {
public boolean mIsFixedOrder;
/* package */int mTopRowAdjustment;
diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
index a50617693..9c450e994 100644
--- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
@@ -48,7 +48,7 @@ public final class MoreKeysKeyboardView extends KeyboardView implements MoreKeys
private final KeyboardActionListener mMoreKeysKeyboardListener =
new KeyboardActionListener.Adapter() {
@Override
- public void onCodeInput(int primaryCode, int x, int y) {
+ public void onCodeInput(final int primaryCode, final int x, final int y) {
// Because a more keys keyboard doesn't need proximity characters correction, we don't
// send touch event coordinates.
mListener.onCodeInput(
@@ -56,7 +56,7 @@ public final class MoreKeysKeyboardView extends KeyboardView implements MoreKeys
}
@Override
- public void onTextInput(CharSequence text) {
+ public void onTextInput(final String text) {
mListener.onTextInput(text);
}
@@ -66,12 +66,12 @@ public final class MoreKeysKeyboardView extends KeyboardView implements MoreKeys
}
@Override
- public void onUpdateBatchInput(InputPointers batchPointers) {
+ public void onUpdateBatchInput(final InputPointers batchPointers) {
mListener.onUpdateBatchInput(batchPointers);
}
@Override
- public void onEndBatchInput(InputPointers batchPointers) {
+ public void onEndBatchInput(final InputPointers batchPointers) {
mListener.onEndBatchInput(batchPointers);
}
@@ -81,21 +81,22 @@ public final class MoreKeysKeyboardView extends KeyboardView implements MoreKeys
}
@Override
- public void onPressKey(int primaryCode) {
+ public void onPressKey(final int primaryCode) {
mListener.onPressKey(primaryCode);
}
@Override
- public void onReleaseKey(int primaryCode, boolean withSliding) {
+ public void onReleaseKey(final int primaryCode, final boolean withSliding) {
mListener.onReleaseKey(primaryCode, withSliding);
}
};
- public MoreKeysKeyboardView(Context context, AttributeSet attrs) {
+ public MoreKeysKeyboardView(final Context context, final AttributeSet attrs) {
this(context, attrs, R.attr.moreKeysKeyboardViewStyle);
}
- public MoreKeysKeyboardView(Context context, AttributeSet attrs, int defStyle) {
+ public MoreKeysKeyboardView(final Context context, final AttributeSet attrs,
+ final int defStyle) {
super(context, attrs, defStyle);
final Resources res = context.getResources();
@@ -105,7 +106,7 @@ public final class MoreKeysKeyboardView extends KeyboardView implements MoreKeys
}
@Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
final Keyboard keyboard = getKeyboard();
if (keyboard != null) {
final int width = keyboard.mOccupiedWidth + getPaddingLeft() + getPaddingRight();
@@ -117,7 +118,7 @@ public final class MoreKeysKeyboardView extends KeyboardView implements MoreKeys
}
@Override
- public void setKeyboard(Keyboard keyboard) {
+ public void setKeyboard(final Keyboard keyboard) {
super.setKeyboard(keyboard);
mKeyDetector.setKeyboard(keyboard, -getPaddingLeft(),
-getPaddingTop() + mVerticalCorrection);
@@ -144,15 +145,16 @@ public final class MoreKeysKeyboardView extends KeyboardView implements MoreKeys
}
@Override
- public void setKeyPreviewPopupEnabled(boolean previewEnabled, int delay) {
+ public void setKeyPreviewPopupEnabled(final boolean previewEnabled, final int delay) {
// More keys keyboard needs no pop-up key preview displayed, so we pass always false with a
// delay of 0. The delay does not matter actually since the popup is not shown anyway.
super.setKeyPreviewPopupEnabled(false, 0);
}
@Override
- public void showMoreKeysPanel(View parentView, Controller controller, int pointX, int pointY,
- PopupWindow window, KeyboardActionListener listener) {
+ public void showMoreKeysPanel(final View parentView, final Controller controller,
+ final int pointX, final int pointY, final PopupWindow window,
+ final KeyboardActionListener listener) {
mController = controller;
mListener = listener;
final View container = (View)getParent();
@@ -185,12 +187,12 @@ public final class MoreKeysKeyboardView extends KeyboardView implements MoreKeys
}
@Override
- public int translateX(int x) {
+ public int translateX(final int x) {
return x - mOriginX;
}
@Override
- public int translateY(int y) {
+ public int translateY(final int y) {
return y - mOriginY;
}
}
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index d0f7cb276..92e1f5473 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -702,6 +702,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
sLastRecognitionTime = 0;
mListener.onStartBatchInput();
}
+ mTimerProxy.cancelLongPressTimer();
final boolean isOldestTracker = sPointerTrackerQueue.getOldestElement() == this;
mDrawingProxy.showGesturePreviewTrail(this, isOldestTracker);
}
@@ -807,9 +808,9 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
if (!sShouldHandleGesture) {
return;
}
- // A gesture should start only from the letter key.
+ // A gesture should start only from a non-modifier key.
mIsDetectingGesture = (mKeyboard != null) && mKeyboard.mId.isAlphabetKeyboard()
- && !mIsShowingMoreKeysPanel && key != null && Keyboard.isLetterCode(key.mCode);
+ && !mIsShowingMoreKeysPanel && key != null && !key.isModifier();
if (mIsDetectingGesture) {
if (getActivePointerTrackerCount() == 1) {
sGestureFirstDownTime = eventTime;
@@ -901,7 +902,6 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
// Register move event on gesture tracker.
onGestureMoveEvent(x, y, eventTime, true /* isMajorEvent */, key);
if (sInGesture) {
- mTimerProxy.cancelLongPressTimer();
mCurrentKey = null;
setReleasedKeyGraphics(oldKey);
return;
diff --git a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
index 06a9e9252..94fc80507 100644
--- a/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
+++ b/java/src/com/android/inputmethod/keyboard/ProximityInfo.java
@@ -18,6 +18,7 @@ package com.android.inputmethod.keyboard;
import android.graphics.Rect;
import android.text.TextUtils;
+import android.util.Log;
import com.android.inputmethod.keyboard.internal.TouchPositionCorrection;
import com.android.inputmethod.latin.Constants;
@@ -26,11 +27,14 @@ import com.android.inputmethod.latin.JniUtils;
import java.util.Arrays;
public final class ProximityInfo {
+ private static final String TAG = ProximityInfo.class.getSimpleName();
+ private static final boolean DEBUG = false;
+
/** MAX_PROXIMITY_CHARS_SIZE must be the same as MAX_PROXIMITY_CHARS_SIZE_INTERNAL
* in defines.h */
public static final int MAX_PROXIMITY_CHARS_SIZE = 16;
/** Number of key widths from current touch point to search for nearest keys. */
- private static float SEARCH_DISTANCE = 1.2f;
+ private static final float SEARCH_DISTANCE = 1.2f;
private static final Key[] EMPTY_KEY_ARRAY = new Key[0];
private static final float DEFAULT_TOUCH_POSITION_CORRECTION_RADIUS = 0.15f;
@@ -140,9 +144,13 @@ public final class ProximityInfo {
}
if (touchPositionCorrection != null && touchPositionCorrection.isValid()) {
+ if (DEBUG) {
+ Log.d(TAG, "touchPositionCorrection: ON");
+ }
sweetSpotCenterXs = new float[keyCount];
sweetSpotCenterYs = new float[keyCount];
sweetSpotRadii = new float[keyCount];
+ final int rows = touchPositionCorrection.getRows();
final float defaultRadius = DEFAULT_TOUCH_POSITION_CORRECTION_RADIUS
* (float)Math.hypot(mMostCommonKeyWidth, mMostCommonKeyHeight);
for (int i = 0; i < keyCount; i++) {
@@ -152,7 +160,7 @@ public final class ProximityInfo {
sweetSpotCenterYs[i] = hitBox.exactCenterY();
sweetSpotRadii[i] = defaultRadius;
final int row = hitBox.top / mMostCommonKeyHeight;
- if (row < touchPositionCorrection.getRows()) {
+ if (row < rows) {
final int hitBoxWidth = hitBox.width();
final int hitBoxHeight = hitBox.height();
final float hitBoxDiagonal = (float)Math.hypot(hitBoxWidth, hitBoxHeight);
@@ -160,9 +168,18 @@ public final class ProximityInfo {
sweetSpotCenterYs[i] += touchPositionCorrection.getY(row) * hitBoxHeight;
sweetSpotRadii[i] = touchPositionCorrection.getRadius(row) * hitBoxDiagonal;
}
+ if (DEBUG) {
+ Log.d(TAG, String.format(
+ " [%2d] row=%d x/y/r=%7.2f/%7.2f/%5.2f %s code=%s", i, row,
+ sweetSpotCenterXs[i], sweetSpotCenterYs[i], sweetSpotRadii[i],
+ row < rows ? "correct" : "default", Keyboard.printableCode(key.mCode)));
+ }
}
} else {
sweetSpotCenterXs = sweetSpotCenterYs = sweetSpotRadii = null;
+ if (DEBUG) {
+ Log.d(TAG, "touchPositionCorrection: OFF");
+ }
}
return setProximityInfoNative(mLocaleStr, MAX_PROXIMITY_CHARS_SIZE,
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
index b314a3795..36342688e 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
@@ -27,6 +27,7 @@ import android.util.TypedValue;
import android.util.Xml;
import android.view.InflateException;
+import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.keyboard.Key;
import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.KeyboardId;
@@ -177,7 +178,7 @@ public class KeyboardBuilder<KP extends KeyboardParams> {
return this;
}
- // For test only
+ @UsedForTesting
public void disableTouchPositionCorrectionDataForTest() {
mParams.mTouchPositionCorrection.setEnabled(false);
}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
index 3b7c6ad7a..c3875acb5 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
@@ -19,6 +19,7 @@ package com.android.inputmethod.keyboard.internal;
import android.content.Context;
import android.content.res.Resources;
+import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.latin.CollectionUtils;
import com.android.inputmethod.latin.R;
@@ -64,7 +65,7 @@ public final class KeyboardTextsSet {
loadStringResourcesInternal(context, RESOURCE_NAMES, R.string.english_ime_name);
}
- /* package for test */
+ @UsedForTesting
void loadStringResourcesInternal(Context context, final String[] resourceNames,
int referenceId) {
final Resources res = context.getResources();
diff --git a/java/src/com/android/inputmethod/keyboard/internal/TouchPositionCorrection.java b/java/src/com/android/inputmethod/keyboard/internal/TouchPositionCorrection.java
index d8950a713..d7a2b6f39 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/TouchPositionCorrection.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/TouchPositionCorrection.java
@@ -16,6 +16,7 @@
package com.android.inputmethod.keyboard.internal;
+import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.latin.LatinImeLogger;
public final class TouchPositionCorrection {
@@ -66,7 +67,7 @@ public final class TouchPositionCorrection {
}
}
- // For test only
+ @UsedForTesting
public void setEnabled(final boolean enabled) {
mEnabled = enabled;
}
diff --git a/java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java b/java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java
index 59ef5e09f..91bbd54c7 100644
--- a/java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java
+++ b/java/src/com/android/inputmethod/latin/AudioAndHapticFeedbackManager.java
@@ -31,17 +31,15 @@ import com.android.inputmethod.latin.VibratorUtils;
* complexity of settings and the like.
*/
public final class AudioAndHapticFeedbackManager {
- final private SettingsValues mSettingsValues;
- final private AudioManager mAudioManager;
- final private VibratorUtils mVibratorUtils;
+ private final AudioManager mAudioManager;
+ private final VibratorUtils mVibratorUtils;
+
+ private SettingsValues mSettingsValues;
private boolean mSoundOn;
- public AudioAndHapticFeedbackManager(final LatinIME latinIme,
- final SettingsValues settingsValues) {
- mSettingsValues = settingsValues;
+ public AudioAndHapticFeedbackManager(final LatinIME latinIme) {
mVibratorUtils = VibratorUtils.getInstance(latinIme);
mAudioManager = (AudioManager) latinIme.getSystemService(Context.AUDIO_SERVICE);
- mSoundOn = reevaluateIfSoundIsOn();
}
public void hapticAndAudioFeedback(final int primaryCode,
@@ -51,7 +49,7 @@ public final class AudioAndHapticFeedbackManager {
}
private boolean reevaluateIfSoundIsOn() {
- if (!mSettingsValues.mSoundOn || mAudioManager == null) {
+ if (mSettingsValues == null || !mSettingsValues.mSoundOn || mAudioManager == null) {
return false;
} else {
return mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_NORMAL;
@@ -81,8 +79,7 @@ public final class AudioAndHapticFeedbackManager {
}
}
- // TODO: make this private when LatinIME does not call it any more
- public void vibrate(final View viewToPerformHapticFeedbackOn) {
+ private void vibrate(final View viewToPerformHapticFeedbackOn) {
if (!mSettingsValues.mVibrateOn) {
return;
}
@@ -98,6 +95,11 @@ public final class AudioAndHapticFeedbackManager {
}
}
+ public void onSettingsChanged(final SettingsValues settingsValues) {
+ mSettingsValues = settingsValues;
+ mSoundOn = reevaluateIfSoundIsOn();
+ }
+
public void onRingerModeChanged() {
mSoundOn = reevaluateIfSoundIsOn();
}
diff --git a/java/src/com/android/inputmethod/latin/AutoCorrection.java b/java/src/com/android/inputmethod/latin/AutoCorrection.java
index 84fad158f..fa35922b0 100644
--- a/java/src/com/android/inputmethod/latin/AutoCorrection.java
+++ b/java/src/com/android/inputmethod/latin/AutoCorrection.java
@@ -33,11 +33,11 @@ public final class AutoCorrection {
}
public static boolean isValidWord(final ConcurrentHashMap<String, Dictionary> dictionaries,
- CharSequence word, boolean ignoreCase) {
+ final String word, final boolean ignoreCase) {
if (TextUtils.isEmpty(word)) {
return false;
}
- final CharSequence lowerCasedWord = word.toString().toLowerCase();
+ final String lowerCasedWord = word.toLowerCase();
for (final String key : dictionaries.keySet()) {
final Dictionary dictionary = dictionaries.get(key);
// It's unclear how realistically 'dictionary' can be null, but the monkey is somehow
@@ -57,7 +57,7 @@ public final class AutoCorrection {
}
public static int getMaxFrequency(final ConcurrentHashMap<String, Dictionary> dictionaries,
- CharSequence word) {
+ final String word) {
if (TextUtils.isEmpty(word)) {
return Dictionary.NOT_A_PROBABILITY;
}
@@ -76,12 +76,13 @@ public final class AutoCorrection {
// Returns true if this is in any of the dictionaries.
public static boolean isInTheDictionary(
final ConcurrentHashMap<String, Dictionary> dictionaries,
- final CharSequence word, final boolean ignoreCase) {
+ final String word, final boolean ignoreCase) {
return isValidWord(dictionaries, word, ignoreCase);
}
- public static boolean suggestionExceedsAutoCorrectionThreshold(SuggestedWordInfo suggestion,
- CharSequence consideredWord, float autoCorrectionThreshold) {
+ public static boolean suggestionExceedsAutoCorrectionThreshold(
+ final SuggestedWordInfo suggestion, final String consideredWord,
+ final float autoCorrectionThreshold) {
if (null != suggestion) {
// Shortlist a whitelisted word
if (suggestion.mKind == SuggestedWordInfo.KIND_WHITELIST) return true;
@@ -89,8 +90,7 @@ public final class AutoCorrection {
// TODO: when the normalized score of the first suggestion is nearly equals to
// the normalized score of the second suggestion, behave less aggressive.
final float normalizedScore = BinaryDictionary.calcNormalizedScore(
- consideredWord.toString(), suggestion.mWord.toString(),
- autoCorrectionSuggestionScore);
+ consideredWord, suggestion.mWord, autoCorrectionSuggestionScore);
if (DBG) {
Log.d(TAG, "Normalized " + consideredWord + "," + suggestion + ","
+ autoCorrectionSuggestionScore + ", " + normalizedScore
@@ -100,8 +100,7 @@ public final class AutoCorrection {
if (DBG) {
Log.d(TAG, "Auto corrected by S-threshold.");
}
- return !shouldBlockAutoCorrectionBySafetyNet(consideredWord.toString(),
- suggestion.mWord);
+ return !shouldBlockAutoCorrectionBySafetyNet(consideredWord, suggestion.mWord);
}
}
return false;
@@ -110,7 +109,7 @@ public final class AutoCorrection {
// TODO: Resolve the inconsistencies between the native auto correction algorithms and
// this safety net
public static boolean shouldBlockAutoCorrectionBySafetyNet(final String typedWord,
- final CharSequence suggestion) {
+ final String suggestion) {
// Safety net for auto correction.
// Actually if we hit this safety net, it's a bug.
// If user selected aggressive auto correction mode, there is no need to use the safety
@@ -123,7 +122,7 @@ public final class AutoCorrection {
}
final int maxEditDistanceOfNativeDictionary =
(typedWordLength < 5 ? 2 : typedWordLength / 2) + 1;
- final int distance = BinaryDictionary.editDistance(typedWord, suggestion.toString());
+ final int distance = BinaryDictionary.editDistance(typedWord, suggestion);
if (DBG) {
Log.d(TAG, "Autocorrected edit distance = " + distance
+ ", " + maxEditDistanceOfNativeDictionary);
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index 7184f1d8a..80af4b9fa 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -31,7 +31,7 @@ import java.util.Locale;
* Implements a static, compacted, binary dictionary of standard words.
*/
public final class BinaryDictionary extends Dictionary {
-
+ private static final String TAG = BinaryDictionary.class.getSimpleName();
public static final String DICTIONARY_PACK_AUTHORITY =
"com.android.inputmethod.latin.dictionarypack";
@@ -45,12 +45,9 @@ public final class BinaryDictionary extends Dictionary {
public static final int MAX_WORDS = 18;
public static final int MAX_SPACES = 16;
- private static final String TAG = BinaryDictionary.class.getSimpleName();
private static final int MAX_PREDICTIONS = 60;
private static final int MAX_RESULTS = Math.max(MAX_PREDICTIONS, MAX_WORDS);
- private static final int TYPED_LETTER_MULTIPLIER = 2;
-
private long mNativeDict;
private final Locale mLocale;
private final int[] mInputCodePoints = new int[MAX_WORD_LENGTH];
@@ -67,7 +64,7 @@ public final class BinaryDictionary extends Dictionary {
// TODO: There should be a way to remove used DicTraverseSession objects from
// {@code mDicTraverseSessions}.
- private DicTraverseSession getTraverseSession(int traverseSessionId) {
+ private DicTraverseSession getTraverseSession(final int traverseSessionId) {
synchronized(mDicTraverseSessions) {
DicTraverseSession traverseSession = mDicTraverseSessions.get(traverseSessionId);
if (traverseSession == null) {
@@ -84,7 +81,6 @@ public final class BinaryDictionary extends Dictionary {
/**
* Constructor for the binary dictionary. This is supposed to be called from the
* dictionary factory.
- * All implementations should pass null into flagArray, except for testing purposes.
* @param context the context to access the environment from.
* @param filename the name of the file to read through native code.
* @param offset the offset of the dictionary data within the file.
@@ -106,8 +102,7 @@ public final class BinaryDictionary extends Dictionary {
}
private native long openNative(String sourceDir, long dictOffset, long dictSize,
- int typedLetterMultiplier, int fullWordMultiplier, int maxWordLength, int maxWords,
- int maxPredictions);
+ int fullWordMultiplier, int maxWordLength, int maxWords, int maxPredictions);
private native void closeNative(long dict);
private native int getFrequencyNative(long dict, int[] word);
private native boolean isValidBigramNative(long dict, int[] word1, int[] word2);
@@ -120,26 +115,27 @@ public final class BinaryDictionary extends Dictionary {
private static native int editDistanceNative(char[] before, char[] after);
// TODO: Move native dict into session
- private final void loadDictionary(String path, long startOffset, long length) {
- mNativeDict = openNative(path, startOffset, length, TYPED_LETTER_MULTIPLIER,
- FULL_WORD_SCORE_MULTIPLIER, MAX_WORD_LENGTH, MAX_WORDS, MAX_PREDICTIONS);
+ private final void loadDictionary(final String path, final long startOffset,
+ final long length) {
+ mNativeDict = openNative(path, startOffset, length, FULL_WORD_SCORE_MULTIPLIER,
+ MAX_WORD_LENGTH, MAX_WORDS, MAX_PREDICTIONS);
}
@Override
public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
- final CharSequence prevWord, final ProximityInfo proximityInfo) {
+ final String prevWord, final ProximityInfo proximityInfo) {
return getSuggestionsWithSessionId(composer, prevWord, proximityInfo, 0);
}
@Override
public ArrayList<SuggestedWordInfo> getSuggestionsWithSessionId(final WordComposer composer,
- final CharSequence prevWord, final ProximityInfo proximityInfo, int sessionId) {
+ final String prevWord, final ProximityInfo proximityInfo, int sessionId) {
if (!isValidDictionary()) return null;
Arrays.fill(mInputCodePoints, Constants.NOT_A_CODE);
// TODO: toLowerCase in the native code
final int[] prevWordCodePointArray = (null == prevWord)
- ? null : StringUtils.toCodePointArray(prevWord.toString());
+ ? null : StringUtils.toCodePointArray(prevWord);
final int composerSize = composer.size();
final boolean isGesture = composer.isBatchMode();
@@ -178,15 +174,16 @@ public final class BinaryDictionary extends Dictionary {
return suggestions;
}
- /* package for test */ boolean isValidDictionary() {
+ public boolean isValidDictionary() {
return mNativeDict != 0;
}
- public static float calcNormalizedScore(String before, String after, int score) {
+ public static float calcNormalizedScore(final String before, final String after,
+ final int score) {
return calcNormalizedScoreNative(before.toCharArray(), after.toCharArray(), score);
}
- public static int editDistance(String before, String after) {
+ public static int editDistance(final String before, final String after) {
if (before == null || after == null) {
throw new IllegalArgumentException();
}
@@ -194,23 +191,23 @@ public final class BinaryDictionary extends Dictionary {
}
@Override
- public boolean isValidWord(CharSequence word) {
+ public boolean isValidWord(final String word) {
return getFrequency(word) >= 0;
}
@Override
- public int getFrequency(CharSequence word) {
+ public int getFrequency(final String word) {
if (word == null) return -1;
- int[] codePoints = StringUtils.toCodePointArray(word.toString());
+ int[] codePoints = StringUtils.toCodePointArray(word);
return getFrequencyNative(mNativeDict, codePoints);
}
// TODO: Add a batch process version (isValidBigramMultiple?) to avoid excessive numbers of jni
// calls when checking for changes in an entire dictionary.
- public boolean isValidBigram(CharSequence word1, CharSequence word2) {
+ public boolean isValidBigram(final String word1, final String word2) {
if (TextUtils.isEmpty(word1) || TextUtils.isEmpty(word2)) return false;
- int[] chars1 = StringUtils.toCodePointArray(word1.toString());
- int[] chars2 = StringUtils.toCodePointArray(word2.toString());
+ final int[] chars1 = StringUtils.toCodePointArray(word1);
+ final int[] chars2 = StringUtils.toCodePointArray(word2);
return isValidBigramNative(mNativeDict, chars1, chars2);
}
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
index b0b65edb6..bed31a7d1 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryFileDumper.java
@@ -149,7 +149,13 @@ public final class BinaryDictionaryFileDumper {
final Uri.Builder wordListUriBuilder = getProviderUriBuilder(id);
final String finalFileName = BinaryDictionaryGetter.getCacheFileName(id, locale, context);
- final String tempFileName = BinaryDictionaryGetter.getTempFileName(id, context);
+ String tempFileName;
+ try {
+ tempFileName = BinaryDictionaryGetter.getTempFileName(id, context);
+ } catch (IOException e) {
+ Log.e(TAG, "Can't open the temporary file", e);
+ return null;
+ }
for (int mode = MODE_MIN; mode <= MODE_MAX; ++mode) {
InputStream originalSourceStream = null;
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
index c747dc673..ecb61b46f 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
@@ -165,14 +165,9 @@ final class BinaryDictionaryGetter {
/**
* Generates a unique temporary file name in the app cache directory.
- *
- * This is unique as long as it doesn't get called twice in the same millisecond by the same
- * thread, which should be more than enough for our purposes.
*/
- public static String getTempFileName(String id, Context context) {
- final String fileName = replaceFileNameDangerousCharacters(id);
- return context.getCacheDir() + File.separator + fileName + "."
- + Thread.currentThread().getId() + "." + System.currentTimeMillis();
+ public static String getTempFileName(String id, Context context) throws IOException {
+ return File.createTempFile(replaceFileNameDangerousCharacters(id), null).getAbsolutePath();
}
/**
diff --git a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
index 5edc4314f..7c0448024 100644
--- a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
@@ -62,7 +62,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
*/
private final boolean mUseFirstLastBigrams;
- public ContactsBinaryDictionary(final Context context, Locale locale) {
+ public ContactsBinaryDictionary(final Context context, final Locale locale) {
super(context, getFilenameWithLocale(NAME, locale.toString()), Dictionary.TYPE_CONTACTS);
mLocale = locale;
mUseFirstLastBigrams = useFirstLastBigramsForLocale(locale);
@@ -120,7 +120,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
}
}
- private boolean useFirstLastBigramsForLocale(Locale locale) {
+ private boolean useFirstLastBigramsForLocale(final Locale locale) {
// TODO: Add firstname/lastname bigram rules for other languages.
if (locale != null && locale.getLanguage().equals(Locale.ENGLISH.getLanguage())) {
return true;
@@ -128,7 +128,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
return false;
}
- private void addWords(Cursor cursor) {
+ private void addWords(final Cursor cursor) {
clearFusionDictionary();
int count = 0;
while (!cursor.isAfterLast() && count < MAX_CONTACT_COUNT) {
@@ -160,7 +160,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
* Adds the words in a name (e.g., firstname/lastname) to the binary dictionary along with their
* bigrams depending on locale.
*/
- private void addName(String name) {
+ private void addName(final String name) {
int len = StringUtils.codePointCount(name);
String prevWord = null;
// TODO: Better tokenization for non-Latin writing systems
@@ -188,7 +188,8 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
/**
* Returns the index of the last letter in the word, starting from position startIndex.
*/
- private static int getWordEndPosition(String string, int len, int startIndex) {
+ private static int getWordEndPosition(final String string, final int len,
+ final int startIndex) {
int end;
int cp = 0;
for (end = startIndex + 1; end < len; end += Character.charCount(cp)) {
@@ -249,7 +250,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
return false;
}
- private static boolean isValidName(String name) {
+ private static boolean isValidName(final String name) {
if (name != null && -1 == name.indexOf('@')) {
return true;
}
@@ -259,7 +260,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
/**
* Checks if the words in a name are in the current binary dictionary.
*/
- private boolean isNameInDictionary(String name) {
+ private boolean isNameInDictionary(final String name) {
int len = StringUtils.codePointCount(name);
String prevWord = null;
for (int i = 0; i < len; i++) {
diff --git a/java/src/com/android/inputmethod/latin/Dictionary.java b/java/src/com/android/inputmethod/latin/Dictionary.java
index 88d0c09dd..8207bc47f 100644
--- a/java/src/com/android/inputmethod/latin/Dictionary.java
+++ b/java/src/com/android/inputmethod/latin/Dictionary.java
@@ -59,12 +59,12 @@ public abstract class Dictionary {
// TODO: pass more context than just the previous word, to enable better suggestions (n-gram
// and more)
abstract public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
- final CharSequence prevWord, final ProximityInfo proximityInfo);
+ final String prevWord, final ProximityInfo proximityInfo);
// The default implementation of this method ignores sessionId.
// Subclasses that want to use sessionId need to override this method.
public ArrayList<SuggestedWordInfo> getSuggestionsWithSessionId(final WordComposer composer,
- final CharSequence prevWord, final ProximityInfo proximityInfo, int sessionId) {
+ final String prevWord, final ProximityInfo proximityInfo, final int sessionId) {
return getSuggestions(composer, prevWord, proximityInfo);
}
@@ -73,9 +73,9 @@ public abstract class Dictionary {
* @param word the word to search for. The search should be case-insensitive.
* @return true if the word exists, false otherwise
*/
- abstract public boolean isValidWord(CharSequence word);
+ abstract public boolean isValidWord(final String word);
- public int getFrequency(CharSequence word) {
+ public int getFrequency(final String word) {
return NOT_A_PROBABILITY;
}
@@ -87,7 +87,7 @@ public abstract class Dictionary {
* @param typedWord the word to compare with
* @return true if they are the same, false otherwise.
*/
- protected boolean same(final char[] word, final int length, final CharSequence typedWord) {
+ protected boolean same(final char[] word, final int length, final String typedWord) {
if (typedWord.length() != length) {
return false;
}
diff --git a/java/src/com/android/inputmethod/latin/DictionaryCollection.java b/java/src/com/android/inputmethod/latin/DictionaryCollection.java
index d3b120989..7f78ac8a2 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryCollection.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryCollection.java
@@ -56,7 +56,7 @@ public final class DictionaryCollection extends Dictionary {
@Override
public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
- final CharSequence prevWord, final ProximityInfo proximityInfo) {
+ final String prevWord, final ProximityInfo proximityInfo) {
final CopyOnWriteArrayList<Dictionary> dictionaries = mDictionaries;
if (dictionaries.isEmpty()) return null;
// To avoid creating unnecessary objects, we get the list out of the first
@@ -74,14 +74,14 @@ public final class DictionaryCollection extends Dictionary {
}
@Override
- public boolean isValidWord(CharSequence word) {
+ public boolean isValidWord(final String word) {
for (int i = mDictionaries.size() - 1; i >= 0; --i)
if (mDictionaries.get(i).isValidWord(word)) return true;
return false;
}
@Override
- public int getFrequency(CharSequence word) {
+ public int getFrequency(final String word) {
int maxFreq = -1;
for (int i = mDictionaries.size() - 1; i >= 0; --i) {
final int tempFreq = mDictionaries.get(i).getFrequency(word);
diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
index b93c17f11..159867ade 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
@@ -198,7 +198,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
@Override
public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
- final CharSequence prevWord, final ProximityInfo proximityInfo) {
+ final String prevWord, final ProximityInfo proximityInfo) {
asyncReloadDictionaryIfRequired();
if (mLocalDictionaryController.tryLock()) {
try {
@@ -213,12 +213,12 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
}
@Override
- public boolean isValidWord(final CharSequence word) {
+ public boolean isValidWord(final String word) {
asyncReloadDictionaryIfRequired();
return isValidWordInner(word);
}
- protected boolean isValidWordInner(final CharSequence word) {
+ protected boolean isValidWordInner(final String word) {
if (mLocalDictionaryController.tryLock()) {
try {
return isValidWordLocked(word);
@@ -229,17 +229,17 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
return false;
}
- protected boolean isValidWordLocked(final CharSequence word) {
+ protected boolean isValidWordLocked(final String word) {
if (mBinaryDictionary == null) return false;
return mBinaryDictionary.isValidWord(word);
}
- protected boolean isValidBigram(final CharSequence word1, final CharSequence word2) {
+ protected boolean isValidBigram(final String word1, final String word2) {
if (mBinaryDictionary == null) return false;
return mBinaryDictionary.isValidBigram(word1, word2);
}
- protected boolean isValidBigramInner(final CharSequence word1, final CharSequence word2) {
+ protected boolean isValidBigramInner(final String word1, final String word2) {
if (mLocalDictionaryController.tryLock()) {
try {
return isValidBigramLocked(word1, word2);
@@ -250,7 +250,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
return false;
}
- protected boolean isValidBigramLocked(final CharSequence word1, final CharSequence word2) {
+ protected boolean isValidBigramLocked(final String word1, final String word2) {
if (mBinaryDictionary == null) return false;
return mBinaryDictionary.isValidBigram(word1, word2);
}
diff --git a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
index 8cdc2a0af..c6a81033d 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
@@ -69,7 +69,7 @@ public class ExpandableDictionary extends Dictionary {
mData = new Node[INCREMENT];
}
- void add(Node n) {
+ void add(final Node n) {
if (mLength + 1 > mData.length) {
Node[] tempData = new Node[mLength + INCREMENT];
if (mLength > 0) {
@@ -172,7 +172,7 @@ public class ExpandableDictionary extends Dictionary {
}
}
- public void setRequiresReload(boolean reload) {
+ public void setRequiresReload(final boolean reload) {
synchronized (mUpdatingLock) {
mRequiresReload = reload;
}
@@ -202,8 +202,8 @@ public class ExpandableDictionary extends Dictionary {
addWordRec(mRoots, word, 0, shortcutTarget, frequency, null);
}
- private void addWordRec(NodeArray children, final String word, final int depth,
- final String shortcutTarget, final int frequency, Node parentNode) {
+ private void addWordRec(final NodeArray children, final String word, final int depth,
+ final String shortcutTarget, final int frequency, final Node parentNode) {
final int wordLength = word.length();
if (wordLength <= depth) return;
final char c = word.charAt(depth);
@@ -248,7 +248,7 @@ public class ExpandableDictionary extends Dictionary {
@Override
public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
- final CharSequence prevWord, final ProximityInfo proximityInfo) {
+ final String prevWord, final ProximityInfo proximityInfo) {
if (reloadDictionaryIfRequired()) return null;
if (composer.size() > 1) {
if (composer.size() >= BinaryDictionary.MAX_WORD_LENGTH) {
@@ -267,8 +267,7 @@ public class ExpandableDictionary extends Dictionary {
// This reloads the dictionary if required, and returns whether it's currently updating its
// contents or not.
- // @VisibleForTesting
- boolean reloadDictionaryIfRequired() {
+ private boolean reloadDictionaryIfRequired() {
synchronized (mUpdatingLock) {
// If we need to update, start off a background task
if (mRequiresReload) startDictionaryLoadingTaskLocked();
@@ -277,7 +276,7 @@ public class ExpandableDictionary extends Dictionary {
}
protected ArrayList<SuggestedWordInfo> getWordsInner(final WordComposer codes,
- final CharSequence prevWordForBigrams, final ProximityInfo proximityInfo) {
+ final String prevWordForBigrams, final ProximityInfo proximityInfo) {
final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList();
mInputLength = codes.size();
if (mCodes.length < mInputLength) mCodes = new int[mInputLength][];
@@ -305,7 +304,7 @@ public class ExpandableDictionary extends Dictionary {
}
@Override
- public synchronized boolean isValidWord(CharSequence word) {
+ public synchronized boolean isValidWord(final String word) {
synchronized (mUpdatingLock) {
// If we need to update, start off a background task
if (mRequiresReload) startDictionaryLoadingTaskLocked();
@@ -320,7 +319,7 @@ public class ExpandableDictionary extends Dictionary {
return (node == null) ? false : !node.mShortcutOnly;
}
- protected boolean removeBigram(String word1, String word2) {
+ protected boolean removeBigram(final String word1, final String word2) {
// Refer to addOrSetBigram() about word1.toLowerCase()
final Node firstWord = searchWord(mRoots, word1.toLowerCase(), 0, null);
final Node secondWord = searchWord(mRoots, word2, 0, null);
@@ -345,13 +344,13 @@ public class ExpandableDictionary extends Dictionary {
/**
* Returns the word's frequency or -1 if not found
*/
- protected int getWordFrequency(CharSequence word) {
+ protected int getWordFrequency(final String word) {
// Case-sensitive search
final Node node = searchNode(mRoots, word, 0, word.length());
return (node == null) ? -1 : node.mFrequency;
}
- protected NextWord getBigramWord(String word1, String word2) {
+ protected NextWord getBigramWord(final String word1, final String word2) {
// Refer to addOrSetBigram() about word1.toLowerCase()
final Node firstWord = searchWord(mRoots, word1.toLowerCase(), 0, null);
final Node secondWord = searchWord(mRoots, word2, 0, null);
@@ -368,7 +367,8 @@ public class ExpandableDictionary extends Dictionary {
return null;
}
- private static int computeSkippedWordFinalFreq(int freq, int snr, int inputLength) {
+ private static int computeSkippedWordFinalFreq(final int freq, final int snr,
+ final int inputLength) {
// The computation itself makes sense for >= 2, but the == 2 case returns 0
// anyway so we may as well test against 3 instead and return the constant
if (inputLength >= 3) {
@@ -431,9 +431,9 @@ public class ExpandableDictionary extends Dictionary {
* @param suggestions the list in which to add suggestions
*/
// TODO: Share this routine with the native code for BinaryDictionary
- protected void getWordsRec(NodeArray roots, final WordComposer codes, final char[] word,
- final int depth, final boolean completion, int snr, int inputIndex, int skipPos,
- final ArrayList<SuggestedWordInfo> suggestions) {
+ protected void getWordsRec(final NodeArray roots, final WordComposer codes, final char[] word,
+ final int depth, final boolean completion, final int snr, final int inputIndex,
+ final int skipPos, final ArrayList<SuggestedWordInfo> suggestions) {
final int count = roots.mLength;
final int codeSize = mInputLength;
// Optimization: Prune out words that are too long compared to how much was typed.
@@ -524,11 +524,13 @@ public class ExpandableDictionary extends Dictionary {
}
}
- public int setBigramAndGetFrequency(String word1, String word2, int frequency) {
+ public int setBigramAndGetFrequency(final String word1, final String word2,
+ final int frequency) {
return setBigramAndGetFrequency(word1, word2, frequency, null /* unused */);
}
- public int setBigramAndGetFrequency(String word1, String word2, ForgettingCurveParams fcp) {
+ public int setBigramAndGetFrequency(final String word1, final String word2,
+ final ForgettingCurveParams fcp) {
return setBigramAndGetFrequency(word1, word2, 0 /* unused */, fcp);
}
@@ -540,8 +542,8 @@ public class ExpandableDictionary extends Dictionary {
* @param fcp an instance of ForgettingCurveParams to use for decay policy
* @return returns the final bigram frequency
*/
- private int setBigramAndGetFrequency(
- String word1, String word2, int frequency, ForgettingCurveParams fcp) {
+ private int setBigramAndGetFrequency(final String word1, final String word2,
+ final int frequency, final ForgettingCurveParams fcp) {
// We don't want results to be different according to case of the looked up left hand side
// word. We do want however to return the correct case for the right hand side.
// So we want to squash the case of the left hand side, and preserve that of the right
@@ -572,7 +574,8 @@ public class ExpandableDictionary extends Dictionary {
* Searches for the word and add the word if it does not exist.
* @return Returns the terminal node of the word we are searching for.
*/
- private Node searchWord(NodeArray children, String word, int depth, Node parentNode) {
+ private Node searchWord(final NodeArray children, final String word, final int depth,
+ final Node parentNode) {
final int wordLength = word.length();
final char c = word.charAt(depth);
// Does children have the current character?
@@ -602,36 +605,17 @@ public class ExpandableDictionary extends Dictionary {
return searchWord(childNode.mChildren, word, depth + 1, childNode);
}
- private void runBigramReverseLookUp(final CharSequence previousWord,
+ private void runBigramReverseLookUp(final String previousWord,
final ArrayList<SuggestedWordInfo> suggestions) {
// Search for the lowercase version of the word only, because that's where bigrams
// store their sons.
- Node prevWord = searchNode(mRoots, previousWord.toString().toLowerCase(), 0,
+ final Node prevWord = searchNode(mRoots, previousWord.toLowerCase(), 0,
previousWord.length());
if (prevWord != null && prevWord.mNGrams != null) {
reverseLookUp(prevWord.mNGrams, suggestions);
}
}
- /**
- * Used for testing purposes and in the spell checker
- * This function will wait for loading from database to be done
- */
- void waitForDictionaryLoading() {
- while (mUpdatingDictionary) {
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) {
- //
- }
- }
- }
-
- protected final void blockingReloadDictionaryIfRequired() {
- reloadDictionaryIfRequired();
- waitForDictionaryLoading();
- }
-
// Local to reverseLookUp, but do not allocate each time.
private final char[] mLookedUpString = new char[BinaryDictionary.MAX_WORD_LENGTH];
@@ -641,7 +625,7 @@ public class ExpandableDictionary extends Dictionary {
* @param terminalNodes list of terminal nodes we want to add
* @param suggestions the suggestion collection to add the word to
*/
- private void reverseLookUp(LinkedList<NextWord> terminalNodes,
+ private void reverseLookUp(final LinkedList<NextWord> terminalNodes,
final ArrayList<SuggestedWordInfo> suggestions) {
Node node;
int freq;
@@ -714,7 +698,7 @@ public class ExpandableDictionary extends Dictionary {
}
}
- private static char toLowerCase(char c) {
+ private static char toLowerCase(final char c) {
char baseChar = c;
if (c < BASE_CHARS.length) {
baseChar = BASE_CHARS[c];
diff --git a/java/src/com/android/inputmethod/latin/InputPointers.java b/java/src/com/android/inputmethod/latin/InputPointers.java
index 6b48aabb3..7dffd96dd 100644
--- a/java/src/com/android/inputmethod/latin/InputPointers.java
+++ b/java/src/com/android/inputmethod/latin/InputPointers.java
@@ -16,6 +16,8 @@
package com.android.inputmethod.latin;
+import com.android.inputmethod.annotations.UsedForTesting;
+
// TODO: This class is not thread-safe.
public final class InputPointers {
private final int mDefaultCapacity;
@@ -39,7 +41,8 @@ public final class InputPointers {
mTimes.add(index, time);
}
- public void addPointer(int x, int y, int pointerId, int time) {
+ @UsedForTesting
+ void addPointer(int x, int y, int pointerId, int time) {
mXCoordinates.add(x);
mYCoordinates.add(y);
mPointerIds.add(pointerId);
@@ -66,7 +69,8 @@ public final class InputPointers {
* @param startPos the starting index of the pointers in {@code src}.
* @param length the number of pointers to be appended.
*/
- public void append(InputPointers src, int startPos, int length) {
+ @UsedForTesting
+ void append(InputPointers src, int startPos, int length) {
if (length == 0) {
return;
}
diff --git a/java/src/com/android/inputmethod/latin/LastComposedWord.java b/java/src/com/android/inputmethod/latin/LastComposedWord.java
index 94cdc9b85..44ef01204 100644
--- a/java/src/com/android/inputmethod/latin/LastComposedWord.java
+++ b/java/src/com/android/inputmethod/latin/LastComposedWord.java
@@ -44,7 +44,7 @@ public final class LastComposedWord {
public final String mTypedWord;
public final String mCommittedWord;
public final String mSeparatorString;
- public final CharSequence mPrevWord;
+ public final String mPrevWord;
public final InputPointers mInputPointers = new InputPointers(BinaryDictionary.MAX_WORD_LENGTH);
private boolean mActive;
@@ -56,7 +56,7 @@ public final class LastComposedWord {
// immutable. Do not fiddle with their contents after you passed them to this constructor.
public LastComposedWord(final int[] primaryKeyCodes, final InputPointers inputPointers,
final String typedWord, final String committedWord,
- final String separatorString, final CharSequence prevWord) {
+ final String separatorString, final String prevWord) {
mPrimaryKeyCodes = primaryKeyCodes;
if (inputPointers != null) {
mInputPointers.copy(inputPointers);
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 1ade3c422..b4d36a64c 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -60,6 +60,7 @@ import android.view.inputmethod.InputMethodSubtype;
import com.android.inputmethod.accessibility.AccessibilityUtils;
import com.android.inputmethod.accessibility.AccessibleKeyboardViewProxy;
+import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.compat.CompatUtils;
import com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
import com.android.inputmethod.compat.InputMethodServiceCompatUtils;
@@ -132,14 +133,14 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
private View mKeyPreviewBackingView;
private View mSuggestionsContainer;
private SuggestionStripView mSuggestionStripView;
- /* package for tests */ Suggest mSuggest;
+ @UsedForTesting Suggest mSuggest;
private CompletionInfo[] mApplicationSpecifiedCompletions;
private ApplicationInfo mTargetApplicationInfo;
private InputMethodManagerCompatWrapper mImm;
private Resources mResources;
private SharedPreferences mPrefs;
- /* package for tests */ final KeyboardSwitcher mKeyboardSwitcher;
+ @UsedForTesting final KeyboardSwitcher mKeyboardSwitcher;
private final SubtypeSwitcher mSubtypeSwitcher;
private boolean mShouldSwitchToLastSubtype = true;
@@ -163,8 +164,6 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
private int mDeleteCount;
private long mLastKeyTime;
- private AudioAndHapticFeedbackManager mFeedbackManager;
-
// Member variables for remembering the current device orientation.
private int mDisplayOrientation;
@@ -173,7 +172,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
new DictionaryPackInstallBroadcastReceiver(this);
// Keeps track of most recently inserted text (multi-character key) for reverting
- private CharSequence mEnteredText;
+ private String mEnteredText;
private boolean mIsAutoCorrectionIndicatorOn;
@@ -424,7 +423,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
}
// Has to be package-visible for unit tests
- /* package for test */
+ @UsedForTesting
void loadSettings() {
// Note that the calling sequence of onCreate() and onCurrentInputMethodSubtypeChanged()
// is not guaranteed. It may even be called at the same time on a different thread.
@@ -438,7 +437,6 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
}
};
mCurrentSettings = job.runInLocale(mResources, mSubtypeSwitcher.getCurrentSubtypeLocale());
- mFeedbackManager = new AudioAndHapticFeedbackManager(this, mCurrentSettings);
resetContactsDictionary(null == mSuggest ? null : mSuggest.getContactsDictionary());
}
@@ -1096,7 +1094,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
private void commitTyped(final String separatorString) {
if (!mWordComposer.isComposingWord()) return;
- final CharSequence typedWord = mWordComposer.getTypedWord();
+ final String typedWord = mWordComposer.getTypedWord();
if (typedWord.length() > 0) {
commitChosenWord(typedWord, LastComposedWord.COMMIT_TYPE_USER_TYPED_WORD,
separatorString);
@@ -1382,7 +1380,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
// Called from PointerTracker through the KeyboardActionListener interface
@Override
- public void onTextInput(final CharSequence rawText) {
+ public void onTextInput(final String rawText) {
mConnection.beginBatchEdit();
if (mWordComposer.isComposingWord()) {
commitCurrentAutoCorrection(rawText.toString());
@@ -1390,7 +1388,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
resetComposingState(true /* alsoResetLastComposedWord */);
}
mHandler.postUpdateSuggestionStrip();
- final CharSequence text = specificTldProcessingOnTextInput(rawText);
+ final String text = specificTldProcessingOnTextInput(rawText);
if (SPACE_STATE_PHANTOM == mSpaceState) {
sendKeyCodePoint(Keyboard.CODE_SPACE);
}
@@ -1528,8 +1526,8 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
private void showGesturePreviewAndSuggestionStrip(final SuggestedWords suggestedWords,
final boolean dismissGestureFloatingPreviewText) {
- final String batchInputText = (suggestedWords.size() > 0)
- ? suggestedWords.getWord(0) : null;
+ final String batchInputText = suggestedWords.isEmpty()
+ ? null : suggestedWords.getWord(0);
final KeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
mainKeyboardView.showGestureFloatingPreviewText(batchInputText);
showSuggestionStrip(suggestedWords, null);
@@ -1547,8 +1545,8 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
public void onEndBatchInput(final InputPointers batchPointers) {
final SuggestedWords suggestedWords = BatchInputUpdater.getInstance().onEndBatchInput(
batchPointers, this);
- final String batchInputText = (suggestedWords.size() > 0)
- ? suggestedWords.getWord(0) : null;
+ final String batchInputText = suggestedWords.isEmpty()
+ ? null : suggestedWords.getWord(0);
if (TextUtils.isEmpty(batchInputText)) {
return;
}
@@ -1565,7 +1563,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
mKeyboardSwitcher.updateShiftState();
}
- private CharSequence specificTldProcessingOnTextInput(final CharSequence text) {
+ private String specificTldProcessingOnTextInput(final String text) {
if (text.length() <= 1 || text.charAt(0) != Keyboard.CODE_PERIOD
|| !Character.isLetter(text.charAt(1))) {
// Not a tld: do nothing.
@@ -1578,7 +1576,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
final CharSequence lastOne = mConnection.getTextBeforeCursor(1, 0);
if (lastOne != null && lastOne.length() == 1
&& lastOne.charAt(0) == Keyboard.CODE_PERIOD) {
- return text.subSequence(1, text.length());
+ return text.substring(1);
} else {
return text;
}
@@ -1838,7 +1836,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
return didAutoCorrect;
}
- private CharSequence getTextWithUnderline(final CharSequence text) {
+ private CharSequence getTextWithUnderline(final String text) {
return mIsAutoCorrectionIndicatorOn
? SuggestionSpanUtils.getTextWithAutoCorrectionIndicatorUnderline(this, text)
: text;
@@ -1855,7 +1853,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
// TODO: make this private
// Outside LatinIME, only used by the test suite.
- /* package for tests */
+ @UsedForTesting
boolean isShowingPunctuationList() {
if (mSuggestionStripView == null) return false;
return mCurrentSettings.mSuggestPuncList == mSuggestionStripView.getSuggestions();
@@ -1933,7 +1931,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
// whatever is *before* the half-committed word in the buffer, hence 2; if we aren't, we
// should just skip whitespace if any, so 1.
// TODO: this is slow (2-way IPC) - we should probably cache this instead.
- final CharSequence prevWord =
+ final String prevWord =
mConnection.getNthPreviousWord(mCurrentSettings.mWordSeparators,
mWordComposer.isComposingWord() ? 2 : 1);
final SuggestedWords suggestedWords = mSuggest.getSuggestedWords(mWordComposer,
@@ -1942,7 +1940,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
return maybeRetrieveOlderSuggestions(typedWord, suggestedWords);
}
- private SuggestedWords maybeRetrieveOlderSuggestions(final CharSequence typedWord,
+ private SuggestedWords maybeRetrieveOlderSuggestions(final String typedWord,
final SuggestedWords suggestedWords) {
// TODO: consolidate this into getSuggestedWords
// We update the suggestion strip only when we have some suggestions to show, i.e. when
@@ -1972,21 +1970,16 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
}
}
- private void showSuggestionStrip(final SuggestedWords suggestedWords,
- final CharSequence typedWord) {
- if (null == suggestedWords || suggestedWords.size() <= 0) {
+ private void showSuggestionStrip(final SuggestedWords suggestedWords, final String typedWord) {
+ if (suggestedWords.isEmpty()) {
clearSuggestionStrip();
return;
}
- final CharSequence autoCorrection;
- if (suggestedWords.size() > 0) {
- if (suggestedWords.mWillAutoCorrect) {
- autoCorrection = suggestedWords.getWord(1);
- } else {
- autoCorrection = typedWord;
- }
+ final String autoCorrection;
+ if (suggestedWords.mWillAutoCorrect) {
+ autoCorrection = suggestedWords.getWord(1);
} else {
- autoCorrection = null;
+ autoCorrection = typedWord;
}
mWordComposer.setAutoCorrection(autoCorrection);
final boolean isAutoCorrection = suggestedWords.willAutoCorrect();
@@ -2000,9 +1993,9 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
if (mHandler.hasPendingUpdateSuggestions()) {
updateSuggestionStrip();
}
- final CharSequence typedAutoCorrection = mWordComposer.getAutoCorrectionOrNull();
+ final String typedAutoCorrection = mWordComposer.getAutoCorrectionOrNull();
final String typedWord = mWordComposer.getTypedWord();
- final CharSequence autoCorrection = (typedAutoCorrection != null)
+ final String autoCorrection = (typedAutoCorrection != null)
? typedAutoCorrection : typedWord;
if (autoCorrection != null) {
if (TextUtils.isEmpty(typedWord)) {
@@ -2033,7 +2026,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
// Called from {@link SuggestionStripView} through the {@link SuggestionStripView#Listener}
// interface
@Override
- public void pickSuggestionManually(final int index, final CharSequence suggestion) {
+ public void pickSuggestionManually(final int index, final String suggestion) {
final SuggestedWords suggestedWords = mSuggestionStripView.getSuggestions();
// If this is a punctuation picked from the suggestion strip, pass it to onCodeInput
if (suggestion.length() == 1 && isShowingPunctuationList()) {
@@ -2118,13 +2111,13 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
/**
* Commits the chosen word to the text field and saves it for later retrieval.
*/
- private void commitChosenWord(final CharSequence chosenWord, final int commitType,
+ private void commitChosenWord(final String chosenWord, final int commitType,
final String separatorString) {
final SuggestedWords suggestedWords = mSuggestionStripView.getSuggestions();
mConnection.commitText(SuggestionSpanUtils.getTextWithSuggestionSpan(
this, chosenWord, suggestedWords, mIsMainDictionaryAvailable), 1);
// Add the word to the user history dictionary
- final CharSequence prevWord = addToUserHistoryDictionary(chosenWord);
+ final String prevWord = addToUserHistoryDictionary(chosenWord);
// TODO: figure out here if this is an auto-correct or if the best word is actually
// what user typed. Note: currently this is done much later in
// LastComposedWord#didCommitTypedWord by string equality of the remembered
@@ -2143,7 +2136,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
setSuggestionStripShown(isSuggestionsStripVisible());
}
- private CharSequence addToUserHistoryDictionary(final CharSequence suggestion) {
+ private String addToUserHistoryDictionary(final String suggestion) {
if (TextUtils.isEmpty(suggestion)) return null;
if (mSuggest == null) return null;
@@ -2158,19 +2151,18 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
= mConnection.getNthPreviousWord(mCurrentSettings.mWordSeparators, 2);
final String secondWord;
if (mWordComposer.wasAutoCapitalized() && !mWordComposer.isMostlyCaps()) {
- secondWord = suggestion.toString().toLowerCase(
- mSubtypeSwitcher.getCurrentSubtypeLocale());
+ secondWord = suggestion.toLowerCase(mSubtypeSwitcher.getCurrentSubtypeLocale());
} else {
- secondWord = suggestion.toString();
+ secondWord = suggestion;
}
// We demote unrecognized words (frequency < 0, below) by specifying them as "invalid".
// We don't add words with 0-frequency (assuming they would be profanity etc.).
final int maxFreq = AutoCorrection.getMaxFrequency(
mSuggest.getUnigramDictionaries(), suggestion);
if (maxFreq == 0) return null;
- userHistoryDictionary.addToUserHistory(null == prevWord ? null : prevWord.toString(),
- secondWord, maxFreq > 0);
- return prevWord;
+ final String prevWordString = (null == prevWord) ? null : prevWord.toString();
+ userHistoryDictionary.addToUserHistory(prevWordString, secondWord, maxFreq > 0);
+ return prevWordString;
}
return null;
}
@@ -2195,9 +2187,9 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
}
private void revertCommit() {
- final CharSequence previousWord = mLastComposedWord.mPrevWord;
+ final String previousWord = mLastComposedWord.mPrevWord;
final String originallyTypedWord = mLastComposedWord.mTypedWord;
- final CharSequence committedWord = mLastComposedWord.mCommittedWord;
+ final String committedWord = mLastComposedWord.mCommittedWord;
final int cancelLength = committedWord.length();
final int separatorLength = LastComposedWord.getSeparatorLength(
mLastComposedWord.mSeparatorString);
@@ -2207,9 +2199,9 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
if (mWordComposer.isComposingWord()) {
throw new RuntimeException("revertCommit, but we are composing a word");
}
- final String wordBeforeCursor =
+ final CharSequence wordBeforeCursor =
mConnection.getTextBeforeCursor(deleteLength, 0)
- .subSequence(0, cancelLength).toString();
+ .subSequence(0, cancelLength);
if (!TextUtils.equals(committedWord, wordBeforeCursor)) {
throw new RuntimeException("revertCommit check failed: we thought we were "
+ "reverting \"" + committedWord
@@ -2243,7 +2235,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
// TODO: Make this private
// Outside LatinIME, only used by the {@link InputTestsBase} test suite.
- /* package for test */
+ @UsedForTesting
void loadKeyboard() {
// When the device locale is changed in SetupWizard etc., this method may get called via
// onConfigurationChanged before SoftInputWindow is shown.
@@ -2259,13 +2251,6 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
mHandler.postUpdateSuggestionStrip();
}
- // TODO: Remove this method from {@link LatinIME} and move {@link FeedbackManager} to
- // {@link KeyboardSwitcher}. Called from KeyboardSwitcher
- public void hapticAndAudioFeedback(final int primaryCode) {
- mFeedbackManager.hapticAndAudioFeedback(
- primaryCode, mKeyboardSwitcher.getMainKeyboardView());
- }
-
// Callback called by PointerTracker through the KeyboardActionListener. This is called when a
// key is depressed; release matching call is onReleaseKey below.
@Override
@@ -2311,7 +2296,7 @@ public final class LatinIME extends InputMethodService implements KeyboardAction
if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
mSubtypeSwitcher.onNetworkStateChanged(intent);
} else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
- mFeedbackManager.onRingerModeChanged();
+ mKeyboardSwitcher.onRingerModeChanged();
}
}
};
diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java
index 21441369e..bf2dfbc0b 100644
--- a/java/src/com/android/inputmethod/latin/RichInputConnection.java
+++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java
@@ -398,7 +398,7 @@ public final class RichInputConnection {
if (DEBUG_PREVIOUS_TEXT) checkConsistencyForDebug();
}
- public CharSequence getNthPreviousWord(final String sentenceSeperators, final int n) {
+ public String getNthPreviousWord(final String sentenceSeperators, final int n) {
mIC = mParent.getCurrentInputConnection();
if (null == mIC) return null;
final CharSequence prev = mIC.getTextBeforeCursor(LOOKBACK_CHARACTER_NUM, 0);
@@ -466,19 +466,22 @@ public final class RichInputConnection {
// (n = 2) "abc|" -> null
// (n = 2) "abc |" -> null
// (n = 2) "abc. def|" -> null
- public static CharSequence getNthPreviousWord(final CharSequence prev,
+ public static String getNthPreviousWord(final CharSequence prev,
final String sentenceSeperators, final int n) {
if (prev == null) return null;
- String[] w = spaceRegex.split(prev);
+ final String[] w = spaceRegex.split(prev);
// If we can't find n words, or we found an empty word, return null.
- if (w.length < n || w[w.length - n].length() <= 0) return null;
+ if (w.length < n) return null;
+ final String nthPrevWord = w[w.length - n];
+ final int length = nthPrevWord.length();
+ if (length <= 0) return null;
// If ends in a separator, return null
- char lastChar = w[w.length - n].charAt(w[w.length - n].length() - 1);
+ final char lastChar = nthPrevWord.charAt(length - 1);
if (sentenceSeperators.contains(String.valueOf(lastChar))) return null;
- return w[w.length - n];
+ return nthPrevWord;
}
/**
@@ -511,19 +514,20 @@ public final class RichInputConnection {
* be included in the returned range
* @return a range containing the text surrounding the cursor
*/
- public Range getWordRangeAtCursor(String sep, int additionalPrecedingWordsCount) {
+ public Range getWordRangeAtCursor(final String sep, final int additionalPrecedingWordsCount) {
mIC = mParent.getCurrentInputConnection();
if (mIC == null || sep == null) {
return null;
}
- CharSequence before = mIC.getTextBeforeCursor(1000, 0);
- CharSequence after = mIC.getTextAfterCursor(1000, 0);
+ final CharSequence before = mIC.getTextBeforeCursor(1000, 0);
+ final CharSequence after = mIC.getTextAfterCursor(1000, 0);
if (before == null || after == null) {
return null;
}
// Going backward, alternate skipping non-separators and separators until enough words
// have been read.
+ int count = additionalPrecedingWordsCount;
int start = before.length();
boolean isStoppingAtWhitespace = true; // toggles to indicate what to stop at
while (true) { // see comments below for why this is guaranteed to halt
@@ -540,7 +544,7 @@ public final class RichInputConnection {
// isStoppingAtWhitespace is true every other time through the loop,
// so additionalPrecedingWordsCount is guaranteed to become < 0, which
// guarantees outer loop termination
- if (isStoppingAtWhitespace && (--additionalPrecedingWordsCount < 0)) {
+ if (isStoppingAtWhitespace && (--count < 0)) {
break; // outer loop
}
isStoppingAtWhitespace = !isStoppingAtWhitespace;
@@ -558,7 +562,7 @@ public final class RichInputConnection {
}
}
- int cursor = getCursorPosition();
+ final int cursor = getCursorPosition();
if (start >= 0 && cursor + end <= after.length() + before.length()) {
String word = before.toString().substring(start, before.length())
+ after.toString().substring(0, end);
@@ -569,8 +573,8 @@ public final class RichInputConnection {
}
public boolean isCursorTouchingWord(final SettingsValues settingsValues) {
- CharSequence before = getTextBeforeCursor(1, 0);
- CharSequence after = getTextAfterCursor(1, 0);
+ final CharSequence before = getTextBeforeCursor(1, 0);
+ final CharSequence after = getTextAfterCursor(1, 0);
if (!TextUtils.isEmpty(before) && !settingsValues.isWordSeparator(before.charAt(0))
&& !settingsValues.isSymbolExcludedFromWordSeparators(before.charAt(0))) {
return true;
diff --git a/java/src/com/android/inputmethod/latin/StringUtils.java b/java/src/com/android/inputmethod/latin/StringUtils.java
index df7709892..44b75b568 100644
--- a/java/src/com/android/inputmethod/latin/StringUtils.java
+++ b/java/src/com/android/inputmethod/latin/StringUtils.java
@@ -16,9 +16,11 @@
package com.android.inputmethod.latin;
+import android.text.InputType;
import android.text.TextUtils;
-import com.android.inputmethod.keyboard.Keyboard; // For character constants
+//For character constants
+import com.android.inputmethod.keyboard.Keyboard;
import java.util.ArrayList;
import java.util.Locale;
@@ -28,30 +30,30 @@ public final class StringUtils {
// This utility class is not publicly instantiable.
}
- public static int codePointCount(String text) {
+ public static int codePointCount(final String text) {
if (TextUtils.isEmpty(text)) return 0;
return text.codePointCount(0, text.length());
}
- public static boolean containsInArray(String key, String[] array) {
+ public static boolean containsInArray(final String key, final String[] array) {
for (final String element : array) {
if (key.equals(element)) return true;
}
return false;
}
- public static boolean containsInCsv(String key, String csv) {
+ public static boolean containsInCsv(final String key, final String csv) {
if (TextUtils.isEmpty(csv)) return false;
return containsInArray(key, csv.split(","));
}
- public static String appendToCsvIfNotExists(String key, String csv) {
+ public static String appendToCsvIfNotExists(final String key, final String csv) {
if (TextUtils.isEmpty(csv)) return key;
if (containsInCsv(key, csv)) return csv;
return csv + "," + key;
}
- public static String removeFromCsvIfExists(String key, String csv) {
+ public static String removeFromCsvIfExists(final String key, final String csv) {
if (TextUtils.isEmpty(csv)) return "";
final String[] elements = csv.split(",");
if (!containsInArray(key, elements)) return csv;
@@ -63,82 +65,20 @@ public final class StringUtils {
}
/**
- * Returns true if a and b are equal ignoring the case of the character.
- * @param a first character to check
- * @param b second character to check
- * @return {@code true} if a and b are equal, {@code false} otherwise.
- */
- public static boolean equalsIgnoreCase(char a, char b) {
- // Some language, such as Turkish, need testing both cases.
- return a == b
- || Character.toLowerCase(a) == Character.toLowerCase(b)
- || Character.toUpperCase(a) == Character.toUpperCase(b);
- }
-
- /**
- * Returns true if a and b are equal ignoring the case of the characters, including if they are
- * both null.
- * @param a first CharSequence to check
- * @param b second CharSequence to check
- * @return {@code true} if a and b are equal, {@code false} otherwise.
- */
- public static boolean equalsIgnoreCase(CharSequence a, CharSequence b) {
- if (a == b)
- return true; // including both a and b are null.
- if (a == null || b == null)
- return false;
- final int length = a.length();
- if (length != b.length())
- return false;
- for (int i = 0; i < length; i++) {
- if (!equalsIgnoreCase(a.charAt(i), b.charAt(i)))
- return false;
- }
- return true;
- }
-
- /**
- * Returns true if a and b are equal ignoring the case of the characters, including if a is null
- * and b is zero length.
- * @param a CharSequence to check
- * @param b character array to check
- * @param offset start offset of array b
- * @param length length of characters in array b
- * @return {@code true} if a and b are equal, {@code false} otherwise.
- * @throws IndexOutOfBoundsException
- * if {@code offset < 0 || length < 0 || offset + length > data.length}.
- * @throws NullPointerException if {@code b == null}.
- */
- public static boolean equalsIgnoreCase(CharSequence a, char[] b, int offset, int length) {
- if (offset < 0 || length < 0 || length > b.length - offset)
- throw new IndexOutOfBoundsException("array.length=" + b.length + " offset=" + offset
- + " length=" + length);
- if (a == null)
- return length == 0; // including a is null and b is zero length.
- if (a.length() != length)
- return false;
- for (int i = 0; i < length; i++) {
- if (!equalsIgnoreCase(a.charAt(i), b[offset + i]))
- return false;
- }
- return true;
- }
-
- /**
* Remove duplicates from an array of strings.
*
* This method will always keep the first occurrence of all strings at their position
* in the array, removing the subsequent ones.
*/
- public static void removeDupes(final ArrayList<CharSequence> suggestions) {
+ public static void removeDupes(final ArrayList<String> suggestions) {
if (suggestions.size() < 2) return;
int i = 1;
// Don't cache suggestions.size(), since we may be removing items
while (i < suggestions.size()) {
- final CharSequence cur = suggestions.get(i);
+ final String cur = suggestions.get(i);
// Compare each suggestion with each previous suggestion
for (int j = 0; j < i; j++) {
- CharSequence previous = suggestions.get(j);
+ final String previous = suggestions.get(j);
if (TextUtils.equals(cur, previous)) {
suggestions.remove(i);
i--;
@@ -149,7 +89,7 @@ public final class StringUtils {
}
}
- public static String toTitleCase(String s, Locale locale) {
+ public static String toTitleCase(final String s, final Locale locale) {
if (s.length() <= 1) {
// TODO: is this really correct? Shouldn't this be s.toUpperCase()?
return s;
@@ -165,21 +105,19 @@ public final class StringUtils {
return s.toUpperCase(locale).charAt(0) + s.substring(1);
}
+ private static final int[] EMPTY_CODEPOINTS = {};
+
public static int[] toCodePointArray(final String string) {
- final char[] characters = string.toCharArray();
- final int length = characters.length;
- final int[] codePoints = new int[Character.codePointCount(characters, 0, length)];
+ final int length = string.length();
if (length <= 0) {
- return new int[0];
+ return EMPTY_CODEPOINTS;
}
- int codePoint = Character.codePointAt(characters, 0);
- int dsti = 0;
- for (int srci = Character.charCount(codePoint);
- srci < length; srci += Character.charCount(codePoint), ++dsti) {
- codePoints[dsti] = codePoint;
- codePoint = Character.codePointAt(characters, srci);
+ final int[] codePoints = new int[string.codePointCount(0, length)];
+ int destIndex = 0;
+ for (int index = 0; index < length; index = string.offsetByCodePoints(index, 1)) {
+ codePoints[destIndex] = string.codePointAt(index);
+ destIndex++;
}
- codePoints[dsti] = codePoint;
return codePoints;
}
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index f0e3b4ebd..c87161bd3 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -19,6 +19,7 @@ package com.android.inputmethod.latin;
import android.content.Context;
import android.text.TextUtils;
+import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.ProximityInfo;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
@@ -38,7 +39,7 @@ public final class Suggest {
public static final String TAG = Suggest.class.getSimpleName();
// Session id for
- // {@link #getSuggestedWords(WordComposer,CharSequence,ProximityInfo,boolean,int)}.
+ // {@link #getSuggestedWords(WordComposer,String,ProximityInfo,boolean,int)}.
public static final int SESSION_TYPING = 0;
public static final int SESSION_GESTURE = 1;
@@ -71,7 +72,8 @@ public final class Suggest {
mLocale = locale;
}
- /* package for test */ Suggest(final Context context, final File dictionary,
+ @UsedForTesting
+ Suggest(final Context context, final File dictionary,
final long startOffset, final long length, final Locale locale) {
final Dictionary mainDict = DictionaryFactory.createDictionaryForTest(context, dictionary,
startOffset, length /* useFullEditDistance */, false, locale);
@@ -138,7 +140,7 @@ public final class Suggest {
* Sets an optional user dictionary resource to be loaded. The user dictionary is consulted
* before the main dictionary, if set. This refers to the system-managed user dictionary.
*/
- public void setUserDictionary(UserBinaryDictionary userDictionary) {
+ public void setUserDictionary(final UserBinaryDictionary userDictionary) {
addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_USER, userDictionary);
}
@@ -147,12 +149,12 @@ public final class Suggest {
* the contacts dictionary by passing null to this method. In this case no contacts dictionary
* won't be used.
*/
- public void setContactsDictionary(ContactsBinaryDictionary contactsDictionary) {
+ public void setContactsDictionary(final ContactsBinaryDictionary contactsDictionary) {
mContactsDict = contactsDictionary;
addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_CONTACTS, contactsDictionary);
}
- public void setUserHistoryDictionary(UserHistoryDictionary userHistoryDictionary) {
+ public void setUserHistoryDictionary(final UserHistoryDictionary userHistoryDictionary) {
addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_USER_HISTORY, userHistoryDictionary);
}
@@ -160,9 +162,9 @@ public final class Suggest {
mAutoCorrectionThreshold = threshold;
}
- public SuggestedWords getSuggestedWords(
- final WordComposer wordComposer, CharSequence prevWordForBigram,
- final ProximityInfo proximityInfo, final boolean isCorrectionEnabled, int sessionId) {
+ public SuggestedWords getSuggestedWords(final WordComposer wordComposer,
+ final String prevWordForBigram, final ProximityInfo proximityInfo,
+ final boolean isCorrectionEnabled, final int sessionId) {
LatinImeLogger.onStartSuggestion(prevWordForBigram);
if (wordComposer.isBatchMode()) {
return getSuggestedWordsForBatchInput(
@@ -174,9 +176,9 @@ public final class Suggest {
}
// Retrieves suggestions for the typing input.
- private SuggestedWords getSuggestedWordsForTypingInput(
- final WordComposer wordComposer, CharSequence prevWordForBigram,
- final ProximityInfo proximityInfo, final boolean isCorrectionEnabled) {
+ private SuggestedWords getSuggestedWordsForTypingInput(final WordComposer wordComposer,
+ final String prevWordForBigram, final ProximityInfo proximityInfo,
+ final boolean isCorrectionEnabled) {
final int trailingSingleQuotesCount = wordComposer.trailingSingleQuotesCount();
final BoundedTreeSet suggestionsSet = new BoundedTreeSet(sSuggestedWordInfoComparator,
MAX_SUGGESTIONS);
@@ -203,7 +205,7 @@ public final class Suggest {
wordComposerForLookup, prevWordForBigram, proximityInfo));
}
- final CharSequence whitelistedWord;
+ final String whitelistedWord;
if (suggestionsSet.isEmpty()) {
whitelistedWord = null;
} else if (SuggestedWordInfo.KIND_WHITELIST != suggestionsSet.first().mKind) {
@@ -287,9 +289,9 @@ public final class Suggest {
}
// Retrieves suggestions for the batch input.
- private SuggestedWords getSuggestedWordsForBatchInput(
- final WordComposer wordComposer, CharSequence prevWordForBigram,
- final ProximityInfo proximityInfo, int sessionId) {
+ private SuggestedWords getSuggestedWordsForBatchInput(final WordComposer wordComposer,
+ final String prevWordForBigram, final ProximityInfo proximityInfo,
+ final int sessionId) {
final BoundedTreeSet suggestionsSet = new BoundedTreeSet(sSuggestedWordInfoComparator,
MAX_SUGGESTIONS);
@@ -307,7 +309,7 @@ public final class Suggest {
}
for (SuggestedWordInfo wordInfo : suggestionsSet) {
- LatinImeLogger.onAddSuggestedWord(wordInfo.mWord.toString(), wordInfo.mSourceDict);
+ LatinImeLogger.onAddSuggestedWord(wordInfo.mWord, wordInfo.mSourceDict);
}
final ArrayList<SuggestedWordInfo> suggestionsContainer =
@@ -372,7 +374,7 @@ public final class Suggest {
if (o1.mScore < o2.mScore) return 1;
if (o1.mCodePointCount < o2.mCodePointCount) return -1;
if (o1.mCodePointCount > o2.mCodePointCount) return 1;
- return o1.mWord.toString().compareTo(o2.mWord.toString());
+ return o1.mWord.compareTo(o2.mWord);
}
}
private static final SuggestedWordInfoComparator sSuggestedWordInfoComparator =
@@ -383,16 +385,17 @@ public final class Suggest {
final boolean isFirstCharCapitalized, final int trailingSingleQuotesCount) {
final StringBuilder sb = new StringBuilder(wordInfo.mWord.length());
if (isAllUpperCase) {
- sb.append(wordInfo.mWord.toString().toUpperCase(locale));
+ sb.append(wordInfo.mWord.toUpperCase(locale));
} else if (isFirstCharCapitalized) {
- sb.append(StringUtils.toTitleCase(wordInfo.mWord.toString(), locale));
+ sb.append(StringUtils.toTitleCase(wordInfo.mWord, locale));
} else {
sb.append(wordInfo.mWord);
}
for (int i = trailingSingleQuotesCount - 1; i >= 0; --i) {
sb.appendCodePoint(Keyboard.CODE_SINGLE_QUOTE);
}
- return new SuggestedWordInfo(sb, wordInfo.mScore, wordInfo.mKind, wordInfo.mSourceDict);
+ return new SuggestedWordInfo(sb.toString(), wordInfo.mScore, wordInfo.mKind,
+ wordInfo.mSourceDict);
}
public void close() {
diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java
index 52e292a86..572f2906e 100644
--- a/java/src/com/android/inputmethod/latin/SuggestedWords.java
+++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java
@@ -53,6 +53,10 @@ public final class SuggestedWords {
mIsPrediction = isPrediction;
}
+ public boolean isEmpty() {
+ return mSuggestedWordInfoList.isEmpty();
+ }
+
public int size() {
return mSuggestedWordInfoList.size();
}
@@ -86,11 +90,14 @@ public final class SuggestedWords {
public static ArrayList<SuggestedWordInfo> getFromApplicationSpecifiedCompletions(
final CompletionInfo[] infos) {
final ArrayList<SuggestedWordInfo> result = CollectionUtils.newArrayList();
- for (CompletionInfo info : infos) {
- if (null != info && info.getText() != null) {
- result.add(new SuggestedWordInfo(info.getText(), SuggestedWordInfo.MAX_SCORE,
- SuggestedWordInfo.KIND_APP_DEFINED, Dictionary.TYPE_APPLICATION_DEFINED));
- }
+ for (final CompletionInfo info : infos) {
+ if (info == null) continue;
+ final CharSequence text = info.getText();
+ if (null == text) continue;
+ final SuggestedWordInfo suggestedWordInfo = new SuggestedWordInfo(text.toString(),
+ SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_APP_DEFINED,
+ Dictionary.TYPE_APPLICATION_DEFINED);
+ result.add(suggestedWordInfo);
}
return result;
}
@@ -98,7 +105,7 @@ public final class SuggestedWords {
// Should get rid of the first one (what the user typed previously) from suggestions
// and replace it with what the user currently typed.
public static ArrayList<SuggestedWordInfo> getTypedWordAndPreviousSuggestions(
- final CharSequence typedWord, final SuggestedWords previousSuggestions) {
+ final String typedWord, final SuggestedWords previousSuggestions) {
final ArrayList<SuggestedWordInfo> suggestionsList = CollectionUtils.newArrayList();
final HashSet<String> alreadySeen = CollectionUtils.newHashSet();
suggestionsList.add(new SuggestedWordInfo(typedWord, SuggestedWordInfo.MAX_SCORE,
@@ -107,7 +114,7 @@ public final class SuggestedWords {
final int previousSize = previousSuggestions.size();
for (int pos = 1; pos < previousSize; pos++) {
final SuggestedWordInfo prevWordInfo = previousSuggestions.getWordInfo(pos);
- final String prevWord = prevWordInfo.mWord.toString();
+ final String prevWord = prevWordInfo.mWord;
// Filter out duplicate suggestion.
if (!alreadySeen.contains(prevWord)) {
suggestionsList.add(prevWordInfo);
@@ -135,9 +142,9 @@ public final class SuggestedWords {
public final String mSourceDict;
private String mDebugString = "";
- public SuggestedWordInfo(final CharSequence word, final int score, final int kind,
+ public SuggestedWordInfo(final String word, final int score, final int kind,
final String sourceDict) {
- mWord = word.toString();
+ mWord = word;
mScore = score;
mKind = kind;
mSourceDict = sourceDict;
@@ -145,7 +152,7 @@ public final class SuggestedWords {
}
- public void setDebugString(String str) {
+ public void setDebugString(final String str) {
if (null == str) throw new NullPointerException("Debug info is null");
mDebugString = str;
}
@@ -167,7 +174,7 @@ public final class SuggestedWords {
if (TextUtils.isEmpty(mDebugString)) {
return mWord;
} else {
- return mWord + " (" + mDebugString.toString() + ")";
+ return mWord + " (" + mDebugString + ")";
}
}
diff --git a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java
index 8f21b7b4a..ec4dc1436 100644
--- a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedContactsBinaryDictionary.java
@@ -33,13 +33,13 @@ public final class SynchronouslyLoadedContactsBinaryDictionary extends ContactsB
@Override
public synchronized ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer codes,
- final CharSequence prevWordForBigrams, final ProximityInfo proximityInfo) {
+ final String prevWordForBigrams, final ProximityInfo proximityInfo) {
syncReloadDictionaryIfRequired();
return super.getSuggestions(codes, prevWordForBigrams, proximityInfo);
}
@Override
- public synchronized boolean isValidWord(CharSequence word) {
+ public synchronized boolean isValidWord(final String word) {
syncReloadDictionaryIfRequired();
return isValidWordInner(word);
}
diff --git a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java
index 612f54d73..4bdaf2039 100644
--- a/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/SynchronouslyLoadedUserBinaryDictionary.java
@@ -36,13 +36,13 @@ public final class SynchronouslyLoadedUserBinaryDictionary extends UserBinaryDic
@Override
public synchronized ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer codes,
- final CharSequence prevWordForBigrams, final ProximityInfo proximityInfo) {
+ final String prevWordForBigrams, final ProximityInfo proximityInfo) {
syncReloadDictionaryIfRequired();
return super.getSuggestions(codes, prevWordForBigrams, proximityInfo);
}
@Override
- public synchronized boolean isValidWord(CharSequence word) {
+ public synchronized boolean isValidWord(final String word) {
syncReloadDictionaryIfRequired();
return isValidWordInner(word);
}
diff --git a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java
index 60e6fa127..00c3cbe0a 100644
--- a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java
@@ -200,7 +200,7 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary {
mContext.startActivity(intent);
}
- private void addWords(Cursor cursor) {
+ private void addWords(final Cursor cursor) {
// 16 is JellyBean, but we want this to compile against ICS.
final boolean hasShortcutColumn = android.os.Build.VERSION.SDK_INT >= 16;
clearFusionDictionary();
diff --git a/java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java b/java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java
index e39011145..787197755 100644
--- a/java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java
+++ b/java/src/com/android/inputmethod/latin/UserHistoryDictIOUtils.java
@@ -18,6 +18,7 @@ package com.android.inputmethod.latin;
import android.util.Log;
+import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.latin.makedict.BinaryDictIOUtils;
import com.android.inputmethod.latin.makedict.BinaryDictInputOutput;
import com.android.inputmethod.latin.makedict.BinaryDictInputOutput.FusionDictionaryBufferInterface;
@@ -129,7 +130,8 @@ public final class UserHistoryDictIOUtils {
/**
* Constructs a new FusionDictionary from BigramDictionaryInterface.
*/
- /* packages for test */ static FusionDictionary constructFusionDictionary(
+ @UsedForTesting
+ static FusionDictionary constructFusionDictionary(
final BigramDictionaryInterface dict, final UserHistoryDictionaryBigramList bigrams) {
final FusionDictionary fusionDict = new FusionDictionary(new Node(),
new FusionDictionary.DictionaryOptions(new HashMap<String, String>(), false,
@@ -193,7 +195,8 @@ public final class UserHistoryDictIOUtils {
/**
* Adds all unigrams and bigrams in maps to OnAddWordListener.
*/
- /* package for test */ static void addWordsFromWordMap(final Map<Integer, String> unigrams,
+ @UsedForTesting
+ static void addWordsFromWordMap(final Map<Integer, String> unigrams,
final Map<Integer, Integer> frequencies,
final Map<Integer, ArrayList<PendingAttribute>> bigrams, final OnAddWordListener to) {
for (Map.Entry<Integer, String> entry : unigrams.entrySet()) {
diff --git a/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java b/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java
index 3615fa1fb..4fd9bfafb 100644
--- a/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java
@@ -21,6 +21,7 @@ import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.util.Log;
+import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.keyboard.ProximityInfo;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.UserHistoryDictIOUtils.BigramDictionaryInterface;
@@ -75,7 +76,7 @@ public final class UserHistoryDictionary extends ExpandableDictionary {
private final SharedPreferences mPrefs;
// Should always be false except when we use this class for test
- /* package for test */ boolean isTest = false;
+ @UsedForTesting boolean isTest = false;
private static final ConcurrentHashMap<String, SoftReference<UserHistoryDictionary>>
sLangDictCache = CollectionUtils.newConcurrentHashMap();
@@ -122,7 +123,7 @@ public final class UserHistoryDictionary extends ExpandableDictionary {
@Override
protected ArrayList<SuggestedWordInfo> getWordsInner(final WordComposer composer,
- final CharSequence prevWord, final ProximityInfo proximityInfo) {
+ final String prevWord, final ProximityInfo proximityInfo) {
// Inhibit suggestions (not predictions) for user history for now. Removing this method
// is enough to use it through the standard ExpandableDictionary way.
return null;
@@ -132,7 +133,7 @@ public final class UserHistoryDictionary extends ExpandableDictionary {
* Return whether the passed charsequence is in the dictionary.
*/
@Override
- public synchronized boolean isValidWord(final CharSequence word) {
+ public synchronized boolean isValidWord(final String word) {
// TODO: figure out what is the correct thing to do here.
return false;
}
@@ -145,7 +146,7 @@ public final class UserHistoryDictionary extends ExpandableDictionary {
* context, as in beginning of a sentence for example.
* The second word may not be null (a NullPointerException would be thrown).
*/
- public int addToUserHistory(final String word1, String word2, boolean isValid) {
+ public int addToUserHistory(final String word1, final String word2, final boolean isValid) {
if (word2.length() >= BinaryDictionary.MAX_WORD_LENGTH ||
(word1 != null && word1.length() >= BinaryDictionary.MAX_WORD_LENGTH)) {
return -1;
@@ -175,7 +176,7 @@ public final class UserHistoryDictionary extends ExpandableDictionary {
return -1;
}
- public boolean cancelAddingUserHistory(String word1, String word2) {
+ public boolean cancelAddingUserHistory(final String word1, final String word2) {
if (mBigramListLock.tryLock()) {
try {
if (mBigramList.removeBigram(word1, word2)) {
@@ -226,7 +227,8 @@ public final class UserHistoryDictionary extends ExpandableDictionary {
final ExpandableDictionary dictionary = this;
final OnAddWordListener listener = new OnAddWordListener() {
@Override
- public void setUnigram(String word, String shortcutTarget, int frequency) {
+ public void setUnigram(final String word, final String shortcutTarget,
+ final int frequency) {
profTotal++;
if (DBG_SAVE_RESTORE) {
Log.d(TAG, "load unigram: " + word + "," + frequency);
@@ -236,7 +238,7 @@ public final class UserHistoryDictionary extends ExpandableDictionary {
}
@Override
- public void setBigram(String word1, String word2, int frequency) {
+ public void setBigram(final String word1, final String word2, final int frequency) {
if (word1.length() < BinaryDictionary.MAX_WORD_LENGTH
&& word2.length() < BinaryDictionary.MAX_WORD_LENGTH) {
profTotal++;
@@ -250,7 +252,7 @@ public final class UserHistoryDictionary extends ExpandableDictionary {
mBigramList.addBigram(word1, word2, (byte)frequency);
}
};
-
+
// Load the dictionary from binary file
FileInputStream inStream = null;
try {
@@ -292,8 +294,9 @@ public final class UserHistoryDictionary extends ExpandableDictionary {
private final SharedPreferences mPrefs;
private final Context mContext;
- public UpdateBinaryTask(UserHistoryDictionaryBigramList pendingWrites, String locale,
- UserHistoryDictionary dict, SharedPreferences prefs, Context context) {
+ public UpdateBinaryTask(final UserHistoryDictionaryBigramList pendingWrites,
+ final String locale, final UserHistoryDictionary dict,
+ final SharedPreferences prefs, final Context context) {
mBigramList = pendingWrites;
mLocale = locale;
mUserHistoryDictionary = dict;
@@ -303,7 +306,7 @@ public final class UserHistoryDictionary extends ExpandableDictionary {
}
@Override
- protected Void doInBackground(Void... v) {
+ protected Void doInBackground(final Void... v) {
if (mUserHistoryDictionary.isTest) {
// If isTest == true, wait until the lock is released.
mUserHistoryDictionary.mBigramListLock.lock();
@@ -360,7 +363,7 @@ public final class UserHistoryDictionary extends ExpandableDictionary {
}
@Override
- public int getFrequency(String word1, String word2) {
+ public int getFrequency(final String word1, final String word2) {
final int freq;
if (word1 == null) { // unigram
freq = FREQUENCY_FOR_TYPED;
@@ -390,6 +393,7 @@ public final class UserHistoryDictionary extends ExpandableDictionary {
}
}
+ @UsedForTesting
void forceAddWordForTest(final String word1, final String word2, final boolean isValid) {
mBigramListLock.lock();
try {
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
index da0071adc..229aa8a48 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -38,7 +38,7 @@ public final class WordComposer {
private int[] mPrimaryKeyCodes;
private final InputPointers mInputPointers = new InputPointers(N);
private final StringBuilder mTypedWord;
- private CharSequence mAutoCorrection;
+ private String mAutoCorrection;
private boolean mIsResumed;
private boolean mIsBatchMode;
@@ -64,7 +64,7 @@ public final class WordComposer {
refreshSize();
}
- public WordComposer(WordComposer source) {
+ public WordComposer(final WordComposer source) {
mPrimaryKeyCodes = Arrays.copyOf(source.mPrimaryKeyCodes, source.mPrimaryKeyCodes.length);
mTypedWord = new StringBuilder(source.mTypedWord);
mInputPointers.copy(source.mInputPointers);
@@ -121,7 +121,8 @@ public final class WordComposer {
return mInputPointers;
}
- private static boolean isFirstCharCapitalized(int index, int codePoint, boolean previous) {
+ private static boolean isFirstCharCapitalized(final int index, final int codePoint,
+ final boolean previous) {
if (index == 0) return Character.isUpperCase(codePoint);
return previous && !Character.isUpperCase(codePoint);
}
@@ -129,7 +130,7 @@ public final class WordComposer {
/**
* Add a new keystroke, with the pressed key's code point with the touch point coordinates.
*/
- public void add(int primaryCode, int keyX, int keyY) {
+ public void add(final int primaryCode, final int keyX, final int keyY) {
final int newIndex = size();
mTypedWord.appendCodePoint(primaryCode);
refreshSize();
@@ -156,12 +157,12 @@ public final class WordComposer {
mAutoCorrection = null;
}
- public void setBatchInputPointers(InputPointers batchPointers) {
+ public void setBatchInputPointers(final InputPointers batchPointers) {
mInputPointers.set(batchPointers);
mIsBatchMode = true;
}
- public void setBatchInputWord(CharSequence word) {
+ public void setBatchInputWord(final String word) {
reset();
mIsBatchMode = true;
final int length = word.length();
@@ -321,14 +322,14 @@ public final class WordComposer {
/**
* Sets the auto-correction for this word.
*/
- public void setAutoCorrection(final CharSequence correction) {
+ public void setAutoCorrection(final String correction) {
mAutoCorrection = correction;
}
/**
* @return the auto-correction for this word, or null if none.
*/
- public CharSequence getAutoCorrectionOrNull() {
+ public String getAutoCorrectionOrNull() {
return mAutoCorrection;
}
@@ -341,7 +342,7 @@ public final class WordComposer {
// `type' should be one of the LastComposedWord.COMMIT_TYPE_* constants above.
public LastComposedWord commitWord(final int type, final String committedWord,
- final String separatorString, final CharSequence prevWord) {
+ final String separatorString, final String prevWord) {
// Note: currently, we come here whenever we commit a word. If it's a MANUAL_PICK
// or a DECIDED_WORD we may cancel the commit later; otherwise, we should deactivate
// the last composed word to ensure this does not happen.
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
index 7b0231a6b..05f2d933c 100644
--- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
+++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
@@ -16,19 +16,32 @@
package com.android.inputmethod.latin.makedict;
+import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.latin.Constants;
+import com.android.inputmethod.latin.makedict.BinaryDictInputOutput.CharEncoding;
import com.android.inputmethod.latin.makedict.BinaryDictInputOutput.FusionDictionaryBufferInterface;
import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
import com.android.inputmethod.latin.makedict.FusionDictionary.CharGroup;
+import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
import java.io.IOException;
+import java.io.OutputStream;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
import java.util.Map;
import java.util.Stack;
public final class BinaryDictIOUtils {
private static final boolean DBG = false;
+ private static final int MSB24 = 0x800000;
+ private static final int SINT24_MAX = 0x7FFFFF;
+ private static final int MAX_JUMPS = 10000;
+
+ private BinaryDictIOUtils() {
+ // This utility class is not publicly instantiable.
+ }
private static final class Position {
public static final int NOT_READ_GROUPCOUNT = -1;
@@ -90,7 +103,9 @@ public final class BinaryDictIOUtils {
final boolean isMovedGroup = BinaryDictInputOutput.isMovedGroup(info.mFlags,
formatOptions);
- if (!isMovedGroup
+ final boolean isDeletedGroup = BinaryDictInputOutput.isDeletedGroup(info.mFlags,
+ formatOptions);
+ if (!isMovedGroup && !isDeletedGroup
&& info.mFrequency != FusionDictionary.CharGroup.NOT_A_TERMINAL) {// found word
words.put(info.mOriginalAddress, new String(pushedChars, 0, index));
frequencies.put(info.mOriginalAddress, info.mFrequency);
@@ -153,6 +168,7 @@ public final class BinaryDictIOUtils {
* @throws IOException
* @throws UnsupportedFormatException
*/
+ @UsedForTesting
public static int getTerminalPosition(final FusionDictionaryBufferInterface buffer,
final String word) throws IOException, UnsupportedFormatException {
if (word == null) return FormatSpec.NOT_VALID_WORD;
@@ -165,19 +181,19 @@ public final class BinaryDictIOUtils {
if (wordPos >= wordLen) return FormatSpec.NOT_VALID_WORD;
do {
- int groupOffset = buffer.position() - header.mHeaderSize;
final int charGroupCount = BinaryDictInputOutput.readCharGroupCount(buffer);
- groupOffset += BinaryDictInputOutput.getGroupCountSize(charGroupCount);
-
boolean foundNextCharGroup = false;
for (int i = 0; i < charGroupCount; ++i) {
final int charGroupPos = buffer.position();
final CharGroupInfo currentInfo = BinaryDictInputOutput.readCharGroup(buffer,
buffer.position(), header.mFormatOptions);
- if (BinaryDictInputOutput.isMovedGroup(currentInfo.mFlags,
- header.mFormatOptions)) {
- continue;
- }
+ final boolean isMovedGroup =
+ BinaryDictInputOutput.isMovedGroup(currentInfo.mFlags,
+ header.mFormatOptions);
+ final boolean isDeletedGroup =
+ BinaryDictInputOutput.isDeletedGroup(currentInfo.mFlags,
+ header.mFormatOptions);
+ if (isMovedGroup) continue;
boolean same = true;
for (int p = 0, j = word.offsetByCodePoints(0, wordPos);
p < currentInfo.mCharacters.length;
@@ -192,7 +208,8 @@ public final class BinaryDictIOUtils {
if (same) {
// found the group matches the word.
if (wordPos + currentInfo.mCharacters.length == wordLen) {
- if (currentInfo.mFrequency == CharGroup.NOT_A_TERMINAL) {
+ if (currentInfo.mFrequency == CharGroup.NOT_A_TERMINAL
+ || isDeletedGroup) {
return FormatSpec.NOT_VALID_WORD;
} else {
return charGroupPos;
@@ -206,7 +223,6 @@ public final class BinaryDictIOUtils {
buffer.position(currentInfo.mChildrenAddress);
break;
}
- groupOffset = currentInfo.mEndAddress;
}
// If we found the next char group, it is under the file pointer.
@@ -228,6 +244,10 @@ public final class BinaryDictIOUtils {
return FormatSpec.NOT_VALID_WORD;
}
+ private static int markAsDeleted(final int flags) {
+ return (flags & (~FormatSpec.MASK_GROUP_ADDRESS_TYPE)) | FormatSpec.FLAG_IS_DELETED;
+ }
+
/**
* Delete the word from the binary file.
*
@@ -236,6 +256,7 @@ public final class BinaryDictIOUtils {
* @throws IOException
* @throws UnsupportedFormatException
*/
+ @UsedForTesting
public static void deleteWord(final FusionDictionaryBufferInterface buffer,
final String word) throws IOException, UnsupportedFormatException {
buffer.position(0);
@@ -245,21 +266,58 @@ public final class BinaryDictIOUtils {
buffer.position(wordPosition);
final int flags = buffer.readUnsignedByte();
- final int newFlags = flags ^ FormatSpec.FLAG_IS_TERMINAL;
buffer.position(wordPosition);
- buffer.put((byte)newFlags);
+ buffer.put((byte)markAsDeleted(flags));
}
- private static void putSInt24(final FusionDictionaryBufferInterface buffer,
+ /**
+ * @return the size written, in bytes. Always 3 bytes.
+ */
+ private static int writeSInt24ToBuffer(final FusionDictionaryBufferInterface buffer,
final int value) {
final int absValue = Math.abs(value);
buffer.put((byte)(((value < 0 ? 0x80 : 0) | (absValue >> 16)) & 0xFF));
buffer.put((byte)((absValue >> 8) & 0xFF));
buffer.put((byte)(absValue & 0xFF));
+ return 3;
+ }
+
+ /**
+ * @return the size written, in bytes. Always 3 bytes.
+ */
+ private static int writeSInt24ToStream(final OutputStream destination, final int value)
+ throws IOException {
+ final int absValue = Math.abs(value);
+ destination.write((byte)(((value < 0 ? 0x80 : 0) | (absValue >> 16)) & 0xFF));
+ destination.write((byte)((absValue >> 8) & 0xFF));
+ destination.write((byte)(absValue & 0xFF));
+ return 3;
}
/**
- * Update a parent address in a CharGroup that is addressed by groupOriginAddress.
+ * @return the size written, in bytes. 1, 2, or 3 bytes.
+ */
+ private static int writeVariableAddress(final OutputStream destination, final int value)
+ throws IOException {
+ switch (BinaryDictInputOutput.getByteSize(value)) {
+ case 1:
+ destination.write((byte)value);
+ break;
+ case 2:
+ destination.write((byte)(0xFF & (value >> 8)));
+ destination.write((byte)(0xFF & value));
+ break;
+ case 3:
+ destination.write((byte)(0xFF & (value >> 16)));
+ destination.write((byte)(0xFF & (value >> 8)));
+ destination.write((byte)(0xFF & value));
+ break;
+ }
+ return BinaryDictInputOutput.getByteSize(value);
+ }
+
+ /**
+ * Update a parent address in a CharGroup that is referred to by groupOriginAddress.
*
* @param buffer the buffer to write.
* @param groupOriginAddress the address of the group.
@@ -275,8 +333,647 @@ public final class BinaryDictIOUtils {
throw new RuntimeException("this file format does not support parent addresses");
}
final int flags = buffer.readUnsignedByte();
+ if (BinaryDictInputOutput.isMovedGroup(flags, formatOptions)) {
+ // if the group is moved, the parent address is stored in the destination group.
+ // We are guaranteed to process the destination group later, so there is no need to
+ // update anything here.
+ buffer.position(originalPosition);
+ return;
+ }
+ if (DBG) {
+ MakedictLog.d("update parent address flags=" + flags + ", " + groupOriginAddress);
+ }
final int parentOffset = newParentAddress - groupOriginAddress;
- putSInt24(buffer, parentOffset);
+ writeSInt24ToBuffer(buffer, parentOffset);
+ buffer.position(originalPosition);
+ }
+
+ private static void skipCharGroup(final FusionDictionaryBufferInterface buffer,
+ final FormatOptions formatOptions) {
+ final int flags = buffer.readUnsignedByte();
+ BinaryDictInputOutput.readParentAddress(buffer, formatOptions);
+ skipString(buffer, (flags & FormatSpec.FLAG_HAS_MULTIPLE_CHARS) != 0);
+ BinaryDictInputOutput.readChildrenAddress(buffer, flags, formatOptions);
+ if ((flags & FormatSpec.FLAG_IS_TERMINAL) != 0) buffer.readUnsignedByte();
+ if ((flags & FormatSpec.FLAG_HAS_SHORTCUT_TARGETS) != 0) {
+ final int shortcutsSize = buffer.readUnsignedShort();
+ buffer.position(buffer.position() + shortcutsSize
+ - FormatSpec.GROUP_SHORTCUT_LIST_SIZE_SIZE);
+ }
+ if ((flags & FormatSpec.FLAG_HAS_BIGRAMS) != 0) {
+ int bigramCount = 0;
+ while (bigramCount++ < FormatSpec.MAX_BIGRAMS_IN_A_GROUP) {
+ final int bigramFlags = buffer.readUnsignedByte();
+ switch (bigramFlags & FormatSpec.MASK_ATTRIBUTE_ADDRESS_TYPE) {
+ case FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE:
+ buffer.readUnsignedByte();
+ break;
+ case FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES:
+ buffer.readUnsignedShort();
+ break;
+ case FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES:
+ buffer.readUnsignedInt24();
+ break;
+ }
+ if ((bigramFlags & FormatSpec.FLAG_ATTRIBUTE_HAS_NEXT) == 0) break;
+ }
+ if (bigramCount >= FormatSpec.MAX_BIGRAMS_IN_A_GROUP) {
+ throw new RuntimeException("Too many bigrams in a group.");
+ }
+ }
+ }
+
+ /**
+ * Update parent addresses in a Node that is referred to by nodeOriginAddress.
+ *
+ * @param buffer the buffer to be modified.
+ * @param nodeOriginAddress the address of a modified Node.
+ * @param newParentAddress the address to be written.
+ * @param formatOptions file format options.
+ */
+ public static void updateParentAddresses(final FusionDictionaryBufferInterface buffer,
+ final int nodeOriginAddress, final int newParentAddress,
+ final FormatOptions formatOptions) {
+ final int originalPosition = buffer.position();
+ buffer.position(nodeOriginAddress);
+ do {
+ final int count = BinaryDictInputOutput.readCharGroupCount(buffer);
+ for (int i = 0; i < count; ++i) {
+ updateParentAddress(buffer, buffer.position(), newParentAddress, formatOptions);
+ skipCharGroup(buffer, formatOptions);
+ }
+ final int forwardLinkAddress = buffer.readUnsignedInt24();
+ buffer.position(forwardLinkAddress);
+ } while (formatOptions.mSupportsDynamicUpdate
+ && buffer.position() != FormatSpec.NO_FORWARD_LINK_ADDRESS);
+ buffer.position(originalPosition);
+ }
+
+ private static void skipString(final FusionDictionaryBufferInterface buffer,
+ final boolean hasMultipleChars) {
+ if (hasMultipleChars) {
+ int character = CharEncoding.readChar(buffer);
+ while (character != FormatSpec.INVALID_CHARACTER) {
+ character = CharEncoding.readChar(buffer);
+ }
+ } else {
+ CharEncoding.readChar(buffer);
+ }
+ }
+
+ /**
+ * Write a string to a stream.
+ *
+ * @param destination the stream to write.
+ * @param word the string to be written.
+ * @return the size written, in bytes.
+ * @throws IOException
+ */
+ private static int writeString(final OutputStream destination, final String word)
+ throws IOException {
+ int size = 0;
+ final int length = word.length();
+ for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) {
+ final int codePoint = word.codePointAt(i);
+ if (CharEncoding.getCharSize(codePoint) == 1) {
+ destination.write((byte)codePoint);
+ size++;
+ } else {
+ destination.write((byte)(0xFF & (codePoint >> 16)));
+ destination.write((byte)(0xFF & (codePoint >> 8)));
+ destination.write((byte)(0xFF & codePoint));
+ size += 3;
+ }
+ }
+ destination.write((byte)FormatSpec.GROUP_CHARACTERS_TERMINATOR);
+ size += FormatSpec.GROUP_TERMINATOR_SIZE;
+ return size;
+ }
+
+ /**
+ * Update a children address in a CharGroup that is addressed by groupOriginAddress.
+ *
+ * @param buffer the buffer to write.
+ * @param groupOriginAddress the address of the group.
+ * @param newChildrenAddress the absolute address of the child.
+ * @param formatOptions file format options.
+ */
+ public static void updateChildrenAddress(final FusionDictionaryBufferInterface buffer,
+ final int groupOriginAddress, final int newChildrenAddress,
+ final FormatOptions formatOptions) {
+ final int originalPosition = buffer.position();
+ buffer.position(groupOriginAddress);
+ final int flags = buffer.readUnsignedByte();
+ final int parentAddress = BinaryDictInputOutput.readParentAddress(buffer, formatOptions);
+ skipString(buffer, (flags & FormatSpec.FLAG_HAS_MULTIPLE_CHARS) != 0);
+ if ((flags & FormatSpec.FLAG_IS_TERMINAL) != 0) buffer.readUnsignedByte();
+ final int childrenOffset = newChildrenAddress == FormatSpec.NO_CHILDREN_ADDRESS
+ ? FormatSpec.NO_CHILDREN_ADDRESS : newChildrenAddress - buffer.position();
+ writeSInt24ToBuffer(buffer, childrenOffset);
buffer.position(originalPosition);
}
+
+ /**
+ * Write a char group to an output stream.
+ * A char group is an in-memory representation of a node in trie.
+ * A char group info is an on-disk representation of a node.
+ *
+ * @param destination the stream to write.
+ * @param info the char group info to be written.
+ * @return the size written, in bytes.
+ */
+ public static int writeCharGroup(final OutputStream destination, final CharGroupInfo info)
+ throws IOException {
+ int size = FormatSpec.GROUP_FLAGS_SIZE;
+ destination.write((byte)info.mFlags);
+ final int parentOffset = info.mParentAddress == FormatSpec.NO_PARENT_ADDRESS ?
+ FormatSpec.NO_PARENT_ADDRESS : info.mParentAddress - info.mOriginalAddress;
+ size += writeSInt24ToStream(destination, parentOffset);
+
+ for (int i = 0; i < info.mCharacters.length; ++i) {
+ if (CharEncoding.getCharSize(info.mCharacters[i]) == 1) {
+ destination.write((byte)info.mCharacters[i]);
+ size++;
+ } else {
+ size += writeSInt24ToStream(destination, info.mCharacters[i]);
+ }
+ }
+ if (info.mCharacters.length > 1) {
+ destination.write((byte)FormatSpec.GROUP_CHARACTERS_TERMINATOR);
+ size++;
+ }
+
+ if ((info.mFlags & FormatSpec.FLAG_IS_TERMINAL) != 0) {
+ destination.write((byte)info.mFrequency);
+ size++;
+ }
+
+ if (DBG) {
+ MakedictLog.d("writeCharGroup origin=" + info.mOriginalAddress + ", size=" + size
+ + ", child=" + info.mChildrenAddress + ", characters ="
+ + new String(info.mCharacters, 0, info.mCharacters.length));
+ }
+ final int childrenOffset = info.mChildrenAddress == FormatSpec.NO_CHILDREN_ADDRESS ?
+ 0 : info.mChildrenAddress - (info.mOriginalAddress + size);
+ writeSInt24ToStream(destination, childrenOffset);
+ size += FormatSpec.SIGNED_CHILDREN_ADDRESS_SIZE;
+
+ if (info.mShortcutTargets != null && info.mShortcutTargets.size() > 0) {
+ final int shortcutListSize =
+ BinaryDictInputOutput.getShortcutListSize(info.mShortcutTargets);
+ destination.write((byte)(shortcutListSize >> 8));
+ destination.write((byte)(shortcutListSize & 0xFF));
+ size += 2;
+ final Iterator<WeightedString> shortcutIterator = info.mShortcutTargets.iterator();
+ while (shortcutIterator.hasNext()) {
+ final WeightedString target = shortcutIterator.next();
+ destination.write((byte)BinaryDictInputOutput.makeShortcutFlags(
+ shortcutIterator.hasNext(), target.mFrequency));
+ size++;
+ size += writeString(destination, target.mWord);
+ }
+ }
+
+ if (info.mBigrams != null) {
+ // TODO: Consolidate this code with the code that computes the size of the bigram list
+ // in BinaryDictionaryInputOutput#computeActualNodeSize
+ for (int i = 0; i < info.mBigrams.size(); ++i) {
+
+ final int bigramFrequency = info.mBigrams.get(i).mFrequency;
+ int bigramFlags = (i < info.mBigrams.size() - 1)
+ ? FormatSpec.FLAG_ATTRIBUTE_HAS_NEXT : 0;
+ size++;
+ final int bigramOffset = info.mBigrams.get(i).mAddress - (info.mOriginalAddress
+ + size);
+ bigramFlags |= (bigramOffset < 0) ? FormatSpec.FLAG_ATTRIBUTE_OFFSET_NEGATIVE : 0;
+ switch (BinaryDictInputOutput.getByteSize(bigramOffset)) {
+ case 1:
+ bigramFlags |= FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE;
+ break;
+ case 2:
+ bigramFlags |= FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES;
+ break;
+ case 3:
+ bigramFlags |= FormatSpec.FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES;
+ break;
+ }
+ bigramFlags |= bigramFrequency & FormatSpec.FLAG_ATTRIBUTE_FREQUENCY;
+ destination.write((byte)bigramFlags);
+ size += writeVariableAddress(destination, Math.abs(bigramOffset));
+ }
+ }
+ return size;
+ }
+
+ private static void updateForwardLink(final FusionDictionaryBufferInterface buffer,
+ final int nodeOriginAddress, final int newNodeAddress,
+ final FormatOptions formatOptions) {
+ buffer.position(nodeOriginAddress);
+ int jumpCount = 0;
+ while (jumpCount++ < MAX_JUMPS) {
+ final int count = BinaryDictInputOutput.readCharGroupCount(buffer);
+ for (int i = 0; i < count; ++i) skipCharGroup(buffer, formatOptions);
+ final int forwardLinkAddress = buffer.readUnsignedInt24();
+ if (forwardLinkAddress == FormatSpec.NO_FORWARD_LINK_ADDRESS) {
+ buffer.position(buffer.position() - FormatSpec.FORWARD_LINK_ADDRESS_SIZE);
+ writeSInt24ToBuffer(buffer, newNodeAddress);
+ return;
+ }
+ buffer.position(forwardLinkAddress);
+ }
+ if (DBG && jumpCount >= MAX_JUMPS) {
+ throw new RuntimeException("too many jumps, probably a bug.");
+ }
+ }
+
+ /**
+ * Helper method to move a char group to the tail of the file.
+ */
+ private static int moveCharGroup(final OutputStream destination,
+ final FusionDictionaryBufferInterface buffer, final CharGroupInfo info,
+ final int nodeOriginAddress, final int oldGroupAddress,
+ final FormatOptions formatOptions) throws IOException {
+ updateParentAddress(buffer, oldGroupAddress, buffer.limit() + 1, formatOptions);
+ buffer.position(oldGroupAddress);
+ final int currentFlags = buffer.readUnsignedByte();
+ buffer.position(oldGroupAddress);
+ buffer.put((byte)(FormatSpec.FLAG_IS_MOVED | (currentFlags
+ & (~FormatSpec.MASK_MOVE_AND_DELETE_FLAG))));
+ int size = FormatSpec.GROUP_FLAGS_SIZE;
+ updateForwardLink(buffer, nodeOriginAddress, buffer.limit(), formatOptions);
+ size += writeNode(destination, new CharGroupInfo[] { info });
+ return size;
+ }
+
+ /**
+ * Compute the size of the char group.
+ */
+ private static int computeGroupSize(final CharGroupInfo info,
+ final FormatOptions formatOptions) {
+ int size = FormatSpec.GROUP_FLAGS_SIZE + FormatSpec.PARENT_ADDRESS_SIZE
+ + BinaryDictInputOutput.getGroupCharactersSize(info.mCharacters)
+ + BinaryDictInputOutput.getChildrenAddressSize(info.mFlags, formatOptions);
+ if ((info.mFlags & FormatSpec.FLAG_IS_TERMINAL) != 0) {
+ size += FormatSpec.GROUP_FREQUENCY_SIZE;
+ }
+ if (info.mShortcutTargets != null && !info.mShortcutTargets.isEmpty()) {
+ size += BinaryDictInputOutput.getShortcutListSize(info.mShortcutTargets);
+ }
+ if (info.mBigrams != null) {
+ for (final PendingAttribute attr : info.mBigrams) {
+ size += FormatSpec.GROUP_FLAGS_SIZE;
+ size += BinaryDictInputOutput.getByteSize(attr.mAddress);
+ }
+ }
+ return size;
+ }
+
+ /**
+ * Write a node to the stream.
+ *
+ * @param destination the stream to write.
+ * @param infos groups to be written.
+ * @return the size written, in bytes.
+ * @throws IOException
+ */
+ private static int writeNode(final OutputStream destination, final CharGroupInfo[] infos)
+ throws IOException {
+ int size = BinaryDictInputOutput.getGroupCountSize(infos.length);
+ switch (BinaryDictInputOutput.getGroupCountSize(infos.length)) {
+ case 1:
+ destination.write((byte)infos.length);
+ break;
+ case 2:
+ destination.write((byte)(infos.length >> 8));
+ destination.write((byte)(infos.length & 0xFF));
+ break;
+ default:
+ throw new RuntimeException("Invalid group count size.");
+ }
+ for (final CharGroupInfo info : infos) size += writeCharGroup(destination, info);
+ writeSInt24ToStream(destination, FormatSpec.NO_FORWARD_LINK_ADDRESS);
+ return size + FormatSpec.FORWARD_LINK_ADDRESS_SIZE;
+ }
+
+ /**
+ * Move a group that is referred to by oldGroupOrigin to the tail of the file.
+ * And set the children address to the byte after the group.
+ *
+ * @param nodeOrigin the address of the tail of the file.
+ * @param characters
+ * @param length
+ * @param flags
+ * @param frequency
+ * @param parentAddress
+ * @param shortcutTargets
+ * @param bigrams
+ * @param destination the stream representing the tail of the file.
+ * @param buffer the buffer representing the (constant-size) body of the file.
+ * @param oldNodeOrigin
+ * @param oldGroupOrigin
+ * @param formatOptions
+ * @return the size written, in bytes.
+ * @throws IOException
+ */
+ private static int moveGroup(final int nodeOrigin, final int[] characters, final int length,
+ final int flags, final int frequency, final int parentAddress,
+ final ArrayList<WeightedString> shortcutTargets,
+ final ArrayList<PendingAttribute> bigrams, final OutputStream destination,
+ final FusionDictionaryBufferInterface buffer, final int oldNodeOrigin,
+ final int oldGroupOrigin, final FormatOptions formatOptions) throws IOException {
+ int size = 0;
+ final int newGroupOrigin = nodeOrigin + 1;
+ final int[] writtenCharacters = Arrays.copyOfRange(characters, 0, length);
+ final CharGroupInfo tmpInfo = new CharGroupInfo(newGroupOrigin, -1 /* endAddress */,
+ flags, writtenCharacters, frequency, parentAddress, FormatSpec.NO_CHILDREN_ADDRESS,
+ shortcutTargets, bigrams);
+ size = computeGroupSize(tmpInfo, formatOptions);
+ final CharGroupInfo newInfo = new CharGroupInfo(newGroupOrigin, newGroupOrigin + size,
+ flags, writtenCharacters, frequency, parentAddress,
+ nodeOrigin + 1 + size + FormatSpec.FORWARD_LINK_ADDRESS_SIZE, shortcutTargets,
+ bigrams);
+ moveCharGroup(destination, buffer, newInfo, oldNodeOrigin, oldGroupOrigin, formatOptions);
+ return 1 + size + FormatSpec.FORWARD_LINK_ADDRESS_SIZE;
+ }
+
+ /**
+ * Insert a word into a binary dictionary.
+ *
+ * @param buffer
+ * @param destination
+ * @param word
+ * @param frequency
+ * @param bigramStrings
+ * @param shortcuts
+ * @throws IOException
+ * @throws UnsupportedFormatException
+ */
+ // TODO: Support batch insertion.
+ // TODO: Remove @UsedForTesting once UserHistoryDictionary is implemented by BinaryDictionary.
+ @UsedForTesting
+ public static void insertWord(final FusionDictionaryBufferInterface buffer,
+ final OutputStream destination, final String word, final int frequency,
+ final ArrayList<WeightedString> bigramStrings,
+ final ArrayList<WeightedString> shortcuts, final boolean isNotAWord,
+ final boolean isBlackListEntry)
+ throws IOException, UnsupportedFormatException {
+ final ArrayList<PendingAttribute> bigrams = new ArrayList<PendingAttribute>();
+ if (bigramStrings != null) {
+ for (final WeightedString bigram : bigramStrings) {
+ int position = getTerminalPosition(buffer, bigram.mWord);
+ if (position == FormatSpec.NOT_VALID_WORD) {
+ // TODO: figure out what is the correct thing to do here.
+ } else {
+ bigrams.add(new PendingAttribute(bigram.mFrequency, position));
+ }
+ }
+ }
+
+ final boolean isTerminal = true;
+ final boolean hasBigrams = !bigrams.isEmpty();
+ final boolean hasShortcuts = shortcuts != null && !shortcuts.isEmpty();
+
+ // find the insert position of the word.
+ if (buffer.position() != 0) buffer.position(0);
+ final FileHeader header = BinaryDictInputOutput.readHeader(buffer);
+
+ int wordPos = 0, address = buffer.position(), nodeOriginAddress = buffer.position();
+ final int[] codePoints = FusionDictionary.getCodePoints(word);
+ final int wordLen = codePoints.length;
+
+ for (int depth = 0; depth < Constants.Dictionary.MAX_WORD_LENGTH; ++depth) {
+ if (wordPos >= wordLen) break;
+ nodeOriginAddress = buffer.position();
+ int nodeParentAddress = -1;
+ final int charGroupCount = BinaryDictInputOutput.readCharGroupCount(buffer);
+ boolean foundNextGroup = false;
+
+ for (int i = 0; i < charGroupCount; ++i) {
+ address = buffer.position();
+ final CharGroupInfo currentInfo = BinaryDictInputOutput.readCharGroup(buffer,
+ buffer.position(), header.mFormatOptions);
+ final boolean isMovedGroup = BinaryDictInputOutput.isMovedGroup(currentInfo.mFlags,
+ header.mFormatOptions);
+ if (isMovedGroup) continue;
+ nodeParentAddress = (currentInfo.mParentAddress == FormatSpec.NO_PARENT_ADDRESS)
+ ? FormatSpec.NO_PARENT_ADDRESS : currentInfo.mParentAddress + address;
+ boolean matched = true;
+ for (int p = 0; p < currentInfo.mCharacters.length; ++p) {
+ if (wordPos + p >= wordLen) {
+ /*
+ * splitting
+ * before
+ * abcd - ef
+ *
+ * insert "abc"
+ *
+ * after
+ * abc - d - ef
+ */
+ final int newNodeAddress = buffer.limit();
+ final int flags = BinaryDictInputOutput.makeCharGroupFlags(p > 1,
+ isTerminal, 0, hasShortcuts, hasBigrams, false /* isNotAWord */,
+ false /* isBlackListEntry */, header.mFormatOptions);
+ int written = moveGroup(newNodeAddress, currentInfo.mCharacters, p, flags,
+ frequency, nodeParentAddress, shortcuts, bigrams, destination,
+ buffer, nodeOriginAddress, address, header.mFormatOptions);
+
+ final int[] characters2 = Arrays.copyOfRange(currentInfo.mCharacters, p,
+ currentInfo.mCharacters.length);
+ if (currentInfo.mChildrenAddress != FormatSpec.NO_CHILDREN_ADDRESS) {
+ updateParentAddresses(buffer, currentInfo.mChildrenAddress,
+ newNodeAddress + written + 1, header.mFormatOptions);
+ }
+ final CharGroupInfo newInfo2 = new CharGroupInfo(
+ newNodeAddress + written + 1, -1 /* endAddress */,
+ currentInfo.mFlags, characters2, currentInfo.mFrequency,
+ newNodeAddress + 1, currentInfo.mChildrenAddress,
+ currentInfo.mShortcutTargets, currentInfo.mBigrams);
+ writeNode(destination, new CharGroupInfo[] { newInfo2 });
+ return;
+ } else if (codePoints[wordPos + p] != currentInfo.mCharacters[p]) {
+ if (p > 0) {
+ /*
+ * splitting
+ * before
+ * ab - cd
+ *
+ * insert "ac"
+ *
+ * after
+ * a - b - cd
+ * |
+ * - c
+ */
+
+ final int newNodeAddress = buffer.limit();
+ final int childrenAddress = currentInfo.mChildrenAddress;
+
+ // move prefix
+ final int prefixFlags = BinaryDictInputOutput.makeCharGroupFlags(p > 1,
+ false /* isTerminal */, 0 /* childrenAddressSize*/,
+ false /* hasShortcut */, false /* hasBigrams */,
+ false /* isNotAWord */, false /* isBlackListEntry */,
+ header.mFormatOptions);
+ int written = moveGroup(newNodeAddress, currentInfo.mCharacters, p,
+ prefixFlags, -1 /* frequency */, nodeParentAddress, null, null,
+ destination, buffer, nodeOriginAddress, address,
+ header.mFormatOptions);
+
+ final int[] suffixCharacters = Arrays.copyOfRange(
+ currentInfo.mCharacters, p, currentInfo.mCharacters.length);
+ if (currentInfo.mChildrenAddress != FormatSpec.NO_CHILDREN_ADDRESS) {
+ updateParentAddresses(buffer, currentInfo.mChildrenAddress,
+ newNodeAddress + written + 1, header.mFormatOptions);
+ }
+ final int suffixFlags = BinaryDictInputOutput.makeCharGroupFlags(
+ suffixCharacters.length > 1,
+ (currentInfo.mFlags & FormatSpec.FLAG_IS_TERMINAL) != 0,
+ 0 /* childrenAddressSize */,
+ (currentInfo.mFlags & FormatSpec.FLAG_HAS_SHORTCUT_TARGETS)
+ != 0,
+ (currentInfo.mFlags & FormatSpec.FLAG_HAS_BIGRAMS) != 0,
+ isNotAWord, isBlackListEntry, header.mFormatOptions);
+ final CharGroupInfo suffixInfo = new CharGroupInfo(
+ newNodeAddress + written + 1, -1 /* endAddress */, suffixFlags,
+ suffixCharacters, currentInfo.mFrequency, newNodeAddress + 1,
+ currentInfo.mChildrenAddress, currentInfo.mShortcutTargets,
+ currentInfo.mBigrams);
+ written += computeGroupSize(suffixInfo, header.mFormatOptions) + 1;
+
+ final int[] newCharacters = Arrays.copyOfRange(codePoints, wordPos + p,
+ codePoints.length);
+ final int flags = BinaryDictInputOutput.makeCharGroupFlags(
+ newCharacters.length > 1, isTerminal,
+ 0 /* childrenAddressSize */, hasShortcuts, hasBigrams,
+ isNotAWord, isBlackListEntry, header.mFormatOptions);
+ final CharGroupInfo newInfo = new CharGroupInfo(
+ newNodeAddress + written, -1 /* endAddress */, flags,
+ newCharacters, frequency, newNodeAddress + 1,
+ FormatSpec.NO_CHILDREN_ADDRESS, shortcuts, bigrams);
+ writeNode(destination, new CharGroupInfo[] { suffixInfo, newInfo });
+ return;
+ }
+ matched = false;
+ break;
+ }
+ }
+
+ if (matched) {
+ if (wordPos + currentInfo.mCharacters.length == wordLen) {
+ // the word exists in the dictionary.
+ // only update group.
+ final int newNodeAddress = buffer.limit();
+ final boolean hasMultipleChars = currentInfo.mCharacters.length > 1;
+ final int flags = BinaryDictInputOutput.makeCharGroupFlags(hasMultipleChars,
+ isTerminal, 0 /* childrenAddressSize */, hasShortcuts, hasBigrams,
+ isNotAWord, isBlackListEntry, header.mFormatOptions);
+ final CharGroupInfo newInfo = new CharGroupInfo(newNodeAddress + 1,
+ -1 /* endAddress */, flags, currentInfo.mCharacters, frequency,
+ nodeParentAddress, currentInfo.mChildrenAddress, shortcuts,
+ bigrams);
+ moveCharGroup(destination, buffer, newInfo, nodeOriginAddress, address,
+ header.mFormatOptions);
+ return;
+ }
+ wordPos += currentInfo.mCharacters.length;
+ if (currentInfo.mChildrenAddress == FormatSpec.NO_CHILDREN_ADDRESS) {
+ /*
+ * found the prefix of the word.
+ * make new node and link to the node from this group.
+ *
+ * before
+ * ab - cd
+ *
+ * insert "abcde"
+ *
+ * after
+ * ab - cd - e
+ */
+ final int newNodeAddress = buffer.limit();
+ updateChildrenAddress(buffer, address, newNodeAddress,
+ header.mFormatOptions);
+ final int newGroupAddress = newNodeAddress + 1;
+ final boolean hasMultipleChars = (wordLen - wordPos) > 1;
+ final int flags = BinaryDictInputOutput.makeCharGroupFlags(hasMultipleChars,
+ isTerminal, 0 /* childrenAddressSize */, hasShortcuts, hasBigrams,
+ isNotAWord, isBlackListEntry, header.mFormatOptions);
+ final int[] characters = Arrays.copyOfRange(codePoints, wordPos, wordLen);
+ final CharGroupInfo newInfo = new CharGroupInfo(newGroupAddress, -1, flags,
+ characters, frequency, address, FormatSpec.NO_CHILDREN_ADDRESS,
+ shortcuts, bigrams);
+ writeNode(destination, new CharGroupInfo[] { newInfo });
+ return;
+ }
+ buffer.position(currentInfo.mChildrenAddress);
+ foundNextGroup = true;
+ break;
+ }
+ }
+
+ if (foundNextGroup) continue;
+
+ // reached the end of the array.
+ final int linkAddressPosition = buffer.position();
+ int nextLink = buffer.readUnsignedInt24();
+ if ((nextLink & MSB24) != 0) {
+ nextLink = -(nextLink & SINT24_MAX);
+ }
+ if (nextLink == FormatSpec.NO_FORWARD_LINK_ADDRESS) {
+ /*
+ * expand this node.
+ *
+ * before
+ * ab - cd
+ *
+ * insert "abef"
+ *
+ * after
+ * ab - cd
+ * |
+ * - ef
+ */
+
+ // change the forward link address.
+ final int newNodeAddress = buffer.limit();
+ buffer.position(linkAddressPosition);
+ writeSInt24ToBuffer(buffer, newNodeAddress);
+
+ final int[] characters = Arrays.copyOfRange(codePoints, wordPos, wordLen);
+ final int flags = BinaryDictInputOutput.makeCharGroupFlags(characters.length > 1,
+ isTerminal, 0 /* childrenAddressSize */, hasShortcuts, hasBigrams,
+ isNotAWord, isBlackListEntry, header.mFormatOptions);
+ final CharGroupInfo newInfo = new CharGroupInfo(newNodeAddress + 1,
+ -1 /* endAddress */, flags, characters, frequency, nodeParentAddress,
+ FormatSpec.NO_CHILDREN_ADDRESS, shortcuts, bigrams);
+ writeNode(destination, new CharGroupInfo[]{ newInfo });
+ return;
+ } else {
+ depth--;
+ buffer.position(nextLink);
+ }
+ }
+ }
+
+ /**
+ * Find a word from the buffer.
+ *
+ * @param buffer the buffer representing the body of the dictionary file.
+ * @param word the word searched
+ * @return the found group
+ * @throws IOException
+ * @throws UnsupportedFormatException
+ */
+ @UsedForTesting
+ public static CharGroupInfo findWordFromBuffer(final FusionDictionaryBufferInterface buffer,
+ final String word) throws IOException, UnsupportedFormatException {
+ int position = getTerminalPosition(buffer, word);
+ if (position != FormatSpec.NOT_VALID_WORD) {
+ buffer.position(0);
+ final FileHeader header = BinaryDictInputOutput.readHeader(buffer);
+ buffer.position(position);
+ return BinaryDictInputOutput.readCharGroup(buffer, position, header.mFormatOptions);
+ }
+ return null;
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java
index b431a4da9..277796dd2 100644
--- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java
+++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java
@@ -16,6 +16,7 @@
package com.android.inputmethod.latin.makedict;
+import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
import com.android.inputmethod.latin.makedict.FusionDictionary.CharGroup;
@@ -124,8 +125,7 @@ public final class BinaryDictInputOutput {
/**
* A class grouping utility function for our specific character encoding.
*/
- private static final class CharEncoding {
-
+ static final class CharEncoding {
private static final int MINIMAL_ONE_BYTE_CHARACTER_VALUE = 0x20;
private static final int MAXIMAL_ONE_BYTE_CHARACTER_VALUE = 0xFF;
@@ -154,7 +154,7 @@ public final class BinaryDictInputOutput {
* @param character the character code.
* @return the size in binary encoded-form, either 1 or 3 bytes.
*/
- private static int getCharSize(final int character) {
+ static int getCharSize(final int character) {
// See char encoding in FusionDictionary.java
if (fitsOnOneByte(character)) return 1;
if (FormatSpec.INVALID_CHARACTER == character) return 1;
@@ -263,7 +263,7 @@ public final class BinaryDictInputOutput {
* @param buffer the buffer, positioned over an encoded character.
* @return the character code.
*/
- private static int readChar(final FusionDictionaryBufferInterface buffer) {
+ static int readChar(final FusionDictionaryBufferInterface buffer) {
int character = buffer.readUnsignedByte();
if (!fitsOnOneByte(character)) {
if (FormatSpec.GROUP_CHARACTERS_TERMINATOR == character) {
@@ -277,6 +277,21 @@ public final class BinaryDictInputOutput {
}
/**
+ * Compute the binary size of the character array.
+ *
+ * If only one character, this is the size of this character. If many, it's the sum of their
+ * sizes + 1 byte for the terminator.
+ *
+ * @param characters the character array
+ * @return the size of the char array, including the terminator if any
+ */
+ static int getGroupCharactersSize(final int[] characters) {
+ int size = CharEncoding.getCharArraySize(characters);
+ if (characters.length > 1) size += FormatSpec.GROUP_TERMINATOR_SIZE;
+ return size;
+ }
+
+ /**
* Compute the binary size of the character array in a group
*
* If only one character, this is the size of this character. If many, it's the sum of their
@@ -286,9 +301,7 @@ public final class BinaryDictInputOutput {
* @return the size of the char array, including the terminator if any
*/
private static int getGroupCharactersSize(final CharGroup group) {
- int size = CharEncoding.getCharArraySize(group.mChars);
- if (group.hasSeveralChars()) size += FormatSpec.GROUP_TERMINATOR_SIZE;
- return size;
+ return getGroupCharactersSize(group.mChars);
}
/**
@@ -338,7 +351,7 @@ public final class BinaryDictInputOutput {
* This is known in advance and does not change according to position in the file
* like address lists do.
*/
- private static int getShortcutListSize(final ArrayList<WeightedString> shortcutList) {
+ static int getShortcutListSize(final ArrayList<WeightedString> shortcutList) {
if (null == shortcutList) return 0;
int size = FormatSpec.GROUP_SHORTCUT_LIST_SIZE_SIZE;
for (final WeightedString shortcut : shortcutList) {
@@ -399,7 +412,16 @@ public final class BinaryDictInputOutput {
* Helper method to check whether the group is moved.
*/
public static boolean isMovedGroup(final int flags, final FormatOptions options) {
- return options.mSupportsDynamicUpdate && ((flags & FormatSpec.FLAG_IS_MOVED) == 1);
+ return options.mSupportsDynamicUpdate
+ && ((flags & FormatSpec.MASK_GROUP_ADDRESS_TYPE) == FormatSpec.FLAG_IS_MOVED);
+ }
+
+ /**
+ * Helper method to check whether the group is deleted.
+ */
+ public static boolean isDeletedGroup(final int flags, final FormatOptions formatOptions) {
+ return formatOptions.mSupportsDynamicUpdate
+ && ((flags & FormatSpec.MASK_GROUP_ADDRESS_TYPE) == FormatSpec.FLAG_IS_DELETED);
}
/**
@@ -439,7 +461,7 @@ public final class BinaryDictInputOutput {
* @param address the address
* @return the byte size.
*/
- private static int getByteSize(final int address) {
+ static int getByteSize(final int address) {
assert(address <= UINT24_MAX);
if (!hasChildrenAddress(address)) {
return 0;
@@ -721,53 +743,60 @@ public final class BinaryDictInputOutput {
return 3;
}
- private static byte makeCharGroupFlags(final CharGroup group, final int groupAddress,
- final int childrenOffset, final FormatOptions formatOptions) {
+ /**
+ * Makes the flag value for a char group.
+ *
+ * @param hasMultipleChars whether the group has multiple chars.
+ * @param isTerminal whether the group is terminal.
+ * @param childrenAddressSize the size of a children address.
+ * @param hasShortcuts whether the group has shortcuts.
+ * @param hasBigrams whether the group has bigrams.
+ * @param isNotAWord whether the group is not a word.
+ * @param isBlackListEntry whether the group is a blacklist entry.
+ * @param formatOptions file format options.
+ * @return the flags
+ */
+ static int makeCharGroupFlags(final boolean hasMultipleChars, final boolean isTerminal,
+ final int childrenAddressSize, final boolean hasShortcuts, final boolean hasBigrams,
+ final boolean isNotAWord, final boolean isBlackListEntry,
+ final FormatOptions formatOptions) {
byte flags = 0;
- if (group.mChars.length > 1) flags |= FormatSpec.FLAG_HAS_MULTIPLE_CHARS;
- if (group.mFrequency >= 0) {
- flags |= FormatSpec.FLAG_IS_TERMINAL;
- }
- if (null != group.mChildren) {
- final int byteSize = formatOptions.mSupportsDynamicUpdate
- ? FormatSpec.SIGNED_CHILDREN_ADDRESS_SIZE : getByteSize(childrenOffset);
- switch (byteSize) {
- case 1:
- flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_ONEBYTE;
- break;
- case 2:
- flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_TWOBYTES;
- break;
- case 3:
- flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_THREEBYTES;
- break;
- default:
- throw new RuntimeException("Node with a strange address");
- }
- } else if (formatOptions.mSupportsDynamicUpdate) {
- flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_THREEBYTES;
- }
- if (null != group.mShortcutTargets) {
- if (DBG && 0 == group.mShortcutTargets.size()) {
- throw new RuntimeException("0-sized shortcut list must be null");
- }
- flags |= FormatSpec.FLAG_HAS_SHORTCUT_TARGETS;
- }
- if (null != group.mBigrams) {
- if (DBG && 0 == group.mBigrams.size()) {
- throw new RuntimeException("0-sized bigram list must be null");
+ if (hasMultipleChars) flags |= FormatSpec.FLAG_HAS_MULTIPLE_CHARS;
+ if (isTerminal) flags |= FormatSpec.FLAG_IS_TERMINAL;
+ if (formatOptions.mSupportsDynamicUpdate) {
+ flags |= FormatSpec.FLAG_IS_NOT_MOVED;
+ } else if (true) {
+ switch (childrenAddressSize) {
+ case 1:
+ flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_ONEBYTE;
+ break;
+ case 2:
+ flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_TWOBYTES;
+ break;
+ case 3:
+ flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_THREEBYTES;
+ break;
+ case 0:
+ flags |= FormatSpec.FLAG_GROUP_ADDRESS_TYPE_NOADDRESS;
+ break;
+ default:
+ throw new RuntimeException("Node with a strange address");
}
- flags |= FormatSpec.FLAG_HAS_BIGRAMS;
- }
- if (group.mIsNotAWord) {
- flags |= FormatSpec.FLAG_IS_NOT_A_WORD;
- }
- if (group.mIsBlacklistEntry) {
- flags |= FormatSpec.FLAG_IS_BLACKLISTED;
}
+ if (hasShortcuts) flags |= FormatSpec.FLAG_HAS_SHORTCUT_TARGETS;
+ if (hasBigrams) flags |= FormatSpec.FLAG_HAS_BIGRAMS;
+ if (isNotAWord) flags |= FormatSpec.FLAG_IS_NOT_A_WORD;
+ if (isBlackListEntry) flags |= FormatSpec.FLAG_IS_BLACKLISTED;
return flags;
}
+ private static byte makeCharGroupFlags(final CharGroup group, final int groupAddress,
+ final int childrenOffset, final FormatOptions formatOptions) {
+ return (byte) makeCharGroupFlags(group.mChars.length > 1, group.mFrequency >= 0,
+ getByteSize(childrenOffset), group.mShortcutTargets != null, group.mBigrams != null,
+ group.mIsNotAWord, group.mIsBlacklistEntry, formatOptions);
+ }
+
/**
* Makes the flag value for a bigram.
*
@@ -859,7 +888,7 @@ public final class BinaryDictInputOutput {
* @param frequency the frequency of the attribute, 0..15
* @return the flags
*/
- private static final int makeShortcutFlags(final boolean more, final int frequency) {
+ static final int makeShortcutFlags(final boolean more, final int frequency) {
return (more ? FormatSpec.FLAG_ATTRIBUTE_HAS_NEXT : 0)
+ (frequency & FormatSpec.FLAG_ATTRIBUTE_FREQUENCY);
}
@@ -897,6 +926,7 @@ public final class BinaryDictInputOutput {
*/
private static int writePlacedNode(final FusionDictionary dict, byte[] buffer,
final Node node, final FormatOptions formatOptions) {
+ // TODO: Make the code in common with BinaryDictIOUtils#writeCharGroup
int index = node.mCachedAddress;
final int groupCount = node.mData.size();
@@ -1178,7 +1208,7 @@ public final class BinaryDictInputOutput {
// Input methods: Read a binary dictionary to memory.
// readDictionaryBinary is the public entry point for them.
- private static int getChildrenAddressSize(final int optionFlags,
+ static int getChildrenAddressSize(final int optionFlags,
final FormatOptions formatOptions) {
if (formatOptions.mSupportsDynamicUpdate) return FormatSpec.SIGNED_CHILDREN_ADDRESS_SIZE;
switch (optionFlags & FormatSpec.MASK_GROUP_ADDRESS_TYPE) {
@@ -1194,7 +1224,7 @@ public final class BinaryDictInputOutput {
}
}
- private static int readChildrenAddress(final FusionDictionaryBufferInterface buffer,
+ static int readChildrenAddress(final FusionDictionaryBufferInterface buffer,
final int optionFlags, final FormatOptions options) {
if (options.mSupportsDynamicUpdate) {
final int address = buffer.readUnsignedInt24();
@@ -1219,7 +1249,7 @@ public final class BinaryDictInputOutput {
}
}
- private static int readParentAddress(final FusionDictionaryBufferInterface buffer,
+ static int readParentAddress(final FusionDictionaryBufferInterface buffer,
final FormatOptions formatOptions) {
if (supportsDynamicUpdate(formatOptions)) {
final int parentAddress = buffer.readUnsignedInt24();
@@ -1290,7 +1320,8 @@ public final class BinaryDictInputOutput {
ArrayList<PendingAttribute> bigrams = null;
if (0 != (flags & FormatSpec.FLAG_HAS_BIGRAMS)) {
bigrams = new ArrayList<PendingAttribute>();
- while (true) {
+ int bigramCount = 0;
+ while (bigramCount++ < FormatSpec.MAX_BIGRAMS_IN_A_GROUP) {
final int bigramFlags = buffer.readUnsignedByte();
++addressPointer;
final int sign = 0 == (bigramFlags & FormatSpec.FLAG_ATTRIBUTE_OFFSET_NEGATIVE)
@@ -1318,6 +1349,9 @@ public final class BinaryDictInputOutput {
bigramAddress));
if (0 == (bigramFlags & FormatSpec.FLAG_ATTRIBUTE_HAS_NEXT)) break;
}
+ if (bigramCount >= FormatSpec.MAX_BIGRAMS_IN_A_GROUP) {
+ MakedictLog.d("too many bigrams in a group.");
+ }
}
return new CharGroupInfo(originalGroupAddress, addressPointer, flags, characters, frequency,
parentAddress, childrenAddress, shortcutTargets, bigrams);
@@ -1618,6 +1652,7 @@ public final class BinaryDictInputOutput {
* @param dict an optional dictionary to add words to, or null.
* @return the created (or merged) dictionary.
*/
+ @UsedForTesting
public static FusionDictionary readDictionaryBinary(
final FusionDictionaryBufferInterface buffer, final FusionDictionary dict)
throws IOException, UnsupportedFormatException {
diff --git a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
index b3fbb9fb5..e88a4aebf 100644
--- a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
+++ b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
@@ -59,9 +59,11 @@ public final class FormatSpec {
* l | 10 = 2 bytes : FLAG_GROUP_ADDRESS_TYPE_TWOBYTES
* a | 11 = 3 bytes : FLAG_GROUP_ADDRESS_TYPE_THREEBYTES
* g | ELSE
- * s | is moved ? 2 bits, 11 = no
- * | 01 = yes
+ * s | is moved ? 2 bits, 11 = no : FLAG_IS_NOT_MOVED
+ * | This must be the same as FLAG_GROUP_ADDRESS_TYPE_THREEBYTES
+ * | 01 = yes : FLAG_IS_MOVED
* | the new address is stored in the same place as the parent address
+ * | is deleted? 10 = yes : FLAG_IS_DELETED
* | has several chars ? 1 bit, 1 = yes, 0 = no : FLAG_HAS_MULTIPLE_CHARS
* | has a terminal ? 1 bit, 1 = yes, 0 = no : FLAG_IS_TERMINAL
* | has shortcut targets ? 1 bit, 1 = yes, 0 = no : FLAG_HAS_SHORTCUT_TARGETS
@@ -170,6 +172,7 @@ public final class FormatSpec {
static final int PARENT_ADDRESS_SIZE = 3;
static final int FORWARD_LINK_ADDRESS_SIZE = 3;
+ // These flags are used only in the static dictionary.
static final int MASK_GROUP_ADDRESS_TYPE = 0xC0;
static final int FLAG_GROUP_ADDRESS_TYPE_NOADDRESS = 0x00;
static final int FLAG_GROUP_ADDRESS_TYPE_ONEBYTE = 0x40;
@@ -183,7 +186,13 @@ public final class FormatSpec {
static final int FLAG_HAS_BIGRAMS = 0x04;
static final int FLAG_IS_NOT_A_WORD = 0x02;
static final int FLAG_IS_BLACKLISTED = 0x01;
- static final int FLAG_IS_MOVED = 0x40;
+
+ // These flags are used only in the dynamic dictionary.
+ static final int MASK_MOVE_AND_DELETE_FLAG = 0xC0;
+ static final int FIXED_BIT_OF_DYNAMIC_UPDATE_MOVE = 0x40;
+ static final int FLAG_IS_MOVED = 0x00 | FIXED_BIT_OF_DYNAMIC_UPDATE_MOVE;
+ static final int FLAG_IS_NOT_MOVED = 0x80 | FIXED_BIT_OF_DYNAMIC_UPDATE_MOVE;
+ static final int FLAG_IS_DELETED = 0x80;
static final int FLAG_ATTRIBUTE_HAS_NEXT = 0x80;
static final int FLAG_ATTRIBUTE_OFFSET_NEGATIVE = 0x40;
@@ -210,6 +219,7 @@ public final class FormatSpec {
static final int MAX_CHARGROUPS_FOR_ONE_BYTE_CHARGROUP_COUNT = 0x7F; // 127
static final int MAX_CHARGROUPS_IN_A_NODE = 0x7FFF; // 32767
+ static final int MAX_BIGRAMS_IN_A_GROUP = 10000;
static final int MAX_TERMINAL_FREQUENCY = 255;
static final int MAX_BIGRAM_FREQUENCY = 15;
diff --git a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java
index 3193ef457..6f1faa192 100644
--- a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java
+++ b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java
@@ -279,7 +279,7 @@ public final class FusionDictionary implements Iterable<Word> {
/**
* Helper method to convert a String to an int array.
*/
- static private int[] getCodePoints(final String word) {
+ static int[] getCodePoints(final String word) {
// TODO: this is a copy-paste of the contents of StringUtils.toCodePointArray,
// which is not visible from the makedict package. Factor this code.
final char[] characters = word.toCharArray();
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
index 5a11ae534..49b98863f 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
@@ -212,7 +212,7 @@ public final class AndroidSpellCheckerService extends SpellCheckerService
}
}
- private final ArrayList<CharSequence> mSuggestions;
+ private final ArrayList<String> mSuggestions;
private final int[] mScores;
private final String mOriginalText;
private final float mSuggestionThreshold;
@@ -335,7 +335,7 @@ public final class AndroidSpellCheckerService extends SpellCheckerService
gatheredSuggestions = mSuggestions.toArray(EMPTY_STRING_ARRAY);
final int bestScore = mScores[mLength - 1];
- final CharSequence bestSuggestion = mSuggestions.get(0);
+ final String bestSuggestion = mSuggestions.get(0);
final float normalizedScore =
BinaryDictionary.calcNormalizedScore(
mOriginalText, bestSuggestion.toString(), bestScore);
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
index 53ed4d3c3..a8f323999 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
@@ -268,7 +268,7 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session {
dictInfo.mDictionary.getSuggestions(composer, prevWord,
dictInfo.mProximityInfo);
for (final SuggestedWordInfo suggestion : suggestions) {
- final String suggestionStr = suggestion.mWord.toString();
+ final String suggestionStr = suggestion.mWord;
suggestionsGatherer.addWord(suggestionStr.toCharArray(), null, 0,
suggestionStr.length(), suggestion.mScore);
}
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java
index 1fb2bbb6a..eae5d2e60 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/DictionaryPool.java
@@ -51,11 +51,11 @@ public final class DictionaryPool extends LinkedBlockingQueue<DictAndProximity>
new Dictionary(Dictionary.TYPE_MAIN) {
@Override
public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
- final CharSequence prevWord, final ProximityInfo proximityInfo) {
+ final String prevWord, final ProximityInfo proximityInfo) {
return noSuggestions;
}
@Override
- public boolean isValidWord(CharSequence word) {
+ public boolean isValidWord(final String word) {
// This is never called. However if for some strange reason it ever gets
// called, returning true is less destructive (it will not underline the
// word in red).
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java
index 11bb97031..6c0d79c2b 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerProximityInfo.java
@@ -16,6 +16,7 @@
package com.android.inputmethod.latin.spellcheck;
+import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.keyboard.ProximityInfo;
import com.android.inputmethod.latin.CollectionUtils;
import com.android.inputmethod.latin.Constants;
@@ -23,7 +24,7 @@ import com.android.inputmethod.latin.Constants;
import java.util.TreeMap;
public final class SpellCheckerProximityInfo {
- /* public for test */
+ @UsedForTesting
final public static int NUL = Constants.NOT_A_CODE;
// This must be the same as MAX_PROXIMITY_CHARS_SIZE else it will not work inside
diff --git a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
index 4e9fd1968..35d5a0067 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestions.java
@@ -22,7 +22,6 @@ import android.graphics.drawable.Drawable;
import com.android.inputmethod.keyboard.Key;
import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.keyboard.KeyboardSwitcher;
import com.android.inputmethod.keyboard.internal.KeyboardBuilder;
import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
import com.android.inputmethod.keyboard.internal.KeyboardParams;
@@ -66,7 +65,7 @@ public final class MoreSuggestions extends Keyboard {
int pos = fromPos, rowStartPos = fromPos;
final int size = Math.min(suggestions.size(), SuggestionStripView.MAX_SUGGESTIONS);
while (pos < size) {
- final String word = suggestions.getWord(pos).toString();
+ final String word = suggestions.getWord(pos);
// TODO: Should take care of text x-scaling.
mWidths[pos] = (int)view.getLabelWidth(word, paint) + padding;
final int numColumn = pos - rowStartPos + 1;
@@ -176,11 +175,11 @@ public final class MoreSuggestions extends Keyboard {
}
public Builder layout(final SuggestedWords suggestions, final int fromPos,
- final int maxWidth, final int minWidth, final int maxRow) {
- final Keyboard keyboard = KeyboardSwitcher.getInstance().getKeyboard();
+ final int maxWidth, final int minWidth, final int maxRow,
+ final Keyboard parentKeyboard) {
final int xmlId = R.xml.kbd_suggestions_pane_template;
- load(xmlId, keyboard.mId);
- mParams.mVerticalGap = mParams.mTopPadding = keyboard.mVerticalGap / 2;
+ load(xmlId, parentKeyboard.mId);
+ mParams.mVerticalGap = mParams.mTopPadding = parentKeyboard.mVerticalGap / 2;
mPaneView.updateKeyboardGeometry(mParams.mDefaultRowHeight);
final int count = mParams.layout(suggestions, fromPos, maxWidth, minWidth, maxRow,
diff --git a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
index 03a2e73d1..6cdd9e2cd 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/MoreSuggestionsView.java
@@ -56,17 +56,17 @@ public final class MoreSuggestionsView extends KeyboardView implements MoreKeysP
final KeyboardActionListener mSuggestionsPaneListener =
new KeyboardActionListener.Adapter() {
@Override
- public void onPressKey(int primaryCode) {
+ public void onPressKey(final int primaryCode) {
mListener.onPressKey(primaryCode);
}
@Override
- public void onReleaseKey(int primaryCode, boolean withSliding) {
+ public void onReleaseKey(final int primaryCode, final boolean withSliding) {
mListener.onReleaseKey(primaryCode, withSliding);
}
@Override
- public void onCodeInput(int primaryCode, int x, int y) {
+ public void onCodeInput(final int primaryCode, final int x, final int y) {
final int index = primaryCode - MoreSuggestions.SUGGESTION_CODE_BASE;
if (index >= 0 && index < SuggestionStripView.MAX_SUGGESTIONS) {
mListener.onCustomRequest(index);
@@ -79,11 +79,12 @@ public final class MoreSuggestionsView extends KeyboardView implements MoreKeysP
}
};
- public MoreSuggestionsView(Context context, AttributeSet attrs) {
+ public MoreSuggestionsView(final Context context, final AttributeSet attrs) {
this(context, attrs, R.attr.moreSuggestionsViewStyle);
}
- public MoreSuggestionsView(Context context, AttributeSet attrs, int defStyle) {
+ public MoreSuggestionsView(final Context context, final AttributeSet attrs,
+ final int defStyle) {
super(context, attrs, defStyle);
final Resources res = context.getResources();
@@ -94,7 +95,7 @@ public final class MoreSuggestionsView extends KeyboardView implements MoreKeysP
}
@Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
final Keyboard keyboard = getKeyboard();
if (keyboard != null) {
final int width = keyboard.mOccupiedWidth + getPaddingLeft() + getPaddingRight();
@@ -110,7 +111,7 @@ public final class MoreSuggestionsView extends KeyboardView implements MoreKeysP
}
@Override
- public void setKeyboard(Keyboard keyboard) {
+ public void setKeyboard(final Keyboard keyboard) {
super.setKeyboard(keyboard);
mModalPanelKeyDetector.setKeyboard(keyboard, -getPaddingLeft(), -getPaddingTop());
mSlidingPanelKeyDetector.setKeyboard(keyboard, -getPaddingLeft(),
@@ -138,15 +139,16 @@ public final class MoreSuggestionsView extends KeyboardView implements MoreKeysP
}
@Override
- public void setKeyPreviewPopupEnabled(boolean previewEnabled, int delay) {
+ public void setKeyPreviewPopupEnabled(final boolean previewEnabled, final int delay) {
// Suggestions pane needs no pop-up key preview displayed, so we pass always false with a
// delay of 0. The delay does not matter actually since the popup is not shown anyway.
super.setKeyPreviewPopupEnabled(false, 0);
}
@Override
- public void showMoreKeysPanel(View parentView, Controller controller, int pointX, int pointY,
- PopupWindow window, KeyboardActionListener listener) {
+ public void showMoreKeysPanel(final View parentView, final Controller controller,
+ final int pointX, final int pointY, final PopupWindow window,
+ final KeyboardActionListener listener) {
mController = controller;
mListener = listener;
final View container = (View)getParent();
@@ -179,12 +181,12 @@ public final class MoreSuggestionsView extends KeyboardView implements MoreKeysP
}
@Override
- public int translateX(int x) {
+ public int translateX(final int x) {
return x - mOriginX;
}
@Override
- public int translateY(int y) {
+ public int translateY(final int y) {
return y - mOriginY;
}
@@ -211,7 +213,7 @@ public final class MoreSuggestionsView extends KeyboardView implements MoreKeysP
};
@Override
- public boolean onTouchEvent(MotionEvent me) {
+ public boolean onTouchEvent(final MotionEvent me) {
final int action = me.getAction();
final long eventTime = me.getEventTime();
final int index = me.getActionIndex();
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
index e926fa209..e7cb97fc2 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java
@@ -52,13 +52,16 @@ import android.widget.PopupWindow;
import android.widget.RelativeLayout;
import android.widget.TextView;
+import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.KeyboardActionListener;
+import com.android.inputmethod.keyboard.KeyboardSwitcher;
import com.android.inputmethod.keyboard.KeyboardView;
import com.android.inputmethod.keyboard.MoreKeysPanel;
import com.android.inputmethod.keyboard.PointerTracker;
import com.android.inputmethod.keyboard.ViewLayoutUtils;
import com.android.inputmethod.latin.AutoCorrection;
import com.android.inputmethod.latin.CollectionUtils;
+import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.LatinImeLogger;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.ResourceUtils;
@@ -74,7 +77,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
OnLongClickListener {
public interface Listener {
public boolean addWordToUserDictionary(String word);
- public void pickSuggestionManually(int index, CharSequence word);
+ public void pickSuggestionManually(int index, String word);
}
// The maximum number of suggestions available. See {@link Suggest#mPrefMaxSuggestions}.
@@ -83,7 +86,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
static final boolean DBG = LatinImeLogger.sDBG;
private final ViewGroup mSuggestionsStrip;
- private KeyboardView mKeyboardView;
+ KeyboardView mKeyboardView;
private final View mMoreSuggestionsContainer;
private final MoreSuggestionsView mMoreSuggestionsView;
@@ -97,8 +100,8 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
private final PopupWindow mPreviewPopup;
private final TextView mPreviewText;
- private Listener mListener;
- private SuggestedWords mSuggestedWords = SuggestedWords.EMPTY;
+ Listener mListener;
+ SuggestedWords mSuggestedWords = SuggestedWords.EMPTY;
private final SuggestionStripViewParams mParams;
private static final float MIN_TEXT_XSCALE = 0.70f;
@@ -108,12 +111,12 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
private static final class UiHandler extends StaticInnerHandlerWrapper<SuggestionStripView> {
private static final int MSG_HIDE_PREVIEW = 0;
- public UiHandler(SuggestionStripView outerInstance) {
+ public UiHandler(final SuggestionStripView outerInstance) {
super(outerInstance);
}
@Override
- public void dispatchMessage(Message msg) {
+ public void dispatchMessage(final Message msg) {
final SuggestionStripView suggestionStripView = getOuterInstance();
switch (msg.what) {
case MSG_HIDE_PREVIEW:
@@ -177,8 +180,9 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
private final TextView mLeftwardsArrowView;
private final TextView mHintToSaveView;
- public SuggestionStripViewParams(Context context, AttributeSet attrs, int defStyle,
- ArrayList<TextView> words, ArrayList<View> dividers, ArrayList<TextView> infos) {
+ public SuggestionStripViewParams(final Context context, final AttributeSet attrs,
+ final int defStyle, final ArrayList<TextView> words, final ArrayList<View> dividers,
+ final ArrayList<TextView> infos) {
mWords = words;
mDividers = dividers;
mInfos = infos;
@@ -250,7 +254,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
return mMaxMoreSuggestionsRow * mMoreSuggestionsRowHeight + mMoreSuggestionsBottomGap;
}
- public int setMoreSuggestionsHeight(int remainingHeight) {
+ public int setMoreSuggestionsHeight(final int remainingHeight) {
final int currentHeight = getMoreSuggestionsHeight();
if (currentHeight <= remainingHeight) {
return currentHeight;
@@ -262,7 +266,8 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
return newHeight;
}
- private static Drawable getMoreSuggestionsHint(Resources res, float textSize, int color) {
+ private static Drawable getMoreSuggestionsHint(final Resources res, final float textSize,
+ final int color) {
final Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setTextAlign(Align.CENTER);
@@ -279,8 +284,9 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
return new BitmapDrawable(res, buffer);
}
- private CharSequence getStyledSuggestionWord(SuggestedWords suggestedWords, int pos) {
- final CharSequence word = suggestedWords.getWord(pos);
+ private CharSequence getStyledSuggestionWord(final SuggestedWords suggestedWords,
+ final int pos) {
+ final String word = suggestedWords.getWord(pos);
final boolean isAutoCorrect = pos == 1 && suggestedWords.willAutoCorrect();
final boolean isTypedWordValid = pos == 0 && suggestedWords.mTypedWordValid;
if (!isAutoCorrect && !isTypedWordValid)
@@ -299,7 +305,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
return spannedWord;
}
- private int getWordPosition(int index, SuggestedWords suggestedWords) {
+ private int getWordPosition(final int index, final SuggestedWords suggestedWords) {
// TODO: This works for 3 suggestions. Revisit this algorithm when there are 5 or more
// suggestions.
final int centerPos = suggestedWords.willAutoCorrect() ? 1 : 0;
@@ -312,7 +318,8 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
}
}
- private int getSuggestionTextColor(int index, SuggestedWords suggestedWords, int pos) {
+ private int getSuggestionTextColor(final int index, final SuggestedWords suggestedWords,
+ final int pos) {
// TODO: Need to revisit this logic with bigram suggestions
final boolean isSuggested = (pos != 0);
@@ -331,7 +338,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
// is in slot 1.
if (index == mCenterSuggestionIndex
&& AutoCorrection.shouldBlockAutoCorrectionBySafetyNet(
- suggestedWords.getWord(1).toString(), suggestedWords.getWord(0))) {
+ suggestedWords.getWord(1), suggestedWords.getWord(0))) {
return 0xFFFF0000;
}
}
@@ -355,8 +362,8 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
params.gravity = Gravity.CENTER;
}
- public void layout(SuggestedWords suggestedWords, ViewGroup stripView, ViewGroup placer,
- int stripWidth) {
+ public void layout(final SuggestedWords suggestedWords, final ViewGroup stripView,
+ final ViewGroup placer, final int stripWidth) {
if (suggestedWords.mIsPunctuationSuggestions) {
layoutPunctuationSuggestions(suggestedWords, stripView);
return;
@@ -402,7 +409,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
x += word.getMeasuredWidth();
if (DBG && pos < suggestedWords.size()) {
- final CharSequence debugInfo = Utils.getDebugInfo(suggestedWords, pos);
+ final String debugInfo = Utils.getDebugInfo(suggestedWords, pos);
if (debugInfo != null) {
final TextView info = mInfos.get(pos);
info.setText(debugInfo);
@@ -418,14 +425,14 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
}
}
- private int getSuggestionWidth(int index, int maxWidth) {
+ private int getSuggestionWidth(final int index, final int maxWidth) {
final int paddings = mPadding * mSuggestionsCountInStrip;
final int dividers = mDividerWidth * (mSuggestionsCountInStrip - 1);
final int availableWidth = maxWidth - paddings - dividers;
return (int)(availableWidth * getSuggestionWeight(index));
}
- private float getSuggestionWeight(int index) {
+ private float getSuggestionWeight(final int index) {
if (index == mCenterSuggestionIndex) {
return mCenterSuggestionWeight;
} else {
@@ -434,7 +441,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
}
}
- private void setupTexts(SuggestedWords suggestedWords, int countInStrip) {
+ private void setupTexts(final SuggestedWords suggestedWords, final int countInStrip) {
mTexts.clear();
final int count = Math.min(suggestedWords.size(), countInStrip);
for (int pos = 0; pos < count; pos++) {
@@ -447,8 +454,8 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
}
}
- private void layoutPunctuationSuggestions(SuggestedWords suggestedWords,
- ViewGroup stripView) {
+ private void layoutPunctuationSuggestions(final SuggestedWords suggestedWords,
+ final ViewGroup stripView) {
final int countInStrip = Math.min(suggestedWords.size(), PUNCTUATIONS_IN_STRIP);
for (int index = 0; index < countInStrip; index++) {
if (index != 0) {
@@ -459,7 +466,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
final TextView word = mWords.get(index);
word.setEnabled(true);
word.setTextColor(mColorAutoCorrect);
- final CharSequence text = suggestedWords.getWord(index);
+ final String text = suggestedWords.getWord(index);
word.setText(text);
word.setTextScaleX(1.0f);
word.setCompoundDrawables(null, null, null, null);
@@ -469,8 +476,8 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
mMoreSuggestionsAvailable = false;
}
- public void layoutAddToDictionaryHint(CharSequence word, ViewGroup stripView,
- int stripWidth, CharSequence hintText, OnClickListener listener) {
+ public void layoutAddToDictionaryHint(final String word, final ViewGroup stripView,
+ final int stripWidth, final CharSequence hintText, final OnClickListener listener) {
final int width = stripWidth - mDividerWidth - mPadding * 2;
final TextView wordView = mWordToSaveView;
@@ -511,11 +518,11 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
return (CharSequence)mWordToSaveView.getTag();
}
- public boolean isAddToDictionaryShowing(View v) {
+ public boolean isAddToDictionaryShowing(final View v) {
return v == mWordToSaveView || v == mHintToSaveView || v == mLeftwardsArrowView;
}
- private static void setLayoutWeight(View v, float weight, int height) {
+ private 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;
@@ -525,7 +532,8 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
}
}
- private static float getTextScaleX(CharSequence text, int maxWidth, TextPaint paint) {
+ private static float getTextScaleX(final CharSequence text, final int maxWidth,
+ final TextPaint paint) {
paint.setTextScaleX(1.0f);
final int width = getTextWidth(text, paint);
if (width <= maxWidth) {
@@ -534,8 +542,8 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
return maxWidth / (float)width;
}
- private static CharSequence getEllipsizedText(CharSequence text, int maxWidth,
- TextPaint paint) {
+ private static CharSequence getEllipsizedText(final CharSequence text, final int maxWidth,
+ final TextPaint paint) {
if (text == null) return null;
paint.setTextScaleX(1.0f);
final int width = getTextWidth(text, paint);
@@ -556,7 +564,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
return ellipsized;
}
- private static int getTextWidth(CharSequence text, TextPaint paint) {
+ private static int getTextWidth(final CharSequence text, final TextPaint paint) {
if (TextUtils.isEmpty(text)) return 0;
final Typeface savedTypeface = paint.getTypeface();
paint.setTypeface(getTextTypeface(text));
@@ -571,7 +579,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
return width;
}
- private static Typeface getTextTypeface(CharSequence text) {
+ private static Typeface getTextTypeface(final CharSequence text) {
if (!(text instanceof SpannableString))
return Typeface.DEFAULT;
@@ -593,11 +601,12 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
* @param context
* @param attrs
*/
- public SuggestionStripView(Context context, AttributeSet attrs) {
+ public SuggestionStripView(final Context context, final AttributeSet attrs) {
this(context, attrs, R.attr.suggestionStripViewStyle);
}
- public SuggestionStripView(Context context, AttributeSet attrs, int defStyle) {
+ public SuggestionStripView(final Context context, final AttributeSet attrs,
+ final int defStyle) {
super(context, attrs, defStyle);
final LayoutInflater inflater = LayoutInflater.from(context);
@@ -658,15 +667,12 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
* A connection back to the input method.
* @param listener
*/
- public void setListener(Listener listener, View inputView) {
+ public void setListener(final Listener listener, final View inputView) {
mListener = listener;
mKeyboardView = (KeyboardView)inputView.findViewById(R.id.keyboard_view);
}
- public void setSuggestions(SuggestedWords suggestedWords) {
- if (suggestedWords == null)
- return;
-
+ public void setSuggestions(final SuggestedWords suggestedWords) {
clear();
mSuggestedWords = suggestedWords;
mParams.layout(mSuggestedWords, mSuggestionsStrip, this, getWidth());
@@ -675,7 +681,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
}
}
- public int setMoreSuggestionsHeight(int remainingHeight) {
+ public int setMoreSuggestionsHeight(final int remainingHeight) {
return mParams.setMoreSuggestionsHeight(remainingHeight);
}
@@ -684,7 +690,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
&& mParams.isAddToDictionaryShowing(mSuggestionsStrip.getChildAt(0));
}
- public void showAddToDictionaryHint(CharSequence word, CharSequence hintText) {
+ public void showAddToDictionaryHint(final String word, final CharSequence hintText) {
clear();
mParams.layoutAddToDictionaryHint(word, mSuggestionsStrip, getWidth(), hintText, this);
}
@@ -708,16 +714,16 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
dismissMoreSuggestions();
}
- private void hidePreview() {
+ void hidePreview() {
mPreviewPopup.dismiss();
}
private final KeyboardActionListener mMoreSuggestionsListener =
new KeyboardActionListener.Adapter() {
@Override
- public boolean onCustomRequest(int requestCode) {
+ public boolean onCustomRequest(final int requestCode) {
final int index = requestCode;
- final CharSequence word = mSuggestedWords.getWord(index);
+ final String word = mSuggestedWords.getWord(index);
mListener.pickSuggestionManually(index, word);
dismissMoreSuggestions();
return true;
@@ -737,7 +743,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
}
};
- private boolean dismissMoreSuggestions() {
+ boolean dismissMoreSuggestions() {
if (mMoreSuggestionsWindow.isShowing()) {
mMoreSuggestionsWindow.dismiss();
return true;
@@ -746,41 +752,43 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
}
@Override
- public boolean onLongClick(View view) {
+ public boolean onLongClick(final View view) {
+ KeyboardSwitcher.getInstance().hapticAndAudioFeedback(Constants.NOT_A_CODE);
return showMoreSuggestions();
}
- private boolean showMoreSuggestions() {
+ boolean showMoreSuggestions() {
+ final Keyboard parentKeyboard = KeyboardSwitcher.getInstance().getKeyboard();
+ if (parentKeyboard == null) {
+ return false;
+ }
final SuggestionStripViewParams params = mParams;
- if (params.mMoreSuggestionsAvailable) {
- final int stripWidth = getWidth();
- final View container = mMoreSuggestionsContainer;
- final int maxWidth = stripWidth - container.getPaddingLeft()
- - container.getPaddingRight();
- final MoreSuggestions.Builder builder = mMoreSuggestionsBuilder;
- builder.layout(mSuggestedWords, params.mSuggestionsCountInStrip, maxWidth,
- (int)(maxWidth * params.mMinMoreSuggestionsWidth),
- params.getMaxMoreSuggestionsRow());
- mMoreSuggestionsView.setKeyboard(builder.build());
- container.measure(
- ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
-
- final MoreKeysPanel moreKeysPanel = mMoreSuggestionsView;
- final int pointX = stripWidth / 2;
- final int pointY = -params.mMoreSuggestionsBottomGap;
- moreKeysPanel.showMoreKeysPanel(
- this, mMoreSuggestionsController, pointX, pointY,
- mMoreSuggestionsWindow, mMoreSuggestionsListener);
- mMoreSuggestionsMode = MORE_SUGGESTIONS_CHECKING_MODAL_OR_SLIDING;
- mOriginX = mLastX;
- mOriginY = mLastY;
- mKeyboardView.dimEntireKeyboard(true);
- for (int i = 0; i < params.mSuggestionsCountInStrip; i++) {
- mWords.get(i).setPressed(false);
- }
- return true;
+ if (!params.mMoreSuggestionsAvailable) {
+ return false;
}
- return false;
+ final int stripWidth = getWidth();
+ final View container = mMoreSuggestionsContainer;
+ final int maxWidth = stripWidth - container.getPaddingLeft() - container.getPaddingRight();
+ final MoreSuggestions.Builder builder = mMoreSuggestionsBuilder;
+ builder.layout(mSuggestedWords, params.mSuggestionsCountInStrip, maxWidth,
+ (int)(maxWidth * params.mMinMoreSuggestionsWidth),
+ params.getMaxMoreSuggestionsRow(), parentKeyboard);
+ mMoreSuggestionsView.setKeyboard(builder.build());
+ container.measure(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+
+ final MoreKeysPanel moreKeysPanel = mMoreSuggestionsView;
+ final int pointX = stripWidth / 2;
+ final int pointY = -params.mMoreSuggestionsBottomGap;
+ moreKeysPanel.showMoreKeysPanel(this, mMoreSuggestionsController, pointX, pointY,
+ mMoreSuggestionsWindow, mMoreSuggestionsListener);
+ mMoreSuggestionsMode = MORE_SUGGESTIONS_CHECKING_MODAL_OR_SLIDING;
+ mOriginX = mLastX;
+ mOriginY = mLastY;
+ mKeyboardView.dimEntireKeyboard(true);
+ for (int i = 0; i < params.mSuggestionsCountInStrip; i++) {
+ mWords.get(i).setPressed(false);
+ }
+ return true;
}
// Working variables for onLongClick and dispatchTouchEvent.
@@ -807,7 +815,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
};
@Override
- public boolean dispatchTouchEvent(MotionEvent me) {
+ public boolean dispatchTouchEvent(final MotionEvent me) {
if (!mMoreSuggestionsWindow.isShowing()
|| mMoreSuggestionsMode == MORE_SUGGESTIONS_IN_MODAL_MODE) {
mLastX = (int)me.getX();
@@ -849,7 +857,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
}
@Override
- public void onClick(View view) {
+ public void onClick(final View view) {
if (mParams.isAddToDictionaryShowing(view)) {
mListener.addWordToUserDictionary(mParams.getAddToDictionaryWord().toString());
clear();
@@ -863,7 +871,7 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick
if (index >= mSuggestedWords.size())
return;
- final CharSequence word = mSuggestedWords.getWord(index);
+ final String word = mSuggestedWords.getWord(index);
mListener.pickSuggestionManually(index, word);
}
diff --git a/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp b/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp
index 560b3a533..2423bb53b 100644
--- a/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp
+++ b/native/jni/com_android_inputmethod_keyboard_ProximityInfo.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "LatinIME: jni: ProximityInfo"
#include "com_android_inputmethod_keyboard_ProximityInfo.h"
+#include "defines.h"
#include "jni.h"
#include "jni_common.h"
#include "proximity_info.h"
@@ -41,7 +42,7 @@ static void latinime_Keyboard_release(JNIEnv *env, jobject object, jlong proximi
delete pi;
}
-static JNINativeMethod sKeyboardMethods[] = {
+static JNINativeMethod sMethods[] = {
{"setProximityInfoNative", "(Ljava/lang/String;IIIIII[II[I[I[I[I[I[F[F[F)J",
reinterpret_cast<void *>(latinime_Keyboard_setProximityInfo)},
{"releaseProximityInfoNative", "(J)V", reinterpret_cast<void *>(latinime_Keyboard_release)}
@@ -49,7 +50,6 @@ static JNINativeMethod sKeyboardMethods[] = {
int register_ProximityInfo(JNIEnv *env) {
const char *const kClassPathName = "com/android/inputmethod/keyboard/ProximityInfo";
- return registerNativeMethods(env, kClassPathName, sKeyboardMethods,
- sizeof(sKeyboardMethods) / sizeof(sKeyboardMethods[0]));
+ return registerNativeMethods(env, kClassPathName, sMethods, NELEMS(sMethods));
}
} // namespace latinime
diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
index dd2513f9f..42f7da9d3 100644
--- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
+++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
@@ -44,9 +44,8 @@ class ProximityInfo;
static void releaseDictBuf(const void *dictBuf, const size_t length, const int fd);
static jlong latinime_BinaryDictionary_open(JNIEnv *env, jobject object,
- jstring sourceDir, jlong dictOffset, jlong dictSize,
- jint typedLetterMultiplier, jint fullWordMultiplier, jint maxWordLength, jint maxWords,
- jint maxPredictions) {
+ jstring sourceDir, jlong dictOffset, jlong dictSize, jint fullWordMultiplier,
+ jint maxWordLength, jint maxWords, jint maxPredictions) {
PROF_OPEN;
PROF_START(66);
const jsize sourceDirUtf8Length = env->GetStringUTFLength(sourceDir);
@@ -121,7 +120,7 @@ static jlong latinime_BinaryDictionary_open(JNIEnv *env, jobject object,
#endif // USE_MMAP_FOR_DICTIONARY
} else {
dictionary = new Dictionary(dictBuf, static_cast<int>(dictSize), fd, adjust,
- typedLetterMultiplier, fullWordMultiplier, maxWordLength, maxWords, maxPredictions);
+ fullWordMultiplier, maxWordLength, maxWords, maxPredictions);
}
PROF_END(66);
PROF_CLOSE;
@@ -277,7 +276,7 @@ static void releaseDictBuf(const void *dictBuf, const size_t length, const int f
}
static JNINativeMethod sMethods[] = {
- {"openNative", "(Ljava/lang/String;JJIIIII)J",
+ {"openNative", "(Ljava/lang/String;JJIIII)J",
reinterpret_cast<void *>(latinime_BinaryDictionary_open)},
{"closeNative", "(J)V", reinterpret_cast<void *>(latinime_BinaryDictionary_close)},
{"getSuggestionsNative", "(JJJ[I[I[I[I[IIIZ[IZ[C[I[I[I)I",
@@ -294,7 +293,6 @@ static JNINativeMethod sMethods[] = {
int register_BinaryDictionary(JNIEnv *env) {
const char *const kClassPathName = "com/android/inputmethod/latin/BinaryDictionary";
- return registerNativeMethods(env, kClassPathName, sMethods,
- sizeof(sMethods) / sizeof(sMethods[0]));
+ return registerNativeMethods(env, kClassPathName, sMethods, NELEMS(sMethods));
}
} // namespace latinime
diff --git a/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp b/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp
index 5d405f117..7bb8dc56e 100644
--- a/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp
+++ b/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp
@@ -17,6 +17,7 @@
#define LOG_TAG "LatinIME: jni: Session"
#include "com_android_inputmethod_latin_DicTraverseSession.h"
+#include "defines.h"
#include "dic_traverse_wrapper.h"
#include "jni.h"
#include "jni_common.h"
@@ -57,7 +58,6 @@ static JNINativeMethod sMethods[] = {
int register_DicTraverseSession(JNIEnv *env) {
const char *const kClassPathName = "com/android/inputmethod/latin/DicTraverseSession";
- return registerNativeMethods(env, kClassPathName, sMethods,
- sizeof(sMethods) / sizeof(sMethods[0]));
+ return registerNativeMethods(env, kClassPathName, sMethods, NELEMS(sMethods));
}
} // namespace latinime
diff --git a/native/jni/src/char_utils.cpp b/native/jni/src/char_utils.cpp
index d0547a982..9d27cadad 100644
--- a/native/jni/src/char_utils.cpp
+++ b/native/jni/src/char_utils.cpp
@@ -17,6 +17,7 @@
#include <cstdlib>
#include "char_utils.h"
+#include "defines.h"
namespace latinime {
@@ -33,7 +34,7 @@ struct LatinCapitalSmallPair {
//
// unsigned short c, cc, ccc, ccc2;
// for (c = 0; c < 0xFFFF ; c++) {
-// if (c < sizeof(BASE_CHARS) / sizeof(BASE_CHARS[0])) {
+// if (c < NELEMS(BASE_CHARS)) {
// cc = BASE_CHARS[c];
// } else {
// cc = c;
@@ -894,9 +895,7 @@ static int compare_pair_capital(const void *a, const void *b) {
unsigned short latin_tolower(const unsigned short c) {
struct LatinCapitalSmallPair *p =
static_cast<struct LatinCapitalSmallPair *>(bsearch(&c, SORTED_CHAR_MAP,
- sizeof(SORTED_CHAR_MAP) / sizeof(SORTED_CHAR_MAP[0]),
- sizeof(SORTED_CHAR_MAP[0]),
- compare_pair_capital));
+ NELEMS(SORTED_CHAR_MAP), sizeof(SORTED_CHAR_MAP[0]), compare_pair_capital));
return p ? p->small : c;
}
} // namespace latinime
diff --git a/native/jni/src/correction.cpp b/native/jni/src/correction.cpp
index 524abe9a1..d57b0e370 100644
--- a/native/jni/src/correction.cpp
+++ b/native/jni/src/correction.cpp
@@ -1118,7 +1118,7 @@ float Correction::RankingAlgorithm::calcNormalizedScore(const unsigned short *be
const int distance = editDistance(before, beforeLength, after, afterLength);
int spaceCount = 0;
for (int i = 0; i < afterLength; ++i) {
- if (after[i] == CODE_SPACE) {
+ if (after[i] == KEYCODE_SPACE) {
++spaceCount;
}
}
diff --git a/native/jni/src/correction.h b/native/jni/src/correction.h
index f016d5453..a099853e6 100644
--- a/native/jni/src/correction.h
+++ b/native/jni/src/correction.h
@@ -116,7 +116,6 @@ class Correction {
static int editDistance(const unsigned short *before,
const int beforeLength, const unsigned short *after, const int afterLength);
private:
- static const int CODE_SPACE = ' ';
static const int MAX_INITIAL_SCORE = 255;
};
diff --git a/native/jni/src/defines.h b/native/jni/src/defines.h
index ea0f0ef70..942068a49 100644
--- a/native/jni/src/defines.h
+++ b/native/jni/src/defines.h
@@ -219,6 +219,8 @@ static inline void prof_out(void) {
#define DEBUG_CORRECTION false
#define DEBUG_CORRECTION_FREQ false
#define DEBUG_WORDS_PRIORITY_QUEUE false
+#define DEBUG_SAMPLING_POINTS true
+#define DEBUG_POINTS_PROBABILITY true
#ifdef FLAG_FULL_DBG
#define DEBUG_GEO_FULL true
@@ -239,6 +241,8 @@ static inline void prof_out(void) {
#define DEBUG_CORRECTION false
#define DEBUG_CORRECTION_FREQ false
#define DEBUG_WORDS_PRIORITY_QUEUE false
+#define DEBUG_SAMPLING_POINTS false
+#define DEBUG_POINTS_PROBABILITY false
#define DEBUG_GEO_FULL false
@@ -344,8 +348,8 @@ static inline void prof_out(void) {
#define MULTIPLE_WORDS_DEMOTION_RATE 80
#define MIN_INPUT_LENGTH_FOR_THREE_OR_MORE_WORDS_CORRECTION 6
-#define TWO_WORDS_CORRECTION_WITH_OTHER_ERROR_THRESHOLD 0.35
-#define START_TWO_WORDS_CORRECTION_THRESHOLD 0.185
+#define TWO_WORDS_CORRECTION_WITH_OTHER_ERROR_THRESHOLD 0.35f
+#define START_TWO_WORDS_CORRECTION_THRESHOLD 0.185f
/* heuristic... This should be changed if we change the unit of the frequency. */
#define SUPPRESS_SHORT_MULTIPLE_WORDS_THRESHOLD_FREQ (MAX_FREQ * 58 / 100)
@@ -392,6 +396,8 @@ static inline void prof_out(void) {
template<typename T> inline T min(T a, T b) { return a < b ? a : b; }
template<typename T> inline T max(T a, T b) { return a > b ? a : b; }
+#define NELEMS(x) (sizeof(x) / sizeof((x)[0]))
+
// The ratio of neutral area radius to sweet spot radius.
#define NEUTRAL_AREA_RADIUS_RATIO 1.3f
diff --git a/native/jni/src/dictionary.cpp b/native/jni/src/dictionary.cpp
index 2fbe83e86..81789ccfc 100644
--- a/native/jni/src/dictionary.cpp
+++ b/native/jni/src/dictionary.cpp
@@ -30,13 +30,12 @@ namespace latinime {
// TODO: Change the type of all keyCodes to uint32_t
Dictionary::Dictionary(void *dict, int dictSize, int mmapFd, int dictBufAdjust,
- int typedLetterMultiplier, int fullWordMultiplier, int maxWordLength, int maxWords,
- int maxPredictions)
+ int fullWordMultiplier, int maxWordLength, int maxWords, int maxPredictions)
: mDict(static_cast<unsigned char *>(dict)),
mOffsetDict((static_cast<unsigned char *>(dict)) + BinaryFormat::getHeaderSize(mDict)),
mDictSize(dictSize), mMmapFd(mmapFd), mDictBufAdjust(dictBufAdjust),
- mUnigramDictionary(new UnigramDictionary(mOffsetDict, typedLetterMultiplier,
- fullWordMultiplier, maxWordLength, maxWords, BinaryFormat::getFlags(mDict))),
+ mUnigramDictionary(new UnigramDictionary(mOffsetDict, fullWordMultiplier, maxWordLength,
+ maxWords, BinaryFormat::getFlags(mDict))),
mBigramDictionary(new BigramDictionary(mOffsetDict, maxWordLength, maxPredictions)),
mGestureDecoder(new GestureDecoderWrapper(maxWordLength, maxWords)) {
if (DEBUG_DICT) {
diff --git a/native/jni/src/dictionary.h b/native/jni/src/dictionary.h
index a1358890d..120ca5f7f 100644
--- a/native/jni/src/dictionary.h
+++ b/native/jni/src/dictionary.h
@@ -41,8 +41,8 @@ class Dictionary {
const static int KIND_SHORTCUT = 7; // A shortcut
const static int KIND_PREDICTION = 8; // A prediction (== a suggestion with no input)
- Dictionary(void *dict, int dictSize, int mmapFd, int dictBufAdjust, int typedLetterMultipler,
- int fullWordMultiplier, int maxWordLength, int maxWords, int maxPredictions);
+ Dictionary(void *dict, int dictSize, int mmapFd, int dictBufAdjust, int fullWordMultiplier,
+ int maxWordLength, int maxWords, int maxPredictions);
int getSuggestions(ProximityInfo *proximityInfo, void *traverseSession, int *xcoordinates,
int *ycoordinates, int *times, int *pointerIds, int *codes, int codesSize,
diff --git a/native/jni/src/geometry_utils.h b/native/jni/src/geometry_utils.h
index 31359e19d..ee7c98562 100644
--- a/native/jni/src/geometry_utils.h
+++ b/native/jni/src/geometry_utils.h
@@ -85,5 +85,24 @@ static inline float pointToLineSegSquaredDistanceFloat(
}
return getSquaredDistanceFloat(x, y, projectionX, projectionY);
}
+
+// Normal distribution N(u, sigma^2).
+struct NormalDistribution {
+ NormalDistribution(const float u, const float sigma)
+ : mU(u), mSigma(sigma),
+ mPreComputedNonExpPart(1.0f / sqrtf(2.0f * M_PI_F * SQUARE_FLOAT(sigma))),
+ mPreComputedExponentPart(-1.0f / (2.0f * SQUARE_FLOAT(sigma))) {}
+
+ float getProbabilityDensity(const float x) {
+ const float shiftedX = x - mU;
+ return mPreComputedNonExpPart * expf(mPreComputedExponentPart * SQUARE_FLOAT(shiftedX));
+ }
+private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(NormalDistribution);
+ float mU; // mean value
+ float mSigma; // standard deviation
+ float mPreComputedNonExpPart; // = 1 / sqrt(2 * PI * sigma^2)
+ float mPreComputedExponentPart; // = -1 / (2 * sigma^2)
+};
} // namespace latinime
#endif // LATINIME_GEOMETRY_UTILS_H
diff --git a/native/jni/src/proximity_info.cpp b/native/jni/src/proximity_info.cpp
index fde93b5a9..e2aa15674 100644
--- a/native/jni/src/proximity_info.cpp
+++ b/native/jni/src/proximity_info.cpp
@@ -239,6 +239,9 @@ int ProximityInfo::getKeyIndexOf(const int c) const {
// We do not have the coordinate data
return NOT_AN_INDEX;
}
+ if (c == NOT_A_CODE_POINT) {
+ return NOT_AN_INDEX;
+ }
const int lowerCode = static_cast<int>(toLowerCase(c));
hash_map_compat<int, int>::const_iterator mapPos = mCodeToKeyMap.find(lowerCode);
if (mapPos != mCodeToKeyMap.end()) {
@@ -296,9 +299,7 @@ int ProximityInfo::getKeyCenterYOfKeyIdG(int keyId) const {
return 0;
}
-int ProximityInfo::getKeyKeyDistanceG(int key0, int key1) const {
- const int keyId0 = getKeyIndexOf(key0);
- const int keyId1 = getKeyIndexOf(key1);
+int ProximityInfo::getKeyKeyDistanceG(const int keyId0, const int keyId1) const {
if (keyId0 >= 0 && keyId1 >= 0) {
return mKeyKeyDistancesG[keyId0][keyId1];
}
diff --git a/native/jni/src/proximity_info.h b/native/jni/src/proximity_info.h
index 70942aa19..7ee15d578 100644
--- a/native/jni/src/proximity_info.h
+++ b/native/jni/src/proximity_info.h
@@ -109,7 +109,7 @@ class ProximityInfo {
int getKeyCenterYOfCodePointG(int charCode) const;
int getKeyCenterXOfKeyIdG(int keyId) const;
int getKeyCenterYOfKeyIdG(int keyId) const;
- int getKeyKeyDistanceG(int key0, int key1) const;
+ int getKeyKeyDistanceG(int keyId0, int keyId1) const;
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(ProximityInfo);
diff --git a/native/jni/src/proximity_info_state.cpp b/native/jni/src/proximity_info_state.cpp
index 392ec8194..d41acdace 100644
--- a/native/jni/src/proximity_info_state.cpp
+++ b/native/jni/src/proximity_info_state.cpp
@@ -15,6 +15,7 @@
*/
#include <cstring> // for memset()
+#include <sstream> // for debug prints
#include <stdint.h>
#define LOG_TAG "LatinIME: proximity_info_state.cpp"
@@ -104,7 +105,10 @@ void ProximityInfoState::initInputParams(const int pointerId, const float maxPoi
mLengthCache.clear();
mDistanceCache.clear();
mNearKeysVector.clear();
+ mSearchKeysVector.clear();
mRelativeSpeeds.clear();
+ mCharProbabilities.clear();
+ mDirections.clear();
}
if (DEBUG_GEO_FULL) {
AKLOGI("Init ProximityInfoState: reused points = %d, last input size = %d",
@@ -130,6 +134,10 @@ void ProximityInfoState::initInputParams(const int pointerId, const float maxPoi
NearKeysDistanceMap *currentNearKeysDistances = &nearKeysDistances[0];
NearKeysDistanceMap *prevNearKeysDistances = &nearKeysDistances[1];
NearKeysDistanceMap *prevPrevNearKeysDistances = &nearKeysDistances[2];
+ // "sumAngle" is accumulated by each angle of input points. And when "sumAngle" exceeds
+ // the threshold we save that point, reset sumAngle. This aims to keep the figure of
+ // the curve.
+ float sumAngle = 0.0f;
for (int i = pushTouchPointStartIndex; i <= lastInputIndex; ++i) {
// Assuming pointerId == 0 if pointerIds is null.
@@ -142,9 +150,18 @@ void ProximityInfoState::initInputParams(const int pointerId, const float maxPoi
const int x = proximityOnly ? NOT_A_COORDINATE : xCoordinates[i];
const int y = proximityOnly ? NOT_A_COORDINATE : yCoordinates[i];
const int time = times ? times[i] : -1;
+
+ if (i > 1) {
+ const float prevAngle = getAngle(xCoordinates[i - 2], yCoordinates[i - 2],
+ xCoordinates[i - 1], yCoordinates[i - 1]);
+ const float currentAngle =
+ getAngle(xCoordinates[i - 1], yCoordinates[i - 1], x, y);
+ sumAngle += getAngleDiff(prevAngle, currentAngle);
+ }
+
if (pushTouchPoint(i, c, x, y, time, isGeometric /* do sampling */,
- i == lastInputIndex, currentNearKeysDistances, prevNearKeysDistances,
- prevPrevNearKeysDistances)) {
+ i == lastInputIndex, sumAngle, currentNearKeysDistances,
+ prevNearKeysDistances, prevPrevNearKeysDistances)) {
// Previous point information was popped.
NearKeysDistanceMap *tmp = prevNearKeysDistances;
prevNearKeysDistances = currentNearKeysDistances;
@@ -154,6 +171,7 @@ void ProximityInfoState::initInputParams(const int pointerId, const float maxPoi
prevPrevNearKeysDistances = prevNearKeysDistances;
prevNearKeysDistances = currentNearKeysDistances;
currentNearKeysDistances = tmp;
+ sumAngle = 0.0f;
}
}
}
@@ -161,43 +179,68 @@ void ProximityInfoState::initInputParams(const int pointerId, const float maxPoi
}
if (mInputSize > 0 && isGeometric) {
- int sumDuration = mTimes.back() - mTimes.front();
- int sumLength = mLengthCache.back() - mLengthCache.front();
- float averageSpeed = static_cast<float>(sumLength) / static_cast<float>(sumDuration);
+ // Relative speed calculation.
+ const int sumDuration = mTimes.back() - mTimes.front();
+ const int sumLength = mLengthCache.back() - mLengthCache.front();
+ const float averageSpeed = static_cast<float>(sumLength) / static_cast<float>(sumDuration);
mRelativeSpeeds.resize(mInputSize);
for (int i = lastSavedInputSize; i < mInputSize; ++i) {
const int index = mInputIndice[i];
int length = 0;
int duration = 0;
- if (index == 0 && index < inputSize - 1) {
- length = getDistanceInt(xCoordinates[index], yCoordinates[index],
- xCoordinates[index + 1], yCoordinates[index + 1]);
- duration = times[index + 1] - times[index];
- } else if (index == inputSize - 1 && index > 0) {
- length = getDistanceInt(xCoordinates[index - 1], yCoordinates[index - 1],
- xCoordinates[index], yCoordinates[index]);
- duration = times[index] - times[index - 1];
- } else if (0 < index && index < inputSize - 1) {
- length = getDistanceInt(xCoordinates[index - 1], yCoordinates[index - 1],
- xCoordinates[index], yCoordinates[index])
- + getDistanceInt(xCoordinates[index], yCoordinates[index],
- xCoordinates[index + 1], yCoordinates[index + 1]);
- duration = times[index + 1] - times[index - 1];
+
+ // Calculate velocity by using distances and durations of
+ // NUM_POINTS_FOR_SPEED_CALCULATION points for both forward and backward.
+ static const int NUM_POINTS_FOR_SPEED_CALCULATION = 2;
+ for (int j = index; j < min(inputSize - 1, index + NUM_POINTS_FOR_SPEED_CALCULATION);
+ ++j) {
+ if (i < mInputSize - 1 && j >= mInputIndice[i + 1]) {
+ break;
+ }
+ length += getDistanceInt(xCoordinates[j], yCoordinates[j],
+ xCoordinates[j + 1], yCoordinates[j + 1]);
+ duration += times[j + 1] - times[j];
+ }
+ for (int j = index - 1; j >= max(0, index - NUM_POINTS_FOR_SPEED_CALCULATION); --j) {
+ if (i > 0 && j < mInputIndice[i - 1]) {
+ break;
+ }
+ length += getDistanceInt(xCoordinates[j], yCoordinates[j],
+ xCoordinates[j + 1], yCoordinates[j + 1]);
+ duration += times[j + 1] - times[j];
+ }
+ if (duration == 0 || sumDuration == 0) {
+ // Cannot calculate speed; thus, it gives an average value (1.0);
+ mRelativeSpeeds[i] = 1.0f;
} else {
- length = 0;
- duration = 1;
+ const float speed = static_cast<float>(length) / static_cast<float>(duration);
+ mRelativeSpeeds[i] = speed / averageSpeed;
}
- const float speed = static_cast<float>(length) / static_cast<float>(duration);
- mRelativeSpeeds[i] = speed / averageSpeed;
+ }
+
+ // Direction calculation.
+ mDirections.resize(mInputSize - 1);
+ for (int i = max(0, lastSavedInputSize - 1); i < mInputSize - 1; ++i) {
+ mDirections[i] = getDirection(i, i + 1);
+ }
+
+ }
+
+ if (DEBUG_GEO_FULL) {
+ for (int i = 0; i < mInputSize; ++i) {
+ AKLOGI("Sampled(%d): x = %d, y = %d, time = %d", i, mInputXs[i], mInputYs[i],
+ mTimes[i]);
}
}
if (mInputSize > 0) {
const int keyCount = mProximityInfo->getKeyCount();
mNearKeysVector.resize(mInputSize);
+ mSearchKeysVector.resize(mInputSize);
mDistanceCache.resize(mInputSize * keyCount);
for (int i = lastSavedInputSize; i < mInputSize; ++i) {
mNearKeysVector[i].reset();
+ mSearchKeysVector[i].reset();
static const float NEAR_KEY_NORMALIZED_SQUARED_THRESHOLD = 4.0f;
for (int k = 0; k < keyCount; ++k) {
const int index = i * keyCount + k;
@@ -207,29 +250,53 @@ void ProximityInfoState::initInputParams(const int pointerId, const float maxPoi
mProximityInfo->getNormalizedSquaredDistanceFromCenterFloatG(k, x, y);
mDistanceCache[index] = normalizedSquaredDistance;
if (normalizedSquaredDistance < NEAR_KEY_NORMALIZED_SQUARED_THRESHOLD) {
- mNearKeysVector[i].set(k, 1);
+ mNearKeysVector[i][k] = true;
}
}
}
-
- static const float READ_FORWORD_LENGTH_SCALE = 0.95f;
- const int readForwordLength = static_cast<int>(
- hypotf(mProximityInfo->getKeyboardWidth(), mProximityInfo->getKeyboardHeight())
- * READ_FORWORD_LENGTH_SCALE);
- for (int i = 0; i < mInputSize; ++i) {
- if (DEBUG_GEO_FULL) {
- AKLOGI("Sampled(%d): x = %d, y = %d, time = %d", i, mInputXs[i], mInputYs[i],
- mTimes[i]);
- }
- for (int j = max(i + 1, lastSavedInputSize); j < mInputSize; ++j) {
- if (mLengthCache[j] - mLengthCache[i] >= readForwordLength) {
- break;
+ if (isGeometric) {
+ // updates probabilities of skipping or mapping each key for all points.
+ updateAlignPointProbabilities(lastSavedInputSize);
+
+ static const float READ_FORWORD_LENGTH_SCALE = 0.95f;
+ const int readForwordLength = static_cast<int>(
+ hypotf(mProximityInfo->getKeyboardWidth(), mProximityInfo->getKeyboardHeight())
+ * READ_FORWORD_LENGTH_SCALE);
+ for (int i = 0; i < mInputSize; ++i) {
+ if (i >= lastSavedInputSize) {
+ mSearchKeysVector[i].reset();
+ }
+ for (int j = max(i, lastSavedInputSize); j < mInputSize; ++j) {
+ if (mLengthCache[j] - mLengthCache[i] >= readForwordLength) {
+ break;
+ }
+ mSearchKeysVector[i] |= mNearKeysVector[j];
}
- mNearKeysVector[i] |= mNearKeysVector[j];
}
}
}
+ if (DEBUG_SAMPLING_POINTS) {
+ std::stringstream originalX, originalY, sampledX, sampledY;
+ for (int i = 0; i < inputSize; ++i) {
+ originalX << xCoordinates[i];
+ originalY << yCoordinates[i];
+ if (i != inputSize - 1) {
+ originalX << ";";
+ originalY << ";";
+ }
+ }
+ for (int i = 0; i < mInputSize; ++i) {
+ sampledX << mInputXs[i];
+ sampledY << mInputYs[i];
+ if (i != mInputSize - 1) {
+ sampledX << ";";
+ sampledY << ";";
+ }
+ }
+ AKLOGI("\n%s, %s,\n%s, %s,\n", originalX.str().c_str(), originalY.str().c_str(),
+ sampledX.str().c_str(), sampledY.str().c_str());
+ }
// end
///////////////////////
@@ -294,7 +361,7 @@ bool ProximityInfoState::checkAndReturnIsContinuationPossible(const int inputSiz
// the given point and the nearest key position.
float ProximityInfoState::updateNearKeysDistances(const int x, const int y,
NearKeysDistanceMap *const currentNearKeysDistances) {
- static const float NEAR_KEY_THRESHOLD = 4.0f;
+ static const float NEAR_KEY_THRESHOLD = 2.0f;
currentNearKeysDistances->clear();
const int keyCount = mProximityInfo->getKeyCount();
@@ -332,64 +399,49 @@ bool ProximityInfoState::isPrevLocalMin(const NearKeysDistanceMap *const current
// Calculating a point score that indicates usefulness of the point.
float ProximityInfoState::getPointScore(
const int x, const int y, const int time, const bool lastPoint, const float nearest,
- const NearKeysDistanceMap *const currentNearKeysDistances,
+ const float sumAngle, const NearKeysDistanceMap *const currentNearKeysDistances,
const NearKeysDistanceMap *const prevNearKeysDistances,
const NearKeysDistanceMap *const prevPrevNearKeysDistances) const {
static const int DISTANCE_BASE_SCALE = 100;
- static const int SAVE_DISTANCE_SCALE = 200;
- static const int SKIP_DISTANCE_SCALE = 25;
- static const int CHECK_LOCALMIN_DISTANCE_THRESHOLD_SCALE = 40;
- static const int STRAIGHT_SKIP_DISTANCE_THRESHOLD_SCALE = 50;
- static const int CORNER_CHECK_DISTANCE_THRESHOLD_SCALE = 27;
- static const float SAVE_DISTANCE_SCORE = 2.0f;
- static const float SKIP_DISTANCE_SCORE = -1.0f;
- static const float CHECK_LOCALMIN_DISTANCE_SCORE = -1.0f;
- static const float STRAIGHT_ANGLE_THRESHOLD = M_PI_F / 36.0f;
- static const float STRAIGHT_SKIP_NEAREST_DISTANCE_THRESHOLD = 0.5f;
- static const float STRAIGHT_SKIP_SCORE = -1.0f;
- static const float CORNER_ANGLE_THRESHOLD = M_PI_F / 2.0f;
+ static const float NEAR_KEY_THRESHOLD = 0.6f;
+ static const int CORNER_CHECK_DISTANCE_THRESHOLD_SCALE = 25;
+ static const float NOT_LOCALMIN_DISTANCE_SCORE = -1.0f;
+ static const float LOCALMIN_DISTANCE_AND_NEAR_TO_KEY_SCORE = 1.0f;
+ static const float CORNER_ANGLE_THRESHOLD = M_PI_F * 2.0f / 3.0f;
+ static const float CORNER_SUM_ANGLE_THRESHOLD = M_PI_F / 4.0f;
static const float CORNER_SCORE = 1.0f;
- const std::size_t size = mInputXs.size();
- if (size <= 1) {
+ const size_t size = mInputXs.size();
+ // If there is only one point, add this point. Besides, if the previous point's distance map
+ // is empty, we re-compute nearby keys distances from the current point.
+ // Note that the current point is the first point in the incremental input that needs to
+ // be re-computed.
+ if (size <= 1 || prevNearKeysDistances->empty()) {
return 0.0f;
}
+
const int baseSampleRate = mProximityInfo->getMostCommonKeyWidth();
- const int distNext = getDistanceInt(x, y, mInputXs.back(), mInputYs.back())
- * DISTANCE_BASE_SCALE;
const int distPrev = getDistanceInt(mInputXs.back(), mInputYs.back(),
mInputXs[size - 2], mInputYs[size - 2]) * DISTANCE_BASE_SCALE;
float score = 0.0f;
- // Sum of distances
- if (distPrev + distNext > baseSampleRate * SAVE_DISTANCE_SCALE) {
- score += SAVE_DISTANCE_SCORE;
- }
- // Distance
- if (distPrev < baseSampleRate * SKIP_DISTANCE_SCALE) {
- score += SKIP_DISTANCE_SCORE;
- }
// Location
- if (distPrev < baseSampleRate * CHECK_LOCALMIN_DISTANCE_THRESHOLD_SCALE) {
- if (!isPrevLocalMin(currentNearKeysDistances, prevNearKeysDistances,
- prevPrevNearKeysDistances)) {
- score += CHECK_LOCALMIN_DISTANCE_SCORE;
- }
+ if (!isPrevLocalMin(currentNearKeysDistances, prevNearKeysDistances,
+ prevPrevNearKeysDistances)) {
+ score += NOT_LOCALMIN_DISTANCE_SCORE;
+ } else if (nearest < NEAR_KEY_THRESHOLD) {
+ // Promote points nearby keys
+ score += LOCALMIN_DISTANCE_AND_NEAR_TO_KEY_SCORE;
}
// Angle
const float angle1 = getAngle(x, y, mInputXs.back(), mInputYs.back());
const float angle2 = getAngle(mInputXs.back(), mInputYs.back(),
mInputXs[size - 2], mInputYs[size - 2]);
const float angleDiff = getAngleDiff(angle1, angle2);
- // Skip straight
- if (nearest > STRAIGHT_SKIP_NEAREST_DISTANCE_THRESHOLD
- && distPrev < baseSampleRate * STRAIGHT_SKIP_DISTANCE_THRESHOLD_SCALE
- && angleDiff < STRAIGHT_ANGLE_THRESHOLD) {
- score += STRAIGHT_SKIP_SCORE;
- }
+
// Save corner
if (distPrev > baseSampleRate * CORNER_CHECK_DISTANCE_THRESHOLD_SCALE
- && angleDiff > CORNER_ANGLE_THRESHOLD) {
+ && (sumAngle > CORNER_SUM_ANGLE_THRESHOLD || angleDiff > CORNER_ANGLE_THRESHOLD)) {
score += CORNER_SCORE;
}
return score;
@@ -398,17 +450,17 @@ float ProximityInfoState::getPointScore(
// Sampling touch point and pushing information to vectors.
// Returning if previous point is popped or not.
bool ProximityInfoState::pushTouchPoint(const int inputIndex, const int nodeChar, int x, int y,
- const int time, const bool sample, const bool isLastPoint,
+ const int time, const bool sample, const bool isLastPoint, const float sumAngle,
NearKeysDistanceMap *const currentNearKeysDistances,
const NearKeysDistanceMap *const prevNearKeysDistances,
const NearKeysDistanceMap *const prevPrevNearKeysDistances) {
- static const float LAST_POINT_SKIP_DISTANCE_SCALE = 0.25f;
+ static const int LAST_POINT_SKIP_DISTANCE_SCALE = 4;
size_t size = mInputXs.size();
bool popped = false;
if (nodeChar < 0 && sample) {
const float nearest = updateNearKeysDistances(x, y, currentNearKeysDistances);
- const float score = getPointScore(x, y, time, isLastPoint, nearest,
+ const float score = getPointScore(x, y, time, isLastPoint, nearest, sumAngle,
currentNearKeysDistances, prevNearKeysDistances, prevPrevNearKeysDistances);
if (score < 0) {
// Pop previous point because it would be useless.
@@ -419,36 +471,18 @@ bool ProximityInfoState::pushTouchPoint(const int inputIndex, const int nodeChar
popped = false;
}
// Check if the last point should be skipped.
- if (isLastPoint) {
- if (size > 0 && getDistanceFloat(x, y, mInputXs.back(), mInputYs.back())
- < mProximityInfo->getMostCommonKeyWidth() * LAST_POINT_SKIP_DISTANCE_SCALE) {
+ if (isLastPoint && size > 0) {
+ if (getDistanceInt(x, y, mInputXs.back(), mInputYs.back())
+ * LAST_POINT_SKIP_DISTANCE_SCALE < mProximityInfo->getMostCommonKeyWidth()) {
+ // This point is not used because it's too close to the previous point.
if (DEBUG_GEO_FULL) {
- AKLOGI("p0: size = %zd, x = %d, y = %d, lx = %d, ly = %d, dist = %f, "
- "width = %f", size, x, y, mInputXs.back(), mInputYs.back(),
- getDistanceFloat(x, y, mInputXs.back(), mInputYs.back()),
+ AKLOGI("p0: size = %zd, x = %d, y = %d, lx = %d, ly = %d, dist = %d, "
+ "width = %d", size, x, y, mInputXs.back(), mInputYs.back(),
+ getDistanceInt(x, y, mInputXs.back(), mInputYs.back()),
mProximityInfo->getMostCommonKeyWidth()
- * LAST_POINT_SKIP_DISTANCE_SCALE);
+ / LAST_POINT_SKIP_DISTANCE_SCALE);
}
return popped;
- } else if (size > 1) {
- int minChar = 0;
- float minDist = mMaxPointToKeyLength;
- for (NearKeysDistanceMap::const_iterator it = currentNearKeysDistances->begin();
- it != currentNearKeysDistances->end(); ++it) {
- if (minDist > it->second) {
- minChar = it->first;
- minDist = it->second;
- }
- }
- NearKeysDistanceMap::const_iterator itPP =
- prevNearKeysDistances->find(minChar);
- if (itPP != prevNearKeysDistances->end() && minDist > itPP->second) {
- if (DEBUG_GEO_FULL) {
- AKLOGI("p1: char = %c, minDist = %f, prevNear key minDist = %f",
- minChar, itPP->second, minDist);
- }
- return popped;
- }
}
}
}
@@ -503,12 +537,11 @@ int ProximityInfoState::getDuration(const int index) const {
return 0;
}
-float ProximityInfoState::getPointToKeyLength(const int inputIndex, const int codePoint,
- const float scale) const {
+float ProximityInfoState::getPointToKeyLength(const int inputIndex, const int codePoint) const {
const int keyId = mProximityInfo->getKeyIndexOf(codePoint);
if (keyId != NOT_AN_INDEX) {
const int index = inputIndex * mProximityInfo->getKeyCount() + keyId;
- return min(mDistanceCache[index] * scale, mMaxPointToKeyLength);
+ return min(mDistanceCache[index], mMaxPointToKeyLength);
}
if (isSkippableChar(codePoint)) {
return 0.0f;
@@ -517,8 +550,17 @@ float ProximityInfoState::getPointToKeyLength(const int inputIndex, const int co
return MAX_POINT_TO_KEY_LENGTH;
}
+float ProximityInfoState::getPointToKeyByIdLength(const int inputIndex, const int keyId) const {
+ if (keyId != NOT_AN_INDEX) {
+ const int index = inputIndex * mProximityInfo->getKeyCount() + keyId;
+ return min(mDistanceCache[index], mMaxPointToKeyLength);
+ }
+ // If the char is not a key on the keyboard then return the max length.
+ return static_cast<float>(MAX_POINT_TO_KEY_LENGTH);
+}
+
int ProximityInfoState::getSpaceY() const {
- const int keyId = mProximityInfo->getKeyIndexOf(' ');
+ const int keyId = mProximityInfo->getKeyIndexOf(KEYCODE_SPACE);
return mProximityInfo->getKeyCenterYOfKeyIdG(keyId);
}
@@ -538,8 +580,9 @@ int32_t ProximityInfoState::getAllPossibleChars(
return filterSize;
}
int newFilterSize = filterSize;
- for (int j = 0; j < mProximityInfo->getKeyCount(); ++j) {
- if (mNearKeysVector[index].test(j)) {
+ const int keyCount = mProximityInfo->getKeyCount();
+ for (int j = 0; j < keyCount; ++j) {
+ if (mSearchKeysVector[index].test(j)) {
const int32_t keyCodePoint = mProximityInfo->getCodePointOf(j);
bool insert = true;
// TODO: Avoid linear search
@@ -557,6 +600,12 @@ int32_t ProximityInfoState::getAllPossibleChars(
return newFilterSize;
}
+bool ProximityInfoState::isKeyInSerchKeysAfterIndex(const int index, const int keyId) const {
+ ASSERT(keyId >= 0);
+ ASSERT(index >= 0 && index < mInputSize);
+ return mSearchKeysVector[index].test(keyId);
+}
+
void ProximityInfoState::popInputData() {
mInputXs.pop_back();
mInputYs.pop_back();
@@ -565,4 +614,389 @@ void ProximityInfoState::popInputData() {
mInputIndice.pop_back();
}
+float ProximityInfoState::getDirection(const int index0, const int index1) const {
+ if (index0 < 0 || index0 > mInputSize - 1) {
+ return 0.0f;
+ }
+ if (index1 < 0 || index1 > mInputSize - 1) {
+ return 0.0f;
+ }
+ const int x1 = mInputXs[index0];
+ const int y1 = mInputYs[index0];
+ const int x2 = mInputXs[index1];
+ const int y2 = mInputYs[index1];
+ return getAngle(x1, y1, x2, y2);
+}
+
+float ProximityInfoState::getPointAngle(const int index) const {
+ if (index <= 0 || index >= mInputSize - 1) {
+ return 0.0f;
+ }
+ const float previousDirection = getDirection(index - 1, index);
+ const float nextDirection = getDirection(index, index + 1);
+ const float directionDiff = getAngleDiff(previousDirection, nextDirection);
+ return directionDiff;
+}
+
+float ProximityInfoState::getPointsAngle(
+ const int index0, const int index1, const int index2) const {
+ if (index0 < 0 || index0 > mInputSize - 1) {
+ return 0.0f;
+ }
+ if (index1 < 0 || index1 > mInputSize - 1) {
+ return 0.0f;
+ }
+ if (index2 < 0 || index2 > mInputSize - 1) {
+ return 0.0f;
+ }
+ const float previousDirection = getDirection(index0, index1);
+ const float nextDirection = getDirection(index1, index2);
+ return getAngleDiff(previousDirection, nextDirection);
+}
+
+float ProximityInfoState::getLineToKeyDistance(
+ const int from, const int to, const int keyId, const bool extend) const {
+ if (from < 0 || from > mInputSize - 1) {
+ return 0.0f;
+ }
+ if (to < 0 || to > mInputSize - 1) {
+ return 0.0f;
+ }
+ const int x0 = mInputXs[from];
+ const int y0 = mInputYs[from];
+ const int x1 = mInputXs[to];
+ const int y1 = mInputYs[to];
+
+ const int keyX = mProximityInfo->getKeyCenterXOfKeyIdG(keyId);
+ const int keyY = mProximityInfo->getKeyCenterYOfKeyIdG(keyId);
+
+ return pointToLineSegSquaredDistanceFloat(keyX, keyY, x0, y0, x1, y1, extend);
+}
+
+// Updates probabilities of aligning to some keys and skipping.
+// Word suggestion should be based on this probabilities.
+void ProximityInfoState::updateAlignPointProbabilities(const int start) {
+ static const float MIN_PROBABILITY = 0.000001f;
+ static const float MAX_SKIP_PROBABILITY = 0.95f;
+ static const float SKIP_FIRST_POINT_PROBABILITY = 0.01f;
+ static const float SKIP_LAST_POINT_PROBABILITY = 0.1f;
+ static const float MIN_SPEED_RATE_FOR_SKIP_PROBABILITY = 0.15f;
+ static const float SPEED_WEIGHT_FOR_SKIP_PROBABILITY = 0.9f;
+ static const float SLOW_STRAIGHT_WEIGHT_FOR_SKIP_PROBABILITY = 0.6f;
+ static const float NEAREST_DISTANCE_WEIGHT = 0.5f;
+ static const float NEAREST_DISTANCE_BIAS = 0.5f;
+ static const float NEAREST_DISTANCE_WEIGHT_FOR_LAST = 0.6f;
+ static const float NEAREST_DISTANCE_BIAS_FOR_LAST = 0.4f;
+
+ static const float ANGLE_WEIGHT = 0.90f;
+ static const float DEEP_CORNER_ANGLE_THRESHOLD = M_PI_F * 60.0f / 180.0f;
+ static const float SKIP_DEEP_CORNER_PROBABILITY = 0.1f;
+ static const float CORNER_ANGLE_THRESHOLD = M_PI_F * 30.0f / 180.0f;
+ static const float STRAIGHT_ANGLE_THRESHOLD = M_PI_F * 15.0f / 180.0f;
+ static const float SKIP_CORNER_PROBABILITY = 0.4f;
+ static const float SPEED_MARGIN = 0.1f;
+ static const float CENTER_VALUE_OF_NORMALIZED_DISTRIBUTION = 0.0f;
+
+ const int keyCount = mProximityInfo->getKeyCount();
+ mCharProbabilities.resize(mInputSize);
+ // Calculates probabilities of using a point as a correlated point with the character
+ // for each point.
+ for (int i = start; i < mInputSize; ++i) {
+ mCharProbabilities[i].clear();
+ // First, calculates skip probability. Starts form MIN_SKIP_PROBABILITY.
+ // Note that all values that are multiplied to this probability should be in [0.0, 1.0];
+ float skipProbability = MAX_SKIP_PROBABILITY;
+
+ const float currentAngle = getPointAngle(i);
+ const float relativeSpeed = getRelativeSpeed(i);
+
+ float nearestKeyDistance = static_cast<float>(MAX_POINT_TO_KEY_LENGTH);
+ for (int j = 0; j < keyCount; ++j) {
+ if (mNearKeysVector[i].test(j)) {
+ const float distance = getPointToKeyByIdLength(i, j);
+ if (distance < nearestKeyDistance) {
+ nearestKeyDistance = distance;
+ }
+ }
+ }
+
+ if (i == 0) {
+ skipProbability *= min(1.0f, nearestKeyDistance * NEAREST_DISTANCE_WEIGHT
+ + NEAREST_DISTANCE_BIAS);
+ // Promote the first point
+ skipProbability *= SKIP_FIRST_POINT_PROBABILITY;
+ } else if (i == mInputSize - 1) {
+ skipProbability *= min(1.0f, nearestKeyDistance * NEAREST_DISTANCE_WEIGHT_FOR_LAST
+ + NEAREST_DISTANCE_BIAS_FOR_LAST);
+ // Promote the last point
+ skipProbability *= SKIP_LAST_POINT_PROBABILITY;
+ } else {
+ // If the current speed is relatively slower than adjacent keys, we promote this point.
+ if (getRelativeSpeed(i - 1) - SPEED_MARGIN > relativeSpeed
+ && relativeSpeed < getRelativeSpeed(i + 1) - SPEED_MARGIN) {
+ if (currentAngle < CORNER_ANGLE_THRESHOLD) {
+ skipProbability *= min(1.0f, relativeSpeed
+ * SLOW_STRAIGHT_WEIGHT_FOR_SKIP_PROBABILITY);
+ } else {
+ // If the angle is small enough, we promote this point more. (e.g. pit vs put)
+ skipProbability *= min(1.0f, relativeSpeed * SPEED_WEIGHT_FOR_SKIP_PROBABILITY
+ + MIN_SPEED_RATE_FOR_SKIP_PROBABILITY);
+ }
+ }
+
+ skipProbability *= min(1.0f, relativeSpeed * nearestKeyDistance *
+ NEAREST_DISTANCE_WEIGHT + NEAREST_DISTANCE_BIAS);
+
+ // Adjusts skip probability by a rate depending on angle.
+ // ANGLE_RATE of skipProbability is adjusted by current angle.
+ skipProbability *= (M_PI_F - currentAngle) / M_PI_F * ANGLE_WEIGHT
+ + (1.0f - ANGLE_WEIGHT);
+ if (currentAngle > DEEP_CORNER_ANGLE_THRESHOLD) {
+ skipProbability *= SKIP_DEEP_CORNER_PROBABILITY;
+ }
+ // We assume the angle of this point is the angle for point[i], point[i - 2]
+ // and point[i - 3]. The reason why we don't use the angle for point[i], point[i - 1]
+ // and point[i - 2] is this angle can be more affected by the noise.
+ const float prevAngle = getPointsAngle(i, i - 2, i - 3);
+ if (i >= 3 && prevAngle < STRAIGHT_ANGLE_THRESHOLD
+ && currentAngle > CORNER_ANGLE_THRESHOLD) {
+ skipProbability *= SKIP_CORNER_PROBABILITY;
+ }
+ }
+
+ // probabilities must be in [0.0, MAX_SKIP_PROBABILITY];
+ ASSERT(skipProbability >= 0.0f);
+ ASSERT(skipProbability <= MAX_SKIP_PROBABILITY);
+ mCharProbabilities[i][NOT_AN_INDEX] = skipProbability;
+
+ // Second, calculates key probabilities by dividing the rest probability
+ // (1.0f - skipProbability).
+ const float inputCharProbability = 1.0f - skipProbability;
+
+ // TODO: The variance is critical for accuracy; thus, adjusting these parameter by machine
+ // learning or something would be efficient.
+ static const float SPEEDxANGLE_WEIGHT_FOR_STANDARD_DIVIATION = 0.3f;
+ static const float MAX_SPEEDxANGLE_RATE_FOR_STANDERD_DIVIATION = 0.25f;
+ static const float SPEEDxNEAREST_WEIGHT_FOR_STANDARD_DIVIATION = 0.5f;
+ static const float MAX_SPEEDxNEAREST_RATE_FOR_STANDERD_DIVIATION = 0.15f;
+ static const float MIN_STANDERD_DIVIATION = 0.37f;
+
+ const float speedxAngleRate = min(relativeSpeed * currentAngle / M_PI_F
+ * SPEEDxANGLE_WEIGHT_FOR_STANDARD_DIVIATION,
+ MAX_SPEEDxANGLE_RATE_FOR_STANDERD_DIVIATION);
+ const float speedxNearestKeyDistanceRate = min(relativeSpeed * nearestKeyDistance
+ * SPEEDxNEAREST_WEIGHT_FOR_STANDARD_DIVIATION,
+ MAX_SPEEDxNEAREST_RATE_FOR_STANDERD_DIVIATION);
+ const float sigma = speedxAngleRate + speedxNearestKeyDistanceRate + MIN_STANDERD_DIVIATION;
+
+ NormalDistribution distribution(CENTER_VALUE_OF_NORMALIZED_DISTRIBUTION, sigma);
+ static const float PREV_DISTANCE_WEIGHT = 0.5f;
+ static const float NEXT_DISTANCE_WEIGHT = 0.6f;
+ // Summing up probability densities of all near keys.
+ float sumOfProbabilityDensities = 0.0f;
+ for (int j = 0; j < keyCount; ++j) {
+ if (mNearKeysVector[i].test(j)) {
+ float distance = sqrtf(getPointToKeyByIdLength(i, j));
+ if (i == 0 && i != mInputSize - 1) {
+ // For the first point, weighted average of distances from first point and the
+ // next point to the key is used as a point to key distance.
+ const float nextDistance = sqrtf(getPointToKeyByIdLength(i + 1, j));
+ if (nextDistance < distance) {
+ // The distance of the first point tends to bigger than continuing
+ // points because the first touch by the user can be sloppy.
+ // So we promote the first point if the distance of that point is larger
+ // than the distance of the next point.
+ distance = (distance + nextDistance * NEXT_DISTANCE_WEIGHT)
+ / (1.0f + NEXT_DISTANCE_WEIGHT);
+ }
+ } else if (i != 0 && i == mInputSize - 1) {
+ // For the first point, weighted average of distances from last point and
+ // the previous point to the key is used as a point to key distance.
+ const float previousDistance = sqrtf(getPointToKeyByIdLength(i - 1, j));
+ if (previousDistance < distance) {
+ // The distance of the last point tends to bigger than continuing points
+ // because the last touch by the user can be sloppy. So we promote the
+ // last point if the distance of that point is larger than the distance of
+ // the previous point.
+ distance = (distance + previousDistance * PREV_DISTANCE_WEIGHT)
+ / (1.0f + PREV_DISTANCE_WEIGHT);
+ }
+ }
+ // TODO: Promote the first point when the extended line from the next input is near
+ // from a key. Also, promote the last point as well.
+ sumOfProbabilityDensities += distribution.getProbabilityDensity(distance);
+ }
+ }
+
+ // Split the probability of an input point to keys that are close to the input point.
+ for (int j = 0; j < keyCount; ++j) {
+ if (mNearKeysVector[i].test(j)) {
+ float distance = sqrtf(getPointToKeyByIdLength(i, j));
+ if (i == 0 && i != mInputSize - 1) {
+ // For the first point, weighted average of distances from the first point and
+ // the next point to the key is used as a point to key distance.
+ const float prevDistance = sqrtf(getPointToKeyByIdLength(i + 1, j));
+ if (prevDistance < distance) {
+ distance = (distance + prevDistance * NEXT_DISTANCE_WEIGHT)
+ / (1.0f + NEXT_DISTANCE_WEIGHT);
+ }
+ } else if (i != 0 && i == mInputSize - 1) {
+ // For the first point, weighted average of distances from last point and
+ // the previous point to the key is used as a point to key distance.
+ const float prevDistance = sqrtf(getPointToKeyByIdLength(i - 1, j));
+ if (prevDistance < distance) {
+ distance = (distance + prevDistance * PREV_DISTANCE_WEIGHT)
+ / (1.0f + PREV_DISTANCE_WEIGHT);
+ }
+ }
+ const float probabilityDensity = distribution.getProbabilityDensity(distance);
+ const float probability = inputCharProbability * probabilityDensity
+ / sumOfProbabilityDensities;
+ mCharProbabilities[i][j] = probability;
+ }
+ }
+ }
+
+
+ if (DEBUG_POINTS_PROBABILITY) {
+ for (int i = 0; i < mInputSize; ++i) {
+ std::stringstream sstream;
+ sstream << i << ", ";
+ sstream << "("<< mInputXs[i] << ", ";
+ sstream << ", "<< mInputYs[i] << "), ";
+ sstream << "Speed: "<< getRelativeSpeed(i) << ", ";
+ sstream << "Angle: "<< getPointAngle(i) << ", \n";
+
+ for (hash_map_compat<int, float>::iterator it = mCharProbabilities[i].begin();
+ it != mCharProbabilities[i].end(); ++it) {
+ if (it->first == NOT_AN_INDEX) {
+ sstream << it->first
+ << "(skip):"
+ << it->second
+ << "\n";
+ } else {
+ sstream << it->first
+ << "("
+ << static_cast<char>(mProximityInfo->getCodePointOf(it->first))
+ << "):"
+ << it->second
+ << "\n";
+ }
+ }
+ AKLOGI("%s", sstream.str().c_str());
+ }
+ }
+
+ // Decrease key probabilities of points which don't have the highest probability of that key
+ // among nearby points. Probabilities of the first point and the last point are not suppressed.
+ for (int i = max(start, 1); i < mInputSize; ++i) {
+ for (int j = i + 1; j < mInputSize; ++j) {
+ if (!suppressCharProbabilities(i, j)) {
+ break;
+ }
+ }
+ for (int j = i - 1; j >= max(start, 0); --j) {
+ if (!suppressCharProbabilities(i, j)) {
+ break;
+ }
+ }
+ }
+
+ // Converting from raw probabilities to log probabilities to calculate spatial distance.
+ for (int i = start; i < mInputSize; ++i) {
+ for (int j = 0; j < keyCount; ++j) {
+ hash_map_compat<int, float>::iterator it = mCharProbabilities[i].find(j);
+ if (it == mCharProbabilities[i].end()){
+ mNearKeysVector[i].reset(j);
+ } else if(it->second < MIN_PROBABILITY) {
+ // Erases from near keys vector because it has very low probability.
+ mNearKeysVector[i].reset(j);
+ mCharProbabilities[i].erase(j);
+ } else {
+ it->second = -logf(it->second);
+ }
+ }
+ mCharProbabilities[i][NOT_AN_INDEX] = -logf(mCharProbabilities[i][NOT_AN_INDEX]);
+ }
+}
+
+// Decreases char probabilities of index0 by checking probabilities of a near point (index1) and
+// increases char probabilities of index1 by checking probabilities of index0.
+bool ProximityInfoState::suppressCharProbabilities(const int index0, const int index1) {
+ ASSERT(0 <= index0 && index0 < mInputSize);
+ ASSERT(0 <= index1 && index1 < mInputSize);
+
+ static const float SUPPRESSION_LENGTH_WEIGHT = 1.5f;
+ static const float MIN_SUPPRESSION_RATE = 0.1f;
+ static const float SUPPRESSION_WEIGHT = 0.5f;
+ static const float SUPPRESSION_WEIGHT_FOR_PROBABILITY_GAIN = 0.1f;
+ static const float SKIP_PROBABALITY_WEIGHT_FOR_PROBABILITY_GAIN = 0.3f;
+
+ const float keyWidthFloat = static_cast<float>(mProximityInfo->getMostCommonKeyWidth());
+ const float diff = fabsf(static_cast<float>(mLengthCache[index0] - mLengthCache[index1]));
+ if (diff > keyWidthFloat * SUPPRESSION_LENGTH_WEIGHT) {
+ return false;
+ }
+ const float suppressionRate = MIN_SUPPRESSION_RATE
+ + diff / keyWidthFloat / SUPPRESSION_LENGTH_WEIGHT * SUPPRESSION_WEIGHT;
+ for (hash_map_compat<int, float>::iterator it = mCharProbabilities[index0].begin();
+ it != mCharProbabilities[index0].end(); ++it) {
+ hash_map_compat<int, float>::iterator it2 = mCharProbabilities[index1].find(it->first);
+ if (it2 != mCharProbabilities[index1].end() && it->second < it2->second) {
+ const float newProbability = it->second * suppressionRate;
+ const float suppression = it->second - newProbability;
+ it->second = newProbability;
+ // mCharProbabilities[index0][NOT_AN_INDEX] is the probability of skipping this point.
+ mCharProbabilities[index0][NOT_AN_INDEX] += suppression;
+
+ // Add the probability of the same key nearby index1
+ const float probabilityGain = min(suppression * SUPPRESSION_WEIGHT_FOR_PROBABILITY_GAIN,
+ mCharProbabilities[index1][NOT_AN_INDEX]
+ * SKIP_PROBABALITY_WEIGHT_FOR_PROBABILITY_GAIN);
+ it2->second += probabilityGain;
+ mCharProbabilities[index1][NOT_AN_INDEX] -= probabilityGain;
+ }
+ }
+ return true;
+}
+
+// Get a word that is detected by tracing highest probability sequence into charBuf and returns
+// probability of generating the word.
+float ProximityInfoState::getHighestProbabilitySequence(uint16_t *const charBuf) const {
+ static const float DEMOTION_LOG_PROBABILITY = 0.3f;
+ int index = 0;
+ float sumLogProbability = 0.0f;
+ // TODO: Current implementation is greedy algorithm. DP would be efficient for many cases.
+ for (int i = 0; i < mInputSize && index < MAX_WORD_LENGTH_INTERNAL - 1; ++i) {
+ float minLogProbability = static_cast<float>(MAX_POINT_TO_KEY_LENGTH);
+ int character = NOT_AN_INDEX;
+ for (hash_map_compat<int, float>::const_iterator it = mCharProbabilities[i].begin();
+ it != mCharProbabilities[i].end(); ++it) {
+ const float logProbability = (it->first != NOT_AN_INDEX)
+ ? it->second + DEMOTION_LOG_PROBABILITY : it->second;
+ if (logProbability < minLogProbability) {
+ minLogProbability = logProbability;
+ character = it->first;
+ }
+ }
+ if (character != NOT_AN_INDEX) {
+ charBuf[index] = mProximityInfo->getCodePointOf(character);
+ index++;
+ }
+ sumLogProbability += minLogProbability;
+ }
+ charBuf[index] = '\0';
+ return sumLogProbability;
+}
+
+// Returns a probability of mapping index to keyIndex.
+float ProximityInfoState::getProbability(const int index, const int keyIndex) const {
+ ASSERT(0 <= index && index < mInputSize);
+ hash_map_compat<int, float>::const_iterator it = mCharProbabilities[index].find(keyIndex);
+ if (it != mCharProbabilities[index].end()) {
+ return it->second;
+ }
+ return static_cast<float>(MAX_POINT_TO_KEY_LENGTH);
+}
+
} // namespace latinime
diff --git a/native/jni/src/proximity_info_state.h b/native/jni/src/proximity_info_state.h
index c1ec76c38..1a3f2869d 100644
--- a/native/jni/src/proximity_info_state.h
+++ b/native/jni/src/proximity_info_state.h
@@ -55,7 +55,8 @@ class ProximityInfoState {
mHasTouchPositionCorrectionData(false), mMostCommonKeyWidthSquare(0), mLocaleStr(),
mKeyCount(0), mCellHeight(0), mCellWidth(0), mGridHeight(0), mGridWidth(0),
mIsContinuationPossible(false), mInputXs(), mInputYs(), mTimes(), mInputIndice(),
- mDistanceCache(), mLengthCache(), mRelativeSpeeds(), mNearKeysVector(),
+ mDistanceCache(), mLengthCache(), mRelativeSpeeds(), mDirections(),
+ mCharProbabilities(), mNearKeysVector(), mSearchKeysVector(),
mTouchPositionCorrectionEnabled(false), mInputSize(0) {
memset(mInputCodes, 0, sizeof(mInputCodes));
memset(mNormalizedSquaredDistances, 0, sizeof(mNormalizedSquaredDistances));
@@ -213,7 +214,8 @@ class ProximityInfoState {
return mIsContinuationPossible;
}
- float getPointToKeyLength(const int inputIndex, const int charCode, const float scale) const;
+ float getPointToKeyLength(const int inputIndex, const int charCode) const;
+ float getPointToKeyByIdLength(const int inputIndex, const int keyId) const;
int getSpaceY() const;
@@ -223,6 +225,25 @@ class ProximityInfoState {
float getRelativeSpeed(const int index) const {
return mRelativeSpeeds[index];
}
+
+ float getDirection(const int index) const {
+ return mDirections[index];
+ }
+ // get xy direction
+ float getDirection(const int x, const int y) const;
+
+ float getPointAngle(const int index) const;
+ // Returns angle of three points. x, y, and z are indices.
+ float getPointsAngle(const int index0, const int index1, const int index2) const;
+
+ float getHighestProbabilitySequence(uint16_t *const charBuf) const;
+
+ float getProbability(const int index, const int charCode) const;
+
+ float getLineToKeyDistance(
+ const int from, const int to, const int keyId, const bool extend) const;
+
+ bool isKeyInSerchKeysAfterIndex(const int index, const int keyId) const;
private:
DISALLOW_COPY_AND_ASSIGN(ProximityInfoState);
typedef hash_map_compat<int, float> NearKeysDistanceMap;
@@ -235,7 +256,7 @@ class ProximityInfoState {
const int keyIndex, const int inputIndex) const;
bool pushTouchPoint(const int inputIndex, const int nodeChar, int x, int y, const int time,
- const bool sample, const bool isLastPoint,
+ const bool sample, const bool isLastPoint, const float sumAngle,
NearKeysDistanceMap *const currentNearKeysDistances,
const NearKeysDistanceMap *const prevNearKeysDistances,
const NearKeysDistanceMap *const prevPrevNearKeysDistances);
@@ -259,12 +280,14 @@ class ProximityInfoState {
const NearKeysDistanceMap *const prevPrevNearKeysDistances) const;
float getPointScore(
const int x, const int y, const int time, const bool last, const float nearest,
- const NearKeysDistanceMap *const currentNearKeysDistances,
+ const float sumAngle, const NearKeysDistanceMap *const currentNearKeysDistances,
const NearKeysDistanceMap *const prevNearKeysDistances,
const NearKeysDistanceMap *const prevPrevNearKeysDistances) const;
bool checkAndReturnIsContinuationPossible(const int inputSize, const int *const xCoordinates,
const int *const yCoordinates, const int *const times);
void popInputData();
+ void updateAlignPointProbabilities(const int start);
+ bool suppressCharProbabilities(const int index1, const int index2);
// const
const ProximityInfo *mProximityInfo;
@@ -286,7 +309,18 @@ class ProximityInfoState {
std::vector<float> mDistanceCache;
std::vector<int> mLengthCache;
std::vector<float> mRelativeSpeeds;
+ std::vector<float> mDirections;
+ // probabilities of skipping or mapping to a key for each point.
+ std::vector<hash_map_compat<int, float> > mCharProbabilities;
+ // The vector for the key code set which holds nearby keys for each sampled input point
+ // 1. Used to calculate the probability of the key
+ // 2. Used to calculate mSearchKeysVector
std::vector<NearKeycodesSet> mNearKeysVector;
+ // The vector for the key code set which holds nearby keys of some trailing sampled input points
+ // for each sampled input point. These nearby keys contain the next characters which can be in
+ // the dictionary. Specifically, currently we are looking for keys nearby trailing sampled
+ // inputs including the current input point.
+ std::vector<NearKeycodesSet> mSearchKeysVector;
bool mTouchPositionCorrectionEnabled;
int32_t mInputCodes[MAX_PROXIMITY_CHARS_SIZE_INTERNAL * MAX_WORD_LENGTH_INTERNAL];
int mNormalizedSquaredDistances[MAX_PROXIMITY_CHARS_SIZE_INTERNAL * MAX_WORD_LENGTH_INTERNAL];
diff --git a/native/jni/src/unigram_dictionary.cpp b/native/jni/src/unigram_dictionary.cpp
index e3649bd4b..f1fd1389a 100644
--- a/native/jni/src/unigram_dictionary.cpp
+++ b/native/jni/src/unigram_dictionary.cpp
@@ -41,14 +41,12 @@ const UnigramDictionary::digraph_t UnigramDictionary::FRENCH_LIGATURES_DIGRAPHS[
{ 'o', 'e', 0x0153 } }; // U+0153 : LATIN SMALL LIGATURE OE
// TODO: check the header
-UnigramDictionary::UnigramDictionary(const uint8_t *const streamStart, int typedLetterMultiplier,
- int fullWordMultiplier, int maxWordLength, int maxWords, const unsigned int flags)
- : DICT_ROOT(streamStart), MAX_WORD_LENGTH(maxWordLength), MAX_WORDS(maxWords),
- TYPED_LETTER_MULTIPLIER(typedLetterMultiplier), FULL_WORD_MULTIPLIER(fullWordMultiplier),
- // TODO : remove this variable.
- ROOT_POS(0),
- BYTES_IN_ONE_CHAR(sizeof(int)),
- MAX_DIGRAPH_SEARCH_DEPTH(DEFAULT_MAX_DIGRAPH_SEARCH_DEPTH), FLAGS(flags) {
+UnigramDictionary::UnigramDictionary(const uint8_t *const streamStart, int fullWordMultiplier,
+ int maxWordLength, int maxWords, const unsigned int flags)
+ : DICT_ROOT(streamStart), MAX_WORD_LENGTH(maxWordLength), MAX_WORDS(maxWords),
+ FULL_WORD_MULTIPLIER(fullWordMultiplier), // TODO : remove this variable.
+ ROOT_POS(0), BYTES_IN_ONE_CHAR(sizeof(int)),
+ MAX_DIGRAPH_SEARCH_DEPTH(DEFAULT_MAX_DIGRAPH_SEARCH_DEPTH), FLAGS(flags) {
if (DEBUG_DICT) {
AKLOGI("UnigramDictionary - constructor");
}
@@ -188,8 +186,7 @@ int UnigramDictionary::getSuggestions(ProximityInfo *proximityInfo, const int *x
getWordWithDigraphSuggestionsRec(proximityInfo, xcoordinates, ycoordinates, codesBuffer,
xCoordinatesBuffer, yCoordinatesBuffer, codesSize, bigramMap, bigramFilter,
useFullEditDistance, codes, codesSize, 0, codesBuffer, &masterCorrection,
- &queuePool, GERMAN_UMLAUT_DIGRAPHS,
- sizeof(GERMAN_UMLAUT_DIGRAPHS) / sizeof(GERMAN_UMLAUT_DIGRAPHS[0]));
+ &queuePool, GERMAN_UMLAUT_DIGRAPHS, NELEMS(GERMAN_UMLAUT_DIGRAPHS));
} else if (BinaryFormat::REQUIRES_FRENCH_LIGATURES_PROCESSING & FLAGS) {
int codesBuffer[getCodesBufferSize(codes, codesSize)];
int xCoordinatesBuffer[codesSize];
@@ -197,8 +194,7 @@ int UnigramDictionary::getSuggestions(ProximityInfo *proximityInfo, const int *x
getWordWithDigraphSuggestionsRec(proximityInfo, xcoordinates, ycoordinates, codesBuffer,
xCoordinatesBuffer, yCoordinatesBuffer, codesSize, bigramMap, bigramFilter,
useFullEditDistance, codes, codesSize, 0, codesBuffer, &masterCorrection,
- &queuePool, FRENCH_LIGATURES_DIGRAPHS,
- sizeof(FRENCH_LIGATURES_DIGRAPHS) / sizeof(FRENCH_LIGATURES_DIGRAPHS[0]));
+ &queuePool, FRENCH_LIGATURES_DIGRAPHS, NELEMS(FRENCH_LIGATURES_DIGRAPHS));
} else { // Normal processing
getWordSuggestions(proximityInfo, xcoordinates, ycoordinates, codes, codesSize,
bigramMap, bigramFilter, useFullEditDistance, &masterCorrection, &queuePool);
@@ -314,8 +310,6 @@ void UnigramDictionary::initSuggestions(ProximityInfo *proximityInfo, const int
correction->initCorrection(proximityInfo, inputSize, maxDepth);
}
-static const char SPACE = ' ';
-
void UnigramDictionary::getOneWordSuggestions(ProximityInfo *proximityInfo,
const int *xcoordinates, const int *ycoordinates, const int *codes,
const std::map<int, int> *bigramMap, const uint8_t *bigramFilter,
@@ -570,7 +564,7 @@ int UnigramDictionary::getSubStringSuggestion(
if (outputWordStartPos + nextWordLength >= MAX_WORD_LENGTH) {
return FLAG_MULTIPLE_SUGGEST_SKIP;
}
- outputWord[tempOutputWordLength] = SPACE;
+ outputWord[tempOutputWordLength] = KEYCODE_SPACE;
if (outputWordLength) {
++*outputWordLength;
}
diff --git a/native/jni/src/unigram_dictionary.h b/native/jni/src/unigram_dictionary.h
index 57129bb07..244d78d8c 100644
--- a/native/jni/src/unigram_dictionary.h
+++ b/native/jni/src/unigram_dictionary.h
@@ -39,8 +39,8 @@ class UnigramDictionary {
static const int FLAG_MULTIPLE_SUGGEST_ABORT = 0;
static const int FLAG_MULTIPLE_SUGGEST_SKIP = 1;
static const int FLAG_MULTIPLE_SUGGEST_CONTINUE = 2;
- UnigramDictionary(const uint8_t *const streamStart, int typedLetterMultipler,
- int fullWordMultiplier, int maxWordLength, int maxWords, const unsigned int flags);
+ UnigramDictionary(const uint8_t *const streamStart, int fullWordMultiplier, int maxWordLength,
+ int maxWords, const unsigned int flags);
int getFrequency(const int32_t *const inWord, const int length) const;
int getBigramPosition(int pos, unsigned short *word, int offset, int length) const;
int getSuggestions(ProximityInfo *proximityInfo, const int *xcoordinates,
@@ -115,7 +115,6 @@ class UnigramDictionary {
const uint8_t *const DICT_ROOT;
const int MAX_WORD_LENGTH;
const int MAX_WORDS;
- const int TYPED_LETTER_MULTIPLIER;
const int FULL_WORD_MULTIPLIER;
const int ROOT_POS;
const unsigned int BYTES_IN_ONE_CHAR;
diff --git a/tests/src/com/android/inputmethod/latin/BlueUnderlineTests.java b/tests/src/com/android/inputmethod/latin/BlueUnderlineTests.java
index a9947c1bd..2544bd87c 100644
--- a/tests/src/com/android/inputmethod/latin/BlueUnderlineTests.java
+++ b/tests/src/com/android/inputmethod/latin/BlueUnderlineTests.java
@@ -88,17 +88,19 @@ public class BlueUnderlineTests extends InputTestsBase {
public void testBlueUnderlineDisappearsWhenCursorMoved() {
final String STRING_TO_TYPE = "tgis";
+ final int typedLength = STRING_TO_TYPE.length();
final int NEW_CURSOR_POSITION = 0;
type(STRING_TO_TYPE);
sleep(DELAY_TO_WAIT_FOR_UNDERLINE);
// Simulate the onUpdateSelection() event
- mLatinIME.onUpdateSelection(0, 0, STRING_TO_TYPE.length(), STRING_TO_TYPE.length(), -1, -1);
+ mLatinIME.onUpdateSelection(0, 0, typedLength, typedLength, -1, -1);
runMessages();
// Here the blue underline has been set. testBlueUnderline() is testing for this already,
// so let's not test it here again.
// Now simulate the user moving the cursor.
mInputConnection.setSelection(NEW_CURSOR_POSITION, NEW_CURSOR_POSITION);
- mLatinIME.onUpdateSelection(0, 0, NEW_CURSOR_POSITION, NEW_CURSOR_POSITION, -1, -1);
+ mLatinIME.onUpdateSelection(typedLength, typedLength,
+ NEW_CURSOR_POSITION, NEW_CURSOR_POSITION, -1, -1);
sleep(DELAY_TO_WAIT_FOR_UNDERLINE);
runMessages();
final SpanGetter span = new SpanGetter(mTextView.getText(), SuggestionSpan.class);
diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTests.java b/tests/src/com/android/inputmethod/latin/InputLogicTests.java
index 38e57aaed..3b9eda212 100644
--- a/tests/src/com/android/inputmethod/latin/InputLogicTests.java
+++ b/tests/src/com/android/inputmethod/latin/InputLogicTests.java
@@ -86,6 +86,7 @@ public class InputLogicTests extends InputTestsBase {
public void testDeleteSelection() {
final String STRING_TO_TYPE = "some text delete me some text";
+ final int typedLength = STRING_TO_TYPE.length();
final int SELECTION_START = 10;
final int SELECTION_END = 19;
final String EXPECTED_RESULT = "some text some text";
@@ -94,10 +95,11 @@ public class InputLogicTests extends InputTestsBase {
// Send once to simulate the cursor actually responding to the move caused by typing.
// This is necessary because LatinIME is bookkeeping to avoid confusing a real cursor
// move with a move triggered by LatinIME inputting stuff.
- mLatinIME.onUpdateSelection(0, 0, STRING_TO_TYPE.length(), STRING_TO_TYPE.length(), -1, -1);
+ mLatinIME.onUpdateSelection(0, 0, typedLength, typedLength, -1, -1);
mInputConnection.setSelection(SELECTION_START, SELECTION_END);
// And now we simulate the user actually selecting some text.
- mLatinIME.onUpdateSelection(0, 0, SELECTION_START, SELECTION_END, -1, -1);
+ mLatinIME.onUpdateSelection(typedLength, typedLength,
+ SELECTION_START, SELECTION_END, -1, -1);
type(Keyboard.CODE_DELETE);
assertEquals("delete selection", EXPECTED_RESULT, mTextView.getText().toString());
}
@@ -163,12 +165,14 @@ public class InputLogicTests extends InputTestsBase {
public void testBackspaceAtStartAfterAutocorrect() {
final String STRING_TO_TYPE = "tgis ";
+ final int typedLength = STRING_TO_TYPE.length();
final String EXPECTED_RESULT = "this ";
final int NEW_CURSOR_POSITION = 0;
type(STRING_TO_TYPE);
- mLatinIME.onUpdateSelection(0, 0, STRING_TO_TYPE.length(), STRING_TO_TYPE.length(), -1, -1);
+ mLatinIME.onUpdateSelection(0, 0, typedLength, typedLength, -1, -1);
mInputConnection.setSelection(NEW_CURSOR_POSITION, NEW_CURSOR_POSITION);
- mLatinIME.onUpdateSelection(0, 0, NEW_CURSOR_POSITION, NEW_CURSOR_POSITION, -1, -1);
+ mLatinIME.onUpdateSelection(typedLength, typedLength,
+ NEW_CURSOR_POSITION, NEW_CURSOR_POSITION, -1, -1);
type(Keyboard.CODE_DELETE);
assertEquals("auto correct then move cursor to start of line then backspace",
EXPECTED_RESULT, mTextView.getText().toString());
@@ -176,12 +180,14 @@ public class InputLogicTests extends InputTestsBase {
public void testAutoCorrectThenMoveCursorThenBackspace() {
final String STRING_TO_TYPE = "and tgis ";
+ final int typedLength = STRING_TO_TYPE.length();
final String EXPECTED_RESULT = "andthis ";
final int NEW_CURSOR_POSITION = STRING_TO_TYPE.indexOf('t');
type(STRING_TO_TYPE);
- mLatinIME.onUpdateSelection(0, 0, STRING_TO_TYPE.length(), STRING_TO_TYPE.length(), -1, -1);
+ mLatinIME.onUpdateSelection(0, 0, typedLength, typedLength, -1, -1);
mInputConnection.setSelection(NEW_CURSOR_POSITION, NEW_CURSOR_POSITION);
- mLatinIME.onUpdateSelection(0, 0, NEW_CURSOR_POSITION, NEW_CURSOR_POSITION, -1, -1);
+ mLatinIME.onUpdateSelection(typedLength, typedLength,
+ NEW_CURSOR_POSITION, NEW_CURSOR_POSITION, -1, -1);
type(Keyboard.CODE_DELETE);
assertEquals("auto correct then move cursor then backspace",
EXPECTED_RESULT, mTextView.getText().toString());
diff --git a/tests/src/com/android/inputmethod/latin/InputLogicTestsNonEnglish.java b/tests/src/com/android/inputmethod/latin/InputLogicTestsNonEnglish.java
index 78143ac5b..42823f538 100644
--- a/tests/src/com/android/inputmethod/latin/InputLogicTestsNonEnglish.java
+++ b/tests/src/com/android/inputmethod/latin/InputLogicTestsNonEnglish.java
@@ -16,7 +16,10 @@
package com.android.inputmethod.latin;
+import com.android.inputmethod.latin.suggestions.SuggestionStripView;
+
public class InputLogicTestsNonEnglish extends InputTestsBase {
+ final String NEXT_WORD_PREDICTION_OPTION = "next_word_prediction";
public void testAutoCorrectForFrench() {
final String STRING_TO_TYPE = "irq ";
@@ -43,16 +46,40 @@ public class InputLogicTestsNonEnglish extends InputTestsBase {
final String WORD_TO_TYPE = "test ";
final String PUNCTUATION_FROM_STRIP = "!";
final String EXPECTED_RESULT = "test !!";
+ final boolean defaultNextWordPredictionOption =
+ mLatinIME.getResources().getBoolean(R.bool.config_default_next_word_prediction);
+ final boolean previousNextWordPredictionOption =
+ setBooleanPreference(NEXT_WORD_PREDICTION_OPTION, false,
+ defaultNextWordPredictionOption);
+ try {
+ changeLanguage("fr");
+ type(WORD_TO_TYPE);
+ sleep(DELAY_TO_WAIT_FOR_UNDERLINE);
+ runMessages();
+ assertTrue("type word then type space should display punctuation strip",
+ mLatinIME.isShowingPunctuationList());
+ pickSuggestionManually(0, PUNCTUATION_FROM_STRIP);
+ pickSuggestionManually(0, PUNCTUATION_FROM_STRIP);
+ assertEquals("type word then type space then punctuation from strip twice for French",
+ EXPECTED_RESULT, mTextView.getText().toString());
+ } finally {
+ setBooleanPreference(NEXT_WORD_PREDICTION_OPTION, previousNextWordPredictionOption,
+ defaultNextWordPredictionOption);
+ }
+ }
+
+ public void testWordThenSpaceDisplaysPredictions() {
+ final String WORD_TO_TYPE = "beaujolais ";
+ final String EXPECTED_RESULT = "nouveau";
changeLanguage("fr");
type(WORD_TO_TYPE);
sleep(DELAY_TO_WAIT_FOR_UNDERLINE);
runMessages();
- assertTrue("type word then type space should display punctuation strip",
- mLatinIME.isShowingPunctuationList());
- pickSuggestionManually(0, PUNCTUATION_FROM_STRIP);
- pickSuggestionManually(0, PUNCTUATION_FROM_STRIP);
- assertEquals("type word then type space then punctuation from strip twice for French",
- EXPECTED_RESULT, mTextView.getText().toString());
+ final SuggestionStripView suggestionStripView =
+ (SuggestionStripView)mInputView.findViewById(R.id.suggestion_strip_view);
+ final SuggestedWords suggestedWords = suggestionStripView.getSuggestions();
+ assertEquals("type word then type space yields predictions for French",
+ EXPECTED_RESULT, suggestedWords.getWord(0));
}
public void testAutoCorrectForGerman() {
diff --git a/tests/src/com/android/inputmethod/latin/InputTestsBase.java b/tests/src/com/android/inputmethod/latin/InputTestsBase.java
index fe58cb84e..70330509f 100644
--- a/tests/src/com/android/inputmethod/latin/InputTestsBase.java
+++ b/tests/src/com/android/inputmethod/latin/InputTestsBase.java
@@ -53,6 +53,7 @@ public class InputTestsBase extends ServiceTestCase<LatinIME> {
protected LatinIME mLatinIME;
protected Keyboard mKeyboard;
protected MyTextView mTextView;
+ protected View mInputView;
protected InputConnection mInputConnection;
private final HashMap<String, InputMethodSubtype> mSubtypeMap =
new HashMap<String, InputMethodSubtype>();
@@ -150,9 +151,9 @@ public class InputTestsBase extends ServiceTestCase<LatinIME> {
final LayoutInflater inflater =
(LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
final ViewGroup vg = new FrameLayout(getContext());
- final View inputView = inflater.inflate(R.layout.input_view, vg);
+ mInputView = inflater.inflate(R.layout.input_view, vg);
mLatinIME.onCreateInputMethodInterface().startInput(ic, ei);
- mLatinIME.setInputView(inputView);
+ mLatinIME.setInputView(mInputView);
mLatinIME.onBindInput();
mLatinIME.onCreateInputView();
mLatinIME.onStartInputView(ei, false);
@@ -211,7 +212,7 @@ public class InputTestsBase extends ServiceTestCase<LatinIME> {
// any subsequent post in this queue. However the queue itself is still fully functional!
// If we have a way of resetting "queue.mQuiting" then we can continue using it as normal,
// coming back to this method to run the messages.
- MessageQueue queue = looper.getQueue();
+ MessageQueue queue = Looper.myQueue();
try {
// However there is no way of doing it externally, and mQuiting is private.
// So... get out the big guns.
@@ -280,7 +281,7 @@ public class InputTestsBase extends ServiceTestCase<LatinIME> {
waitForDictionaryToBeLoaded();
}
- protected void pickSuggestionManually(final int index, final CharSequence suggestion) {
+ protected void pickSuggestionManually(final int index, final String suggestion) {
mLatinIME.pickSuggestionManually(index, suggestion);
}
diff --git a/tests/src/com/android/inputmethod/latin/UserHistoryDictIOUtilsTests.java b/tests/src/com/android/inputmethod/latin/UserHistoryDictIOUtilsTests.java
index 70f916c1a..66c9e1851 100644
--- a/tests/src/com/android/inputmethod/latin/UserHistoryDictIOUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/UserHistoryDictIOUtilsTests.java
@@ -188,7 +188,7 @@ public class UserHistoryDictIOUtilsTests extends AndroidTestCase
File file = null;
try {
- file = File.createTempFile("testReadAndWrite", ".dict");
+ file = File.createTempFile("testReadAndWrite", ".dict", getContext().getCacheDir());
} catch (IOException e) {
Log.d(TAG, "IOException while creating a temporary file: " + e);
}
diff --git a/tests/src/com/android/inputmethod/latin/UserHistoryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/UserHistoryDictionaryTests.java
index f2a17d206..7536f64ac 100644
--- a/tests/src/com/android/inputmethod/latin/UserHistoryDictionaryTests.java
+++ b/tests/src/com/android/inputmethod/latin/UserHistoryDictionaryTests.java
@@ -23,6 +23,8 @@ import android.preference.PreferenceManager;
import android.test.AndroidTestCase;
import android.util.Log;
+import java.io.File;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
@@ -76,34 +78,43 @@ public class UserHistoryDictionaryTests extends AndroidTestCase {
}
public void testRandomWords() {
- Log.d(TAG, "This test can be used for profiling.");
- Log.d(TAG, "Usage: please set UserHisotoryDictionary.PROFILE_SAVE_RESTORE to true.");
- final int numberOfWords = 1000;
- final Random random = new Random(123456);
- List<String> words = generateWords(numberOfWords, random);
-
- final String locale = "testRandomWords";
- final UserHistoryDictionary dict = UserHistoryDictionary.getInstance(getContext(),
- locale, mPrefs);
- dict.isTest = true;
-
- addToDict(dict, words);
-
- try {
- Log.d(TAG, "waiting for adding the word ...");
- Thread.sleep(2000);
- } catch (InterruptedException e) {
- Log.d(TAG, "InterruptedException: " + e);
- }
-
- // write to file
- dict.close();
-
+ File dictFile = null;
try {
- Log.d(TAG, "waiting for writing ...");
- Thread.sleep(5000);
- } catch (InterruptedException e) {
- Log.d(TAG, "InterruptedException: " + e);
+ Log.d(TAG, "This test can be used for profiling.");
+ Log.d(TAG, "Usage: please set UserHisotoryDictionary.PROFILE_SAVE_RESTORE to true.");
+ final int numberOfWords = 1000;
+ final Random random = new Random(123456);
+ List<String> words = generateWords(numberOfWords, random);
+
+ final String locale = "testRandomWords";
+ final String fileName = "UserHistoryDictionary." + locale + ".dict";
+ dictFile = new File(getContext().getFilesDir(), fileName);
+ final UserHistoryDictionary dict = UserHistoryDictionary.getInstance(getContext(),
+ locale, mPrefs);
+ dict.isTest = true;
+
+ addToDict(dict, words);
+
+ try {
+ Log.d(TAG, "waiting for adding the word ...");
+ Thread.sleep(2000);
+ } catch (InterruptedException e) {
+ Log.d(TAG, "InterruptedException: " + e);
+ }
+
+ // write to file
+ dict.close();
+
+ try {
+ Log.d(TAG, "waiting for writing ...");
+ Thread.sleep(5000);
+ } catch (InterruptedException e) {
+ Log.d(TAG, "InterruptedException: " + e);
+ }
+ } finally {
+ if (dictFile != null) {
+ dictFile.delete();
+ }
}
}
}
diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOTests.java
index 2f954318c..b6f6fea4d 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOTests.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOTests.java
@@ -271,7 +271,7 @@ public class BinaryDictIOTests extends AndroidTestCase {
final String message) {
File file = null;
try {
- file = File.createTempFile("runReadAndWrite", ".dict");
+ file = File.createTempFile("runReadAndWrite", ".dict", getContext().getCacheDir());
} catch (IOException e) {
Log.e(TAG, "IOException: " + e);
}
@@ -412,7 +412,7 @@ public class BinaryDictIOTests extends AndroidTestCase {
final FormatSpec.FormatOptions formatOptions, final String message) {
File file = null;
try {
- file = File.createTempFile("runReadUnigrams", ".dict");
+ file = File.createTempFile("runReadUnigrams", ".dict", getContext().getCacheDir());
} catch (IOException e) {
Log.e(TAG, "IOException: " + e);
}
@@ -510,7 +510,8 @@ public class BinaryDictIOTests extends AndroidTestCase {
public void testGetTerminalPosition() {
File file = null;
try {
- file = File.createTempFile("testGetTerminalPosition", ".dict");
+ file = File.createTempFile("testGetTerminalPosition", ".dict",
+ getContext().getCacheDir());
} catch (IOException e) {
// do nothing
}
@@ -561,7 +562,7 @@ public class BinaryDictIOTests extends AndroidTestCase {
public void testDeleteWord() {
File file = null;
try {
- file = File.createTempFile("testDeleteWord", ".dict");
+ file = File.createTempFile("testDeleteWord", ".dict", getContext().getCacheDir());
} catch (IOException e) {
// do nothing
}
diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java
new file mode 100644
index 000000000..318516845
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java
@@ -0,0 +1,397 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.makedict;
+
+import com.android.inputmethod.latin.CollectionUtils;
+import com.android.inputmethod.latin.makedict.BinaryDictInputOutput.ByteBufferWrapper;
+import com.android.inputmethod.latin.makedict.BinaryDictInputOutput.CharEncoding;
+import com.android.inputmethod.latin.makedict.BinaryDictInputOutput.FusionDictionaryBufferInterface;
+import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
+import com.android.inputmethod.latin.makedict.FusionDictionary.Node;
+import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
+
+import android.test.AndroidTestCase;
+import android.test.MoreAsserts;
+import android.util.Log;
+
+import java.io.BufferedOutputStream;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.channels.FileChannel;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Random;
+
+public class BinaryDictIOUtilsTests extends AndroidTestCase {
+ private static final String TAG = BinaryDictIOUtilsTests.class.getSimpleName();
+ private static final FormatSpec.FormatOptions FORMAT_OPTIONS =
+ new FormatSpec.FormatOptions(3, true);
+ private static final int MAX_UNIGRAMS = 1500;
+
+ private static final ArrayList<String> sWords = CollectionUtils.newArrayList();
+
+ private static final String[] CHARACTERS = {
+ "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
+ "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
+ "\u00FC" /* ü */, "\u00E2" /* â */, "\u00F1" /* ñ */, // accented characters
+ "\u4E9C" /* 亜 */, "\u4F0A" /* 伊 */, "\u5B87" /* 宇 */, // kanji
+ "\uD841\uDE28" /* 𠘨 */, "\uD840\uDC0B" /* 𠀋 */, "\uD861\uDeD7" /* 𨛗 */ // surrogate pair
+ };
+
+ public BinaryDictIOUtilsTests() {
+ super();
+ final Random random = new Random(123456);
+ sWords.clear();
+ for (int i = 0; i < MAX_UNIGRAMS; ++i) {
+ sWords.add(generateWord(random.nextInt()));
+ }
+ }
+
+ // Utilities for test
+ private String generateWord(final int value) {
+ final int lengthOfChars = CHARACTERS.length;
+ StringBuilder builder = new StringBuilder("");
+ long lvalue = Math.abs((long)value);
+ while (lvalue > 0) {
+ builder.append(CHARACTERS[(int)(lvalue % lengthOfChars)]);
+ lvalue /= lengthOfChars;
+ }
+ if (builder.toString().equals("")) return "a";
+ return builder.toString();
+ }
+
+ private static void printCharGroup(final CharGroupInfo info) {
+ Log.d(TAG, " CharGroup at " + info.mOriginalAddress);
+ Log.d(TAG, " flags = " + info.mFlags);
+ Log.d(TAG, " parentAddress = " + info.mParentAddress);
+ Log.d(TAG, " characters = " + new String(info.mCharacters, 0,
+ info.mCharacters.length));
+ if (info.mFrequency != -1) Log.d(TAG, " frequency = " + info.mFrequency);
+ if (info.mChildrenAddress == FormatSpec.NO_CHILDREN_ADDRESS) {
+ Log.d(TAG, " children address = no children address");
+ } else {
+ Log.d(TAG, " children address = " + info.mChildrenAddress);
+ }
+ if (info.mShortcutTargets != null) {
+ for (final WeightedString ws : info.mShortcutTargets) {
+ Log.d(TAG, " shortcuts = " + ws.mWord);
+ }
+ }
+ if (info.mBigrams != null) {
+ for (final PendingAttribute attr : info.mBigrams) {
+ Log.d(TAG, " bigram = " + attr.mAddress);
+ }
+ }
+ Log.d(TAG, " end address = " + info.mEndAddress);
+ }
+
+ private static void printNode(final FusionDictionaryBufferInterface buffer,
+ final FormatSpec.FormatOptions formatOptions) {
+ Log.d(TAG, "Node at " + buffer.position());
+ final int count = BinaryDictInputOutput.readCharGroupCount(buffer);
+ Log.d(TAG, " charGroupCount = " + count);
+ for (int i = 0; i < count; ++i) {
+ final CharGroupInfo currentInfo = BinaryDictInputOutput.readCharGroup(buffer,
+ buffer.position(), formatOptions);
+ printCharGroup(currentInfo);
+ }
+ if (formatOptions.mSupportsDynamicUpdate) {
+ final int forwardLinkAddress = buffer.readUnsignedInt24();
+ Log.d(TAG, " forwardLinkAddress = " + forwardLinkAddress);
+ }
+ }
+
+ private static void printBinaryFile(final FusionDictionaryBufferInterface buffer)
+ throws IOException, UnsupportedFormatException {
+ FileHeader header = BinaryDictInputOutput.readHeader(buffer);
+ while (buffer.position() < buffer.limit()) {
+ printNode(buffer, header.mFormatOptions);
+ }
+ }
+
+ private int getWordPosition(final File file, final String word) {
+ int position = FormatSpec.NOT_VALID_WORD;
+ FileInputStream inStream = null;
+ try {
+ inStream = new FileInputStream(file);
+ final FusionDictionaryBufferInterface buffer = new ByteBufferWrapper(
+ inStream.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, file.length()));
+ position = BinaryDictIOUtils.getTerminalPosition(buffer, word);
+ } catch (IOException e) {
+ } catch (UnsupportedFormatException e) {
+ } finally {
+ if (inStream != null) {
+ try {
+ inStream.close();
+ } catch (IOException e) {
+ // do nothing
+ }
+ }
+ }
+ return position;
+ }
+
+ private CharGroupInfo findWordFromFile(final File file, final String word) {
+ FileInputStream inStream = null;
+ CharGroupInfo info = null;
+ try {
+ inStream = new FileInputStream(file);
+ final FusionDictionaryBufferInterface buffer = new ByteBufferWrapper(
+ inStream.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, file.length()));
+ info = BinaryDictIOUtils.findWordFromBuffer(buffer, word);
+ } catch (IOException e) {
+ } catch (UnsupportedFormatException e) {
+ } finally {
+ if (inStream != null) {
+ try {
+ inStream.close();
+ } catch (IOException e) {
+ // do nothing
+ }
+ }
+ }
+ return info;
+ }
+
+ // return amount of time to insert a word
+ private long insertAndCheckWord(final File file, final String word, final int frequency,
+ final boolean exist, final ArrayList<WeightedString> bigrams,
+ final ArrayList<WeightedString> shortcuts) {
+ RandomAccessFile raFile = null;
+ BufferedOutputStream outStream = null;
+ FusionDictionaryBufferInterface buffer = null;
+ long amountOfTime = -1;
+ try {
+ raFile = new RandomAccessFile(file, "rw");
+ buffer = new ByteBufferWrapper(raFile.getChannel().map(
+ FileChannel.MapMode.READ_WRITE, 0, file.length()));
+ outStream = new BufferedOutputStream(new FileOutputStream(file, true));
+
+ if (!exist) {
+ assertEquals(FormatSpec.NOT_VALID_WORD, getWordPosition(file, word));
+ }
+ final long now = System.nanoTime();
+ BinaryDictIOUtils.insertWord(buffer, outStream, word, frequency, bigrams, shortcuts,
+ false, false);
+ amountOfTime = System.nanoTime() - now;
+ outStream.flush();
+ MoreAsserts.assertNotEqual(FormatSpec.NOT_VALID_WORD, getWordPosition(file, word));
+ outStream.close();
+ raFile.close();
+ } catch (IOException e) {
+ } catch (UnsupportedFormatException e) {
+ } finally {
+ if (outStream != null) {
+ try {
+ outStream.close();
+ } catch (IOException e) {
+ // do nothing
+ }
+ }
+ if (raFile != null) {
+ try {
+ raFile.close();
+ } catch (IOException e) {
+ // do nothing
+ }
+ }
+ }
+ return amountOfTime;
+ }
+
+ private void deleteWord(final File file, final String word) {
+ RandomAccessFile raFile = null;
+ FusionDictionaryBufferInterface buffer = null;
+ try {
+ raFile = new RandomAccessFile(file, "rw");
+ buffer = new ByteBufferWrapper(raFile.getChannel().map(
+ FileChannel.MapMode.READ_WRITE, 0, file.length()));
+ BinaryDictIOUtils.deleteWord(buffer, word);
+ } catch (IOException e) {
+ } catch (UnsupportedFormatException e) {
+ } finally {
+ if (raFile != null) {
+ try {
+ raFile.close();
+ } catch (IOException e) {
+ // do nothing
+ }
+ }
+ }
+ }
+
+ private void checkReverseLookup(final File file, final String word, final int position) {
+ FileInputStream inStream = null;
+ try {
+ inStream = new FileInputStream(file);
+ final FusionDictionaryBufferInterface buffer = new ByteBufferWrapper(
+ inStream.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, file.length()));
+ final FileHeader header = BinaryDictInputOutput.readHeader(buffer);
+ assertEquals(word, BinaryDictInputOutput.getWordAtAddress(buffer, header.mHeaderSize,
+ position - header.mHeaderSize, header.mFormatOptions));
+ } catch (IOException e) {
+ } catch (UnsupportedFormatException e) {
+ } finally {
+ if (inStream != null) {
+ try {
+ inStream.close();
+ } catch (IOException e) {
+ // do nothing
+ }
+ }
+ }
+ }
+
+ public void testInsertWord() {
+ File file = null;
+ try {
+ file = File.createTempFile("testInsertWord", ".dict", getContext().getCacheDir());
+ } catch (IOException e) {
+ fail("IOException while creating temporary file: " + e);
+ }
+
+ // set an initial dictionary.
+ final FusionDictionary dict = new FusionDictionary(new Node(),
+ new FusionDictionary.DictionaryOptions(new HashMap<String,String>(), false, false));
+ dict.add("abcd", 10, null, false);
+
+ try {
+ final FileOutputStream out = new FileOutputStream(file);
+ BinaryDictInputOutput.writeDictionaryBinary(out, dict, FORMAT_OPTIONS);
+ out.close();
+ } catch (IOException e) {
+ fail("IOException while writing an initial dictionary : " + e);
+ } catch (UnsupportedFormatException e) {
+ fail("UnsupportedFormatException while writing an initial dictionary : " + e);
+ }
+
+ MoreAsserts.assertNotEqual(FormatSpec.NOT_VALID_WORD, getWordPosition(file, "abcd"));
+ insertAndCheckWord(file, "abcde", 10, false, null, null);
+
+ insertAndCheckWord(file, "abcdefghijklmn", 10, false, null, null);
+ checkReverseLookup(file, "abcdefghijklmn", getWordPosition(file, "abcdefghijklmn"));
+
+ insertAndCheckWord(file, "abcdabcd", 10, false, null, null);
+ checkReverseLookup(file, "abcdabcd", getWordPosition(file, "abcdabcd"));
+
+ // update the existing word.
+ insertAndCheckWord(file, "abcdabcd", 15, true, null, null);
+
+ // split 1
+ insertAndCheckWord(file, "ab", 20, false, null, null);
+
+ // split 2
+ insertAndCheckWord(file, "ami", 30, false, null, null);
+
+ deleteWord(file, "ami");
+ assertEquals(FormatSpec.NOT_VALID_WORD, getWordPosition(file, "ami"));
+
+ insertAndCheckWord(file, "abcdabfg", 30, false, null, null);
+
+ deleteWord(file, "abcd");
+ assertEquals(FormatSpec.NOT_VALID_WORD, getWordPosition(file, "abcd"));
+ }
+
+ public void testInsertWordWithBigrams() {
+ File file = null;
+ try {
+ file = File.createTempFile("testInsertWordWithBigrams", ".dict",
+ getContext().getCacheDir());
+ } catch (IOException e) {
+ fail("IOException while creating temporary file: " + e);
+ }
+
+ // set an initial dictionary.
+ final FusionDictionary dict = new FusionDictionary(new Node(),
+ new FusionDictionary.DictionaryOptions(new HashMap<String,String>(), false, false));
+ dict.add("abcd", 10, null, false);
+ dict.add("efgh", 15, null, false);
+
+ try {
+ final FileOutputStream out = new FileOutputStream(file);
+ BinaryDictInputOutput.writeDictionaryBinary(out, dict, FORMAT_OPTIONS);
+ out.close();
+ } catch (IOException e) {
+ fail("IOException while writing an initial dictionary : " + e);
+ } catch (UnsupportedFormatException e) {
+ fail("UnsupportedFormatException while writing an initial dictionary : " + e);
+ }
+
+ final ArrayList<WeightedString> banana = new ArrayList<WeightedString>();
+ banana.add(new WeightedString("banana", 10));
+
+ insertAndCheckWord(file, "banana", 0, false, null, null);
+ insertAndCheckWord(file, "recursive", 60, true, banana, null);
+
+ final CharGroupInfo info = findWordFromFile(file, "recursive");
+ int bananaPos = getWordPosition(file, "banana");
+ assertNotNull(info.mBigrams);
+ assertEquals(info.mBigrams.size(), 1);
+ assertEquals(info.mBigrams.get(0).mAddress, bananaPos);
+ }
+
+ public void testRandomWords() {
+ File file = null;
+ try {
+ file = File.createTempFile("testRandomWord", ".dict", getContext().getCacheDir());
+ } catch (IOException e) {
+ }
+ assertNotNull(file);
+
+ // set an initial dictionary.
+ final FusionDictionary dict = new FusionDictionary(new Node(),
+ new FusionDictionary.DictionaryOptions(new HashMap<String, String>(), false,
+ false));
+ dict.add("initial", 10, null, false);
+
+ try {
+ final FileOutputStream out = new FileOutputStream(file);
+ BinaryDictInputOutput.writeDictionaryBinary(out, dict, FORMAT_OPTIONS);
+ out.close();
+ } catch (IOException e) {
+ assertTrue(false);
+ } catch (UnsupportedFormatException e) {
+ assertTrue(false);
+ }
+
+ long maxTimeToInsert = 0, sum = 0;
+ long minTimeToInsert = 100000000; // 1000000000 is an upper bound for minTimeToInsert.
+ int cnt = 0;
+ for (final String word : sWords) {
+ final long diff = insertAndCheckWord(file, word,
+ cnt % FormatSpec.MAX_TERMINAL_FREQUENCY, false, null, null);
+ maxTimeToInsert = Math.max(maxTimeToInsert, diff);
+ minTimeToInsert = Math.min(minTimeToInsert, diff);
+ sum += diff;
+ cnt++;
+ }
+ cnt = 0;
+ for (final String word : sWords) {
+ MoreAsserts.assertNotEqual(FormatSpec.NOT_VALID_WORD, getWordPosition(file, word));
+ }
+
+ Log.d(TAG, "max = " + ((double)maxTimeToInsert/1000000) + " ms.");
+ Log.d(TAG, "min = " + ((double)minTimeToInsert/1000000) + " ms.");
+ Log.d(TAG, "avg = " + ((double)sum/MAX_UNIGRAMS/1000000) + " ms.");
+ }
+}
diff --git a/tools/dicttool/Android.mk b/tools/dicttool/Android.mk
index 5bd836a01..159c1c160 100644
--- a/tools/dicttool/Android.mk
+++ b/tools/dicttool/Android.mk
@@ -16,14 +16,18 @@
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
-LATINIME_CORE_SOURCE_DIRECTORY := ../../java/src/com/android/inputmethod/latin
+LATINIME_BASE_SOURCE_DIRECTORY := ../../java/src/com/android/inputmethod
+LATINIME_CORE_SOURCE_DIRECTORY := $(LATINIME_BASE_SOURCE_DIRECTORY)/latin
+LATINIME_ANNOTATIONS_SOURCE_DIRECTORY := $(LATINIME_BASE_SOURCE_DIRECTORY)/annotations
MAKEDICT_CORE_SOURCE_DIRECTORY := $(LATINIME_CORE_SOURCE_DIRECTORY)/makedict
LOCAL_MAIN_SRC_FILES := $(call all-java-files-under,$(MAKEDICT_CORE_SOURCE_DIRECTORY))
LOCAL_TOOL_SRC_FILES := $(call all-java-files-under,src)
+LOCAL_ANNOTATIONS_SRC_FILES := $(call all-java-files-under,$(LATINIME_ANNOTATIONS_SOURCE_DIRECTORY))
LOCAL_SRC_FILES := $(LOCAL_TOOL_SRC_FILES) \
$(filter-out $(addprefix %/, $(notdir $(LOCAL_TOOL_SRC_FILES))), $(LOCAL_MAIN_SRC_FILES)) \
$(call all-java-files-under,tests) \
+ $(LOCAL_ANNOTATIONS_SRC_FILES) \
$(LATINIME_CORE_SOURCE_DIRECTORY)/Constants.java
LOCAL_JAR_MANIFEST := etc/manifest.txt
diff --git a/tools/dicttool/src/android/inputmethod/latin/dicttool/AdditionalCommandList.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/AdditionalCommandList.java
index 8d4eb751b..8d4eb751b 100644
--- a/tools/dicttool/src/android/inputmethod/latin/dicttool/AdditionalCommandList.java
+++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/AdditionalCommandList.java
diff --git a/tools/dicttool/src/android/inputmethod/latin/dicttool/CommandList.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/CommandList.java
index d16b069fe..d16b069fe 100644
--- a/tools/dicttool/src/android/inputmethod/latin/dicttool/CommandList.java
+++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/CommandList.java
diff --git a/tools/dicttool/src/android/inputmethod/latin/dicttool/Compress.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Compress.java
index 3cb0a12c4..3cb0a12c4 100644
--- a/tools/dicttool/src/android/inputmethod/latin/dicttool/Compress.java
+++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Compress.java
diff --git a/tools/dicttool/src/android/inputmethod/latin/dicttool/DictionaryMaker.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/DictionaryMaker.java
index 4f8874985..2cdd83e96 100644
--- a/tools/dicttool/src/android/inputmethod/latin/dicttool/DictionaryMaker.java
+++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/DictionaryMaker.java
@@ -22,6 +22,7 @@ import com.android.inputmethod.latin.makedict.FusionDictionary;
import com.android.inputmethod.latin.makedict.MakedictLog;
import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
+import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@@ -328,6 +329,7 @@ public class DictionaryMaker {
*/
private static void writeXmlDictionary(final String outputFilename,
final FusionDictionary dict) throws FileNotFoundException, IOException {
- XmlDictInputOutput.writeDictionaryXml(new FileWriter(outputFilename), dict);
+ XmlDictInputOutput.writeDictionaryXml(new BufferedWriter(new FileWriter(outputFilename)),
+ dict);
}
}
diff --git a/tools/dicttool/src/android/inputmethod/latin/dicttool/Dicttool.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Dicttool.java
index bf417fb5a..bf417fb5a 100644
--- a/tools/dicttool/src/android/inputmethod/latin/dicttool/Dicttool.java
+++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Dicttool.java
diff --git a/tools/dicttool/src/android/inputmethod/latin/dicttool/Info.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Info.java
index e59261706..e59261706 100644
--- a/tools/dicttool/src/android/inputmethod/latin/dicttool/Info.java
+++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Info.java
diff --git a/tools/dicttool/src/android/inputmethod/latin/dicttool/Makedict.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Makedict.java
index c004cfbe4..c004cfbe4 100644
--- a/tools/dicttool/src/android/inputmethod/latin/dicttool/Makedict.java
+++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Makedict.java
diff --git a/tools/dicttool/src/android/inputmethod/latin/dicttool/XmlDictInputOutput.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/XmlDictInputOutput.java
index 252c3d655..252c3d655 100644
--- a/tools/dicttool/src/android/inputmethod/latin/dicttool/XmlDictInputOutput.java
+++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/XmlDictInputOutput.java
diff --git a/tools/dicttool/src/android/inputmethod/latin/dicttool/MakedictLog.java b/tools/dicttool/src/com/android/inputmethod/latin/makedict/MakedictLog.java
index 7eccff2b4..7eccff2b4 100644
--- a/tools/dicttool/src/android/inputmethod/latin/dicttool/MakedictLog.java
+++ b/tools/dicttool/src/com/android/inputmethod/latin/makedict/MakedictLog.java
diff --git a/tools/dicttool/tests/com/android/inputmethod/latin/makedict/BinaryDictInputOutputTest.java b/tools/dicttool/tests/com/android/inputmethod/latin/makedict/BinaryDictInputOutputTest.java
index 88589b815..096902879 100644
--- a/tools/dicttool/tests/com/android/inputmethod/latin/makedict/BinaryDictInputOutputTest.java
+++ b/tools/dicttool/tests/com/android/inputmethod/latin/makedict/BinaryDictInputOutputTest.java
@@ -19,24 +19,15 @@ package com.android.inputmethod.latin.makedict;
import com.android.inputmethod.latin.makedict.FusionDictionary.DictionaryOptions;
import com.android.inputmethod.latin.makedict.FusionDictionary.Node;
+import junit.framework.TestCase;
+
import java.util.ArrayList;
import java.util.HashMap;
-import junit.framework.TestCase;
-
/**
* Unit tests for BinaryDictInputOutput.
*/
public class BinaryDictInputOutputTest extends TestCase {
-
- public void setUp() throws Exception {
- super.setUp();
- }
-
- public void tearDown() throws Exception {
- super.tearDown();
- }
-
// Test the flattened array contains the expected number of nodes, and
// that it does not contain any duplicates.
public void testFlattenNodes() {
@@ -55,5 +46,4 @@ public class BinaryDictInputOutputTest extends TestCase {
assertFalse("Flattened array contained the same node twice", result.contains(n));
}
}
-
}
diff --git a/tools/dicttool/tests/etc/test-dicttool.sh b/tools/dicttool/tests/etc/test-dicttool.sh
index 8834611cd..1283be21a 100755
--- a/tools/dicttool/tests/etc/test-dicttool.sh
+++ b/tools/dicttool/tests/etc/test-dicttool.sh
@@ -13,4 +13,4 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-java -classpath ${ANDROID_HOST_OUT}/framework/junit.jar:${ANDROID_HOST_OUT}/../common/obj/JAVA_LIBRARIES/dicttool_intermediates/classes junit.textui.TestRunner com.android.inputmethod.latin.makedict.BinaryDictInputOutputTest
+java -classpath ${ANDROID_HOST_OUT}/framework/junit.jar:${ANDROID_HOST_OUT}/framework/dicttool_aosp.jar junit.textui.TestRunner com.android.inputmethod.latin.makedict.BinaryDictInputOutputTest
diff --git a/tools/maketext/res/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.tmpl b/tools/maketext/res/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.tmpl
index 774094cd7..15aea9084 100644
--- a/tools/maketext/res/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.tmpl
+++ b/tools/maketext/res/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.tmpl
@@ -19,6 +19,7 @@ package com.android.inputmethod.keyboard.internal;
import android.content.Context;
import android.content.res.Resources;
+import com.android.inputmethod.annotations.VisibleForTesting;
import com.android.inputmethod.latin.CollectionUtils;
import com.android.inputmethod.latin.R;
@@ -64,7 +65,7 @@ public final class KeyboardTextsSet {
loadStringResourcesInternal(context, RESOURCE_NAMES, R.string.english_ime_name);
}
- /* package for test */
+ @VisibleForTesting
void loadStringResourcesInternal(Context context, final String[] resourceNames,
int referenceId) {
final Resources res = context.getResources();