aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--java/AndroidManifest.xml6
-rw-r--r--java/res/color/key_text_color_ics.xml48
-rw-r--r--java/res/values-af/strings.xml6
-rw-r--r--java/res/values-am/strings.xml6
-rw-r--r--java/res/values-ar/strings.xml6
-rw-r--r--java/res/values-bg/strings.xml6
-rw-r--r--java/res/values-ca/strings.xml6
-rw-r--r--java/res/values-cs/strings.xml6
-rw-r--r--java/res/values-da/strings.xml6
-rw-r--r--java/res/values-de/strings.xml6
-rw-r--r--java/res/values-el/strings.xml6
-rw-r--r--java/res/values-en-rGB/strings.xml6
-rw-r--r--java/res/values-en-rIN/strings.xml6
-rw-r--r--java/res/values-es-rUS/strings.xml6
-rw-r--r--java/res/values-es/strings.xml6
-rw-r--r--java/res/values-et-rEE/strings.xml6
-rw-r--r--java/res/values-fa/strings.xml6
-rw-r--r--java/res/values-fi/strings.xml6
-rw-r--r--java/res/values-fr-rCA/strings.xml6
-rw-r--r--java/res/values-fr/strings.xml6
-rw-r--r--java/res/values-hi/strings.xml6
-rw-r--r--java/res/values-hr/strings.xml6
-rw-r--r--java/res/values-hu/strings.xml6
-rw-r--r--java/res/values-hy-rAM/strings.xml6
-rw-r--r--java/res/values-in/strings.xml6
-rw-r--r--java/res/values-it/strings.xml6
-rw-r--r--java/res/values-iw/strings.xml6
-rw-r--r--java/res/values-ja/strings.xml6
-rw-r--r--java/res/values-ka-rGE/strings.xml6
-rw-r--r--java/res/values-km-rKH/strings.xml6
-rw-r--r--java/res/values-ko/strings.xml6
-rw-r--r--java/res/values-lo-rLA/strings.xml6
-rw-r--r--java/res/values-lt/strings.xml6
-rw-r--r--java/res/values-lv/strings.xml6
-rw-r--r--java/res/values-mn-rMN/strings.xml6
-rw-r--r--java/res/values-ms-rMY/strings.xml6
-rw-r--r--java/res/values-nb/strings.xml6
-rw-r--r--java/res/values-nl/strings.xml6
-rw-r--r--java/res/values-pl/strings.xml6
-rw-r--r--java/res/values-pt-rPT/strings.xml6
-rw-r--r--java/res/values-pt/strings.xml6
-rw-r--r--java/res/values-ro/strings.xml6
-rw-r--r--java/res/values-ru/strings.xml6
-rw-r--r--java/res/values-sk/strings.xml6
-rw-r--r--java/res/values-sl/strings.xml6
-rw-r--r--java/res/values-sr/strings.xml6
-rw-r--r--java/res/values-sv/strings.xml6
-rw-r--r--java/res/values-sw/strings.xml6
-rw-r--r--java/res/values-th/strings.xml6
-rw-r--r--java/res/values-tl/strings.xml6
-rw-r--r--java/res/values-tr/strings.xml6
-rw-r--r--java/res/values-uk/strings.xml6
-rw-r--r--java/res/values-vi/strings.xml6
-rw-r--r--java/res/values-zh-rCN/strings.xml6
-rw-r--r--java/res/values-zh-rHK/strings.xml6
-rw-r--r--java/res/values-zh-rTW/strings.xml6
-rw-r--r--java/res/values-zu/strings.xml6
-rw-r--r--java/res/values/colors.xml4
-rw-r--r--java/res/values/strings.xml6
-rw-r--r--java/res/xml-sw600dp/rows_symbols.xml2
-rw-r--r--java/res/xml/key_styles_common.xml21
-rw-r--r--java/res/xml/row_symbols4.xml4
-rw-r--r--java/res/xml/rows_symbols.xml3
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/DictionarySettingsActivity.java10
-rw-r--r--java/src/com/android/inputmethod/keyboard/EmojiLayoutParams.java4
-rw-r--r--java/src/com/android/inputmethod/keyboard/Key.java19
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java2
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyDrawParams.java9
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyVisualAttributes.java5
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java2
-rw-r--r--java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java34
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java4
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java1
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java8
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java36
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/DictUpdater.java50
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/DynamicBinaryDictIOUtils.java107
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/FormatSpec.java7
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/SparseTable.java131
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/Ver3DictDecoder.java7
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/Ver3DictEncoder.java8
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/Ver3DictUpdater.java82
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java26
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java154
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java5
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/DictionaryDecayBroadcastReciever.java66
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java13
-rw-r--r--java/src/com/android/inputmethod/latin/settings/DebugSettingsActivity.java6
-rw-r--r--java/src/com/android/inputmethod/latin/settings/SettingsActivity.java6
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsActivity.java10
-rw-r--r--java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java29
-rw-r--r--native/jni/Android.mk2
-rw-r--r--native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp1
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/bigram/dynamic_bigram_list_policy.cpp14
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_gc_event_listeners.cpp6
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.cpp8
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.cpp9
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.cpp3
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/utils/decaying_utils.cpp129
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.cpp123
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h (renamed from native/jni/src/suggest/policyimpl/dictionary/utils/decaying_utils.h)57
-rw-r--r--tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java12
-rw-r--r--tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java55
-rw-r--r--tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java25
-rw-r--r--tests/src/com/android/inputmethod/latin/makedict/SparseTableTests.java107
-rw-r--r--tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java92
106 files changed, 1116 insertions, 792 deletions
diff --git a/java/AndroidManifest.xml b/java/AndroidManifest.xml
index b54406f4b..031d62e0c 100644
--- a/java/AndroidManifest.xml
+++ b/java/AndroidManifest.xml
@@ -110,6 +110,12 @@
</intent-filter>
</receiver>
+ <receiver android:name=".personalization.DictionaryDecayBroadcastReciever">
+ <intent-filter>
+ <action android:name="com.android.inputmethod.latin.personalization.DICT_DECAY" />
+ </intent-filter>
+ </receiver>
+
<receiver android:name=".DictionaryPackInstallBroadcastReceiver">
<intent-filter>
<action android:name="com.android.inputmethod.dictionarypack.aosp.UNKNOWN_CLIENT" />
diff --git a/java/res/color/key_text_color_ics.xml b/java/res/color/key_text_color_ics.xml
new file mode 100644
index 000000000..c6f111ad2
--- /dev/null
+++ b/java/res/color/key_text_color_ics.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <!-- Functional keys. -->
+ <item android:state_single="true" android:state_pressed="true"
+ android:color="@color/key_text_color_functional_ics" />
+ <item android:state_single="true"
+ android:color="@color/key_text_color_functional_ics" />
+
+ <!-- Action keys. -->
+ <item android:state_active="true" android:state_pressed="true"
+ android:color="@color/key_text_color_normal_ics" />
+ <item android:state_active="true"
+ android:color="@color/key_text_color_normal_ics" />
+
+ <!-- Toggle keys. Use checkable/checked state. -->
+ <item android:state_checkable="true" android:state_checked="true" android:state_pressed="true"
+ android:color="@color/key_text_color_normal_ics" />
+ <item android:state_checkable="true" android:state_pressed="true"
+ android:color="@color/key_text_color_normal_ics" />
+ <item android:state_checkable="true" android:state_checked="true"
+ android:color="@color/key_text_color_normal_ics" />
+ <item android:state_checkable="true"
+ android:color="@color/key_text_color_normal_ics" />
+
+ <!-- Empty background keys. -->
+ <item android:state_empty="true"
+ android:color="@color/key_text_color_normal_ics" />
+
+ <!-- Normal keys. -->
+ <item android:state_pressed="true"
+ android:color="@color/key_text_color_normal_ics" />
+ <item android:color="@color/key_text_color_normal_ics" />
+</selector>
diff --git a/java/res/values-af/strings.xml b/java/res/values-af/strings.xml
index a3c95b290..f187a73a5 100644
--- a/java/res/values-af/strings.xml
+++ b/java/res/values-af/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"Koppel \'n kopstuk om te hoor hoe wagwoordsleutels hardop gesĂȘ word."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"Huidige teks is %s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"Geen teks ingevoer nie"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> korrigeer <xliff:g id="ORIGINAL">%2$s</xliff:g> na <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> het outokorreksie"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"Sleutelkode %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift aan (tik om te deaktiveer)"</string>
diff --git a/java/res/values-am/strings.xml b/java/res/values-am/strings.xml
index 89e37b274..6504e64c7 100644
--- a/java/res/values-am/strings.xml
+++ b/java/res/values-am/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"ዹይለፍቃል ቁልፎቜ ጼክ በለው áˆČነገሩ ለመሔማቔ ዹጆሼ áˆ›á‹łáˆ˜áŒ« ሰካ::"</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"ዹአሁኑ ፅሁፍ %s ነው"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"ምንም ፅሁፍ አልገባም"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> <xliff:g id="ORIGINAL">%2$s</xliff:g>ን ወደ <xliff:g id="CORRECTED">%3$s</xliff:g> ያርመዋል"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> ራሔ-አርም አለው"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"ዹቁልፍ ኟዔ%d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"ቀይር"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"ቅያር በርቷል (ለማሰናክል ንካ)"</string>
diff --git a/java/res/values-ar/strings.xml b/java/res/values-ar/strings.xml
index 9c3ff51c8..46bff1294 100644
--- a/java/res/values-ar/strings.xml
+++ b/java/res/values-ar/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"يمكنك ŰȘÙˆŰ”ÙŠÙ„ ŰłÙ…Ű§ŰčŰ© ۱ۣ۳ Ù„ŰłÙ…Ű§Űč Ù…ÙŰ§ŰȘÙŠŰ­ ÙƒÙ„Ù…Ű© Ű§Ù„Ù…Ű±ÙˆŰ± Ù…Ù†Ű·ÙˆÙ‚Ű© ŰšŰ”ÙˆŰȘ ŰčŰ§Ù„Ù."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"Ű§Ù„Ù†Ű” Ű§Ù„Ű­Ű§Ù„ÙŠ هو %s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"لم يŰȘم Ű„ŰŻŰźŰ§Ù„ Ù†Ű”"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> لŰȘŰ”Ű­ÙŠŰ­ <xliff:g id="ORIGINAL">%2$s</xliff:g> Ű„Ù„Ù‰ <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> للŰȘŰ”Ű­ÙŠŰ­ Ű§Ù„ŰȘÙ„Ù‚Ű§ŰŠÙŠ"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"Ű±Ù…ŰČ Ű§Ù„Ù…ÙŰȘۭۧ %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"Ű§Ù„ŰčŰ§Ù„ÙŠ"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift يŰčمل (Ű§Ù†Ù‚Ű± للŰȘŰčŰ·ÙŠÙ„)"</string>
diff --git a/java/res/values-bg/strings.xml b/java/res/values-bg/strings.xml
index c21b53490..0e6c8dd45 100644
--- a/java/res/values-bg/strings.xml
+++ b/java/res/values-bg/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"ВĐșлючДтД слушалĐșĐž, за Ўа Ń‡ŃƒĐ”Ń‚Đ” ĐșлаĐČĐžŃˆĐžŃ‚Đ” за ĐżĐ°Ń€ĐŸĐ»Đ°Ń‚Đ° ĐœĐ° ĐČĐžŃĐŸĐș глас."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"йДĐșущоят тДĐșст Đ” %s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"ĐŃĐŒĐ° ĐČъĐČĐ”ĐŽĐ”Đœ тДĐșст"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"„<xliff:g id="KEY">%1$s</xliff:g>“ ĐșĐŸŃ€ĐžĐłĐžŃ€Đ° „<xliff:g id="ORIGINAL">%2$s</xliff:g>“ ĐœĐ° „<xliff:g id="CORRECTED">%3$s</xliff:g>“"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"„<xliff:g id="KEY">%1$s</xliff:g>“ Đ” с аĐČŃ‚ĐŸĐŒĐ°Ń‚ĐžŃ‡ĐœĐŸ ĐșĐŸŃ€ĐžĐłĐžŃ€Đ°ĐœĐ”"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"ĐšĐŸĐŽ ĐœĐ° ĐșлаĐČОшa %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"„Shift“ Đ” ĐČĐșĐ»ŃŽŃ‡Đ”Đœ (ĐŽĐŸĐșĐŸŃĐœĐ”Ń‚Đ” за ЎДаĐșтоĐČĐžŃ€Đ°ĐœĐ”)"</string>
diff --git a/java/res/values-ca/strings.xml b/java/res/values-ca/strings.xml
index d791452d4..043fbd980 100644
--- a/java/res/values-ca/strings.xml
+++ b/java/res/values-ca/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"Connecta un auricular per escoltar les claus de la contrasenya en veu alta."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"El text actual és %s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"No s\'ha introduĂŻt cap text"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> corregeix <xliff:g id="ORIGINAL">%2$s</xliff:g> per <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> té correcció automàtica"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"Clau de codi %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"Maj"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Maj activat (pica per desactivar)"</string>
diff --git a/java/res/values-cs/strings.xml b/java/res/values-cs/strings.xml
index 2cd159d0f..2d5c386a5 100644
--- a/java/res/values-cs/strings.xml
+++ b/java/res/values-cs/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"Chcete-li slyĆĄet, kterĂ© klĂĄvesy jste pƙi zadĂĄvĂĄnĂ­ hesla stiskli, pƙipojte sluchĂĄtka."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"AktuĂĄlnĂ­ text je %s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"NenĂ­ zadĂĄn ĆŸĂĄdnĂœ text"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"KlĂĄvesou <xliff:g id="KEY">%1$s</xliff:g> opravĂ­te <xliff:g id="ORIGINAL">%2$s</xliff:g> na <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"Klávese <xliff:g id="KEY">%1$s</xliff:g> je pƙiƙazena automatická oprava"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"KĂłd klĂĄvesy %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"KlĂĄvesa Shift je zapnutĂĄ (vypnete ji klepnutĂ­m)."</string>
diff --git a/java/res/values-da/strings.xml b/java/res/values-da/strings.xml
index 6c9d9d786..0a53691b2 100644
--- a/java/res/values-da/strings.xml
+++ b/java/res/values-da/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"Tilslut et headset for at hĂžre indtastningen blive lĂŠst hĂžjt ved angivelse af adgangskode."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"NuvĂŠrende tekst er %s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"Der er ingen indtastet tekst"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> retter <xliff:g id="ORIGINAL">%2$s</xliff:g> til <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> udfĂžrer automatisk rettelse"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"Tastekode %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"Shift-tast"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Skift er slÄet til (tryk for at deaktivere)"</string>
diff --git a/java/res/values-de/strings.xml b/java/res/values-de/strings.xml
index 1a4e38542..1485cc60d 100644
--- a/java/res/values-de/strings.xml
+++ b/java/res/values-de/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"Schließen Sie ein Headset an, um das Passwort gesprochen zu hören."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"Aktueller Text lautet %s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"Kein Text eingegeben"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"Mit <xliff:g id="KEY">%1$s</xliff:g> wird <xliff:g id="ORIGINAL">%2$s</xliff:g> in <xliff:g id="CORRECTED">%3$s</xliff:g> korrigiert."</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"Mit <xliff:g id="KEY">%1$s</xliff:g> wird automatisch korrigiert."</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"Tastencode %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"Umschalttaste"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Umschalttaste aktiviert (zum Deaktivieren berĂŒhren)"</string>
diff --git a/java/res/values-el/strings.xml b/java/res/values-el/strings.xml
index c2ee2d39b..8b6d0d135 100644
--- a/java/res/values-el/strings.xml
+++ b/java/res/values-el/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"ÎŁÏ…ÎœÎŽÎ­ÏƒÏ„Î” έΜα σΔτ αÎșÎżÏ…ÏƒÏ„ÎčÎșώΜ ÎłÎčα Μα αÎșÎżÏÏƒÎ”Ï„Î” τα Ï€Î»ÎźÎșτρα Ï„ÎżÏ… ÎșωΎÎčÎșÎżÏ πρόσÎČασης Μα ΔÎșÏ†Ï‰ÎœÎżÏÎœÏ„Î±Îč ÎŽÏ…ÎœÎ±Ï„ÎŹ."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"΀ο Ï„ÏÎ­Ï‡ÎżÎœ ÎșÎ”ÎŻÎŒÎ”ÎœÎż Î”ÎŻÎœÎ±Îč %s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"ΔΔΜ Ï…Ï€ÎŹÏÏ‡Î”Îč ÎșÎ”ÎŻÎŒÎ”ÎœÎż"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"΀ο Ï€Î»ÎźÎșÏ„ÏÎż <xliff:g id="KEY">%1$s</xliff:g> ÎŽÎčÎżÏÎžÏŽÎœÎ”Îč Ï„Îż ÏƒÏ„ÎżÎčÏ‡Î”ÎŻÎż <xliff:g id="ORIGINAL">%2$s</xliff:g> σΔ <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"΀ο Ï€Î»ÎźÎșÏ„ÏÎż <xliff:g id="KEY">%1$s</xliff:g> ÎŽÎčαΞέτΔÎč αυτόΌατη ÎŽÎčόρΞωση"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"ΚωΎÎčÎșός Ï€Î»ÎźÎșÏ„ÏÎżÏ… %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"΀ο Shift Î”ÎŻÎœÎ±Îč Î”ÎœÎ”ÏÎłÎżÏ€ÎżÎčÎ·ÎŒÎ­ÎœÎż (Ï€Î±Ï„ÎźÏƒÏ„Î” ÎłÎčα Î±Ï€Î”ÎœÎ”ÏÎłÎżÏ€ÎżÎŻÎ·ÏƒÎ·)"</string>
diff --git a/java/res/values-en-rGB/strings.xml b/java/res/values-en-rGB/strings.xml
index 0586bcca4..1891a3fdb 100644
--- a/java/res/values-en-rGB/strings.xml
+++ b/java/res/values-en-rGB/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"Plug in a headset to hear password keys spoken aloud."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"Current text is %s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"No text entered"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> corrects <xliff:g id="ORIGINAL">%2$s</xliff:g> to <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> has auto-correction"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"Key code %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift on (tap to disable)"</string>
diff --git a/java/res/values-en-rIN/strings.xml b/java/res/values-en-rIN/strings.xml
index 0586bcca4..1891a3fdb 100644
--- a/java/res/values-en-rIN/strings.xml
+++ b/java/res/values-en-rIN/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"Plug in a headset to hear password keys spoken aloud."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"Current text is %s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"No text entered"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> corrects <xliff:g id="ORIGINAL">%2$s</xliff:g> to <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> has auto-correction"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"Key code %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift on (tap to disable)"</string>
diff --git a/java/res/values-es-rUS/strings.xml b/java/res/values-es-rUS/strings.xml
index 42c3932f6..331eb3878 100644
--- a/java/res/values-es-rUS/strings.xml
+++ b/java/res/values-es-rUS/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"Enchufa tus auriculares para escuchar en voz alta qué teclas presionas al ingresar una contraseña."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"El texto actual es %s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"No se ingresĂł texto."</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"La tecla <xliff:g id="KEY">%1$s</xliff:g> corrige <xliff:g id="ORIGINAL">%2$s</xliff:g> por <xliff:g id="CORRECTED">%3$s</xliff:g>."</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"La tecla <xliff:g id="KEY">%1$s</xliff:g> corrige automĂĄticamente."</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"Clave de cĂłdigo %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"MayĂșs"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Se activĂł el modo MayĂșscula (toca para desactivarlo)."</string>
diff --git a/java/res/values-es/strings.xml b/java/res/values-es/strings.xml
index 4d72799f4..75069703b 100644
--- a/java/res/values-es/strings.xml
+++ b/java/res/values-es/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"Conecta un auricular para escuchar las contraseñas en voz alta."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"El texto actual es %s."</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"No se ha introducido texto."</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"La tecla <xliff:g id="KEY">%1$s</xliff:g> corrige <xliff:g id="ORIGINAL">%2$s</xliff:g> a <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"La tecla <xliff:g id="KEY">%1$s</xliff:g> corrige automĂĄticamente"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"CĂłdigo del teclado: %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"MayĂșs"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"MayĂșsculas activadas (tocar para inhabilitar)"</string>
diff --git a/java/res/values-et-rEE/strings.xml b/java/res/values-et-rEE/strings.xml
index 5706fa105..c7e6fe90c 100644
--- a/java/res/values-et-rEE/strings.xml
+++ b/java/res/values-et-rEE/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"Ühendage peakomplekt, et kuulata paroole."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"Praegune tekst on %s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"Teksti ei ole sisestatud"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> parandab valiku <xliff:g id="ORIGINAL">%2$s</xliff:g> valikuks <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"Klahv <xliff:g id="KEY">%1$s</xliff:g> rakendab automaatse paranduse"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"Klahvi kood: %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"TÔstuklahv"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"TÔstuklahv sees (puudutage keelamiseks)"</string>
diff --git a/java/res/values-fa/strings.xml b/java/res/values-fa/strings.xml
index c03e440e4..febe01bf1 100644
--- a/java/res/values-fa/strings.xml
+++ b/java/res/values-fa/strings.xml
@@ -86,10 +86,8 @@
<!-- no translation found for spoken_current_text_is (2485723011272583845) -->
<skip />
<string name="spoken_no_text_entered" msgid="7479685225597344496">"مŰȘنی ÙˆŰ§Ű±ŰŻ Ù†ŰŽŰŻÙ‡ ۧ۳ŰȘ"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g>ی ‏<xliff:g id="ORIGINAL">%2$s</xliff:g> ۱ۧ ŰšÙ‡ <xliff:g id="CORRECTED">%3$s</xliff:g> ŰȘŰ”Ű­ÛŒŰ­ Ù…ÛŒâ€ŒÚ©Ù†ŰŻ"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> ŰŻŰ§Ű±Ű§ÛŒ ŰȘŰ”Ű­ÛŒŰ­ ŰźÙˆŰŻÚ©Ű§Ű± ۧ۳ŰȘ"</string>
<!-- String.format failed for translation -->
<!-- no translation found for spoken_description_unknown (3197434010402179157) -->
<skip />
diff --git a/java/res/values-fi/strings.xml b/java/res/values-fi/strings.xml
index 744e60420..8f2caabbf 100644
--- a/java/res/values-fi/strings.xml
+++ b/java/res/values-fi/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"LiitÀ kuulokkeet, niin kuulet mitÀ nÀppÀimiÀ painat kirjoittaessasi salasanaa."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"Nykyinen teksti on %s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"Ei kirjoitettua tekstiÀ"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> korjaa kohteen <xliff:g id="ORIGINAL">%2$s</xliff:g> kohteeksi <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> suorittaa automaattisen korjauksen"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"NÀppÀimen koodi %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"Vaihto"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Vaihto pÀÀllÀ (poista kÀytöstÀ napauttamalla)"</string>
diff --git a/java/res/values-fr-rCA/strings.xml b/java/res/values-fr-rCA/strings.xml
index 52a9640b9..fba029887 100644
--- a/java/res/values-fr-rCA/strings.xml
+++ b/java/res/values-fr-rCA/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"Branchez des écouteurs pour entendre l\'énoncé à haute voix des touches lors de la saisie du mot de passe."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"Le texte actuel est %s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"Aucun texte saisi"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> corrige <xliff:g id="ORIGINAL">%2$s</xliff:g> en <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> dispose de la fonctionnalité de correction automatique"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"Code touche %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"Maj"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Touche Maj activée (appuyer pour désactiver)"</string>
diff --git a/java/res/values-fr/strings.xml b/java/res/values-fr/strings.xml
index 5fedd4ebd..9d6c8f489 100644
--- a/java/res/values-fr/strings.xml
+++ b/java/res/values-fr/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"Branchez des écouteurs pour entendre l\'énoncé à haute voix des touches lors de la saisie du mot de passe."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"Le texte actuel est %s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"Aucun texte saisi"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"La touche <xliff:g id="KEY">%1$s</xliff:g> permet de remplacer \"<xliff:g id="ORIGINAL">%2$s</xliff:g>\" par \"<xliff:g id="CORRECTED">%3$s</xliff:g>\"."</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"La touche <xliff:g id="KEY">%1$s</xliff:g> permet d\'activer la correction automatique."</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"Code touche %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"Maj"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Touche Maj activée (appuyer pour désactiver)"</string>
diff --git a/java/res/values-hi/strings.xml b/java/res/values-hi/strings.xml
index 3b3228a9f..0e9729629 100644
--- a/java/res/values-hi/strings.xml
+++ b/java/res/values-hi/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"à€œà€Œà„‹à€° à€žà„‡ à€Źà„‹à€Čà„€ à€—à€ˆ à€Șà€Ÿà€žà€”à€°à„à€Ą à€•à„à€‚à€œà€żà€Żà€Ÿà€‚ à€žà„à€šà€šà„‡ à€•à„‡ à€Čà€żà€ à€čà„‡à€Ąà€žà„‡à€Ÿ à€Șà„â€à€Čà€— à€‡à€š à€•à€°à„‡à€‚."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"à€”à€°à„à€€à€źà€Ÿà€š à€Șà€Ÿà€  %s à€čà„ˆ"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"à€•à„‹à€ˆ à€Șà€Ÿà€  à€Šà€°à„à€œ à€šà€čà„€à€‚ à€•à€żà€Żà€Ÿ à€—à€Żà€Ÿ"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g>, <xliff:g id="ORIGINAL">%2$s</xliff:g> à€•à„‹ à€žà„à€§à€Ÿà€°à€•à€° <xliff:g id="CORRECTED">%3$s</xliff:g> à€Źà€šà€Ÿ à€Šà„‡à€€à„€ à€čà„ˆ"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> à€žà„‡ à€žà„â€à€”à€€: à€žà„à€§à€Ÿà€° à€čà„‹à€—à€Ÿ"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"à€•à„à€‚à€œà„€ à€•à„‹à€Ą %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"à€¶à€żà€«à€Œà„à€Ÿ"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift à€šà€Ÿà€Čà„‚ (à€…à€•à„à€·à€ź à€•à€°à€šà„‡ à€•à„‡ à€Čà€żà€ à€Ÿà„ˆà€Ș à€•à€°à„‡à€‚)"</string>
diff --git a/java/res/values-hr/strings.xml b/java/res/values-hr/strings.xml
index caef9541d..f5c9ad503 100644
--- a/java/res/values-hr/strings.xml
+++ b/java/res/values-hr/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"Priključite sluơalice da biste čuli tipke zaporke izgovorene naglas."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"Trenutačni tekst je %s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"Nije unesen tekst"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> ispravlja <xliff:g id="ORIGINAL">%2$s</xliff:g> u <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> ima samoispravljanje"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"KĂŽd tipke %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Uključena tipka Shift (dotaknite da onemogućite)"</string>
diff --git a/java/res/values-hu/strings.xml b/java/res/values-hu/strings.xml
index 3b0ee4798..5f4a18106 100644
--- a/java/res/values-hu/strings.xml
+++ b/java/res/values-hu/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"Csatlakoztasson egy headsetet, ha hallani szeretné a jelszót felolvasva."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"A jelenlegi szöveg: %s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"Szöveg nincs megadva"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> gomb: a(z) <xliff:g id="ORIGINAL">%2$s</xliff:g> értéket <xliff:g id="CORRECTED">%3$s</xliff:g> értékre javítja"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"Automatikus javĂ­tĂĄs van beĂĄllĂ­tva a következƑhöz: <xliff:g id="KEY">%1$s</xliff:g>"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"BillentyƱkód: %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift be van kapcsolva (érintse meg a kikapcsolåshoz)"</string>
diff --git a/java/res/values-hy-rAM/strings.xml b/java/res/values-hy-rAM/strings.xml
index 409c61e5a..57c0e61d9 100644
--- a/java/res/values-hy-rAM/strings.xml
+++ b/java/res/values-hy-rAM/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"ՄիեցրՄք ŐĄŐŻŐĄŐ¶Ő»ŐĄŐŻŐĄŐŹŐšŐ ŐąŐĄÖ€Ő±Ö€ŐĄŐ±ŐĄŐ”Ő¶ ŐĄÖ€ŐżŐĄŐœŐĄŐ¶ŐŸŐžŐČ ŐŁŐĄŐČŐżŐ¶ŐĄŐąŐĄŐŒŐš ŐŹŐœŐ„ŐŹŐžÖ‚ հածար:"</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"ŐŐŸŐ”ŐĄŐŹ ŐżŐ„Ö„ŐœŐżŐš %s Ő§"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"ŐŐ„Ö„ŐœŐż ŐčŐ« ŐŽŐžÖ‚ŐżÖ„ŐĄŐŁÖ€ŐŸŐ„ŐŹ"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g>-Őš Ő·ŐżŐŻŐžÖ‚ŐŽ Ő§ <xliff:g id="ORIGINAL">%2$s</xliff:g>-Őš և Ő€ŐĄÖ€Ő±Ő¶ŐžÖ‚ŐŽ <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g>-Ő¶ ŐžÖ‚Ő¶Ő« Ő«Ő¶Ö„Ő¶ŐžÖ‚Ö€ŐžÖ‚Ő”Ő¶ Ő·ŐżŐŻŐžÖ‚ŐŽ"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"ÔČŐĄŐ¶ŐĄŐŹŐžÖ‚ ŐŻŐžŐ€ŐšŐ %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift-Őš ŐŽŐ«ŐĄÖŐŸŐĄŐź Ő§ (Ő°ŐșŐ„ŐŹ ŐĄŐ¶Ő»ŐĄŐżŐ„ŐŹŐžÖ‚ հածար)"</string>
diff --git a/java/res/values-in/strings.xml b/java/res/values-in/strings.xml
index bfbd2a700..acd839413 100644
--- a/java/res/values-in/strings.xml
+++ b/java/res/values-in/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"Pasang headset untuk mendengar tombol sandi yang diucapkan dengan keras."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"Teks saat ini adalah %s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"Tidak ada teks yang dimasukkan"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> mengoreksi <xliff:g id="ORIGINAL">%2$s</xliff:g> menjadi <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> memiliki koreksi otomatis"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"Kode tombol %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift hidup (ketuk untuk mematikan)"</string>
diff --git a/java/res/values-it/strings.xml b/java/res/values-it/strings.xml
index 6989643af..2357aa267 100644
--- a/java/res/values-it/strings.xml
+++ b/java/res/values-it/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"Collega gli auricolari per ascoltare la pronuncia dei tasti premuti per la password."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"Il testo attuale Ăš %s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"Nessun testo inserito"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> corregge <xliff:g id="ORIGINAL">%2$s</xliff:g> con <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> ha la funzione di correzione automatica"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"Codice tasto %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"Maiuscolo"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Maiuscolo attivo (tocca per disattivare)"</string>
diff --git a/java/res/values-iw/strings.xml b/java/res/values-iw/strings.xml
index b3ee01410..1ad2c3e45 100644
--- a/java/res/values-iw/strings.xml
+++ b/java/res/values-iw/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"Ś—Ś‘Śš ڐڕږڠڙڕŚȘ ڛړڙ ŚœŚ©ŚžŚ•Śą Ś”Ś§ŚšŚŚ” کڜ ŚžŚ€ŚȘŚ—Ś•ŚȘ ŚĄŚ™ŚĄŚžŚ”."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"Ś”Ś˜Ś§ŚĄŚ˜ ڔڠڕڛڗڙ ڔڕڐ %s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"ڜڐ Ś”Ś•Ś–ŚŸ Ś˜Ś§ŚĄŚ˜"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> ŚžŚȘڧڟ ڐŚȘ <xliff:g id="ORIGINAL">%2$s</xliff:g> ڜ-<xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> ŚžŚ‘ŚŠŚą ŚȘŚ™Ś§Ś•ŚŸ ŚŚ•Ś˜Ś•ŚžŚ˜Ś™"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"ڧڕړ ŚžŚ§Ś© %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift Ś€Ś•ŚąŚœ (ڔڧک ڛړڙ ŚœŚ”Ś©Ś‘Ś™ŚȘ)"</string>
diff --git a/java/res/values-ja/strings.xml b/java/res/values-ja/strings.xml
index 37f06377e..9cda30b73 100644
--- a/java/res/values-ja/strings.xml
+++ b/java/res/values-ja/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"パă‚čăƒŻăƒŒăƒ‰ăźă‚­ăƒŒăŒéŸłćŁ°ć‡șćŠ›ă•ă‚Œă‚‹ăźă§ăƒ˜ăƒƒăƒ‰ă‚»ăƒƒăƒˆă‚’æŽ„ç¶šă—ăŠăă ă•ă„ă€‚"</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"çŸćœšăźăƒ†ă‚­ă‚čト:%s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"テキă‚čăƒˆăŒć…„ćŠ›ă•ă‚ŒăŠă„ăŸă›ă‚“"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g>は<xliff:g id="ORIGINAL">%2$s</xliff:g>を<xliff:g id="CORRECTED">%3$s</xliff:g>ă«äżźæ­Łă—ăŸă™"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g>でè‡Șć‹•äżźæ­ŁăŒćźŸèĄŒă•ă‚ŒăŸă™"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"ă‚­ăƒŒă‚łăƒŒăƒ‰:%d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift有ćŠčïŒˆă‚żăƒƒăƒ—ă—ăŠè§Łé™€ïŒ‰"</string>
diff --git a/java/res/values-ka-rGE/strings.xml b/java/res/values-ka-rGE/strings.xml
index 193f5115d..3fd168095 100644
--- a/java/res/values-ka-rGE/strings.xml
+++ b/java/res/values-ka-rGE/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"ჹეაერთეთ ყურქაáƒȘვამი, რათა მოისმინოთ აკრეჀილი პაროლის კლავიჹების საჼელები."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"მიმდინარე áƒąáƒ”áƒ„áƒĄáƒąáƒ˜ არიქ %s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"áƒąáƒ”áƒ„áƒĄáƒąáƒ˜ არ ჹეყვანილა"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> ჹეასწორებს <xliff:g id="ORIGINAL">%2$s</xliff:g>-ს <xliff:g id="CORRECTED">%3$s</xliff:g>-ად"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g>-ქ áƒáƒ•áƒąáƒáƒ™áƒáƒ áƒ”áƒ„áƒȘია გააჩნია"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"კლავიაჱურის კოდი %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift áƒ©áƒáƒ áƒ—áƒŁáƒšáƒ˜áƒ (ჹეეჼეთ გამოსართავად)"</string>
diff --git a/java/res/values-km-rKH/strings.xml b/java/res/values-km-rKH/strings.xml
index 7294c7ee0..34c426944 100644
--- a/java/res/values-km-rKH/strings.xml
+++ b/java/res/values-km-rKH/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"ដោត​កាស ដសម្បឞ​ស្ដាប់​ពាក្យ​សម្ងាត់។"</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"ážąážáŸ’ážáž”áž‘â€‹áž”áž…áŸ’áž…áž»áž”áŸ’áž”áž“áŸ’áž“â€‹áž‚ážș %s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"áž‚áŸ’áž˜áž¶áž“â€‹ážąážáŸ’ážáž”áž‘â€‹â€‹â€‹áž”áž¶áž“â€‹áž”áž‰áŸ’áž…ážŒáž›"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> កែ <xliff:g id="ORIGINAL">%2$s</xliff:g> ទៅ <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> មាន​ការ​កែ​ស្វ័យ​ប្រវត្តិ"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"កឌដ​គ្រាប់​ចុច %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"បសក Shift (​ប៉ះ​ដសម្បឞ​បិទ)"</string>
diff --git a/java/res/values-ko/strings.xml b/java/res/values-ko/strings.xml
index 1497812e9..630ae7e4b 100644
--- a/java/res/values-ko/strings.xml
+++ b/java/res/values-ko/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"ëč„ë°€ëȈ혞 킀넌 ìŒì„±ìœŒëĄœ 듀윌렀멎 헀드셋을 연êČ°í•˜ì„žìš”."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"입렄한 텍슀튞: %s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"입렄한 텍슀튞 없음"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g>을(넌) 누넎멎 <xliff:g id="ORIGINAL">%2$s</xliff:g>을(넌) <xliff:g id="CORRECTED">%3$s</xliff:g>(윌)로 수정합니닀."</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g>을(넌) 누넎멎 자동 수정됩니닀."</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"í‚€ 윔드 %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"시프튞 í‚€"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift ì‚Źìš©(ì‚Źìš©í•˜ì§€ 않윌렀멎 탭하섞요.)"</string>
diff --git a/java/res/values-lo-rLA/strings.xml b/java/res/values-lo-rLA/strings.xml
index 9bf46275d..0d7f7a27d 100644
--- a/java/res/values-lo-rLA/strings.xml
+++ b/java/res/values-lo-rLA/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"àșȘàșœàșšàșȘàșČàșàș«àșčàșŸàș±àș‡à»€àșžàș·à»ˆàș­àșŸàș±àș‡àș„àș°àș«àș±àș”àșœà»ˆàșČàș™."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"àș‚ໍ້àș„àș§àșČàșĄàș›àș°àșˆàșžàșšàș±àș™à»àșĄà»ˆàș™ %s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"àșšà»à»ˆàșĄàș”àșàșČàș™à»ƒàșȘ່àș‚ໍ້àș„àș§àșČàșĄ"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> àș–àș·àșà»àș›àș‡àșˆàșČàș <xliff:g id="ORIGINAL">%2$s</xliff:g> ເàș›àș±àș™ <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> àșšà»à»ˆàșĄàș”àșàșČàș™àșàș§àș”àș„àșłàș–àș·àșàș­àș±àș”àș•àș°à»‚àș™àșĄàș±àș”"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"àș„àș°àș«àș±àș”àșàș°à»àșˆ %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift ເàș›àș”àș”àș™àșłà»ƒàșŠà»‰àșąàșč່ (àșàș»àș”ເàșžàș·à»ˆàș­àș›àșŽàș”àș™àșłà»ƒàșŠà»‰)"</string>
diff --git a/java/res/values-lt/strings.xml b/java/res/values-lt/strings.xml
index 88328db7d..d40b54bc4 100644
--- a/java/res/values-lt/strings.xml
+++ b/java/res/values-lt/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"Prijunkite ausines, kad iĆĄgirstumėte sakomus slaptaĆŸodĆŸio klaviĆĄus."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"Dabartinis tekstas yra %s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"Nėra įvesto teksto"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"„<xliff:g id="KEY">%1$s</xliff:g>“ pataiso „<xliff:g id="ORIGINAL">%2$s</xliff:g>“ į „<xliff:g id="CORRECTED">%3$s</xliff:g>“"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"„<xliff:g id="KEY">%1$s</xliff:g>“ atlieka automatinį taisymą"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"KlaviĆĄo kodas %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"Antrojo lygio klaviĆĄas"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"ļjungtas antrasis lygis (palieskite, kad iơjungtumėte)"</string>
diff --git a/java/res/values-lv/strings.xml b/java/res/values-lv/strings.xml
index 069c48701..cde9c3462 100644
--- a/java/res/values-lv/strings.xml
+++ b/java/res/values-lv/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"Pievienojiet austiƆas, lai dzirdētu paroles rakstzÄ«mes."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"Paơreizējais teksts ir %s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"Nav ievadīts teksts"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"NospieĆŸot taustiƆu <xliff:g id="KEY">%1$s</xliff:g>, “<xliff:g id="ORIGINAL">%2$s</xliff:g>” tiek labots uz “<xliff:g id="CORRECTED">%3$s</xliff:g>”."</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"TaustiƆam <xliff:g id="KEY">%1$s</xliff:g> ir automātiskas laboơanas funkcija."</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"TaustiƆu kods %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"Pārslēgơanas taustiƆơ"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Pārslēgơanas taustiƆơ iespējots (pieskarieties, lai atspējotu)"</string>
diff --git a/java/res/values-mn-rMN/strings.xml b/java/res/values-mn-rMN/strings.xml
index 3b039f120..177b5334f 100644
--- a/java/res/values-mn-rMN/strings.xml
+++ b/java/res/values-mn-rMN/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"Нууц ÒŻĐłĐœĐžĐč Ń‚ĐŸĐČŃ‡ĐœŃƒŃƒĐŽŃ‹Đł Ń‡Đ°ĐœĐłĐ°Đ°Ń€ ŃƒĐœŃˆĐžŃ…Ń‹Đł ŃĐŸĐœŃĐŸŃ…Ń‹Đœ Ń‚ŃƒĐ»ĐŽ чохэĐČчээ Đ·Đ°Đ»ĐłĐ°ĐœĐ° уу."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"ĐžĐŽĐŸĐŸĐłĐžĐčĐœ тДĐșст %s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"йДĐșст ĐŸŃ€ŃƒŃƒĐ»Đ°Đ°ĐłÒŻĐč"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> ĐœŃŒ <xliff:g id="ORIGINAL">%2$s</xliff:g>-Đł <xliff:g id="CORRECTED">%3$s</xliff:g> Đ±ĐŸĐ»ĐłĐŸĐ¶ Đ·Đ°Đ»Ń€ŃƒŃƒĐ»ĐœĐ°"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> аĐČŃ‚ĐŸĐŒĐ°Ń‚ Đ·Đ°Đ»Ń€ŃƒŃƒĐ»Đ°ĐłŃ‡Ń‚Đ°Đč"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"ĐąĐŸĐČчоĐčĐœ ĐșĐŸĐŽ %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"ĐĄŃĐ»ĐłŃŃ…"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"ĐĄŃĐ»ĐłŃŃ…ĐžĐčĐł оЮэĐČŃ…Đ¶ÒŻÒŻĐ»ŃŃĐœ (Ń‚ĐŸĐČшОж оЮэĐČŃ…ĐłÒŻĐčĐ¶ÒŻÒŻĐ»ĐœŃ ÒŻÒŻ)"</string>
diff --git a/java/res/values-ms-rMY/strings.xml b/java/res/values-ms-rMY/strings.xml
index ee241d64b..0e8b4eb00 100644
--- a/java/res/values-ms-rMY/strings.xml
+++ b/java/res/values-ms-rMY/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"Pasangkan set kepala untuk mendengar kekunci kata laluan disebut dengan kuat."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"Teks semasa adalah %s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"Tiada teks dimasukkan"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> membetulkan <xliff:g id="ORIGINAL">%2$s</xliff:g> menjadi <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> mempunyai auto pembetulan"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"Kod kunci %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Kunci anjak dihidupkan (ketik untuk melumpuhkan)"</string>
diff --git a/java/res/values-nb/strings.xml b/java/res/values-nb/strings.xml
index ae43d1078..1bd91b6b4 100644
--- a/java/res/values-nb/strings.xml
+++ b/java/res/values-nb/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"Koble til hodetelefoner for Ă„ hĂžre opplesing av bokstavene i passordet."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"Gjeldende tekst er %s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"Ingen tekst er skrevet inn"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> korrigerer <xliff:g id="ORIGINAL">%2$s</xliff:g> til <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> har automatisk korrigering"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"Tastaturkode %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift er pÄ (trykk for Ä deaktivere)"</string>
diff --git a/java/res/values-nl/strings.xml b/java/res/values-nl/strings.xml
index de20da4f8..b1d1bb3dd 100644
--- a/java/res/values-nl/strings.xml
+++ b/java/res/values-nl/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"Sluit een headset aan om wachtwoordtoetsen hardop te laten voorlezen."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"Huidige tekst is %s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"Geen tekst ingevoerd"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"Met <xliff:g id="KEY">%1$s</xliff:g> wordt <xliff:g id="ORIGINAL">%2$s</xliff:g> gecorrigeerd naar <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"Met <xliff:g id="KEY">%1$s</xliff:g> voert u automatische correctie uit"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"Toetscode %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift aan (tik om uit te schakelen)"</string>
diff --git a/java/res/values-pl/strings.xml b/java/res/values-pl/strings.xml
index 390837596..f830b37c1 100644
--- a/java/res/values-pl/strings.xml
+++ b/java/res/values-pl/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"PodƂącz zestaw sƂuchawkowy, aby usƂyszeć znaki hasƂa wypowiadane na gƂos."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"Aktualny tekst: %s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"Nie wprowadzono tekstu"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> poprawia <xliff:g id="ORIGINAL">%2$s</xliff:g> na <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> zapewnia autokorektę"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"Kod klawisza: %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift wƂączony (kliknij, by wyƂączyć)"</string>
diff --git a/java/res/values-pt-rPT/strings.xml b/java/res/values-pt-rPT/strings.xml
index 4c81479e5..dbf34f95a 100644
--- a/java/res/values-pt-rPT/strings.xml
+++ b/java/res/values-pt-rPT/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"Ligar auscultadores com microfone integrado para ouvir as teclas da palavra-passe."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"O texto atual Ă© %s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"Nenhum texto digitado"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> corrige <xliff:g id="ORIGINAL">%2$s</xliff:g> para <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> tem correção automåtica"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"CĂłdigo da tecla %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift ativado (tocar para desativar)"</string>
diff --git a/java/res/values-pt/strings.xml b/java/res/values-pt/strings.xml
index ef533ae83..3f9837223 100644
--- a/java/res/values-pt/strings.xml
+++ b/java/res/values-pt/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"Conecte um fone de ouvido para ouvir as chaves de senha em voz alta."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"O texto atual Ă© %s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"Nenhum texto digitado"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> corrige <xliff:g id="ORIGINAL">%2$s</xliff:g> para <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> possui correção automåtica"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"CĂłdigo de tecla %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift ativado (toque para desativar)"</string>
diff --git a/java/res/values-ro/strings.xml b/java/res/values-ro/strings.xml
index 930b68bd6..f67f58ebd 100644
--- a/java/res/values-ro/strings.xml
+++ b/java/res/values-ro/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"Conectaƣi un set căƟti-microfon pentru a auzi tastele apăsate cñnd introduceƣi parola."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"Textul curent este %s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"Nu a fost introdus text"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> corectează <xliff:g id="ORIGINAL">%2$s</xliff:g> cu <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> dispune de corectare automată"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"Tasta cu codul %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Tasta Shift este activată (apăsaƣi pentru a o dezactiva)"</string>
diff --git a/java/res/values-ru/strings.xml b/java/res/values-ru/strings.xml
index 8bfb011c5..20f358a77 100644
--- a/java/res/values-ru/strings.xml
+++ b/java/res/values-ru/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"ĐŸĐŸĐŽĐșлючОтД ĐłĐ°Ń€ĐœĐžŃ‚ŃƒŃ€Ńƒ, Ń‡Ń‚ĐŸĐ±Ń‹ ŃƒŃĐ»Ń‹ŃˆĐ°Ń‚ŃŒ ĐżĐ°Ń€ĐŸĐ»ŃŒ."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"ВĐČĐ”ĐŽĐ”ĐœĐœŃ‹Đč тДĐșст: %s."</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"йДĐșст ĐœĐ” ĐČĐČĐ”ĐŽĐ”Đœ"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"Про ĐœĐ°Đ¶Đ°Ń‚ĐžĐž ĐșлаĐČОшО \"<xliff:g id="KEY">%1$s</xliff:g>\" ŃĐ»ĐŸĐČĐŸ \"<xliff:g id="ORIGINAL">%2$s</xliff:g>\" Đ±ŃƒĐŽĐ”Ń‚ оспраĐČĐ»Đ”ĐœĐŸ ĐœĐ° \"<xliff:g id="CORRECTED">%3$s</xliff:g>\""</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"Đ”Đ»Ń ĐșлаĐČОшО \"<xliff:g id="KEY">%1$s</xliff:g>\" ĐœĐ°Đ·ĐœĐ°Ń‡Đ”ĐœĐ° ĐŸĐżĐ”Ń€Đ°Ń†ĐžŃ аĐČŃ‚ĐŸĐžŃĐżŃ€Đ°ĐČĐ»Đ”ĐœĐžŃ"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"ĐšĐŸĐŽ ĐșлаĐČОшО:%d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"КлаĐČОша ĐČĐ”Ń€Ń…ĐœĐ”ĐłĐŸ рДгОстра"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Đ’Đ”Ń€Ń…ĐœĐžĐč рДгОстр ĐČĐșĐ»ŃŽŃ‡Đ”Đœ (ĐœĐ°Đ¶ĐŒĐžŃ‚Đ”, Ń‡Ń‚ĐŸĐ±Ń‹ ĐŸŃ‚ĐșĐ»ŃŽŃ‡ĐžŃ‚ŃŒ)"</string>
diff --git a/java/res/values-sk/strings.xml b/java/res/values-sk/strings.xml
index 5c25f28ce..3f6706c08 100644
--- a/java/res/values-sk/strings.xml
+++ b/java/res/values-sk/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"Ak si chcete pri zadĂĄvanĂ­ hesla vypočuĆ„ nahlas vyslovenĂ© klĂĄvesy, pripojte nĂĄhlavnĂș sĂșpravu."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"AktuĂĄlny text je %s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"Nie je zadanĂœ ĆŸiadny text"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"KlĂĄvesom <xliff:g id="KEY">%1$s</xliff:g> opravĂ­te <xliff:g id="ORIGINAL">%2$s</xliff:g> na <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"Klåvesom <xliff:g id="KEY">%1$s</xliff:g> spustíte automatické opravy"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"KĂłd klĂĄvesu %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"KlĂĄves Shift je zapnutĂœ (zakĂĄĆŸete ho klepnutĂ­m)"</string>
diff --git a/java/res/values-sl/strings.xml b/java/res/values-sl/strings.xml
index dde01dd68..6c8115e78 100644
--- a/java/res/values-sl/strings.xml
+++ b/java/res/values-sl/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"Priključite sluĆĄalke, če ĆŸelite sliĆĄati izgovorjene tipke gesla."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"Trenutno besedilo je %s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"Ni vnesenega besedila"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"Tipka <xliff:g id="KEY">%1$s</xliff:g> popravi <xliff:g id="ORIGINAL">%2$s</xliff:g> v <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"Tipka <xliff:g id="KEY">%1$s</xliff:g> izvede samodejno popravljanje"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"Koda tipke %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift je vklopljen (dotaknite se, da onemogočite)"</string>
diff --git a/java/res/values-sr/strings.xml b/java/res/values-sr/strings.xml
index e7f6904c1..92e465c63 100644
--- a/java/res/values-sr/strings.xml
+++ b/java/res/values-sr/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"ĐŁĐșŃ™ŃƒŃ‡ĐžŃ‚Đ” ŃĐ»ŃƒŃˆĐ°Đ»ĐžŃ†Đ” Ўа бОстД Ń‡ŃƒĐ»Đž ĐœĐ°ĐłĐ»Đ°Ń ĐžĐ·ĐłĐŸĐČĐŸŃ€Đ”ĐœĐ” тастДрД за Đ»ĐŸĐ·ĐžĐœĐșу."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"ĐąŃ€Đ”ĐœŃƒŃ‚ĐœĐž тДĐșст јД %s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"йДĐșст ĐœĐžŃ˜Đ” ŃƒĐœĐ”Ń‚"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> оспраĐČља <xliff:g id="ORIGINAL">%2$s</xliff:g> у <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> ĐžĐŒĐ° Ń„ŃƒĐœĐșцоју Đ°ŃƒŃ‚ĐŸĐŒĐ°Ń‚ŃĐșĐŸĐł оспраĐČљања"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"КîЮ тастДра %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift јД уĐșŃ™ŃƒŃ‡Đ”Đœ (ĐŽĐŸĐŽĐžŃ€ĐœĐžŃ‚Đ” Ўа бОстД га ĐŸĐœĐ”ĐŒĐŸĐłŃƒŃ›ĐžĐ»Đž)"</string>
diff --git a/java/res/values-sv/strings.xml b/java/res/values-sv/strings.xml
index 0fcca4837..009e8299a 100644
--- a/java/res/values-sv/strings.xml
+++ b/java/res/values-sv/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"Anslut hörlurar om du vill att lösenordet ska lÀsas upp."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"Nuvarande text Àr %s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"Ingen text har angetts"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> Àndrar <xliff:g id="ORIGINAL">%2$s</xliff:g> till <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"Automatisk korrigering anvÀnds för <xliff:g id="KEY">%1$s</xliff:g>"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"Nyckelkod %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"Skift"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Skift pÄ (knacka lÀtt för att inaktivera)"</string>
diff --git a/java/res/values-sw/strings.xml b/java/res/values-sw/strings.xml
index fbf868f24..a6ada5ba4 100644
--- a/java/res/values-sw/strings.xml
+++ b/java/res/values-sw/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"Chomeka plagi ya kifaa cha kichwa cha kusikiza ili kusikiliza msimbo wa nenosiri inayozungumwa kwa sauti ya juu."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"Maandishi ya sasa ni %s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"Hakuna maandishi yaliyoingizwa"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> hurekebisha <xliff:g id="ORIGINAL">%2$s</xliff:g> hadi <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> ina urekebishaji wa kiotomatiki"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"Msimbo wa kitufe %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"Badilisha"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift imewashwa (gonga ili kulemaza)"</string>
diff --git a/java/res/values-th/strings.xml b/java/res/values-th/strings.xml
index f84b32c62..f2e252d4d 100644
--- a/java/res/values-th/strings.xml
+++ b/java/res/values-th/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"àč€àžȘàž”àžąàžšàžŠàžžàž”àž«àžčàžŸàž±àž‡àč€àžžàž·àčˆàž­àžŸàž±àž‡àč€àžȘàž”àžąàž‡àč€àžĄàž·àčˆàž­àžžàžŽàžĄàžžàčŒàžŁàž«àž±àžȘàžœàčˆàžČàž™"</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"àž‚àč‰àž­àž„àž§àžČàžĄàž›àž±àžˆàžˆàžžàžšàž±àž™àž„àž·àž­ %s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"àč„àžĄàčˆàžĄàž”àž‚àč‰àž­àž„àž§àžČàžĄ"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> àžˆàž°àčàžàč‰àč„àž‚ <xliff:g id="ORIGINAL">%2$s</xliff:g> àč€àž›àč‡àž™ <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> àžˆàž°àžĄàž”àžàžČàžŁàčàžàč‰àč„àž‚àž­àž±àž•àč‚àž™àžĄàž±àž•àžŽ"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"àžŁàž«àž±àžȘàž„àž”àžąàčŒ %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift àč€àž›àžŽàž”àž­àžąàžčàčˆ (àčàž•àž°àč€àžžàž·àčˆàž­àž›àžŽàž”àčƒàžŠàč‰àž‡àžČàž™)"</string>
diff --git a/java/res/values-tl/strings.xml b/java/res/values-tl/strings.xml
index af1c68e01..9a3554588 100644
--- a/java/res/values-tl/strings.xml
+++ b/java/res/values-tl/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"Mag-plug in ng headset upang marinig ang mga password key na binabanggit nang malakas."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"Ang kasalukuyang teksto ay %s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"Walang tekstong inilagay"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"Itatama ng pagpindot sa <xliff:g id="KEY">%1$s</xliff:g> ang <xliff:g id="ORIGINAL">%2$s</xliff:g> at gagawing <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"May awtomatikong pagwasto ang <xliff:g id="KEY">%1$s</xliff:g>"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"Code ng key %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Naka-on ang shift (i-tap upang huwag paganahin)"</string>
diff --git a/java/res/values-tr/strings.xml b/java/res/values-tr/strings.xml
index 4c8e1a81d..ab376e190 100644
--- a/java/res/values-tr/strings.xml
+++ b/java/res/values-tr/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"ƞifre tußlarının sesli okunmasını dinlemek için mikrofonlu kulaklık takın."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"Mevcut metin: %s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"Hiç metin girilmedi"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> tußuna basıldığında <xliff:g id="ORIGINAL">%2$s</xliff:g>, <xliff:g id="CORRECTED">%3$s</xliff:g> olarak dĂŒzeltilir"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> tußunda otomatik dĂŒzeltme ißlevi var"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"Tuß kodu: %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"Üst Karakter"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Üst karakter açık (devre dıßı bırakmak için hafifçe vurun)"</string>
diff --git a/java/res/values-uk/strings.xml b/java/res/values-uk/strings.xml
index 287bcb397..2c516523f 100644
--- a/java/res/values-uk/strings.xml
+++ b/java/res/values-uk/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"ПіЮĐșĐ»ŃŽŃ‡Ń–Ń‚ŃŒ ĐłĐ°Ń€ĐœŃ–Ń‚ŃƒŃ€Ńƒ, Ń‰ĐŸĐ± ĐżŃ€ĐŸŃĐ»ŃƒŃ…Đ°Ń‚Đž ĐČіЮтĐČĐŸŃ€Đ”ĐœŃ– ĐČĐłĐŸĐ»ĐŸŃ ŃĐžĐŒĐČĐŸĐ»Đž ĐżĐ°Ń€ĐŸĐ»Ń."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"ĐŸĐŸŃ‚ĐŸŃ‡ĐœĐžĐč тДĐșст – %s."</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"йДĐșст ĐœĐ” ĐČĐČĐ”ĐŽĐ”ĐœĐŸ"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> ĐČопраĐČĐ»ŃŃ” <xliff:g id="ORIGINAL">%2$s</xliff:g> ĐœĐ° <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> ĐŒĐ°Ń” Ń„ŃƒĐœĐșцію аĐČŃ‚ĐŸĐŒĐ°Ń‚ĐžŃ‡ĐœĐŸĐłĐŸ ĐČопраĐČĐ»Đ”ĐœĐœŃ"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"ĐšĐŸĐŽ ĐșлаĐČіші – %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"КлаĐČіша Shift"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift уĐČŃ–ĐŒĐșĐœĐ”ĐœĐŸ (шĐČОЎĐșĐŸ Ń‚ĐŸŃ€ĐșĐœŃ–Ń‚ŃŒŃŃ, Ń‰ĐŸĐ± ĐČĐžĐŒĐșĐœŃƒŃ‚Đž)"</string>
diff --git a/java/res/values-vi/strings.xml b/java/res/values-vi/strings.xml
index 8764df5e0..64b804af5 100644
--- a/java/res/values-vi/strings.xml
+++ b/java/res/values-vi/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"CáșŻm tai nghe để nghe máș­t kháș©u."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"KĂœ tá»± hiện táșĄi lĂ  %s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"KhĂŽng cĂł kĂœ tá»± nĂ o Ä‘Æ°á»Łc nháș­p"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> sá»­a <xliff:g id="ORIGINAL">%2$s</xliff:g> thĂ nh <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"<xliff:g id="KEY">%1$s</xliff:g> cĂł tĂ­nh năng tá»± động sá»­a"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"MĂŁ phĂ­m %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift đang báș­t (báș„m để táșŻt)"</string>
diff --git a/java/res/values-zh-rCN/strings.xml b/java/res/values-zh-rCN/strings.xml
index f7ceff44f..7683c848e 100644
--- a/java/res/values-zh-rCN/strings.xml
+++ b/java/res/values-zh-rCN/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"éœ€èŠæ’ć…„è€łæœșæ‰èƒœćŹćˆ°ćŻ†ç çš„æŒ‰é”źćŁ°ă€‚"</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"ćœ“ć‰æ–‡æœŹäžș%s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"æœȘèŸ“ć…„æ–‡ć­—"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"按<xliff:g id="KEY">%1$s</xliff:g>揯氆<xliff:g id="ORIGINAL">%2$s</xliff:g>æ›Žæ­Łäžș<xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"按<xliff:g id="KEY">%1$s</xliff:g>ćŻæ‰§èĄŒè‡ȘćŠšæ›Žæ­Ł"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"锼码äžș %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift æšĄćŒć·ČćŻç”šïŒˆç‚čæŒ‰ćłćŻćœç”šïŒ‰"</string>
diff --git a/java/res/values-zh-rHK/strings.xml b/java/res/values-zh-rHK/strings.xml
index be00628e9..b18e0d9d3 100644
--- a/java/res/values-zh-rHK/strings.xml
+++ b/java/res/values-zh-rHK/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"æ’äžŠè€łæ©ŸćłćŻèœćˆ°çł»ç”±æœ—èź€ćŻ†çąŒé”ă€‚"</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"ç›źć‰æ–‡ć­—ç‚ș %s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"æœȘèŒžć…„æ–‡ć­—"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"æŒ‰ă€Œ<xliff:g id="KEY">%1$s</xliff:g>」揯氇「<xliff:g id="ORIGINAL">%2$s</xliff:g>ă€äżźæ­Łç‚ș「<xliff:g id="CORRECTED">%3$s</xliff:g>」"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"「<xliff:g id="KEY">%1$s</xliff:g>ă€é”ć…·è‡Șć‹•äżźæ­ŁćŠŸèƒœ"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"æŒ‰é”ä»ŁçąŒ %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"Shift 鍔"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift 鍔ć·Č開敟 (èŒ•æŒ‰ćłćŻćœç”š)"</string>
diff --git a/java/res/values-zh-rTW/strings.xml b/java/res/values-zh-rTW/strings.xml
index b773ad21c..ef3c833db 100644
--- a/java/res/values-zh-rTW/strings.xml
+++ b/java/res/values-zh-rTW/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"é€ŁæŽ„è€łæ©ŸćłćŻèœć–çł»ç”±æœ—èź€ćŻ†çąŒæŒ‰é”ă€‚"</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"ç›źć‰æ–‡ć­—ç‚ș %s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"æœȘèŒžć…„æ–‡ć­—"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"æŒ‰äž‹ă€Œ<xliff:g id="KEY">%1$s</xliff:g>」揯氇「<xliff:g id="ORIGINAL">%2$s</xliff:g>ă€äżźæ­Łç‚ș「<xliff:g id="CORRECTED">%3$s</xliff:g>」"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"æŒ‰äž‹ă€Œ<xliff:g id="KEY">%1$s</xliff:g>ă€ćŻćŸ·èĄŒè‡Șć‹•äżźæ­Ł"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"æŒ‰é”ä»ŁçąŒ %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"Shift 鍔"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"Shift 鍔ć·Č開敟 (èŒ•æŒ‰ćłćŻćœç”š)"</string>
diff --git a/java/res/values-zu/strings.xml b/java/res/values-zu/strings.xml
index 8f722cce3..2dafde941 100644
--- a/java/res/values-zu/strings.xml
+++ b/java/res/values-zu/strings.xml
@@ -84,10 +84,8 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"Plaka ku-headset ukuze uzwe okhiye bephasiwedi ezindlebeni zakho bezwakala kakhulu."</string>
<string name="spoken_current_text_is" msgid="2485723011272583845">"Umbhalo wamanje ngu %s"</string>
<string name="spoken_no_text_entered" msgid="7479685225597344496">"Awukho umbhalo ofakiwe"</string>
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
- <skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
- <skip />
+ <string name="spoken_auto_correct" msgid="5381764628886369268">"I-<xliff:g id="KEY">%1$s</xliff:g> ilungisa i-<xliff:g id="ORIGINAL">%2$s</xliff:g> ibe yi-<xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="1186884531440481089">"I-<xliff:g id="KEY">%1$s</xliff:g> inokulungiswa okuzenzakalelayo"</string>
<string name="spoken_description_unknown" msgid="3197434010402179157">"Ikhodi yokhiye %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"Shift"</string>
<string name="spoken_description_shift_shifted" msgid="1681877323344195035">"U-Shift uvuliwe (thepha ukuwuvimbela)"</string>
diff --git a/java/res/values/colors.xml b/java/res/values/colors.xml
index 3803cb776..94fadb964 100644
--- a/java/res/values/colors.xml
+++ b/java/res/values/colors.xml
@@ -39,7 +39,6 @@
<color name="typed_word_color_ics">#D833B5E5</color>
<color name="suggested_word_color_ics">#B233B5E5</color>
<color name="highlight_translucent_color_ics">#9933B5E5</color>
- <color name="key_text_color_ics">@android:color/white</color>
<color name="key_text_shadow_color_ics">@android:color/transparent</color>
<color name="key_text_inactivated_color_ics">#66E0E4E5</color>
<color name="key_hint_letter_color_ics">#80000000</color>
@@ -66,4 +65,7 @@
<!-- TODO: Color which should be included in the theme -->
<color name="emoji_key_background_color">#00000000</color>
<color name="emoji_key_pressed_background_color">#30FFFFFF</color>
+
+ <color name="key_text_color_normal_ics">@android:color/white</color>
+ <color name="key_text_color_functional_ics">@android:color/white</color>
</resources>
diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml
index 06f4b4789..a779c6efa 100644
--- a/java/res/values/strings.xml
+++ b/java/res/values/strings.xml
@@ -174,10 +174,10 @@
<!-- Spoken description when there is no text entered -->
<string name="spoken_no_text_entered">No text entered</string>
- <!-- Spoken description to let the user know what auto-correction will be performed when a key is pressed. -->
- <string name="spoken_auto_correct"><xliff:g id="key" example="Space">%1$s</xliff:g> corrects <xliff:g id="original">%2$s</xliff:g> to <xliff:g id="corrected">%3$s</xliff:g></string>
+ <!-- Spoken description to let the user know what auto-correction will be performed when a key is pressed. An auto-correction replaces a single word with one or more words. -->
+ <string name="spoken_auto_correct"><xliff:g id="key" example="Space">%1$s</xliff:g> corrects <xliff:g id="original_word">%2$s</xliff:g> to <xliff:g id="corrected">%3$s</xliff:g></string>
<!-- Spoken description used during obscured (e.g. password) entry to let the user know that auto-correction will be performed when a key is pressed. -->
- <string name="spoken_auto_correct_obscured"><xliff:g id="key" example="Space">%1$s</xliff:g> has auto-correction</string>
+ <string name="spoken_auto_correct_obscured"><xliff:g id="key" example="Space">%1$s</xliff:g> performs auto-correction</string>
<!-- Spoken description for unknown keyboard keys. -->
<string name="spoken_description_unknown">Key code %d</string>
diff --git a/java/res/xml-sw600dp/rows_symbols.xml b/java/res/xml-sw600dp/rows_symbols.xml
index fbd8492cd..cf94b06ed 100644
--- a/java/res/xml-sw600dp/rows_symbols.xml
+++ b/java/res/xml-sw600dp/rows_symbols.xml
@@ -68,5 +68,7 @@
latin:keyWidth="10.0%p" />
<include
latin:keyboardLayout="@xml/row_symbols4" />
+ <include
+ latin:keyboardLayout="@xml/key_f2" />
</Row>
</merge>
diff --git a/java/res/xml/key_styles_common.xml b/java/res/xml/key_styles_common.xml
index 67ed9620d..c9d87bfd4 100644
--- a/java/res/xml/key_styles_common.xml
+++ b/java/res/xml/key_styles_common.xml
@@ -121,6 +121,27 @@
latin:keyIcon="!icon/emoji_key"
latin:keyActionFlags="noKeyPreview"
latin:backgroundType="functional" />
+ <!-- Overriding EnterKeyStyle here -->
+ <switch>
+ <!-- Shift + Enter in textMultiLine field. -->
+ <case
+ latin:isMultiLine="true"
+ latin:keyboardLayoutSetElement="alphabetManualShifted|alphabetShiftLockShifted"
+ >
+ <key-style
+ latin:styleName="enterKeyStyle"
+ latin:parentStyle="shiftEnterKeyStyle" />
+ </case>
+ <!-- Smiley in textShortMessage field.
+ Overrides common enter key style. -->
+ <case
+ latin:mode="im"
+ >
+ <key-style
+ latin:styleName="enterKeyStyle"
+ latin:parentStyle="emojiKeyStyle" />
+ </case>
+ </switch>
<key-style
latin:styleName="tabKeyStyle"
latin:code="!code/key_tab"
diff --git a/java/res/xml/row_symbols4.xml b/java/res/xml/row_symbols4.xml
index 0bf412fff..fbfdc5f72 100644
--- a/java/res/xml/row_symbols4.xml
+++ b/java/res/xml/row_symbols4.xml
@@ -39,8 +39,4 @@
<include latin:keyboardLayout="@xml/key_space_symbols" />
<include latin:keyboardLayout="@xml/keys_comma_period" />
- <Key
- latin:keyStyle="emojiKeyStyle"
- latin:keyWidth="fillRight" />
-
</merge>
diff --git a/java/res/xml/rows_symbols.xml b/java/res/xml/rows_symbols.xml
index 3f102e277..d0606c63b 100644
--- a/java/res/xml/rows_symbols.xml
+++ b/java/res/xml/rows_symbols.xml
@@ -60,5 +60,8 @@
latin:keyWidth="15%p" />
<include
latin:keyboardLayout="@xml/row_symbols4" />
+ <Key
+ latin:keyStyle="enterKeyStyle"
+ latin:keyWidth="fillRight" />
</Row>
</merge>
diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsActivity.java b/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsActivity.java
index 684165240..c28d72949 100644
--- a/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsActivity.java
+++ b/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsActivity.java
@@ -24,6 +24,8 @@ import android.preference.PreferenceActivity;
* Preference screen.
*/
public final class DictionarySettingsActivity extends PreferenceActivity {
+ private static final String DEFAULT_FRAGMENT = DictionarySettingsFragment.class.getName();
+
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -32,11 +34,17 @@ public final class DictionarySettingsActivity extends PreferenceActivity {
@Override
public Intent getIntent() {
final Intent modIntent = new Intent(super.getIntent());
- modIntent.putExtra(EXTRA_SHOW_FRAGMENT, DictionarySettingsFragment.class.getName());
+ modIntent.putExtra(EXTRA_SHOW_FRAGMENT, DEFAULT_FRAGMENT);
modIntent.putExtra(EXTRA_NO_HEADERS, true);
// Important note : the original intent should contain a String extra with the key
// DictionarySettingsFragment.DICT_SETTINGS_FRAGMENT_CLIENT_ID_ARGUMENT so that the
// fragment can know who the client is.
return modIntent;
}
+
+ // TODO: Uncomment the override annotation once we start using SDK version 19.
+ // @Override
+ public boolean isValidFragment(String fragmentName) {
+ return fragmentName.equals(DEFAULT_FRAGMENT);
+ }
}
diff --git a/java/src/com/android/inputmethod/keyboard/EmojiLayoutParams.java b/java/src/com/android/inputmethod/keyboard/EmojiLayoutParams.java
index 267fad5cd..71790b7d6 100644
--- a/java/src/com/android/inputmethod/keyboard/EmojiLayoutParams.java
+++ b/java/src/com/android/inputmethod/keyboard/EmojiLayoutParams.java
@@ -75,9 +75,7 @@ public class EmojiLayoutParams {
public void setActionBarProperties(LinearLayout ll) {
final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) ll.getLayoutParams();
- lp.height = mEmojiActionBarHeight;
- lp.topMargin = 0;
- lp.bottomMargin = mBottomPadding;
+ lp.height = mEmojiActionBarHeight - mBottomPadding;
ll.setLayoutParams(lp);
}
diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java
index 3ea68806b..f7ec9509d 100644
--- a/java/src/com/android/inputmethod/keyboard/Key.java
+++ b/java/src/com/android/inputmethod/keyboard/Key.java
@@ -139,6 +139,8 @@ public class Key implements Comparable<Key> {
private final OptionalAttributes mOptionalAttributes;
+ private static final int DEFAULT_TEXT_COLOR = 0xFFFFFFFF;
+
private static final class OptionalAttributes {
/** Text to output when pressed. This can be multiple characters, like ".com" */
public final String mOutputText;
@@ -602,7 +604,22 @@ public class Key implements Comparable<Key> {
}
public final int selectTextColor(final KeyDrawParams params) {
- return isShiftedLetterActivated() ? params.mTextInactivatedColor : params.mTextColor;
+ if (isShiftedLetterActivated()) {
+ return params.mTextInactivatedColor;
+ }
+ if (params.mTextColorStateList == null) {
+ return DEFAULT_TEXT_COLOR;
+ }
+ final int[] state;
+ // TODO: Hack!!!!!!!! Consider having a new attribute for the functional text labels.
+ // Currently, we distinguish "input key" from "functional key" by checking the
+ // length of the label( > 1) and "functional" attributes (= true).
+ if (mLabel != null && mLabel.length() > 1) {
+ state = getCurrentDrawableState();
+ } else {
+ state = KEY_STATE_NORMAL;
+ }
+ return params.mTextColorStateList.getColorForState(state, DEFAULT_TEXT_COLOR);
}
public final int selectHintTextSize(final KeyDrawParams params) {
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index 74edd87cf..ad6e2c0f2 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -155,7 +155,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
}
public void saveKeyboardState() {
- if (getKeyboard() != null) {
+ if (getKeyboard() != null || isShowingEmojiKeyboard()) {
mState.onSaveKeyboardState();
}
}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyDrawParams.java b/java/src/com/android/inputmethod/keyboard/internal/KeyDrawParams.java
index 1716fa049..b528b692e 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyDrawParams.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyDrawParams.java
@@ -16,6 +16,7 @@
package com.android.inputmethod.keyboard.internal;
+import android.content.res.ColorStateList;
import android.graphics.Typeface;
import com.android.inputmethod.latin.utils.ResourceUtils;
@@ -32,7 +33,7 @@ public final class KeyDrawParams {
public int mHintLabelSize;
public int mPreviewTextSize;
- public int mTextColor;
+ public ColorStateList mTextColorStateList;
public int mTextInactivatedColor;
public int mTextShadowColor;
public int mHintLetterColor;
@@ -57,7 +58,7 @@ public final class KeyDrawParams {
mHintLabelSize = copyFrom.mHintLabelSize;
mPreviewTextSize = copyFrom.mPreviewTextSize;
- mTextColor = copyFrom.mTextColor;
+ mTextColorStateList = copyFrom.mTextColorStateList;
mTextInactivatedColor = copyFrom.mTextInactivatedColor;
mTextShadowColor = copyFrom.mTextShadowColor;
mHintLetterColor = copyFrom.mHintLetterColor;
@@ -89,8 +90,8 @@ public final class KeyDrawParams {
attr.mShiftedLetterHintRatio, mShiftedLetterHintSize);
mHintLabelSize = selectTextSize(keyHeight, attr.mHintLabelRatio, mHintLabelSize);
mPreviewTextSize = selectTextSize(keyHeight, attr.mPreviewTextRatio, mPreviewTextSize);
-
- mTextColor = selectColor(attr.mTextColor, mTextColor);
+ mTextColorStateList =
+ attr.mTextColorStateList != null ? attr.mTextColorStateList : mTextColorStateList;
mTextInactivatedColor = selectColor(attr.mTextInactivatedColor, mTextInactivatedColor);
mTextShadowColor = selectColor(attr.mTextShadowColor, mTextShadowColor);
mHintLetterColor = selectColor(attr.mHintLetterColor, mHintLetterColor);
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyVisualAttributes.java b/java/src/com/android/inputmethod/keyboard/internal/KeyVisualAttributes.java
index 7a2622cbb..8bdad364c 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyVisualAttributes.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyVisualAttributes.java
@@ -16,6 +16,7 @@
package com.android.inputmethod.keyboard.internal;
+import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Typeface;
import android.util.SparseIntArray;
@@ -37,7 +38,7 @@ public final class KeyVisualAttributes {
public final float mHintLabelRatio;
public final float mPreviewTextRatio;
- public final int mTextColor;
+ public final ColorStateList mTextColorStateList;
public final int mTextInactivatedColor;
public final int mTextShadowColor;
public final int mHintLetterColor;
@@ -115,7 +116,7 @@ public final class KeyVisualAttributes {
mPreviewTextRatio = ResourceUtils.getFraction(keyAttr,
R.styleable.Keyboard_Key_keyPreviewTextRatio);
- mTextColor = keyAttr.getColor(R.styleable.Keyboard_Key_keyTextColor, 0);
+ mTextColorStateList = keyAttr.getColorStateList(R.styleable.Keyboard_Key_keyTextColor);
mTextInactivatedColor = keyAttr.getColor(
R.styleable.Keyboard_Key_keyTextInactivatedColor, 0);
mTextShadowColor = keyAttr.getColor(R.styleable.Keyboard_Key_keyTextShadowColor, 0);
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java
index 9f9fdaa6f..506dfa751 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java
@@ -178,6 +178,8 @@ public final class KeyboardState {
if (!state.mIsAlphabetShiftLocked) {
setShifted(state.mShiftMode);
}
+ // TODO: is this the right place to do this? Should we do this in setShift* instead?
+ mSwitchActions.requestUpdatingShiftState();
} else {
mPrevMainKeyboardWasShiftLocked = state.mIsAlphabetShiftLocked;
}
diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
index 306c1a253..c79a4ff90 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
@@ -249,6 +249,9 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
final File file = new File(mContext.getFilesDir(), mFilename);
BinaryDictionary.createEmptyDictFile(file.getAbsolutePath(),
DICTIONARY_FORMAT_VERSION, getHeaderAttributeMap());
+ mBinaryDictionary = new BinaryDictionary(
+ file.getAbsolutePath(), 0 /* offset */, file.length(),
+ true /* useFullEditDistance */, null, mDictType, mIsUpdatable);
} else {
mDictionaryWriter.clear();
}
@@ -273,11 +276,26 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
lastModifiedTime);
}
- private void runGCIfRequired() {
+ /**
+ * Check whether GC is needed and run GC if required.
+ */
+ protected void runGCIfRequired(final boolean mindsBlockByGC) {
+ if (!ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) return;
+ getExecutor(mFilename).execute(new Runnable() {
+ @Override
+ public void run() {
+ runGCIfRequiredInternalLocked(mindsBlockByGC);
+ }
+ });
+ }
+
+ private void runGCIfRequiredInternalLocked(final boolean mindsBlockByGC) {
if (!ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) return;
- if (mBinaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) {
+ // Calls to needsToRunGC() need to be serialized.
+ if (mBinaryDictionary.needsToRunGC(mindsBlockByGC)) {
if (setIsRegeneratingIfNotRegenerating()) {
- getExecutor(mFilename).execute(new Runnable() {
+ // Run GC after currently existing time sensitive operations.
+ getExecutor(mFilename).executePrioritized(new Runnable() {
@Override
public void run() {
try {
@@ -300,11 +318,11 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
Log.w(TAG, "addWordDynamically is called for non-updatable dictionary: " + mFilename);
return;
}
- runGCIfRequired();
getExecutor(mFilename).execute(new Runnable() {
@Override
public void run() {
if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) {
+ runGCIfRequiredInternalLocked(true /* mindsBlockByGC */);
mBinaryDictionary.addUnigramWord(word, frequency);
} else {
// TODO: Remove.
@@ -324,11 +342,11 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
+ mFilename);
return;
}
- runGCIfRequired();
getExecutor(mFilename).execute(new Runnable() {
@Override
public void run() {
if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) {
+ runGCIfRequiredInternalLocked(true /* mindsBlockByGC */);
mBinaryDictionary.addBigramWords(word0, word1, frequency);
} else {
// TODO: Remove.
@@ -348,11 +366,11 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
+ mFilename);
return;
}
- runGCIfRequired();
getExecutor(mFilename).execute(new Runnable() {
@Override
public void run() {
if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) {
+ runGCIfRequiredInternalLocked(true /* mindsBlockByGC */);
mBinaryDictionary.removeBigramWords(word0, word1);
} else {
// TODO: Remove.
@@ -479,8 +497,8 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
final long length = file.length();
// Build the new binary dictionary
- final BinaryDictionary newBinaryDictionary = new BinaryDictionary(filename, 0, length,
- true /* useFullEditDistance */, null, mDictType, mIsUpdatable);
+ final BinaryDictionary newBinaryDictionary = new BinaryDictionary(filename, 0 /* offset */,
+ length, true /* useFullEditDistance */, null, mDictType, mIsUpdatable);
// Ensure all threads accessing the current dictionary have finished before
// swapping in the new one.
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 96e16de0d..0f3d28976 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -77,6 +77,7 @@ import com.android.inputmethod.keyboard.MainKeyboardView;
import com.android.inputmethod.latin.Suggest.OnGetSuggestedWordsCallback;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.define.ProductionFlag;
+import com.android.inputmethod.latin.personalization.DictionaryDecayBroadcastReciever;
import com.android.inputmethod.latin.personalization.PersonalizationDictionary;
import com.android.inputmethod.latin.personalization.PersonalizationDictionarySessionRegister;
import com.android.inputmethod.latin.personalization.PersonalizationHelper;
@@ -567,6 +568,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
newDictFilter.addAction(DictionaryPackConstants.NEW_DICTIONARY_INTENT_ACTION);
registerReceiver(mDictionaryPackInstallReceiver, newDictFilter);
+ DictionaryDecayBroadcastReciever.setUpIntervalAlarmForDictionaryDecaying(this);
+
mInputUpdater = new InputUpdater(this);
}
@@ -2929,6 +2932,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
return;
}
tryFixLyingCursorPosition();
+ mKeyboardSwitcher.updateShiftState();
if (tryResumeSuggestions) mHandler.postResumeSuggestions();
}
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java
index 665c7a27c..2c3d1346f 100644
--- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java
+++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java
@@ -295,7 +295,6 @@ public final class BinaryDictDecoderUtils {
return address;
}
}
- int address;
switch (optionFlags & FormatSpec.MASK_CHILDREN_ADDRESS_TYPE) {
case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_ONEBYTE:
return dictBuffer.readUnsignedByte();
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java
index af61f2979..b6024243f 100644
--- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java
+++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java
@@ -278,7 +278,6 @@ public class BinaryDictEncoderUtils {
// For future reference, the code to remove duplicate is a simple : list.remove(node);
list.add(ptNodeArray);
final ArrayList<PtNode> branches = ptNodeArray.mData;
- final int nodeSize = branches.size();
for (PtNode ptNode : branches) {
if (null != ptNode.mChildren) flattenTreeInner(list, ptNode.mChildren);
}
@@ -427,9 +426,6 @@ public class BinaryDictEncoderUtils {
nodeCountSize + nodeArrayOffset + nodeffset;
nodeffset += ptNode.mCachedSize;
}
- final int nodeSize = nodeCountSize + nodeffset
- + (formatOptions.mSupportsDynamicUpdate
- ? FormatSpec.FORWARD_LINK_ADDRESS_SIZE : 0);
nodeArrayOffset += nodeArray.mCachedSize;
}
return nodeArrayOffset;
@@ -653,8 +649,8 @@ public class BinaryDictEncoderUtils {
return flags;
}
- /* package */ static byte makePtNodeFlags(final PtNode node, final int ptNodeAddress,
- final int childrenOffset, final FormatOptions formatOptions) {
+ /* package */ static byte makePtNodeFlags(final PtNode node, final int childrenOffset,
+ final FormatOptions formatOptions) {
return (byte) makePtNodeFlags(node.mChars.length > 1, node.mFrequency >= 0,
getByteSize(childrenOffset),
node.mShortcutTargets != null && !node.mShortcutTargets.isEmpty(),
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
index e90137674..0f7d2f6c9 100644
--- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
+++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
@@ -288,42 +288,6 @@ public final class BinaryDictIOUtils {
return BinaryDictEncoderUtils.getByteSize(value);
}
- // TODO: Remove this method.
- @Deprecated
- static void skipPtNode(final DictBuffer dictBuffer, final FormatOptions formatOptions) {
- final int flags = dictBuffer.readUnsignedByte();
- BinaryDictDecoderUtils.readParentAddress(dictBuffer, formatOptions);
- skipString(dictBuffer, (flags & FormatSpec.FLAG_HAS_MULTIPLE_CHARS) != 0);
- BinaryDictDecoderUtils.readChildrenAddress(dictBuffer, flags, formatOptions);
- if ((flags & FormatSpec.FLAG_IS_TERMINAL) != 0) dictBuffer.readUnsignedByte();
- if ((flags & FormatSpec.FLAG_HAS_SHORTCUT_TARGETS) != 0) {
- final int shortcutsSize = dictBuffer.readUnsignedShort();
- dictBuffer.position(dictBuffer.position() + shortcutsSize
- - FormatSpec.PTNODE_SHORTCUT_LIST_SIZE_SIZE);
- }
- if ((flags & FormatSpec.FLAG_HAS_BIGRAMS) != 0) {
- int bigramCount = 0;
- while (bigramCount++ < FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) {
- final int bigramFlags = dictBuffer.readUnsignedByte();
- switch (bigramFlags & FormatSpec.MASK_BIGRAM_ATTR_ADDRESS_TYPE) {
- case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_ONEBYTE:
- dictBuffer.readUnsignedByte();
- break;
- case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_TWOBYTES:
- dictBuffer.readUnsignedShort();
- break;
- case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_THREEBYTES:
- dictBuffer.readUnsignedInt24();
- break;
- }
- if ((bigramFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT) == 0) break;
- }
- if (bigramCount >= FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) {
- throw new RuntimeException("Too many bigrams in a PtNode.");
- }
- }
- }
-
static void skipString(final DictBuffer dictBuffer,
final boolean hasMultipleChars) {
if (hasMultipleChars) {
diff --git a/java/src/com/android/inputmethod/latin/makedict/DictUpdater.java b/java/src/com/android/inputmethod/latin/makedict/DictUpdater.java
new file mode 100644
index 000000000..413d0301c
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/makedict/DictUpdater.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.makedict;
+
+import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * An interface of a binary dictionary updater.
+ */
+public interface DictUpdater {
+
+ /**
+ * Deletes the word from the binary dictionary.
+ *
+ * @param word the word to be deleted.
+ */
+ public void deleteWord(final String word) throws IOException, UnsupportedFormatException;
+
+ /**
+ * Inserts a word into a binary dictionary.
+ *
+ * @param word the word to be inserted.
+ * @param frequency the frequency of the new word.
+ * @param bigramStrings bigram list, or null if none.
+ * @param shortcuts shortcut list, or null if none.
+ * @param isBlackListEntry whether this should be a blacklist entry.
+ */
+ // TODO: Support batch insertion.
+ public void insertWord(final String word, final int frequency,
+ final ArrayList<WeightedString> bigramStrings,
+ final ArrayList<WeightedString> shortcuts, final boolean isNotAWord,
+ final boolean isBlackListEntry) throws IOException, UnsupportedFormatException;
+}
diff --git a/java/src/com/android/inputmethod/latin/makedict/DynamicBinaryDictIOUtils.java b/java/src/com/android/inputmethod/latin/makedict/DynamicBinaryDictIOUtils.java
index 411e265b3..336277196 100644
--- a/java/src/com/android/inputmethod/latin/makedict/DynamicBinaryDictIOUtils.java
+++ b/java/src/com/android/inputmethod/latin/makedict/DynamicBinaryDictIOUtils.java
@@ -42,44 +42,22 @@ public final class DynamicBinaryDictIOUtils {
// This utility class is not publicly instantiable.
}
- private static int markAsDeleted(final int flags) {
+ /* package */ static int markAsDeleted(final int flags) {
return (flags & (~FormatSpec.MASK_CHILDREN_ADDRESS_TYPE)) | FormatSpec.FLAG_IS_DELETED;
}
/**
- * Delete the word from the binary file.
- *
- * @param dictDecoder the dict decoder.
- * @param word the word we delete
- * @throws IOException
- * @throws UnsupportedFormatException
- */
- @UsedForTesting
- public static void deleteWord(final Ver3DictDecoder dictDecoder, final String word)
- throws IOException, UnsupportedFormatException {
- final DictBuffer dictBuffer = dictDecoder.getDictBuffer();
- dictBuffer.position(0);
- final FileHeader header = dictDecoder.readHeader();
- final int wordPosition = dictDecoder.getTerminalPosition(word);
- if (wordPosition == FormatSpec.NOT_VALID_WORD) return;
-
- dictBuffer.position(wordPosition);
- final int flags = dictBuffer.readUnsignedByte();
- dictBuffer.position(wordPosition);
- dictBuffer.put((byte)markAsDeleted(flags));
- }
-
- /**
* Update a parent address in a PtNode that is referred to by ptNodeOriginAddress.
*
- * @param dictBuffer the DictBuffer to write.
+ * @param dictUpdater the DictUpdater to write.
* @param ptNodeOriginAddress the address of the PtNode.
* @param newParentAddress the absolute address of the parent.
* @param formatOptions file format options.
*/
- private static void updateParentAddress(final DictBuffer dictBuffer,
+ private static void updateParentAddress(final Ver3DictUpdater dictUpdater,
final int ptNodeOriginAddress, final int newParentAddress,
final FormatOptions formatOptions) {
+ final DictBuffer dictBuffer = dictUpdater.getDictBuffer();
final int originalPosition = dictBuffer.position();
dictBuffer.position(ptNodeOriginAddress);
if (!formatOptions.mSupportsDynamicUpdate) {
@@ -104,46 +82,45 @@ public final class DynamicBinaryDictIOUtils {
/**
* Update parent addresses in a node array stored at ptNodeOriginAddress.
*
- * @param dictBuffer the DictBuffer to be modified.
+ * @param dictUpdater the DictUpdater to be modified.
* @param ptNodeOriginAddress the address of the node array to update.
* @param newParentAddress the address to be written.
* @param formatOptions file format options.
*/
- private static void updateParentAddresses(final DictBuffer dictBuffer,
+ private static void updateParentAddresses(final Ver3DictUpdater dictUpdater,
final int ptNodeOriginAddress, final int newParentAddress,
final FormatOptions formatOptions) {
- final int originalPosition = dictBuffer.position();
- dictBuffer.position(ptNodeOriginAddress);
+ final int originalPosition = dictUpdater.getPosition();
+ dictUpdater.setPosition(ptNodeOriginAddress);
do {
- final int count = BinaryDictDecoderUtils.readPtNodeCount(dictBuffer);
+ final int count = dictUpdater.readPtNodeCount();
for (int i = 0; i < count; ++i) {
- updateParentAddress(dictBuffer, dictBuffer.position(), newParentAddress,
+ updateParentAddress(dictUpdater, dictUpdater.getPosition(), newParentAddress,
formatOptions);
- BinaryDictIOUtils.skipPtNode(dictBuffer, formatOptions);
+ dictUpdater.skipPtNode(formatOptions);
}
- final int forwardLinkAddress = dictBuffer.readUnsignedInt24();
- dictBuffer.position(forwardLinkAddress);
- } while (formatOptions.mSupportsDynamicUpdate
- && dictBuffer.position() != FormatSpec.NO_FORWARD_LINK_ADDRESS);
- dictBuffer.position(originalPosition);
+ if (!dictUpdater.readAndFollowForwardLink()) break;
+ if (dictUpdater.getPosition() == FormatSpec.NO_FORWARD_LINK_ADDRESS) break;
+ } while (formatOptions.mSupportsDynamicUpdate);
+ dictUpdater.setPosition(originalPosition);
}
/**
* Update a children address in a PtNode that is addressed by ptNodeOriginAddress.
*
- * @param dictBuffer the DictBuffer to write.
+ * @param dictUpdater the DictUpdater to write.
* @param ptNodeOriginAddress the address of the PtNode.
* @param newChildrenAddress the absolute address of the child.
* @param formatOptions file format options.
*/
- private static void updateChildrenAddress(final DictBuffer dictBuffer,
+ private static void updateChildrenAddress(final Ver3DictUpdater dictUpdater,
final int ptNodeOriginAddress, final int newChildrenAddress,
final FormatOptions formatOptions) {
+ final DictBuffer dictBuffer = dictUpdater.getDictBuffer();
final int originalPosition = dictBuffer.position();
dictBuffer.position(ptNodeOriginAddress);
final int flags = dictBuffer.readUnsignedByte();
- final int parentAddress = BinaryDictDecoderUtils.readParentAddress(dictBuffer,
- formatOptions);
+ BinaryDictDecoderUtils.readParentAddress(dictBuffer, formatOptions);
BinaryDictIOUtils.skipString(dictBuffer, (flags & FormatSpec.FLAG_HAS_MULTIPLE_CHARS) != 0);
if ((flags & FormatSpec.FLAG_IS_TERMINAL) != 0) dictBuffer.readUnsignedByte();
final int childrenOffset = newChildrenAddress == FormatSpec.NO_CHILDREN_ADDRESS
@@ -156,31 +133,33 @@ public final class DynamicBinaryDictIOUtils {
* Helper method to move a PtNode to the tail of the file.
*/
private static int movePtNode(final OutputStream destination,
- final DictBuffer dictBuffer, final PtNodeInfo info,
+ final Ver3DictUpdater dictUpdater, final PtNodeInfo info,
final int nodeArrayOriginAddress, final int oldNodeAddress,
final FormatOptions formatOptions) throws IOException {
- updateParentAddress(dictBuffer, oldNodeAddress, dictBuffer.limit() + 1, formatOptions);
+ final DictBuffer dictBuffer = dictUpdater.getDictBuffer();
+ updateParentAddress(dictUpdater, oldNodeAddress, dictBuffer.limit() + 1, formatOptions);
dictBuffer.position(oldNodeAddress);
final int currentFlags = dictBuffer.readUnsignedByte();
dictBuffer.position(oldNodeAddress);
dictBuffer.put((byte)(FormatSpec.FLAG_IS_MOVED | (currentFlags
& (~FormatSpec.MASK_MOVE_AND_DELETE_FLAG))));
int size = FormatSpec.PTNODE_FLAGS_SIZE;
- updateForwardLink(dictBuffer, nodeArrayOriginAddress, dictBuffer.limit(), formatOptions);
+ updateForwardLink(dictUpdater, nodeArrayOriginAddress, dictBuffer.limit(), formatOptions);
size += BinaryDictIOUtils.writeNodes(destination, new PtNodeInfo[] { info });
return size;
}
@SuppressWarnings("unused")
- private static void updateForwardLink(final DictBuffer dictBuffer,
+ private static void updateForwardLink(final Ver3DictUpdater dictUpdater,
final int nodeArrayOriginAddress, final int newNodeArrayAddress,
final FormatOptions formatOptions) {
- dictBuffer.position(nodeArrayOriginAddress);
+ final DictBuffer dictBuffer = dictUpdater.getDictBuffer();
+ dictUpdater.setPosition(nodeArrayOriginAddress);
int jumpCount = 0;
while (jumpCount++ < MAX_JUMPS) {
- final int count = BinaryDictDecoderUtils.readPtNodeCount(dictBuffer);
+ final int count = dictUpdater.readPtNodeCount();
for (int i = 0; i < count; ++i) {
- BinaryDictIOUtils.skipPtNode(dictBuffer, formatOptions);
+ dictUpdater.readPtNode(dictUpdater.getPosition(), formatOptions);
}
final int forwardLinkAddress = dictBuffer.readUnsignedInt24();
if (forwardLinkAddress == FormatSpec.NO_FORWARD_LINK_ADDRESS) {
@@ -208,7 +187,7 @@ public final class DynamicBinaryDictIOUtils {
* @param shortcutTargets the shortcut targets for this PtNode.
* @param bigrams the bigrams for this PtNode.
* @param destination the stream representing the tail of the file.
- * @param dictBuffer the DictBuffer representing the (constant-size) body of the file.
+ * @param dictUpdater the DictUpdater.
* @param oldPtNodeArrayOrigin the origin of the old PtNode array this PtNode was a part of.
* @param oldPtNodeOrigin the old origin where this PtNode used to be stored.
* @param formatOptions format options for this dictionary.
@@ -219,7 +198,7 @@ public final class DynamicBinaryDictIOUtils {
final int length, final int flags, final int frequency, final int parentAddress,
final ArrayList<WeightedString> shortcutTargets,
final ArrayList<PendingAttribute> bigrams, final OutputStream destination,
- final DictBuffer dictBuffer, final int oldPtNodeArrayOrigin,
+ final Ver3DictUpdater dictUpdater, final int oldPtNodeArrayOrigin,
final int oldPtNodeOrigin, final FormatOptions formatOptions) throws IOException {
int size = 0;
final int newPtNodeOrigin = fileEndAddress + 1;
@@ -232,7 +211,7 @@ public final class DynamicBinaryDictIOUtils {
flags, writtenCharacters, frequency, parentAddress,
fileEndAddress + 1 + size + FormatSpec.FORWARD_LINK_ADDRESS_SIZE, shortcutTargets,
bigrams);
- movePtNode(destination, dictBuffer, newInfo, oldPtNodeArrayOrigin, oldPtNodeOrigin,
+ movePtNode(destination, dictUpdater, newInfo, oldPtNodeArrayOrigin, oldPtNodeOrigin,
formatOptions);
return 1 + size + FormatSpec.FORWARD_LINK_ADDRESS_SIZE;
}
@@ -240,7 +219,7 @@ public final class DynamicBinaryDictIOUtils {
/**
* Insert a word into a binary dictionary.
*
- * @param dictDecoder the dict decoder.
+ * @param dictUpdater the dict updater.
* @param destination a stream to the underlying file, with the pointer at the end of the file.
* @param word the word to insert.
* @param frequency the frequency of the new word.
@@ -253,17 +232,17 @@ public final class DynamicBinaryDictIOUtils {
// TODO: Support batch insertion.
// TODO: Remove @UsedForTesting once UserHistoryDictionary is implemented by BinaryDictionary.
@UsedForTesting
- public static void insertWord(final Ver3DictDecoder dictDecoder,
+ public static void insertWord(final Ver3DictUpdater dictUpdater,
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>();
- final DictBuffer dictBuffer = dictDecoder.getDictBuffer();
+ final DictBuffer dictBuffer = dictUpdater.getDictBuffer();
if (bigramStrings != null) {
for (final WeightedString bigram : bigramStrings) {
- int position = dictDecoder.getTerminalPosition(bigram.mWord);
+ int position = dictUpdater.getTerminalPosition(bigram.mWord);
if (position == FormatSpec.NOT_VALID_WORD) {
// TODO: figure out what is the correct thing to do here.
} else {
@@ -278,7 +257,7 @@ public final class DynamicBinaryDictIOUtils {
// find the insert position of the word.
if (dictBuffer.position() != 0) dictBuffer.position(0);
- final FileHeader fileHeader = dictDecoder.readHeader();
+ final FileHeader fileHeader = dictUpdater.readHeader();
int wordPos = 0, address = dictBuffer.position(), nodeOriginAddress = dictBuffer.position();
final int[] codePoints = FusionDictionary.getCodePoints(word);
@@ -293,7 +272,7 @@ public final class DynamicBinaryDictIOUtils {
for (int i = 0; i < ptNodeCount; ++i) {
address = dictBuffer.position();
- final PtNodeInfo currentInfo = dictDecoder.readPtNode(address,
+ final PtNodeInfo currentInfo = dictUpdater.readPtNode(address,
fileHeader.mFormatOptions);
final boolean isMovedNode = BinaryDictIOUtils.isMovedPtNode(currentInfo.mFlags,
fileHeader.mFormatOptions);
@@ -319,12 +298,12 @@ public final class DynamicBinaryDictIOUtils {
false /* isBlackListEntry */, fileHeader.mFormatOptions);
int written = movePtNode(newNodeAddress, currentInfo.mCharacters, p, flags,
frequency, nodeParentAddress, shortcuts, bigrams, destination,
- dictBuffer, nodeOriginAddress, address, fileHeader.mFormatOptions);
+ dictUpdater, nodeOriginAddress, address, fileHeader.mFormatOptions);
final int[] characters2 = Arrays.copyOfRange(currentInfo.mCharacters, p,
currentInfo.mCharacters.length);
if (currentInfo.mChildrenAddress != FormatSpec.NO_CHILDREN_ADDRESS) {
- updateParentAddresses(dictBuffer, currentInfo.mChildrenAddress,
+ updateParentAddresses(dictUpdater, currentInfo.mChildrenAddress,
newNodeAddress + written + 1, fileHeader.mFormatOptions);
}
final PtNodeInfo newInfo2 = new PtNodeInfo(
@@ -360,13 +339,13 @@ public final class DynamicBinaryDictIOUtils {
fileHeader.mFormatOptions);
int written = movePtNode(newNodeAddress, currentInfo.mCharacters, p,
prefixFlags, -1 /* frequency */, nodeParentAddress, null, null,
- destination, dictBuffer, nodeOriginAddress, address,
+ destination, dictUpdater, nodeOriginAddress, address,
fileHeader.mFormatOptions);
final int[] suffixCharacters = Arrays.copyOfRange(
currentInfo.mCharacters, p, currentInfo.mCharacters.length);
if (currentInfo.mChildrenAddress != FormatSpec.NO_CHILDREN_ADDRESS) {
- updateParentAddresses(dictBuffer, currentInfo.mChildrenAddress,
+ updateParentAddresses(dictUpdater, currentInfo.mChildrenAddress,
newNodeAddress + written + 1, fileHeader.mFormatOptions);
}
final int suffixFlags = BinaryDictEncoderUtils.makePtNodeFlags(
@@ -417,7 +396,7 @@ public final class DynamicBinaryDictIOUtils {
-1 /* endAddress */, flags, currentInfo.mCharacters, frequency,
nodeParentAddress, currentInfo.mChildrenAddress, shortcuts,
bigrams);
- movePtNode(destination, dictBuffer, newInfo, nodeOriginAddress, address,
+ movePtNode(destination, dictUpdater, newInfo, nodeOriginAddress, address,
fileHeader.mFormatOptions);
return;
}
@@ -436,7 +415,7 @@ public final class DynamicBinaryDictIOUtils {
* ab - cd - e
*/
final int newNodeArrayAddress = dictBuffer.limit();
- updateChildrenAddress(dictBuffer, address, newNodeArrayAddress,
+ updateChildrenAddress(dictUpdater, address, newNodeArrayAddress,
fileHeader.mFormatOptions);
final int newNodeAddress = newNodeArrayAddress + 1;
final boolean hasMultipleChars = (wordLen - wordPos) > 1;
diff --git a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
index 9481a8c14..a5516bd41 100644
--- a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
+++ b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
@@ -266,11 +266,14 @@ public final class FormatSpec {
// tat = Terminal Address Table
static final String TERMINAL_ADDRESS_TABLE_FILE_EXTENSION = ".tat";
static final String BIGRAM_FILE_EXTENSION = ".bigram";
- static final String BIGRAM_LOOKUP_TABLE_FILE_EXTENSION = ".bigram_lookup";
- static final String BIGRAM_ADDRESS_TABLE_FILE_EXTENSION = ".bigram_index";
+ static final String LOOKUP_TABLE_FILE_SUFFIX = "_lookup";
+ static final String CONTENT_TABLE_FILE_SUFFIX = "_index";
static final int FREQUENCY_AND_FLAGS_SIZE = 2;
static final int TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE = 3;
static final int BIGRAM_ADDRESS_TABLE_BLOCK_SIZE = 4;
+ static final int BIGRAM_CONTENT_COUNT = 1;
+ static final int BIGRAM_FREQ_CONTENT_INDEX = 0;
+ static final String BIGRAM_FREQ_CONTENT_ID = "_freq";
static final int NO_CHILDREN_ADDRESS = Integer.MIN_VALUE;
static final int NO_PARENT_ADDRESS = 0;
diff --git a/java/src/com/android/inputmethod/latin/makedict/SparseTable.java b/java/src/com/android/inputmethod/latin/makedict/SparseTable.java
index 96d057a44..7592a0c13 100644
--- a/java/src/com/android/inputmethod/latin/makedict/SparseTable.java
+++ b/java/src/com/android/inputmethod/latin/makedict/SparseTable.java
@@ -17,6 +17,7 @@
package com.android.inputmethod.latin.makedict;
import com.android.inputmethod.annotations.UsedForTesting;
+import com.android.inputmethod.latin.utils.CollectionUtils;
import java.io.File;
import java.io.FileInputStream;
@@ -37,35 +38,39 @@ public class SparseTable {
/**
* mLookupTable is indexed by terminal ID, containing exactly one entry for every mBlockSize
* terminals.
- * It contains at index i = j / mBlockSize the index in mContentsTable where the values for
- * terminals with IDs j to j + mBlockSize - 1 are stored as an mBlockSize-sized integer array.
+ * It contains at index i = j / mBlockSize the index in each ArrayList in mContentsTables where
+ * the values for terminals with IDs j to j + mBlockSize - 1 are stored as an mBlockSize-sized
+ * integer array.
*/
private final ArrayList<Integer> mLookupTable;
- private final ArrayList<Integer> mContentTable;
+ private final ArrayList<ArrayList<Integer>> mContentTables;
private final int mBlockSize;
+ private final int mContentTableCount;
public static final int NOT_EXIST = -1;
+ public static final int SIZE_OF_INT_IN_BYTES = 4;
@UsedForTesting
- public SparseTable(final int initialCapacity, final int blockSize) {
+ public SparseTable(final int initialCapacity, final int blockSize,
+ final int contentTableCount) {
mBlockSize = blockSize;
final int lookupTableSize = initialCapacity / mBlockSize
+ (initialCapacity % mBlockSize > 0 ? 1 : 0);
mLookupTable = new ArrayList<Integer>(Collections.nCopies(lookupTableSize, NOT_EXIST));
- mContentTable = new ArrayList<Integer>();
+ mContentTableCount = contentTableCount;
+ mContentTables = CollectionUtils.newArrayList();
+ for (int i = 0; i < mContentTableCount; ++i) {
+ mContentTables.add(new ArrayList<Integer>());
+ }
}
@UsedForTesting
- public SparseTable(final int[] lookupTable, final int[] contentTable, final int blockSize) {
+ public SparseTable(final ArrayList<Integer> lookupTable,
+ final ArrayList<ArrayList<Integer>> contentTables, final int blockSize) {
mBlockSize = blockSize;
- mLookupTable = new ArrayList<Integer>(lookupTable.length);
- for (int i = 0; i < lookupTable.length; ++i) {
- mLookupTable.add(lookupTable[i]);
- }
- mContentTable = new ArrayList<Integer>(contentTable.length);
- for (int i = 0; i < contentTable.length; ++i) {
- mContentTable.add(contentTable[i]);
- }
+ mContentTableCount = contentTables.size();
+ mLookupTable = lookupTable;
+ mContentTables = contentTables;
}
/**
@@ -75,8 +80,8 @@ public class SparseTable {
* Otherwise, IndexOutOfBoundsException will be raised.
*/
@UsedForTesting
- private static void convertByteArrayToIntegerArray(final byte[] byteArray,
- final ArrayList<Integer> integerArray) {
+ private static ArrayList<Integer> convertByteArrayToIntegerArray(final byte[] byteArray) {
+ final ArrayList<Integer> integerArray = new ArrayList<Integer>(byteArray.length / 4);
for (int i = 0; i < byteArray.length; i += 4) {
int value = 0;
for (int j = i; j < i + 4; ++j) {
@@ -85,39 +90,43 @@ public class SparseTable {
}
integerArray.add(value);
}
+ return integerArray;
}
@UsedForTesting
- public SparseTable(final byte[] lookupTable, final byte[] contentTable, final int blockSize) {
- mBlockSize = blockSize;
- mLookupTable = new ArrayList<Integer>(lookupTable.length / 4);
- mContentTable = new ArrayList<Integer>(contentTable.length / 4);
- convertByteArrayToIntegerArray(lookupTable, mLookupTable);
- convertByteArrayToIntegerArray(contentTable, mContentTable);
+ public int get(final int contentTableIndex, final int index) {
+ if (!contains(index)) {
+ return NOT_EXIST;
+ }
+ return mContentTables.get(contentTableIndex).get(
+ mLookupTable.get(index / mBlockSize) + (index % mBlockSize));
}
@UsedForTesting
- public int get(final int index) {
- if (index < 0 || index / mBlockSize >= mLookupTable.size()
- || mLookupTable.get(index / mBlockSize) == NOT_EXIST) {
- return NOT_EXIST;
+ public ArrayList<Integer> getAll(final int index) {
+ final ArrayList<Integer> ret = CollectionUtils.newArrayList();
+ for (int i = 0; i < mContentTableCount; ++i) {
+ ret.add(get(i, index));
}
- return mContentTable.get(mLookupTable.get(index / mBlockSize) + (index % mBlockSize));
+ return ret;
}
@UsedForTesting
- public void set(final int index, final int value) {
+ public void set(final int contentTableIndex, final int index, final int value) {
if (mLookupTable.get(index / mBlockSize) == NOT_EXIST) {
- mLookupTable.set(index / mBlockSize, mContentTable.size());
- for (int i = 0; i < mBlockSize; ++i) {
- mContentTable.add(NOT_EXIST);
+ mLookupTable.set(index / mBlockSize, mContentTables.get(contentTableIndex).size());
+ for (int i = 0; i < mContentTableCount; ++i) {
+ for (int j = 0; j < mBlockSize; ++j) {
+ mContentTables.get(i).add(NOT_EXIST);
+ }
}
}
- mContentTable.set(mLookupTable.get(index / mBlockSize) + (index % mBlockSize), value);
+ mContentTables.get(contentTableIndex).set(
+ mLookupTable.get(index / mBlockSize) + (index % mBlockSize), value);
}
- public void remove(final int index) {
- set(index, NOT_EXIST);
+ public void remove(final int indexOfContent, final int index) {
+ set(indexOfContent, index, NOT_EXIST);
}
@UsedForTesting
@@ -127,7 +136,8 @@ public class SparseTable {
@UsedForTesting
/* package */ int getContentTableSize() {
- return mContentTable.size();
+ // This class always has at least one content table.
+ return mContentTables.get(0).size();
}
@UsedForTesting
@@ -136,36 +146,51 @@ public class SparseTable {
}
public boolean contains(final int index) {
- return get(index) != NOT_EXIST;
+ if (index < 0 || index / mBlockSize >= mLookupTable.size()
+ || mLookupTable.get(index / mBlockSize) == NOT_EXIST) {
+ return false;
+ }
+ return true;
}
@UsedForTesting
- public void write(final OutputStream lookupOutStream, final OutputStream contentOutStream)
+ public void write(final OutputStream lookupOutStream, final OutputStream[] contentOutStreams)
throws IOException {
+ if (contentOutStreams.length != mContentTableCount) {
+ throw new RuntimeException(contentOutStreams.length + " streams are given, but the"
+ + " table has " + mContentTableCount + " content tables.");
+ }
for (final int index : mLookupTable) {
- BinaryDictEncoderUtils.writeUIntToStream(lookupOutStream, index, 4);
+ BinaryDictEncoderUtils.writeUIntToStream(lookupOutStream, index, SIZE_OF_INT_IN_BYTES);
}
- for (final int index : mContentTable) {
- BinaryDictEncoderUtils.writeUIntToStream(contentOutStream, index, 4);
+ for (int i = 0; i < contentOutStreams.length; ++i) {
+ for (final int data : mContentTables.get(i)) {
+ BinaryDictEncoderUtils.writeUIntToStream(contentOutStreams[i], data,
+ SIZE_OF_INT_IN_BYTES);
+ }
}
}
@UsedForTesting
- public void writeToFiles(final File lookupTableFile, final File contentFile)
+ public void writeToFiles(final File lookupTableFile, final File[] contentFiles)
throws IOException {
- FileOutputStream lookupTableOutStream = null;
- FileOutputStream contentOutStream = null;
+ FileOutputStream lookupTableOutStream = null;
+ final FileOutputStream[] contentTableOutStreams = new FileOutputStream[mContentTableCount];
try {
lookupTableOutStream = new FileOutputStream(lookupTableFile);
- contentOutStream = new FileOutputStream(contentFile);
- write(lookupTableOutStream, contentOutStream);
+ for (int i = 0; i < contentFiles.length; ++i) {
+ contentTableOutStreams[i] = new FileOutputStream(contentFiles[i]);
+ }
+ write(lookupTableOutStream, contentTableOutStreams);
} finally {
if (lookupTableOutStream != null) {
lookupTableOutStream.close();
}
- if (contentOutStream != null) {
- contentOutStream.close();
+ for (int i = 0; i < contentTableOutStreams.length; ++i) {
+ if (contentTableOutStreams[i] != null) {
+ contentTableOutStreams[i].close();
+ }
}
}
}
@@ -185,10 +210,14 @@ public class SparseTable {
}
@UsedForTesting
- public static SparseTable readFromFiles(final File lookupTableFile, final File contentFile,
+ public static SparseTable readFromFiles(final File lookupTableFile, final File[] contentFiles,
final int blockSize) throws IOException {
- final byte[] lookupTable = readFileToByteArray(lookupTableFile);
- final byte[] content = readFileToByteArray(contentFile);
- return new SparseTable(lookupTable, content, blockSize);
+ final ArrayList<ArrayList<Integer>> contentTables =
+ new ArrayList<ArrayList<Integer>>(contentFiles.length);
+ for (int i = 0; i < contentFiles.length; ++i) {
+ contentTables.add(convertByteArrayToIntegerArray(readFileToByteArray(contentFiles[i])));
+ }
+ return new SparseTable(convertByteArrayToIntegerArray(readFileToByteArray(lookupTableFile)),
+ contentTables, blockSize);
}
}
diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver3DictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver3DictDecoder.java
index 75d1058ad..b87259c38 100644
--- a/java/src/com/android/inputmethod/latin/makedict/Ver3DictDecoder.java
+++ b/java/src/com/android/inputmethod/latin/makedict/Ver3DictDecoder.java
@@ -53,9 +53,9 @@ public class Ver3DictDecoder extends DictDecoder {
}
}
- private final File mDictionaryBinaryFile;
+ protected final File mDictionaryBinaryFile;
private final DictionaryBufferFactory mBufferFactory;
- private DictBuffer mDictBuffer;
+ protected DictBuffer mDictBuffer;
/* package */ Ver3DictDecoder(final File file, final int factoryFlag) {
mDictionaryBinaryFile = file;
@@ -169,7 +169,8 @@ public class Ver3DictDecoder extends DictDecoder {
addressPointer += PtNodeReader.readBigramAddresses(mDictBuffer, bigrams,
addressPointer);
if (bigrams.size() >= FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) {
- MakedictLog.d("too many bigrams in a PtNode.");
+ throw new RuntimeException("Too many bigrams in a PtNode (" + bigrams.size()
+ + " but max is " + FormatSpec.MAX_BIGRAMS_IN_A_PTNODE + ")");
}
} else {
bigrams = null;
diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver3DictEncoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver3DictEncoder.java
index 76f0f4052..d9e19899c 100644
--- a/java/src/com/android/inputmethod/latin/makedict/Ver3DictEncoder.java
+++ b/java/src/com/android/inputmethod/latin/makedict/Ver3DictEncoder.java
@@ -133,12 +133,10 @@ public class Ver3DictEncoder implements DictEncoder {
countSize);
}
- private void writePtNodeFlags(final PtNode ptNode, final int parentAddress,
- final FormatOptions formatOptions) {
+ private void writePtNodeFlags(final PtNode ptNode, final FormatOptions formatOptions) {
final int childrenPos = BinaryDictEncoderUtils.getChildrenPosition(ptNode, formatOptions);
mPosition = BinaryDictEncoderUtils.writeUIntToBuffer(mBuffer, mPosition,
- BinaryDictEncoderUtils.makePtNodeFlags(ptNode, mPosition, childrenPos,
- formatOptions),
+ BinaryDictEncoderUtils.makePtNodeFlags(ptNode, childrenPos, formatOptions),
FormatSpec.PTNODE_FLAGS_SIZE);
}
@@ -244,7 +242,7 @@ public class Ver3DictEncoder implements DictEncoder {
@Override
public void writePtNode(final PtNode ptNode, final int parentPosition,
final FormatOptions formatOptions, final FusionDictionary dict) {
- writePtNodeFlags(ptNode, parentPosition, formatOptions);
+ writePtNodeFlags(ptNode, formatOptions);
writeParentPosition(parentPosition, ptNode, formatOptions);
writeCharacters(ptNode.mChars, ptNode.hasSeveralChars());
writeFrequency(ptNode.mFrequency);
diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver3DictUpdater.java b/java/src/com/android/inputmethod/latin/makedict/Ver3DictUpdater.java
new file mode 100644
index 000000000..fa7ae310a
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/makedict/Ver3DictUpdater.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.makedict;
+
+import com.android.inputmethod.annotations.UsedForTesting;
+import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+
+/**
+ * An implementation of DictUpdater for version 3 binary dictionary.
+ */
+@UsedForTesting
+public class Ver3DictUpdater extends Ver3DictDecoder implements DictUpdater {
+ private OutputStream mOutStream;
+
+ @UsedForTesting
+ public Ver3DictUpdater(final File dictFile, final int factoryType) {
+ // DictUpdater must have an updatable DictBuffer.
+ super(dictFile, ((factoryType & MASK_DICTBUFFER) == USE_BYTEARRAY)
+ ? USE_BYTEARRAY : USE_WRITABLE_BYTEBUFFER);
+ mOutStream = null;
+ }
+
+ private void openStreamAndBuffer() throws FileNotFoundException, IOException {
+ super.openDictBuffer();
+ mOutStream = new FileOutputStream(mDictionaryBinaryFile, true /* append */);
+ }
+
+ private void close() throws IOException {
+ if (mOutStream != null) {
+ mOutStream.close();
+ mOutStream = null;
+ }
+ }
+
+ @Override @UsedForTesting
+ public void deleteWord(final String word) throws IOException, UnsupportedFormatException {
+ if (mOutStream == null) openStreamAndBuffer();
+ mDictBuffer.position(0);
+ super.readHeader();
+ final int wordPos = getTerminalPosition(word);
+ if (wordPos != FormatSpec.NOT_VALID_WORD) {
+ mDictBuffer.position(wordPos);
+ final int flags = mDictBuffer.readUnsignedByte();
+ mDictBuffer.position(wordPos);
+ mDictBuffer.put((byte) DynamicBinaryDictIOUtils.markAsDeleted(flags));
+ }
+ close();
+ }
+
+ @Override @UsedForTesting
+ public void insertWord(final String word, final int frequency,
+ final ArrayList<WeightedString> bigramStrings,
+ final ArrayList<WeightedString> shortcuts,
+ final boolean isNotAWord, final boolean isBlackListEntry)
+ throws IOException, UnsupportedFormatException {
+ if (mOutStream == null) openStreamAndBuffer();
+ DynamicBinaryDictIOUtils.insertWord(this, mOutStream, word, frequency, bigramStrings,
+ shortcuts, isNotAWord, isBlackListEntry);
+ close();
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java
index fa19e2677..5089687da 100644
--- a/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java
+++ b/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java
@@ -42,7 +42,7 @@ public class Ver4DictDecoder extends DictDecoder {
private static final int FILETYPE_TRIE = 1;
private static final int FILETYPE_FREQUENCY = 2;
private static final int FILETYPE_TERMINAL_ADDRESS_TABLE = 3;
- private static final int FILETYPE_BIGRAM = 4;
+ private static final int FILETYPE_BIGRAM_FREQ = 4;
private final File mDictDirectory;
private final DictionaryBufferFactory mBufferFactory;
@@ -85,9 +85,10 @@ public class Ver4DictDecoder extends DictDecoder {
} else if (fileType == FILETYPE_TERMINAL_ADDRESS_TABLE) {
return new File(mDictDirectory,
mDictDirectory.getName() + FormatSpec.TERMINAL_ADDRESS_TABLE_FILE_EXTENSION);
- } else if (fileType == FILETYPE_BIGRAM) {
+ } else if (fileType == FILETYPE_BIGRAM_FREQ) {
return new File(mDictDirectory,
- mDictDirectory.getName() + FormatSpec.BIGRAM_FILE_EXTENSION);
+ mDictDirectory.getName() + FormatSpec.BIGRAM_FILE_EXTENSION
+ + FormatSpec.BIGRAM_FREQ_CONTENT_ID);
} else {
throw new RuntimeException("Unsupported kind of file : " + fileType);
}
@@ -95,12 +96,11 @@ public class Ver4DictDecoder extends DictDecoder {
@Override
public void openDictBuffer() throws FileNotFoundException, IOException {
- final String filename = mDictDirectory.getName();
mDictBuffer = mBufferFactory.getDictionaryBuffer(getFile(FILETYPE_TRIE));
mFrequencyBuffer = mBufferFactory.getDictionaryBuffer(getFile(FILETYPE_FREQUENCY));
mTerminalAddressTableBuffer = mBufferFactory.getDictionaryBuffer(
getFile(FILETYPE_TERMINAL_ADDRESS_TABLE));
- mBigramBuffer = mBufferFactory.getDictionaryBuffer(getFile(FILETYPE_BIGRAM));
+ mBigramBuffer = mBufferFactory.getDictionaryBuffer(getFile(FILETYPE_BIGRAM_FREQ));
loadBigramAddressSparseTable();
}
@@ -127,11 +127,12 @@ public class Ver4DictDecoder extends DictDecoder {
}
private void loadBigramAddressSparseTable() throws IOException {
- final File lookupIndexFile = new File(mDictDirectory,
- mDictDirectory.getName() + FormatSpec.BIGRAM_LOOKUP_TABLE_FILE_EXTENSION);
- final File contentFile = new File(mDictDirectory,
- mDictDirectory.getName() + FormatSpec.BIGRAM_ADDRESS_TABLE_FILE_EXTENSION);
- mBigramAddressTable = SparseTable.readFromFiles(lookupIndexFile, contentFile,
+ final File lookupIndexFile = new File(mDictDirectory, mDictDirectory.getName()
+ + FormatSpec.BIGRAM_FILE_EXTENSION + FormatSpec.LOOKUP_TABLE_FILE_SUFFIX);
+ final File freqsFile = new File(mDictDirectory, mDictDirectory.getName()
+ + FormatSpec.BIGRAM_FILE_EXTENSION + FormatSpec.CONTENT_TABLE_FILE_SUFFIX
+ + FormatSpec.BIGRAM_FREQ_CONTENT_ID);
+ mBigramAddressTable = SparseTable.readFromFiles(lookupIndexFile, new File[] { freqsFile },
FormatSpec.BIGRAM_ADDRESS_TABLE_BLOCK_SIZE);
}
@@ -208,7 +209,7 @@ public class Ver4DictDecoder extends DictDecoder {
final ArrayList<PendingAttribute> bigrams;
if (0 != (flags & FormatSpec.FLAG_HAS_BIGRAMS)) {
bigrams = new ArrayList<PendingAttribute>();
- final int posOfBigrams = mBigramAddressTable.get(terminalId);
+ final int posOfBigrams = mBigramAddressTable.get(0 /* contentTableIndex */, terminalId);
mBigramBuffer.position(posOfBigrams);
while (bigrams.size() < FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) {
// If bigrams.size() reaches FormatSpec.MAX_BIGRAMS_IN_A_PTNODE,
@@ -224,7 +225,8 @@ public class Ver4DictDecoder extends DictDecoder {
if (0 == (bigramFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT)) break;
}
if (bigrams.size() >= FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) {
- MakedictLog.d("too many bigrams in a node.");
+ throw new RuntimeException("Too many bigrams in a PtNode (" + bigrams.size()
+ + " but max is " + FormatSpec.MAX_BIGRAMS_IN_A_PTNODE + ")");
}
} else {
bigrams = null;
diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java
index 4c25faf88..b38c33019 100644
--- a/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java
+++ b/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java
@@ -26,7 +26,6 @@ import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray;
import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
-import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
@@ -44,19 +43,115 @@ public class Ver4DictEncoder implements DictEncoder {
private byte[] mTrieBuf;
private int mTriePos;
private int mHeaderSize;
- private SparseTable mBigramAddressTable;
private OutputStream mTrieOutStream;
private OutputStream mFreqOutStream;
private OutputStream mTerminalAddressTableOutStream;
- private OutputStream mBigramOutStream;
private File mDictDir;
private String mBaseFilename;
+ private BigramContentWriter mBigramWriter;
@UsedForTesting
public Ver4DictEncoder(final File dictPlacedDir) {
mDictPlacedDir = dictPlacedDir;
}
+ private interface SparseTableContentWriterInterface {
+ public void write(final OutputStream outStream) throws IOException;
+ }
+
+ private static class SparseTableContentWriter {
+ private final int mContentCount;
+ private final SparseTable mSparseTable;
+ private final File mLookupTableFile;
+ protected final File mBaseDir;
+ private final File[] mAddressTableFiles;
+ private final File[] mContentFiles;
+ protected final OutputStream[] mContentOutStreams;
+
+ public SparseTableContentWriter(final String name, final int contentCount,
+ final int initialCapacity, final int blockSize, final File baseDir,
+ final String[] contentFilenames, final String[] contentIds) {
+ if (contentFilenames.length != contentIds.length) {
+ throw new RuntimeException("The length of contentFilenames and the length of"
+ + " contentIds are different " + contentFilenames.length + ", "
+ + contentIds.length);
+ }
+ mContentCount = contentCount;
+ mSparseTable = new SparseTable(initialCapacity, blockSize, contentCount);
+ mLookupTableFile = new File(baseDir, name + FormatSpec.LOOKUP_TABLE_FILE_SUFFIX);
+ mAddressTableFiles = new File[mContentCount];
+ mContentFiles = new File[mContentCount];
+ mBaseDir = baseDir;
+ for (int i = 0; i < mContentCount; ++i) {
+ mAddressTableFiles[i] = new File(mBaseDir,
+ name + FormatSpec.CONTENT_TABLE_FILE_SUFFIX + contentIds[i]);
+ mContentFiles[i] = new File(mBaseDir, contentFilenames[i] + contentIds[i]);
+ }
+ mContentOutStreams = new OutputStream[mContentCount];
+ }
+
+ public void openStreams() throws FileNotFoundException {
+ for (int i = 0; i < mContentCount; ++i) {
+ mContentOutStreams[i] = new FileOutputStream(mContentFiles[i]);
+ }
+ }
+
+ protected void write(final int contentIndex, final int index,
+ final SparseTableContentWriterInterface writer) throws IOException {
+ mSparseTable.set(contentIndex, index, (int) mContentFiles[contentIndex].length());
+ writer.write(mContentOutStreams[contentIndex]);
+ mContentOutStreams[contentIndex].flush();
+ }
+
+ public void closeStreams() throws IOException {
+ mSparseTable.writeToFiles(mLookupTableFile, mAddressTableFiles);
+ for (int i = 0; i < mContentCount; ++i) {
+ mContentOutStreams[i].close();
+ }
+ }
+ }
+
+ private static class BigramContentWriter extends SparseTableContentWriter {
+
+ public BigramContentWriter(final String name, final int initialCapacity,
+ final File baseDir) {
+ super(name + FormatSpec.BIGRAM_FILE_EXTENSION, FormatSpec.BIGRAM_CONTENT_COUNT,
+ initialCapacity, FormatSpec.BIGRAM_ADDRESS_TABLE_BLOCK_SIZE, baseDir,
+ new String[] { name + FormatSpec.BIGRAM_FILE_EXTENSION },
+ new String[] { FormatSpec.BIGRAM_FREQ_CONTENT_ID });
+ }
+
+ public void writeBigramsForOneWord(final int terminalId,
+ final Iterator<WeightedString> bigramIterator, final FusionDictionary dict)
+ throws IOException {
+ write(FormatSpec.BIGRAM_FREQ_CONTENT_INDEX, terminalId,
+ new SparseTableContentWriterInterface() {
+ @Override
+ public void write(final OutputStream outStream) throws IOException {
+ writeBigramsForOneWordInternal(outStream, bigramIterator, dict);
+ }
+ });
+ }
+
+ private void writeBigramsForOneWordInternal(final OutputStream outStream,
+ final Iterator<WeightedString> bigramIterator, final FusionDictionary dict)
+ throws IOException {
+ while (bigramIterator.hasNext()) {
+ final WeightedString bigram = bigramIterator.next();
+ final PtNode target =
+ FusionDictionary.findWordInTree(dict.mRootNodeArray, bigram.mWord);
+ final int unigramFrequencyForThisWord = target.mFrequency;
+ final int bigramFlags = BinaryDictEncoderUtils.makeBigramFlags(
+ bigramIterator.hasNext(), 0, bigram.mFrequency,
+ unigramFrequencyForThisWord, bigram.mWord);
+ BinaryDictEncoderUtils.writeUIntToStream(outStream, bigramFlags,
+ FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE);
+ BinaryDictEncoderUtils.writeUIntToStream(outStream, target.mTerminalId,
+ FormatSpec.PTNODE_ATTRIBUTE_MAX_ADDRESS_SIZE);
+ }
+ }
+ }
+
private void openStreams(final FormatOptions formatOptions, final DictionaryOptions dictOptions)
throws FileNotFoundException, IOException {
final FileHeader header = new FileHeader(0, dictOptions, formatOptions);
@@ -66,8 +161,6 @@ public class Ver4DictEncoder implements DictEncoder {
final File freqFile = new File(mDictDir, mBaseFilename + FormatSpec.FREQ_FILE_EXTENSION);
final File terminalAddressTableFile = new File(mDictDir,
mBaseFilename + FormatSpec.TERMINAL_ADDRESS_TABLE_FILE_EXTENSION);
- final File bigramFile = new File(mDictDir,
- mBaseFilename + FormatSpec.BIGRAM_FILE_EXTENSION);
if (!mDictDir.isDirectory()) {
if (mDictDir.exists()) mDictDir.delete();
mDictDir.mkdirs();
@@ -78,7 +171,6 @@ public class Ver4DictEncoder implements DictEncoder {
mTrieOutStream = new FileOutputStream(trieFile);
mFreqOutStream = new FileOutputStream(freqFile);
mTerminalAddressTableOutStream = new FileOutputStream(terminalAddressTableFile);
- mBigramOutStream = new FileOutputStream(bigramFile);
}
private void close() throws IOException {
@@ -92,14 +184,10 @@ public class Ver4DictEncoder implements DictEncoder {
if (mTerminalAddressTableOutStream != null) {
mTerminalAddressTableOutStream.close();
}
- if (mBigramOutStream != null) {
- mBigramOutStream.close();
- }
} finally {
mTrieOutStream = null;
mFreqOutStream = null;
mTerminalAddressTableOutStream = null;
- mBigramOutStream = null;
}
}
@@ -135,10 +223,8 @@ public class Ver4DictEncoder implements DictEncoder {
if (MakedictLog.DBG) BinaryDictEncoderUtils.checkFlatPtNodeArrayList(flatNodes);
writeTerminalData(flatNodes, terminalCount);
- mBigramAddressTable = new SparseTable(terminalCount,
- FormatSpec.BIGRAM_ADDRESS_TABLE_BLOCK_SIZE);
+ mBigramWriter = new BigramContentWriter(mBaseFilename, terminalCount, mDictDir);
writeBigrams(flatNodes, dict);
- writeBigramAddressSparseTable();
final PtNodeArray lastNodeArray = flatNodes.get(flatNodes.size() - 1);
final int bufferSize = lastNodeArray.mCachedAddressAfterUpdate + lastNodeArray.mCachedSize;
@@ -181,12 +267,10 @@ public class Ver4DictEncoder implements DictEncoder {
countSize);
}
- private void writePtNodeFlags(final PtNode ptNode, final int parentAddress,
- final FormatOptions formatOptions) {
+ private void writePtNodeFlags(final PtNode ptNode, final FormatOptions formatOptions) {
final int childrenPos = BinaryDictEncoderUtils.getChildrenPosition(ptNode, formatOptions);
mTriePos = BinaryDictEncoderUtils.writeUIntToBuffer(mTrieBuf, mTriePos,
- BinaryDictEncoderUtils.makePtNodeFlags(ptNode, mTriePos, childrenPos,
- formatOptions),
+ BinaryDictEncoderUtils.makePtNodeFlags(ptNode, childrenPos, formatOptions),
FormatSpec.PTNODE_FLAGS_SIZE);
}
@@ -231,8 +315,7 @@ public class Ver4DictEncoder implements DictEncoder {
while (shortcutIterator.hasNext()) {
final WeightedString target = shortcutIterator.next();
final int shortcutFlags = BinaryDictEncoderUtils.makeShortcutFlags(
- shortcutIterator.hasNext(),
- target.mFrequency);
+ shortcutIterator.hasNext(), target.mFrequency);
mTrieBuf[mTriePos++] = (byte)shortcutFlags;
final int shortcutShift = CharEncoding.writeString(mTrieBuf, mTriePos,
target.mWord);
@@ -248,39 +331,16 @@ public class Ver4DictEncoder implements DictEncoder {
private void writeBigrams(final ArrayList<PtNodeArray> flatNodes, final FusionDictionary dict)
throws IOException {
- final ByteArrayOutputStream bigramBuffer = new ByteArrayOutputStream();
-
+ mBigramWriter.openStreams();
for (final PtNodeArray nodeArray : flatNodes) {
for (final PtNode ptNode : nodeArray.mData) {
if (ptNode.mBigrams != null) {
- final int startPos = bigramBuffer.size();
- mBigramAddressTable.set(ptNode.mTerminalId, startPos);
- final Iterator<WeightedString> bigramIterator = ptNode.mBigrams.iterator();
- while (bigramIterator.hasNext()) {
- final WeightedString bigram = bigramIterator.next();
- final PtNode target =
- FusionDictionary.findWordInTree(dict.mRootNodeArray, bigram.mWord);
- final int unigramFrequencyForThisWord = target.mFrequency;
- final int bigramFlags = BinaryDictEncoderUtils.makeBigramFlags(
- bigramIterator.hasNext(), 0, bigram.mFrequency,
- unigramFrequencyForThisWord, bigram.mWord);
- BinaryDictEncoderUtils.writeUIntToStream(bigramBuffer, bigramFlags,
- FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE);
- BinaryDictEncoderUtils.writeUIntToStream(bigramBuffer, target.mTerminalId,
- FormatSpec.PTNODE_ATTRIBUTE_MAX_ADDRESS_SIZE);
- }
+ mBigramWriter.writeBigramsForOneWord(ptNode.mTerminalId,
+ ptNode.mBigrams.iterator(), dict);
}
}
}
- bigramBuffer.writeTo(mBigramOutStream);
- }
-
- private void writeBigramAddressSparseTable() throws IOException {
- final File lookupIndexFile =
- new File(mDictDir, mBaseFilename + FormatSpec.BIGRAM_LOOKUP_TABLE_FILE_EXTENSION);
- final File contentFile =
- new File(mDictDir, mBaseFilename + FormatSpec.BIGRAM_ADDRESS_TABLE_FILE_EXTENSION);
- mBigramAddressTable.writeToFiles(lookupIndexFile, contentFile);
+ mBigramWriter.closeStreams();
}
@Override
@@ -292,7 +352,7 @@ public class Ver4DictEncoder implements DictEncoder {
@Override
public void writePtNode(final PtNode ptNode, final int parentPosition,
final FormatOptions formatOptions, final FusionDictionary dict) {
- writePtNodeFlags(ptNode, parentPosition, formatOptions);
+ writePtNodeFlags(ptNode, formatOptions);
writeParentPosition(parentPosition, ptNode, formatOptions);
writeCharacters(ptNode.mChars, ptNode.hasSeveralChars());
if (ptNode.isTerminal()) {
diff --git a/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java b/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java
index 7cf4f0c88..c8b62b6c8 100644
--- a/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java
+++ b/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java
@@ -230,10 +230,15 @@ public abstract class DecayingExpandableBinaryDictionaryBase extends ExpandableB
mSessions.remove(session);
}
+ @UsedForTesting
public void clearAndFlushDictionary() {
// Clear the node structure on memory
clear();
// Then flush the cleared state of the dictionary on disk.
asyncFlashAllBinaryDictionary();
}
+
+ /* package */ void decayIfNeeded() {
+ runGCIfRequired(false /* mindsBlockByGC */);
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/personalization/DictionaryDecayBroadcastReciever.java b/java/src/com/android/inputmethod/latin/personalization/DictionaryDecayBroadcastReciever.java
new file mode 100644
index 000000000..e9ca662e7
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/personalization/DictionaryDecayBroadcastReciever.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.personalization;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Broadcast receiver for periodically updating decaying dictionaries.
+ */
+public class DictionaryDecayBroadcastReciever extends BroadcastReceiver {
+ /**
+ * The root domain for the personalization.
+ */
+ private static final String PERSONALIZATION_DOMAIN =
+ "com.android.inputmethod.latin.personalization";
+
+ /**
+ * The action of the intent to tell the time to decay dictionaries.
+ */
+ private static final String DICTIONARY_DECAY_INTENT_ACTION =
+ PERSONALIZATION_DOMAIN + ".DICT_DECAY";
+
+ /**
+ * Interval to update for decaying dictionaries.
+ */
+ private static final long DICTIONARY_DECAY_INTERVAL = TimeUnit.MINUTES.toMillis(60);
+
+ public static void setUpIntervalAlarmForDictionaryDecaying(Context context) {
+ AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
+ final Intent updateIntent = new Intent(DICTIONARY_DECAY_INTENT_ACTION);
+ updateIntent.setClass(context, DictionaryDecayBroadcastReciever.class);
+ final long alarmTime = System.currentTimeMillis() + DICTIONARY_DECAY_INTERVAL;
+ final PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0 /* requestCode */,
+ updateIntent, PendingIntent.FLAG_CANCEL_CURRENT);
+ if (null != alarmManager) alarmManager.setInexactRepeating(AlarmManager.RTC,
+ alarmTime, DICTIONARY_DECAY_INTERVAL, pendingIntent);
+ }
+
+ @Override
+ public void onReceive(final Context context, final Intent intent) {
+ final String action = intent.getAction();
+ if (action.equals(DICTIONARY_DECAY_INTENT_ACTION)) {
+ PersonalizationHelper.tryDecayingAllOpeningUserHistoryDictionary();
+ }
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java
index 8c9484b12..221ddeeba 100644
--- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java
+++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java
@@ -29,7 +29,6 @@ import java.util.concurrent.ConcurrentHashMap;
public class PersonalizationHelper {
private static final String TAG = PersonalizationHelper.class.getSimpleName();
private static final boolean DEBUG = false;
-
private static final ConcurrentHashMap<String, SoftReference<UserHistoryDictionary>>
sLangUserHistoryDictCache = CollectionUtils.newConcurrentHashMap();
@@ -62,6 +61,18 @@ public class PersonalizationHelper {
}
}
+ public static void tryDecayingAllOpeningUserHistoryDictionary() {
+ for (final ConcurrentHashMap.Entry<String, SoftReference<UserHistoryDictionary>> entry
+ : sLangUserHistoryDictCache.entrySet()) {
+ if (entry.getValue() != null) {
+ final UserHistoryDictionary dict = entry.getValue().get();
+ if (dict != null) {
+ dict.decayIfNeeded();
+ }
+ }
+ }
+ }
+
public static void registerPersonalizationDictionaryUpdateSession(final Context context,
final PersonalizationDictionaryUpdateSession session, String locale) {
final PersonalizationPredictionDictionary predictionDictionary =
diff --git a/java/src/com/android/inputmethod/latin/settings/DebugSettingsActivity.java b/java/src/com/android/inputmethod/latin/settings/DebugSettingsActivity.java
index b499c26b6..ef6ab2a38 100644
--- a/java/src/com/android/inputmethod/latin/settings/DebugSettingsActivity.java
+++ b/java/src/com/android/inputmethod/latin/settings/DebugSettingsActivity.java
@@ -38,4 +38,10 @@ public final class DebugSettingsActivity extends PreferenceActivity {
super.onCreate(savedInstanceState);
setTitle(R.string.english_ime_debug_settings);
}
+
+ // TODO: Uncomment the override annotation once we start using SDK version 19.
+ // @Override
+ public boolean isValidFragment(String fragmentName) {
+ return fragmentName.equals(DEFAULT_FRAGMENT);
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsActivity.java b/java/src/com/android/inputmethod/latin/settings/SettingsActivity.java
index 6c3818651..ad68f8c37 100644
--- a/java/src/com/android/inputmethod/latin/settings/SettingsActivity.java
+++ b/java/src/com/android/inputmethod/latin/settings/SettingsActivity.java
@@ -32,4 +32,10 @@ public final class SettingsActivity extends PreferenceActivity {
intent.putExtra(EXTRA_NO_HEADERS, true);
return intent;
}
+
+ // TODO: Uncomment the override annotation once we start using SDK version 19.
+ // @Override
+ public boolean isValidFragment(String fragmentName) {
+ return fragmentName.equals(DEFAULT_FRAGMENT);
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsActivity.java b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsActivity.java
index 119ca4755..aba563746 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsActivity.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/SpellCheckerSettingsActivity.java
@@ -24,6 +24,8 @@ import android.preference.PreferenceActivity;
* Spell checker preference screen.
*/
public final class SpellCheckerSettingsActivity extends PreferenceActivity {
+ private static final String DEFAULT_FRAGMENT = SpellCheckerSettingsFragment.class.getName();
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -32,8 +34,14 @@ public final class SpellCheckerSettingsActivity extends PreferenceActivity {
@Override
public Intent getIntent() {
final Intent modIntent = new Intent(super.getIntent());
- modIntent.putExtra(EXTRA_SHOW_FRAGMENT, SpellCheckerSettingsFragment.class.getName());
+ modIntent.putExtra(EXTRA_SHOW_FRAGMENT, DEFAULT_FRAGMENT);
modIntent.putExtra(EXTRA_NO_HEADERS, true);
return modIntent;
}
+
+ // TODO: Uncomment the override annotation once we start using SDK version 19.
+ // @Override
+ public boolean isValidFragment(String fragmentName) {
+ return fragmentName.equals(DEFAULT_FRAGMENT);
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java b/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java
index 44b201642..ff332cdee 100644
--- a/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/AdditionalSubtypeUtils.java
@@ -61,10 +61,8 @@ public final class AdditionalSubtypeUtils {
StringUtils.appendToCommaSplittableTextIfNotExists(
IS_ADDITIONAL_SUBTYPE, layoutDisplayNameExtraValue);
final int nameId = SubtypeLocaleUtils.getSubtypeNameId(localeString, keyboardLayoutSetName);
- return new InputMethodSubtype(nameId, R.drawable.ic_ime_switcher_dark,
- localeString, KEYBOARD_MODE, layoutExtraValue + "," + additionalSubtypeExtraValue
- + "," + Constants.Subtype.ExtraValue.ASCII_CAPABLE
- + "," + Constants.Subtype.ExtraValue.EMOJI_CAPABLE, false, false);
+ return buildInputMethodSubtype(
+ nameId, localeString, layoutExtraValue, additionalSubtypeExtraValue);
}
public static String getPrefSubtype(final InputMethodSubtype subtype) {
@@ -137,4 +135,27 @@ public final class AdditionalSubtypeUtils {
}
return sb.toString();
}
+
+ private static InputMethodSubtype buildInputMethodSubtype(int nameId, String localeString,
+ String layoutExtraValue, String additionalSubtypeExtraValue) {
+ // CAVEAT! If you want to change subtypeId after changing the extra values,
+ // you must change "getInputMethodSubtypeId". But it will remove the additional keyboard
+ // from the current users. So, you should be really careful to change it.
+ final int subtypeId = getInputMethodSubtypeId(nameId, localeString, layoutExtraValue,
+ additionalSubtypeExtraValue);
+ // TODO: Use InputMethodSubtypeBuilder once we use SDK version 19.
+ return new InputMethodSubtype(nameId, R.drawable.ic_ime_switcher_dark,
+ localeString, KEYBOARD_MODE, layoutExtraValue + "," + additionalSubtypeExtraValue
+ + "," + Constants.Subtype.ExtraValue.ASCII_CAPABLE
+ + "," + Constants.Subtype.ExtraValue.EMOJI_CAPABLE, false, false,
+ subtypeId);
+ }
+
+ private static int getInputMethodSubtypeId(int nameId, String localeString,
+ String layoutExtraValue, String additionalSubtypeExtraValue) {
+ // TODO: Use InputMethodSubtypeBuilder once we use SDK version 19.
+ return (new InputMethodSubtype(nameId, R.drawable.ic_ime_switcher_dark,
+ localeString, KEYBOARD_MODE, layoutExtraValue + "," + additionalSubtypeExtraValue,
+ false, false)).hashCode();
+ }
}
diff --git a/native/jni/Android.mk b/native/jni/Android.mk
index 36afea54b..ca6a77997 100644
--- a/native/jni/Android.mk
+++ b/native/jni/Android.mk
@@ -85,8 +85,8 @@ LATIN_IME_CORE_SRC_FILES := \
$(addprefix suggest/policyimpl/dictionary/utils/, \
buffer_with_extendable_buffer.cpp \
byte_array_utils.cpp \
- decaying_utils.cpp \
dict_file_writing_utils.cpp \
+ forgetting_curve_utils.cpp \
format_utils.cpp) \
suggest/policyimpl/gesture/gesture_suggest_policy_factory.cpp \
$(addprefix suggest/policyimpl/typing/, \
diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
index 38159b0f3..8f21c50ec 100644
--- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
+++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
@@ -67,7 +67,6 @@ static jboolean latinime_BinaryDictionary_createEmptyDictFile(JNIEnv *env, jclas
valueChars[valueUtf8Length] = '\0';
HeaderReadWriteUtils::AttributeMap::mapped_type value;
HeaderReadWriteUtils::insertCharactersIntoVector(valueChars, &value);
-
attributeMap[key] = value;
}
diff --git a/native/jni/src/suggest/policyimpl/dictionary/bigram/dynamic_bigram_list_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/bigram/dynamic_bigram_list_policy.cpp
index 67a085de3..8753c6eb0 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/bigram/dynamic_bigram_list_policy.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/bigram/dynamic_bigram_list_policy.cpp
@@ -20,7 +20,7 @@
#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_node_reader.h"
#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.h"
#include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h"
-#include "suggest/policyimpl/dictionary/utils/decaying_utils.h"
+#include "suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h"
namespace latinime {
@@ -43,7 +43,7 @@ void DynamicBigramListPolicy::getNextBigram(int *const outBigramPos, int *const
}
*outProbability = BigramListReadWriteUtils::getProbabilityFromFlags(bigramFlags);
*outHasNext = BigramListReadWriteUtils::hasNext(bigramFlags);
- if (mIsDecayingDict && !DecayingUtils::isValidBigram(*outProbability)) {
+ if (mIsDecayingDict && !ForgettingCurveUtils::isValidEncodedProbability(*outProbability)) {
// This bigram is too weak to output.
*outBigramPos = NOT_A_DICT_POS;
} else {
@@ -261,8 +261,8 @@ bool DynamicBigramListPolicy::addNewBigramEntryToBigramList(const int bigramTarg
const int originalProbability = BigramListReadWriteUtils::getProbabilityFromFlags(
bigramFlags);
const int probabilityToWrite = mIsDecayingDict ?
- DecayingUtils::getUpdatedBigramProbabilityDelta(
- originalProbability, probability) : probability;
+ ForgettingCurveUtils::getUpdatedEncodedProbability(originalProbability,
+ probability) : probability;
const BigramListReadWriteUtils::BigramFlags updatedFlags =
BigramListReadWriteUtils::setProbabilityInFlags(bigramFlags,
probabilityToWrite);
@@ -294,7 +294,7 @@ bool DynamicBigramListPolicy::writeNewBigramEntry(const int bigramTargetPos, con
int *const writingPos) {
// hasNext is false because we are adding a new bigram entry at the end of the bigram list.
const int probabilityToWrite = mIsDecayingDict ?
- DecayingUtils::getUpdatedBigramProbabilityDelta(NOT_A_PROBABILITY, probability) :
+ ForgettingCurveUtils::getUpdatedEncodedProbability(NOT_A_PROBABILITY, probability) :
probability;
return BigramListReadWriteUtils::createAndWriteBigramEntry(mBuffer, bigramTargetPos,
probabilityToWrite, false /* hasNext */, writingPos);
@@ -365,9 +365,9 @@ bool DynamicBigramListPolicy::updateProbabilityForDecay(
*outRemoved = false;
if (mIsDecayingDict) {
// Update bigram probability for decaying.
- const int newProbability = DecayingUtils::getBigramProbabilityDeltaToSave(
+ const int newProbability = ForgettingCurveUtils::getEncodedProbabilityToSave(
BigramListReadWriteUtils::getProbabilityFromFlags(bigramFlags));
- if (DecayingUtils::isValidBigram(newProbability)) {
+ if (ForgettingCurveUtils::isValidEncodedProbability(newProbability)) {
// Write new probability.
const BigramListReadWriteUtils::BigramFlags updatedBigramFlags =
BigramListReadWriteUtils::setProbabilityInFlags(
diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_gc_event_listeners.cpp b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_gc_event_listeners.cpp
index 081163a4d..324b53062 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_gc_event_listeners.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_gc_event_listeners.cpp
@@ -16,7 +16,7 @@
#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_gc_event_listeners.h"
-#include "suggest/policyimpl/dictionary/utils/decaying_utils.h"
+#include "suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h"
namespace latinime {
@@ -29,14 +29,14 @@ bool DynamicPatriciaTrieGcEventListeners
bool isUselessPtNode = !node->isTerminal();
if (node->isTerminal() && mIsDecayingDict) {
const int newProbability =
- DecayingUtils::getUnigramProbabilityToSave(node->getProbability());
+ ForgettingCurveUtils::getEncodedProbabilityToSave(node->getProbability());
int writingPos = node->getProbabilityFieldPos();
// Update probability.
if (!DynamicPatriciaTrieWritingUtils::writeProbabilityAndAdvancePosition(
mBuffer, newProbability, &writingPos)) {
return false;
}
- if (!DecayingUtils::isValidUnigram(newProbability)) {
+ if (!ForgettingCurveUtils::isValidEncodedProbability(newProbability)) {
isUselessPtNode = false;
}
}
diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.cpp
index a5a42ead1..60d0db0c0 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_policy.cpp
@@ -28,7 +28,7 @@
#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_reading_utils.h"
#include "suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.h"
#include "suggest/policyimpl/dictionary/patricia_trie_reading_utils.h"
-#include "suggest/policyimpl/dictionary/utils/decaying_utils.h"
+#include "suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h"
#include "suggest/policyimpl/dictionary/utils/probability_utils.h"
namespace latinime {
@@ -154,7 +154,7 @@ int DynamicPatriciaTriePolicy::getTerminalNodePositionOfWord(const int *const in
int DynamicPatriciaTriePolicy::getProbability(const int unigramProbability,
const int bigramProbability) const {
if (mHeaderPolicy.isDecayingDict()) {
- return DecayingUtils::getProbability(unigramProbability, bigramProbability);
+ return ForgettingCurveUtils::getProbability(unigramProbability, bigramProbability);
} else {
if (unigramProbability == NOT_A_PROBABILITY) {
return NOT_A_PROBABILITY;
@@ -344,10 +344,10 @@ bool DynamicPatriciaTriePolicy::needsToRunGC(const bool mindsBlockByGC) const {
// Needs to reduce dictionary size.
return true;
} else if (mHeaderPolicy.isDecayingDict()) {
- if (mUnigramCount >= DecayingUtils::MAX_UNIGRAM_COUNT) {
+ if (mUnigramCount >= ForgettingCurveUtils::MAX_UNIGRAM_COUNT) {
// Unigram count exceeds the limit.
return true;
- } else if (mBigramCount >= DecayingUtils::MAX_BIGRAM_COUNT) {
+ } else if (mBigramCount >= ForgettingCurveUtils::MAX_BIGRAM_COUNT) {
// Bigram count exceeds the limit.
return true;
} else if (mindsBlockByGC && needsToDecay()) {
diff --git a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.cpp b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.cpp
index db6d48fdb..70a9ee564 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/dynamic_patricia_trie_writing_helper.cpp
@@ -25,8 +25,8 @@
#include "suggest/policyimpl/dictionary/header/header_policy.h"
#include "suggest/policyimpl/dictionary/patricia_trie_reading_utils.h"
#include "suggest/policyimpl/dictionary/shortcut/dynamic_shortcut_list_policy.h"
-#include "suggest/policyimpl/dictionary/utils/decaying_utils.h"
#include "suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h"
+#include "suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h"
#include "utils/hash_map_compat.h"
namespace latinime {
@@ -494,7 +494,7 @@ bool DynamicPatriciaTrieWritingHelper::runGC(const int rootPtNodeArrayPos,
return false;
}
if (mNeedsToDecay && traversePolicyToUpdateUnigramProbabilityAndMarkUselessPtNodesAsDeleted
- .getValidUnigramCount() > DecayingUtils::MAX_UNIGRAM_COUNT_AFTER_GC) {
+ .getValidUnigramCount() > ForgettingCurveUtils::MAX_UNIGRAM_COUNT_AFTER_GC) {
// TODO: Remove more unigrams.
}
@@ -507,7 +507,7 @@ bool DynamicPatriciaTrieWritingHelper::runGC(const int rootPtNodeArrayPos,
}
if (mNeedsToDecay && traversePolicyToUpdateBigramProbability.getValidBigramEntryCount()
- > DecayingUtils::MAX_BIGRAM_COUNT_AFTER_GC) {
+ > ForgettingCurveUtils::MAX_BIGRAM_COUNT_AFTER_GC) {
// TODO: Remove more bigrams.
}
@@ -545,7 +545,8 @@ bool DynamicPatriciaTrieWritingHelper::runGC(const int rootPtNodeArrayPos,
int DynamicPatriciaTrieWritingHelper::getUpdatedProbability(const int originalProbability,
const int newProbability) {
if (mNeedsToDecay) {
- return DecayingUtils::getUpdatedUnigramProbability(originalProbability, newProbability);
+ return ForgettingCurveUtils::getUpdatedEncodedProbability(originalProbability,
+ newProbability);
} else {
return newProbability;
}
diff --git a/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.cpp
index 2694ce8d5..5ded8f6a1 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.cpp
@@ -139,6 +139,9 @@ const char *const HeaderReadWriteUtils::REQUIRES_FRENCH_LIGATURE_PROCESSING_KEY
int *const writingPos) {
for (AttributeMap::const_iterator it = headerAttributes->begin();
it != headerAttributes->end(); ++it) {
+ if (it->first.empty() || it->second.empty()) {
+ continue;
+ }
// Write a key.
if (!buffer->writeCodePointsAndAdvancePosition(&(it->first.at(0)), it->first.size(),
true /* writesTerminator */, writingPos)) {
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/decaying_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/decaying_utils.cpp
deleted file mode 100644
index 942a74238..000000000
--- a/native/jni/src/suggest/policyimpl/dictionary/utils/decaying_utils.cpp
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright (C) 2013, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "suggest/policyimpl/dictionary/utils/decaying_utils.h"
-
-#include "suggest/policyimpl/dictionary/utils/probability_utils.h"
-
-namespace latinime {
-
-const int DecayingUtils::MAX_UNIGRAM_COUNT = 12000;
-const int DecayingUtils::MAX_UNIGRAM_COUNT_AFTER_GC = 10000;
-const int DecayingUtils::MAX_BIGRAM_COUNT = 12000;
-const int DecayingUtils::MAX_BIGRAM_COUNT_AFTER_GC = 10000;
-
-const int DecayingUtils::MAX_COMPUTED_PROBABILITY = 127;
-const int DecayingUtils::MAX_UNIGRAM_PROBABILITY = 120;
-const int DecayingUtils::MIN_VALID_UNIGRAM_PROBABILITY = 24;
-const int DecayingUtils::UNIGRAM_PROBABILITY_STEP = 8;
-const int DecayingUtils::MAX_BIGRAM_PROBABILITY_DELTA = 15;
-const int DecayingUtils::MIN_VALID_BIGRAM_PROBABILITY_DELTA = 3;
-const int DecayingUtils::BIGRAM_PROBABILITY_DELTA_STEP = 1;
-
-/* static */ int DecayingUtils::getProbability(const int encodedUnigramProbability,
- const int encodedBigramProbabilityDelta) {
- if (encodedUnigramProbability == NOT_A_PROBABILITY) {
- return NOT_A_PROBABILITY;
- } else if (encodedBigramProbabilityDelta == NOT_A_PROBABILITY) {
- const int rawProbability = ProbabilityUtils::backoff(decodeUnigramProbability(
- encodedUnigramProbability));
- return min(getDecayedProbability(rawProbability), MAX_COMPUTED_PROBABILITY);
- } else {
- const int rawProbability = ProbabilityUtils::computeProbabilityForBigram(
- decodeUnigramProbability(encodedUnigramProbability),
- decodeBigramProbabilityDelta(encodedBigramProbabilityDelta));
- return min(getDecayedProbability(rawProbability), MAX_COMPUTED_PROBABILITY);
- }
-}
-
-/* static */ int DecayingUtils::getUpdatedUnigramProbability(const int originalEncodedProbability,
- const int newProbability) {
- if (originalEncodedProbability == NOT_A_PROBABILITY) {
- // The unigram is not in this dictionary.
- if (newProbability == NOT_A_PROBABILITY) {
- // The unigram is not in other dictionaries.
- return 0;
- } else {
- return MIN_VALID_UNIGRAM_PROBABILITY;
- }
- } else {
- if (newProbability != NOT_A_PROBABILITY
- && originalEncodedProbability < MIN_VALID_UNIGRAM_PROBABILITY) {
- return MIN_VALID_UNIGRAM_PROBABILITY;
- }
- return min(originalEncodedProbability + UNIGRAM_PROBABILITY_STEP, MAX_UNIGRAM_PROBABILITY);
- }
-}
-
-/* static */ int DecayingUtils::getUnigramProbabilityToSave(const int encodedProbability) {
- return max(encodedProbability - UNIGRAM_PROBABILITY_STEP, 0);
-}
-
-/* static */ int DecayingUtils::getBigramProbabilityDeltaToSave(const int encodedProbabilityDelta) {
- return max(encodedProbabilityDelta - BIGRAM_PROBABILITY_DELTA_STEP, 0);
-}
-
-/* static */ int DecayingUtils::getUpdatedBigramProbabilityDelta(
- const int originalEncodedProbabilityDelta, const int newProbability) {
- if (originalEncodedProbabilityDelta == NOT_A_PROBABILITY) {
- // The bigram relation is not in this dictionary.
- if (newProbability == NOT_A_PROBABILITY) {
- // The bigram target is not in other dictionaries.
- return 0;
- } else {
- return MIN_VALID_BIGRAM_PROBABILITY_DELTA;
- }
- } else {
- if (newProbability != NOT_A_PROBABILITY
- && originalEncodedProbabilityDelta < MIN_VALID_BIGRAM_PROBABILITY_DELTA) {
- return MIN_VALID_BIGRAM_PROBABILITY_DELTA;
- }
- return min(originalEncodedProbabilityDelta + BIGRAM_PROBABILITY_DELTA_STEP,
- MAX_BIGRAM_PROBABILITY_DELTA);
- }
-}
-
-/* static */ int DecayingUtils::isValidUnigram(const int encodedUnigramProbability) {
- return encodedUnigramProbability >= MIN_VALID_UNIGRAM_PROBABILITY;
-}
-
-/* static */ int DecayingUtils::isValidBigram(const int encodedBigramProbabilityDelta) {
- return encodedBigramProbabilityDelta >= MIN_VALID_BIGRAM_PROBABILITY_DELTA;
-}
-
-/* static */ int DecayingUtils::decodeUnigramProbability(const int encodedProbability) {
- const int probability = encodedProbability - MIN_VALID_UNIGRAM_PROBABILITY;
- if (probability < 0) {
- return NOT_A_PROBABILITY;
- } else {
- return min(probability, MAX_UNIGRAM_PROBABILITY);
- }
-}
-
-/* static */ int DecayingUtils::decodeBigramProbabilityDelta(const int encodedProbabilityDelta) {
- const int probabilityDelta = encodedProbabilityDelta - MIN_VALID_BIGRAM_PROBABILITY_DELTA;
- if (probabilityDelta < 0) {
- return NOT_A_PROBABILITY;
- } else {
- return min(probabilityDelta, MAX_BIGRAM_PROBABILITY_DELTA);
- }
-}
-
-/* static */ int DecayingUtils::getDecayedProbability(const int rawProbability) {
- return rawProbability;
-}
-
-} // namespace latinime
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.cpp
new file mode 100644
index 000000000..b502fe25d
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.cpp
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2013, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cmath>
+#include <stdlib.h>
+
+#include "suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h"
+
+#include "suggest/policyimpl/dictionary/utils/probability_utils.h"
+
+namespace latinime {
+
+const int ForgettingCurveUtils::MAX_UNIGRAM_COUNT = 12000;
+const int ForgettingCurveUtils::MAX_UNIGRAM_COUNT_AFTER_GC = 10000;
+const int ForgettingCurveUtils::MAX_BIGRAM_COUNT = 12000;
+const int ForgettingCurveUtils::MAX_BIGRAM_COUNT_AFTER_GC = 10000;
+
+const int ForgettingCurveUtils::MAX_COMPUTED_PROBABILITY = 127;
+const int ForgettingCurveUtils::MAX_ENCODED_PROBABILITY = 15;
+const int ForgettingCurveUtils::MIN_VALID_ENCODED_PROBABILITY = 3;
+const int ForgettingCurveUtils::ENCODED_PROBABILITY_STEP = 1;
+// Currently, we try to decay each uni/bigram once every 2 hours. Accordingly, the expected
+// duration of the decay is approximately 66hours.
+const float ForgettingCurveUtils::MIN_PROBABILITY_TO_DECAY = 0.03f;
+
+const ForgettingCurveUtils::ProbabilityTable ForgettingCurveUtils::sProbabilityTable;
+
+/* static */ int ForgettingCurveUtils::getProbability(const int encodedUnigramProbability,
+ const int encodedBigramProbability) {
+ if (encodedUnigramProbability == NOT_A_PROBABILITY) {
+ return NOT_A_PROBABILITY;
+ } else if (encodedBigramProbability == NOT_A_PROBABILITY) {
+ return backoff(decodeProbability(encodedUnigramProbability));
+ } else {
+ const int unigramProbability = decodeProbability(encodedUnigramProbability);
+ const int bigramProbability = decodeProbability(encodedBigramProbability);
+ return min(max(unigramProbability, bigramProbability), MAX_COMPUTED_PROBABILITY);
+ }
+}
+
+// Caveat: Unlike getProbability(), this method doesn't assume special bigram probability encoding
+// (i.e. unigram probability + bigram probability delta).
+/* static */ int ForgettingCurveUtils::getUpdatedEncodedProbability(
+ const int originalEncodedProbability, const int newProbability) {
+ if (originalEncodedProbability == NOT_A_PROBABILITY) {
+ // The bigram relation is not in this dictionary.
+ if (newProbability == NOT_A_PROBABILITY) {
+ // The bigram target is not in other dictionaries.
+ return 0;
+ } else {
+ return MIN_VALID_ENCODED_PROBABILITY;
+ }
+ } else {
+ if (newProbability != NOT_A_PROBABILITY
+ && originalEncodedProbability < MIN_VALID_ENCODED_PROBABILITY) {
+ return MIN_VALID_ENCODED_PROBABILITY;
+ }
+ return min(originalEncodedProbability + ENCODED_PROBABILITY_STEP, MAX_ENCODED_PROBABILITY);
+ }
+}
+
+/* static */ int ForgettingCurveUtils::isValidEncodedProbability(const int encodedProbability) {
+ return encodedProbability >= MIN_VALID_ENCODED_PROBABILITY;
+}
+
+/* static */ int ForgettingCurveUtils::getEncodedProbabilityToSave(const int encodedProbability) {
+ const int currentEncodedProbability = max(min(encodedProbability, MAX_ENCODED_PROBABILITY), 0);
+ // TODO: Implement the decay in more proper way.
+ const float currentRate = static_cast<float>(currentEncodedProbability)
+ / static_cast<float>(MAX_ENCODED_PROBABILITY);
+ const float thresholdToDecay = MIN_PROBABILITY_TO_DECAY
+ + (1.0f - MIN_PROBABILITY_TO_DECAY) * (1.0f - currentRate);
+ const float randValue = static_cast<float>(rand()) / static_cast<float>(RAND_MAX);
+ if (thresholdToDecay < randValue) {
+ return max(currentEncodedProbability - ENCODED_PROBABILITY_STEP, 0);
+ } else {
+ return currentEncodedProbability;
+ }
+}
+
+/* static */ int ForgettingCurveUtils::decodeProbability(const int encodedProbability) {
+ if (encodedProbability < MIN_VALID_ENCODED_PROBABILITY) {
+ return NOT_A_PROBABILITY;
+ } else {
+ return min(sProbabilityTable.getProbability(encodedProbability), MAX_ENCODED_PROBABILITY);
+ }
+}
+
+// See comments in ProbabilityUtils::backoff().
+/* static */ int ForgettingCurveUtils::backoff(const int unigramProbability) {
+ if (unigramProbability == NOT_A_PROBABILITY) {
+ return NOT_A_PROBABILITY;
+ } else {
+ return max(unigramProbability - 8, 0);
+ }
+}
+
+ForgettingCurveUtils::ProbabilityTable::ProbabilityTable() : mTable() {
+ // Table entry is as follows:
+ // 1, 1, 1, 2, 3, 5, 6, 9, 13, 18, 25, 34, 48, 66, 91, 127.
+ // Note that first MIN_VALID_ENCODED_PROBABILITY values are not used.
+ mTable.resize(MAX_ENCODED_PROBABILITY + 1);
+ for (int i = 0; i <= MAX_ENCODED_PROBABILITY; ++i) {
+ const int probability = static_cast<int>(powf(static_cast<float>(MAX_COMPUTED_PROBABILITY),
+ static_cast<float>(i) / static_cast<float>(MAX_ENCODED_PROBABILITY)));
+ mTable[i] = min(MAX_COMPUTED_PROBABILITY, max(0, probability));
+ }
+}
+
+} // namespace latinime
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/decaying_utils.h b/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h
index 1ca03918f..d666f22aa 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/utils/decaying_utils.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/forgetting_curve_utils.h
@@ -14,8 +14,10 @@
* limitations under the License.
*/
-#ifndef LATINIME_DECAYING_UTILS_H
-#define LATINIME_DECAYING_UTILS_H
+#ifndef LATINIME_FORGETTING_CURVE_UTILS_H
+#define LATINIME_FORGETTING_CURVE_UTILS_H
+
+#include <vector>
#include "defines.h"
@@ -24,8 +26,7 @@ namespace latinime {
// TODO: Check the elapsed time and decrease the probability depending on the time. Time field is
// required to introduced to each terminal PtNode and bigram entry.
// TODO: Quit using bigram probability to indicate the delta.
-// TODO: Quit using bigram probability delta.
-class DecayingUtils {
+class ForgettingCurveUtils {
public:
static const int MAX_UNIGRAM_COUNT;
static const int MAX_UNIGRAM_COUNT_AFTER_GC;
@@ -33,38 +34,46 @@ class DecayingUtils {
static const int MAX_BIGRAM_COUNT_AFTER_GC;
static int getProbability(const int encodedUnigramProbability,
- const int encodedBigramProbabilityDelta);
+ const int encodedBigramProbability);
- static int getUpdatedUnigramProbability(const int originalEncodedProbability,
+ static int getUpdatedEncodedProbability(const int originalEncodedProbability,
const int newProbability);
- static int getUpdatedBigramProbabilityDelta(const int originalEncodedProbabilityDelta,
- const int newProbability);
+ static int isValidEncodedProbability(const int encodedProbability);
- static int isValidUnigram(const int encodedUnigramProbability);
+ static int getEncodedProbabilityToSave(const int encodedProbability);
- static int isValidBigram(const int encodedProbabilityDelta);
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ForgettingCurveUtils);
- static int getUnigramProbabilityToSave(const int encodedProbability);
+ class ProbabilityTable {
+ public:
+ ProbabilityTable();
- static int getBigramProbabilityDeltaToSave(const int encodedProbabilityDelta);
+ int getProbability(const int encodedProbability) const {
+ if (encodedProbability < 0 || encodedProbability > static_cast<int>(mTable.size())) {
+ return NOT_A_PROBABILITY;
+ }
+ return mTable[encodedProbability];
+ }
- private:
- DISALLOW_IMPLICIT_CONSTRUCTORS(DecayingUtils);
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ProbabilityTable);
+
+ std::vector<int> mTable;
+ };
static const int MAX_COMPUTED_PROBABILITY;
- static const int MAX_UNIGRAM_PROBABILITY;
- static const int MIN_VALID_UNIGRAM_PROBABILITY;
- static const int UNIGRAM_PROBABILITY_STEP;
- static const int MAX_BIGRAM_PROBABILITY_DELTA;
- static const int MIN_VALID_BIGRAM_PROBABILITY_DELTA;
- static const int BIGRAM_PROBABILITY_DELTA_STEP;
+ static const int MAX_ENCODED_PROBABILITY;
+ static const int MIN_VALID_ENCODED_PROBABILITY;
+ static const int ENCODED_PROBABILITY_STEP;
+ static const float MIN_PROBABILITY_TO_DECAY;
- static int decodeUnigramProbability(const int encodedProbability);
+ static const ProbabilityTable sProbabilityTable;
- static int decodeBigramProbabilityDelta(const int encodedProbability);
+ static int decodeProbability(const int encodedProbability);
- static int getDecayedProbability(const int rawProbability);
+ static int backoff(const int unigramProbability);
};
} // namespace latinime
-#endif /* LATINIME_DECAYING_UTILS_H */
+#endif /* LATINIME_FORGETTING_CURVE_UTILS_H */
diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
index 8a439fc22..b2d31c21f 100644
--- a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
+++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
@@ -50,14 +50,18 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase {
}
private void forcePassingShortTime(final BinaryDictionary binaryDictionary) {
- binaryDictionary.getPropertyForTests(SET_NEEDS_TO_DECAY_FOR_TESTING_KEY);
- binaryDictionary.flushWithGC();
+ // Entries having low probability would be suppressed once in 2 GCs.
+ final int count = 2;
+ for (int i = 0; i < count; i++) {
+ binaryDictionary.getPropertyForTests(SET_NEEDS_TO_DECAY_FOR_TESTING_KEY);
+ binaryDictionary.flushWithGC();
+ }
}
private void forcePassingLongTime(final BinaryDictionary binaryDictionary) {
// Currently, probabilities are decayed when GC is run. All entries that have never been
- // typed in 32 GCs are removed.
- final int count = 32;
+ // typed in 128 GCs would be removed.
+ final int count = 128;
for (int i = 0; i < count; i++) {
binaryDictionary.getPropertyForTests(SET_NEEDS_TO_DECAY_FOR_TESTING_KEY);
binaryDictionary.flushWithGC();
diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java
index a4d94262f..aa1658301 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java
@@ -104,7 +104,9 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase {
}
sStarBigrams.put(0, new ArrayList<Integer>());
- for (int i = 1; i < sWords.size(); ++i) {
+ // MAX - 1 because we added one above already
+ final int maxBigrams = Math.min(sWords.size(), FormatSpec.MAX_BIGRAMS_IN_A_PTNODE - 1);
+ for (int i = 1; i < maxBigrams; ++i) {
sStarBigrams.get(0).add(i);
}
@@ -544,8 +546,7 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase {
}
private long checkGetTerminalPosition(final DictDecoder dictDecoder, final String word,
- int index, boolean contained) {
- final int expectedFrequency = (UNIGRAM_FREQ + index) % 255;
+ final boolean contained) {
long diff = -1;
int position = -1;
try {
@@ -603,7 +604,7 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase {
// Test a word that is contained within the dictionary.
long sum = 0;
for (int i = 0; i < sWords.size(); ++i) {
- final long time = checkGetTerminalPosition(dictDecoder, sWords.get(i), i, true);
+ final long time = checkGetTerminalPosition(dictDecoder, sWords.get(i), true);
sum += time == -1 ? 0 : time;
}
Log.d(TAG, "per search : " + (((double)sum) / sWords.size() / 1000000) + " : " + message
@@ -616,11 +617,11 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase {
for (int i = 0; i < 1000; ++i) {
final String word = CodePointUtils.generateWord(random, codePointSet);
if (sWords.indexOf(word) != -1) continue;
- checkGetTerminalPosition(dictDecoder, word, i, false);
+ checkGetTerminalPosition(dictDecoder, word, false);
}
}
- private void runGetTerminalPositionTests(final ArrayList<String> results, final int bufferType,
+ private void runGetTerminalPositionTests(final int bufferType,
final FormatOptions formatOptions) {
runGetTerminalPosition(sWords, sEmptyBigrams, bufferType, formatOptions, "unigram");
}
@@ -628,17 +629,17 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase {
public void testGetTerminalPosition() {
final ArrayList<String> results = CollectionUtils.newArrayList();
- runGetTerminalPositionTests(results, USE_BYTE_ARRAY, VERSION2);
- runGetTerminalPositionTests(results, USE_BYTE_ARRAY, VERSION3_WITHOUT_DYNAMIC_UPDATE);
- runGetTerminalPositionTests(results, USE_BYTE_ARRAY, VERSION3_WITH_DYNAMIC_UPDATE);
- runGetTerminalPositionTests(results, USE_BYTE_ARRAY, VERSION4_WITHOUT_DYNAMIC_UPDATE);
- runGetTerminalPositionTests(results, USE_BYTE_ARRAY, VERSION4_WITH_DYNAMIC_UPDATE);
+ runGetTerminalPositionTests(USE_BYTE_ARRAY, VERSION2);
+ runGetTerminalPositionTests(USE_BYTE_ARRAY, VERSION3_WITHOUT_DYNAMIC_UPDATE);
+ runGetTerminalPositionTests(USE_BYTE_ARRAY, VERSION3_WITH_DYNAMIC_UPDATE);
+ runGetTerminalPositionTests(USE_BYTE_ARRAY, VERSION4_WITHOUT_DYNAMIC_UPDATE);
+ runGetTerminalPositionTests(USE_BYTE_ARRAY, VERSION4_WITH_DYNAMIC_UPDATE);
- runGetTerminalPositionTests(results, USE_BYTE_BUFFER, VERSION2);
- runGetTerminalPositionTests(results, USE_BYTE_BUFFER, VERSION3_WITHOUT_DYNAMIC_UPDATE);
- runGetTerminalPositionTests(results, USE_BYTE_BUFFER, VERSION3_WITH_DYNAMIC_UPDATE);
- runGetTerminalPositionTests(results, USE_BYTE_BUFFER, VERSION4_WITHOUT_DYNAMIC_UPDATE);
- runGetTerminalPositionTests(results, USE_BYTE_BUFFER, VERSION4_WITH_DYNAMIC_UPDATE);
+ runGetTerminalPositionTests(USE_BYTE_BUFFER, VERSION2);
+ runGetTerminalPositionTests(USE_BYTE_BUFFER, VERSION3_WITHOUT_DYNAMIC_UPDATE);
+ runGetTerminalPositionTests(USE_BYTE_BUFFER, VERSION3_WITH_DYNAMIC_UPDATE);
+ runGetTerminalPositionTests(USE_BYTE_BUFFER, VERSION4_WITHOUT_DYNAMIC_UPDATE);
+ runGetTerminalPositionTests(USE_BYTE_BUFFER, VERSION4_WITH_DYNAMIC_UPDATE);
for (final String result : results) {
Log.d(TAG, result);
@@ -656,27 +657,21 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase {
addUnigrams(sWords.size(), dict, sWords, null /* shortcutMap */);
timeWritingDictToFile(file, dict, VERSION3_WITH_DYNAMIC_UPDATE);
- final Ver3DictDecoder dictDecoder = new Ver3DictDecoder(file, DictDecoder.USE_BYTEARRAY);
- try {
- dictDecoder.openDictBuffer();
- } catch (IOException e) {
- // ignore
- Log.e(TAG, "IOException while opening the buffer", e);
- }
- assertTrue("Can't get the buffer", dictDecoder.isDictBufferOpen());
+ final Ver3DictUpdater dictUpdater = new Ver3DictUpdater(file,
+ DictDecoder.USE_WRITABLE_BYTEBUFFER);
try {
MoreAsserts.assertNotEqual(FormatSpec.NOT_VALID_WORD,
- dictDecoder.getTerminalPosition(sWords.get(0)));
- DynamicBinaryDictIOUtils.deleteWord(dictDecoder, sWords.get(0));
+ dictUpdater.getTerminalPosition(sWords.get(0)));
+ dictUpdater.deleteWord(sWords.get(0));
assertEquals(FormatSpec.NOT_VALID_WORD,
- dictDecoder.getTerminalPosition(sWords.get(0)));
+ dictUpdater.getTerminalPosition(sWords.get(0)));
MoreAsserts.assertNotEqual(FormatSpec.NOT_VALID_WORD,
- dictDecoder.getTerminalPosition(sWords.get(5)));
- DynamicBinaryDictIOUtils.deleteWord(dictDecoder, sWords.get(5));
+ dictUpdater.getTerminalPosition(sWords.get(5)));
+ dictUpdater.deleteWord(sWords.get(5));
assertEquals(FormatSpec.NOT_VALID_WORD,
- dictDecoder.getTerminalPosition(sWords.get(5)));
+ dictUpdater.getTerminalPosition(sWords.get(5)));
} catch (IOException e) {
} catch (UnsupportedFormatException e) {
}
diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java
index a83749499..acd65856c 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtilsTests.java
@@ -27,9 +27,7 @@ import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray;
import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
import com.android.inputmethod.latin.utils.CollectionUtils;
-import java.io.BufferedOutputStream;
import java.io.File;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
@@ -186,46 +184,31 @@ public class BinaryDictIOUtilsTests extends AndroidTestCase {
private long insertAndCheckWord(final File file, final String word, final int frequency,
final boolean exist, final ArrayList<WeightedString> bigrams,
final ArrayList<WeightedString> shortcuts) {
- BufferedOutputStream outStream = null;
long amountOfTime = -1;
try {
- final Ver3DictDecoder dictDecoder = new Ver3DictDecoder(file,
+ final Ver3DictUpdater dictUpdater = new Ver3DictUpdater(file,
DictDecoder.USE_WRITABLE_BYTEBUFFER);
- dictDecoder.openDictBuffer();
- outStream = new BufferedOutputStream(new FileOutputStream(file, true));
if (!exist) {
assertEquals(FormatSpec.NOT_VALID_WORD, getWordPosition(file, word));
}
final long now = System.nanoTime();
- DynamicBinaryDictIOUtils.insertWord(dictDecoder, outStream, word, frequency, bigrams,
- shortcuts, false, false);
+ dictUpdater.insertWord(word, frequency, bigrams, shortcuts, false, false);
amountOfTime = System.nanoTime() - now;
- outStream.flush();
MoreAsserts.assertNotEqual(FormatSpec.NOT_VALID_WORD, getWordPosition(file, word));
- outStream.close();
} catch (IOException e) {
Log.e(TAG, "Raised an IOException while inserting a word", e);
} catch (UnsupportedFormatException e) {
Log.e(TAG, "Raised an UnsupportedFormatException error while inserting a word", e);
- } finally {
- if (outStream != null) {
- try {
- outStream.close();
- } catch (IOException e) {
- Log.e(TAG, "Failed to close the output stream", e);
- }
- }
}
return amountOfTime;
}
private void deleteWord(final File file, final String word) {
try {
- final Ver3DictDecoder dictDecoder = new Ver3DictDecoder(file,
+ final Ver3DictUpdater dictUpdater = new Ver3DictUpdater(file,
DictDecoder.USE_WRITABLE_BYTEBUFFER);
- dictDecoder.openDictBuffer();
- DynamicBinaryDictIOUtils.deleteWord(dictDecoder, word);
+ dictUpdater.deleteWord(word);
} catch (IOException e) {
} catch (UnsupportedFormatException e) {
}
diff --git a/tests/src/com/android/inputmethod/latin/makedict/SparseTableTests.java b/tests/src/com/android/inputmethod/latin/makedict/SparseTableTests.java
index 132483d5e..aeb8552bd 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/SparseTableTests.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/SparseTableTests.java
@@ -21,10 +21,8 @@ import android.test.suitebuilder.annotation.LargeTest;
import android.util.Log;
import java.io.File;
-import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
-import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Random;
@@ -36,9 +34,6 @@ import java.util.Random;
public class SparseTableTests extends AndroidTestCase {
private static final String TAG = SparseTableTests.class.getSimpleName();
- private static final int[] SMALL_INDEX = { SparseTable.NOT_EXIST, 0 };
- private static final int[] BIG_INDEX = { SparseTable.NOT_EXIST, 1, 2, 3, 4, 5, 6, 7};
-
private final Random mRandom;
private final ArrayList<Integer> mRandomIndex;
@@ -59,32 +54,21 @@ public class SparseTableTests extends AndroidTestCase {
}
}
- public void testInitializeWithArray() {
- final SparseTable table = new SparseTable(SMALL_INDEX, BIG_INDEX, BLOCK_SIZE);
- for (int i = 0; i < 8; ++i) {
- assertEquals(SparseTable.NOT_EXIST, table.get(i));
- }
- assertEquals(SparseTable.NOT_EXIST, table.get(8));
- for (int i = 9; i < 16; ++i) {
- assertEquals(i - 8, table.get(i));
- }
- }
-
public void testSet() {
- final SparseTable table = new SparseTable(16, BLOCK_SIZE);
- table.set(3, 6);
- table.set(8, 16);
+ final SparseTable table = new SparseTable(16, BLOCK_SIZE, 1);
+ table.set(0, 3, 6);
+ table.set(0, 8, 16);
for (int i = 0; i < 16; ++i) {
if (i == 3 || i == 8) {
- assertEquals(i * 2, table.get(i));
+ assertEquals(i * 2, table.get(0, i));
} else {
- assertEquals(SparseTable.NOT_EXIST, table.get(i));
+ assertEquals(SparseTable.NOT_EXIST, table.get(0, i));
}
}
}
private void generateRandomIndex(final int size, final int prop) {
- for (int i = 0; i < DEFAULT_SIZE; ++i) {
+ for (int i = 0; i < size; ++i) {
if (mRandom.nextInt(100) < prop) {
mRandomIndex.set(i, mRandom.nextInt());
} else {
@@ -94,11 +78,11 @@ public class SparseTableTests extends AndroidTestCase {
}
private void runTestRandomSet() {
- final SparseTable table = new SparseTable(DEFAULT_SIZE, BLOCK_SIZE);
+ final SparseTable table = new SparseTable(DEFAULT_SIZE, BLOCK_SIZE, 1);
int elementCount = 0;
for (int i = 0; i < DEFAULT_SIZE; ++i) {
if (mRandomIndex.get(i) != SparseTable.NOT_EXIST) {
- table.set(i, mRandomIndex.get(i));
+ table.set(0, i, mRandomIndex.get(i));
elementCount++;
}
}
@@ -107,29 +91,24 @@ public class SparseTableTests extends AndroidTestCase {
+ table.getContentTableSize());
Log.d(TAG, "the table has " + elementCount + " elements");
for (int i = 0; i < DEFAULT_SIZE; ++i) {
- assertEquals(table.get(i), (int)mRandomIndex.get(i));
+ assertEquals(table.get(0, i), (int)mRandomIndex.get(i));
}
// flush and reload
OutputStream lookupOutStream = null;
OutputStream contentOutStream = null;
- InputStream lookupInStream = null;
- InputStream contentInStream = null;
try {
final File lookupIndexFile = File.createTempFile("testRandomSet", ".small");
final File contentFile = File.createTempFile("testRandomSet", ".big");
lookupOutStream = new FileOutputStream(lookupIndexFile);
contentOutStream = new FileOutputStream(contentFile);
- table.write(lookupOutStream, contentOutStream);
- lookupInStream = new FileInputStream(lookupIndexFile);
- contentInStream = new FileInputStream(contentFile);
- final byte[] lookupArray = new byte[(int) lookupIndexFile.length()];
- final byte[] contentArray = new byte[(int) contentFile.length()];
- lookupInStream.read(lookupArray);
- contentInStream.read(contentArray);
- final SparseTable newTable = new SparseTable(lookupArray, contentArray, BLOCK_SIZE);
+ table.write(lookupOutStream, new OutputStream[] { contentOutStream });
+ lookupOutStream.flush();
+ contentOutStream.flush();
+ final SparseTable newTable = SparseTable.readFromFiles(lookupIndexFile,
+ new File[] { contentFile }, BLOCK_SIZE);
for (int i = 0; i < DEFAULT_SIZE; ++i) {
- assertEquals(table.get(i), newTable.get(i));
+ assertEquals(table.get(0, i), newTable.get(0, i));
}
} catch (IOException e) {
Log.d(TAG, "IOException while flushing and realoding", e);
@@ -157,4 +136,60 @@ public class SparseTableTests extends AndroidTestCase {
runTestRandomSet();
}
}
+
+ public void testMultipleContents() {
+ final int numOfContents = 5;
+ generateRandomIndex(DEFAULT_SIZE, 20);
+ final SparseTable table = new SparseTable(DEFAULT_SIZE, BLOCK_SIZE, numOfContents);
+ for (int i = 0; i < mRandomIndex.size(); ++i) {
+ if (mRandomIndex.get(i) != SparseTable.NOT_EXIST) {
+ for (int j = 0; j < numOfContents; ++j) {
+ table.set(j, i, mRandomIndex.get(i));
+ }
+ }
+ }
+
+ OutputStream lookupOutStream = null;
+ OutputStream[] contentsOutStream = new OutputStream[numOfContents];
+ try {
+ final File lookupIndexFile = File.createTempFile("testMultipleContents", "small");
+ lookupOutStream = new FileOutputStream(lookupIndexFile);
+ final File[] contentFiles = new File[numOfContents];
+ for (int i = 0; i < numOfContents; ++i) {
+ contentFiles[i] = File.createTempFile("testMultipleContents", "big" + i);
+ contentsOutStream[i] = new FileOutputStream(contentFiles[i]);
+ }
+ table.write(lookupOutStream, contentsOutStream);
+ lookupOutStream.flush();
+ for (int i = 0; i < numOfContents; ++i) {
+ contentsOutStream[i].flush();
+ }
+ final SparseTable newTable = SparseTable.readFromFiles(lookupIndexFile, contentFiles,
+ BLOCK_SIZE);
+ for (int i = 0; i < numOfContents; ++i) {
+ for (int j = 0; j < DEFAULT_SIZE; ++j) {
+ assertEquals(table.get(i, j), newTable.get(i, j));
+ }
+ }
+ } catch (IOException e) {
+ Log.d(TAG, "IOException while flushing and reloading", e);
+ } finally {
+ if (lookupOutStream != null) {
+ try {
+ lookupOutStream.close();
+ } catch (IOException e) {
+ Log.d(TAG, "IOException while closing the stream", e);
+ }
+ }
+ for (int i = 0; i < numOfContents; ++i) {
+ if (contentsOutStream[i] != null) {
+ try {
+ contentsOutStream[i].close();
+ } catch (IOException e) {
+ Log.d(TAG, "IOException while closing the stream.", e);
+ }
+ }
+ }
+ }
+ }
}
diff --git a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java
index ddc9546c5..7c1decb71 100644
--- a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java
+++ b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java
@@ -109,35 +109,54 @@ public class UserHistoryDictionaryTests extends AndroidTestCase {
dict.close();
}
+ /**
+ * Clear all entries in the user history dictionary.
+ * @param testFilenameSuffix file name suffix used for testing.
+ */
+ private void clearHistory(final String testFilenameSuffix) {
+ final UserHistoryDictionary dict =
+ PersonalizationHelper.getUserHistoryDictionary(getContext(),
+ testFilenameSuffix /* locale */, mPrefs);
+ dict.clearAndFlushDictionary();
+ dict.close();
+ }
+
+ /**
+ * Shut down executer and wait until all operations of user history are done.
+ * @param testFilenameSuffix file name suffix used for testing.
+ */
+ private void waitForWriting(final String testFilenameSuffix) {
+ try {
+ final UserHistoryDictionary dict =
+ PersonalizationHelper.getUserHistoryDictionary(getContext(),
+ testFilenameSuffix, mPrefs);
+ dict.shutdownExecutorForTests();
+ while (!dict.isTerminatedForTests()) {
+ Thread.sleep(WAIT_TERMINATING_IN_MILLISECONDS);
+ }
+ } catch (InterruptedException e) {
+ Log.d(TAG, "InterruptedException: ", e);
+ }
+ }
+
public void testRandomWords() {
- File dictFile = null;
Log.d(TAG, "This test can be used for profiling.");
Log.d(TAG, "Usage: please set UserHistoryDictionary.PROFILE_SAVE_RESTORE to true.");
final String testFilenameSuffix = "testRandomWords" + System.currentTimeMillis();
+ final String fileName = UserHistoryDictionary.NAME + "." + testFilenameSuffix
+ + ExpandableBinaryDictionary.DICT_FILE_EXTENSION;
+
final int numberOfWords = 1000;
final Random random = new Random(123456);
try {
+ clearHistory(testFilenameSuffix);
addAndWriteRandomWords(testFilenameSuffix, numberOfWords, random,
true /* checksContents */);
} finally {
- try {
- final UserHistoryDictionary dict =
- PersonalizationHelper.getUserHistoryDictionary(getContext(),
- testFilenameSuffix, mPrefs);
- Log.d(TAG, "waiting for writing ...");
- dict.shutdownExecutorForTests();
- while (!dict.isTerminatedForTests()) {
- Thread.sleep(WAIT_TERMINATING_IN_MILLISECONDS);
- }
- } catch (InterruptedException e) {
- Log.d(TAG, "InterruptedException: " + e);
- }
-
- final String fileName = UserHistoryDictionary.NAME + "." + testFilenameSuffix
- + ExpandableBinaryDictionary.DICT_FILE_EXTENSION;
- dictFile = new File(getContext().getFilesDir(), fileName);
-
+ Log.d(TAG, "waiting for writing ...");
+ waitForWriting(testFilenameSuffix);
+ final File dictFile = new File(getContext().getFilesDir(), fileName);
if (dictFile != null) {
assertTrue(dictFile.exists());
assertTrue(dictFile.length() >= MIN_USER_HISTORY_DICTIONARY_FILE_SIZE);
@@ -162,6 +181,7 @@ public class UserHistoryDictionaryTests extends AndroidTestCase {
final String fileName = UserHistoryDictionary.NAME + "." +
testFilenameSuffixes[i] + ExpandableBinaryDictionary.DICT_FILE_EXTENSION;
dictFiles[i] = new File(getContext().getFilesDir(), fileName);
+ clearHistory(testFilenameSuffixes[i]);
}
final long start = System.currentTimeMillis();
@@ -178,19 +198,9 @@ public class UserHistoryDictionaryTests extends AndroidTestCase {
Log.d(TAG, "testStressTestForSwitchingLanguageAndAddingWords took "
+ (end - start) + " ms");
} finally {
- try {
- Log.d(TAG, "waiting for writing ...");
- for (int i = 0; i < numberOfLanguages; i++) {
- final UserHistoryDictionary dict =
- PersonalizationHelper.getUserHistoryDictionary(getContext(),
- testFilenameSuffixes[i], mPrefs);
- dict.shutdownExecutorForTests();
- while (!dict.isTerminatedForTests()) {
- Thread.sleep(WAIT_TERMINATING_IN_MILLISECONDS);
- }
- }
- } catch (InterruptedException e) {
- Log.d(TAG, "InterruptedException: " + e);
+ Log.d(TAG, "waiting for writing ...");
+ for (int i = 0; i < numberOfLanguages; i++) {
+ waitForWriting(testFilenameSuffixes[i]);
}
for (final File file : dictFiles) {
if (file != null) {
@@ -203,33 +213,21 @@ public class UserHistoryDictionaryTests extends AndroidTestCase {
}
public void testAddManyWords() {
- File dictFile = null;
final String testFilenameSuffix = "testRandomWords" + System.currentTimeMillis();
final int numberOfWords =
ExpandableBinaryDictionary.ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE ?
10000 : 1000;
final Random random = new Random(123456);
-
- UserHistoryDictionary dict =
- PersonalizationHelper.getUserHistoryDictionary(getContext(),
- testFilenameSuffix, mPrefs);
+ clearHistory(testFilenameSuffix);
try {
addAndWriteRandomWords(testFilenameSuffix, numberOfWords, random,
true /* checksContents */);
- dict.close();
} finally {
- try {
- Log.d(TAG, "waiting for writing ...");
- dict.shutdownExecutorForTests();
- while (!dict.isTerminatedForTests()) {
- Thread.sleep(WAIT_TERMINATING_IN_MILLISECONDS);
- }
- } catch (InterruptedException e) {
- Log.d(TAG, "InterruptedException: ", e);
- }
+ Log.d(TAG, "waiting for writing ...");
+ waitForWriting(testFilenameSuffix);
final String fileName = UserHistoryDictionary.NAME + "." + testFilenameSuffix
+ ExpandableBinaryDictionary.DICT_FILE_EXTENSION;
- dictFile = new File(getContext().getFilesDir(), fileName);
+ final File dictFile = new File(getContext().getFilesDir(), fileName);
if (dictFile != null) {
assertTrue(dictFile.exists());
assertTrue(dictFile.length() >= MIN_USER_HISTORY_DICTIONARY_FILE_SIZE);