aboutsummaryrefslogtreecommitdiffstats
path: root/java
diff options
context:
space:
mode:
Diffstat (limited to 'java')
-rw-r--r--java/AndroidManifest.xml6
-rw-r--r--java/res/color/key_text_color_ics.xml48
-rw-r--r--java/res/drawable-hdpi/sym_keyboard_settings_holo_dark.pngbin889 -> 787 bytes
-rw-r--r--java/res/drawable-hdpi/sym_keyboard_smiley_holo_dark.pngbin0 -> 1767 bytes
-rw-r--r--java/res/drawable-mdpi/sym_keyboard_settings_holo_dark.pngbin708 -> 585 bytes
-rw-r--r--java/res/drawable-mdpi/sym_keyboard_smiley_holo_dark.pngbin897 -> 1170 bytes
-rw-r--r--java/res/drawable-xhdpi/sym_keyboard_settings_holo_dark.pngbin1309 -> 1062 bytes
-rw-r--r--java/res/drawable-xhdpi/sym_keyboard_smiley_holo_dark.pngbin1851 -> 2519 bytes
-rw-r--r--java/res/drawable-xxhdpi/sym_keyboard_settings_holo_dark.pngbin1471 -> 1455 bytes
-rw-r--r--java/res/drawable-xxhdpi/sym_keyboard_smiley_holo_dark.pngbin2571 -> 3118 bytes
-rw-r--r--java/res/layout/emoji_keyboard_tab_icon.xml1
-rw-r--r--java/res/layout/emoji_palettes_view.xml (renamed from java/res/layout/emoji_keyboard_view.xml)10
-rw-r--r--java/res/layout/input_view.xml2
-rw-r--r--java/res/raw/main_en.dictbin1069846 -> 1069849 bytes
-rw-r--r--java/res/raw/main_fr.dictbin1329192 -> 1329192 bytes
-rw-r--r--java/res/values-af/strings.xml4
-rw-r--r--java/res/values-am/strings.xml4
-rw-r--r--java/res/values-ar/strings.xml4
-rw-r--r--java/res/values-be/strings.xml4
-rw-r--r--java/res/values-bg/strings.xml6
-rw-r--r--java/res/values-ca/strings.xml4
-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.xml4
-rw-r--r--java/res/values-el/strings.xml4
-rw-r--r--java/res/values-en-rGB/strings.xml4
-rw-r--r--java/res/values-en-rIN/strings.xml4
-rw-r--r--java/res/values-es-rUS/strings.xml4
-rw-r--r--java/res/values-es/strings.xml4
-rw-r--r--java/res/values-et-rEE/strings.xml4
-rw-r--r--java/res/values-fa/strings.xml4
-rw-r--r--java/res/values-fi/strings.xml6
-rw-r--r--java/res/values-fr-rCA/strings.xml4
-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.xml4
-rw-r--r--java/res/values-hy-rAM/strings.xml6
-rw-r--r--java/res/values-hy/donottranslate.xml3
-rw-r--r--java/res/values-in/strings.xml6
-rw-r--r--java/res/values-it/strings.xml4
-rw-r--r--java/res/values-iw/strings.xml4
-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.xml4
-rw-r--r--java/res/values-lt/strings.xml4
-rw-r--r--java/res/values-lv/strings.xml4
-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.xml4
-rw-r--r--java/res/values-nl/strings.xml4
-rw-r--r--java/res/values-pl/strings.xml4
-rw-r--r--java/res/values-pt-rPT/strings.xml4
-rw-r--r--java/res/values-pt/strings.xml6
-rw-r--r--java/res/values-rm/strings.xml4
-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.xml4
-rw-r--r--java/res/values-sw600dp-land/dimens.xml2
-rw-r--r--java/res/values-sw600dp/dimens.xml2
-rw-r--r--java/res/values-sw768dp-land/dimens.xml2
-rw-r--r--java/res/values-sw768dp/dimens.xml2
-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.xml4
-rw-r--r--java/res/values-v19/emoji-categories.xml44
-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.xml4
-rw-r--r--java/res/values/attrs.xml6
-rw-r--r--java/res/values/colors.xml4
-rw-r--r--java/res/values/config.xml2
-rw-r--r--java/res/values/dimens.xml2
-rw-r--r--java/res/values/donottranslate.xml3
-rw-r--r--java/res/values/strings.xml6
-rw-r--r--java/res/values/themes-common.xml4
-rw-r--r--java/res/values/themes-gb.xml6
-rw-r--r--java/res/values/themes-ics.xml6
-rw-r--r--java/res/xml-sw600dp/keys_comma_period.xml103
-rw-r--r--java/res/xml-sw600dp/keys_exclamation_question.xml4
-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/key_styles_enter.xml164
-rw-r--r--java/res/xml/keys_comma_period.xml34
-rw-r--r--java/res/xml/row_symbols4.xml4
-rw-r--r--java/res/xml/rowkeys_symbols3.xml6
-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/EmojiCategoryPageIndicatorView.java5
-rw-r--r--java/src/com/android/inputmethod/keyboard/EmojiLayoutParams.java6
-rw-r--r--java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java (renamed from java/src/com/android/inputmethod/keyboard/EmojiKeyboardView.java)114
-rw-r--r--java/src/com/android/inputmethod/keyboard/Key.java19
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java32
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardView.java2
-rw-r--r--java/src/com/android/inputmethod/keyboard/PointerTracker.java6
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/CodesArrayParser.java32
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java61
-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/KeyboardBuilder.java8
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java71
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/ScrollKeyboardView.java3
-rw-r--r--java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java11
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionary.java35
-rw-r--r--java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java4
-rw-r--r--java/src/com/android/inputmethod/latin/DictionaryWriter.java4
-rw-r--r--java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java61
-rw-r--r--java/src/com/android/inputmethod/latin/ExpandableDictionary.java30
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java86
-rw-r--r--java/src/com/android/inputmethod/latin/RichInputConnection.java8
-rw-r--r--java/src/com/android/inputmethod/latin/Suggest.java6
-rw-r--r--java/src/com/android/inputmethod/latin/UserBinaryDictionary.java9
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java206
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java9
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java10
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java34
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/DictDecoder.java193
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/DictUpdater.java54
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/DynamicBinaryDictIOUtils.java107
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/FormatSpec.java20
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java5
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/SparseTable.java131
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/Ver3DictDecoder.java47
-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.java89
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java221
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/Ver4DictUpdater.java59
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java11
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/DictionaryDecayBroadcastReciever.java66
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/DynamicPersonalizationDictionaryWriter.java10
-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/settings/SettingsValues.java21
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java14
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java42
-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--java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java20
-rw-r--r--java/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtils.java13
150 files changed, 2106 insertions, 855 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/drawable-hdpi/sym_keyboard_settings_holo_dark.png b/java/res/drawable-hdpi/sym_keyboard_settings_holo_dark.png
index c76008ab3..5af09ad8c 100644
--- a/java/res/drawable-hdpi/sym_keyboard_settings_holo_dark.png
+++ b/java/res/drawable-hdpi/sym_keyboard_settings_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_smiley_holo_dark.png b/java/res/drawable-hdpi/sym_keyboard_smiley_holo_dark.png
new file mode 100644
index 000000000..cfacbc2e7
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_smiley_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_settings_holo_dark.png b/java/res/drawable-mdpi/sym_keyboard_settings_holo_dark.png
index a76a976c5..36c8c9623 100644
--- a/java/res/drawable-mdpi/sym_keyboard_settings_holo_dark.png
+++ b/java/res/drawable-mdpi/sym_keyboard_settings_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_smiley_holo_dark.png b/java/res/drawable-mdpi/sym_keyboard_smiley_holo_dark.png
index 71272bb88..067ad5496 100644
--- a/java/res/drawable-mdpi/sym_keyboard_smiley_holo_dark.png
+++ b/java/res/drawable-mdpi/sym_keyboard_smiley_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_settings_holo_dark.png b/java/res/drawable-xhdpi/sym_keyboard_settings_holo_dark.png
index 05eaffe2e..99ee97dbf 100644
--- a/java/res/drawable-xhdpi/sym_keyboard_settings_holo_dark.png
+++ b/java/res/drawable-xhdpi/sym_keyboard_settings_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_smiley_holo_dark.png b/java/res/drawable-xhdpi/sym_keyboard_smiley_holo_dark.png
index 686831fd3..e6baa2e59 100644
--- a/java/res/drawable-xhdpi/sym_keyboard_smiley_holo_dark.png
+++ b/java/res/drawable-xhdpi/sym_keyboard_smiley_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_settings_holo_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_settings_holo_dark.png
index e4358463b..7041bb6ce 100644
--- a/java/res/drawable-xxhdpi/sym_keyboard_settings_holo_dark.png
+++ b/java/res/drawable-xxhdpi/sym_keyboard_settings_holo_dark.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_smiley_holo_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_smiley_holo_dark.png
index 04b721617..5973ac355 100644
--- a/java/res/drawable-xxhdpi/sym_keyboard_smiley_holo_dark.png
+++ b/java/res/drawable-xxhdpi/sym_keyboard_smiley_holo_dark.png
Binary files differ
diff --git a/java/res/layout/emoji_keyboard_tab_icon.xml b/java/res/layout/emoji_keyboard_tab_icon.xml
index d79276eb9..1609f6a26 100644
--- a/java/res/layout/emoji_keyboard_tab_icon.xml
+++ b/java/res/layout/emoji_keyboard_tab_icon.xml
@@ -23,4 +23,5 @@
android:layout_weight="1.0"
android:layout_height="wrap_content"
android:gravity="center"
+ android:scaleType="center"
/>
diff --git a/java/res/layout/emoji_keyboard_view.xml b/java/res/layout/emoji_palettes_view.xml
index 4566a5a1f..1c6da90ba 100644
--- a/java/res/layout/emoji_keyboard_view.xml
+++ b/java/res/layout/emoji_palettes_view.xml
@@ -18,13 +18,13 @@
*/
-->
-<com.android.inputmethod.keyboard.EmojiKeyboardView
+<com.android.inputmethod.keyboard.EmojiPalettesView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/emoji_keyboard_view"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- style="?attr/emojiKeyboardViewStyle"
+ style="?attr/emojiPalettesViewStyle"
>
<LinearLayout
android:orientation="horizontal"
@@ -101,10 +101,10 @@
android:layout_weight="0.70"
android:layout_height="match_parent" />
<ImageButton
- android:id="@+id/emoji_keyboard_send"
+ android:id="@+id/emoji_keyboard_alphabet2"
android:layout_width="0dip"
android:layout_weight="0.15"
android:layout_height="match_parent"
- android:src="@drawable/sym_keyboard_return_holo_dark" />
+ android:src="@drawable/ic_ime_switcher_dark" />
</LinearLayout>
-</com.android.inputmethod.keyboard.EmojiKeyboardView>
+</com.android.inputmethod.keyboard.EmojiPalettesView>
diff --git a/java/res/layout/input_view.xml b/java/res/layout/input_view.xml
index 0b682d198..1e7a3844e 100644
--- a/java/res/layout/input_view.xml
+++ b/java/res/layout/input_view.xml
@@ -56,5 +56,5 @@
android:layout_height="wrap_content" />
</LinearLayout>
<include
- layout="@layout/emoji_keyboard_view" />
+ layout="@layout/emoji_palettes_view" />
</com.android.inputmethod.latin.InputView>
diff --git a/java/res/raw/main_en.dict b/java/res/raw/main_en.dict
index bef6b1005..09b69927f 100644
--- a/java/res/raw/main_en.dict
+++ b/java/res/raw/main_en.dict
Binary files differ
diff --git a/java/res/raw/main_fr.dict b/java/res/raw/main_fr.dict
index 18f529887..0e5a71360 100644
--- a/java/res/raw/main_fr.dict
+++ b/java/res/raw/main_fr.dict
Binary files differ
diff --git a/java/res/values-af/strings.xml b/java/res/values-af/strings.xml
index f187a73a5..adf9e5bf5 100644
--- a/java/res/values-af/strings.xml
+++ b/java/res/values-af/strings.xml
@@ -84,8 +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>
- <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_auto_correct" msgid="8005997889020109763">"<xliff:g id="KEY">%1$s</xliff:g> korrigeer <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> na <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"<xliff:g id="KEY">%1$s</xliff:g> voer outokorreksie uit"</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 10e791ca7..9fa68f365 100644
--- a/java/res/values-am/strings.xml
+++ b/java/res/values-am/strings.xml
@@ -84,8 +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>
- <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_auto_correct" msgid="8005997889020109763">"<xliff:g id="KEY">%1$s</xliff:g> <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g>ን ወደ <xliff:g id="CORRECTED">%3$s</xliff:g> ያርመዋል"</string>
+ <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"<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 46bff1294..1d58832f4 100644
--- a/java/res/values-ar/strings.xml
+++ b/java/res/values-ar/strings.xml
@@ -84,8 +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>
- <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_auto_correct" msgid="8005997889020109763">"<xliff:g id="KEY">%1$s</xliff:g> لتصحيح <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> إلى <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"<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-be/strings.xml b/java/res/values-be/strings.xml
index 9e591dea9..d9b970807 100644
--- a/java/res/values-be/strings.xml
+++ b/java/res/values-be/strings.xml
@@ -87,9 +87,9 @@
<string name="spoken_use_headphones" msgid="896961781287283493">"Каб праслухаць паролi, падключыце гарнiтуру."</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) -->
+ <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
<skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
+ <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
<skip />
<string name="spoken_description_unknown" msgid="3197434010402179157">"Клавішны код %d"</string>
<string name="spoken_description_shift" msgid="244197883292549308">"Зрух"</string>
diff --git a/java/res/values-bg/strings.xml b/java/res/values-bg/strings.xml
index 0e6c8dd45..70649074a 100644
--- a/java/res/values-bg/strings.xml
+++ b/java/res/values-bg/strings.xml
@@ -84,8 +84,10 @@
<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>
- <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>
+ <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+ <skip />
+ <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+ <skip />
<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 043fbd980..ad3c92e68 100644
--- a/java/res/values-ca/strings.xml
+++ b/java/res/values-ca/strings.xml
@@ -84,8 +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>
- <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_auto_correct" msgid="8005997889020109763">"<xliff:g id="KEY">%1$s</xliff:g> corregeix <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> per <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"<xliff:g id="KEY">%1$s</xliff:g> aplica correccions automàtiques"</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 2d5c386a5..271208db6 100644
--- a/java/res/values-cs/strings.xml
+++ b/java/res/values-cs/strings.xml
@@ -84,8 +84,10 @@
<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>
- <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>
+ <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+ <skip />
+ <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+ <skip />
<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 0a53691b2..0439aafeb 100644
--- a/java/res/values-da/strings.xml
+++ b/java/res/values-da/strings.xml
@@ -84,8 +84,10 @@
<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>
- <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>
+ <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+ <skip />
+ <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+ <skip />
<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 010bbd2e1..28d82aeec 100644
--- a/java/res/values-de/strings.xml
+++ b/java/res/values-de/strings.xml
@@ -84,8 +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>
- <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> erfolgt eine Autokorrektur."</string>
+ <string name="spoken_auto_correct" msgid="8005997889020109763">"Mit <xliff:g id="KEY">%1$s</xliff:g> wird <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> in <xliff:g id="CORRECTED">%3$s</xliff:g> korrigiert."</string>
+ <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"Mit <xliff:g id="KEY">%1$s</xliff:g> erfolgt eine Autokorrektur."</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 8b6d0d135..54b3c3efa 100644
--- a/java/res/values-el/strings.xml
+++ b/java/res/values-el/strings.xml
@@ -84,8 +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>
- <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_auto_correct" msgid="8005997889020109763">"<xliff:g id="KEY">%1$s</xliff:g> διορθώνει το <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> σε <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"<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 1891a3fdb..bdffb94c1 100644
--- a/java/res/values-en-rGB/strings.xml
+++ b/java/res/values-en-rGB/strings.xml
@@ -84,8 +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>
- <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_auto_correct" msgid="8005997889020109763">"<xliff:g id="KEY">%1$s</xliff:g> corrects <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> to <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"<xliff:g id="KEY">%1$s</xliff:g> performs 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 1891a3fdb..bdffb94c1 100644
--- a/java/res/values-en-rIN/strings.xml
+++ b/java/res/values-en-rIN/strings.xml
@@ -84,8 +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>
- <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_auto_correct" msgid="8005997889020109763">"<xliff:g id="KEY">%1$s</xliff:g> corrects <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> to <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"<xliff:g id="KEY">%1$s</xliff:g> performs 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 331eb3878..2d7872aaa 100644
--- a/java/res/values-es-rUS/strings.xml
+++ b/java/res/values-es-rUS/strings.xml
@@ -84,8 +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>
- <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_auto_correct" msgid="8005997889020109763">"La tecla <xliff:g id="KEY">%1$s</xliff:g> corrige <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> por <xliff:g id="CORRECTED">%3$s</xliff:g>."</string>
+ <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"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 73cc978cb..4dfc57fa6 100644
--- a/java/res/values-es/strings.xml
+++ b/java/res/values-es/strings.xml
@@ -84,8 +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>
- <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_auto_correct" msgid="8005997889020109763">"La tecla <xliff:g id="KEY">%1$s</xliff:g> corrige <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> a <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"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 c7e6fe90c..3a27a8892 100644
--- a/java/res/values-et-rEE/strings.xml
+++ b/java/res/values-et-rEE/strings.xml
@@ -84,8 +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>
- <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_auto_correct" msgid="8005997889020109763">"Klahvi <xliff:g id="KEY">%1$s</xliff:g> vajutamisel parandatakse sõna <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> sõnaks <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"Klahvi <xliff:g id="KEY">%1$s</xliff:g> vajutamisel tehakse automaatne parandus"</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 81a331446..a6d9bbafd 100644
--- a/java/res/values-fa/strings.xml
+++ b/java/res/values-fa/strings.xml
@@ -86,8 +86,8 @@
<!-- no translation found for spoken_current_text_is (2485723011272583845) -->
<skip />
<string name="spoken_no_text_entered" msgid="7479685225597344496">"متنی وارد نشده است"</string>
- <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> دارای تصحیح خودکار استj"</string>
+ <string name="spoken_auto_correct" msgid="8005997889020109763">"<xliff:g id="KEY">%1$s</xliff:g>، ‏<xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> را به <xliff:g id="CORRECTED">%3$s</xliff:g> تصحیح می‌کند"</string>
+ <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"<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 8f2caabbf..9f098a172 100644
--- a/java/res/values-fi/strings.xml
+++ b/java/res/values-fi/strings.xml
@@ -84,8 +84,10 @@
<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>
- <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>
+ <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+ <skip />
+ <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+ <skip />
<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 fba029887..0d0c08299 100644
--- a/java/res/values-fr-rCA/strings.xml
+++ b/java/res/values-fr-rCA/strings.xml
@@ -84,8 +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>
- <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_auto_correct" msgid="8005997889020109763">"La touche <xliff:g id="KEY">%1$s</xliff:g> permet de corriger« <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> » par « <xliff:g id="CORRECTED">%3$s</xliff:g> »"</string>
+ <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"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-fr/strings.xml b/java/res/values-fr/strings.xml
index 9d6c8f489..0f92379c9 100644
--- a/java/res/values-fr/strings.xml
+++ b/java/res/values-fr/strings.xml
@@ -84,8 +84,10 @@
<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>
- <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>
+ <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+ <skip />
+ <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+ <skip />
<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 0e9729629..c013e5ff7 100644
--- a/java/res/values-hi/strings.xml
+++ b/java/res/values-hi/strings.xml
@@ -84,8 +84,10 @@
<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>
- <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>
+ <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+ <skip />
+ <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+ <skip />
<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 f5c9ad503..1928fb95f 100644
--- a/java/res/values-hr/strings.xml
+++ b/java/res/values-hr/strings.xml
@@ -84,8 +84,10 @@
<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>
- <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>
+ <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+ <skip />
+ <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+ <skip />
<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 5f4a18106..1a07772f3 100644
--- a/java/res/values-hu/strings.xml
+++ b/java/res/values-hu/strings.xml
@@ -84,8 +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>
- <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_auto_correct" msgid="8005997889020109763">"<xliff:g id="KEY">%1$s</xliff:g> billentyű: <xliff:g id="CORRECTED">%3$s</xliff:g> szóra javítja a következőt: <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"<xliff:g id="KEY">%1$s</xliff:g> billentyű: automatikus javítás"</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 57c0e61d9..f584cfaa8 100644
--- a/java/res/values-hy-rAM/strings.xml
+++ b/java/res/values-hy-rAM/strings.xml
@@ -84,8 +84,10 @@
<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>
- <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>
+ <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+ <skip />
+ <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+ <skip />
<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-hy/donottranslate.xml b/java/res/values-hy/donottranslate.xml
index 4a6d188fb..7b0c56655 100644
--- a/java/res/values-hy/donottranslate.xml
+++ b/java/res/values-hy/donottranslate.xml
@@ -26,4 +26,7 @@
<!-- Symbols that separate words. Adding armenian period and comma. -->
<!-- Don't remove the enclosing double quotes, they protect whitespace (not just U+0020) -->
<string name="symbols_word_separators">"&#x0009;&#x0020;\n"()[]{}*&amp;&lt;&gt;+=|.,;:!?/_\"&#x0589;&#x055D;</string>
+ <!-- The sentence separator code point, for capitalization -->
+ <!-- U+0589: "։" ARMENIAN FULL STOP ; 589h = 1417d -->
+ <integer name="sentence_separator">1417</integer>
</resources>
diff --git a/java/res/values-in/strings.xml b/java/res/values-in/strings.xml
index acd839413..db2167355 100644
--- a/java/res/values-in/strings.xml
+++ b/java/res/values-in/strings.xml
@@ -84,8 +84,10 @@
<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>
- <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>
+ <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+ <skip />
+ <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+ <skip />
<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 830975f2d..ec27fd5a7 100644
--- a/java/res/values-it/strings.xml
+++ b/java/res/values-it/strings.xml
@@ -84,8 +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>
- <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_auto_correct" msgid="8005997889020109763">"<xliff:g id="KEY">%1$s</xliff:g> corregge <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> con <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"<xliff:g id="KEY">%1$s</xliff:g> esegue 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 1ad2c3e45..81d2e406d 100644
--- a/java/res/values-iw/strings.xml
+++ b/java/res/values-iw/strings.xml
@@ -84,8 +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>
- <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_auto_correct" msgid="8005997889020109763">"<xliff:g id="KEY">%1$s</xliff:g> מתקן את <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> ל-<xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"<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 9cda30b73..7e66a7b4a 100644
--- a/java/res/values-ja/strings.xml
+++ b/java/res/values-ja/strings.xml
@@ -84,8 +84,10 @@
<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>
- <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>
+ <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+ <skip />
+ <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+ <skip />
<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 9e46c4d23..b5da8ba59 100644
--- a/java/res/values-ka-rGE/strings.xml
+++ b/java/res/values-ka-rGE/strings.xml
@@ -84,8 +84,10 @@
<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>
- <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>
+ <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+ <skip />
+ <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+ <skip />
<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 34c426944..150ec0242 100644
--- a/java/res/values-km-rKH/strings.xml
+++ b/java/res/values-km-rKH/strings.xml
@@ -84,8 +84,10 @@
<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>
- <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>
+ <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+ <skip />
+ <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+ <skip />
<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 630ae7e4b..2253ce34c 100644
--- a/java/res/values-ko/strings.xml
+++ b/java/res/values-ko/strings.xml
@@ -84,8 +84,10 @@
<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>
- <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>
+ <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+ <skip />
+ <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+ <skip />
<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 0d7f7a27d..1597f9237 100644
--- a/java/res/values-lo-rLA/strings.xml
+++ b/java/res/values-lo-rLA/strings.xml
@@ -84,8 +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>
- <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_auto_correct" msgid="8005997889020109763">"<xliff:g id="KEY">%1$s</xliff:g> ແກ້ໄຂ <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> ເປັນ <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"<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 d40b54bc4..1cb84d00b 100644
--- a/java/res/values-lt/strings.xml
+++ b/java/res/values-lt/strings.xml
@@ -84,8 +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>
- <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_auto_correct" msgid="8005997889020109763">"<xliff:g id="KEY">%1$s</xliff:g> pataiso „<xliff:g id="ORIGINAL_WORD">%2$s</xliff:g>“ į „<xliff:g id="CORRECTED">%3$s</xliff:g>“"</string>
+ <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"<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 cde9c3462..221cc2e05 100644
--- a/java/res/values-lv/strings.xml
+++ b/java/res/values-lv/strings.xml
@@ -84,8 +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>
- <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_auto_correct" msgid="8005997889020109763">"Nospiežot taustiņu <xliff:g id="KEY">%1$s</xliff:g>, “<xliff:g id="ORIGINAL_WORD">%2$s</xliff:g>” tiek labots uz “<xliff:g id="CORRECTED">%3$s</xliff:g>”."</string>
+ <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"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 177b5334f..05ac4acab 100644
--- a/java/res/values-mn-rMN/strings.xml
+++ b/java/res/values-mn-rMN/strings.xml
@@ -84,8 +84,10 @@
<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>
- <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>
+ <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+ <skip />
+ <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+ <skip />
<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 0e8b4eb00..d59791a44 100644
--- a/java/res/values-ms-rMY/strings.xml
+++ b/java/res/values-ms-rMY/strings.xml
@@ -84,8 +84,10 @@
<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>
- <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>
+ <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+ <skip />
+ <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+ <skip />
<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 1bd91b6b4..0a2def8b2 100644
--- a/java/res/values-nb/strings.xml
+++ b/java/res/values-nb/strings.xml
@@ -84,8 +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>
- <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_auto_correct" msgid="8005997889020109763">"<xliff:g id="KEY">%1$s</xliff:g> retter <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> til <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"<xliff:g id="KEY">%1$s</xliff:g> utfører automatisk retting"</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 b1d1bb3dd..60db9f0e8 100644
--- a/java/res/values-nl/strings.xml
+++ b/java/res/values-nl/strings.xml
@@ -84,8 +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>
- <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_auto_correct" msgid="8005997889020109763">"Met <xliff:g id="KEY">%1$s</xliff:g> wordt <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> gecorrigeerd naar <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"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 f830b37c1..c3ed2d897 100644
--- a/java/res/values-pl/strings.xml
+++ b/java/res/values-pl/strings.xml
@@ -84,8 +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>
- <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_auto_correct" msgid="8005997889020109763">"<xliff:g id="KEY">%1$s</xliff:g> poprawia <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> na <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"<xliff:g id="KEY">%1$s</xliff:g> wykonuje 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 dbf34f95a..2a4c2b823 100644
--- a/java/res/values-pt-rPT/strings.xml
+++ b/java/res/values-pt-rPT/strings.xml
@@ -84,8 +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>
- <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_auto_correct" msgid="8005997889020109763">"<xliff:g id="KEY">%1$s</xliff:g> corrige <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> para <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"<xliff:g id="KEY">%1$s</xliff:g> executa 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 3f9837223..8cc071840 100644
--- a/java/res/values-pt/strings.xml
+++ b/java/res/values-pt/strings.xml
@@ -84,8 +84,10 @@
<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>
- <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>
+ <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+ <skip />
+ <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+ <skip />
<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-rm/strings.xml b/java/res/values-rm/strings.xml
index c68d30c0c..b956619c5 100644
--- a/java/res/values-rm/strings.xml
+++ b/java/res/values-rm/strings.xml
@@ -139,9 +139,9 @@
<skip />
<!-- no translation found for spoken_no_text_entered (7479685225597344496) -->
<skip />
- <!-- no translation found for spoken_auto_correct (5381764628886369268) -->
+ <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
<skip />
- <!-- no translation found for spoken_auto_correct_obscured (1186884531440481089) -->
+ <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
<skip />
<!-- no translation found for spoken_description_unknown (3197434010402179157) -->
<skip />
diff --git a/java/res/values-ro/strings.xml b/java/res/values-ro/strings.xml
index f67f58ebd..d8b32e72f 100644
--- a/java/res/values-ro/strings.xml
+++ b/java/res/values-ro/strings.xml
@@ -84,8 +84,10 @@
<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>
- <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>
+ <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+ <skip />
+ <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+ <skip />
<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 8a5b74716..d27207c75 100644
--- a/java/res/values-ru/strings.xml
+++ b/java/res/values-ru/strings.xml
@@ -84,8 +84,10 @@
<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>
- <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>
+ <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+ <skip />
+ <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+ <skip />
<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 3f6706c08..4ca07c94e 100644
--- a/java/res/values-sk/strings.xml
+++ b/java/res/values-sk/strings.xml
@@ -84,8 +84,10 @@
<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>
- <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>
+ <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+ <skip />
+ <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+ <skip />
<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 6c8115e78..34bbe9187 100644
--- a/java/res/values-sl/strings.xml
+++ b/java/res/values-sl/strings.xml
@@ -84,8 +84,10 @@
<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>
- <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>
+ <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+ <skip />
+ <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+ <skip />
<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 92e465c63..4d5df5046 100644
--- a/java/res/values-sr/strings.xml
+++ b/java/res/values-sr/strings.xml
@@ -84,8 +84,10 @@
<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>
- <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>
+ <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+ <skip />
+ <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+ <skip />
<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 009e8299a..84ee1d8d5 100644
--- a/java/res/values-sv/strings.xml
+++ b/java/res/values-sv/strings.xml
@@ -84,8 +84,10 @@
<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>
- <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>
+ <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+ <skip />
+ <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+ <skip />
<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 984b6cb60..2f8f85e04 100644
--- a/java/res/values-sw/strings.xml
+++ b/java/res/values-sw/strings.xml
@@ -84,8 +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>
- <string name="spoken_auto_correct" msgid="5381764628886369268">"<xliff:g id="KEY">%1$s</xliff:g> hurekebisha <xliff:g id="ORIGINAL">%2$s</xliff:g> kuwa <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_auto_correct" msgid="8005997889020109763">"<xliff:g id="KEY">%1$s</xliff:g> hurekebisha <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> kuwa <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"<xliff:g id="KEY">%1$s</xliff:g> hutekeleza 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-sw600dp-land/dimens.xml b/java/res/values-sw600dp-land/dimens.xml
index d067265e3..d79e8ca35 100644
--- a/java/res/values-sw600dp-land/dimens.xml
+++ b/java/res/values-sw600dp-land/dimens.xml
@@ -66,7 +66,7 @@
<!-- Emoji keyboard -->
<fraction name="emoji_keyboard_key_width">10%p</fraction>
<fraction name="emoji_keyboard_row_height">33%p</fraction>
- <fraction name="emoji_keyboard_key_letter_size">85%p</fraction>
+ <fraction name="emoji_keyboard_key_letter_size">70%p</fraction>
<integer name="emoji_keyboard_max_key_count">30</integer>
</resources>
diff --git a/java/res/values-sw600dp/dimens.xml b/java/res/values-sw600dp/dimens.xml
index 591355d3c..b2f4ae043 100644
--- a/java/res/values-sw600dp/dimens.xml
+++ b/java/res/values-sw600dp/dimens.xml
@@ -92,7 +92,7 @@
<!-- Emoji keyboard -->
<fraction name="emoji_keyboard_key_width">12.5%p</fraction>
<fraction name="emoji_keyboard_row_height">33%p</fraction>
- <fraction name="emoji_keyboard_key_letter_size">76%p</fraction>
+ <fraction name="emoji_keyboard_key_letter_size">60%p</fraction>
<integer name="emoji_keyboard_max_key_count">24</integer>
</resources>
diff --git a/java/res/values-sw768dp-land/dimens.xml b/java/res/values-sw768dp-land/dimens.xml
index 664630b4f..ce315b0fc 100644
--- a/java/res/values-sw768dp-land/dimens.xml
+++ b/java/res/values-sw768dp-land/dimens.xml
@@ -67,7 +67,7 @@
<!-- Emoji keyboard -->
<fraction name="emoji_keyboard_key_width">7.69%p</fraction>
<fraction name="emoji_keyboard_row_height">33%p</fraction>
- <fraction name="emoji_keyboard_key_letter_size">68%p</fraction>
+ <fraction name="emoji_keyboard_key_letter_size">60%p</fraction>
<integer name="emoji_keyboard_max_key_count">39</integer>
</resources>
diff --git a/java/res/values-sw768dp/dimens.xml b/java/res/values-sw768dp/dimens.xml
index 1fd933abf..c90da7fed 100644
--- a/java/res/values-sw768dp/dimens.xml
+++ b/java/res/values-sw768dp/dimens.xml
@@ -92,7 +92,7 @@
<!-- Emoji keyboard -->
<fraction name="emoji_keyboard_key_width">10%p</fraction>
<fraction name="emoji_keyboard_row_height">33%p</fraction>
- <fraction name="emoji_keyboard_key_letter_size">76%p</fraction>
+ <fraction name="emoji_keyboard_key_letter_size">68%p</fraction>
<integer name="emoji_keyboard_max_key_count">30</integer>
</resources>
diff --git a/java/res/values-th/strings.xml b/java/res/values-th/strings.xml
index f2e252d4d..06fa120dd 100644
--- a/java/res/values-th/strings.xml
+++ b/java/res/values-th/strings.xml
@@ -84,8 +84,10 @@
<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>
- <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>
+ <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+ <skip />
+ <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+ <skip />
<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 9a3554588..f914958f1 100644
--- a/java/res/values-tl/strings.xml
+++ b/java/res/values-tl/strings.xml
@@ -84,8 +84,10 @@
<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>
- <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>
+ <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+ <skip />
+ <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+ <skip />
<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 ab376e190..bd20e56c9 100644
--- a/java/res/values-tr/strings.xml
+++ b/java/res/values-tr/strings.xml
@@ -84,8 +84,10 @@
<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>
- <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>
+ <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+ <skip />
+ <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+ <skip />
<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 2c516523f..2a8b937c2 100644
--- a/java/res/values-uk/strings.xml
+++ b/java/res/values-uk/strings.xml
@@ -84,8 +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>
- <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_auto_correct" msgid="8005997889020109763">"<xliff:g id="KEY">%1$s</xliff:g> виправляє <xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> на <xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"<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-v19/emoji-categories.xml b/java/res/values-v19/emoji-categories.xml
index 658bbfa83..51aad6ede 100644
--- a/java/res/values-v19/emoji-categories.xml
+++ b/java/res/values-v19/emoji-categories.xml
@@ -204,18 +204,18 @@
name="emoji_symbols"
format="string"
>
- <item>fe82e|0031,20e3</item>
- <item>fe82f|0032,20e3</item>
- <item>fe830|0033,20e3</item>
- <item>fe831|0034,20e3</item>
- <item>fe832|0035,20e3</item>
- <item>fe833|0036,20e3</item>
- <item>fe834|0037,20e3</item>
- <item>fe835|0038,20e3</item>
- <item>fe836|0039,20e3</item>
- <item>fe837|0030,20e3</item>
- <item>1f51f</item>
- <item>fe82c|0023,20e3</item>
+ <item>fe82e|0031,20e3|99</item> <!-- TODO: fix support min sdk version (99) -->
+ <item>fe82f|0032,20e3|99</item> <!-- TODO: fix support min sdk version (99) -->
+ <item>fe830|0033,20e3|99</item> <!-- TODO: fix support min sdk version (99) -->
+ <item>fe831|0034,20e3|99</item> <!-- TODO: fix support min sdk version (99) -->
+ <item>fe832|0035,20e3|99</item> <!-- TODO: fix support min sdk version (99) -->
+ <item>fe833|0036,20e3|99</item> <!-- TODO: fix support min sdk version (99) -->
+ <item>fe834|0037,20e3|99</item> <!-- TODO: fix support min sdk version (99) -->
+ <item>fe835|0038,20e3|99</item> <!-- TODO: fix support min sdk version (99) -->
+ <item>fe836|0039,20e3|99</item> <!-- TODO: fix support min sdk version (99) -->
+ <item>fe837|0030,20e3|99</item> <!-- TODO: fix support min sdk version (99) -->
+ <item>1f51f||99</item> <!-- TODO: fix support min sdk version (99) -->
+ <item>fe82c|0023,20e3|99</item> <!-- TODO: fix support min sdk version (99) -->
<item>1f51d</item>
<item>1f519</item>
<item>1f51b</item>
@@ -875,16 +875,16 @@
<item>1f48e</item>
<item>1f490</item>
<item>1f492</item>
- <item>fe4e5|1f1ef,1f1f5</item>
- <item>fe4e6|1f1fa,1f1f8</item>
- <item>fe4e7|1f1eb,1f1f7</item>
- <item>fe4e8|1f1e9,1f1ea</item>
- <item>fe4e9|1f1ee,1f1f9</item>
- <item>fe4ea|1f1ec,1f1e7</item>
- <item>fe4eb|1f1ea,1f1f8</item>
- <item>fe4ec|1f1f7,1f1fa</item>
- <item>fe4ed|1f1e8,1f1f3</item>
- <item>fe4ee|1f1f0,1f1f7</item>
+ <item>fe4e5|1f1ef,1f1f5|99</item> <!-- TODO: fix support min sdk version (99) -->
+ <item>fe4e6|1f1fa,1f1f8|99</item> <!-- TODO: fix support min sdk version (99) -->
+ <item>fe4e7|1f1eb,1f1f7|99</item> <!-- TODO: fix support min sdk version (99) -->
+ <item>fe4e8|1f1e9,1f1ea|99</item> <!-- TODO: fix support min sdk version (99) -->
+ <item>fe4e9|1f1ee,1f1f9|99</item> <!-- TODO: fix support min sdk version (99) -->
+ <item>fe4ea|1f1ec,1f1e7|99</item> <!-- TODO: fix support min sdk version (99) -->
+ <item>fe4eb|1f1ea,1f1f8|99</item> <!-- TODO: fix support min sdk version (99) -->
+ <item>fe4ec|1f1f7,1f1fa|99</item> <!-- TODO: fix support min sdk version (99) -->
+ <item>fe4ed|1f1e8,1f1f3|99</item> <!-- TODO: fix support min sdk version (99) -->
+ <item>fe4ee|1f1f0,1f1f7|99</item> <!-- TODO: fix support min sdk version (99) -->
</array>
<array
name="emoji_emoticons"
diff --git a/java/res/values-vi/strings.xml b/java/res/values-vi/strings.xml
index 64b804af5..c103e9658 100644
--- a/java/res/values-vi/strings.xml
+++ b/java/res/values-vi/strings.xml
@@ -84,8 +84,10 @@
<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>
- <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>
+ <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+ <skip />
+ <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+ <skip />
<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 7683c848e..6df35f1c1 100644
--- a/java/res/values-zh-rCN/strings.xml
+++ b/java/res/values-zh-rCN/strings.xml
@@ -84,8 +84,10 @@
<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>
- <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>
+ <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+ <skip />
+ <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+ <skip />
<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 0090ac371..194136f6f 100644
--- a/java/res/values-zh-rHK/strings.xml
+++ b/java/res/values-zh-rHK/strings.xml
@@ -84,8 +84,10 @@
<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>
- <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>
+ <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+ <skip />
+ <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+ <skip />
<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 ef3c833db..e9853e28f 100644
--- a/java/res/values-zh-rTW/strings.xml
+++ b/java/res/values-zh-rTW/strings.xml
@@ -84,8 +84,10 @@
<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>
- <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>
+ <!-- no translation found for spoken_auto_correct (8005997889020109763) -->
+ <skip />
+ <!-- no translation found for spoken_auto_correct_obscured (6276420476908833791) -->
+ <skip />
<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 2dafde941..d245c32d4 100644
--- a/java/res/values-zu/strings.xml
+++ b/java/res/values-zu/strings.xml
@@ -84,8 +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>
- <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_auto_correct" msgid="8005997889020109763">"I-<xliff:g id="KEY">%1$s</xliff:g> ilungisa i-<xliff:g id="ORIGINAL_WORD">%2$s</xliff:g> ibe yi-<xliff:g id="CORRECTED">%3$s</xliff:g>"</string>
+ <string name="spoken_auto_correct_obscured" msgid="6276420476908833791">"I-<xliff:g id="KEY">%1$s</xliff:g> yenza ukulungiswa kokuzenzakalela"</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/attrs.xml b/java/res/values/attrs.xml
index 09782143f..31945d020 100644
--- a/java/res/values/attrs.xml
+++ b/java/res/values/attrs.xml
@@ -26,8 +26,8 @@
<attr name="keyboardViewStyle" format="reference" />
<!-- MainKeyboardView style -->
<attr name="mainKeyboardViewStyle" format="reference" />
- <!-- EmojiKeyboardView style -->
- <attr name="emojiKeyboardViewStyle" format="reference" />
+ <!-- EmojiPalettesView style -->
+ <attr name="emojiPalettesViewStyle" format="reference" />
<!-- MoreKeysKeyboard style -->
<attr name="moreKeysKeyboardStyle" format="reference" />
<!-- MoreKeysKeyboardView style -->
@@ -167,7 +167,7 @@
<attr name="suppressKeyPreviewAfterBatchInputDuration" format="integer" />
</declare-styleable>
- <declare-styleable name="EmojiKeyboardView">
+ <declare-styleable name="EmojiPalettesView">
<attr name="emojiTabLabelColor" format="reference" />
</declare-styleable>
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/config.xml b/java/res/values/config.xml
index 465d52cec..66b9b7082 100644
--- a/java/res/values/config.xml
+++ b/java/res/values/config.xml
@@ -103,7 +103,7 @@
-->
<string-array name="auto_correction_threshold_values" translatable="false">
<!-- Off, When auto correction setting is Off, this value is not used. -->
- <item></item>
+ <item>floatMaxValue</item>
<!-- Modest : Suggestion whose normalized score is greater than this value
will be subject to auto-correction. -->
<item>0.185</item>
diff --git a/java/res/values/dimens.xml b/java/res/values/dimens.xml
index 2d626dbcf..18cb262e2 100644
--- a/java/res/values/dimens.xml
+++ b/java/res/values/dimens.xml
@@ -118,7 +118,7 @@
<!-- Emoji keyboard -->
<fraction name="emoji_keyboard_key_width">14.2857%p</fraction>
<fraction name="emoji_keyboard_row_height">33%p</fraction>
- <fraction name="emoji_keyboard_key_letter_size">81%p</fraction>
+ <fraction name="emoji_keyboard_key_letter_size">68%p</fraction>
<integer name="emoji_keyboard_max_key_count">21</integer>
<dimen name="emoji_category_page_id_height">3dp</dimen>
diff --git a/java/res/values/donottranslate.xml b/java/res/values/donottranslate.xml
index 42e692d2f..4733aa257 100644
--- a/java/res/values/donottranslate.xml
+++ b/java/res/values/donottranslate.xml
@@ -31,6 +31,9 @@
<string name="symbols_word_separators">"&#x0009;&#x0020;\n"()[]{}*&amp;&lt;&gt;+=|.,;:!?/_\"</string>
<!-- Word connectors -->
<string name="symbols_word_connectors">\'-</string>
+ <!-- The sentence separator code point, for capitalization -->
+ <!-- U+002E: "." FULL STOP ; 2Eh = 46d -->
+ <integer name="sentence_separator">46</integer>
<!-- Whether this language uses spaces between words -->
<bool name="current_language_has_spaces">true</bool>
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/values/themes-common.xml b/java/res/values/themes-common.xml
index 8e9cfc90b..37607711d 100644
--- a/java/res/values/themes-common.xml
+++ b/java/res/values/themes-common.xml
@@ -104,10 +104,10 @@
<style
name="MainKeyboardView"
parent="KeyboardView" />
- <!-- Though {@link EmojiKeyboardView} doesn't extend {@link KeyboardView}, some views inside it,
+ <!-- Though {@link EmojiPalettesView} doesn't extend {@link KeyboardView}, some views inside it,
for instance delete button, need themed {@link KeyboardView} attributes. -->
<style
- name="EmojiKeyboardView"
+ name="EmojiPalettesView"
parent="KeyboardView"
>
<item name="emojiTabLabelColor">@color/emoji_tab_label_color_ics</item>
diff --git a/java/res/values/themes-gb.xml b/java/res/values/themes-gb.xml
index d9ac4acb2..f52695f55 100644
--- a/java/res/values/themes-gb.xml
+++ b/java/res/values/themes-gb.xml
@@ -23,7 +23,7 @@
<item name="keyboardStyle">@style/Keyboard.GB</item>
<item name="keyboardViewStyle">@style/KeyboardView.GB</item>
<item name="mainKeyboardViewStyle">@style/MainKeyboardView.GB</item>
- <item name="emojiKeyboardViewStyle">@style/EmojiKeyboardView.GB</item>
+ <item name="emojiPalettesViewStyle">@style/EmojiPalettesView.GB</item>
<item name="moreKeysKeyboardStyle">@style/MoreKeysKeyboard.GB</item>
<item name="moreKeysKeyboardViewStyle">@style/MoreKeysKeyboardView.GB</item>
<item name="moreKeysKeyboardContainerStyle">@style/MoreKeysKeyboardContainer.GB</item>
@@ -96,10 +96,10 @@
<item name="spacebarTextColor">@color/spacebar_text_color_gb</item>
<item name="spacebarTextShadowColor">@color/spacebar_text_shadow_color_gb</item>
</style>
- <!-- Though {@link EmojiKeyboardView} doesn't extend {@link KeyboardView}, some views inside it,
+ <!-- Though {@link EmojiPalettesView} doesn't extend {@link KeyboardView}, some views inside it,
for instance delete button, need themed {@link KeyboardView} attributes. -->
<style
- name="EmojiKeyboardView.GB"
+ name="EmojiPalettesView.GB"
parent="KeyboardView.GB"
>
<item name="keyBackground">@drawable/btn_keyboard_key_functional_gb</item>
diff --git a/java/res/values/themes-ics.xml b/java/res/values/themes-ics.xml
index 33dd50c2c..a77e685c2 100644
--- a/java/res/values/themes-ics.xml
+++ b/java/res/values/themes-ics.xml
@@ -23,7 +23,7 @@
<item name="keyboardStyle">@style/Keyboard.ICS</item>
<item name="keyboardViewStyle">@style/KeyboardView.ICS</item>
<item name="mainKeyboardViewStyle">@style/MainKeyboardView.ICS</item>
- <item name="emojiKeyboardViewStyle">@style/EmojiKeyboardView.ICS</item>
+ <item name="emojiPalettesViewStyle">@style/EmojiPalettesView.ICS</item>
<item name="moreKeysKeyboardStyle">@style/MoreKeysKeyboard.ICS</item>
<item name="moreKeysKeyboardViewStyle">@style/MoreKeysKeyboardView.ICS</item>
<item name="moreKeysKeyboardContainerStyle">@style/MoreKeysKeyboardContainer.ICS</item>
@@ -97,10 +97,10 @@
<item name="spacebarTextColor">@color/spacebar_text_color_ics</item>
<item name="spacebarTextShadowColor">@color/spacebar_text_shadow_color_ics</item>
</style>
- <!-- Though {@link EmojiKeyboardView} doesn't extend {@link KeyboardView}, some views inside it,
+ <!-- Though {@link EmojiPalettesView} doesn't extend {@link KeyboardView}, some views inside it,
for instance delete button, need themed {@link KeyboardView} attributes. -->
<style
- name="EmojiKeyboardView.ICS"
+ name="EmojiPalettesView.ICS"
parent="KeyboardView.ICS"
>
<item name="keyBackgroundEmojiFunctional">@drawable/btn_keyboard_key_functional_ics</item>
diff --git a/java/res/xml-sw600dp/keys_comma_period.xml b/java/res/xml-sw600dp/keys_comma_period.xml
new file mode 100644
index 000000000..7604e033d
--- /dev/null
+++ b/java/res/xml-sw600dp/keys_comma_period.xml
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 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.
+*/
+-->
+
+<merge
+ xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
+>
+ <switch>
+ <case
+ latin:mode="email|url"
+ >
+ <Key
+ latin:keyLabel=","
+ latin:keyHintLabel="-"
+ latin:moreKeys="-"
+ latin:backgroundType="functional"
+ latin:keyStyle="hasShiftedLetterHintStyle" />
+ <Key
+ latin:keyLabel="."
+ latin:keyHintLabel="_"
+ latin:moreKeys="_"
+ latin:backgroundType="functional"
+ latin:keyStyle="hasShiftedLetterHintStyle" />
+ </case>
+ <case
+ latin:languageCode="ar"
+ >
+ <Key
+ latin:keyLabel="!text/keylabel_for_apostrophe"
+ latin:keyHintLabel="!text/keyhintlabel_for_apostrophe"
+ latin:moreKeys="!text/more_keys_for_apostrophe"
+ latin:backgroundType="functional"
+ latin:keyStyle="hasShiftedLetterHintStyle" />
+ <Key
+ latin:keyLabel="."
+ latin:keyHintLabel="!text/keyhintlabel_for_arabic_diacritics"
+ latin:keyLabelFlags="hasPopupHint"
+ latin:moreKeys="!text/more_keys_for_arabic_diacritics"
+ latin:backgroundType="functional"
+ latin:keyStyle="hasShiftedLetterHintStyle" />
+ </case>
+ <case
+ latin:languageCode="fa"
+ >
+ <Key
+ latin:keyLabel="!text/keylabel_for_apostrophe"
+ latin:keyHintLabel="!text/keyhintlabel_for_apostrophe"
+ latin:keyLabelFlags="hasPopupHint"
+ latin:moreKeys="!text/more_keys_for_apostrophe"
+ latin:backgroundType="functional"
+ latin:keyStyle="hasShiftedLetterHintStyle" />
+ <Key
+ latin:keyLabel="."
+ latin:keyHintLabel="!text/keyhintlabel_for_arabic_diacritics"
+ latin:keyLabelFlags="hasPopupHint"
+ latin:moreKeys="!text/more_keys_for_arabic_diacritics"
+ latin:backgroundType="functional"
+ latin:keyStyle="hasShiftedLetterHintStyle" />
+ </case>
+ <case
+ latin:languageCode="hy"
+ >
+ <!-- U+055D: "՝" ARMENIAN COMMA -->
+ <Key
+ latin:keyLabel="&#x055D;"
+ latin:backgroundType="functional" />
+ <!-- U+0589: "։" ARMENIAN FULL STOP -->
+ <Key
+ latin:keyLabel="&#x0589;"
+ latin:keyLabelFlags="hasPopupHint"
+ latin:backgroundType="functional"
+ latin:moreKeys="!text/more_keys_for_punctuation" />
+ </case>
+ <default>
+ <Key
+ latin:keyLabel="!text/keylabel_for_tablet_comma"
+ latin:keyHintLabel="!text/keyhintlabel_for_tablet_comma"
+ latin:backgroundType="functional"
+ latin:moreKeys="!text/more_keys_for_tablet_comma" />
+ <Key
+ latin:keyLabel="."
+ latin:keyHintLabel="!text/keyhintlabel_for_period"
+ latin:backgroundType="functional"
+ latin:moreKeys="!text/more_keys_for_period" />
+ </default>
+ </switch>
+</merge>
diff --git a/java/res/xml-sw600dp/keys_exclamation_question.xml b/java/res/xml-sw600dp/keys_exclamation_question.xml
index 983ef3897..cd38282ee 100644
--- a/java/res/xml-sw600dp/keys_exclamation_question.xml
+++ b/java/res/xml-sw600dp/keys_exclamation_question.xml
@@ -22,7 +22,7 @@
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
>
<Key
- latin:keyLabel="\?" />
- <Key
latin:keyLabel="!" />
+ <Key
+ latin:keyLabel="\?" />
</merge>
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/key_styles_enter.xml b/java/res/xml/key_styles_enter.xml
index 5976e95db..568c60270 100644
--- a/java/res/xml/key_styles_enter.xml
+++ b/java/res/xml/key_styles_enter.xml
@@ -21,11 +21,14 @@
<merge
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
>
+ <!-- TODO: Stop using many conditional cases for emoji_key_as_more_key. There are way too many to maintain. -->
<!-- Navigate more keys style -->
<switch>
+ <!-- latin:passwordInput="true" -->
<case
latin:imeAction="actionNext"
latin:navigatePrevious="true"
+ latin:passwordInput="true"
>
<key-style
latin:styleName="navigateMoreKeysStyle"
@@ -35,6 +38,7 @@
<case
latin:imeAction="actionNext"
latin:navigatePrevious="false"
+ latin:passwordInput="true"
>
<key-style
latin:styleName="navigateMoreKeysStyle" />
@@ -42,6 +46,7 @@
<case
latin:imeAction="actionPrevious"
latin:navigateNext="true"
+ latin:passwordInput="true"
>
<key-style
latin:styleName="navigateMoreKeysStyle"
@@ -51,14 +56,15 @@
<case
latin:imeAction="actionPrevious"
latin:navigateNext="false"
+ latin:passwordInput="true"
>
<key-style
latin:styleName="navigateMoreKeysStyle" />
</case>
- <!-- imeAction!="actionNext" and imeAction!="actionPrevious" -->
<case
latin:navigateNext="true"
latin:navigatePrevious="true"
+ latin:passwordInput="true"
>
<key-style
latin:styleName="navigateMoreKeysStyle"
@@ -68,6 +74,7 @@
<case
latin:navigateNext="true"
latin:navigatePrevious="false"
+ latin:passwordInput="true"
>
<key-style
latin:styleName="navigateMoreKeysStyle"
@@ -77,13 +84,166 @@
<case
latin:navigateNext="false"
latin:navigatePrevious="true"
+ latin:passwordInput="true"
>
<key-style
latin:styleName="navigateMoreKeysStyle"
latin:keyLabelFlags="hasPopupHint|preserveCase"
latin:moreKeys="!text/action_previous_as_more_key" />
</case>
- <!-- naviagteNext="false" and navigatePrevious="false" -->
+ <case
+ latin:navigateNext="false"
+ latin:navigatePrevious="false"
+ latin:passwordInput="true"
+ >
+ <key-style
+ latin:styleName="navigateMoreKeysStyle" />
+ </case>
+ <!-- latin:mode="email|url|phone|number|date|time|datetime" -->
+ <case
+ latin:imeAction="actionNext"
+ latin:navigatePrevious="true"
+ latin:mode="email|url|phone|number|date|time|datetime"
+ >
+ <key-style
+ latin:styleName="navigateMoreKeysStyle"
+ latin:keyLabelFlags="hasPopupHint|preserveCase"
+ latin:moreKeys="!text/action_previous_as_more_key" />
+ </case>
+ <case
+ latin:imeAction="actionNext"
+ latin:navigatePrevious="false"
+ latin:mode="email|url|phone|number|date|time|datetime"
+ >
+ <key-style
+ latin:styleName="navigateMoreKeysStyle" />
+ </case>
+ <case
+ latin:imeAction="actionPrevious"
+ latin:navigateNext="true"
+ latin:mode="email|url|phone|number|date|time|datetime"
+ >
+ <key-style
+ latin:styleName="navigateMoreKeysStyle"
+ latin:keyLabelFlags="hasPopupHint|preserveCase"
+ latin:moreKeys="!text/action_next_as_more_key" />
+ </case>
+ <case
+ latin:imeAction="actionPrevious"
+ latin:navigateNext="false"
+ latin:mode="email|url|phone|number|date|time|datetime"
+ >
+ <key-style
+ latin:styleName="navigateMoreKeysStyle" />
+ </case>
+ <case
+ latin:navigateNext="true"
+ latin:navigatePrevious="true"
+ latin:mode="email|url|phone|number|date|time|datetime"
+ >
+ <key-style
+ latin:styleName="navigateMoreKeysStyle"
+ latin:keyLabelFlags="hasPopupHint|preserveCase"
+ latin:moreKeys="!fixedColumnOrder!2,!needsDividers!,!text/action_previous_as_more_key,!text/action_next_as_more_key" />
+ </case>
+ <case
+ latin:navigateNext="true"
+ latin:navigatePrevious="false"
+ latin:mode="email|url|phone|number|date|time|datetime"
+ >
+ <key-style
+ latin:styleName="navigateMoreKeysStyle"
+ latin:keyLabelFlags="hasPopupHint|preserveCase"
+ latin:moreKeys="!text/action_next_as_more_key" />
+ </case>
+ <case
+ latin:navigateNext="false"
+ latin:navigatePrevious="true"
+ latin:mode="email|url|phone|number|date|time|datetime"
+ >
+ <key-style
+ latin:styleName="navigateMoreKeysStyle"
+ latin:keyLabelFlags="hasPopupHint|preserveCase"
+ latin:moreKeys="!text/action_previous_as_more_key" />
+ </case>
+ <case
+ latin:navigateNext="false"
+ latin:navigatePrevious="false"
+ latin:mode="email|url|phone|number|date|time|datetime"
+ >
+ <key-style
+ latin:styleName="navigateMoreKeysStyle" />
+ </case>
+ <!-- default -->
+ <case
+ latin:imeAction="actionNext"
+ latin:navigatePrevious="true"
+ >
+ <key-style
+ latin:styleName="navigateMoreKeysStyle"
+ latin:keyLabelFlags="hasPopupHint|preserveCase"
+ latin:moreKeys="!fixedColumnOrder!2,!needsDividers!,!text/emoji_key_as_more_key,!text/action_previous_as_more_key" />
+ </case>
+ <case
+ latin:imeAction="actionNext"
+ latin:navigatePrevious="false"
+ >
+ <key-style
+ latin:styleName="navigateMoreKeysStyle"
+ latin:moreKeys="!text/emoji_key_as_more_key" />
+ </case>
+ <case
+ latin:imeAction="actionPrevious"
+ latin:navigateNext="true"
+ >
+ <key-style
+ latin:styleName="navigateMoreKeysStyle"
+ latin:keyLabelFlags="hasPopupHint|preserveCase"
+ latin:moreKeys="!fixedColumnOrder!2,!needsDividers!,!text/emoji_key_as_more_key,!text/action_next_as_more_key" />
+ </case>
+ <case
+ latin:imeAction="actionPrevious"
+ latin:navigateNext="false"
+ >
+ <key-style
+ latin:styleName="navigateMoreKeysStyle"
+ latin:moreKeys="!text/emoji_key_as_more_key" />
+ </case>
+ <case
+ latin:navigateNext="true"
+ latin:navigatePrevious="true"
+ >
+ <key-style
+ latin:styleName="navigateMoreKeysStyle"
+ latin:keyLabelFlags="hasPopupHint|preserveCase"
+ latin:moreKeys="!fixedColumnOrder!3,!needsDividers!,!text/emoji_key_as_more_key,!text/action_previous_as_more_key,!text/action_next_as_more_key" />
+ </case>
+ <case
+ latin:navigateNext="true"
+ latin:navigatePrevious="false"
+ >
+ <key-style
+ latin:styleName="navigateMoreKeysStyle"
+ latin:keyLabelFlags="hasPopupHint|preserveCase"
+ latin:moreKeys="!fixedColumnOrder!2,!needsDividers!,!text/emoji_key_as_more_key,!text/action_next_as_more_key" />
+ </case>
+ <case
+ latin:navigateNext="false"
+ latin:navigatePrevious="true"
+ >
+ <key-style
+ latin:styleName="navigateMoreKeysStyle"
+ latin:keyLabelFlags="hasPopupHint|preserveCase"
+ latin:moreKeys="!fixedColumnOrder!2,!needsDividers!,!text/emoji_key_as_more_key,!text/action_previous_as_more_key" />
+ </case>
+ <case
+ latin:navigateNext="false"
+ latin:navigatePrevious="false"
+ >
+ <key-style
+ latin:styleName="navigateMoreKeysStyle"
+ latin:moreKeys="!text/emoji_key_as_more_key" />
+ </case>
<default>
<key-style
latin:styleName="navigateMoreKeysStyle" />
diff --git a/java/res/xml/keys_comma_period.xml b/java/res/xml/keys_comma_period.xml
index 02b46c23a..1b51e45ed 100644
--- a/java/res/xml/keys_comma_period.xml
+++ b/java/res/xml/keys_comma_period.xml
@@ -23,22 +23,6 @@
>
<switch>
<case
- latin:mode="email|url"
- >
- <Key
- latin:keyLabel="."
- latin:keyHintLabel="_"
- latin:moreKeys="_"
- latin:backgroundType="functional"
- latin:keyStyle="hasShiftedLetterHintStyle" />
- <Key
- latin:keyLabel=","
- latin:keyHintLabel="-"
- latin:moreKeys="-"
- latin:backgroundType="functional"
- latin:keyStyle="hasShiftedLetterHintStyle" />
- </case>
- <case
latin:languageCode="ar"
>
<Key
@@ -76,28 +60,28 @@
<case
latin:languageCode="hy"
>
+ <!-- U+055D: "՝" ARMENIAN COMMA -->
+ <Key
+ latin:keyLabel="&#x055D;"
+ latin:backgroundType="functional" />
<!-- U+0589: "։" ARMENIAN FULL STOP -->
<Key
latin:keyLabel="&#x0589;"
latin:keyLabelFlags="hasPopupHint"
latin:backgroundType="functional"
latin:moreKeys="!text/more_keys_for_punctuation" />
- <!-- U+055D: "՝" ARMENIAN COMMA -->
- <Key
- latin:keyLabel="&#x055D;"
- latin:backgroundType="functional" />
</case>
<default>
<Key
- latin:keyLabel="."
- latin:keyHintLabel="!text/keyhintlabel_for_tablet_period"
- latin:backgroundType="functional"
- latin:moreKeys="!text/more_keys_for_tablet_period" />
- <Key
latin:keyLabel="!text/keylabel_for_tablet_comma"
latin:keyHintLabel="!text/keyhintlabel_for_tablet_comma"
latin:backgroundType="functional"
latin:moreKeys="!text/more_keys_for_tablet_comma" />
+ <Key
+ latin:keyLabel="."
+ latin:keyHintLabel="!text/keyhintlabel_for_period"
+ latin:backgroundType="functional"
+ latin:moreKeys="!text/more_keys_for_period" />
</default>
</switch>
</merge>
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/rowkeys_symbols3.xml b/java/res/xml/rowkeys_symbols3.xml
index 9f5e620e6..074078cb6 100644
--- a/java/res/xml/rowkeys_symbols3.xml
+++ b/java/res/xml/rowkeys_symbols3.xml
@@ -54,9 +54,9 @@
latin:keyLabel="!text/keylabel_for_symbols_semicolon"
latin:moreKeys="!text/more_keys_for_symbols_semicolon" />
<Key
- latin:keyLabel="!text/keylabel_for_symbols_question"
- latin:moreKeys="!text/more_keys_for_symbols_question" />
- <Key
latin:keyLabel="!"
latin:moreKeys="!text/more_keys_for_symbols_exclamation" />
+ <Key
+ latin:keyLabel="!text/keylabel_for_symbols_question"
+ latin:moreKeys="!text/more_keys_for_symbols_question" />
</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/EmojiCategoryPageIndicatorView.java b/java/src/com/android/inputmethod/keyboard/EmojiCategoryPageIndicatorView.java
index fed134eb9..e23131a30 100644
--- a/java/src/com/android/inputmethod/keyboard/EmojiCategoryPageIndicatorView.java
+++ b/java/src/com/android/inputmethod/keyboard/EmojiCategoryPageIndicatorView.java
@@ -50,8 +50,9 @@ public class EmojiCategoryPageIndicatorView extends LinearLayout {
@Override
protected void onDraw(Canvas canvas) {
- if (mCategoryPageSize == 0) {
- // If the category is not set yet, just clear and return.
+ if (mCategoryPageSize <= 1) {
+ // If the category is not set yet or contains only one category,
+ // just clear and return.
canvas.drawColor(0);
return;
}
diff --git a/java/src/com/android/inputmethod/keyboard/EmojiLayoutParams.java b/java/src/com/android/inputmethod/keyboard/EmojiLayoutParams.java
index 267fad5cd..ceb44e79f 100644
--- a/java/src/com/android/inputmethod/keyboard/EmojiLayoutParams.java
+++ b/java/src/com/android/inputmethod/keyboard/EmojiLayoutParams.java
@@ -56,7 +56,7 @@ public class EmojiLayoutParams {
- (mKeyVerticalGap - mBottomPadding) / 2;
mEmojiPagerHeight = defaultKeyboardHeight - mEmojiActionBarHeight
- mEmojiCategoryPageIdViewHeight;
- mEmojiPagerBottomMargin = mKeyVerticalGap / 2;
+ mEmojiPagerBottomMargin = 0;
mEmojiKeyboardHeight = mEmojiPagerHeight - mEmojiPagerBottomMargin - 1;
}
@@ -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/EmojiKeyboardView.java b/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java
index eb48d01f6..9779c683c 100644
--- a/java/src/com/android/inputmethod/keyboard/EmojiKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/EmojiPalettesView.java
@@ -60,8 +60,8 @@ import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;
/**
- * View class to implement Emoji keyboards.
- * The Emoji keyboard consists of group of views {@link R.layout#emoji_keyboard_view}.
+ * View class to implement Emoji palettes.
+ * The Emoji keyboard consists of group of views {@link R.layout#emoji_palettes_view}.
* <ol>
* <li> Emoji category tabs.
* <li> Delete button.
@@ -70,19 +70,21 @@ import java.util.concurrent.ConcurrentHashMap;
* </ol>
* Because of the above reasons, this class doesn't extend {@link KeyboardView}.
*/
-public final class EmojiKeyboardView extends LinearLayout implements OnTabChangeListener,
+public final class EmojiPalettesView extends LinearLayout implements OnTabChangeListener,
ViewPager.OnPageChangeListener, View.OnClickListener,
ScrollKeyboardView.OnKeyClickListener {
- private static final String TAG = EmojiKeyboardView.class.getSimpleName();
+ private static final String TAG = EmojiPalettesView.class.getSimpleName();
+ private static final boolean DEBUG_PAGER = false;
private final int mKeyBackgroundId;
private final int mEmojiFunctionalKeyBackgroundId;
private final KeyboardLayoutSet mLayoutSet;
private final ColorStateList mTabLabelColor;
private final DeleteKeyOnTouchListener mDeleteKeyOnTouchListener;
- private EmojiKeyboardAdapter mEmojiKeyboardAdapter;
+ private EmojiPalettesAdapter mEmojiPalettesAdapter;
private TabHost mTabHost;
private ViewPager mEmojiPager;
+ private int mCurrentPagerPosition = 0;
private EmojiCategoryPageIndicatorView mEmojiCategoryPageIndicatorView;
private KeyboardActionListener mKeyboardActionListener = KeyboardActionListener.EMPTY_LISTENER;
@@ -378,11 +380,11 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange
private final EmojiCategory mEmojiCategory;
- public EmojiKeyboardView(final Context context, final AttributeSet attrs) {
- this(context, attrs, R.attr.emojiKeyboardViewStyle);
+ public EmojiPalettesView(final Context context, final AttributeSet attrs) {
+ this(context, attrs, R.attr.emojiPalettesViewStyle);
}
- public EmojiKeyboardView(final Context context, final AttributeSet attrs, final int defStyle) {
+ public EmojiPalettesView(final Context context, final AttributeSet attrs, final int defStyle) {
super(context, attrs, defStyle);
final TypedArray keyboardViewAttr = context.obtainStyledAttributes(attrs,
R.styleable.KeyboardView, defStyle, R.style.KeyboardView);
@@ -391,11 +393,11 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange
mEmojiFunctionalKeyBackgroundId = keyboardViewAttr.getResourceId(
R.styleable.KeyboardView_keyBackgroundEmojiFunctional, 0);
keyboardViewAttr.recycle();
- final TypedArray emojiKeyboardViewAttr = context.obtainStyledAttributes(attrs,
- R.styleable.EmojiKeyboardView, defStyle, R.style.EmojiKeyboardView);
- mTabLabelColor = emojiKeyboardViewAttr.getColorStateList(
- R.styleable.EmojiKeyboardView_emojiTabLabelColor);
- emojiKeyboardViewAttr.recycle();
+ final TypedArray emojiPalettesViewAttr = context.obtainStyledAttributes(attrs,
+ R.styleable.EmojiPalettesView, defStyle, R.style.EmojiPalettesView);
+ mTabLabelColor = emojiPalettesViewAttr.getColorStateList(
+ R.styleable.EmojiPalettesView_emojiTabLabelColor);
+ emojiPalettesViewAttr.recycle();
final KeyboardLayoutSet.Builder builder = new KeyboardLayoutSet.Builder(
context, null /* editorInfo */);
final Resources res = context.getResources();
@@ -453,12 +455,13 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange
mTabHost.setOnTabChangedListener(this);
mTabHost.getTabWidget().setStripEnabled(true);
- mEmojiKeyboardAdapter = new EmojiKeyboardAdapter(mEmojiCategory, mLayoutSet, this);
+ mEmojiPalettesAdapter = new EmojiPalettesAdapter(mEmojiCategory, mLayoutSet, this);
mEmojiPager = (ViewPager)findViewById(R.id.emoji_keyboard_pager);
- mEmojiPager.setAdapter(mEmojiKeyboardAdapter);
+ mEmojiPager.setAdapter(mEmojiPalettesAdapter);
mEmojiPager.setOnPageChangeListener(this);
mEmojiPager.setOffscreenPageLimit(0);
+ mEmojiPager.setPersistentDrawingCache(ViewPager.PERSISTENT_NO_CACHE);
final Resources res = getResources();
final EmojiLayoutParams emojiLp = new EmojiLayoutParams(res);
emojiLp.setPagerProperties(mEmojiPager);
@@ -484,10 +487,10 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange
spaceKey.setTag(Constants.CODE_SPACE);
spaceKey.setOnClickListener(this);
emojiLp.setKeyProperties(spaceKey);
- final ImageView sendKey = (ImageView)findViewById(R.id.emoji_keyboard_send);
- sendKey.setBackgroundResource(mEmojiFunctionalKeyBackgroundId);
- sendKey.setTag(Constants.CODE_ENTER);
- sendKey.setOnClickListener(this);
+ final ImageView alphabetKey2 = (ImageView)findViewById(R.id.emoji_keyboard_alphabet2);
+ alphabetKey2.setBackgroundResource(mEmojiFunctionalKeyBackgroundId);
+ alphabetKey2.setTag(Constants.CODE_SWITCH_ALPHA_SYMBOL);
+ alphabetKey2.setOnClickListener(this);
}
@Override
@@ -505,6 +508,7 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange
setCurrentCategoryId(newPos.first /* categoryId */, false /* force */);
mEmojiCategory.setCurrentCategoryPageId(newPos.second /* categoryPageId */);
updateEmojiCategoryPageIdView();
+ mCurrentPagerPosition = position;
}
@Override
@@ -551,7 +555,7 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange
@Override
public void onKeyClick(final Key key) {
- mEmojiKeyboardAdapter.addRecentKey(key);
+ mEmojiPalettesAdapter.addRecentKey(key);
mEmojiCategory.saveLastTypedCategoryPage();
final int code = key.getCode();
if (code == Constants.CODE_OUTPUT_TEXT) {
@@ -565,6 +569,22 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange
// TODO:
}
+ public void startEmojiPalettes() {
+ if (DEBUG_PAGER) {
+ Log.d(TAG, "allocate emoji palettes memory " + mCurrentPagerPosition);
+ }
+ mEmojiPager.setAdapter(mEmojiPalettesAdapter);
+ mEmojiPager.setCurrentItem(mCurrentPagerPosition);
+ }
+
+ public void stopEmojiPalettes() {
+ if (DEBUG_PAGER) {
+ Log.d(TAG, "deallocate emoji palettes memory");
+ }
+ mEmojiPalettesAdapter.flushPendingRecentKeys();
+ mEmojiPager.setAdapter(null);
+ }
+
public void setKeyboardActionListener(final KeyboardActionListener listener) {
mKeyboardActionListener = listener;
mDeleteKeyOnTouchListener.setKeyboardActionListener(mKeyboardActionListener);
@@ -580,10 +600,18 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange
}
private void setCurrentCategoryId(final int categoryId, final boolean force) {
- if (mEmojiCategory.getCurrentCategoryId() == categoryId && !force) {
+ final int oldCategoryId = mEmojiCategory.getCurrentCategoryId();
+ if (oldCategoryId == categoryId && !force) {
return;
}
+ if (oldCategoryId == CATEGORY_ID_RECENTS) {
+ // Needs to save pending updates for recent keys when we get out of the recents
+ // category because we don't want to move the recent emojis around while the user
+ // is in the recents category.
+ mEmojiPalettesAdapter.flushPendingRecentKeys();
+ }
+
mEmojiCategory.setCurrentCategoryId(categoryId);
final int newTabId = mEmojiCategory.getTabIdFromCategoryId(categoryId);
final int newCategoryPageId = mEmojiCategory.getPageIdFromCategoryId(categoryId);
@@ -596,15 +624,15 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange
}
}
- private static class EmojiKeyboardAdapter extends PagerAdapter {
+ private static class EmojiPalettesAdapter extends PagerAdapter {
private final ScrollKeyboardView.OnKeyClickListener mListener;
private final DynamicGridKeyboard mRecentsKeyboard;
- private final SparseArray<ScrollKeyboardView> mActiveKeyboardView =
+ private final SparseArray<ScrollKeyboardView> mActiveKeyboardViews =
CollectionUtils.newSparseArray();
private final EmojiCategory mEmojiCategory;
private int mActivePosition = 0;
- public EmojiKeyboardAdapter(final EmojiCategory emojiCategory,
+ public EmojiPalettesAdapter(final EmojiCategory emojiCategory,
final KeyboardLayoutSet layoutSet,
final ScrollKeyboardView.OnKeyClickListener listener) {
mEmojiCategory = emojiCategory;
@@ -612,13 +640,23 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange
mRecentsKeyboard = mEmojiCategory.getKeyboard(CATEGORY_ID_RECENTS, 0);
}
+ public void flushPendingRecentKeys() {
+ mRecentsKeyboard.flushPendingRecentKeys();
+ final KeyboardView recentKeyboardView =
+ mActiveKeyboardViews.get(mEmojiCategory.getRecentTabId());
+ if (recentKeyboardView != null) {
+ recentKeyboardView.invalidateAllKeys();
+ }
+ }
+
public void addRecentKey(final Key key) {
if (mEmojiCategory.isInRecentTab()) {
+ mRecentsKeyboard.addPendingKey(key);
return;
}
mRecentsKeyboard.addKeyFirst(key);
final KeyboardView recentKeyboardView =
- mActiveKeyboardView.get(mEmojiCategory.getRecentTabId());
+ mActiveKeyboardViews.get(mEmojiCategory.getRecentTabId());
if (recentKeyboardView != null) {
recentKeyboardView.invalidateAllKeys();
}
@@ -634,7 +672,7 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange
if (mActivePosition == position) {
return;
}
- final ScrollKeyboardView oldKeyboardView = mActiveKeyboardView.get(mActivePosition);
+ final ScrollKeyboardView oldKeyboardView = mActiveKeyboardViews.get(mActivePosition);
if (oldKeyboardView != null) {
oldKeyboardView.releaseCurrentKey();
oldKeyboardView.deallocateMemory();
@@ -644,6 +682,15 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange
@Override
public Object instantiateItem(final ViewGroup container, final int position) {
+ if (DEBUG_PAGER) {
+ Log.d(TAG, "instantiate item: " + position);
+ }
+ final ScrollKeyboardView oldKeyboardView = mActiveKeyboardViews.get(position);
+ if (oldKeyboardView != null) {
+ oldKeyboardView.deallocateMemory();
+ // This may be redundant but wanted to be safer..
+ mActiveKeyboardViews.remove(position);
+ }
final Keyboard keyboard =
mEmojiCategory.getKeyboardFromPagePosition(position);
final LayoutInflater inflater = LayoutInflater.from(container.getContext());
@@ -657,7 +704,7 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange
R.id.emoji_keyboard_scroller);
keyboardView.setScrollView(scrollView);
container.addView(view);
- mActiveKeyboardView.put(position, keyboardView);
+ mActiveKeyboardViews.put(position, keyboardView);
return view;
}
@@ -669,12 +716,19 @@ public final class EmojiKeyboardView extends LinearLayout implements OnTabChange
@Override
public void destroyItem(final ViewGroup container, final int position,
final Object object) {
- final ScrollKeyboardView keyboardView = mActiveKeyboardView.get(position);
+ if (DEBUG_PAGER) {
+ Log.d(TAG, "destroy item: " + position + ", " + object.getClass().getSimpleName());
+ }
+ final ScrollKeyboardView keyboardView = mActiveKeyboardViews.get(position);
if (keyboardView != null) {
keyboardView.deallocateMemory();
- mActiveKeyboardView.remove(position);
+ mActiveKeyboardViews.remove(position);
+ }
+ if (object instanceof View) {
+ container.removeView((View)object);
+ } else {
+ Log.w(TAG, "Warning!!! Emoji palette may be leaking. " + object);
}
- container.removeView(keyboardView);
}
}
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..97609837e 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -68,7 +68,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
private InputView mCurrentInputView;
private View mMainKeyboardFrame;
private MainKeyboardView mKeyboardView;
- private EmojiKeyboardView mEmojiKeyboardView;
+ private EmojiPalettesView mEmojiPalettesView;
private LatinIME mLatinIME;
private Resources mResources;
@@ -155,7 +155,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
}
public void saveKeyboardState() {
- if (getKeyboard() != null) {
+ if (getKeyboard() != null || isShowingEmojiKeyboard()) {
mState.onSaveKeyboardState();
}
}
@@ -169,7 +169,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
}
private void setKeyboard(final Keyboard keyboard) {
- // Make {@link MainKeyboardView} visible and hide {@link EmojiKeyboardView}.
+ // Make {@link MainKeyboardView} visible and hide {@link EmojiPalettesView}.
setMainKeyboardFrame();
final MainKeyboardView keyboardView = mKeyboardView;
final Keyboard oldKeyboard = keyboardView.getKeyboard();
@@ -259,14 +259,16 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
private void setMainKeyboardFrame() {
mMainKeyboardFrame.setVisibility(View.VISIBLE);
- mEmojiKeyboardView.setVisibility(View.GONE);
+ mEmojiPalettesView.setVisibility(View.GONE);
+ mEmojiPalettesView.stopEmojiPalettes();
}
// Implements {@link KeyboardState.SwitchActions}.
@Override
public void setEmojiKeyboard() {
mMainKeyboardFrame.setVisibility(View.GONE);
- mEmojiKeyboardView.setVisibility(View.VISIBLE);
+ mEmojiPalettesView.startEmojiPalettes();
+ mEmojiPalettesView.setVisibility(View.VISIBLE);
}
// Implements {@link KeyboardState.SwitchActions}.
@@ -315,7 +317,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
}
public boolean isShowingEmojiKeyboard() {
- return mEmojiKeyboardView != null && mEmojiKeyboardView.getVisibility() == View.VISIBLE;
+ return mEmojiPalettesView != null && mEmojiPalettesView.getVisibility() == View.VISIBLE;
}
public boolean isShowingMoreKeysPanel() {
@@ -327,7 +329,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
public View getVisibleKeyboardView() {
if (isShowingEmojiKeyboard()) {
- return mEmojiKeyboardView;
+ return mEmojiPalettesView;
}
return mKeyboardView;
}
@@ -336,6 +338,16 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
return mKeyboardView;
}
+ public void deallocateMemory() {
+ if (mKeyboardView != null) {
+ mKeyboardView.cancelAllOngoingEvents();
+ mKeyboardView.deallocateMemory();
+ }
+ if (mEmojiPalettesView != null) {
+ mEmojiPalettesView.stopEmojiPalettes();
+ }
+ }
+
public View onCreateInputView(final boolean isHardwareAcceleratedDrawingEnabled) {
if (mKeyboardView != null) {
mKeyboardView.closing();
@@ -345,15 +357,15 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
mCurrentInputView = (InputView)LayoutInflater.from(mThemeContext).inflate(
R.layout.input_view, null);
mMainKeyboardFrame = mCurrentInputView.findViewById(R.id.main_keyboard_frame);
- mEmojiKeyboardView = (EmojiKeyboardView)mCurrentInputView.findViewById(
+ mEmojiPalettesView = (EmojiPalettesView)mCurrentInputView.findViewById(
R.id.emoji_keyboard_view);
mKeyboardView = (MainKeyboardView) mCurrentInputView.findViewById(R.id.keyboard_view);
mKeyboardView.setHardwareAcceleratedDrawingEnabled(isHardwareAcceleratedDrawingEnabled);
mKeyboardView.setKeyboardActionListener(mLatinIME);
- mEmojiKeyboardView.setHardwareAcceleratedDrawingEnabled(
+ mEmojiPalettesView.setHardwareAcceleratedDrawingEnabled(
isHardwareAcceleratedDrawingEnabled);
- mEmojiKeyboardView.setKeyboardActionListener(mLatinIME);
+ mEmojiPalettesView.setKeyboardActionListener(mLatinIME);
// This always needs to be set since the accessibility state can
// potentially change without the input view being re-created.
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardView.java b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
index aeb9e67b2..5578713a0 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardView.java
@@ -243,6 +243,8 @@ public class KeyboardView extends View {
}
private void freeOffscreenBuffer() {
+ mOffscreenCanvas.setBitmap(null);
+ mOffscreenCanvas.setMatrix(null);
if (mOffscreenBuffer != null) {
mOffscreenBuffer.recycle();
mOffscreenBuffer = null;
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index ee4ac950c..52f190e77 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -823,14 +823,16 @@ public final class PointerTracker implements PointerTrackerQueue.Element {
final int size = sAggregratedPointers.getPointerSize();
if (size > sLastRecognitionPointSize
&& stroke.hasRecognitionTimePast(eventTime, sLastRecognitionTime)) {
- sLastRecognitionPointSize = size;
- sLastRecognitionTime = eventTime;
if (DEBUG_LISTENER) {
Log.d(TAG, String.format("[%d] onUpdateBatchInput: batchPoints=%d", mPointerId,
size));
}
mTimerProxy.startUpdateBatchInputTimer(this);
mListener.onUpdateBatchInput(sAggregratedPointers);
+ // The listener may change the size of the pointers (when auto-committing
+ // for example), so we need to get the size from the pointers again.
+ sLastRecognitionPointSize = sAggregratedPointers.getPointerSize();
+ sLastRecognitionTime = eventTime;
}
}
}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/CodesArrayParser.java b/java/src/com/android/inputmethod/keyboard/internal/CodesArrayParser.java
index c10fdbace..4ccecb2f0 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/CodesArrayParser.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/CodesArrayParser.java
@@ -18,6 +18,8 @@ package com.android.inputmethod.keyboard.internal;
import com.android.inputmethod.latin.Constants;
+import android.text.TextUtils;
+
/**
* The string parser of codesArray specification for <GridRows />. The attribute codesArray is an
* array of string.
@@ -34,7 +36,7 @@ import com.android.inputmethod.latin.Constants;
public final class CodesArrayParser {
// Constants for parsing.
private static final char COMMA = ',';
- private static final char VERTICAL_BAR = '|';
+ private static final String VERTICAL_BAR_STRING = "\\|";
private static final String COMMA_STRING = ",";
private static final int BASE_HEX = 16;
@@ -43,8 +45,11 @@ public final class CodesArrayParser {
}
private static String getLabelSpec(final String codesArraySpec) {
- final int pos = codesArraySpec.indexOf(VERTICAL_BAR);
- return (pos < 0) ? codesArraySpec : codesArraySpec.substring(0, pos);
+ final String[] strs = codesArraySpec.split(VERTICAL_BAR_STRING, -1);
+ if (strs.length <= 1) {
+ return codesArraySpec;
+ }
+ return strs[0];
}
public static String parseLabel(final String codesArraySpec) {
@@ -58,8 +63,25 @@ public final class CodesArrayParser {
}
private static String getCodeSpec(final String codesArraySpec) {
- final int pos = codesArraySpec.indexOf(VERTICAL_BAR);
- return (pos < 0) ? codesArraySpec : codesArraySpec.substring(pos + 1);
+ final String[] strs = codesArraySpec.split(VERTICAL_BAR_STRING, -1);
+ if (strs.length <= 1) {
+ return codesArraySpec;
+ }
+ return TextUtils.isEmpty(strs[1]) ? strs[0] : strs[1];
+ }
+
+ // codesArraySpec consists of:
+ // <label>|<code0>,<code1>,...|<minSupportSdkVersion>
+ public static int getMinSupportSdkVersion(final String codesArraySpec) {
+ final String[] strs = codesArraySpec.split(VERTICAL_BAR_STRING, -1);
+ if (strs.length <= 2) {
+ return 0;
+ }
+ try {
+ return Integer.parseInt(strs[2]);
+ } catch (NumberFormatException e) {
+ return 0;
+ }
}
public static int parseCode(final String codesArraySpec) {
diff --git a/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java b/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java
index 0dd71e2ec..09766ac6c 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/DynamicGridKeyboard.java
@@ -20,7 +20,7 @@ import android.content.SharedPreferences;
import android.text.TextUtils;
import android.util.Log;
-import com.android.inputmethod.keyboard.EmojiKeyboardView;
+import com.android.inputmethod.keyboard.EmojiPalettesView;
import com.android.inputmethod.keyboard.Key;
import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.latin.settings.Settings;
@@ -39,15 +39,16 @@ public class DynamicGridKeyboard extends Keyboard {
private static final String TAG = DynamicGridKeyboard.class.getSimpleName();
private static final int TEMPLATE_KEY_CODE_0 = 0x30;
private static final int TEMPLATE_KEY_CODE_1 = 0x31;
+ private final Object mLock = new Object();
private final SharedPreferences mPrefs;
- private final int mLeftPadding;
private final int mHorizontalStep;
private final int mVerticalStep;
private final int mColumnsNum;
private final int mMaxKeyCount;
private final boolean mIsRecents;
private final ArrayDeque<GridKey> mGridKeys = CollectionUtils.newArrayDeque();
+ private final ArrayDeque<Key> mPendingKeys = CollectionUtils.newArrayDeque();
private Key[] mCachedGridKeys;
@@ -56,12 +57,11 @@ public class DynamicGridKeyboard extends Keyboard {
super(templateKeyboard);
final Key key0 = getTemplateKey(TEMPLATE_KEY_CODE_0);
final Key key1 = getTemplateKey(TEMPLATE_KEY_CODE_1);
- mLeftPadding = key0.getX();
mHorizontalStep = Math.abs(key1.getX() - key0.getX());
mVerticalStep = key0.getHeight() + mVerticalGap;
mColumnsNum = mBaseWidth / mHorizontalStep;
mMaxKeyCount = maxKeyCount;
- mIsRecents = categoryId == EmojiKeyboardView.CATEGORY_ID_RECENTS;
+ mIsRecents = categoryId == EmojiPalettesView.CATEGORY_ID_RECENTS;
mPrefs = prefs;
}
@@ -74,6 +74,21 @@ public class DynamicGridKeyboard extends Keyboard {
throw new RuntimeException("Can't find template key: code=" + code);
}
+ public void addPendingKey(final Key usedKey) {
+ synchronized (mLock) {
+ mPendingKeys.addLast(usedKey);
+ }
+ }
+
+ public void flushPendingRecentKeys() {
+ synchronized (mLock) {
+ while (!mPendingKeys.isEmpty()) {
+ addKey(mPendingKeys.pollFirst(), true);
+ }
+ saveRecentKeys();
+ }
+ }
+
public void addKeyFirst(final Key usedKey) {
addKey(usedKey, true);
if (mIsRecents) {
@@ -89,7 +104,7 @@ public class DynamicGridKeyboard extends Keyboard {
if (usedKey == null) {
return;
}
- synchronized (mGridKeys) {
+ synchronized (mLock) {
mCachedGridKeys = null;
final GridKey key = new GridKey(usedKey);
while (mGridKeys.remove(key)) {
@@ -105,9 +120,11 @@ public class DynamicGridKeyboard extends Keyboard {
}
int index = 0;
for (final GridKey gridKey : mGridKeys) {
- final int keyX = getKeyX(index);
- final int keyY = getKeyY(index);
- gridKey.updateCorrdinates(keyX, keyY);
+ final int keyX0 = getKeyX0(index);
+ final int keyY0 = getKeyY0(index);
+ final int keyX1 = getKeyX1(index);
+ final int keyY1 = getKeyY1(index);
+ gridKey.updateCorrdinates(keyX0, keyY0, keyX1, keyY1);
index++;
}
}
@@ -155,19 +172,29 @@ public class DynamicGridKeyboard extends Keyboard {
}
}
- private int getKeyX(final int index) {
+ private int getKeyX0(final int index) {
final int column = index % mColumnsNum;
- return column * mHorizontalStep + mLeftPadding;
+ return column * mHorizontalStep;
+ }
+
+ private int getKeyX1(final int index) {
+ final int column = index % mColumnsNum + 1;
+ return column * mHorizontalStep;
}
- private int getKeyY(final int index) {
+ private int getKeyY0(final int index) {
final int row = index / mColumnsNum;
- return row * mVerticalStep + mTopPadding;
+ return row * mVerticalStep;
+ }
+
+ private int getKeyY1(final int index) {
+ final int row = index / mColumnsNum + 1;
+ return row * mVerticalStep;
}
@Override
public Key[] getKeys() {
- synchronized (mGridKeys) {
+ synchronized (mLock) {
if (mCachedGridKeys != null) {
return mCachedGridKeys;
}
@@ -190,10 +217,10 @@ public class DynamicGridKeyboard extends Keyboard {
super(originalKey);
}
- public void updateCorrdinates(final int x, final int y) {
- mCurrentX = x;
- mCurrentY = y;
- getHitBox().set(x, y, x + getWidth(), y + getHeight());
+ public void updateCorrdinates(final int x0, final int y0, final int x1, final int y1) {
+ mCurrentX = x0;
+ mCurrentY = y0;
+ getHitBox().set(x0, y0, x1, y1);
}
@Override
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/KeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
index 22f7a83fc..c1ae65695 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
@@ -20,6 +20,7 @@ import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
+import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
@@ -436,17 +437,24 @@ public class KeyboardBuilder<KP extends KeyboardParams> {
final String label;
final int code;
final String outputText;
+ final int supportedMinSdkVersion;
if (codesArrayId != 0) {
final String codeArraySpec = array[i];
label = CodesArrayParser.parseLabel(codeArraySpec);
code = CodesArrayParser.parseCode(codeArraySpec);
outputText = CodesArrayParser.parseOutputText(codeArraySpec);
+ supportedMinSdkVersion =
+ CodesArrayParser.getMinSupportSdkVersion(codeArraySpec);
} else {
final String textArraySpec = array[i];
// TODO: Utilize KeySpecParser or write more generic TextsArrayParser.
label = textArraySpec;
code = Constants.CODE_OUTPUT_TEXT;
outputText = textArraySpec + (char)Constants.CODE_SPACE;
+ supportedMinSdkVersion = 0;
+ }
+ if (Build.VERSION.SDK_INT < supportedMinSdkVersion) {
+ continue;
}
final int x = (int)row.getKeyX(null);
final int y = row.getKeyY();
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
index 67553fb75..bcb80b455 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java
@@ -209,8 +209,8 @@ public final class KeyboardTextsSet {
/* 104 */ "keylabel_for_tablet_comma",
/* 105 */ "keyhintlabel_for_tablet_comma",
/* 106 */ "more_keys_for_tablet_comma",
- /* 107 */ "keyhintlabel_for_tablet_period",
- /* 108 */ "more_keys_for_tablet_period",
+ /* 107 */ "keyhintlabel_for_period",
+ /* 108 */ "more_keys_for_period",
/* 109 */ "keylabel_for_apostrophe",
/* 110 */ "keyhintlabel_for_apostrophe",
/* 111 */ "more_keys_for_apostrophe",
@@ -251,6 +251,7 @@ public final class KeyboardTextsSet {
/* 146 */ "more_keys_for_single_quote",
/* 147 */ "more_keys_for_double_quote",
/* 148 */ "more_keys_for_tablet_double_quote",
+ /* 149 */ "emoji_key_as_more_key",
};
private static final String EMPTY = "";
@@ -439,6 +440,7 @@ public final class KeyboardTextsSet {
/* 146 */ "!fixedColumnOrder!5,!text/single_quotes,!text/single_angle_quotes",
/* 147 */ "!fixedColumnOrder!5,!text/double_quotes,!text/double_angle_quotes",
/* 148 */ "!fixedColumnOrder!6,!text/double_quotes,!text/single_quotes,!text/double_angle_quotes,!text/single_angle_quotes",
+ /* 149 */ "!icon/emoji_key|!code/key_emoji",
};
/* Language af: Afrikaans */
@@ -2893,33 +2895,69 @@ public final class KeyboardTextsSet {
/* Language sv: Swedish */
private static final String[] LANGUAGE_sv = {
- /* 0 */ null,
+ // U+00E1: "á" LATIN SMALL LETTER A WITH ACUTE
+ // U+00E0: "à" LATIN SMALL LETTER A WITH GRAVE
+ // U+00E2: "â" LATIN SMALL LETTER A WITH CIRCUMFLEX
+ // U+0105: "ą" LATIN SMALL LETTER A WITH OGONEK
+ // U+00E3: "ã" LATIN SMALL LETTER A WITH TILDE
+ /* 0 */ "\u00E1,\u00E0,\u00E2,\u0105,\u00E3",
// U+00E9: "é" LATIN SMALL LETTER E WITH ACUTE
// U+00E8: "è" LATIN SMALL LETTER E WITH GRAVE
// U+00EA: "ê" LATIN SMALL LETTER E WITH CIRCUMFLEX
// U+00EB: "ë" LATIN SMALL LETTER E WITH DIAERESIS
// U+0119: "ę" LATIN SMALL LETTER E WITH OGONEK
/* 1 */ "\u00E9,\u00E8,\u00EA,\u00EB,\u0119",
- /* 2 */ null,
- // U+0153: "œ" LATIN SMALL LIGATURE OE
- // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
- // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
+ // U+00ED: "í" LATIN SMALL LETTER I WITH ACUTE
+ // U+00EC: "ì" LATIN SMALL LETTER I WITH GRAVE
+ // U+00EE: "î" LATIN SMALL LETTER I WITH CIRCUMFLEX
+ // U+00EF: "ï" LATIN SMALL LETTER I WITH DIAERESIS
+ /* 2 */ "\u00ED,\u00EC,\u00EE,\u00EF",
// U+00F3: "ó" LATIN SMALL LETTER O WITH ACUTE
+ // U+00F2: "ò" LATIN SMALL LETTER O WITH GRAVE
+ // U+00F4: "ô" LATIN SMALL LETTER O WITH CIRCUMFLEX
// U+00F5: "õ" LATIN SMALL LETTER O WITH TILDE
// U+014D: "ō" LATIN SMALL LETTER O WITH MACRON
- /* 3 */ "\u0153,\u00F4,\u00F2,\u00F3,\u00F5,\u014D",
+ /* 3 */ "\u00F3,\u00F2,\u00F4,\u00F5,\u014D",
// U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
- // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
- // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
// U+00FA: "ú" LATIN SMALL LETTER U WITH ACUTE
+ // U+00F9: "ù" LATIN SMALL LETTER U WITH GRAVE
+ // U+00FB: "û" LATIN SMALL LETTER U WITH CIRCUMFLEX
// U+016B: "ū" LATIN SMALL LETTER U WITH MACRON
- /* 4 */ "\u00FC,\u00FB,\u00F9,\u00FA,\u016B",
- // U+00DF: "ß" LATIN SMALL LETTER SHARP S
+ /* 4 */ "\u00FC,\u00FA,\u00F9,\u00FB,\u016B",
// U+015B: "ś" LATIN SMALL LETTER S WITH ACUTE
// U+0161: "š" LATIN SMALL LETTER S WITH CARON
- /* 5 */ "\u00DF,\u015B,\u0161",
- /* 6~ */
- null, null, null, null, null, null, null, null, null, null, null, null, null, null,
+ // U+015F: "ş" LATIN SMALL LETTER S WITH CEDILLA
+ // U+00DF: "ß" LATIN SMALL LETTER SHARP S
+ /* 5 */ "\u015B,\u0161,\u015F,\u00DF",
+ // U+0144: "ń" LATIN SMALL LETTER N WITH ACUTE
+ // U+00F1: "ñ" LATIN SMALL LETTER N WITH TILDE
+ // U+0148: "ň" LATIN SMALL LETTER N WITH CARON
+ /* 6 */ "\u0144,\u00F1,\u0148",
+ // U+00E7: "ç" LATIN SMALL LETTER C WITH CEDILLA
+ // U+0107: "ć" LATIN SMALL LETTER C WITH ACUTE
+ // U+010D: "č" LATIN SMALL LETTER C WITH CARON
+ /* 7 */ "\u00E7,\u0107,\u010D",
+ // U+00FD: "ý" LATIN SMALL LETTER Y WITH ACUTE
+ // U+00FF: "ÿ" LATIN SMALL LETTER Y WITH DIAERESIS
+ // U+00FC: "ü" LATIN SMALL LETTER U WITH DIAERESIS
+ /* 8 */ "\u00FD,\u00FF,\u00FC",
+ // U+00F0: "ð" LATIN SMALL LETTER ETH
+ // U+010F: "ď" LATIN SMALL LETTER D WITH CARON
+ /* 9 */ "\u00F0,\u010F",
+ // U+0159: "ř" LATIN SMALL LETTER R WITH CARON
+ /* 10 */ "\u0159",
+ // U+0165: "ť" LATIN SMALL LETTER T WITH CARON
+ // U+00FE: "þ" LATIN SMALL LETTER THORN
+ /* 11 */ "\u0165,\u00FE",
+ // U+017A: "ź" LATIN SMALL LETTER Z WITH ACUTE
+ // U+017E: "ž" LATIN SMALL LETTER Z WITH CARON
+ // U+017C: "ż" LATIN SMALL LETTER Z WITH DOT ABOVE
+ /* 12 */ "\u017A,\u017E,\u017C",
+ /* 13 */ null,
+ // U+0142: "ł" LATIN SMALL LETTER L WITH STROKE
+ /* 14 */ "\u0142",
+ /* 15~ */
+ null, null, null, null, null,
/* ~19 */
// U+00E5: "å" LATIN SMALL LETTER A WITH RING ABOVE
/* 20 */ "\u00E5",
@@ -2928,7 +2966,8 @@ public final class KeyboardTextsSet {
// U+00E4: "ä" LATIN SMALL LETTER A WITH DIAERESIS
/* 22 */ "\u00E4",
// U+00F8: "ø" LATIN SMALL LETTER O WITH STROKE
- /* 23 */ "\u00F8",
+ // U+0153: "œ" LATIN SMALL LIGATURE OE
+ /* 23 */ "\u00F8,\u0153",
// U+00E6: "æ" LATIN SMALL LETTER AE
/* 24 */ "\u00E6",
/* 25~ */
diff --git a/java/src/com/android/inputmethod/keyboard/internal/ScrollKeyboardView.java b/java/src/com/android/inputmethod/keyboard/internal/ScrollKeyboardView.java
index b8ee976e8..9cf68d43d 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/ScrollKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/ScrollKeyboardView.java
@@ -30,8 +30,9 @@ import com.android.inputmethod.keyboard.KeyboardView;
import com.android.inputmethod.latin.R;
/**
- * This is an extended {@link KeyboardView} class that hosts a scroll keyboard.
+ * This is an extended {@link KeyboardView} class that hosts a vertical scroll keyboard.
* Multi-touch unsupported. No {@link PointerTracker}s. No gesture support.
+ * TODO: Vertical scroll capability should be removed from this class because it's no longer used.
*/
// TODO: Implement key popup preview.
public final class ScrollKeyboardView extends KeyboardView implements
diff --git a/java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java b/java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java
index 4a0ce3735..463d09344 100644
--- a/java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java
+++ b/java/src/com/android/inputmethod/latin/AbstractDictionaryWriter.java
@@ -41,8 +41,17 @@ abstract public class AbstractDictionaryWriter extends Dictionary {
abstract public void clear();
+ /**
+ * Add a unigram with an optional shortcut to the dictionary.
+ * @param word The word to add.
+ * @param shortcutTarget A shortcut target for this word, or null if none.
+ * @param frequency The frequency for this unigram.
+ * @param shortcutFreq The frequency of the shortcut (0~15, with 15 = whitelist). Ignored
+ * if shortcutTarget is null.
+ * @param isNotAWord true if this is not a word, i.e. shortcut only.
+ */
abstract public void addUnigramWord(final String word, final String shortcutTarget,
- final int frequency, final boolean isNotAWord);
+ final int frequency, final int shortcutFreq, final boolean isNotAWord);
// TODO: Remove lastModifiedTime after making binary dictionary support forgetting curve.
abstract public void addBigramWords(final String word0, final String word1,
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index 29c6c0451..fd296988e 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -44,14 +44,18 @@ public final class BinaryDictionary extends Dictionary {
private static final int MAX_WORD_LENGTH = Constants.DICTIONARY_MAX_WORD_LENGTH;
// Must be equal to MAX_RESULTS in native/jni/src/defines.h
private static final int MAX_RESULTS = 18;
- // Required space count for auto commit.
- // TODO: Remove this heuristic.
- private static final int SPACE_COUNT_FOR_AUTO_COMMIT = 3;
+ // The cutoff returned by native for auto-commit confidence.
+ // Must be equal to CONFIDENCE_TO_AUTO_COMMIT in native/jni/src/defines.h
+ private static final int CONFIDENCE_TO_AUTO_COMMIT = 1000000;
@UsedForTesting
public static final String UNIGRAM_COUNT_QUERY = "UNIGRAM_COUNT";
@UsedForTesting
public static final String BIGRAM_COUNT_QUERY = "BIGRAM_COUNT";
+ @UsedForTesting
+ public static final String MAX_UNIGRAM_COUNT_QUERY = "MAX_UNIGRAM_COUNT";
+ @UsedForTesting
+ public static final String MAX_BIGRAM_COUNT_QUERY = "MAX_BIGRAM_COUNT";
private long mNativeDict;
private final Locale mLocale;
@@ -62,7 +66,8 @@ public final class BinaryDictionary extends Dictionary {
private final int[] mSpaceIndices = new int[MAX_RESULTS];
private final int[] mOutputScores = new int[MAX_RESULTS];
private final int[] mOutputTypes = new int[MAX_RESULTS];
- private final int[] mOutputAutoCommitFirstWordConfidence = new int[MAX_RESULTS];
+ // Only one result is ever used
+ private final int[] mOutputAutoCommitFirstWordConfidence = new int[1];
private final NativeSuggestOptions mNativeSuggestOptions = new NativeSuggestOptions();
@@ -269,18 +274,11 @@ public final class BinaryDictionary extends Dictionary {
return getBigramProbabilityNative(mNativeDict, codePoints0, codePoints1);
}
- private void runGCIfRequired() {
- if (needsToRunGC(true /* mindsBlockByGC */)) {
- flushWithGC();
- }
- }
-
// Add a unigram entry to binary dictionary in native code.
public void addUnigramWord(final String word, final int probability) {
if (TextUtils.isEmpty(word)) {
return;
}
- runGCIfRequired();
final int[] codePoints = StringUtils.toCodePointArray(word);
addUnigramWordNative(mNativeDict, codePoints, probability);
}
@@ -290,7 +288,6 @@ public final class BinaryDictionary extends Dictionary {
if (TextUtils.isEmpty(word0) || TextUtils.isEmpty(word1)) {
return;
}
- runGCIfRequired();
final int[] codePoints0 = StringUtils.toCodePointArray(word0);
final int[] codePoints1 = StringUtils.toCodePointArray(word1);
addBigramWordsNative(mNativeDict, codePoints0, codePoints1, probability);
@@ -301,7 +298,6 @@ public final class BinaryDictionary extends Dictionary {
if (TextUtils.isEmpty(word0) || TextUtils.isEmpty(word1)) {
return;
}
- runGCIfRequired();
final int[] codePoints0 = StringUtils.toCodePointArray(word0);
final int[] codePoints1 = StringUtils.toCodePointArray(word1);
removeBigramWordsNative(mNativeDict, codePoints0, codePoints1);
@@ -351,18 +347,7 @@ public final class BinaryDictionary extends Dictionary {
@Override
public boolean shouldAutoCommit(final SuggestedWordInfo candidate) {
- // TODO: actually use the confidence rather than use this completely broken heuristic
- final String word = candidate.mWord;
- final int length = word.length();
- int remainingSpaces = SPACE_COUNT_FOR_AUTO_COMMIT;
- for (int i = 0; i < length; ++i) {
- // This is okay because no low-surrogate and no high-surrogate can ever match the
- // space character, so we don't need to take care of iterating on code points.
- if (Constants.CODE_SPACE == word.charAt(i)) {
- if (0 >= --remainingSpaces) return true;
- }
- }
- return false;
+ return candidate.mAutoCommitFirstWordConfidence > CONFIDENCE_TO_AUTO_COMMIT;
}
@Override
diff --git a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
index ffeb92784..47891c6b7 100644
--- a/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ContactsBinaryDictionary.java
@@ -127,7 +127,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
if (DEBUG) {
Log.d(TAG, "loadAccountVocabulary: " + word);
}
- super.addWord(word, null /* shortcut */, FREQUENCY_FOR_CONTACTS,
+ super.addWord(word, null /* shortcut */, FREQUENCY_FOR_CONTACTS, 0 /* shortcutFreq */,
false /* isNotAWord */);
}
}
@@ -213,7 +213,7 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
Log.d(TAG, "addName " + name + ", " + word + ", " + prevWord);
}
super.addWord(word, null /* shortcut */, FREQUENCY_FOR_CONTACTS,
- false /* isNotAWord */);
+ 0 /* shortcutFreq */, false /* isNotAWord */);
if (!TextUtils.isEmpty(prevWord)) {
if (mUseFirstLastBigrams) {
super.addBigram(prevWord, word, FREQUENCY_FOR_CONTACTS_BIGRAM,
diff --git a/java/src/com/android/inputmethod/latin/DictionaryWriter.java b/java/src/com/android/inputmethod/latin/DictionaryWriter.java
index 84abfa66d..3df2a2b63 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryWriter.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryWriter.java
@@ -62,13 +62,13 @@ public class DictionaryWriter extends AbstractDictionaryWriter {
// considering performance regression.
@Override
public void addUnigramWord(final String word, final String shortcutTarget, final int frequency,
- final boolean isNotAWord) {
+ final int shortcutFreq, final boolean isNotAWord) {
if (shortcutTarget == null) {
mFusionDictionary.add(word, frequency, null, isNotAWord);
} else {
// TODO: Do this in the subclass, with this class taking an arraylist.
final ArrayList<WeightedString> shortcutTargets = CollectionUtils.newArrayList();
- shortcutTargets.add(new WeightedString(shortcutTarget, frequency));
+ shortcutTargets.add(new WeightedString(shortcutTarget, shortcutFreq));
mFusionDictionary.add(word, frequency, shortcutTargets, isNotAWord);
}
}
diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
index 2d1ca51e2..eb8650e6f 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();
}
@@ -258,10 +261,16 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
/**
* Adds a word unigram to the dictionary. Used for loading a dictionary.
+ * @param word The word to add.
+ * @param shortcutTarget A shortcut target for this word, or null if none.
+ * @param frequency The frequency for this unigram.
+ * @param shortcutFreq The frequency of the shortcut (0~15, with 15 = whitelist). Ignored
+ * if shortcutTarget is null.
+ * @param isNotAWord true if this is not a word, i.e. shortcut only.
*/
protected void addWord(final String word, final String shortcutTarget,
- final int frequency, final boolean isNotAWord) {
- mDictionaryWriter.addUnigramWord(word, shortcutTarget, frequency, isNotAWord);
+ final int frequency, final int shortcutFreq, final boolean isNotAWord) {
+ mDictionaryWriter.addUnigramWord(word, shortcutTarget, frequency, shortcutFreq, isNotAWord);
}
/**
@@ -274,23 +283,57 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
}
/**
+ * 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;
+ // Calls to needsToRunGC() need to be serialized.
+ if (mBinaryDictionary.needsToRunGC(mindsBlockByGC)) {
+ if (setIsRegeneratingIfNotRegenerating()) {
+ // Run GC after currently existing time sensitive operations.
+ getExecutor(mFilename).executePrioritized(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ mBinaryDictionary.flushWithGC();
+ } finally {
+ mFilenameDictionaryUpdateController.mIsRegenerating.set(false);
+ }
+ }
+ });
+ }
+ }
+ }
+
+ /**
* Dynamically adds a word unigram to the dictionary. May overwrite an existing entry.
*/
protected void addWordDynamically(final String word, final String shortcutTarget,
- final int frequency, final boolean isNotAWord) {
+ final int frequency, final int shortcutFreq, final boolean isNotAWord) {
if (!mIsUpdatable) {
Log.w(TAG, "addWordDynamically is called for non-updatable dictionary: " + mFilename);
return;
}
-
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.
- mDictionaryWriter.addUnigramWord(word, shortcutTarget, frequency, isNotAWord);
+ mDictionaryWriter.addUnigramWord(word, shortcutTarget, frequency, shortcutFreq,
+ isNotAWord);
}
}
});
@@ -306,11 +349,11 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
+ mFilename);
return;
}
-
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.
@@ -330,11 +373,11 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
+ mFilename);
return;
}
-
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.
@@ -461,8 +504,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/ExpandableDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
index d491f988a..95c9bcab9 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java
@@ -156,15 +156,36 @@ public class ExpandableDictionary extends Dictionary {
return Constants.DICTIONARY_MAX_WORD_LENGTH;
}
- public void addWord(final String word, final String shortcutTarget, final int frequency) {
+ /**
+ * Add a word with an optional shortcut to the dictionary.
+ * @param word The word to add.
+ * @param shortcutTarget A shortcut target for this word, or null if none.
+ * @param frequency The frequency for this unigram.
+ * @param shortcutFreq The frequency of the shortcut (0~15, with 15 = whitelist). Ignored
+ * if shortcutTarget is null.
+ */
+ public void addWord(final String word, final String shortcutTarget, final int frequency,
+ final int shortcutFreq) {
if (word.length() >= Constants.DICTIONARY_MAX_WORD_LENGTH) {
return;
}
- addWordRec(mRoots, word, 0, shortcutTarget, frequency, null);
+ addWordRec(mRoots, word, 0, shortcutTarget, frequency, shortcutFreq, null);
}
+ /**
+ * Add a word, recursively searching for its correct place in the trie tree.
+ * @param children The node to recursively search for addition. Initially, the root of the tree.
+ * @param word The word to add.
+ * @param depth The current depth in the tree.
+ * @param shortcutTarget A shortcut target for this word, or null if none.
+ * @param frequency The frequency for this unigram.
+ * @param shortcutFreq The frequency of the shortcut (0~15, with 15 = whitelist). Ignored
+ * if shortcutTarget is null.
+ * @param parentNode The parent node, for up linking. Initially null, as the root has no parent.
+ */
private void addWordRec(final NodeArray children, final String word, final int depth,
- final String shortcutTarget, final int frequency, final Node parentNode) {
+ final String shortcutTarget, final int frequency, final int shortcutFreq,
+ final Node parentNode) {
final int wordLength = word.length();
if (wordLength <= depth) return;
final char c = word.charAt(depth);
@@ -204,7 +225,8 @@ public class ExpandableDictionary extends Dictionary {
if (childNode.mChildren == null) {
childNode.mChildren = new NodeArray();
}
- addWordRec(childNode.mChildren, word, depth + 1, shortcutTarget, frequency, childNode);
+ addWordRec(childNode.mChildren, word, depth + 1, shortcutTarget, frequency, shortcutFreq,
+ childNode);
}
@Override
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 96e16de0d..ccdbd0d4d 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);
}
@@ -602,8 +605,24 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
private void initSuggest() {
- final Locale subtypeLocale = mSubtypeSwitcher.getCurrentSubtypeLocale();
- final String localeStr = subtypeLocale.toString();
+ final Locale switcherSubtypeLocale = mSubtypeSwitcher.getCurrentSubtypeLocale();
+ final String switcherLocaleStr = switcherSubtypeLocale.toString();
+ final Locale subtypeLocale;
+ final String localeStr;
+ if (TextUtils.isEmpty(switcherLocaleStr)) {
+ // This happens in very rare corner cases - for example, immediately after a switch
+ // to LatinIME has been requested, about a frame later another switch happens. In this
+ // case, we are about to go down but we still don't know it, however the system tells
+ // us there is no current subtype so the locale is the empty string. Take the best
+ // possible guess instead -- it's bound to have no consequences, and we have no way
+ // of knowing anyway.
+ Log.e(TAG, "System is reporting no current subtype.");
+ subtypeLocale = getResources().getConfiguration().locale;
+ localeStr = subtypeLocale.toString();
+ } else {
+ subtypeLocale = switcherSubtypeLocale;
+ localeStr = switcherLocaleStr;
+ }
final Suggest newSuggest = new Suggest(this /* Context */, subtypeLocale,
this /* SuggestInitializationListener */);
@@ -789,6 +808,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
@SuppressWarnings("deprecation")
private void onStartInputViewInternal(final EditorInfo editorInfo, final boolean restarting) {
super.onStartInputView(editorInfo, restarting);
+ mRichImm.clearSubtypeCaches();
final KeyboardSwitcher switcher = mKeyboardSwitcher;
final MainKeyboardView mainKeyboardView = switcher.getMainKeyboardView();
// If we are starting input in a different text field from before, we'll have to reload
@@ -884,12 +904,17 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// Sometimes, while rotating, for some reason the framework tells the app we are not
// connected to it and that means we can't refresh the cache. In this case, schedule a
// refresh later.
+ final boolean canReachInputConnection;
if (!mConnection.resetCachesUponCursorMoveAndReturnSuccess(editorInfo.initialSelStart,
false /* shouldFinishComposition */)) {
// We try resetting the caches up to 5 times before giving up.
mHandler.postResetCaches(isDifferentTextField, 5 /* remainingTries */);
+ canReachInputConnection = false;
} else {
- if (isDifferentTextField) mHandler.postResumeSuggestions();
+ if (isDifferentTextField) {
+ mHandler.postResumeSuggestions();
+ }
+ canReachInputConnection = true;
}
if (isDifferentTextField) {
@@ -902,6 +927,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
switcher.loadKeyboard(editorInfo, currentSettingsValues);
+ if (!canReachInputConnection) {
+ // If we can't reach the input connection, we will call loadKeyboard again later,
+ // so we need to save its state now. The call will be done in #retryResetCaches.
+ switcher.saveKeyboardState();
+ }
} else if (restarting) {
// TODO: Come up with a more comprehensive way to reset the keyboard layout when
// a keyboard layout set doesn't get reloaded in this method.
@@ -1020,17 +1050,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private void onFinishInputViewInternal(final boolean finishingInput) {
super.onFinishInputView(finishingInput);
mKeyboardSwitcher.onFinishInputView();
- final MainKeyboardView mainKeyboardView = mKeyboardSwitcher.getMainKeyboardView();
- if (mainKeyboardView != null) {
- mainKeyboardView.cancelAllOngoingEvents();
- mainKeyboardView.deallocateMemory();
- }
+ mKeyboardSwitcher.deallocateMemory();
// Remove pending messages related to update suggestions
mHandler.cancelUpdateSuggestionStrip();
// Should do the following in onFinishInputInternal but until JB MR2 it's not called :(
if (mWordComposer.isComposingWord()) mConnection.finishComposingText();
resetComposingState(true /* alsoResetLastComposedWord */);
- mRichImm.clearSubtypeCaches();
// Notify ResearchLogger
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
ResearchLogger.latinIME_onFinishInputViewInternal(finishingInput, mLastSelectionStart,
@@ -1383,14 +1408,15 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// Called from the KeyboardSwitcher which needs to know auto caps state to display
// the right layout.
public int getCurrentAutoCapsState() {
- if (!mSettings.getCurrent().mAutoCap) return Constants.TextUtils.CAP_MODE_OFF;
+ final SettingsValues currentSettingsValues = mSettings.getCurrent();
+ if (!currentSettingsValues.mAutoCap) return Constants.TextUtils.CAP_MODE_OFF;
final EditorInfo ei = getCurrentInputEditorInfo();
if (ei == null) return Constants.TextUtils.CAP_MODE_OFF;
final int inputType = ei.inputType;
// Warning: this depends on mSpaceState, which may not be the most current value. If
// mSpaceState gets updated later, whoever called this may need to be told about it.
- return mConnection.getCursorCapsMode(inputType, mSubtypeSwitcher.getCurrentSubtypeLocale(),
+ return mConnection.getCursorCapsMode(inputType, currentSettingsValues,
SPACE_STATE_PHANTOM == mSpaceState);
}
@@ -1431,18 +1457,30 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
private boolean maybeDoubleSpacePeriod() {
- final SettingsValues settingsValues = mSettings.getCurrent();
- if (!settingsValues.mCorrectionEnabled) return false;
- if (!settingsValues.mUseDoubleSpacePeriod) return false;
+ final SettingsValues currentSettingsValues = mSettings.getCurrent();
+ if (!currentSettingsValues.mCorrectionEnabled) return false;
+ if (!currentSettingsValues.mUseDoubleSpacePeriod) return false;
if (!mHandler.isAcceptingDoubleSpacePeriod()) return false;
- final CharSequence lastThree = mConnection.getTextBeforeCursor(3, 0);
- if (lastThree != null && lastThree.length() == 3
- && canBeFollowedByDoubleSpacePeriod(lastThree.charAt(0))
- && lastThree.charAt(1) == Constants.CODE_SPACE
- && lastThree.charAt(2) == Constants.CODE_SPACE) {
+ // We only do this when we see two spaces and an accepted code point before the cursor.
+ // The code point may be a surrogate pair but the two spaces may not, so we need 4 chars.
+ final CharSequence lastThree = mConnection.getTextBeforeCursor(4, 0);
+ if (null == lastThree) return false;
+ final int length = lastThree.length();
+ if (length < 3) return false;
+ if (lastThree.charAt(length - 1) != Constants.CODE_SPACE) return false;
+ if (lastThree.charAt(length - 2) != Constants.CODE_SPACE) return false;
+ // We know there are spaces in pos -1 and -2, and we have at least three chars.
+ // If we have only three chars, isSurrogatePairs can't return true as charAt(1) is a space,
+ // so this is fine.
+ final int firstCodePoint =
+ Character.isSurrogatePair(lastThree.charAt(0), lastThree.charAt(1)) ?
+ Character.codePointAt(lastThree, 0) : lastThree.charAt(length - 3);
+ if (canBeFollowedByDoubleSpacePeriod(firstCodePoint)) {
mHandler.cancelDoubleSpacePeriodTimer();
mConnection.deleteSurroundingText(2, 0);
- final String textToInsert = ". ";
+ final String textToInsert = new String(
+ new int[] { currentSettingsValues.mSentenceSeparator, Constants.CODE_SPACE },
+ 0, 2);
mConnection.commitText(textToInsert, 1);
if (ProductionFlag.USES_DEVELOPMENT_ONLY_DIAGNOSTICS) {
ResearchLogger.latinIME_maybeDoubleSpacePeriod(textToInsert,
@@ -1464,7 +1502,8 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
|| codePoint == Constants.CODE_CLOSING_SQUARE_BRACKET
|| codePoint == Constants.CODE_CLOSING_CURLY_BRACKET
|| codePoint == Constants.CODE_CLOSING_ANGLE_BRACKET
- || codePoint == Constants.CODE_PLUS;
+ || codePoint == Constants.CODE_PLUS
+ || Character.getType(codePoint) == Character.OTHER_SYMBOL;
}
// Callback for the {@link SuggestionStripView}, to call when the "add to dictionary" hint is
@@ -2925,10 +2964,13 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (!mConnection.resetCachesUponCursorMoveAndReturnSuccess(mLastSelectionStart, false)) {
if (0 < remainingTries) {
mHandler.postResetCaches(tryResumeSuggestions, remainingTries - 1);
+ return;
}
- return;
+ // If remainingTries is 0, we should stop waiting for new tries, but it's still
+ // better to load the keyboard (less things will be broken).
}
tryFixLyingCursorPosition();
+ mKeyboardSwitcher.loadKeyboard(getCurrentInputEditorInfo(), mSettings.getCurrent());
if (tryResumeSuggestions) mHandler.postResumeSuggestions();
}
diff --git a/java/src/com/android/inputmethod/latin/RichInputConnection.java b/java/src/com/android/inputmethod/latin/RichInputConnection.java
index 8580a6e54..e43cab5ca 100644
--- a/java/src/com/android/inputmethod/latin/RichInputConnection.java
+++ b/java/src/com/android/inputmethod/latin/RichInputConnection.java
@@ -245,11 +245,11 @@ public final class RichInputConnection {
* American English, it's just the most common set of rules for English).
*
* @param inputType a mask of the caps modes to test for.
- * @param locale what language should be considered.
+ * @param settingsValues the values of the settings to use for locale and separators.
* @param hasSpaceBefore if we should consider there should be a space after the string.
* @return the caps modes that should be on as a set of bits
*/
- public int getCursorCapsMode(final int inputType, final Locale locale,
+ public int getCursorCapsMode(final int inputType, final SettingsValues settingsValues,
final boolean hasSpaceBefore) {
mIC = mParent.getCurrentInputConnection();
if (null == mIC) return Constants.TextUtils.CAP_MODE_OFF;
@@ -277,8 +277,8 @@ public final class RichInputConnection {
}
// This never calls InputConnection#getCapsMode - in fact, it's a static method that
// never blocks or initiates IPC.
- return CapsModeUtils.getCapsMode(mCommittedTextBeforeComposingText, inputType, locale,
- hasSpaceBefore);
+ return CapsModeUtils.getCapsMode(mCommittedTextBeforeComposingText, inputType,
+ settingsValues, hasSpaceBefore);
}
public int getCodePointBeforeCursor() {
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index 9fd1f53a2..c270d47d0 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -286,14 +286,16 @@ public final class Suggest {
// the word *would* have been auto-corrected.
if (!isCorrectionEnabled || !allowsToBeAutoCorrected || !wordComposer.isComposingWord()
|| suggestionsSet.isEmpty() || wordComposer.hasDigits()
- || wordComposer.isMostlyCaps() || wordComposer.isResumed()
- || !hasMainDictionary()) {
+ || wordComposer.isMostlyCaps() || wordComposer.isResumed() || !hasMainDictionary()
+ || SuggestedWordInfo.KIND_SHORTCUT == suggestionsSet.first().mKind) {
// If we don't have a main dictionary, we never want to auto-correct. The reason for
// this is, the user may have a contact whose name happens to match a valid word in
// their language, and it will unexpectedly auto-correct. For example, if the user
// types in English with no dictionary and has a "Will" in their contact list, "will"
// would always auto-correct to "Will" which is unwanted. Hence, no main dict => no
// auto-correct.
+ // Also, shortcuts should never auto-correct unless they are whitelist entries.
+ // TODO: we may want to have shortcut-only entries auto-correct in the future.
hasAutoCorrection = false;
} else {
hasAutoCorrection = AutoCorrectionUtils.suggestionExceedsAutoCorrectionThreshold(
diff --git a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java
index 864a17375..15b3d8d02 100644
--- a/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/UserBinaryDictionary.java
@@ -47,6 +47,9 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary {
private static final String USER_DICTIONARY_ALL_LANGUAGES = "";
private static final int HISTORICAL_DEFAULT_USER_DICTIONARY_FREQUENCY = 250;
private static final int LATINIME_DEFAULT_USER_DICTIONARY_FREQUENCY = 160;
+ // Shortcut frequency is 0~15, with 15 = whitelist. We don't want user dictionary entries
+ // to auto-correct, so we set this to the highest frequency that won't, i.e. 14.
+ private static final int USER_DICT_SHORTCUT_FREQUENCY = 14;
// TODO: use Words.SHORTCUT when we target JellyBean or above
final static String SHORTCUT = "shortcut";
@@ -243,10 +246,12 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary {
final int adjustedFrequency = scaleFrequencyFromDefaultToLatinIme(frequency);
// Safeguard against adding really long words.
if (word.length() < MAX_WORD_LENGTH) {
- super.addWord(word, null, adjustedFrequency, false /* isNotAWord */);
+ super.addWord(word, null, adjustedFrequency, 0 /* shortcutFreq */,
+ false /* isNotAWord */);
}
if (null != shortcut && shortcut.length() < MAX_WORD_LENGTH) {
- super.addWord(shortcut, word, adjustedFrequency, true /* isNotAWord */);
+ super.addWord(shortcut, word, adjustedFrequency, USER_DICT_SHORTCUT_FREQUENCY,
+ true /* isNotAWord */);
}
cursor.moveToNext();
}
diff --git a/java/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java
new file mode 100644
index 000000000..9f7f502ea
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java
@@ -0,0 +1,206 @@
+/*
+ * 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.BinaryDictDecoderUtils.CharEncoding;
+import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer;
+import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
+import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
+import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.TreeMap;
+
+/**
+ * A base class of the binary dictionary decoder.
+ */
+public abstract class AbstractDictDecoder implements DictDecoder {
+ protected FileHeader readHeader(final DictBuffer dictBuffer)
+ throws IOException, UnsupportedFormatException {
+ if (dictBuffer == null) {
+ openDictBuffer();
+ }
+
+ final int version = HeaderReader.readVersion(dictBuffer);
+ if (version < FormatSpec.MINIMUM_SUPPORTED_VERSION
+ || version > FormatSpec.MAXIMUM_SUPPORTED_VERSION) {
+ throw new UnsupportedFormatException("Unsupported version : " + version);
+ }
+ // TODO: Remove this field.
+ final int optionsFlags = HeaderReader.readOptionFlags(dictBuffer);
+
+ final int headerSize = HeaderReader.readHeaderSize(dictBuffer);
+
+ if (headerSize < 0) {
+ throw new UnsupportedFormatException("header size can't be negative.");
+ }
+
+ final HashMap<String, String> attributes = HeaderReader.readAttributes(dictBuffer,
+ headerSize);
+
+ final FileHeader header = new FileHeader(headerSize,
+ new FusionDictionary.DictionaryOptions(attributes,
+ 0 != (optionsFlags & FormatSpec.GERMAN_UMLAUT_PROCESSING_FLAG),
+ 0 != (optionsFlags & FormatSpec.FRENCH_LIGATURE_PROCESSING_FLAG)),
+ new FormatOptions(version,
+ 0 != (optionsFlags & FormatSpec.SUPPORTS_DYNAMIC_UPDATE)));
+ return header;
+ }
+
+ @Override @UsedForTesting
+ public int getTerminalPosition(final String word)
+ throws IOException, UnsupportedFormatException {
+ if (!isDictBufferOpen()) {
+ openDictBuffer();
+ }
+ return BinaryDictIOUtils.getTerminalPosition(this, word);
+ }
+
+ @Override @UsedForTesting
+ public void readUnigramsAndBigramsBinary(final TreeMap<Integer, String> words,
+ final TreeMap<Integer, Integer> frequencies,
+ final TreeMap<Integer, ArrayList<PendingAttribute>> bigrams)
+ throws IOException, UnsupportedFormatException {
+ if (!isDictBufferOpen()) {
+ openDictBuffer();
+ }
+ BinaryDictIOUtils.readUnigramsAndBigramsBinary(this, words, frequencies, bigrams);
+ }
+
+ /**
+ * A utility class for reading a file header.
+ */
+ protected static class HeaderReader {
+ protected static int readVersion(final DictBuffer dictBuffer)
+ throws IOException, UnsupportedFormatException {
+ return BinaryDictDecoderUtils.checkFormatVersion(dictBuffer);
+ }
+
+ protected static int readOptionFlags(final DictBuffer dictBuffer) {
+ return dictBuffer.readUnsignedShort();
+ }
+
+ protected static int readHeaderSize(final DictBuffer dictBuffer) {
+ return dictBuffer.readInt();
+ }
+
+ protected static HashMap<String, String> readAttributes(final DictBuffer dictBuffer,
+ final int headerSize) {
+ final HashMap<String, String> attributes = new HashMap<String, String>();
+ while (dictBuffer.position() < headerSize) {
+ // We can avoid an infinite loop here since dictBuffer.position() is always
+ // increased by calling CharEncoding.readString.
+ final String key = CharEncoding.readString(dictBuffer);
+ final String value = CharEncoding.readString(dictBuffer);
+ attributes.put(key, value);
+ }
+ dictBuffer.position(headerSize);
+ return attributes;
+ }
+ }
+
+ /**
+ * A utility class for reading a PtNode.
+ */
+ protected static class PtNodeReader {
+ protected static int readPtNodeOptionFlags(final DictBuffer dictBuffer) {
+ return dictBuffer.readUnsignedByte();
+ }
+
+ protected static int readParentAddress(final DictBuffer dictBuffer,
+ final FormatOptions formatOptions) {
+ if (BinaryDictIOUtils.supportsDynamicUpdate(formatOptions)) {
+ return BinaryDictDecoderUtils.readSInt24(dictBuffer);
+ } else {
+ return FormatSpec.NO_PARENT_ADDRESS;
+ }
+ }
+
+ protected static int readChildrenAddress(final DictBuffer dictBuffer, final int optionFlags,
+ final FormatOptions formatOptions) {
+ if (BinaryDictIOUtils.supportsDynamicUpdate(formatOptions)) {
+ final int address = BinaryDictDecoderUtils.readSInt24(dictBuffer);
+ if (address == 0) return FormatSpec.NO_CHILDREN_ADDRESS;
+ return address;
+ } else {
+ switch (optionFlags & FormatSpec.MASK_CHILDREN_ADDRESS_TYPE) {
+ case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_ONEBYTE:
+ return dictBuffer.readUnsignedByte();
+ case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_TWOBYTES:
+ return dictBuffer.readUnsignedShort();
+ case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_THREEBYTES:
+ return dictBuffer.readUnsignedInt24();
+ case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_NOADDRESS:
+ default:
+ return FormatSpec.NO_CHILDREN_ADDRESS;
+ }
+ }
+ }
+
+ // Reads shortcuts and returns the read length.
+ protected static int readShortcut(final DictBuffer dictBuffer,
+ final ArrayList<WeightedString> shortcutTargets) {
+ final int pointerBefore = dictBuffer.position();
+ dictBuffer.readUnsignedShort(); // skip the size
+ while (true) {
+ final int targetFlags = dictBuffer.readUnsignedByte();
+ final String word = CharEncoding.readString(dictBuffer);
+ shortcutTargets.add(new WeightedString(word,
+ targetFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY));
+ if (0 == (targetFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT)) break;
+ }
+ return dictBuffer.position() - pointerBefore;
+ }
+
+ protected static int readBigramAddresses(final DictBuffer dictBuffer,
+ final ArrayList<PendingAttribute> bigrams, final int baseAddress) {
+ int readLength = 0;
+ int bigramCount = 0;
+ while (bigramCount++ < FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) {
+ final int bigramFlags = dictBuffer.readUnsignedByte();
+ ++readLength;
+ final int sign = 0 == (bigramFlags & FormatSpec.FLAG_BIGRAM_ATTR_OFFSET_NEGATIVE)
+ ? 1 : -1;
+ int bigramAddress = baseAddress + readLength;
+ switch (bigramFlags & FormatSpec.MASK_BIGRAM_ATTR_ADDRESS_TYPE) {
+ case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_ONEBYTE:
+ bigramAddress += sign * dictBuffer.readUnsignedByte();
+ readLength += 1;
+ break;
+ case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_TWOBYTES:
+ bigramAddress += sign * dictBuffer.readUnsignedShort();
+ readLength += 2;
+ break;
+ case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_THREEBYTES:
+ bigramAddress += sign * dictBuffer.readUnsignedInt24();
+ readLength += 3;
+ break;
+ default:
+ throw new RuntimeException("Has bigrams with no address");
+ }
+ bigrams.add(new PendingAttribute(
+ bigramFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY,
+ bigramAddress));
+ if (0 == (bigramFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT)) break;
+ }
+ return readLength;
+ }
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java
index 665c7a27c..216492b4d 100644
--- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java
+++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java
@@ -23,11 +23,11 @@ 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.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
+import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
@@ -219,14 +219,14 @@ public final class BinaryDictDecoderUtils {
}
/**
- * Writes a string with our character format to a ByteArrayOutputStream.
+ * Writes a string with our character format to an OutputStream.
*
* This will also write the terminator byte.
*
- * @param buffer the ByteArrayOutputStream to write to.
+ * @param buffer the OutputStream to write to.
* @param word the string to write.
*/
- static void writeString(final ByteArrayOutputStream buffer, final String word) {
+ static void writeString(final OutputStream buffer, final String word) throws IOException {
final int length = word.length();
for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) {
final int codePoint = word.codePointAt(i);
@@ -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..f761829de 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);
}
@@ -384,8 +383,8 @@ public class BinaryDictEncoderUtils {
nodeSize += getByteSize(getOffsetToTargetNodeArrayDuringUpdate(ptNodeArray,
nodeSize + size, ptNode.mChildren));
}
- nodeSize += getShortcutListSize(ptNode.mShortcutTargets);
if (formatOptions.mVersion < FormatSpec.FIRST_VERSION_WITH_TERMINAL_ID) {
+ nodeSize += getShortcutListSize(ptNode.mShortcutTargets);
if (null != ptNode.mBigrams) {
for (WeightedString bigram : ptNode.mBigrams) {
final int offset = getOffsetToTargetPtNodeDuringUpdate(ptNodeArray,
@@ -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 a282f595c..0f7d2f6c9 100644
--- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
+++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java
@@ -288,40 +288,6 @@ public final class BinaryDictIOUtils {
return BinaryDictEncoderUtils.getByteSize(value);
}
- 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/DictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/DictDecoder.java
index 3796a466c..3dbeee099 100644
--- a/java/src/com/android/inputmethod/latin/makedict/DictDecoder.java
+++ b/java/src/com/android/inputmethod/latin/makedict/DictDecoder.java
@@ -17,11 +17,9 @@
package com.android.inputmethod.latin.makedict;
import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.CharEncoding;
import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer;
import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
-import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
import com.android.inputmethod.latin.utils.ByteArrayDictBuffer;
import java.io.File;
@@ -32,50 +30,17 @@ import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.TreeMap;
/**
- * The base class of binary dictionary decoders.
+ * An interface of binary dictionary decoders.
*/
-public abstract class DictDecoder {
-
- protected FileHeader readHeader(final DictBuffer dictBuffer)
- throws IOException, UnsupportedFormatException {
- if (dictBuffer == null) {
- openDictBuffer();
- }
-
- final int version = HeaderReader.readVersion(dictBuffer);
- if (version < FormatSpec.MINIMUM_SUPPORTED_VERSION
- || version > FormatSpec.MAXIMUM_SUPPORTED_VERSION) {
- throw new UnsupportedFormatException("Unsupported version : " + version);
- }
- // TODO: Remove this field.
- final int optionsFlags = HeaderReader.readOptionFlags(dictBuffer);
-
- final int headerSize = HeaderReader.readHeaderSize(dictBuffer);
-
- if (headerSize < 0) {
- throw new UnsupportedFormatException("header size can't be negative.");
- }
-
- final HashMap<String, String> attributes = HeaderReader.readAttributes(dictBuffer,
- headerSize);
-
- final FileHeader header = new FileHeader(headerSize,
- new FusionDictionary.DictionaryOptions(attributes,
- 0 != (optionsFlags & FormatSpec.GERMAN_UMLAUT_PROCESSING_FLAG),
- 0 != (optionsFlags & FormatSpec.FRENCH_LIGATURE_PROCESSING_FLAG)),
- new FormatOptions(version,
- 0 != (optionsFlags & FormatSpec.SUPPORTS_DYNAMIC_UPDATE)));
- return header;
- }
+public interface DictDecoder {
/**
* Reads and returns the file header.
*/
- public abstract FileHeader readHeader() throws IOException, UnsupportedFormatException;
+ public FileHeader readHeader() throws IOException, UnsupportedFormatException;
/**
* Reads PtNode from nodeAddress.
@@ -83,7 +48,7 @@ public abstract class DictDecoder {
* @param formatOptions the format options.
* @return PtNodeInfo.
*/
- public abstract PtNodeInfo readPtNode(final int ptNodePos, final FormatOptions formatOptions);
+ public PtNodeInfo readPtNode(final int ptNodePos, final FormatOptions formatOptions);
/**
* Reads a buffer and returns the memory representation of the dictionary.
@@ -98,7 +63,7 @@ public abstract class DictDecoder {
* @return the created (or merged) dictionary.
*/
@UsedForTesting
- public abstract FusionDictionary readDictionaryBinary(final FusionDictionary dict,
+ public FusionDictionary readDictionaryBinary(final FusionDictionary dict,
final boolean deleteDictIfBroken)
throws FileNotFoundException, IOException, UnsupportedFormatException;
@@ -113,12 +78,7 @@ public abstract class DictDecoder {
*/
@UsedForTesting
public int getTerminalPosition(final String word)
- throws IOException, UnsupportedFormatException {
- if (!isDictBufferOpen()) {
- openDictBuffer();
- }
- return BinaryDictIOUtils.getTerminalPosition(this, word);
- }
+ throws IOException, UnsupportedFormatException;
/**
* Reads unigrams and bigrams from the binary file.
@@ -134,47 +94,42 @@ public abstract class DictDecoder {
public void readUnigramsAndBigramsBinary(final TreeMap<Integer, String> words,
final TreeMap<Integer, Integer> frequencies,
final TreeMap<Integer, ArrayList<PendingAttribute>> bigrams)
- throws IOException, UnsupportedFormatException {
- if (!isDictBufferOpen()) {
- openDictBuffer();
- }
- BinaryDictIOUtils.readUnigramsAndBigramsBinary(this, words, frequencies, bigrams);
- }
+ throws IOException, UnsupportedFormatException;
/**
* Sets the position of the buffer to the given value.
*
* @param newPos the new position
*/
- public abstract void setPosition(final int newPos);
+ public void setPosition(final int newPos);
/**
* Gets the position of the buffer.
*
* @return the position
*/
- public abstract int getPosition();
+ public int getPosition();
/**
* Reads and returns the PtNode count out of a buffer and forwards the pointer.
*/
- public abstract int readPtNodeCount();
+ public int readPtNodeCount();
/**
* Reads the forward link and advances the position.
*
* @return true if this method moves the file pointer, false otherwise.
*/
- public abstract boolean readAndFollowForwardLink();
- public abstract boolean hasNextPtNodeArray();
+ public boolean readAndFollowForwardLink();
+ public boolean hasNextPtNodeArray();
/**
* Opens the dictionary file and makes DictBuffer.
*/
@UsedForTesting
- public abstract void openDictBuffer() throws FileNotFoundException, IOException;
+ public void openDictBuffer() throws FileNotFoundException, IOException;
@UsedForTesting
- public abstract boolean isDictBufferOpen();
+ public boolean isDictBufferOpen();
// Constants for DictionaryBufferFactory.
public static final int USE_READONLY_BYTEBUFFER = 0x01000000;
@@ -272,123 +227,5 @@ public abstract class DictDecoder {
}
}
- /**
- * A utility class for reading a file header.
- */
- protected static class HeaderReader {
- protected static int readVersion(final DictBuffer dictBuffer)
- throws IOException, UnsupportedFormatException {
- return BinaryDictDecoderUtils.checkFormatVersion(dictBuffer);
- }
-
- protected static int readOptionFlags(final DictBuffer dictBuffer) {
- return dictBuffer.readUnsignedShort();
- }
-
- protected static int readHeaderSize(final DictBuffer dictBuffer) {
- return dictBuffer.readInt();
- }
-
- protected static HashMap<String, String> readAttributes(final DictBuffer dictBuffer,
- final int headerSize) {
- final HashMap<String, String> attributes = new HashMap<String, String>();
- while (dictBuffer.position() < headerSize) {
- // We can avoid an infinite loop here since dictBuffer.position() is always
- // increased by calling CharEncoding.readString.
- final String key = CharEncoding.readString(dictBuffer);
- final String value = CharEncoding.readString(dictBuffer);
- attributes.put(key, value);
- }
- dictBuffer.position(headerSize);
- return attributes;
- }
- }
-
- /**
- * A utility class for reading a PtNode.
- */
- protected static class PtNodeReader {
- protected static int readPtNodeOptionFlags(final DictBuffer dictBuffer) {
- return dictBuffer.readUnsignedByte();
- }
-
- protected static int readParentAddress(final DictBuffer dictBuffer,
- final FormatOptions formatOptions) {
- if (BinaryDictIOUtils.supportsDynamicUpdate(formatOptions)) {
- return BinaryDictDecoderUtils.readSInt24(dictBuffer);
- } else {
- return FormatSpec.NO_PARENT_ADDRESS;
- }
- }
-
- protected static int readChildrenAddress(final DictBuffer dictBuffer, final int optionFlags,
- final FormatOptions formatOptions) {
- if (BinaryDictIOUtils.supportsDynamicUpdate(formatOptions)) {
- final int address = BinaryDictDecoderUtils.readSInt24(dictBuffer);
- if (address == 0) return FormatSpec.NO_CHILDREN_ADDRESS;
- return address;
- } else {
- switch (optionFlags & FormatSpec.MASK_CHILDREN_ADDRESS_TYPE) {
- case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_ONEBYTE:
- return dictBuffer.readUnsignedByte();
- case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_TWOBYTES:
- return dictBuffer.readUnsignedShort();
- case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_THREEBYTES:
- return dictBuffer.readUnsignedInt24();
- case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_NOADDRESS:
- default:
- return FormatSpec.NO_CHILDREN_ADDRESS;
- }
- }
- }
-
- // Reads shortcuts and returns the read length.
- protected static int readShortcut(final DictBuffer dictBuffer,
- final ArrayList<WeightedString> shortcutTargets) {
- final int pointerBefore = dictBuffer.position();
- dictBuffer.readUnsignedShort(); // skip the size
- while (true) {
- final int targetFlags = dictBuffer.readUnsignedByte();
- final String word = CharEncoding.readString(dictBuffer);
- shortcutTargets.add(new WeightedString(word,
- targetFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY));
- if (0 == (targetFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT)) break;
- }
- return dictBuffer.position() - pointerBefore;
- }
-
- protected static int readBigramAddresses(final DictBuffer dictBuffer,
- final ArrayList<PendingAttribute> bigrams, final int baseAddress) {
- int readLength = 0;
- int bigramCount = 0;
- while (bigramCount++ < FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) {
- final int bigramFlags = dictBuffer.readUnsignedByte();
- ++readLength;
- final int sign = 0 == (bigramFlags & FormatSpec.FLAG_BIGRAM_ATTR_OFFSET_NEGATIVE)
- ? 1 : -1;
- int bigramAddress = baseAddress + readLength;
- switch (bigramFlags & FormatSpec.MASK_BIGRAM_ATTR_ADDRESS_TYPE) {
- case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_ONEBYTE:
- bigramAddress += sign * dictBuffer.readUnsignedByte();
- readLength += 1;
- break;
- case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_TWOBYTES:
- bigramAddress += sign * dictBuffer.readUnsignedShort();
- readLength += 2;
- break;
- case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_THREEBYTES:
- bigramAddress += sign * dictBuffer.readUnsignedInt24();
- readLength += 3;
- break;
- default:
- throw new RuntimeException("Has bigrams with no address");
- }
- bigrams.add(new PendingAttribute(
- bigramFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY,
- bigramAddress));
- if (0 == (bigramFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT)) break;
- }
- return readLength;
- }
- }
+ public void skipPtNode(final FormatOptions formatOptions);
}
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..c4f7ec91f
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/makedict/DictUpdater.java
@@ -0,0 +1,54 @@
+/*
+ * 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.IOException;
+import java.util.ArrayList;
+
+/**
+ * An interface of a binary dictionary updater.
+ */
+@UsedForTesting
+public interface DictUpdater extends DictDecoder {
+
+ /**
+ * Deletes the word from the binary dictionary.
+ *
+ * @param word the word to be deleted.
+ */
+ @UsedForTesting
+ 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.
+ @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;
+}
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..5a5d7af6b 100644
--- a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
+++ b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
@@ -266,11 +266,27 @@ 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 SHORTCUT_FILE_EXTENSION = ".shortcut";
+ 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;
+
+ // With the English main dictionary as of October 2013, the size of bigram address table is
+ // is 584KB with the block size being 4.
+ // This is 91% of that of full address table.
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 SHORTCUT_CONTENT_COUNT = 1;
+ static final int SHORTCUT_CONTENT_INDEX = 0;
+ // With the English main dictionary as of October 2013, the size of shortcut address table is
+ // 29KB with the block size being 64.
+ // This is only 4.4% of that of full address table.
+ static final int SHORTCUT_ADDRESS_TABLE_BLOCK_SIZE = 64;
+ static final String SHORTCUT_CONTENT_ID = "_shortcut";
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/FusionDictionary.java b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java
index be653feec..3bb218bea 100644
--- a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java
+++ b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java
@@ -367,10 +367,11 @@ public final class FusionDictionary implements Iterable<Word> {
* Helper method to convert a String to an int array.
*/
static int[] getCodePoints(final String word) {
- // TODO: this is a copy-paste of the contents of StringUtils.toCodePointArray,
+ // TODO: this is a copy-paste of the old contents of StringUtils.toCodePointArray,
// which is not visible from the makedict package. Factor this code.
+ final int length = word.length();
+ if (length <= 0) return new int[] {};
final char[] characters = word.toCharArray();
- final int length = characters.length;
final int[] codePoints = new int[Character.codePointCount(characters, 0, length)];
int codePoint = Character.codePointAt(characters, 0);
int dsti = 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 848277cd4..acab4f8a5 100644
--- a/java/src/com/android/inputmethod/latin/makedict/Ver3DictDecoder.java
+++ b/java/src/com/android/inputmethod/latin/makedict/Ver3DictDecoder.java
@@ -37,7 +37,7 @@ import java.util.Arrays;
* An implementation of DictDecoder for version 3 binary dictionary.
*/
@UsedForTesting
-public class Ver3DictDecoder extends DictDecoder {
+public class Ver3DictDecoder extends AbstractDictDecoder {
private static final String TAG = Ver3DictDecoder.class.getSimpleName();
static {
@@ -47,15 +47,15 @@ public class Ver3DictDecoder extends DictDecoder {
// TODO: implement something sensical instead of just a phony method
private static native int doNothing();
- protected static class PtNodeReader extends DictDecoder.PtNodeReader {
+ protected static class PtNodeReader extends AbstractDictDecoder.PtNodeReader {
private static int readFrequency(final DictBuffer dictBuffer) {
return dictBuffer.readUnsignedByte();
}
}
- 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;
@@ -231,4 +232,40 @@ public class Ver3DictDecoder extends DictDecoder {
public boolean hasNextPtNodeArray() {
return mDictBuffer.position() != FormatSpec.NO_FORWARD_LINK_ADDRESS;
}
+
+ @Override
+ public void skipPtNode(final FormatOptions formatOptions) {
+ final int flags = PtNodeReader.readPtNodeOptionFlags(mDictBuffer);
+ PtNodeReader.readParentAddress(mDictBuffer, formatOptions);
+ BinaryDictIOUtils.skipString(mDictBuffer,
+ (flags & FormatSpec.FLAG_HAS_MULTIPLE_CHARS) != 0);
+ PtNodeReader.readChildrenAddress(mDictBuffer, flags, formatOptions);
+ if ((flags & FormatSpec.FLAG_IS_TERMINAL) != 0) PtNodeReader.readFrequency(mDictBuffer);
+ if ((flags & FormatSpec.FLAG_HAS_SHORTCUT_TARGETS) != 0) {
+ final int shortcutsSize = mDictBuffer.readUnsignedShort();
+ mDictBuffer.position(mDictBuffer.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 = mDictBuffer.readUnsignedByte();
+ switch (bigramFlags & FormatSpec.MASK_BIGRAM_ATTR_ADDRESS_TYPE) {
+ case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_ONEBYTE:
+ mDictBuffer.readUnsignedByte();
+ break;
+ case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_TWOBYTES:
+ mDictBuffer.readUnsignedShort();
+ break;
+ case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_THREEBYTES:
+ mDictBuffer.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.");
+ }
+ }
+ }
}
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..07adda625
--- /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);
+ 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 0aa431966..53729075f 100644
--- a/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java
+++ b/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java
@@ -23,6 +23,7 @@ import com.android.inputmethod.latin.makedict.FormatSpec.FileHeader;
import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString;
+import com.android.inputmethod.latin.utils.CollectionUtils;
import android.util.Log;
@@ -36,21 +37,24 @@ import java.util.Arrays;
* An implementation of binary dictionary decoder for version 4 binary dictionary.
*/
@UsedForTesting
-public class Ver4DictDecoder extends DictDecoder {
+public class Ver4DictDecoder extends AbstractDictDecoder {
private static final String TAG = Ver4DictDecoder.class.getSimpleName();
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 static final int FILETYPE_SHORTCUT = 5;
private final File mDictDirectory;
private final DictionaryBufferFactory mBufferFactory;
- private DictBuffer mDictBuffer;
+ protected DictBuffer mDictBuffer;
private DictBuffer mFrequencyBuffer;
private DictBuffer mTerminalAddressTableBuffer;
private DictBuffer mBigramBuffer;
+ private DictBuffer mShortcutBuffer;
private SparseTable mBigramAddressTable;
+ private SparseTable mShortcutAddressTable;
@UsedForTesting
/* package */ Ver4DictDecoder(final File dictDirectory, final int factoryFlag) {
@@ -85,9 +89,14 @@ 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 if (fileType == FILETYPE_SHORTCUT) {
+ return new File(mDictDirectory,
+ mDictDirectory.getName() + FormatSpec.SHORTCUT_FILE_EXTENSION
+ + FormatSpec.SHORTCUT_CONTENT_ID);
} else {
throw new RuntimeException("Unsupported kind of file : " + fileType);
}
@@ -95,13 +104,14 @@ 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();
+ mShortcutBuffer = mBufferFactory.getDictionaryBuffer(getFile(FILETYPE_SHORTCUT));
+ loadShortcutAddressSparseTable();
}
@Override
@@ -127,15 +137,27 @@ 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);
}
- protected static class PtNodeReader extends DictDecoder.PtNodeReader {
+ // TODO: Let's have something like SparseTableContentsReader in this class.
+ private void loadShortcutAddressSparseTable() throws IOException {
+ final File lookupIndexFile = new File(mDictDirectory, mDictDirectory.getName()
+ + FormatSpec.SHORTCUT_FILE_EXTENSION + FormatSpec.LOOKUP_TABLE_FILE_SUFFIX);
+ final File contentFile = new File(mDictDirectory, mDictDirectory.getName()
+ + FormatSpec.SHORTCUT_FILE_EXTENSION + FormatSpec.CONTENT_TABLE_FILE_SUFFIX
+ + FormatSpec.SHORTCUT_CONTENT_ID);
+ mShortcutAddressTable = SparseTable.readFromFiles(lookupIndexFile,
+ new File[] { contentFile }, FormatSpec.SHORTCUT_ADDRESS_TABLE_BLOCK_SIZE);
+ }
+
+ protected static class PtNodeReader extends AbstractDictDecoder.PtNodeReader {
protected static int readFrequency(final DictBuffer frequencyBuffer, final int terminalId) {
frequencyBuffer.position(terminalId * FormatSpec.FREQUENCY_AND_FLAGS_SIZE + 1);
return frequencyBuffer.readUnsignedByte();
@@ -146,6 +168,23 @@ public class Ver4DictDecoder extends DictDecoder {
}
}
+ private ArrayList<WeightedString> readShortcuts(final int terminalId) {
+ if (mShortcutAddressTable.get(0, terminalId) == SparseTable.NOT_EXIST) return null;
+
+ final ArrayList<WeightedString> ret = CollectionUtils.newArrayList();
+ final int posOfShortcuts = mShortcutAddressTable.get(FormatSpec.SHORTCUT_CONTENT_INDEX,
+ terminalId);
+ mShortcutBuffer.position(posOfShortcuts);
+ while (true) {
+ final int flags = mShortcutBuffer.readUnsignedByte();
+ final String word = CharEncoding.readString(mShortcutBuffer);
+ ret.add(new WeightedString(word,
+ flags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY));
+ if (0 == (flags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT)) break;
+ }
+ return ret;
+ }
+
// TODO: Make this buffer thread safe.
// TODO: Support words longer than FormatSpec.MAX_WORD_LENGTH.
private final int[] mCharacterBuffer = new int[FormatSpec.MAX_WORD_LENGTH];
@@ -196,19 +235,12 @@ public class Ver4DictDecoder extends DictDecoder {
childrenAddress += addressPointer;
}
addressPointer += BinaryDictIOUtils.getChildrenAddressSize(flags, options);
- final ArrayList<WeightedString> shortcutTargets;
- if (0 != (flags & FormatSpec.FLAG_HAS_SHORTCUT_TARGETS)) {
- // readShortcut will add shortcuts to shortcutTargets.
- shortcutTargets = new ArrayList<WeightedString>();
- addressPointer += PtNodeReader.readShortcut(mDictBuffer, shortcutTargets);
- } else {
- shortcutTargets = null;
- }
+ final ArrayList<WeightedString> shortcutTargets = readShortcuts(terminalId);
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 +256,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;
@@ -293,4 +326,14 @@ public class Ver4DictDecoder extends DictDecoder {
public boolean hasNextPtNodeArray() {
return mDictBuffer.position() != FormatSpec.NO_FORWARD_LINK_ADDRESS;
}
+
+ @Override
+ public void skipPtNode(final FormatOptions formatOptions) {
+ final int flags = PtNodeReader.readPtNodeOptionFlags(mDictBuffer);
+ PtNodeReader.readParentAddress(mDictBuffer, formatOptions);
+ BinaryDictIOUtils.skipString(mDictBuffer,
+ (flags & FormatSpec.FLAG_HAS_MULTIPLE_CHARS) != 0);
+ if ((flags & FormatSpec.FLAG_IS_TERMINAL) != 0) PtNodeReader.readTerminalId(mDictBuffer);
+ PtNodeReader.readChildrenAddress(mDictBuffer, flags, formatOptions);
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java
index 4c25faf88..f9dcacf77 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,149 @@ 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;
+ private ShortcutContentWriter mShortcutWriter;
@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 static class ShortcutContentWriter extends SparseTableContentWriter {
+ public ShortcutContentWriter(final String name, final int initialCapacity,
+ final File baseDir) {
+ super(name + FormatSpec.SHORTCUT_FILE_EXTENSION, FormatSpec.SHORTCUT_CONTENT_COUNT,
+ initialCapacity, FormatSpec.SHORTCUT_ADDRESS_TABLE_BLOCK_SIZE, baseDir,
+ new String[] { name + FormatSpec.SHORTCUT_FILE_EXTENSION },
+ new String[] { FormatSpec.SHORTCUT_CONTENT_ID });
+ }
+
+ public void writeShortcutForOneWord(final int terminalId,
+ final Iterator<WeightedString> shortcutIterator) throws IOException {
+ write(FormatSpec.SHORTCUT_CONTENT_INDEX, terminalId,
+ new SparseTableContentWriterInterface() {
+ @Override
+ public void write(final OutputStream outStream) throws IOException {
+ writeShortcutForOneWordInternal(outStream, shortcutIterator);
+ }
+ });
+ }
+
+ private void writeShortcutForOneWordInternal(final OutputStream outStream,
+ final Iterator<WeightedString> shortcutIterator) throws IOException {
+ while (shortcutIterator.hasNext()) {
+ final WeightedString target = shortcutIterator.next();
+ final int shortcutFlags = BinaryDictEncoderUtils.makeShortcutFlags(
+ shortcutIterator.hasNext(), target.mFrequency);
+ BinaryDictEncoderUtils.writeUIntToStream(outStream, shortcutFlags,
+ FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE);
+ CharEncoding.writeString(outStream, target.mWord);
+ }
+ }
+ }
+
private void openStreams(final FormatOptions formatOptions, final DictionaryOptions dictOptions)
throws FileNotFoundException, IOException {
final FileHeader header = new FileHeader(0, dictOptions, formatOptions);
@@ -66,8 +195,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 +205,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 +218,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 +257,10 @@ 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();
+ mShortcutWriter = new ShortcutContentWriter(mBaseFilename, terminalCount, mDictDir);
+ writeShortcuts(flatNodes);
final PtNodeArray lastNodeArray = flatNodes.get(flatNodes.size() - 1);
final int bufferSize = lastNodeArray.mCachedAddressAfterUpdate + lastNodeArray.mCachedSize;
@@ -181,12 +303,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);
}
@@ -222,65 +342,31 @@ public class Ver4DictEncoder implements DictEncoder {
}
}
- private void writeShortcuts(ArrayList<WeightedString> shortcuts) {
- if (null == shortcuts || shortcuts.isEmpty()) return;
-
- final int indexOfShortcutByteSize = mTriePos;
- mTriePos += FormatSpec.PTNODE_SHORTCUT_LIST_SIZE_SIZE;
- final Iterator<WeightedString> shortcutIterator = shortcuts.iterator();
- while (shortcutIterator.hasNext()) {
- final WeightedString target = shortcutIterator.next();
- final int shortcutFlags = BinaryDictEncoderUtils.makeShortcutFlags(
- shortcutIterator.hasNext(),
- target.mFrequency);
- mTrieBuf[mTriePos++] = (byte)shortcutFlags;
- final int shortcutShift = CharEncoding.writeString(mTrieBuf, mTriePos,
- target.mWord);
- mTriePos += shortcutShift;
- }
- final int shortcutByteSize = mTriePos - indexOfShortcutByteSize;
- if (shortcutByteSize > FormatSpec.MAX_SHORTCUT_LIST_SIZE_IN_A_PTNODE) {
- throw new RuntimeException("Shortcut list too large : " + shortcutByteSize);
- }
- BinaryDictEncoderUtils.writeUIntToBuffer(mTrieBuf, indexOfShortcutByteSize,
- shortcutByteSize, FormatSpec.PTNODE_SHORTCUT_LIST_SIZE_SIZE);
- }
-
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);
+ mBigramWriter.closeStreams();
}
- 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);
+ private void writeShortcuts(final ArrayList<PtNodeArray> flatNodes) throws IOException {
+ mShortcutWriter.openStreams();
+ for (final PtNodeArray nodeArray : flatNodes) {
+ for (final PtNode ptNode : nodeArray.mData) {
+ if (ptNode.mShortcutTargets != null && !ptNode.mShortcutTargets.isEmpty()) {
+ mShortcutWriter.writeShortcutForOneWord(ptNode.mTerminalId,
+ ptNode.mShortcutTargets.iterator());
+ }
+ }
+ }
+ mShortcutWriter.closeStreams();
}
@Override
@@ -292,14 +378,13 @@ 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()) {
writeTerminalId(ptNode.mTerminalId);
}
writeChildrenPosition(ptNode, formatOptions);
- writeShortcuts(ptNode.mShortcutTargets);
}
private void writeTerminalData(final ArrayList<PtNodeArray> flatNodes,
diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver4DictUpdater.java b/java/src/com/android/inputmethod/latin/makedict/Ver4DictUpdater.java
new file mode 100644
index 000000000..3d8f186ba
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/makedict/Ver4DictUpdater.java
@@ -0,0 +1,59 @@
+/*
+ * 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.IOException;
+import java.util.ArrayList;
+
+/**
+ * An implementation of DictUpdater for version 4 binary dictionary.
+ */
+@UsedForTesting
+public class Ver4DictUpdater extends Ver4DictDecoder implements DictUpdater {
+
+ @UsedForTesting
+ public Ver4DictUpdater(final File dictDirectory, final int factoryType) {
+ // DictUpdater must have an updatable DictBuffer.
+ super(dictDirectory, ((factoryType & MASK_DICTBUFFER) == USE_BYTEARRAY)
+ ? USE_BYTEARRAY : USE_WRITABLE_BYTEBUFFER);
+ }
+
+ @Override
+ public void deleteWord(final String word) throws IOException, UnsupportedFormatException {
+ if (mDictBuffer == null) openDictBuffer();
+ readHeader();
+ final int wordPos = getTerminalPosition(word);
+ if (wordPos != FormatSpec.NOT_VALID_WORD) {
+ mDictBuffer.position(wordPos);
+ final int flags = PtNodeReader.readPtNodeOptionFlags(mDictBuffer);
+ mDictBuffer.position(wordPos);
+ mDictBuffer.put((byte) DynamicBinaryDictIOUtils.markAsDeleted(flags));
+ }
+ }
+
+ @Override
+ 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 {
+ // TODO: Implement this method.
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java b/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java
index 7cf4f0c88..a1e36006b 100644
--- a/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java
+++ b/java/src/com/android/inputmethod/latin/personalization/DecayingExpandableBinaryDictionaryBase.java
@@ -138,7 +138,7 @@ public abstract class DecayingExpandableBinaryDictionaryBase extends ExpandableB
final int frequency = ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE ?
(isValid ? FREQUENCY_FOR_WORDS_IN_DICTS : FREQUENCY_FOR_WORDS_NOT_IN_DICTS) :
FREQUENCY_FOR_TYPED;
- addWordDynamically(word1, null /* the "shortcut" parameter is null */, frequency,
+ addWordDynamically(word1, null /* shortcutTarget */, frequency, 0 /* shortcutFreq */,
false /* isNotAWord */);
// Do not insert a word as a bigram of itself
if (word1.equals(word0)) {
@@ -171,11 +171,11 @@ public abstract class DecayingExpandableBinaryDictionaryBase extends ExpandableB
final OnAddWordListener listener = new OnAddWordListener() {
@Override
public void setUnigram(final String word, final String shortcutTarget,
- final int frequency) {
+ final int frequency, final int shortcutFreq) {
if (DBG_SAVE_RESTORE) {
Log.d(TAG, "load unigram: " + word + "," + frequency);
}
- addWord(word, shortcutTarget, frequency, false /* isNotAWord */);
+ addWord(word, shortcutTarget, frequency, shortcutFreq, false /* isNotAWord */);
++profTotalCount[0];
}
@@ -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/DynamicPersonalizationDictionaryWriter.java b/java/src/com/android/inputmethod/latin/personalization/DynamicPersonalizationDictionaryWriter.java
index 039b25337..6f152bb91 100644
--- a/java/src/com/android/inputmethod/latin/personalization/DynamicPersonalizationDictionaryWriter.java
+++ b/java/src/com/android/inputmethod/latin/personalization/DynamicPersonalizationDictionaryWriter.java
@@ -75,15 +75,21 @@ public class DynamicPersonalizationDictionaryWriter extends AbstractDictionaryWr
/**
* Adds a word unigram to the fusion dictionary. Call updateBinaryDictionary when all changes
* are done to update the binary dictionary.
+ * @param word The word to add.
+ * @param shortcutTarget A shortcut target for this word, or null if none.
+ * @param frequency The frequency for this unigram.
+ * @param shortcutFreq The frequency of the shortcut (0~15, with 15 = whitelist). Ignored
+ * if shortcutTarget is null.
+ * @param isNotAWord true if this is not a word, i.e. shortcut only.
*/
@Override
public void addUnigramWord(final String word, final String shortcutTarget, final int frequency,
- final boolean isNotAWord) {
+ final int shortcutFreq, final boolean isNotAWord) {
if (mBigramList.size() > mMaxHistoryBigrams * 2) {
// Too many entries: just stop adding new vocabulary and wait next refresh.
return;
}
- mExpandableDictionary.addWord(word, shortcutTarget, frequency);
+ mExpandableDictionary.addWord(word, shortcutTarget, frequency, shortcutFreq);
mBigramList.addBigram(null, word, (byte)frequency);
}
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/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
index ee322e91b..f331c78e5 100644
--- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
@@ -24,6 +24,7 @@ import android.view.inputmethod.EditorInfo;
import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.keyboard.internal.KeySpecParser;
+import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.Dictionary;
import com.android.inputmethod.latin.InputAttributes;
import com.android.inputmethod.latin.R;
@@ -45,8 +46,9 @@ import java.util.Locale;
*/
public final class SettingsValues {
private static final String TAG = SettingsValues.class.getSimpleName();
- // "floatNegativeInfinity" is a special marker string for Float.NEGATIVE_INFINITE
- // currently used for auto-correction
+ // "floatMaxValue" and "floatNegativeInfinity" are special marker strings for
+ // Float.NEGATIVE_INFINITE and Float.MAX_VALUE. Currently used for auto-correction settings.
+ private static final String FLOAT_MAX_VALUE_MARKER_STRING = "floatMaxValue";
private static final String FLOAT_NEGATIVE_INFINITY_MARKER_STRING = "floatNegativeInfinity";
// From resources:
@@ -56,6 +58,7 @@ public final class SettingsValues {
public final int[] mWordConnectors;
public final SuggestedWords mSuggestPuncList;
public final String mWordSeparators;
+ public final int mSentenceSeparator;
public final CharSequence mHintToSaveText;
public final boolean mCurrentLanguageHasSpaces;
@@ -119,6 +122,7 @@ public final class SettingsValues {
R.string.suggested_punctuations));
mSuggestPuncList = createSuggestPuncList(suggestPuncsSpec);
mWordSeparators = res.getString(R.string.symbols_word_separators);
+ mSentenceSeparator = res.getInteger(R.integer.sentence_separator);
mHintToSaveText = res.getText(R.string.hint_add_to_dictionary);
mCurrentLanguageHasSpaces = res.getBoolean(R.bool.current_language_has_spaces);
@@ -186,6 +190,7 @@ public final class SettingsValues {
Arrays.sort(mSymbolsFollowedBySpace);
mWordConnectors = new int[] { '\'', '-' };
Arrays.sort(mWordConnectors);
+ mSentenceSeparator = Constants.CODE_PERIOD;
final String[] suggestPuncsSpec = new String[] { "!", "?", ",", ":", ";" };
mSuggestPuncList = createSuggestPuncList(suggestPuncsSpec);
mWordSeparators = "&\t \n()[]{}*&<>+=|.,;:!?/_\"";
@@ -343,24 +348,28 @@ public final class SettingsValues {
final String[] autoCorrectionThresholdValues = res.getStringArray(
R.array.auto_correction_threshold_values);
// When autoCorrectionThreshold is greater than 1.0, it's like auto correction is off.
- float autoCorrectionThreshold = Float.MAX_VALUE;
+ final float autoCorrectionThreshold;
try {
final int arrayIndex = Integer.valueOf(currentAutoCorrectionSetting);
if (arrayIndex >= 0 && arrayIndex < autoCorrectionThresholdValues.length) {
final String val = autoCorrectionThresholdValues[arrayIndex];
- if (FLOAT_NEGATIVE_INFINITY_MARKER_STRING.equals(val)) {
+ if (FLOAT_MAX_VALUE_MARKER_STRING.equals(val)) {
+ autoCorrectionThreshold = Float.MAX_VALUE;
+ } else if (FLOAT_NEGATIVE_INFINITY_MARKER_STRING.equals(val)) {
autoCorrectionThreshold = Float.NEGATIVE_INFINITY;
} else {
autoCorrectionThreshold = Float.parseFloat(val);
}
+ } else {
+ autoCorrectionThreshold = Float.MAX_VALUE;
}
- } catch (NumberFormatException e) {
+ } catch (final NumberFormatException e) {
// Whenever the threshold settings are correct, never come here.
- autoCorrectionThreshold = Float.MAX_VALUE;
Log.w(TAG, "Cannot load auto correction threshold setting."
+ " currentAutoCorrectionSetting: " + currentAutoCorrectionSetting
+ ", autoCorrectionThresholdValues: "
+ Arrays.toString(autoCorrectionThresholdValues), e);
+ return Float.MAX_VALUE;
}
return autoCorrectionThreshold;
}
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
index eb6d7c106..503b18b1b 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
@@ -204,10 +204,20 @@ public final class AndroidSpellCheckerService extends SpellCheckerService
return AndroidSpellCheckerSessionFactory.newInstance(this);
}
- public static SuggestionsInfo getNotInDictEmptySuggestions() {
- return new SuggestionsInfo(0, EMPTY_STRING_ARRAY);
+ /**
+ * Returns an empty SuggestionsInfo with flags signaling the word is not in the dictionary.
+ * @param reportAsTypo whether this should include the flag LOOKS_LIKE_TYPO, for red underline.
+ * @return the empty SuggestionsInfo with the appropriate flags set.
+ */
+ public static SuggestionsInfo getNotInDictEmptySuggestions(final boolean reportAsTypo) {
+ return new SuggestionsInfo(reportAsTypo ? SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO : 0,
+ EMPTY_STRING_ARRAY);
}
+ /**
+ * Returns an empty suggestionInfo with flags signaling the word is in the dictionary.
+ * @return the empty SuggestionsInfo with the appropriate flags set.
+ */
public static SuggestionsInfo getInDictEmptySuggestions() {
return new SuggestionsInfo(SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY,
EMPTY_STRING_ARRAY);
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
index 69f9a467f..d6e5b75ad 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
@@ -161,6 +161,12 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session {
}
}
+ private static final int CHECKABILITY_CHECKABLE = 0;
+ private static final int CHECKABILITY_TOO_MANY_NON_LETTERS = 1;
+ private static final int CHECKABILITY_CONTAINS_PERIOD = 2;
+ private static final int CHECKABILITY_EMAIL_OR_URL = 3;
+ private static final int CHECKABILITY_FIRST_LETTER_UNCHECKABLE = 4;
+ private static final int CHECKABILITY_TOO_SHORT = 5;
/**
* Finds out whether a particular string should be filtered out of spell checking.
*
@@ -171,10 +177,10 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session {
*
* @param text the string to evaluate.
* @param script the identifier for the script this spell checker recognizes
- * @return true if we should filter this text out, false otherwise
+ * @return one of the FILTER_OUT_* constants above.
*/
- private static boolean shouldFilterOut(final String text, final int script) {
- if (TextUtils.isEmpty(text) || text.length() <= 1) return true;
+ private static int getCheckabilityInScript(final String text, final int script) {
+ if (TextUtils.isEmpty(text) || text.length() <= 1) return CHECKABILITY_TOO_SHORT;
// TODO: check if an equivalent processing can't be done more quickly with a
// compiled regexp.
@@ -182,7 +188,7 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session {
final int firstCodePoint = text.codePointAt(0);
// Filter out words that don't start with a letter or an apostrophe
if (!isLetterCheckableByLanguage(firstCodePoint, script)
- && '\'' != firstCodePoint) return true;
+ && '\'' != firstCodePoint) return CHECKABILITY_FIRST_LETTER_UNCHECKABLE;
// Filter contents
final int length = text.length();
@@ -193,13 +199,21 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session {
// Any word containing a SLASH is probably either an ad-hoc combination of two
// words or a URI - in either case we don't want to spell check that
if (Constants.CODE_COMMERCIAL_AT == codePoint || Constants.CODE_SLASH == codePoint) {
- return true;
+ return CHECKABILITY_EMAIL_OR_URL;
+ }
+ // If the string contains a period, native returns strange suggestions (it seems
+ // to return suggestions for everything up to the period only and to ignore the
+ // rest), so we suppress lookup if there is a period.
+ // TODO: investigate why native returns these suggestions and remove this code.
+ if (Constants.CODE_PERIOD == codePoint) {
+ return CHECKABILITY_CONTAINS_PERIOD;
}
if (isLetterCheckableByLanguage(codePoint, script)) ++letterCount;
}
// Guestimate heuristic: perform spell checking if at least 3/4 of the characters
// in this word are letters
- return (letterCount * 4 < length * 3);
+ return (letterCount * 4 < length * 3)
+ ? CHECKABILITY_TOO_MANY_NON_LETTERS : CHECKABILITY_CHECKABLE;
}
/**
@@ -256,16 +270,20 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session {
cachedSuggestionsParams.mFlags, cachedSuggestionsParams.mSuggestions);
}
- if (shouldFilterOut(inText, mScript)) {
+ final int checkability = getCheckabilityInScript(inText, mScript);
+ if (CHECKABILITY_CHECKABLE != checkability) {
DictAndKeyboard dictInfo = null;
try {
dictInfo = mDictionaryPool.pollWithDefaultTimeout();
if (!DictionaryPool.isAValidDictionary(dictInfo)) {
- return AndroidSpellCheckerService.getNotInDictEmptySuggestions();
+ return AndroidSpellCheckerService.getNotInDictEmptySuggestions(
+ false /* reportAsTypo */);
}
return dictInfo.mDictionary.isValidWord(inText)
? AndroidSpellCheckerService.getInDictEmptySuggestions()
- : AndroidSpellCheckerService.getNotInDictEmptySuggestions();
+ : AndroidSpellCheckerService.getNotInDictEmptySuggestions(
+ CHECKABILITY_CONTAINS_PERIOD == checkability
+ /* reportAsTypo */);
} finally {
if (null != dictInfo) {
if (!mDictionaryPool.offer(dictInfo)) {
@@ -290,7 +308,8 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session {
try {
dictInfo = mDictionaryPool.pollWithDefaultTimeout();
if (!DictionaryPool.isAValidDictionary(dictInfo)) {
- return AndroidSpellCheckerService.getNotInDictEmptySuggestions();
+ return AndroidSpellCheckerService.getNotInDictEmptySuggestions(
+ false /* reportAsTypo */);
}
final WordComposer composer = new WordComposer();
final int length = text.length();
@@ -351,7 +370,8 @@ public abstract class AndroidWordLevelSpellCheckerSession extends Session {
throw e;
} else {
Log.e(TAG, "Exception while spellcheking", e);
- return AndroidSpellCheckerService.getNotInDictEmptySuggestions();
+ return AndroidSpellCheckerService.getNotInDictEmptySuggestions(
+ false /* reportAsTypo */);
}
}
}
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/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java b/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java
index 60b24d5d5..3d4404a98 100644
--- a/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java
@@ -21,6 +21,7 @@ import android.text.TextUtils;
import com.android.inputmethod.latin.Constants;
import com.android.inputmethod.latin.WordComposer;
+import com.android.inputmethod.latin.settings.SettingsValues;
import java.util.Locale;
@@ -60,11 +61,6 @@ public final class CapsModeUtils {
|| WordComposer.CAPS_MODE_AUTO_SHIFT_LOCKED == mode;
}
- private static boolean isPeriod(final int codePoint) {
- // TODO: make this a resource.
- return codePoint == Constants.CODE_PERIOD || codePoint == Constants.CODE_ARMENIAN_PERIOD;
- }
-
/**
* Determine what caps mode should be in effect at the current offset in
* the text. Only the mode bits set in <var>reqModes</var> will be
@@ -78,7 +74,7 @@ public final class CapsModeUtils {
* @param reqModes The modes to be checked: may be any combination of
* {@link TextUtils#CAP_MODE_CHARACTERS}, {@link TextUtils#CAP_MODE_WORDS}, and
* {@link TextUtils#CAP_MODE_SENTENCES}.
- * @param locale The locale to consider for capitalization rules
+ * @param settingsValues The current settings values.
* @param hasSpaceBefore Whether we should consider there is a space inserted at the end of cs
*
* @return Returns the actual capitalization modes that can be in effect
@@ -86,8 +82,8 @@ public final class CapsModeUtils {
* {@link TextUtils#CAP_MODE_CHARACTERS}, {@link TextUtils#CAP_MODE_WORDS}, and
* {@link TextUtils#CAP_MODE_SENTENCES}.
*/
- public static int getCapsMode(final CharSequence cs, final int reqModes, final Locale locale,
- final boolean hasSpaceBefore) {
+ public static int getCapsMode(final CharSequence cs, final int reqModes,
+ final SettingsValues settingsValues, final boolean hasSpaceBefore) {
// Quick description of what we want to do:
// CAP_MODE_CHARACTERS is always on.
// CAP_MODE_WORDS is on if there is some whitespace before the cursor.
@@ -172,7 +168,7 @@ public final class CapsModeUtils {
// mark as the exact thing quoted and handling the surrounding punctuation independently,
// e.g. <<Did he say, "let's go home"?>>
// Hence, specifically for English, we treat this special case here.
- if (Locale.ENGLISH.getLanguage().equals(locale.getLanguage())) {
+ if (Locale.ENGLISH.getLanguage().equals(settingsValues.mLocale.getLanguage())) {
for (; j > 0; j--) {
// Here we look to go over any closing punctuation. This is because in dominant
// variants of English, the final period is placed within double quotes and maybe
@@ -195,7 +191,7 @@ public final class CapsModeUtils {
if (c == Constants.CODE_QUESTION_MARK || c == Constants.CODE_EXCLAMATION_MARK) {
return (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_SENTENCES) & reqModes;
}
- if (!isPeriod(c) || j <= 0) {
+ if (settingsValues.mSentenceSeparator != c || j <= 0) {
return (TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS) & reqModes;
}
@@ -245,7 +241,7 @@ public final class CapsModeUtils {
case WORD:
if (Character.isLetter(c)) {
state = WORD;
- } else if (isPeriod(c)) {
+ } else if (settingsValues.mSentenceSeparator == c) {
state = PERIOD;
} else {
return caps;
@@ -261,7 +257,7 @@ public final class CapsModeUtils {
case LETTER:
if (Character.isLetter(c)) {
state = LETTER;
- } else if (isPeriod(c)) {
+ } else if (settingsValues.mSentenceSeparator == c) {
state = PERIOD;
} else {
return noCaps;
diff --git a/java/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtils.java b/java/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtils.java
index ea32a74ff..635afe7cc 100644
--- a/java/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/UserHistoryDictIOUtils.java
@@ -49,7 +49,16 @@ public final class UserHistoryDictIOUtils {
private static final String LAST_UPDATED_TIME_KEY = "date";
public interface OnAddWordListener {
- public void setUnigram(final String word, final String shortcutTarget, final int frequency);
+ /**
+ * Callback to be notified when a word is added to the dictionary.
+ * @param word The added word.
+ * @param shortcutTarget A shortcut target for this word, or null if none.
+ * @param frequency The frequency for this word.
+ * @param shortcutFreq The frequency of the shortcut (0~15, with 15 = whitelist).
+ * Unspecified if shortcutTarget is null - do not rely on its value.
+ */
+ public void setUnigram(final String word, final String shortcutTarget, final int frequency,
+ final int shortcutFreq);
public void setBigram(final String word1, final String word2, final int frequency);
}
@@ -153,7 +162,7 @@ public final class UserHistoryDictIOUtils {
for (Entry<Integer, String> entry : unigrams.entrySet()) {
final String word1 = entry.getValue();
final int unigramFrequency = frequencies.get(entry.getKey());
- to.setUnigram(word1, null, unigramFrequency);
+ to.setUnigram(word1, null /* shortcutTarget */, unigramFrequency, 0 /* shortcutFreq */);
final ArrayList<PendingAttribute> attrList = bigrams.get(entry.getKey());
if (attrList != null) {
for (final PendingAttribute attr : attrList) {