aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common/Android.mk2
-rw-r--r--common/src/com/android/inputmethod/latin/common/CodePointUtils.java20
-rw-r--r--common/src/com/android/inputmethod/latin/common/CollectionUtils.java (renamed from java/src/com/android/inputmethod/latin/utils/CollectionUtils.java)15
-rw-r--r--common/src/com/android/inputmethod/latin/common/ComposedData.java66
-rw-r--r--common/src/com/android/inputmethod/latin/common/Constants.java6
-rw-r--r--common/src/com/android/inputmethod/latin/common/CoordinateUtils.java (renamed from java/src/com/android/inputmethod/latin/utils/CoordinateUtils.java)2
-rw-r--r--common/src/com/android/inputmethod/latin/common/FileUtils.java (renamed from java/src/com/android/inputmethod/latin/utils/FileUtils.java)2
-rw-r--r--common/src/com/android/inputmethod/latin/common/InputPointers.java22
-rw-r--r--common/src/com/android/inputmethod/latin/common/LocaleUtils.java (renamed from java/src/com/android/inputmethod/dictionarypack/LocaleUtils.java)82
-rw-r--r--common/src/com/android/inputmethod/latin/common/NativeSuggestOptions.java (renamed from java/src/com/android/inputmethod/latin/settings/NativeSuggestOptions.java)9
-rw-r--r--common/src/com/android/inputmethod/latin/common/ResizableIntArray.java10
-rw-r--r--dictionaries/de_wordlist.combined.gzbin1292666 -> 1293426 bytes
-rw-r--r--dictionaries/en_GB_wordlist.combined.gzbin860708 -> 865437 bytes
-rw-r--r--dictionaries/en_US_wordlist.combined.gzbin877710 -> 883015 bytes
-rw-r--r--dictionaries/en_wordlist.combined.gzbin909096 -> 914050 bytes
-rw-r--r--dictionaries/es_wordlist.combined.gzbin1164983 -> 1165885 bytes
-rw-r--r--dictionaries/fr_wordlist.combined.gzbin1106371 -> 1108423 bytes
-rw-r--r--dictionaries/it_wordlist.combined.gzbin933697 -> 935024 bytes
-rw-r--r--dictionaries/nl_wordlist.combined.gzbin1053242 -> 1054497 bytes
-rw-r--r--dictionaries/pl_wordlist.combined.gzbin1089296 -> 1090690 bytes
-rw-r--r--dictionaries/pt_BR_wordlist.combined.gzbin878505 -> 879877 bytes
-rw-r--r--dictionaries/pt_PT_wordlist.combined.gzbin1105959 -> 1106730 bytes
-rw-r--r--dictionaries/ru_wordlist.combined.gzbin1394944 -> 1397626 bytes
-rw-r--r--dictionaries/sv_wordlist.combined.gzbin1139281 -> 1140879 bytes
-rw-r--r--dictionaries/tr_wordlist.combined.gzbin925948 -> 926338 bytes
-rw-r--r--java-overridable/src/com/android/inputmethod/latin/define/ProductionFlags.java6
-rw-r--r--java-overridable/src/com/android/inputmethod/latin/settings/AdditionalFeaturesSettingUtils.java1
-rw-r--r--java/Android.mk2
-rw-r--r--java/AndroidManifest.xml1
-rw-r--r--java/res/drawable/btn_keyboard_key_ics.xml2
-rw-r--r--java/res/drawable/btn_keyboard_key_klp.xml2
-rw-r--r--java/res/drawable/btn_keyboard_key_lxx_dark.xml4
-rw-r--r--java/res/drawable/btn_keyboard_key_lxx_light.xml4
-rw-r--r--java/res/raw/main_de.dictbin1606096 -> 1606096 bytes
-rw-r--r--java/res/raw/main_en.dictbin1070485 -> 1070485 bytes
-rw-r--r--java/res/raw/main_es.dictbin1377071 -> 1377071 bytes
-rw-r--r--java/res/raw/main_fr.dictbin1328940 -> 1328933 bytes
-rw-r--r--java/res/raw/main_it.dictbin1143338 -> 1143338 bytes
-rw-r--r--java/res/raw/main_pt_br.dictbin1092054 -> 1092054 bytes
-rw-r--r--java/res/raw/main_ru.dictbin1292026 -> 1292026 bytes
-rw-r--r--java/res/values-af/strings-emoji-descriptions.xml2
-rw-r--r--java/res/values-af/strings.xml3
-rw-r--r--java/res/values-ar/strings-emoji-descriptions.xml2
-rw-r--r--java/res/values-ar/strings.xml3
-rw-r--r--java/res/values-az-rAZ/strings.xml3
-rw-r--r--java/res/values-bg/strings.xml3
-rw-r--r--java/res/values-bn-rBD/strings.xml3
-rw-r--r--java/res/values-ca/strings.xml3
-rw-r--r--java/res/values-cs/strings-emoji-descriptions.xml2
-rw-r--r--java/res/values-da/strings.xml3
-rw-r--r--java/res/values-el/strings-emoji-descriptions.xml2
-rw-r--r--java/res/values-en-rGB/strings.xml3
-rw-r--r--java/res/values-en-rIN/strings.xml3
-rw-r--r--java/res/values-es-rUS/strings.xml3
-rw-r--r--java/res/values-eu-rES/strings-emoji-descriptions.xml2
-rw-r--r--java/res/values-eu-rES/strings.xml3
-rw-r--r--java/res/values-fa/strings-emoji-descriptions.xml2
-rw-r--r--java/res/values-fa/strings.xml5
-rw-r--r--java/res/values-fi/strings-emoji-descriptions.xml2
-rw-r--r--java/res/values-fi/strings.xml3
-rw-r--r--java/res/values-fr-rCA/strings.xml3
-rw-r--r--java/res/values-fr/strings.xml3
-rw-r--r--java/res/values-gl-rES/strings.xml3
-rw-r--r--java/res/values-hi/strings.xml3
-rw-r--r--java/res/values-hr/strings.xml3
-rw-r--r--java/res/values-hu/strings-emoji-descriptions.xml2
-rw-r--r--java/res/values-hy-rAM/strings-emoji-descriptions.xml2
-rw-r--r--java/res/values-hy-rAM/strings.xml3
-rw-r--r--java/res/values-is-rIS/strings.xml3
-rw-r--r--java/res/values-iw/strings-emoji-descriptions.xml2
-rw-r--r--java/res/values-ja/strings.xml3
-rw-r--r--java/res/values-ka-rGE/strings.xml3
-rw-r--r--java/res/values-kk-rKZ/strings.xml3
-rw-r--r--java/res/values-km-rKH/strings-emoji-descriptions.xml2
-rw-r--r--java/res/values-kn-rIN/strings-emoji-descriptions.xml2
-rw-r--r--java/res/values-ko/strings.xml3
-rw-r--r--java/res/values-ky-rKG/strings.xml3
-rw-r--r--java/res/values-lo-rLA/strings-emoji-descriptions.xml2
-rw-r--r--java/res/values-lo-rLA/strings.xml3
-rw-r--r--java/res/values-lt/strings-emoji-descriptions.xml2
-rw-r--r--java/res/values-lv/strings-emoji-descriptions.xml2
-rw-r--r--java/res/values-lv/strings.xml3
-rw-r--r--java/res/values-ml-rIN/strings.xml3
-rw-r--r--java/res/values-mr-rIN/strings.xml3
-rw-r--r--java/res/values-ms-rMY/strings.xml3
-rw-r--r--java/res/values-my-rMM/strings.xml3
-rw-r--r--java/res/values-nb/strings-emoji-descriptions.xml2
-rw-r--r--java/res/values-ne-rNP/strings-emoji-descriptions.xml2
-rw-r--r--java/res/values-ne-rNP/strings.xml3
-rw-r--r--java/res/values-pt/strings.xml3
-rw-r--r--java/res/values-ro/strings-emoji-descriptions.xml2
-rw-r--r--java/res/values-ro/strings.xml3
-rw-r--r--java/res/values-ru/strings.xml3
-rw-r--r--java/res/values-si-rLK/strings.xml3
-rw-r--r--java/res/values-sk/strings.xml3
-rw-r--r--java/res/values-sl/strings.xml3
-rw-r--r--java/res/values-sr/strings.xml3
-rw-r--r--java/res/values-sv/strings-emoji-descriptions.xml2
-rw-r--r--java/res/values-sw/strings-emoji-descriptions.xml2
-rw-r--r--java/res/values-sw/strings.xml3
-rw-r--r--java/res/values-ta-rIN/strings.xml3
-rw-r--r--java/res/values-te-rIN/strings.xml3
-rw-r--r--java/res/values-th/strings.xml3
-rw-r--r--java/res/values-tl/strings.xml3
-rw-r--r--java/res/values-tr/strings.xml3
-rw-r--r--java/res/values-uk/strings.xml3
-rw-r--r--java/res/values-ur-rPK/strings.xml3
-rw-r--r--java/res/values-uz-rUZ/strings.xml3
-rw-r--r--java/res/values-vi/strings-emoji-descriptions.xml2
-rw-r--r--java/res/values-vi/strings.xml3
-rw-r--r--java/res/values-zh-rCN/strings.xml3
-rw-r--r--java/res/values-zh-rHK/strings.xml3
-rw-r--r--java/res/values-zh-rTW/strings-emoji-descriptions.xml2
-rw-r--r--java/res/values-zh-rTW/strings.xml3
-rw-r--r--java/res/xml-sw600dp/key_space_3kw.xml8
-rw-r--r--java/res/xml-sw600dp/key_space_7kw.xml8
-rw-r--r--java/res/xml-sw600dp/rows_number_normal.xml28
-rw-r--r--java/res/xml-sw600dp/rows_number_password.xml3
-rw-r--r--java/res/xml-sw600dp/rows_phone.xml31
-rw-r--r--java/res/xml/key_space_5kw.xml8
-rw-r--r--java/res/xml/key_styles_number.xml20
-rw-r--r--java/res/xml/keyboard_layout_set_bengali_akkhor.xml2
-rw-r--r--java/res/xml/rows_number_normal.xml14
-rw-r--r--java/res/xml/rows_number_password.xml3
-rw-r--r--java/res/xml/rows_phone.xml6
-rw-r--r--java/res/xml/rows_phone_symbols.xml32
-rw-r--r--java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityNodeProvider.java2
-rw-r--r--java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatUtils.java3
-rw-r--r--java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java2
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java1
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/DictionaryService.java1
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java2
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/DownloadOverMeteredDialog.java1
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java3
-rw-r--r--java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java5
-rw-r--r--java/src/com/android/inputmethod/keyboard/Keyboard.java2
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java54
-rw-r--r--java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java69
-rw-r--r--java/src/com/android/inputmethod/keyboard/MainKeyboardView.java6
-rw-r--r--java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java2
-rw-r--r--java/src/com/android/inputmethod/keyboard/PointerTracker.java70
-rw-r--r--java/src/com/android/inputmethod/keyboard/emoji/EmojiPageKeyboardView.java2
-rw-r--r--java/src/com/android/inputmethod/keyboard/emoji/EmojiPalettesView.java4
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/DrawingPreviewPlacerView.java2
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/DrawingProxy.java72
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/GestureFloatingTextDrawingPreview.java2
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java2
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java3
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java152
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/LanguageOnSpacebarHelper.java31
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java2
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/NonDistinctMultitouchHelper.java2
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/SlidingKeyInputDrawingPreview.java2
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java20
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/TimerProxy.java133
-rw-r--r--java/src/com/android/inputmethod/latin/AssetFileAddress.java7
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionary.java19
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java5
-rw-r--r--java/src/com/android/inputmethod/latin/DicTraverseSession.java6
-rw-r--r--java/src/com/android/inputmethod/latin/Dictionary.java14
-rw-r--r--java/src/com/android/inputmethod/latin/DictionaryCollection.java16
-rw-r--r--java/src/com/android/inputmethod/latin/DictionaryFacilitator.java7
-rw-r--r--java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java17
-rw-r--r--java/src/com/android/inputmethod/latin/LatinIME.java41
-rw-r--r--java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java12
-rw-r--r--java/src/com/android/inputmethod/latin/RichInputMethodManager.java176
-rw-r--r--java/src/com/android/inputmethod/latin/RichInputMethodSubtype.java107
-rw-r--r--java/src/com/android/inputmethod/latin/SubtypeSwitcher.java282
-rw-r--r--java/src/com/android/inputmethod/latin/Suggest.java14
-rw-r--r--java/src/com/android/inputmethod/latin/SuggestedWords.java14
-rw-r--r--java/src/com/android/inputmethod/latin/WordComposer.java39
-rw-r--r--java/src/com/android/inputmethod/latin/debug/ExternalDictionaryGetterForDebug.java2
-rw-r--r--java/src/com/android/inputmethod/latin/makedict/FormatSpec.java14
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java27
-rw-r--r--java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java60
-rw-r--r--java/src/com/android/inputmethod/latin/settings/CustomInputStylePreference.java4
-rw-r--r--java/src/com/android/inputmethod/latin/settings/PreferencesSettingsFragment.java6
-rw-r--r--java/src/com/android/inputmethod/latin/settings/SettingsValues.java3
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java3
-rw-r--r--java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java4
-rw-r--r--java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java5
-rw-r--r--java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java2
-rw-r--r--java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryList.java2
-rw-r--r--java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettingsUtils.java2
-rw-r--r--java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java28
-rw-r--r--java/src/com/android/inputmethod/latin/utils/CombinedFormatUtils.java13
-rw-r--r--java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java1
-rw-r--r--java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatchesAndSuggestions.java5
-rw-r--r--java/src/com/android/inputmethod/latin/utils/LocaleUtils.java190
-rw-r--r--java/src/com/android/inputmethod/latin/utils/RecapitalizeStatus.java11
-rw-r--r--java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java76
-rw-r--r--native/dicttoolkit/Android.mk67
-rw-r--r--native/dicttoolkit/CleanupNativeFileList.mk17
-rw-r--r--native/dicttoolkit/NativeFileList.mk43
-rw-r--r--native/dicttoolkit/UnitTests.mk69
-rw-r--r--native/dicttoolkit/dict_toolkit_main.cpp39
-rwxr-xr-xnative/dicttoolkit/run_tests.sh34
-rw-r--r--native/dicttoolkit/src/command_executors/diff_executor.cpp49
-rw-r--r--native/dicttoolkit/src/command_executors/diff_executor.h40
-rw-r--r--native/dicttoolkit/src/command_executors/header_executor.cpp48
-rw-r--r--native/dicttoolkit/src/command_executors/header_executor.h40
-rw-r--r--native/dicttoolkit/src/command_executors/help_executor.cpp52
-rw-r--r--native/dicttoolkit/src/command_executors/help_executor.h38
-rw-r--r--native/dicttoolkit/src/command_executors/info_executor.cpp54
-rw-r--r--native/dicttoolkit/src/command_executors/info_executor.h40
-rw-r--r--native/dicttoolkit/src/command_executors/makedict_executor.cpp55
-rw-r--r--native/dicttoolkit/src/command_executors/makedict_executor.h40
-rw-r--r--native/dicttoolkit/src/dict_toolkit_defines.h24
-rw-r--r--native/dicttoolkit/src/offdevice_intermediate_dict/offdevice_intermediate_dict.cpp126
-rw-r--r--native/dicttoolkit/src/offdevice_intermediate_dict/offdevice_intermediate_dict.h54
-rw-r--r--native/dicttoolkit/src/offdevice_intermediate_dict/offdevice_intermediate_dict_header.h44
-rw-r--r--native/dicttoolkit/src/offdevice_intermediate_dict/offdevice_intermediate_dict_pt_node.h79
-rw-r--r--native/dicttoolkit/src/offdevice_intermediate_dict/offdevice_intermediate_dict_pt_node_array.h48
-rw-r--r--native/dicttoolkit/src/utils/arguments_and_options.h54
-rw-r--r--native/dicttoolkit/src/utils/arguments_parser.cpp84
-rw-r--r--native/dicttoolkit/src/utils/arguments_parser.h118
-rw-r--r--native/dicttoolkit/src/utils/command_utils.cpp74
-rw-r--r--native/dicttoolkit/src/utils/command_utils.h50
-rw-r--r--native/dicttoolkit/src/utils/utf8_utils.cpp119
-rw-r--r--native/dicttoolkit/src/utils/utf8_utils.h56
-rw-r--r--native/dicttoolkit/tests/command_executors/diff_executor_test.cpp31
-rw-r--r--native/dicttoolkit/tests/command_executors/header_executor_test.cpp31
-rw-r--r--native/dicttoolkit/tests/command_executors/info_executor_test.cpp31
-rw-r--r--native/dicttoolkit/tests/command_executors/makedict_executor_test.cpp31
-rw-r--r--native/dicttoolkit/tests/dict_toolkit_defines_test.cpp32
-rw-r--r--native/dicttoolkit/tests/offdevice_intermediate_dict/offdevice_intermediate_dict_test.cpp85
-rw-r--r--native/dicttoolkit/tests/utils/command_utils_test.cpp37
-rw-r--r--native/dicttoolkit/tests/utils/utf8_utils_test.cpp85
-rw-r--r--native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp12
-rw-r--r--native/jni/src/defines.h67
-rw-r--r--native/jni/src/suggest/core/dictionary/error_type_utils.cpp1
-rw-r--r--native/jni/src/suggest/core/dictionary/error_type_utils.h5
-rw-r--r--native/jni/src/suggest/core/dictionary/property/historical_info.h1
-rw-r--r--native/jni/src/suggest/core/dictionary/property/word_property.h21
-rw-r--r--native/jni/src/suggest/core/policy/scoring.h2
-rw-r--r--native/jni/src/suggest/core/result/suggestions_output_utils.cpp66
-rw-r--r--native/jni/src/suggest/core/result/suggestions_output_utils.h9
-rw-r--r--native/jni/src/suggest/core/suggest.cpp21
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h16
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.cpp7
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/bigram_dict_content.cpp2
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/probability_dict_content.cpp3
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.cpp6
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.cpp18
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_params.h11
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.cpp14
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h2
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp4
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h1
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp14
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.cpp4
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.cpp14
-rw-r--r--native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.h8
-rw-r--r--native/jni/src/suggest/policyimpl/typing/scoring_params.cpp1
-rw-r--r--native/jni/src/suggest/policyimpl/typing/scoring_params.h1
-rw-r--r--native/jni/src/suggest/policyimpl/typing/typing_scoring.h47
-rw-r--r--native/jni/src/utils/int_array_view.h23
-rw-r--r--native/jni/src/utils/profiler.h86
-rw-r--r--native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_test.cpp4
-rw-r--r--native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/probability_entry_test.cpp8
-rw-r--r--native/jni/tests/suggest/policyimpl/dictionary/utils/format_utils_test.cpp8
-rw-r--r--native/jni/tests/utils/int_array_view_test.cpp47
-rw-r--r--tests/Android.mk2
-rw-r--r--tests/src/com/android/inputmethod/keyboard/action/ActionTestsBase.java2
-rw-r--r--tests/src/com/android/inputmethod/keyboard/internal/LanguageOnSpacebarHelperTests.java244
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBengaliBD.java5
-rw-r--r--tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java10
-rw-r--r--tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java121
-rw-r--r--tests/src/com/android/inputmethod/latin/InputTestsBase.java4
-rw-r--r--tests/src/com/android/inputmethod/latin/RichInputMethodSubtypeTests.java (renamed from tests/src/com/android/inputmethod/latin/utils/SpacebarLanguageUtilsTests.java)9
-rw-r--r--tests/src/com/android/inputmethod/latin/WordComposerTests.java2
-rw-r--r--tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java43
-rw-r--r--tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java42
-rw-r--r--tests/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java16
-rw-r--r--tests/src/com/android/inputmethod/latin/makedict/BinaryDictUtils.java15
-rw-r--r--tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java3
-rw-r--r--tests/src/com/android/inputmethod/latin/makedict/Ver2DictEncoder.java3
-rw-r--r--tests/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java2
-rw-r--r--tests/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java2
-rw-r--r--tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java66
-rw-r--r--tests/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtilsTests.java2
-rw-r--r--tests/src/com/android/inputmethod/latin/utils/CapsModeUtilsTests.java2
-rw-r--r--tests/src/com/android/inputmethod/latin/utils/CollectionUtilsTests.java35
-rw-r--r--tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java14
-rw-r--r--tools/dicttool/Android.mk10
-rw-r--r--tools/dicttool/compat/com/android/inputmethod/event/CombinerChain.java54
-rw-r--r--tools/dicttool/src/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtils.java272
-rw-r--r--tools/dicttool/src/com/android/inputmethod/latin/dicttool/CombinedInputOutput.java51
-rw-r--r--tools/dicttool/src/com/android/inputmethod/latin/dicttool/CommandList.java2
-rw-r--r--tools/dicttool/src/com/android/inputmethod/latin/dicttool/DictionaryMaker.java2
-rw-r--r--tools/dicttool/src/com/android/inputmethod/latin/dicttool/Header.java70
-rw-r--r--tools/dicttool/src/com/android/inputmethod/latin/dicttool/Package.java10
-rw-r--r--tools/dicttool/src/com/android/inputmethod/latin/dicttool/Test.java2
-rw-r--r--tools/dicttool/tests/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtilsTests.java109
294 files changed, 4513 insertions, 1794 deletions
diff --git a/common/Android.mk b/common/Android.mk
index 085543f75..132a22358 100644
--- a/common/Android.mk
+++ b/common/Android.mk
@@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-LOCAL_PATH:= $(call my-dir)
+LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := latinime-common
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/common/src/com/android/inputmethod/latin/common/CodePointUtils.java b/common/src/com/android/inputmethod/latin/common/CodePointUtils.java
index 592da5c1f..ec59de850 100644
--- a/common/src/com/android/inputmethod/latin/common/CodePointUtils.java
+++ b/common/src/com/android/inputmethod/latin/common/CodePointUtils.java
@@ -20,6 +20,8 @@ import com.android.inputmethod.annotations.UsedForTesting;
import java.util.Random;
+import javax.annotation.Nonnull;
+
// Utility methods related with code points used for tests.
// TODO: Figure out where this class should be.
@UsedForTesting
@@ -65,17 +67,23 @@ public class CodePointUtils {
};
@UsedForTesting
- public static int[] generateCodePointSet(final int codePointSetSize, final Random random) {
+ @Nonnull
+ public static int[] generateCodePointSet(final int codePointSetSize,
+ @Nonnull final Random random) {
final int[] codePointSet = new int[codePointSetSize];
for (int i = codePointSet.length - 1; i >= 0; ) {
final int r = Math.abs(random.nextInt());
- if (r < 0) continue;
+ if (r < 0) {
+ continue;
+ }
// Don't insert 0~0x20, but insert any other code point.
// Code points are in the range 0~0x10FFFF.
final int candidateCodePoint = 0x20 + r % (Character.MAX_CODE_POINT - 0x20);
// Code points between MIN_ and MAX_SURROGATE are not valid on their own.
if (candidateCodePoint >= Character.MIN_SURROGATE
- && candidateCodePoint <= Character.MAX_SURROGATE) continue;
+ && candidateCodePoint <= Character.MAX_SURROGATE) {
+ continue;
+ }
codePointSet[i] = candidateCodePoint;
--i;
}
@@ -86,8 +94,10 @@ public class CodePointUtils {
* Generates a random word.
*/
@UsedForTesting
- public static String generateWord(final Random random, final int[] codePointSet) {
- StringBuilder builder = new StringBuilder();
+ @Nonnull
+ public static String generateWord(@Nonnull final Random random,
+ @Nonnull final int[] codePointSet) {
+ final StringBuilder builder = new StringBuilder();
// 8 * 4 = 32 chars max, but we do it the following way so as to bias the random toward
// longer words. This should be closer to natural language, and more importantly, it will
// exercise the algorithms in dicttool much more.
diff --git a/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java b/common/src/com/android/inputmethod/latin/common/CollectionUtils.java
index f9839eb91..f7ba693af 100644
--- a/java/src/com/android/inputmethod/latin/utils/CollectionUtils.java
+++ b/common/src/com/android/inputmethod/latin/common/CollectionUtils.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.inputmethod.latin.utils;
+package com.android.inputmethod.latin.common;
import java.util.ArrayList;
import java.util.Collection;
@@ -22,16 +22,27 @@ import java.util.Collection;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
+/**
+ * Utility methods for working with collections.
+ */
public final class CollectionUtils {
private CollectionUtils() {
// This utility class is not publicly instantiable.
}
+ /**
+ * Converts a sub-range of the given array to an ArrayList of the appropriate type.
+ * @param array Array to be converted.
+ * @param start First index inclusive to be converted.
+ * @param end Last index exclusive to be converted.
+ * @throws IllegalArgumentException if start or end are out of range or start &gt; end.
+ */
@Nonnull
public static <E> ArrayList<E> arrayAsList(@Nonnull final E[] array, final int start,
final int end) {
if (start < 0 || start > end || end > array.length) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("Invalid start: " + start + " end: " + end
+ + " with array.length: " + array.length);
}
final ArrayList<E> list = new ArrayList<>(end - start);
diff --git a/common/src/com/android/inputmethod/latin/common/ComposedData.java b/common/src/com/android/inputmethod/latin/common/ComposedData.java
new file mode 100644
index 000000000..7f0966050
--- /dev/null
+++ b/common/src/com/android/inputmethod/latin/common/ComposedData.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.latin.common;
+
+import javax.annotation.Nonnull;
+
+/**
+ * An immutable class that encapsulates a snapshot of word composition data.
+ */
+public class ComposedData {
+ @Nonnull
+ public final InputPointers mInputPointers;
+ public final boolean mIsBatchMode;
+ @Nonnull
+ public final String mTypedWord;
+
+ public ComposedData(@Nonnull final InputPointers inputPointers, final boolean isBatchMode,
+ @Nonnull final String typedWord) {
+ mInputPointers = inputPointers;
+ mIsBatchMode = isBatchMode;
+ mTypedWord = typedWord;
+ }
+
+ /**
+ * Copy the code points in the typed word to a destination array of ints.
+ *
+ * If the array is too small to hold the code points in the typed word, nothing is copied and
+ * -1 is returned.
+ *
+ * @param destination the array of ints.
+ * @return the number of copied code points.
+ */
+ public int copyCodePointsExceptTrailingSingleQuotesAndReturnCodePointCount(
+ @Nonnull final int[] destination) {
+ // lastIndex is exclusive
+ final int lastIndex = mTypedWord.length()
+ - StringUtils.getTrailingSingleQuotesCount(mTypedWord);
+ if (lastIndex <= 0) {
+ // The string is empty or contains only single quotes.
+ return 0;
+ }
+
+ // The following function counts the number of code points in the text range which begins
+ // at index 0 and extends to the character at lastIndex.
+ final int codePointSize = Character.codePointCount(mTypedWord, 0, lastIndex);
+ if (codePointSize > destination.length) {
+ return -1;
+ }
+ return StringUtils.copyCodePointsAndReturnCodePointCount(destination, mTypedWord, 0,
+ lastIndex, true /* downCase */);
+ }
+}
diff --git a/common/src/com/android/inputmethod/latin/common/Constants.java b/common/src/com/android/inputmethod/latin/common/Constants.java
index 8f4a1e50d..abc377a84 100644
--- a/common/src/com/android/inputmethod/latin/common/Constants.java
+++ b/common/src/com/android/inputmethod/latin/common/Constants.java
@@ -18,6 +18,8 @@ package com.android.inputmethod.latin.common;
import com.android.inputmethod.annotations.UsedForTesting;
+import javax.annotation.Nonnull;
+
public final class Constants {
public static final class Color {
/**
@@ -259,6 +261,7 @@ public final class Constants {
return code >= CODE_SPACE;
}
+ @Nonnull
public static String printableCode(final int code) {
switch (code) {
case CODE_SHIFT: return "shift";
@@ -286,7 +289,8 @@ public final class Constants {
}
}
- public static String printableCodes(final int[] codes) {
+ @Nonnull
+ public static String printableCodes(@Nonnull final int[] codes) {
final StringBuilder sb = new StringBuilder();
boolean addDelimiter = false;
for (final int code : codes) {
diff --git a/java/src/com/android/inputmethod/latin/utils/CoordinateUtils.java b/common/src/com/android/inputmethod/latin/common/CoordinateUtils.java
index 3a9705904..031662411 100644
--- a/java/src/com/android/inputmethod/latin/utils/CoordinateUtils.java
+++ b/common/src/com/android/inputmethod/latin/common/CoordinateUtils.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.inputmethod.latin.utils;
+package com.android.inputmethod.latin.common;
import javax.annotation.Nonnull;
diff --git a/java/src/com/android/inputmethod/latin/utils/FileUtils.java b/common/src/com/android/inputmethod/latin/common/FileUtils.java
index f1106a6c6..676845842 100644
--- a/java/src/com/android/inputmethod/latin/utils/FileUtils.java
+++ b/common/src/com/android/inputmethod/latin/common/FileUtils.java
@@ -14,7 +14,7 @@
* the License.
*/
-package com.android.inputmethod.latin.utils;
+package com.android.inputmethod.latin.common;
import java.io.File;
import java.io.FilenameFilter;
diff --git a/common/src/com/android/inputmethod/latin/common/InputPointers.java b/common/src/com/android/inputmethod/latin/common/InputPointers.java
index 40131aca4..7beee1536 100644
--- a/common/src/com/android/inputmethod/latin/common/InputPointers.java
+++ b/common/src/com/android/inputmethod/latin/common/InputPointers.java
@@ -18,6 +18,8 @@ package com.android.inputmethod.latin.common;
import com.android.inputmethod.annotations.UsedForTesting;
+import javax.annotation.Nonnull;
+
// TODO: This class is not thread-safe.
public final class InputPointers {
private static final boolean DEBUG_TIME = false;
@@ -28,7 +30,7 @@ public final class InputPointers {
private final ResizableIntArray mPointerIds;
private final ResizableIntArray mTimes;
- public InputPointers(int defaultCapacity) {
+ public InputPointers(final int defaultCapacity) {
mDefaultCapacity = defaultCapacity;
mXCoordinates = new ResizableIntArray(defaultCapacity);
mYCoordinates = new ResizableIntArray(defaultCapacity);
@@ -51,7 +53,8 @@ public final class InputPointers {
mTimes.fill(lastTime, fromIndex, fillLength);
}
- public void addPointerAt(int index, int x, int y, int pointerId, int time) {
+ public void addPointerAt(final int index, final int x, final int y, final int pointerId,
+ final int time) {
mXCoordinates.addAt(index, x);
mYCoordinates.addAt(index, y);
mPointerIds.addAt(index, pointerId);
@@ -62,21 +65,21 @@ public final class InputPointers {
}
@UsedForTesting
- public void addPointer(int x, int y, int pointerId, int time) {
+ public void addPointer(final int x, final int y, final int pointerId, final int time) {
mXCoordinates.add(x);
mYCoordinates.add(y);
mPointerIds.add(pointerId);
mTimes.add(time);
}
- public void set(InputPointers ip) {
+ public void set(@Nonnull final InputPointers ip) {
mXCoordinates.set(ip.mXCoordinates);
mYCoordinates.set(ip.mYCoordinates);
mPointerIds.set(ip.mPointerIds);
mTimes.set(ip.mTimes);
}
- public void copy(InputPointers ip) {
+ public void copy(@Nonnull final InputPointers ip) {
mXCoordinates.copy(ip.mXCoordinates);
mYCoordinates.copy(ip.mYCoordinates);
mPointerIds.copy(ip.mPointerIds);
@@ -93,8 +96,9 @@ public final class InputPointers {
* @param startPos the starting index of the data in {@code times} and etc.
* @param length the number of data to be appended.
*/
- public void append(int pointerId, ResizableIntArray times, ResizableIntArray xCoordinates,
- ResizableIntArray yCoordinates, int startPos, int length) {
+ public void append(final int pointerId, @Nonnull final ResizableIntArray times,
+ @Nonnull final ResizableIntArray xCoordinates,
+ @Nonnull final ResizableIntArray yCoordinates, final int startPos, final int length) {
if (length == 0) {
return;
}
@@ -127,14 +131,17 @@ public final class InputPointers {
return mXCoordinates.getLength();
}
+ @Nonnull
public int[] getXCoordinates() {
return mXCoordinates.getPrimitiveArray();
}
+ @Nonnull
public int[] getYCoordinates() {
return mYCoordinates.getPrimitiveArray();
}
+ @Nonnull
public int[] getPointerIds() {
return mPointerIds.getPrimitiveArray();
}
@@ -145,6 +152,7 @@ public final class InputPointers {
* @return The time each point was registered, in milliseconds, relative to the first event in
* the sequence.
*/
+ @Nonnull
public int[] getTimes() {
return mTimes.getPrimitiveArray();
}
diff --git a/java/src/com/android/inputmethod/dictionarypack/LocaleUtils.java b/common/src/com/android/inputmethod/latin/common/LocaleUtils.java
index 4f0805c5c..7f2333be5 100644
--- a/java/src/com/android/inputmethod/dictionarypack/LocaleUtils.java
+++ b/common/src/com/android/inputmethod/latin/common/LocaleUtils.java
@@ -14,15 +14,15 @@
* the License.
*/
-package com.android.inputmethod.dictionarypack;
-
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.text.TextUtils;
+package com.android.inputmethod.latin.common;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Locale;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
/**
* A class to help with handling Locales in string form.
*
@@ -105,8 +105,8 @@ public final class LocaleUtils {
* @return a constant that measures how well the tested locale matches the reference locale.
*/
public static int getMatchLevel(final String referenceLocale, final String testedLocale) {
- if (TextUtils.isEmpty(referenceLocale)) {
- return TextUtils.isEmpty(testedLocale) ? LOCALE_FULL_MATCH : LOCALE_ANY_MATCH;
+ if (StringUtils.isEmpty(referenceLocale)) {
+ return StringUtils.isEmpty(testedLocale) ? LOCALE_FULL_MATCH : LOCALE_ANY_MATCH;
}
if (null == testedLocale) return LOCALE_NO_MATCH;
final String[] referenceParams = referenceLocale.split("_", 3);
@@ -160,45 +160,53 @@ public final class LocaleUtils {
return LOCALE_MATCH <= level;
}
- /**
- * Sets the system locale for this process.
- *
- * @param res the resources to use. Pass current resources.
- * @param newLocale the locale to change to.
- * @return the old locale.
- */
- public static Locale setSystemLocale(final Resources res, final Locale newLocale) {
- final Configuration conf = res.getConfiguration();
- final Locale saveLocale = conf.locale;
- conf.locale = newLocale;
- res.updateConfiguration(conf, res.getDisplayMetrics());
- return saveLocale;
- }
-
private static final HashMap<String, Locale> sLocaleCache = new HashMap<>();
/**
* Creates a locale from a string specification.
+ * @param localeString a string specification of a locale, in a format of "ll_cc_variant" where
+ * "ll" is a language code, "cc" is a country code.
*/
- public static Locale constructLocaleFromString(final String localeStr) {
- if (localeStr == null)
+ @Nullable
+ public static Locale constructLocaleFromString(@Nullable final String localeString) {
+ if (localeString == null) {
return null;
+ }
synchronized (sLocaleCache) {
- if (sLocaleCache.containsKey(localeStr))
- return sLocaleCache.get(localeStr);
- Locale retval = null;
- String[] localeParams = localeStr.split("_", 3);
- if (localeParams.length == 1) {
- retval = new Locale(localeParams[0]);
- } else if (localeParams.length == 2) {
- retval = new Locale(localeParams[0], localeParams[1]);
- } else if (localeParams.length == 3) {
- retval = new Locale(localeParams[0], localeParams[1], localeParams[2]);
+ if (sLocaleCache.containsKey(localeString)) {
+ return sLocaleCache.get(localeString);
}
- if (retval != null) {
- sLocaleCache.put(localeStr, retval);
+ final String[] elements = localeString.split("_", 3);
+ final Locale locale;
+ if (elements.length == 1) {
+ locale = new Locale(elements[0] /* language */);
+ } else if (elements.length == 2) {
+ locale = new Locale(elements[0] /* language */, elements[1] /* country */);
+ } else { // localeParams.length == 3
+ locale = new Locale(elements[0] /* language */, elements[1] /* country */,
+ elements[2] /* variant */);
}
- return retval;
+ sLocaleCache.put(localeString, locale);
+ return locale;
}
}
+
+ // TODO: Get this information from the framework instead of maintaining here by ourselves.
+ private static final HashSet<String> sRtlLanguageCodes = new HashSet<>();
+ static {
+ // List of known Right-To-Left language codes.
+ sRtlLanguageCodes.add("ar"); // Arabic
+ sRtlLanguageCodes.add("fa"); // Persian
+ sRtlLanguageCodes.add("iw"); // Hebrew
+ sRtlLanguageCodes.add("ku"); // Kurdish
+ sRtlLanguageCodes.add("ps"); // Pashto
+ sRtlLanguageCodes.add("sd"); // Sindhi
+ sRtlLanguageCodes.add("ug"); // Uyghur
+ sRtlLanguageCodes.add("ur"); // Urdu
+ sRtlLanguageCodes.add("yi"); // Yiddish
+ }
+
+ public static boolean isRtlLanguage(@Nonnull final Locale locale) {
+ return sRtlLanguageCodes.contains(locale.getLanguage());
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/settings/NativeSuggestOptions.java b/common/src/com/android/inputmethod/latin/common/NativeSuggestOptions.java
index 7603dbba5..7ef741cc2 100644
--- a/java/src/com/android/inputmethod/latin/settings/NativeSuggestOptions.java
+++ b/common/src/com/android/inputmethod/latin/common/NativeSuggestOptions.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.inputmethod.latin.settings;
+package com.android.inputmethod.latin.common;
public class NativeSuggestOptions {
// Need to update suggest_options.h when you add, remove or reorder options.
@@ -25,8 +25,11 @@ public class NativeSuggestOptions {
private static final int WEIGHT_FOR_LOCALE_IN_THOUSANDS = 4;
private static final int OPTIONS_SIZE = 5;
- private final int[] mOptions = new int[OPTIONS_SIZE
- + AdditionalFeaturesSettingUtils.ADDITIONAL_FEATURES_SETTINGS_SIZE];
+ private final int[] mOptions;
+
+ public NativeSuggestOptions(final int additionalFeaturesSettingsSize) {
+ mOptions = new int[OPTIONS_SIZE + additionalFeaturesSettingsSize];
+ }
public void setIsGesture(final boolean value) {
setBooleanOption(IS_GESTURE, value);
diff --git a/common/src/com/android/inputmethod/latin/common/ResizableIntArray.java b/common/src/com/android/inputmethod/latin/common/ResizableIntArray.java
index ea23d8a33..340abb23e 100644
--- a/common/src/com/android/inputmethod/latin/common/ResizableIntArray.java
+++ b/common/src/com/android/inputmethod/latin/common/ResizableIntArray.java
@@ -18,8 +18,11 @@ package com.android.inputmethod.latin.common;
import java.util.Arrays;
+import javax.annotation.Nonnull;
+
// TODO: This class is not thread-safe.
public final class ResizableIntArray {
+ @Nonnull
private int[] mArray;
private int mLength;
@@ -89,17 +92,18 @@ public final class ResizableIntArray {
mLength = 0;
}
+ @Nonnull
public int[] getPrimitiveArray() {
return mArray;
}
- public void set(final ResizableIntArray ip) {
+ public void set(@Nonnull final ResizableIntArray ip) {
// TODO: Implement primitive array pool.
mArray = ip.mArray;
mLength = ip.mLength;
}
- public void copy(final ResizableIntArray ip) {
+ public void copy(@Nonnull final ResizableIntArray ip) {
final int newCapacity = calculateCapacity(ip.mLength);
if (newCapacity > 0) {
// TODO: Implement primitive array pool.
@@ -109,7 +113,7 @@ public final class ResizableIntArray {
mLength = ip.mLength;
}
- public void append(final ResizableIntArray src, final int startPos, final int length) {
+ public void append(@Nonnull final ResizableIntArray src, final int startPos, final int length) {
if (length == 0) {
return;
}
diff --git a/dictionaries/de_wordlist.combined.gz b/dictionaries/de_wordlist.combined.gz
index 803211c01..92c95540c 100644
--- a/dictionaries/de_wordlist.combined.gz
+++ b/dictionaries/de_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/en_GB_wordlist.combined.gz b/dictionaries/en_GB_wordlist.combined.gz
index 1fa9b85ea..217660fc4 100644
--- a/dictionaries/en_GB_wordlist.combined.gz
+++ b/dictionaries/en_GB_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/en_US_wordlist.combined.gz b/dictionaries/en_US_wordlist.combined.gz
index 2e039ff05..8aed9c5e0 100644
--- a/dictionaries/en_US_wordlist.combined.gz
+++ b/dictionaries/en_US_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/en_wordlist.combined.gz b/dictionaries/en_wordlist.combined.gz
index e845346d6..7fe6618cf 100644
--- a/dictionaries/en_wordlist.combined.gz
+++ b/dictionaries/en_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/es_wordlist.combined.gz b/dictionaries/es_wordlist.combined.gz
index 3391e64b4..71e7309fc 100644
--- a/dictionaries/es_wordlist.combined.gz
+++ b/dictionaries/es_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/fr_wordlist.combined.gz b/dictionaries/fr_wordlist.combined.gz
index 1b9fd73f9..afe44a6d9 100644
--- a/dictionaries/fr_wordlist.combined.gz
+++ b/dictionaries/fr_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/it_wordlist.combined.gz b/dictionaries/it_wordlist.combined.gz
index 5a5cbdc7a..ed58a12c5 100644
--- a/dictionaries/it_wordlist.combined.gz
+++ b/dictionaries/it_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/nl_wordlist.combined.gz b/dictionaries/nl_wordlist.combined.gz
index 37ba8ab42..19c3a7ea8 100644
--- a/dictionaries/nl_wordlist.combined.gz
+++ b/dictionaries/nl_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/pl_wordlist.combined.gz b/dictionaries/pl_wordlist.combined.gz
index ba71a5581..2b84eecfd 100644
--- a/dictionaries/pl_wordlist.combined.gz
+++ b/dictionaries/pl_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/pt_BR_wordlist.combined.gz b/dictionaries/pt_BR_wordlist.combined.gz
index 02df1c1ee..7aac61e50 100644
--- a/dictionaries/pt_BR_wordlist.combined.gz
+++ b/dictionaries/pt_BR_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/pt_PT_wordlist.combined.gz b/dictionaries/pt_PT_wordlist.combined.gz
index bcd50ab03..5bf9a60e8 100644
--- a/dictionaries/pt_PT_wordlist.combined.gz
+++ b/dictionaries/pt_PT_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/ru_wordlist.combined.gz b/dictionaries/ru_wordlist.combined.gz
index 401ad08b0..5e9266221 100644
--- a/dictionaries/ru_wordlist.combined.gz
+++ b/dictionaries/ru_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/sv_wordlist.combined.gz b/dictionaries/sv_wordlist.combined.gz
index b6ebab320..db44ae4c4 100644
--- a/dictionaries/sv_wordlist.combined.gz
+++ b/dictionaries/sv_wordlist.combined.gz
Binary files differ
diff --git a/dictionaries/tr_wordlist.combined.gz b/dictionaries/tr_wordlist.combined.gz
index 306cea184..d3c8825b9 100644
--- a/dictionaries/tr_wordlist.combined.gz
+++ b/dictionaries/tr_wordlist.combined.gz
Binary files differ
diff --git a/java-overridable/src/com/android/inputmethod/latin/define/ProductionFlags.java b/java-overridable/src/com/android/inputmethod/latin/define/ProductionFlags.java
index f80625644..43675682d 100644
--- a/java-overridable/src/com/android/inputmethod/latin/define/ProductionFlags.java
+++ b/java-overridable/src/com/android/inputmethod/latin/define/ProductionFlags.java
@@ -48,4 +48,10 @@ public final class ProductionFlags {
* When {@code true}, personal dictionary sync feature is ready to be enabled.
*/
public static final boolean ENABLE_PERSONAL_DICTIONARY_SYNC = ENABLE_ACCOUNT_SIGN_IN && false;
+
+ /**
+ * When {@code true}, the IME maintains per account {@link UserHistoryDictionary}.
+ */
+ public static final boolean ENABLE_PER_ACCOUNT_USER_HISTORY_DICTIONARY =
+ ENABLE_ACCOUNT_SIGN_IN && false;
}
diff --git a/java-overridable/src/com/android/inputmethod/latin/settings/AdditionalFeaturesSettingUtils.java b/java-overridable/src/com/android/inputmethod/latin/settings/AdditionalFeaturesSettingUtils.java
index bd54238f8..4e8a10b1f 100644
--- a/java-overridable/src/com/android/inputmethod/latin/settings/AdditionalFeaturesSettingUtils.java
+++ b/java-overridable/src/com/android/inputmethod/latin/settings/AdditionalFeaturesSettingUtils.java
@@ -47,6 +47,7 @@ public class AdditionalFeaturesSettingUtils {
// do nothing.
}
+ @Nonnull
public static RichInputMethodSubtype createRichInputMethodSubtype(
@Nonnull final RichInputMethodManager imm,
@Nonnull final InputMethodSubtype subtype,
diff --git a/java/Android.mk b/java/Android.mk
index a2c5697d3..b84b3471e 100644
--- a/java/Android.mk
+++ b/java/Android.mk
@@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-LOCAL_PATH:= $(call my-dir)
+LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
diff --git a/java/AndroidManifest.xml b/java/AndroidManifest.xml
index ee1cef6b5..88e867f09 100644
--- a/java/AndroidManifest.xml
+++ b/java/AndroidManifest.xml
@@ -37,7 +37,6 @@
<application android:label="@string/english_ime_name"
android:icon="@drawable/ic_launcher_keyboard"
- android:killAfterRestore="false"
android:supportsRtl="true"
android:allowBackup="true">
diff --git a/java/res/drawable/btn_keyboard_key_ics.xml b/java/res/drawable/btn_keyboard_key_ics.xml
index 0bb098d23..bacd5d78f 100644
--- a/java/res/drawable/btn_keyboard_key_ics.xml
+++ b/java/res/drawable/btn_keyboard_key_ics.xml
@@ -32,6 +32,8 @@
android:drawable="@drawable/btn_keyboard_key_normal_off_holo_dark" />
<!-- Empty background keys. -->
+ <item android:state_empty="true" android:state_pressed="true"
+ android:drawable="@drawable/btn_keyboard_key_pressed_ics_light" />
<item android:state_empty="true"
android:drawable="@android:color/transparent" />
diff --git a/java/res/drawable/btn_keyboard_key_klp.xml b/java/res/drawable/btn_keyboard_key_klp.xml
index 2a202a179..e2f208585 100644
--- a/java/res/drawable/btn_keyboard_key_klp.xml
+++ b/java/res/drawable/btn_keyboard_key_klp.xml
@@ -32,6 +32,8 @@
android:drawable="@drawable/btn_keyboard_key_normal_off_holo_dark" />
<!-- Empty background keys. -->
+ <item android:state_empty="true" android:state_pressed="true"
+ android:drawable="@drawable/btn_keyboard_key_pressed_klp_light" />
<item android:state_empty="true"
android:drawable="@android:color/transparent" />
diff --git a/java/res/drawable/btn_keyboard_key_lxx_dark.xml b/java/res/drawable/btn_keyboard_key_lxx_dark.xml
index bb1789ae3..161592d3d 100644
--- a/java/res/drawable/btn_keyboard_key_lxx_dark.xml
+++ b/java/res/drawable/btn_keyboard_key_lxx_dark.xml
@@ -32,8 +32,10 @@
android:drawable="@drawable/btn_keyboard_key_normal_off_lxx_dark" />
<!-- Empty background keys. -->
+ <item android:state_empty="true" android:state_pressed="true"
+ android:drawable="@color/key_background_pressed_lxx_dark" />
<item android:state_empty="true"
- android:drawable="@color/key_background_lxx_dark" />
+ android:drawable="@android:color/transparent" />
<!-- Normal keys. -->
<item android:state_pressed="true"
diff --git a/java/res/drawable/btn_keyboard_key_lxx_light.xml b/java/res/drawable/btn_keyboard_key_lxx_light.xml
index 60fe02dd2..0154d75b4 100644
--- a/java/res/drawable/btn_keyboard_key_lxx_light.xml
+++ b/java/res/drawable/btn_keyboard_key_lxx_light.xml
@@ -32,8 +32,10 @@
android:drawable="@drawable/btn_keyboard_key_normal_off_lxx_light" />
<!-- Empty background keys. -->
+ <item android:state_empty="true" android:state_pressed="true"
+ android:drawable="@color/key_background_pressed_lxx_light" />
<item android:state_empty="true"
- android:drawable="@color/key_background_lxx_light" />
+ android:drawable="@android:color/transparent" />
<!-- Normal keys. -->
<item android:state_pressed="true"
diff --git a/java/res/raw/main_de.dict b/java/res/raw/main_de.dict
index 45b288375..c3c2cbe46 100644
--- a/java/res/raw/main_de.dict
+++ b/java/res/raw/main_de.dict
Binary files differ
diff --git a/java/res/raw/main_en.dict b/java/res/raw/main_en.dict
index 5bbb85761..b9e5bc77b 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_es.dict b/java/res/raw/main_es.dict
index fae131850..076d5aa8f 100644
--- a/java/res/raw/main_es.dict
+++ b/java/res/raw/main_es.dict
Binary files differ
diff --git a/java/res/raw/main_fr.dict b/java/res/raw/main_fr.dict
index 19532d9bf..0e8686092 100644
--- a/java/res/raw/main_fr.dict
+++ b/java/res/raw/main_fr.dict
Binary files differ
diff --git a/java/res/raw/main_it.dict b/java/res/raw/main_it.dict
index ff11b9798..609ef13b7 100644
--- a/java/res/raw/main_it.dict
+++ b/java/res/raw/main_it.dict
Binary files differ
diff --git a/java/res/raw/main_pt_br.dict b/java/res/raw/main_pt_br.dict
index 9fa50442a..c33865187 100644
--- a/java/res/raw/main_pt_br.dict
+++ b/java/res/raw/main_pt_br.dict
Binary files differ
diff --git a/java/res/raw/main_ru.dict b/java/res/raw/main_ru.dict
index 76b5f805a..d0af70730 100644
--- a/java/res/raw/main_ru.dict
+++ b/java/res/raw/main_ru.dict
Binary files differ
diff --git a/java/res/values-af/strings-emoji-descriptions.xml b/java/res/values-af/strings-emoji-descriptions.xml
index 26bfc5f08..5ed066a87 100644
--- a/java/res/values-af/strings-emoji-descriptions.xml
+++ b/java/res/values-af/strings-emoji-descriptions.xml
@@ -267,7 +267,7 @@
<string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Koekie"</string>
<string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Staaf sjokolade"</string>
<string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Lekkergoed"</string>
- <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Suiglekker"</string>
+ <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string>
<string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Vla"</string>
<string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Heuningpot"</string>
<string name="spoken_emoji_1F370" msgid="7243244547866114951">"Broskoek"</string>
diff --git a/java/res/values-af/strings.xml b/java/res/values-af/strings.xml
index ca8b816f4..f8be8f1d3 100644
--- a/java/res/values-af/strings.xml
+++ b/java/res/values-af/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serwies (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Tradisioneel)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Kompak)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"Geen taal nie (alfabet)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabet (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabet (QWERTZ)"</string>
diff --git a/java/res/values-ar/strings-emoji-descriptions.xml b/java/res/values-ar/strings-emoji-descriptions.xml
index 875f626c2..c1decd303 100644
--- a/java/res/values-ar/strings-emoji-descriptions.xml
+++ b/java/res/values-ar/strings-emoji-descriptions.xml
@@ -267,7 +267,7 @@
<string name="spoken_emoji_1F36A" msgid="2726271795913042295">"كعكة"</string>
<string name="spoken_emoji_1F36B" msgid="6342163604299875931">"بار شيكولاتة"</string>
<string name="spoken_emoji_1F36C" msgid="2168934753998218790">"حلوى"</string>
- <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"مصاصة"</string>
+ <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string>
<string name="spoken_emoji_1F36E" msgid="4630541402785165902">"كاسترد"</string>
<string name="spoken_emoji_1F36F" msgid="5577915387425169439">"جرة عسل"</string>
<string name="spoken_emoji_1F370" msgid="7243244547866114951">"كعكة بسمن وسكر"</string>
diff --git a/java/res/values-ar/strings.xml b/java/res/values-ar/strings.xml
index 62d5f3dbd..da414a66d 100644
--- a/java/res/values-ar/strings.xml
+++ b/java/res/values-ar/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"هنجليزية (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"الصربية (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (التقليدية)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (مكثفة)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"بدون لغة (أبجدية)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"‏الأبجدية (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"‏الأبجدية (QWERTZ)"</string>
diff --git a/java/res/values-az-rAZ/strings.xml b/java/res/values-az-rAZ/strings.xml
index b13c90536..b1a192ce5 100644
--- a/java/res/values-az-rAZ/strings.xml
+++ b/java/res/values-az-rAZ/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hingilis (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serb (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Ənənəvi)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Kompakt)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"Dil yoxdur (Əlifba)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"Əlifba (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"Əlifba (QWERTZ)"</string>
diff --git a/java/res/values-bg/strings.xml b/java/res/values-bg/strings.xml
index 805014b05..5d7e6b4b1 100644
--- a/java/res/values-bg/strings.xml
+++ b/java/res/values-bg/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Хинглиш (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Сръбска (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (традиционна клавиатура)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (компактна)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"Без език (латиница)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"Латиница (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"Латиница (QWERTZ)"</string>
diff --git a/java/res/values-bn-rBD/strings.xml b/java/res/values-bn-rBD/strings.xml
index 5487bb5f2..8d77be4ea 100644
--- a/java/res/values-bn-rBD/strings.xml
+++ b/java/res/values-bn-rBD/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"হিংলিশ (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"সার্বিয়ান (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ঐতিহ্যবাহি)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (কম্প্যাক্ট)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"কোনো ভাষা নয় (বর্ণমালা)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"বর্ণমালা (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"বর্ণমালা (QWERTZ)"</string>
diff --git a/java/res/values-ca/strings.xml b/java/res/values-ca/strings.xml
index ef792b8d0..3f0e447f9 100644
--- a/java/res/values-ca/strings.xml
+++ b/java/res/values-ca/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbi (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradicional)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (compacte)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"Cap idioma (alfabet)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabet (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabet (QWERTZ)"</string>
diff --git a/java/res/values-cs/strings-emoji-descriptions.xml b/java/res/values-cs/strings-emoji-descriptions.xml
index b66420247..b7f359c55 100644
--- a/java/res/values-cs/strings-emoji-descriptions.xml
+++ b/java/res/values-cs/strings-emoji-descriptions.xml
@@ -267,7 +267,7 @@
<string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Sušenka"</string>
<string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Tabulka čokolády"</string>
<string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Bonbon"</string>
- <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lízátko"</string>
+ <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string>
<string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Pudink"</string>
<string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Hrnek medu"</string>
<string name="spoken_emoji_1F370" msgid="7243244547866114951">"Dort"</string>
diff --git a/java/res/values-da/strings.xml b/java/res/values-da/strings.xml
index 0e92e4711..39c51e9ea 100644
--- a/java/res/values-da/strings.xml
+++ b/java/res/values-da/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbisk (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (traditionelt)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (kompakt)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"Intet sprog (Alfabet)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabet (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabet (QWERTZ)"</string>
diff --git a/java/res/values-el/strings-emoji-descriptions.xml b/java/res/values-el/strings-emoji-descriptions.xml
index aa8966e97..a15d0fdba 100644
--- a/java/res/values-el/strings-emoji-descriptions.xml
+++ b/java/res/values-el/strings-emoji-descriptions.xml
@@ -269,7 +269,7 @@
<string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Κούκι"</string>
<string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Σοκολάτα"</string>
<string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Γλυκά"</string>
- <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Γλειφιτζούρι"</string>
+ <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string>
<string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Κρέμα"</string>
<string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Βάζο με μέλι"</string>
<string name="spoken_emoji_1F370" msgid="7243244547866114951">"Μπισκότο"</string>
diff --git a/java/res/values-en-rGB/strings.xml b/java/res/values-en-rGB/strings.xml
index 3ed282651..1778cded3 100644
--- a/java/res/values-en-rGB/strings.xml
+++ b/java/res/values-en-rGB/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbian (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Traditional)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Compact)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"No language (Alphabet)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alphabet (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alphabet (QWERTZ)"</string>
diff --git a/java/res/values-en-rIN/strings.xml b/java/res/values-en-rIN/strings.xml
index 3ed282651..1778cded3 100644
--- a/java/res/values-en-rIN/strings.xml
+++ b/java/res/values-en-rIN/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbian (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Traditional)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Compact)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"No language (Alphabet)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alphabet (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alphabet (QWERTZ)"</string>
diff --git a/java/res/values-es-rUS/strings.xml b/java/res/values-es-rUS/strings.xml
index 8142b1d0b..7b9e3ba66 100644
--- a/java/res/values-es-rUS/strings.xml
+++ b/java/res/values-es-rUS/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbio (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradicional)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (compacto)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"Ningún idioma (alfabeto)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabeto (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabeto (QWERTZ)"</string>
diff --git a/java/res/values-eu-rES/strings-emoji-descriptions.xml b/java/res/values-eu-rES/strings-emoji-descriptions.xml
index 2faec968a..c774ae1b8 100644
--- a/java/res/values-eu-rES/strings-emoji-descriptions.xml
+++ b/java/res/values-eu-rES/strings-emoji-descriptions.xml
@@ -846,6 +846,6 @@
<string name="spoken_emoji_1F6C1" msgid="2845056048320031158">"Bainuontzia"</string>
<string name="spoken_emoji_1F6C2" msgid="8117262514698011877">"Pasaporte-kontrola"</string>
<string name="spoken_emoji_1F6C3" msgid="1176342001834630675">"Aduana"</string>
- <string name="spoken_emoji_1F6C4" msgid="1477622834179978886">"Maleta-erreklamazioa"</string>
+ <string name="spoken_emoji_1F6C4" msgid="1477622834179978886">"Ekipaje-erreklamazioa"</string>
<string name="spoken_emoji_1F6C5" msgid="2495834050856617451">"Ahaztutako maletak"</string>
</resources>
diff --git a/java/res/values-eu-rES/strings.xml b/java/res/values-eu-rES/strings.xml
index 9496d95e9..23d15930c 100644
--- a/java/res/values-eu-rES/strings.xml
+++ b/java/res/values-eu-rES/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglisha (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbiarra (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradizionala)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (trinkoa)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"Ez dago hizkuntzarik (alfabetoa)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabetoa (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabetoa (QWERTZ)"</string>
diff --git a/java/res/values-fa/strings-emoji-descriptions.xml b/java/res/values-fa/strings-emoji-descriptions.xml
index cc670eef9..8adb530be 100644
--- a/java/res/values-fa/strings-emoji-descriptions.xml
+++ b/java/res/values-fa/strings-emoji-descriptions.xml
@@ -267,7 +267,7 @@
<string name="spoken_emoji_1F36A" msgid="2726271795913042295">"کلوچه"</string>
<string name="spoken_emoji_1F36B" msgid="6342163604299875931">"تخته شکلات"</string>
<string name="spoken_emoji_1F36C" msgid="2168934753998218790">"آبنبات"</string>
- <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"آبنبات چوبی"</string>
+ <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"آب‌نبات چوبی"</string>
<string name="spoken_emoji_1F36E" msgid="4630541402785165902">"کاستارد"</string>
<string name="spoken_emoji_1F36F" msgid="5577915387425169439">"ظرف عسل"</string>
<string name="spoken_emoji_1F370" msgid="7243244547866114951">"کیک روغنی"</string>
diff --git a/java/res/values-fa/strings.xml b/java/res/values-fa/strings.xml
index a0390dfdb..16a6f8767 100644
--- a/java/res/values-fa/strings.xml
+++ b/java/res/values-fa/strings.xml
@@ -84,7 +84,7 @@
<string name="hint_add_to_dictionary_without_word" msgid="3040385779511255101">"برای ذخیره اینجا را لمس کنید"</string>
<string name="has_dictionary" msgid="6071847973466625007">"دیکشنری موجود است"</string>
<string name="keyboard_layout" msgid="8451164783510487501">"طرح زمینه صفحه‌کلید"</string>
- <string name="switch_accounts" msgid="3321216593719006162">"جابجایی بین حساب‌ها"</string>
+ <string name="switch_accounts" msgid="3321216593719006162">"جابه‌جایی بین حساب‌ها"</string>
<string name="no_accounts_selected" msgid="2073821619103904330">"هیچ حسابی انتخاب نشده است"</string>
<string name="account_selected" msgid="2846876462199625974">"در حال حاضر در حال استفاده از <xliff:g id="EMAIL_ADDRESS">%1$s</xliff:g>"</string>
<string name="account_select_ok" msgid="9141195141763227797">"تأیید"</string>
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"هندی انگلیسی (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"صربی (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (سنتی)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (فشرده)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"بدون زبان (حروف الفبا)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"‏حروف الفبا (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"‏حروف الفبا (QWERTZ)"</string>
diff --git a/java/res/values-fi/strings-emoji-descriptions.xml b/java/res/values-fi/strings-emoji-descriptions.xml
index ad08bbda2..7d6a9f957 100644
--- a/java/res/values-fi/strings-emoji-descriptions.xml
+++ b/java/res/values-fi/strings-emoji-descriptions.xml
@@ -267,7 +267,7 @@
<string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Pikkuleipä"</string>
<string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Suklaapatukka"</string>
<string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Karamelli"</string>
- <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Tikkukaramelli"</string>
+ <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Tikkari"</string>
<string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Vanukas"</string>
<string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Hunajapurkki"</string>
<string name="spoken_emoji_1F370" msgid="7243244547866114951">"Kakkuviipale"</string>
diff --git a/java/res/values-fi/strings.xml b/java/res/values-fi/strings.xml
index b133cc9d9..0308d18d7 100644
--- a/java/res/values-fi/strings.xml
+++ b/java/res/values-fi/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hindienglanti (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"serbialainen (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (perinteinen)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tiivis)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"Ei kieltä (aakkoset)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"Aakkoset (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"Aakkoset (QWERTZ)"</string>
diff --git a/java/res/values-fr-rCA/strings.xml b/java/res/values-fr-rCA/strings.xml
index ee522059c..6730a7982 100644
--- a/java/res/values-fr-rCA/strings.xml
+++ b/java/res/values-fr-rCA/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbe (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (traditionnel)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (compact)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"Aucune langue (alphabet)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alphabet latin (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alphabet latin (QWERTZ)"</string>
diff --git a/java/res/values-fr/strings.xml b/java/res/values-fr/strings.xml
index 2a93c992d..3349f4a6a 100644
--- a/java/res/values-fr/strings.xml
+++ b/java/res/values-fr/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hindi/Anglais (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbe (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (traditionnel)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Compact)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"Aucune langue (latin)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alphabet latin (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alphabet latin (QWERTZ)"</string>
diff --git a/java/res/values-gl-rES/strings.xml b/java/res/values-gl-rES/strings.xml
index d266ad5a6..d72bcb8b1 100644
--- a/java/res/values-gl-rES/strings.xml
+++ b/java/res/values-gl-rES/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbio (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradicional)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (compacto)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"Ningún idioma (alfabeto)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabeto (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabeto (QWERTZ)"</string>
diff --git a/java/res/values-hi/strings.xml b/java/res/values-hi/strings.xml
index 9fc08ac8e..a4f2dd39d 100644
--- a/java/res/values-hi/strings.xml
+++ b/java/res/values-hi/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"हिंग्लिश (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"सर्बियाई (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (पारंपरिक)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (संक्षिप्त)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"भाषा उपलब्ध नहीं है (लैटिन वर्णाक्षर)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"वर्णाक्षर (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"वर्णाक्षर (QWERTZ)"</string>
diff --git a/java/res/values-hr/strings.xml b/java/res/values-hr/strings.xml
index 7a5a665b3..e1893714e 100644
--- a/java/res/values-hr/strings.xml
+++ b/java/res/values-hr/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Srpski (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradicionalni)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (kompaktna)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"Nema jezika (abeceda)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"Abeceda (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"Abeceda (QWERTZ)"</string>
diff --git a/java/res/values-hu/strings-emoji-descriptions.xml b/java/res/values-hu/strings-emoji-descriptions.xml
index 4f3d01c86..b72f29853 100644
--- a/java/res/values-hu/strings-emoji-descriptions.xml
+++ b/java/res/values-hu/strings-emoji-descriptions.xml
@@ -267,7 +267,7 @@
<string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Sütemény"</string>
<string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Csokoládé"</string>
<string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Cukorka"</string>
- <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Nyalóka"</string>
+ <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string>
<string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Sodó"</string>
<string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Mézesbödön"</string>
<string name="spoken_emoji_1F370" msgid="7243244547866114951">"Tortaszelet"</string>
diff --git a/java/res/values-hy-rAM/strings-emoji-descriptions.xml b/java/res/values-hy-rAM/strings-emoji-descriptions.xml
index f41f2fc96..dcc718e22 100644
--- a/java/res/values-hy-rAM/strings-emoji-descriptions.xml
+++ b/java/res/values-hy-rAM/strings-emoji-descriptions.xml
@@ -267,7 +267,7 @@
<string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Թխվածքաբլիթ"</string>
<string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Շոկոլադի սալիկ"</string>
<string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Կոնֆետ"</string>
- <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Սառնաշաքար կոնֆետ"</string>
+ <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Շաքարաքլոր"</string>
<string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Եփովի կրեմ"</string>
<string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Մեղրանոթ"</string>
<string name="spoken_emoji_1F370" msgid="7243244547866114951">"Տորթի կտոր"</string>
diff --git a/java/res/values-hy-rAM/strings.xml b/java/res/values-hy-rAM/strings.xml
index 7d301195a..8d7c5c8c1 100644
--- a/java/res/values-hy-rAM/strings.xml
+++ b/java/res/values-hy-rAM/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Հինգլիշ (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Սերբերեն (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ավանդական)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (սեղմ)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"Ոչ մի լեզվով (Այբուբեն)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"Այբուբեն (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"Այբուբեն (QWERTZ)"</string>
diff --git a/java/res/values-is-rIS/strings.xml b/java/res/values-is-rIS/strings.xml
index 2bc3bfbeb..8a6927af1 100644
--- a/java/res/values-is-rIS/strings.xml
+++ b/java/res/values-is-rIS/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbneskt (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (hefðbundið)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (lítið)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"Ekkert tungumál (stafróf)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"Stafróf (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"Stafróf (QWERTZ)"</string>
diff --git a/java/res/values-iw/strings-emoji-descriptions.xml b/java/res/values-iw/strings-emoji-descriptions.xml
index ab31403aa..fc4435a14 100644
--- a/java/res/values-iw/strings-emoji-descriptions.xml
+++ b/java/res/values-iw/strings-emoji-descriptions.xml
@@ -267,7 +267,7 @@
<string name="spoken_emoji_1F36A" msgid="2726271795913042295">"עוגייה"</string>
<string name="spoken_emoji_1F36B" msgid="6342163604299875931">"חפיסת שוקולד"</string>
<string name="spoken_emoji_1F36C" msgid="2168934753998218790">"סוכרייה"</string>
- <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"סוכרייה על מקל"</string>
+ <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string>
<string name="spoken_emoji_1F36E" msgid="4630541402785165902">"רפרפת"</string>
<string name="spoken_emoji_1F36F" msgid="5577915387425169439">"סיר דבש"</string>
<string name="spoken_emoji_1F370" msgid="7243244547866114951">"עוגת פירות"</string>
diff --git a/java/res/values-ja/strings.xml b/java/res/values-ja/strings.xml
index 4caaae394..53ef5779f 100644
--- a/java/res/values-ja/strings.xml
+++ b/java/res/values-ja/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"ヒングリッシュ(<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"セルビア語(<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g>(伝統言語)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g>(コンパクト)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"言語なし(アルファベット)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"アルファベット(QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"アルファベット(QWERTZ)"</string>
diff --git a/java/res/values-ka-rGE/strings.xml b/java/res/values-ka-rGE/strings.xml
index dc2308c2e..d4457a508 100644
--- a/java/res/values-ka-rGE/strings.xml
+++ b/java/res/values-ka-rGE/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"ჰინგლისური (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"სერბული (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ტრადიციული)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (კომპაქტური)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"ენის გარეშე (ანბანი)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"ანბანი (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"ანბანი (QWERTZ)"</string>
diff --git a/java/res/values-kk-rKZ/strings.xml b/java/res/values-kk-rKZ/strings.xml
index 838a07a77..58f8f306e 100644
--- a/java/res/values-kk-rKZ/strings.xml
+++ b/java/res/values-kk-rKZ/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Хинглиш (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Серб (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (дәстүрлі)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (шағын)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"Тіл жоқ (әліпби)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"Әліпби (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"Әліпби (QWERTZ)"</string>
diff --git a/java/res/values-km-rKH/strings-emoji-descriptions.xml b/java/res/values-km-rKH/strings-emoji-descriptions.xml
index 757df50e7..e9b8780a5 100644
--- a/java/res/values-km-rKH/strings-emoji-descriptions.xml
+++ b/java/res/values-km-rKH/strings-emoji-descriptions.xml
@@ -267,7 +267,7 @@
<string name="spoken_emoji_1F36A" msgid="2726271795913042295">"ខូគី"</string>
<string name="spoken_emoji_1F36B" msgid="6342163604299875931">"​​សូកូឡា"</string>
<string name="spoken_emoji_1F36C" msgid="2168934753998218790">"ស្ករគ្រាប់"</string>
- <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"ស្ករ​គ្រាប់​មាន​ដង​​កាន់"</string>
+ <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string>
<string name="spoken_emoji_1F36E" msgid="4630541402785165902">"សង់ខ្យា"</string>
<string name="spoken_emoji_1F36F" msgid="5577915387425169439">"ថូ"</string>
<string name="spoken_emoji_1F370" msgid="7243244547866114951">"នំ​ខេក"</string>
diff --git a/java/res/values-kn-rIN/strings-emoji-descriptions.xml b/java/res/values-kn-rIN/strings-emoji-descriptions.xml
index 4e6d5ced5..a013c2755 100644
--- a/java/res/values-kn-rIN/strings-emoji-descriptions.xml
+++ b/java/res/values-kn-rIN/strings-emoji-descriptions.xml
@@ -267,7 +267,7 @@
<string name="spoken_emoji_1F36A" msgid="2726271795913042295">"ಕುಕಿ"</string>
<string name="spoken_emoji_1F36B" msgid="6342163604299875931">"ಚಾಕೊಲೇಟ್ ಬಾರ್"</string>
<string name="spoken_emoji_1F36C" msgid="2168934753998218790">"ಕ್ಯಾಂಡಿ"</string>
- <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"ಲಾಲಿಪಪ್"</string>
+ <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string>
<string name="spoken_emoji_1F36E" msgid="4630541402785165902">"ಕಸ್ಟರ್ಡ್"</string>
<string name="spoken_emoji_1F36F" msgid="5577915387425169439">"ಜೇನಿನ ಮಡಕೆ"</string>
<string name="spoken_emoji_1F370" msgid="7243244547866114951">"ಶಾರ್ಟ್‌ಕೇಕ್"</string>
diff --git a/java/res/values-ko/strings.xml b/java/res/values-ko/strings.xml
index e3935174e..68195f0d1 100644
--- a/java/res/values-ko/strings.xml
+++ b/java/res/values-ko/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"인도 영어(<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"세르비아어(<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g>(번체)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g>(컴팩트)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"언어 없음(알파벳)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"알파벳(QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"알파벳(QWERTZ)"</string>
diff --git a/java/res/values-ky-rKG/strings.xml b/java/res/values-ky-rKG/strings.xml
index 90c2db3b1..6b46785e3 100644
--- a/java/res/values-ky-rKG/strings.xml
+++ b/java/res/values-ky-rKG/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Хинглиш (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Сербче (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Салттык)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Чакан)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"Тил жок (Алфавит)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"Алфавит (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"Алфавит (QWERTZ)"</string>
diff --git a/java/res/values-lo-rLA/strings-emoji-descriptions.xml b/java/res/values-lo-rLA/strings-emoji-descriptions.xml
index 0747fa629..84b9d0502 100644
--- a/java/res/values-lo-rLA/strings-emoji-descriptions.xml
+++ b/java/res/values-lo-rLA/strings-emoji-descriptions.xml
@@ -267,7 +267,7 @@
<string name="spoken_emoji_1F36A" msgid="2726271795913042295">"​ຄຸ​ກ​ກີ້"</string>
<string name="spoken_emoji_1F36B" msgid="6342163604299875931">"​ຊັອກ​ໂກ​ແລັດບາ"</string>
<string name="spoken_emoji_1F36C" msgid="2168934753998218790">"ແຄນດີ້"</string>
- <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"​ໂລ​ລິ​ປັອບ"</string>
+ <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string>
<string name="spoken_emoji_1F36E" msgid="4630541402785165902">"​ຄັ​ສ​ຕາດ"</string>
<string name="spoken_emoji_1F36F" msgid="5577915387425169439">"ໝໍ້​ນ້ຳ​ເຜິ້ງ"</string>
<string name="spoken_emoji_1F370" msgid="7243244547866114951">"ຊັອດ​ເຄັກ"</string>
diff --git a/java/res/values-lo-rLA/strings.xml b/java/res/values-lo-rLA/strings.xml
index b23eb31b7..efc09bebc 100644
--- a/java/res/values-lo-rLA/strings.xml
+++ b/java/res/values-lo-rLA/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"ຮິງ​ລິສ (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"ເຊີ​ບຽນ (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ດັ້ງ​ເດີມ)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ກະ​ທັດ​ຮັດ)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"ບໍ່ມີພາສາ (ໂຕອັກສອນ)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"ໂຕອັກສອນ (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"ໂຕອັກສອນ (QWERTZ)"</string>
diff --git a/java/res/values-lt/strings-emoji-descriptions.xml b/java/res/values-lt/strings-emoji-descriptions.xml
index fa81dbbd8..afe9ac0e6 100644
--- a/java/res/values-lt/strings-emoji-descriptions.xml
+++ b/java/res/values-lt/strings-emoji-descriptions.xml
@@ -267,7 +267,7 @@
<string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Sausainis"</string>
<string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Šokolado plytelė"</string>
<string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Saldainis"</string>
- <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Ledinukas ant pagaliuko"</string>
+ <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string>
<string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Saldus kremas"</string>
<string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Medaus puodynė"</string>
<string name="spoken_emoji_1F370" msgid="7243244547866114951">"Trapus pyragas"</string>
diff --git a/java/res/values-lv/strings-emoji-descriptions.xml b/java/res/values-lv/strings-emoji-descriptions.xml
index a51991b81..525a1a0e8 100644
--- a/java/res/values-lv/strings-emoji-descriptions.xml
+++ b/java/res/values-lv/strings-emoji-descriptions.xml
@@ -267,7 +267,7 @@
<string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Cepums"</string>
<string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Šokolādes tāfelīte"</string>
<string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Konfekte"</string>
- <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Cukurgailītis"</string>
+ <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string>
<string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Olu krēms"</string>
<string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Medus pods"</string>
<string name="spoken_emoji_1F370" msgid="7243244547866114951">"Smilšu torte ar augļu pildījumu"</string>
diff --git a/java/res/values-lv/strings.xml b/java/res/values-lv/strings.xml
index ed7e9d457..833839b8a 100644
--- a/java/res/values-lv/strings.xml
+++ b/java/res/values-lv/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hindi–angļu valoda (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbu (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradicionālā)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (kompaktā)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"Nav valodas (alfabēts)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabēts (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabēts (QWERTZ)"</string>
diff --git a/java/res/values-ml-rIN/strings.xml b/java/res/values-ml-rIN/strings.xml
index acf00c2b6..a34de64ac 100644
--- a/java/res/values-ml-rIN/strings.xml
+++ b/java/res/values-ml-rIN/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"ഹിംഗ്ലീഷ് (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"സെർബിയൻ (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (പരമ്പരാഗതം)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (കോം‌പാക്‌ട്)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"ഭാഷയില്ല (അക്ഷരമാല)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"അക്ഷരമാല (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"അക്ഷരമാല (QWERTZ)"</string>
diff --git a/java/res/values-mr-rIN/strings.xml b/java/res/values-mr-rIN/strings.xml
index 492226a8c..2c6746e35 100644
--- a/java/res/values-mr-rIN/strings.xml
+++ b/java/res/values-mr-rIN/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"हिंग्लिश (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"सर्बियन (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (पारंपारिक)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (संक्षिप्त)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"भाषा नाही (वर्णमाला)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"वर्णमाला (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"वर्णमाला (QWERTZ)"</string>
diff --git a/java/res/values-ms-rMY/strings.xml b/java/res/values-ms-rMY/strings.xml
index f33bdb4b3..3136c3bbd 100644
--- a/java/res/values-ms-rMY/strings.xml
+++ b/java/res/values-ms-rMY/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Bahasa Serbia (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Tradisional)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Sarat)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"Tiada bahasa (Abjad)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"Abjad (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"Abjad (QWERTZ)"</string>
diff --git a/java/res/values-my-rMM/strings.xml b/java/res/values-my-rMM/strings.xml
index 3e5a491ea..ab4037861 100644
--- a/java/res/values-my-rMM/strings.xml
+++ b/java/res/values-my-rMM/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"ဟင်ဂလိပ် (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"ဆားဘီယား (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ရိုးရာ)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ကျစ်လစ်သော)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"ဘာသာစကားမရှိ (ဗျည်းအက္ခရာ)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"ဗျည်းအက္ခရာ (ကွာတီ)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"ဗျည်းအက္ခရာ (ကွာတီ)"</string>
diff --git a/java/res/values-nb/strings-emoji-descriptions.xml b/java/res/values-nb/strings-emoji-descriptions.xml
index fa655f12f..681118833 100644
--- a/java/res/values-nb/strings-emoji-descriptions.xml
+++ b/java/res/values-nb/strings-emoji-descriptions.xml
@@ -267,7 +267,7 @@
<string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Kjeks"</string>
<string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Sjokoladeplate"</string>
<string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Godteri"</string>
- <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Kjærlighet på pinne"</string>
+ <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string>
<string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Pudding"</string>
<string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Honningkrukke"</string>
<string name="spoken_emoji_1F370" msgid="7243244547866114951">"Kakestykke"</string>
diff --git a/java/res/values-ne-rNP/strings-emoji-descriptions.xml b/java/res/values-ne-rNP/strings-emoji-descriptions.xml
index 43f4d892b..39e5bc075 100644
--- a/java/res/values-ne-rNP/strings-emoji-descriptions.xml
+++ b/java/res/values-ne-rNP/strings-emoji-descriptions.xml
@@ -267,7 +267,7 @@
<string name="spoken_emoji_1F36A" msgid="2726271795913042295">"कुकीज"</string>
<string name="spoken_emoji_1F36B" msgid="6342163604299875931">"चकलेट बार"</string>
<string name="spoken_emoji_1F36C" msgid="2168934753998218790">"क्यान्डी"</string>
- <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"लालीपप"</string>
+ <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string>
<string name="spoken_emoji_1F36E" msgid="4630541402785165902">"कस्तार्ड"</string>
<string name="spoken_emoji_1F36F" msgid="5577915387425169439">"महदानी"</string>
<string name="spoken_emoji_1F370" msgid="7243244547866114951">"सर्टकेक"</string>
diff --git a/java/res/values-ne-rNP/strings.xml b/java/res/values-ne-rNP/strings.xml
index ec17c4e6f..40c2d4f96 100644
--- a/java/res/values-ne-rNP/strings.xml
+++ b/java/res/values-ne-rNP/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"हिङ्लिस (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"सर्बियाई (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (परम्परागत)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (संकुचित)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"कुनै भाषा होइन (वर्णमाला)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"वर्णमाला (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"वर्णमाला (QWERTZ)"</string>
diff --git a/java/res/values-pt/strings.xml b/java/res/values-pt/strings.xml
index 2e0cd3b55..2cc86e12f 100644
--- a/java/res/values-pt/strings.xml
+++ b/java/res/values-pt/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Híndi-inglês (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Sérvio (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradicional)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (compacto)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"Nenhum idioma (alfabeto)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabeto (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabeto (QWERTZ)"</string>
diff --git a/java/res/values-ro/strings-emoji-descriptions.xml b/java/res/values-ro/strings-emoji-descriptions.xml
index f44a0b974..2ac84e5a0 100644
--- a/java/res/values-ro/strings-emoji-descriptions.xml
+++ b/java/res/values-ro/strings-emoji-descriptions.xml
@@ -267,7 +267,7 @@
<string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Biscuit"</string>
<string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Ciocolată"</string>
<string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Bomboane"</string>
- <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Acadea"</string>
+ <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string>
<string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Budincă"</string>
<string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Oală de miere"</string>
<string name="spoken_emoji_1F370" msgid="7243244547866114951">"Prăjitură"</string>
diff --git a/java/res/values-ro/strings.xml b/java/res/values-ro/strings.xml
index 8e3ebf2bf..03398733c 100644
--- a/java/res/values-ro/strings.xml
+++ b/java/res/values-ro/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Sârbă (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradițională)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Compact)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"Nicio limbă (alfabet)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabet (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabet (QWERTZ)"</string>
diff --git a/java/res/values-ru/strings.xml b/java/res/values-ru/strings.xml
index bd45c68a7..e5aaf8ba7 100644
--- a/java/res/values-ru/strings.xml
+++ b/java/res/values-ru/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Хинглиш (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Сербский (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (классическая)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (компактная раскладка)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"Язык не определен (латиница)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"Латиница (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"Латиница (QWERTZ)"</string>
diff --git a/java/res/values-si-rLK/strings.xml b/java/res/values-si-rLK/strings.xml
index 2e5f3d9f4..f8fa5e7c3 100644
--- a/java/res/values-si-rLK/strings.xml
+++ b/java/res/values-si-rLK/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"හින්ග්ලිෂ් (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"සර්බියානු (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (සාම්ප්‍රදායික)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (සංයුක්ත)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"භාෂාවක් නැත (අකාරාදිය)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"අකාරාදිය (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"අකාරාදිය (QWERTZ)"</string>
diff --git a/java/res/values-sk/strings.xml b/java/res/values-sk/strings.xml
index 80c91c89f..0d0e69fee 100644
--- a/java/res/values-sk/strings.xml
+++ b/java/res/values-sk/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"srbčina (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradičná)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (kompaktná)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"Žiadny jazyk (latinka)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"Latinka (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"Latinka (QWERTZ)"</string>
diff --git a/java/res/values-sl/strings.xml b/java/res/values-sl/strings.xml
index 396334cdb..e1f86c36c 100644
--- a/java/res/values-sl/strings.xml
+++ b/java/res/values-sl/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hindujska angleščina (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Srbščina (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (tradicionalna)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (kompaktna)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"Brez jezika (latinice)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"Latinica (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"Latinica (QWERTZ)"</string>
diff --git a/java/res/values-sr/strings.xml b/java/res/values-sr/strings.xml
index 28f6cae4e..f8c4ff601 100644
--- a/java/res/values-sr/strings.xml
+++ b/java/res/values-sr/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"хенглески (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"српски (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (традиционални)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (компактна)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"Нема језика (абецеда)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"Абецеда (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"Абецеда (QWERTZ)"</string>
diff --git a/java/res/values-sv/strings-emoji-descriptions.xml b/java/res/values-sv/strings-emoji-descriptions.xml
index 879de0b90..e49c73ce4 100644
--- a/java/res/values-sv/strings-emoji-descriptions.xml
+++ b/java/res/values-sv/strings-emoji-descriptions.xml
@@ -267,7 +267,7 @@
<string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Småkaka"</string>
<string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Chokladkaka"</string>
<string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Godis"</string>
- <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Klubba"</string>
+ <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string>
<string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Vaniljkräm"</string>
<string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Honungsburk"</string>
<string name="spoken_emoji_1F370" msgid="7243244547866114951">"Sockerkaka"</string>
diff --git a/java/res/values-sw/strings-emoji-descriptions.xml b/java/res/values-sw/strings-emoji-descriptions.xml
index 57163878e..329bf398a 100644
--- a/java/res/values-sw/strings-emoji-descriptions.xml
+++ b/java/res/values-sw/strings-emoji-descriptions.xml
@@ -267,7 +267,7 @@
<string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Biskuti"</string>
<string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Mchi wa chokoleti"</string>
<string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Peremende"</string>
- <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Peremende ya kijiti"</string>
+ <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string>
<string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Faluda au Kastadi"</string>
<string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Emoji ya chungu cha asali"</string>
<string name="spoken_emoji_1F370" msgid="7243244547866114951">"Keki tamu yenye vitandamlo inayofanana na biskuti"</string>
diff --git a/java/res/values-sw/strings.xml b/java/res/values-sw/strings.xml
index ae1a3a8f7..04c4f2bb7 100644
--- a/java/res/values-sw/strings.xml
+++ b/java/res/values-sw/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Kiserbia (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (cha Jadi)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Thabiti)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"Hakuna lugha (Alfabeti)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabeti (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabeti (QWERTZ)"</string>
diff --git a/java/res/values-ta-rIN/strings.xml b/java/res/values-ta-rIN/strings.xml
index 0a2d13890..64c573ba8 100644
--- a/java/res/values-ta-rIN/strings.xml
+++ b/java/res/values-ta-rIN/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"ஹிங்கிலிஷ் (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"செர்பியன் (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (பாரம்பரியமானது)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (வசதியான)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"மொழியில்லை (அகரவரிசை)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"அகரவரிசை (க்வெர்டி)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"அகரவரிசை (க்வெர்ட்ச்)"</string>
diff --git a/java/res/values-te-rIN/strings.xml b/java/res/values-te-rIN/strings.xml
index 15e9aaf38..0fb55b4b2 100644
--- a/java/res/values-te-rIN/strings.xml
+++ b/java/res/values-te-rIN/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"హింగ్లీష్ (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"సెర్బియన్ (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (సాంప్రదాయకం)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (కాంపాక్ట్)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"భాష లేదు (ఆల్ఫాబెట్)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"ఆల్ఫాబెట్ (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"ఆల్ఫాబెట్ (QWERTZ)"</string>
diff --git a/java/res/values-th/strings.xml b/java/res/values-th/strings.xml
index 5c239dbd4..fcc76d9b9 100644
--- a/java/res/values-th/strings.xml
+++ b/java/res/values-th/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"ภาษาอังกฤษผสมกับฮินดู (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"เซอร์เบีย (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ดั้งเดิม)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (แบบกะทัดรัด)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"ไม่มีภาษา (ตัวอักษรละติน)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"ตัวอักษร (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"ตัวอักษร (QWERTZ)"</string>
diff --git a/java/res/values-tl/strings.xml b/java/res/values-tl/strings.xml
index 8d755e42f..82617b889 100644
--- a/java/res/values-tl/strings.xml
+++ b/java/res/values-tl/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serbian (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Traditional)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Compact)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"Walang wika (Alpabeto)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alpabeto (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alpabeto (QWERTZ)"</string>
diff --git a/java/res/values-tr/strings.xml b/java/res/values-tr/strings.xml
index 86426fa1a..359e0d16f 100644
--- a/java/res/values-tr/strings.xml
+++ b/java/res/values-tr/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hingilizce (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Sırpça (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Geleneksel)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Kompakt)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"Dil yok (Alfabe)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"Alfabe (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"Alfabe (QWERTZ)"</string>
diff --git a/java/res/values-uk/strings.xml b/java/res/values-uk/strings.xml
index 33ab58497..dd625cb70 100644
--- a/java/res/values-uk/strings.xml
+++ b/java/res/values-uk/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Хінґліш (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"сербська (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (традиційна)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (компактна)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"Стандартна (латиниця)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"Латиниця (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"Латиниця (QWERTZ)"</string>
diff --git a/java/res/values-ur-rPK/strings.xml b/java/res/values-ur-rPK/strings.xml
index f00e503e0..4b1f03b03 100644
--- a/java/res/values-ur-rPK/strings.xml
+++ b/java/res/values-ur-rPK/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"ہنگلش (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"سربیائی (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (روایتی)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (کمپیکٹ)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"کوئی زبان نہیں (الفابیٹ)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"‏حروف تہجی (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"‏حروف تہجی (QWERTZ)"</string>
diff --git a/java/res/values-uz-rUZ/strings.xml b/java/res/values-uz-rUZ/strings.xml
index 62143d7af..59aba60f0 100644
--- a/java/res/values-uz-rUZ/strings.xml
+++ b/java/res/values-uz-rUZ/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Hinglish (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Serb (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (an’anaviy)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (ixcham)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"Til aniqlanmadi (lotin)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"Lotin (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"Lotin (QWERTZ)"</string>
diff --git a/java/res/values-vi/strings-emoji-descriptions.xml b/java/res/values-vi/strings-emoji-descriptions.xml
index 8b44dcfbf..492c726d1 100644
--- a/java/res/values-vi/strings-emoji-descriptions.xml
+++ b/java/res/values-vi/strings-emoji-descriptions.xml
@@ -267,7 +267,7 @@
<string name="spoken_emoji_1F36A" msgid="2726271795913042295">"Bánh quy"</string>
<string name="spoken_emoji_1F36B" msgid="6342163604299875931">"Thanh sôcôla"</string>
<string name="spoken_emoji_1F36C" msgid="2168934753998218790">"Kẹo"</string>
- <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Kẹo que"</string>
+ <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string>
<string name="spoken_emoji_1F36E" msgid="4630541402785165902">"Món sữa trứng"</string>
<string name="spoken_emoji_1F36F" msgid="5577915387425169439">"Mắt ong"</string>
<string name="spoken_emoji_1F370" msgid="7243244547866114951">"Bánh bơ giòn"</string>
diff --git a/java/res/values-vi/strings.xml b/java/res/values-vi/strings.xml
index fa0e11b51..ec0deb117 100644
--- a/java/res/values-vi/strings.xml
+++ b/java/res/values-vi/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"Tiếng Anh-Hindi (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"Tiếng Serbia (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Truyền thống)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (Viết tắt)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"Không ngôn ngữ nào (Bảng chữ cái)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"Bảng chữ cái (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"Bảng chữ cái (QWERTZ)"</string>
diff --git a/java/res/values-zh-rCN/strings.xml b/java/res/values-zh-rCN/strings.xml
index 4562c4bd1..52b0dac74 100644
--- a/java/res/values-zh-rCN/strings.xml
+++ b/java/res/values-zh-rCN/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"印地英语(<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"塞尔维亚语(<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>布局)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g>(传统)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g>(紧凑型)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"无语言(字母)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"字母 (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"字母 (QWERTZ)"</string>
diff --git a/java/res/values-zh-rHK/strings.xml b/java/res/values-zh-rHK/strings.xml
index e584a88fe..840f33387 100644
--- a/java/res/values-zh-rHK/strings.xml
+++ b/java/res/values-zh-rHK/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"印度英文 (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"塞爾維亞文 (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (傳統)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (精簡版)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"無語言 (字母)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"字母 (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"字母 (QWERTZ)"</string>
diff --git a/java/res/values-zh-rTW/strings-emoji-descriptions.xml b/java/res/values-zh-rTW/strings-emoji-descriptions.xml
index 06e260a69..b5c723066 100644
--- a/java/res/values-zh-rTW/strings-emoji-descriptions.xml
+++ b/java/res/values-zh-rTW/strings-emoji-descriptions.xml
@@ -267,7 +267,7 @@
<string name="spoken_emoji_1F36A" msgid="2726271795913042295">"餅乾"</string>
<string name="spoken_emoji_1F36B" msgid="6342163604299875931">"巧克力棒"</string>
<string name="spoken_emoji_1F36C" msgid="2168934753998218790">"糖果"</string>
- <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"棒棒糖"</string>
+ <string name="spoken_emoji_1F36D" msgid="3671507903799975792">"Lollipop"</string>
<string name="spoken_emoji_1F36E" msgid="4630541402785165902">"卡士達"</string>
<string name="spoken_emoji_1F36F" msgid="5577915387425169439">"蜂蜜罐"</string>
<string name="spoken_emoji_1F370" msgid="7243244547866114951">"水果蛋糕"</string>
diff --git a/java/res/values-zh-rTW/strings.xml b/java/res/values-zh-rTW/strings.xml
index aa534c869..8fac3105e 100644
--- a/java/res/values-zh-rTW/strings.xml
+++ b/java/res/values-zh-rTW/strings.xml
@@ -102,8 +102,7 @@
<string name="subtype_with_layout_hi_ZZ" msgid="6827402953860547044">"印度英文 (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_with_layout_sr_ZZ" msgid="2859024772719772407">"塞爾維亞文 (<xliff:g id="KEYBOARD_LAYOUT">%s</xliff:g>)"</string>
<string name="subtype_generic_traditional" msgid="8584594350973800586">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (傳統)"</string>
- <!-- no translation found for subtype_generic_compact (3353673321203202922) -->
- <skip />
+ <string name="subtype_generic_compact" msgid="3353673321203202922">"<xliff:g id="LANGUAGE_NAME">%s</xliff:g> (精簡)"</string>
<string name="subtype_no_language" msgid="7137390094240139495">"無語言 (字母)"</string>
<string name="subtype_no_language_qwerty" msgid="244337630616742604">"字母 (QWERTY)"</string>
<string name="subtype_no_language_qwertz" msgid="443066912507547976">"字母 (QWERTZ)"</string>
diff --git a/java/res/xml-sw600dp/key_space_3kw.xml b/java/res/xml-sw600dp/key_space_3kw.xml
index 9932d342e..8cc3a38a5 100644
--- a/java/res/xml-sw600dp/key_space_3kw.xml
+++ b/java/res/xml-sw600dp/key_space_3kw.xml
@@ -22,12 +22,8 @@
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
>
<switch>
- <!-- fa: Perisan
- kn: Kannada
- ne: Nepali
- te: Telugu -->
<case
- latin:languageCode="fa|kn|ne|te"
+ latin:keyboardLayoutSet="bengali_akkhor|farsi|kannada|nepali_romanized|nepali_traditional|telugu"
latin:languageSwitchKeyEnabled="true"
>
<Key
@@ -39,7 +35,7 @@
latin:keyStyle="zwnjKeyStyle" />
</case>
<case
- latin:languageCode="fa|kn|ne|te"
+ latin:keyboardLayoutSet="bengali_akkhor|farsi|kannada|nepali_romanized|nepali_traditional|telugu"
latin:languageSwitchKeyEnabled="false"
>
<Key
diff --git a/java/res/xml-sw600dp/key_space_7kw.xml b/java/res/xml-sw600dp/key_space_7kw.xml
index 3311f812a..61e076534 100644
--- a/java/res/xml-sw600dp/key_space_7kw.xml
+++ b/java/res/xml-sw600dp/key_space_7kw.xml
@@ -22,12 +22,8 @@
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
>
<switch>
- <!-- fa: Perisan
- kn: Kannada
- ne: Nepali
- te: Telugu -->
<case
- latin:languageCode="fa|kn|ne|te"
+ latin:keyboardLayoutSet="bengali_akkhor|farsi|kannada|nepali_romanized|nepali_traditional|telugu"
latin:languageSwitchKeyEnabled="true"
>
<Key
@@ -39,7 +35,7 @@
latin:keyStyle="zwnjKeyStyle" />
</case>
<case
- latin:languageCode="fa|kn|ne|te"
+ latin:keyboardLayoutSet="bengali_akkhor|farsi|kannada|nepali_romanized|nepali_traditional|telugu"
latin:languageSwitchKeyEnabled="false"
>
<Key
diff --git a/java/res/xml-sw600dp/rows_number_normal.xml b/java/res/xml-sw600dp/rows_number_normal.xml
index 7a4700d5a..e6fdf73ec 100644
--- a/java/res/xml-sw600dp/rows_number_normal.xml
+++ b/java/res/xml-sw600dp/rows_number_normal.xml
@@ -24,17 +24,17 @@
<Row>
<Key
latin:keySpec="-"
- latin:keyStyle="numKeyStyle"
+ latin:keyStyle="numSymbolKeyStyle"
latin:keyWidth="10%p"
latin:backgroundType="functional" />
<Key
latin:keySpec="+"
- latin:keyStyle="numKeyStyle"
+ latin:keyStyle="numSymbolKeyStyle"
latin:keyWidth="10%p"
latin:backgroundType="functional" />
<Key
latin:keySpec="."
- latin:keyStyle="numKeyStyle"
+ latin:keyStyle="numSymbolKeyStyle"
latin:keyWidth="10%p"
latin:backgroundType="functional" />
<Key
@@ -53,13 +53,15 @@
latin:keyWidth="fillRight" />
</Row>
<Row>
+ <!-- U+FF0A: "*" FULLWIDTH ASTERISK -->
<Key
- latin:keyStyle="numStarKeyStyle"
+ latin:keySpec="&#xFF0A;|*"
+ latin:keyStyle="numSymbolKeyStyle"
latin:keyWidth="10%p"
latin:backgroundType="functional" />
<Key
latin:keySpec="/"
- latin:keyStyle="numKeyStyle"
+ latin:keyStyle="numSymbolKeyStyle"
latin:keyWidth="10%p"
latin:backgroundType="functional" />
<switch>
@@ -70,14 +72,14 @@
latin:keySpec=","
latin:keyLabelFlags="hasPopupHint"
latin:moreKeys="!text/morekeys_am_pm"
- latin:keyStyle="numKeyStyle"
+ latin:keyStyle="numSymbolKeyStyle"
latin:keyWidth="10%p"
latin:backgroundType="functional" />
</case>
<default>
<Key
latin:keySpec=","
- latin:keyStyle="numKeyStyle"
+ latin:keyStyle="numSymbolKeyStyle"
latin:keyWidth="10%p"
latin:backgroundType="functional" />
</default>
@@ -100,12 +102,12 @@
<Row>
<Key
latin:keySpec="("
- latin:keyStyle="numKeyStyle"
+ latin:keyStyle="numSymbolKeyStyle"
latin:keyWidth="10%p"
latin:backgroundType="functional" />
<Key
latin:keySpec=")"
- latin:keyStyle="numKeyStyle"
+ latin:keyStyle="numSymbolKeyStyle"
latin:keyWidth="10%p"
latin:backgroundType="functional" />
<switch>
@@ -114,14 +116,14 @@
>
<Key
latin:keySpec=":"
- latin:keyStyle="numKeyStyle"
+ latin:keyStyle="numSymbolKeyStyle"
latin:keyWidth="10%p"
latin:backgroundType="functional" />
</case>
<default>
<Key
latin:keySpec="="
- latin:keyStyle="numKeyStyle"
+ latin:keyStyle="numSymbolKeyStyle"
latin:keyWidth="10%p"
latin:backgroundType="functional" />
</default>
@@ -143,8 +145,10 @@
<Key
latin:keyStyle="tabletNumSpaceKeyStyle"
latin:keyWidth="30%p" />
+ <!-- U+FF0A: "*" FULLWIDTH ASTERISK -->
<Key
- latin:keyStyle="numStarKeyStyle"
+ latin:keySpec="&#xFF0A;|*"
+ latin:keyStyle="numSymbolKeyStyle"
latin:keyXPos="31%p" />
<Key
latin:keySpec="0"
diff --git a/java/res/xml-sw600dp/rows_number_password.xml b/java/res/xml-sw600dp/rows_number_password.xml
index 6c3855a01..37e63383e 100644
--- a/java/res/xml-sw600dp/rows_number_password.xml
+++ b/java/res/xml-sw600dp/rows_number_password.xml
@@ -70,7 +70,8 @@
<Key
latin:keyStyle="deleteKeyStyle" />
<Key
- latin:keyStyle="num0KeyStyle" />
+ latin:keyStyle="num0KeyStyle"
+ latin:keyHintLabel="+" />
<Key
latin:keyStyle="enterKeyStyle" />
<!-- Note: This Spacer prevents the above key from being marked as a right edge key. -->
diff --git a/java/res/xml-sw600dp/rows_phone.xml b/java/res/xml-sw600dp/rows_phone.xml
index 612397a90..fc86a7670 100644
--- a/java/res/xml-sw600dp/rows_phone.xml
+++ b/java/res/xml-sw600dp/rows_phone.xml
@@ -28,16 +28,18 @@
<Row>
<Key
latin:keySpec="-"
- latin:keyStyle="numKeyStyle"
+ latin:keyStyle="numSymbolKeyStyle"
latin:keyWidth="10%p"
latin:backgroundType="functional" />
<Key
latin:keySpec="+"
- latin:keyStyle="numKeyStyle"
+ latin:keyStyle="numSymbolKeyStyle"
latin:keyWidth="10%p"
latin:backgroundType="functional" />
<Key
- latin:keyStyle="numPauseKeyStyle"
+ latin:keySpec="!string/label_pause_key|,"
+ latin:keyLabelFlags="followKeyLabelRatio|autoXScale"
+ latin:keyStyle="numSymbolKeyStyle"
latin:keyWidth="10%p"
latin:backgroundType="functional" />
<Key
@@ -55,16 +57,18 @@
<Row>
<Key
latin:keySpec=","
- latin:keyStyle="numKeyStyle"
+ latin:keyStyle="numSymbolKeyStyle"
latin:keyWidth="10%p"
latin:backgroundType="functional" />
<Key
latin:keySpec="."
- latin:keyStyle="numKeyStyle"
+ latin:keyStyle="numSymbolKeyStyle"
latin:keyWidth="10%p"
latin:backgroundType="functional" />
<Key
- latin:keyStyle="numWaitKeyStyle"
+ latin:keySpec="!string/label_wait_key|;"
+ latin:keyLabelFlags="followKeyLabelRatio|autoXScale"
+ latin:keyStyle="numSymbolKeyStyle"
latin:keyWidth="10%p"
latin:backgroundType="functional" />
<Key
@@ -82,17 +86,17 @@
<Row>
<Key
latin:keySpec="("
- latin:keyStyle="numKeyStyle"
+ latin:keyStyle="numSymbolKeyStyle"
latin:keyWidth="10%p"
latin:backgroundType="functional" />
<Key
latin:keySpec=")"
- latin:keyStyle="numKeyStyle"
+ latin:keyStyle="numSymbolKeyStyle"
latin:keyWidth="10%p"
latin:backgroundType="functional" />
<Key
latin:keySpec="N"
- latin:keyStyle="numKeyStyle"
+ latin:keyStyle="numSymbolKeyStyle"
latin:keyWidth="10%p"
latin:backgroundType="functional" />
<Key
@@ -109,13 +113,16 @@
<Key
latin:keyStyle="tabletNumSpaceKeyStyle"
latin:keyWidth="30%p" />
+ <!-- U+FF0A: "*" FULLWIDTH ASTERISK -->
<Key
- latin:keyStyle="numStarKeyStyle"
+ latin:keySpec="&#xFF0A;|*"
+ latin:keyStyle="numSymbolKeyStyle"
latin:keyXPos="31%p" />
<Key
- latin:keyStyle="num0KeyStyle" />
+ latin:keyStyle="num0KeyStyle"
+ latin:keyHintLabel="+" />
<Key
latin:keySpec="\#"
- latin:keyStyle="numKeyStyle" />
+ latin:keyStyle="numSymbolKeyStyle" />
</Row>
</merge>
diff --git a/java/res/xml/key_space_5kw.xml b/java/res/xml/key_space_5kw.xml
index b1fe0bbeb..692c245ff 100644
--- a/java/res/xml/key_space_5kw.xml
+++ b/java/res/xml/key_space_5kw.xml
@@ -22,12 +22,8 @@
xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin"
>
<switch>
- <!-- fa: Perisan
- kn: Kannada
- ne: Nepali
- te: Telugu -->
<case
- latin:languageCode="fa|kn|ne|te"
+ latin:keyboardLayoutSet="bengali_akkhor|farsi|kannada|nepali_romanized|nepali_traditional|telugu"
latin:languageSwitchKeyEnabled="true"
>
<Key
@@ -39,7 +35,7 @@
latin:keyStyle="zwnjKeyStyle" />
</case>
<case
- latin:languageCode="fa|kn|ne|te"
+ latin:keyboardLayoutSet="bengali_akkhor|farsi|kannada|nepali_romanized|nepali_traditional|telugu"
latin:languageSwitchKeyEnabled="false"
>
<Key
diff --git a/java/res/xml/key_styles_number.xml b/java/res/xml/key_styles_number.xml
index 847b43610..911c2763d 100644
--- a/java/res/xml/key_styles_number.xml
+++ b/java/res/xml/key_styles_number.xml
@@ -33,9 +33,7 @@
latin:keyLabelFlags="fontNormal|followKeyLetterRatio|followFunctionalTextColor"
latin:parentStyle="numKeyBaseStyle" />
<key-style
- latin:styleName="numFunctionalKeyStyle"
- latin:keyLabelFlags="followKeyLargeLetterRatio"
- latin:backgroundType="functional"
+ latin:styleName="numSymbolKeyStyle"
latin:parentStyle="numKeyBaseStyle" />
<key-style
latin:styleName="numberKeyStyle"
@@ -44,7 +42,6 @@
<key-style
latin:styleName="num0KeyStyle"
latin:keySpec="0"
- latin:keyHintLabel="+"
latin:parentStyle="numberKeyStyle" />
<key-style
latin:styleName="num1KeyStyle"
@@ -90,11 +87,6 @@
latin:keySpec="9"
latin:keyHintLabel="WXYZ"
latin:parentStyle="numberKeyStyle" />
- <!-- U+FF0A: "*" FULLWIDTH ASTERISK -->
- <key-style
- latin:styleName="numStarKeyStyle"
- latin:keySpec="&#xFF0A;|*"
- latin:parentStyle="numKeyStyle" />
<!-- Only for non-tablet device -->
<key-style
latin:styleName="numPhoneToSymbolKeyStyle"
@@ -105,16 +97,6 @@
latin:keySpec="!text/keylabel_to_phone_numeric|!code/key_switch_alpha_symbol"
latin:parentStyle="numModeKeyStyle" />
<key-style
- latin:styleName="numPauseKeyStyle"
- latin:keySpec="!text/label_pause_key|,"
- latin:keyLabelFlags="followKeyHintLabelRatio|autoXScale"
- latin:parentStyle="numKeyBaseStyle" />
- <key-style
- latin:styleName="numWaitKeyStyle"
- latin:keySpec="!text/label_wait_key|;"
- latin:keyLabelFlags="followKeyHintLabelRatio|autoXScale"
- latin:parentStyle="numKeyBaseStyle" />
- <key-style
latin:styleName="numTabKeyStyle"
latin:keyActionFlags="noKeyPreview"
latin:parentStyle="tabKeyStyle" />
diff --git a/java/res/xml/keyboard_layout_set_bengali_akkhor.xml b/java/res/xml/keyboard_layout_set_bengali_akkhor.xml
index b2b09b22d..267064d46 100644
--- a/java/res/xml/keyboard_layout_set_bengali_akkhor.xml
+++ b/java/res/xml/keyboard_layout_set_bengali_akkhor.xml
@@ -19,7 +19,7 @@
-->
<KeyboardLayoutSet xmlns:latin="http://schemas.android.com/apk/res/com.android.inputmethod.latin" >
- <Feature latin:supportedScript="devanagari" />
+ <Feature latin:supportedScript="bengali" />
<Element
latin:elementKeyboard="@xml/kbd_bengali_akkhor"
latin:elementName="alphabet"
diff --git a/java/res/xml/rows_number_normal.xml b/java/res/xml/rows_number_normal.xml
index d8d15080e..0f92ac605 100644
--- a/java/res/xml/rows_number_normal.xml
+++ b/java/res/xml/rows_number_normal.xml
@@ -35,7 +35,8 @@
latin:keySpec="-"
latin:moreKeys="+"
latin:keyLabelFlags="hasPopupHint"
- latin:keyStyle="numFunctionalKeyStyle"
+ latin:keyStyle="numKeyStyle"
+ latin:backgroundType="functional"
latin:keyWidth="fillRight" />
</Row>
<Row>
@@ -54,7 +55,8 @@
>
<Key
latin:keySpec="."
- latin:keyStyle="numFunctionalKeyStyle"
+ latin:keyStyle="numKeyStyle"
+ latin:backgroundType="functional"
latin:keyWidth="fillRight" />
</case>
<case
@@ -62,15 +64,17 @@
>
<Key
latin:keySpec="."
- latin:keyLabelFlags="hasPopupHint"
latin:moreKeys="!text/morekeys_am_pm"
- latin:keyStyle="numFunctionalKeyStyle"
+ latin:keyLabelFlags="hasPopupHint"
+ latin:keyStyle="numKeyStyle"
+ latin:backgroundType="functional"
latin:keyWidth="fillRight" />
</case>
<default>
<Key
latin:keySpec=","
- latin:keyStyle="numFunctionalKeyStyle"
+ latin:keyStyle="numKeyStyle"
+ latin:backgroundType="functional"
latin:keyWidth="fillRight" />
</default>
</switch>
diff --git a/java/res/xml/rows_number_password.xml b/java/res/xml/rows_number_password.xml
index 2e61a08ae..65736c430 100644
--- a/java/res/xml/rows_number_password.xml
+++ b/java/res/xml/rows_number_password.xml
@@ -70,7 +70,8 @@
<Key
latin:keyStyle="deleteKeyStyle" />
<Key
- latin:keyStyle="num0KeyStyle" />
+ latin:keyStyle="num0KeyStyle"
+ latin:keyHintLabel="+" />
<Key
latin:keyStyle="enterKeyStyle" />
<!-- Note: This Spacer prevents the above key from being marked as a right edge key. -->
diff --git a/java/res/xml/rows_phone.xml b/java/res/xml/rows_phone.xml
index 03e45419a..bb5590d4e 100644
--- a/java/res/xml/rows_phone.xml
+++ b/java/res/xml/rows_phone.xml
@@ -36,7 +36,8 @@
latin:keySpec="-"
latin:moreKeys="+"
latin:keyLabelFlags="hasPopupHint"
- latin:keyStyle="numFunctionalKeyStyle"
+ latin:keyStyle="numKeyStyle"
+ latin:backgroundType="functional"
latin:keyWidth="fillRight" />
</Row>
<Row>
@@ -48,7 +49,8 @@
latin:keyStyle="num6KeyStyle" />
<Key
latin:keySpec="."
- latin:keyStyle="numFunctionalKeyStyle"
+ latin:keyStyle="numKeyStyle"
+ latin:backgroundType="functional"
latin:keyWidth="fillRight" />
</Row>
<Row>
diff --git a/java/res/xml/rows_phone_symbols.xml b/java/res/xml/rows_phone_symbols.xml
index 983bfb5c8..195a183a3 100644
--- a/java/res/xml/rows_phone_symbols.xml
+++ b/java/res/xml/rows_phone_symbols.xml
@@ -28,45 +28,53 @@
<Row>
<Key
latin:keySpec="("
- latin:keyStyle="numKeyStyle" />
+ latin:keyStyle="numSymbolKeyStyle" />
<Key
latin:keySpec="/"
- latin:keyStyle="numKeyStyle" />
+ latin:keyStyle="numSymbolKeyStyle" />
<Key
latin:keySpec=")"
- latin:keyStyle="numKeyStyle" />
+ latin:keyStyle="numSymbolKeyStyle" />
<Key
latin:keySpec="-"
latin:moreKeys="+"
latin:keyLabelFlags="hasPopupHint"
- latin:keyStyle="numFunctionalKeyStyle"
+ latin:keyStyle="numKeyStyle"
+ latin:backgroundType="functional"
latin:keyWidth="fillRight" />
</Row>
<Row>
<Key
latin:keySpec="N"
- latin:keyStyle="numKeyBaseStyle" />
+ latin:keyStyle="numSymbolKeyStyle" />
<!-- Pause is a comma. Check PhoneNumberUtils.java to see if this
has changed. -->
<Key
- latin:keyStyle="numPauseKeyStyle" />
+ latin:keySpec="!string/label_pause_key|,"
+ latin:keyLabelFlags="followKeyLabelRatio|autoXScale"
+ latin:keyStyle="numSymbolKeyStyle" />
<Key
latin:keySpec=","
- latin:keyStyle="numKeyStyle" />
+ latin:keyStyle="numSymbolKeyStyle" />
<Key
latin:keySpec="."
- latin:keyStyle="numFunctionalKeyStyle"
+ latin:keyStyle="numKeyStyle"
+ latin:backgroundType="functional"
latin:keyWidth="fillRight" />
</Row>
<Row>
+ <!-- U+FF0A: "*" FULLWIDTH ASTERISK -->
<Key
- latin:keyStyle="numStarKeyStyle" />
+ latin:keySpec="&#xFF0A;|*"
+ latin:keyStyle="numSymbolKeyStyle" />
<!-- Wait is a semicolon. -->
<Key
- latin:keyStyle="numWaitKeyStyle" />
+ latin:keySpec="!string/label_wait_key|;"
+ latin:keyLabelFlags="followKeyLabelRatio|autoXScale"
+ latin:keyStyle="numSymbolKeyStyle" />
<Key
latin:keySpec="\#"
- latin:keyStyle="numKeyStyle" />
+ latin:keyStyle="numSymbolKeyStyle" />
<Key
latin:keyStyle="deleteKeyStyle"
latin:keyWidth="fillRight" />
@@ -76,7 +84,7 @@
latin:keyStyle="numPhoneToNumericKeyStyle" />
<Key
latin:keySpec="+"
- latin:keyStyle="numKeyStyle" />
+ latin:keyStyle="numSymbolKeyStyle" />
<Key
latin:keyStyle="numSpaceKeyStyle" />
<Key
diff --git a/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityNodeProvider.java b/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityNodeProvider.java
index 7fc1e9d8a..2de71cec9 100644
--- a/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityNodeProvider.java
+++ b/java/src/com/android/inputmethod/accessibility/KeyboardAccessibilityNodeProvider.java
@@ -31,9 +31,9 @@ import android.view.inputmethod.EditorInfo;
import com.android.inputmethod.keyboard.Key;
import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.KeyboardView;
+import com.android.inputmethod.latin.common.CoordinateUtils;
import com.android.inputmethod.latin.settings.Settings;
import com.android.inputmethod.latin.settings.SettingsValues;
-import com.android.inputmethod.latin.utils.CoordinateUtils;
import java.util.List;
diff --git a/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatUtils.java b/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatUtils.java
index 3a27c5739..58ad4bd4c 100644
--- a/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatUtils.java
+++ b/java/src/com/android/inputmethod/compat/InputMethodSubtypeCompatUtils.java
@@ -26,6 +26,8 @@ import com.android.inputmethod.latin.common.Constants;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
+import javax.annotation.Nonnull;
+
public final class InputMethodSubtypeCompatUtils {
private static final String TAG = InputMethodSubtypeCompatUtils.class.getSimpleName();
// Note that InputMethodSubtype(int nameId, int iconId, String locale, String mode,
@@ -53,6 +55,7 @@ public final class InputMethodSubtypeCompatUtils {
}
@SuppressWarnings("deprecation")
+ @Nonnull
public static InputMethodSubtype newInputMethodSubtype(int nameId, int iconId, String locale,
String mode, String extraValue, boolean isAuxiliary,
boolean overridesImplicitlyEnabledSubtype, int id) {
diff --git a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
index d3e24e342..be0744393 100644
--- a/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
+++ b/java/src/com/android/inputmethod/compat/SuggestionSpanUtils.java
@@ -27,8 +27,8 @@ import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.latin.SuggestedWords;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.SuggestionSpanPickedNotificationReceiver;
+import com.android.inputmethod.latin.common.LocaleUtils;
import com.android.inputmethod.latin.define.DebugFlags;
-import com.android.inputmethod.latin.utils.LocaleUtils;
import java.lang.reflect.Field;
import java.util.ArrayList;
diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java b/java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java
index 37fa76be7..659fe5c51 100644
--- a/java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java
+++ b/java/src/com/android/inputmethod/dictionarypack/DictionaryProvider.java
@@ -31,6 +31,7 @@ import android.text.TextUtils;
import android.util.Log;
import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.common.LocaleUtils;
import com.android.inputmethod.latin.utils.DebugLogUtils;
import java.io.File;
diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java b/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java
index e9b634eec..e6acb8f36 100644
--- a/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java
+++ b/java/src/com/android/inputmethod/dictionarypack/DictionaryService.java
@@ -25,6 +25,7 @@ import android.os.IBinder;
import android.widget.Toast;
import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.common.LocaleUtils;
import java.util.Locale;
import java.util.Random;
diff --git a/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java b/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java
index c2dc87900..14e005007 100644
--- a/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java
+++ b/java/src/com/android/inputmethod/dictionarypack/DictionarySettingsFragment.java
@@ -16,6 +16,8 @@
package com.android.inputmethod.dictionarypack;
+import com.android.inputmethod.latin.common.LocaleUtils;
+
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
diff --git a/java/src/com/android/inputmethod/dictionarypack/DownloadOverMeteredDialog.java b/java/src/com/android/inputmethod/dictionarypack/DownloadOverMeteredDialog.java
index f1633ff28..50b3c72f3 100644
--- a/java/src/com/android/inputmethod/dictionarypack/DownloadOverMeteredDialog.java
+++ b/java/src/com/android/inputmethod/dictionarypack/DownloadOverMeteredDialog.java
@@ -26,6 +26,7 @@ import android.widget.TextView;
import com.android.inputmethod.annotations.ExternallyReferenced;
import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.common.LocaleUtils;
import java.util.Locale;
diff --git a/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java b/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java
index db4315f8f..ce23eb752 100644
--- a/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java
+++ b/java/src/com/android/inputmethod/dictionarypack/MetadataDbHelper.java
@@ -47,7 +47,8 @@ public class MetadataDbHelper extends SQLiteOpenHelper {
// used to identify the versions for upgrades. This should never change going forward.
private static final int METADATA_DATABASE_VERSION_WITH_CLIENTID = 6;
// The current database version.
- private static final int CURRENT_METADATA_DATABASE_VERSION = 10;
+ // This MUST be increased every time the dictionary pack metadata URL changes.
+ private static final int CURRENT_METADATA_DATABASE_VERSION = 11;
private final static long NOT_A_DOWNLOAD_ID = -1;
diff --git a/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java b/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java
index d59b7a545..aeb666704 100644
--- a/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java
+++ b/java/src/com/android/inputmethod/dictionarypack/UpdateHandler.java
@@ -39,6 +39,8 @@ import com.android.inputmethod.compat.ConnectivityManagerCompatUtils;
import com.android.inputmethod.compat.DownloadManagerCompatUtils;
import com.android.inputmethod.compat.NotificationCompatUtils;
import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.common.LocaleUtils;
+import com.android.inputmethod.latin.makedict.FormatSpec;
import com.android.inputmethod.latin.utils.ApplicationUtils;
import com.android.inputmethod.latin.utils.DebugLogUtils;
@@ -78,7 +80,8 @@ public final class UpdateHandler {
// DownloadManager uses as an ID numbers returned out of an AUTOINCREMENT column
// in SQLite, so it should never return anything < 0.
public static final int NOT_AN_ID = -1;
- public static final int MAXIMUM_SUPPORTED_FORMAT_VERSION = 2;
+ public static final int MAXIMUM_SUPPORTED_FORMAT_VERSION =
+ FormatSpec.MAXIMUM_SUPPORTED_STATIC_VERSION;
// Arbitrary. Probably good if it's a power of 2, and a couple thousand bytes long.
private static final int FILE_COPY_BUFFER_SIZE = 8192;
diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java
index 3c90a04db..619b801f4 100644
--- a/java/src/com/android/inputmethod/keyboard/Keyboard.java
+++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java
@@ -22,7 +22,7 @@ import com.android.inputmethod.keyboard.internal.KeyVisualAttributes;
import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
import com.android.inputmethod.keyboard.internal.KeyboardParams;
import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.utils.CoordinateUtils;
+import com.android.inputmethod.latin.common.CoordinateUtils;
import java.util.ArrayList;
import java.util.Collections;
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
index b674359e6..b1051385d 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardLayoutSet.java
@@ -38,7 +38,6 @@ import com.android.inputmethod.keyboard.internal.KeysCache;
import com.android.inputmethod.latin.InputAttributes;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.RichInputMethodSubtype;
-import com.android.inputmethod.latin.SubtypeSwitcher;
import com.android.inputmethod.latin.define.DebugFlags;
import com.android.inputmethod.latin.utils.InputTypeUtils;
import com.android.inputmethod.latin.utils.ScriptUtils;
@@ -52,6 +51,9 @@ import java.io.IOException;
import java.lang.ref.SoftReference;
import java.util.HashMap;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
/**
* This class represents a set of keyboard layouts. Each of them represents a different keyboard
* specific to a keyboard state, such as alphabet, symbols, and so on. Layouts in the same
@@ -83,6 +85,8 @@ public final class KeyboardLayoutSet {
private static final HashMap<KeyboardId, SoftReference<Keyboard>> sKeyboardCache =
new HashMap<>();
private static final KeysCache sKeysCache = new KeysCache();
+ private final static HashMap<InputMethodSubtype, Integer> sScriptIdsForSubtypes =
+ new HashMap<>();
@SuppressWarnings("serial")
public static final class KeyboardLayoutSetException extends RuntimeException {
@@ -141,6 +145,16 @@ public final class KeyboardLayoutSet {
sKeysCache.clear();
}
+ public static int getScriptId(final Resources resources, final InputMethodSubtype subtype) {
+ final Integer value = sScriptIdsForSubtypes.get(subtype);
+ if (null == value) {
+ final int scriptId = Builder.readScriptId(resources, subtype);
+ sScriptIdsForSubtypes.put(subtype, scriptId);
+ return scriptId;
+ }
+ return value;
+ }
+
KeyboardLayoutSet(final Context context, final Params params) {
mContext = context;
mParams = params;
@@ -245,7 +259,7 @@ public final class KeyboardLayoutSet {
private static final EditorInfo EMPTY_EDITOR_INFO = new EditorInfo();
- public Builder(final Context context, final EditorInfo ei) {
+ public Builder(final Context context, @Nullable final EditorInfo ei) {
mContext = context;
mPackageName = context.getPackageName();
mResources = context.getResources();
@@ -266,7 +280,7 @@ public final class KeyboardLayoutSet {
return this;
}
- public Builder setSubtype(final RichInputMethodSubtype subtype) {
+ public Builder setSubtype(@Nonnull final RichInputMethodSubtype subtype) {
final boolean asciiCapable = InputMethodSubtypeCompatUtils.isAsciiCapable(subtype);
// TODO: Consolidate with {@link InputAttributes}.
@SuppressWarnings("deprecation")
@@ -276,11 +290,11 @@ public final class KeyboardLayoutSet {
mParams.mEditorInfo.imeOptions)
|| deprecatedForceAscii;
final RichInputMethodSubtype keyboardSubtype = (forceAscii && !asciiCapable)
- ? SubtypeSwitcher.getInstance().getNoLanguageSubtype()
+ ? RichInputMethodSubtype.getNoLanguageSubtype()
: subtype;
mParams.mSubtype = keyboardSubtype;
mParams.mKeyboardLayoutSetName = KEYBOARD_LAYOUT_SET_RESOURCE_PREFIX
- + SubtypeLocaleUtils.getKeyboardLayoutSetName(keyboardSubtype);
+ + keyboardSubtype.getKeyboardLayoutSetName();
return this;
}
@@ -304,31 +318,13 @@ public final class KeyboardLayoutSet {
return this;
}
- public Builder setScriptId(final int scriptId) {
- mParams.mScriptId = scriptId;
- return this;
- }
-
public Builder setSplitLayoutEnabledByUser(final boolean enabled) {
mParams.mIsSplitLayoutEnabledByUser = enabled;
return this;
}
- private final static HashMap<InputMethodSubtype, Integer> sScriptIdsForSubtypes =
- new HashMap<>();
- public static int getScriptId(final Resources resources, final InputMethodSubtype subtype) {
- final Integer value = sScriptIdsForSubtypes.get(subtype);
- if (null == value) {
- final int scriptId = readScriptId(resources, subtype);
- sScriptIdsForSubtypes.put(subtype, scriptId);
- return scriptId;
- }
- return value;
- }
-
// Super redux version of reading the script ID for some subtype from Xml.
- private static int readScriptId(final Resources resources,
- final InputMethodSubtype subtype) {
+ static int readScriptId(final Resources resources, final InputMethodSubtype subtype) {
final String layoutSetName = KEYBOARD_LAYOUT_SET_RESOURCE_PREFIX
+ SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype);
final int xmlId = getXmlId(resources, layoutSetName);
@@ -359,7 +355,7 @@ public final class KeyboardLayoutSet {
try {
final int scriptId =
featureAttr.getInt(R.styleable.KeyboardLayoutSet_Feature_supportedScript,
- ScriptUtils.SCRIPT_UNKNOWN);
+ ScriptUtils.SCRIPT_UNKNOWN);
XmlParseUtils.checkEndTag(TAG_FEATURE, parser);
return scriptId;
} finally {
@@ -415,7 +411,7 @@ public final class KeyboardLayoutSet {
if (TAG_ELEMENT.equals(tag)) {
parseKeyboardLayoutSetElement(parser);
} else if (TAG_FEATURE.equals(tag)) {
- parseKeyboardLayoutSetFeature(parser);
+ mParams.mScriptId = readScriptIdFromTagFeature(mResources, parser);
} else {
throw new XmlParseUtils.IllegalStartTag(parser, tag, TAG_KEYBOARD_SET);
}
@@ -460,12 +456,6 @@ public final class KeyboardLayoutSet {
}
}
- private void parseKeyboardLayoutSetFeature(final XmlPullParser parser)
- throws XmlPullParserException, IOException {
- final int scriptId = readScriptIdFromTagFeature(mResources, parser);
- setScriptId(scriptId);
- }
-
private static int getKeyboardMode(final EditorInfo editorInfo) {
final int inputType = editorInfo.inputType;
final int variation = inputType & InputType.TYPE_MASK_VARIATION;
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index 89acc3cd3..5e3a5f17c 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -39,6 +39,8 @@ import com.android.inputmethod.latin.WordComposer;
import com.android.inputmethod.latin.define.ProductionFlags;
import com.android.inputmethod.latin.settings.Settings;
import com.android.inputmethod.latin.settings.SettingsValues;
+import com.android.inputmethod.latin.utils.CapsModeUtils;
+import com.android.inputmethod.latin.utils.RecapitalizeStatus;
import com.android.inputmethod.latin.utils.ResourceUtils;
import com.android.inputmethod.latin.utils.ScriptUtils;
@@ -112,7 +114,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
final int keyboardWidth = ResourceUtils.getDefaultKeyboardWidth(res);
final int keyboardHeight = ResourceUtils.getKeyboardHeight(res, settingsValues);
builder.setKeyboardGeometry(keyboardWidth, keyboardHeight);
- builder.setSubtype(mSubtypeSwitcher.getCurrentSubtype());
+ builder.setSubtype(RichInputMethodManager.getInstance().getCurrentSubtype());
builder.setVoiceInputKeyEnabled(settingsValues.mShowsVoiceInputKey);
builder.setLanguageSwitchKeyEnabled(mLatinIME.shouldShowLanguageSwitchKey());
builder.setSplitLayoutEnabledByUser(ProductionFlags.IS_SPLIT_KEYBOARD_SUPPORTED
@@ -121,7 +123,8 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
try {
mState.onLoadKeyboard(currentAutoCapsState, currentRecapitalizeState);
// TODO: revisit this for multi-lingual input
- mKeyboardTextsSet.setLocale(mSubtypeSwitcher.getCurrentSubtypeLocales()[0],
+ mKeyboardTextsSet.setLocale(
+ RichInputMethodManager.getInstance().getCurrentSubtypeLocales()[0],
mThemeContext);
} catch (KeyboardLayoutSetException e) {
Log.w(TAG, "loading keyboard failed: " + e.mKeyboardId, e.getCause());
@@ -161,7 +164,7 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
currentSettingsValues.mKeyPreviewDismissEndXScale,
currentSettingsValues.mKeyPreviewDismissEndYScale,
currentSettingsValues.mKeyPreviewDismissDuration);
- keyboardView.updateShortcutKey(mSubtypeSwitcher.isShortcutImeReady());
+ keyboardView.updateShortcutKey(RichInputMethodManager.getInstance().isShortcutImeReady());
final boolean subtypeChanged = (oldKeyboard == null)
|| !keyboard.mId.mSubtype.equals(oldKeyboard.mId.mSubtype);
final int languageOnSpacebarFormatType = mSubtypeSwitcher.getLanguageOnSpacebarFormatType(
@@ -204,42 +207,64 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
// Implements {@link KeyboardState.SwitchActions}.
@Override
public void setAlphabetKeyboard() {
+ if (DEBUG_ACTION) {
+ Log.d(TAG, "setAlphabetKeyboard");
+ }
setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET));
}
// Implements {@link KeyboardState.SwitchActions}.
@Override
public void setAlphabetManualShiftedKeyboard() {
+ if (DEBUG_ACTION) {
+ Log.d(TAG, "setAlphabetManualShiftedKeyboard");
+ }
setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_MANUAL_SHIFTED));
}
// Implements {@link KeyboardState.SwitchActions}.
@Override
public void setAlphabetAutomaticShiftedKeyboard() {
+ if (DEBUG_ACTION) {
+ Log.d(TAG, "setAlphabetAutomaticShiftedKeyboard");
+ }
setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_AUTOMATIC_SHIFTED));
}
// Implements {@link KeyboardState.SwitchActions}.
@Override
public void setAlphabetShiftLockedKeyboard() {
+ if (DEBUG_ACTION) {
+ Log.d(TAG, "setAlphabetShiftLockedKeyboard");
+ }
setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCKED));
}
// Implements {@link KeyboardState.SwitchActions}.
@Override
public void setAlphabetShiftLockShiftedKeyboard() {
+ if (DEBUG_ACTION) {
+ Log.d(TAG, "setAlphabetShiftLockShiftedKeyboard");
+ }
setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET_SHIFT_LOCK_SHIFTED));
}
// Implements {@link KeyboardState.SwitchActions}.
@Override
public void setSymbolsKeyboard() {
+ if (DEBUG_ACTION) {
+ Log.d(TAG, "setSymbolsKeyboard");
+ }
setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_SYMBOLS));
}
private void setMainKeyboardFrame(final SettingsValues settingsValues) {
- mMainKeyboardFrame.setVisibility(
- settingsValues.mHasHardwareKeyboard ? View.GONE : View.VISIBLE);
+ final int visibility = settingsValues.mHasHardwareKeyboard ? View.GONE : View.VISIBLE;
+ mKeyboardView.setVisibility(visibility);
+ // The visibility of {@link #mKeyboardView} must be aligned with {@link #MainKeyboardFrame}.
+ // @see #getVisibleKeyboardView() and
+ // @see LatinIME#onComputeInset(android.inputmethodservice.InputMethodService.Insets)
+ mMainKeyboardFrame.setVisibility(visibility);
mEmojiPalettesView.setVisibility(View.GONE);
mEmojiPalettesView.stopEmojiPalettes();
}
@@ -247,8 +272,15 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
// Implements {@link KeyboardState.SwitchActions}.
@Override
public void setEmojiKeyboard() {
+ if (DEBUG_ACTION) {
+ Log.d(TAG, "setEmojiKeyboard");
+ }
final Keyboard keyboard = mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET);
mMainKeyboardFrame.setVisibility(View.GONE);
+ // The visibility of {@link #mKeyboardView} must be aligned with {@link #MainKeyboardFrame}.
+ // @see #getVisibleKeyboardView() and
+ // @see LatinIME#onComputeInset(android.inputmethodservice.InputMethodService.Insets)
+ mKeyboardView.setVisibility(View.GONE);
mEmojiPalettesView.startEmojiPalettes(
mKeyboardTextsSet.getText(KeyboardTextsSet.SWITCH_TO_ALPHA_KEY_LABEL),
mKeyboardView.getKeyVisualAttribute(), keyboard.mIconsSet);
@@ -269,19 +301,29 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
// Implements {@link KeyboardState.SwitchActions}.
@Override
public void setSymbolsShiftedKeyboard() {
+ if (DEBUG_ACTION) {
+ Log.d(TAG, "setSymbolsShiftedKeyboard");
+ }
setKeyboard(mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_SYMBOLS_SHIFTED));
}
// Future method for requesting an updating to the shift state.
@Override
- public void requestUpdatingShiftState(final int currentAutoCapsState,
- final int currentRecapitalizeState) {
- mState.onUpdateShiftState(currentAutoCapsState, currentRecapitalizeState);
+ public void requestUpdatingShiftState(final int autoCapsFlags, final int recapitalizeMode) {
+ if (DEBUG_ACTION) {
+ Log.d(TAG, "requestUpdatingShiftState: "
+ + " autoCapsFlags=" + CapsModeUtils.flagsToString(autoCapsFlags)
+ + " recapitalizeMode=" + RecapitalizeStatus.modeToString(recapitalizeMode));
+ }
+ mState.onUpdateShiftState(autoCapsFlags, recapitalizeMode);
}
// Implements {@link KeyboardState.SwitchActions}.
@Override
public void startDoubleTapShiftKeyTimer() {
+ if (DEBUG_TIMER_ACTION) {
+ Log.d(TAG, "startDoubleTapShiftKeyTimer");
+ }
final MainKeyboardView keyboardView = getMainKeyboardView();
if (keyboardView != null) {
keyboardView.startDoubleTapShiftKeyTimer();
@@ -291,6 +333,9 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
// Implements {@link KeyboardState.SwitchActions}.
@Override
public void cancelDoubleTapShiftKeyTimer() {
+ if (DEBUG_TIMER_ACTION) {
+ Log.d(TAG, "setAlphabetKeyboard");
+ }
final MainKeyboardView keyboardView = getMainKeyboardView();
if (keyboardView != null) {
keyboardView.cancelDoubleTapShiftKeyTimer();
@@ -300,6 +345,9 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
// Implements {@link KeyboardState.SwitchActions}.
@Override
public boolean isInDoubleTapShiftKeyTimeout() {
+ if (DEBUG_TIMER_ACTION) {
+ Log.d(TAG, "isInDoubleTapShiftKeyTimeout");
+ }
final MainKeyboardView keyboardView = getMainKeyboardView();
return keyboardView != null && keyboardView.isInDoubleTapShiftKeyTimeout();
}
@@ -367,9 +415,10 @@ public final class KeyboardSwitcher implements KeyboardState.SwitchActions {
}
public void onNetworkStateChanged() {
- if (mKeyboardView != null) {
- mKeyboardView.updateShortcutKey(mSubtypeSwitcher.isShortcutImeReady());
+ if (mKeyboardView == null) {
+ return;
}
+ mKeyboardView.updateShortcutKey(RichInputMethodManager.getInstance().isShortcutImeReady());
}
public int getKeyboardShiftMode() {
diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
index f23db04f3..cba7ff2a2 100644
--- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
@@ -39,8 +39,8 @@ import android.view.ViewGroup;
import com.android.inputmethod.accessibility.AccessibilityUtils;
import com.android.inputmethod.accessibility.MainKeyboardAccessibilityDelegate;
import com.android.inputmethod.annotations.ExternallyReferenced;
-import com.android.inputmethod.keyboard.PointerTracker.DrawingProxy;
import com.android.inputmethod.keyboard.internal.DrawingPreviewPlacerView;
+import com.android.inputmethod.keyboard.internal.DrawingProxy;
import com.android.inputmethod.keyboard.internal.GestureFloatingTextDrawingPreview;
import com.android.inputmethod.keyboard.internal.GestureTrailsDrawingPreview;
import com.android.inputmethod.keyboard.internal.KeyDrawParams;
@@ -56,9 +56,9 @@ import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.RichInputMethodSubtype;
import com.android.inputmethod.latin.SuggestedWords;
import com.android.inputmethod.latin.common.Constants;
+import com.android.inputmethod.latin.common.CoordinateUtils;
import com.android.inputmethod.latin.common.StringUtils;
import com.android.inputmethod.latin.settings.DebugSettings;
-import com.android.inputmethod.latin.utils.CoordinateUtils;
import com.android.inputmethod.latin.utils.TypefaceUtils;
import java.util.Locale;
@@ -110,7 +110,7 @@ import javax.annotation.Nullable;
* @attr ref R.styleable#MainKeyboardView_gestureRecognitionSpeedThreshold
* @attr ref R.styleable#MainKeyboardView_suppressKeyPreviewAfterBatchInputDuration
*/
-public final class MainKeyboardView extends KeyboardView implements PointerTracker.DrawingProxy,
+public final class MainKeyboardView extends KeyboardView implements DrawingProxy,
MoreKeysPanel.Controller {
private static final String TAG = MainKeyboardView.class.getSimpleName();
diff --git a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
index 01522536f..3acc11b59 100644
--- a/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MoreKeysKeyboardView.java
@@ -31,7 +31,7 @@ import com.android.inputmethod.accessibility.MoreKeysKeyboardAccessibilityDelega
import com.android.inputmethod.keyboard.internal.KeyDrawParams;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.utils.CoordinateUtils;
+import com.android.inputmethod.latin.common.CoordinateUtils;
/**
* A view that renders a virtual {@link MoreKeysKeyboard}. It handles rendering of keys and
diff --git a/java/src/com/android/inputmethod/keyboard/PointerTracker.java b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
index 467f5150a..7902ce852 100644
--- a/java/src/com/android/inputmethod/keyboard/PointerTracker.java
+++ b/java/src/com/android/inputmethod/keyboard/PointerTracker.java
@@ -25,18 +25,20 @@ import android.view.MotionEvent;
import com.android.inputmethod.keyboard.internal.BatchInputArbiter;
import com.android.inputmethod.keyboard.internal.BatchInputArbiter.BatchInputArbiterListener;
import com.android.inputmethod.keyboard.internal.BogusMoveEventDetector;
+import com.android.inputmethod.keyboard.internal.DrawingProxy;
import com.android.inputmethod.keyboard.internal.GestureEnabler;
import com.android.inputmethod.keyboard.internal.GestureStrokeDrawingParams;
import com.android.inputmethod.keyboard.internal.GestureStrokeDrawingPoints;
import com.android.inputmethod.keyboard.internal.GestureStrokeRecognitionParams;
import com.android.inputmethod.keyboard.internal.PointerTrackerQueue;
+import com.android.inputmethod.keyboard.internal.TimerProxy;
import com.android.inputmethod.keyboard.internal.TypingTimeRecorder;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.common.Constants;
+import com.android.inputmethod.latin.common.CoordinateUtils;
import com.android.inputmethod.latin.common.InputPointers;
import com.android.inputmethod.latin.define.DebugFlags;
import com.android.inputmethod.latin.settings.Settings;
-import com.android.inputmethod.latin.utils.CoordinateUtils;
import com.android.inputmethod.latin.utils.ResourceUtils;
import java.util.ArrayList;
@@ -52,66 +54,6 @@ public final class PointerTracker implements PointerTrackerQueue.Element,
private static final boolean DEBUG_LISTENER = false;
private static boolean DEBUG_MODE = DebugFlags.DEBUG_ENABLED || DEBUG_EVENT;
- public interface DrawingProxy {
- public void invalidateKey(@Nullable Key key);
- public void showKeyPreview(@Nonnull Key key);
- public void dismissKeyPreview(@Nonnull Key key);
- public void dismissKeyPreviewWithoutDelay(@Nonnull Key key);
- public void onLongPress(@Nonnull PointerTracker tracker);
- public static final int FADE_IN = 0;
- public static final int FADE_OUT = 1;
- public void startWhileTypingAnimation(final int fadeInOrOut);
- public void showSlidingKeyInputPreview(@Nullable PointerTracker tracker);
- public void showGestureTrail(@Nonnull PointerTracker tracker,
- boolean showsFloatingPreviewText);
- public void dismissGestureFloatingPreviewTextWithoutDelay();
- }
-
- public interface TimerProxy {
- public void startTypingStateTimer(Key typedKey);
- public boolean isTypingState();
- public void startKeyRepeatTimerOf(PointerTracker tracker, int repeatCount, int delay);
- public void startLongPressTimerOf(PointerTracker tracker, int delay);
- public void cancelLongPressTimerOf(PointerTracker tracker);
- public void cancelLongPressShiftKeyTimers();
- public void cancelKeyTimersOf(PointerTracker tracker);
- public void startDoubleTapShiftKeyTimer();
- public void cancelDoubleTapShiftKeyTimer();
- public boolean isInDoubleTapShiftKeyTimeout();
- public void startUpdateBatchInputTimer(PointerTracker tracker);
- public void cancelUpdateBatchInputTimer(PointerTracker tracker);
- public void cancelAllUpdateBatchInputTimers();
-
- public static class Adapter implements TimerProxy {
- @Override
- public void startTypingStateTimer(Key typedKey) {}
- @Override
- public boolean isTypingState() { return false; }
- @Override
- public void startKeyRepeatTimerOf(PointerTracker tracker, int repeatCount, int delay) {}
- @Override
- public void startLongPressTimerOf(PointerTracker tracker, int delay) {}
- @Override
- public void cancelLongPressTimerOf(PointerTracker tracker) {}
- @Override
- public void cancelLongPressShiftKeyTimers() {}
- @Override
- public void cancelKeyTimersOf(PointerTracker tracker) {}
- @Override
- public void startDoubleTapShiftKeyTimer() {}
- @Override
- public void cancelDoubleTapShiftKeyTimer() {}
- @Override
- public boolean isInDoubleTapShiftKeyTimeout() { return false; }
- @Override
- public void startUpdateBatchInputTimer(PointerTracker tracker) {}
- @Override
- public void cancelUpdateBatchInputTimer(PointerTracker tracker) {}
- @Override
- public void cancelAllUpdateBatchInputTimers() {}
- }
- }
-
static final class PointerTrackerParams {
public final boolean mKeySelectionByDraggingFinger;
public final int mTouchNoiseThresholdTime;
@@ -586,7 +528,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element,
}
sListener.onStartBatchInput();
dismissAllMoreKeysPanels();
- sTimerProxy.cancelLongPressTimerOf(this);
+ sTimerProxy.cancelLongPressTimersOf(this);
}
private void showGestureTrail() {
@@ -1094,7 +1036,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element,
}
public void cancelLongPressTimer() {
- sTimerProxy.cancelLongPressTimerOf(this);
+ sTimerProxy.cancelLongPressTimersOf(this);
}
public void onLongPressed() {
@@ -1163,7 +1105,7 @@ public final class PointerTracker implements PointerTrackerQueue.Element,
private void startLongPressTimer(final Key key) {
// Note that we need to cancel all active long press shift key timers if any whenever we
// start a new long press timer for both non-shift and shift keys.
- sTimerProxy.cancelLongPressShiftKeyTimers();
+ sTimerProxy.cancelLongPressShiftKeyTimer();
if (sInGesture) return;
if (key == null) return;
if (!key.isLongPressEnabled()) return;
diff --git a/java/src/com/android/inputmethod/keyboard/emoji/EmojiPageKeyboardView.java b/java/src/com/android/inputmethod/keyboard/emoji/EmojiPageKeyboardView.java
index 54d3e3b88..09313f811 100644
--- a/java/src/com/android/inputmethod/keyboard/emoji/EmojiPageKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/emoji/EmojiPageKeyboardView.java
@@ -148,7 +148,7 @@ final class EmojiPageKeyboardView extends KeyboardView implements
void callListenerOnPressKey(final Key pressedKey) {
mPendingKeyDown = null;
- pressedKey.onReleased();
+ pressedKey.onPressed();
invalidateKey(pressedKey);
mListener.onPressKey(pressedKey);
}
diff --git a/java/src/com/android/inputmethod/keyboard/emoji/EmojiPalettesView.java b/java/src/com/android/inputmethod/keyboard/emoji/EmojiPalettesView.java
index 06184f8d2..cf4dd3db3 100644
--- a/java/src/com/android/inputmethod/keyboard/emoji/EmojiPalettesView.java
+++ b/java/src/com/android/inputmethod/keyboard/emoji/EmojiPalettesView.java
@@ -48,7 +48,7 @@ import com.android.inputmethod.keyboard.internal.KeyVisualAttributes;
import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
import com.android.inputmethod.latin.AudioAndHapticFeedbackManager;
import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.SubtypeSwitcher;
+import com.android.inputmethod.latin.RichInputMethodSubtype;
import com.android.inputmethod.latin.common.Constants;
import com.android.inputmethod.latin.utils.ResourceUtils;
@@ -113,7 +113,7 @@ public final class EmojiPalettesView extends LinearLayout implements OnTabChange
context, null /* editorInfo */);
final Resources res = context.getResources();
mEmojiLayoutParams = new EmojiLayoutParams(res);
- builder.setSubtype(SubtypeSwitcher.getInstance().getEmojiSubtype());
+ builder.setSubtype(RichInputMethodSubtype.getEmojiSubtype());
builder.setKeyboardGeometry(ResourceUtils.getDefaultKeyboardWidth(res),
mEmojiLayoutParams.mEmojiKeyboardHeight);
final KeyboardLayoutSet layoutSet = builder.build();
diff --git a/java/src/com/android/inputmethod/keyboard/internal/DrawingPreviewPlacerView.java b/java/src/com/android/inputmethod/keyboard/internal/DrawingPreviewPlacerView.java
index a5d47adb3..9c0d7436b 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/DrawingPreviewPlacerView.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/DrawingPreviewPlacerView.java
@@ -24,7 +24,7 @@ import android.graphics.PorterDuffXfermode;
import android.util.AttributeSet;
import android.widget.RelativeLayout;
-import com.android.inputmethod.latin.utils.CoordinateUtils;
+import com.android.inputmethod.latin.common.CoordinateUtils;
import java.util.ArrayList;
diff --git a/java/src/com/android/inputmethod/keyboard/internal/DrawingProxy.java b/java/src/com/android/inputmethod/keyboard/internal/DrawingProxy.java
new file mode 100644
index 000000000..7fc586a0f
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/internal/DrawingProxy.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.internal;
+
+import com.android.inputmethod.keyboard.Key;
+import com.android.inputmethod.keyboard.PointerTracker;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+public interface DrawingProxy {
+ // TODO: Remove this method.
+ public void invalidateKey(@Nullable Key key);
+
+ // TODO: Rename this method to onKeyPressed.
+ public void showKeyPreview(@Nonnull Key key);
+
+ // TODO: Rename this method to onKeyReleased.
+ public void dismissKeyPreview(@Nonnull Key key);
+
+ /**
+ * Dismiss a key preview visual without delay.
+ * @param key the key whose preview visual should be dismissed.
+ */
+ public void dismissKeyPreviewWithoutDelay(@Nonnull Key key);
+
+ // TODO: Rename this method to onKeyLongPressed.
+ public void onLongPress(@Nonnull PointerTracker tracker);
+
+ /**
+ * Start a while-typing-animation.
+ * @param fadeInOrOut {@link #FADE_IN} starts while-typing-fade-in animation.
+ * {@link #FADE_OUT} starts while-typing-fade-out animation.
+ */
+ public void startWhileTypingAnimation(int fadeInOrOut);
+ public static final int FADE_IN = 0;
+ public static final int FADE_OUT = 1;
+
+ /**
+ * Show sliding-key input preview.
+ * @param tracker the {@link PointerTracker} that is currently doing the sliding-key input.
+ * null to dismiss the sliding-key input preview.
+ */
+ public void showSlidingKeyInputPreview(@Nullable PointerTracker tracker);
+
+ /**
+ * Show gesture trails.
+ * @param tracker the {@link PointerTracker} whose gesture trail will be shown.
+ * @param showsFloatingPreviewText when true, a gesture floating preview text will be shown
+ * with this <code>tracker</code>'s trail.
+ */
+ public void showGestureTrail(@Nonnull PointerTracker tracker, boolean showsFloatingPreviewText);
+
+ /**
+ * Dismiss a gesture floating preview text without delay.
+ */
+ public void dismissGestureFloatingPreviewTextWithoutDelay();
+}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/GestureFloatingTextDrawingPreview.java b/java/src/com/android/inputmethod/keyboard/internal/GestureFloatingTextDrawingPreview.java
index 330ec52f0..5443c2a8c 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/GestureFloatingTextDrawingPreview.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/GestureFloatingTextDrawingPreview.java
@@ -27,7 +27,7 @@ import android.text.TextUtils;
import com.android.inputmethod.keyboard.PointerTracker;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.SuggestedWords;
-import com.android.inputmethod.latin.utils.CoordinateUtils;
+import com.android.inputmethod.latin.common.CoordinateUtils;
import javax.annotation.Nonnull;
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java b/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java
index d3764877c..448f1b4b1 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyPreviewChoreographer.java
@@ -23,7 +23,7 @@ import android.view.View;
import android.view.ViewGroup;
import com.android.inputmethod.keyboard.Key;
-import com.android.inputmethod.latin.utils.CoordinateUtils;
+import com.android.inputmethod.latin.common.CoordinateUtils;
import com.android.inputmethod.latin.utils.ViewLayoutUtils;
import java.util.ArrayDeque;
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
index c739bf3e0..51f89c122 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardBuilder.java
@@ -36,7 +36,6 @@ import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.common.Constants;
import com.android.inputmethod.latin.common.StringUtils;
import com.android.inputmethod.latin.utils.ResourceUtils;
-import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
import com.android.inputmethod.latin.utils.XmlParseUtils;
import com.android.inputmethod.latin.utils.XmlParseUtils.ParseException;
@@ -648,7 +647,7 @@ public class KeyboardBuilder<KP extends KeyboardParams> {
try {
final boolean keyboardLayoutSetMatched = matchString(caseAttr,
R.styleable.Keyboard_Case_keyboardLayoutSet,
- SubtypeLocaleUtils.getKeyboardLayoutSetName(id.mSubtype));
+ id.mSubtype.getKeyboardLayoutSetName());
final boolean keyboardLayoutSetElementMatched = matchTypedValue(caseAttr,
R.styleable.Keyboard_Case_keyboardLayoutSetElement, id.mElementId,
KeyboardId.elementIdToName(id.mElementId));
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java
index cc28e7ac8..70e116709 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardState.java
@@ -21,6 +21,7 @@ import android.util.Log;
import com.android.inputmethod.event.Event;
import com.android.inputmethod.latin.common.Constants;
+import com.android.inputmethod.latin.utils.CapsModeUtils;
import com.android.inputmethod.latin.utils.RecapitalizeStatus;
/**
@@ -38,9 +39,11 @@ import com.android.inputmethod.latin.utils.RecapitalizeStatus;
public final class KeyboardState {
private static final String TAG = KeyboardState.class.getSimpleName();
private static final boolean DEBUG_EVENT = false;
- private static final boolean DEBUG_ACTION = false;
+ private static final boolean DEBUG_INTERNAL_ACTION = false;
public interface SwitchActions {
+ public static final boolean DEBUG_ACTION = false;
+
public void setAlphabetKeyboard();
public void setAlphabetManualShiftedKeyboard();
public void setAlphabetAutomaticShiftedKeyboard();
@@ -53,8 +56,9 @@ public final class KeyboardState {
/**
* Request to call back {@link KeyboardState#onUpdateShiftState(int, int)}.
*/
- public void requestUpdatingShiftState(final int currentAutoCapsState,
- final int currentRecapitalizeState);
+ public void requestUpdatingShiftState(final int autoCapsFlags, final int recapitalizeMode);
+
+ public static final boolean DEBUG_TIMER_ACTION = false;
public void startDoubleTapShiftKeyTimer();
public boolean isInDoubleTapShiftKeyTimeout();
@@ -119,10 +123,9 @@ public final class KeyboardState {
mRecapitalizeMode = RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE;
}
- public void onLoadKeyboard(final int currentAutoCapsState,
- final int currentRecapitalizeState) {
+ public void onLoadKeyboard(final int autoCapsFlags, final int recapitalizeMode) {
if (DEBUG_EVENT) {
- Log.d(TAG, "onLoadKeyboard: " + this);
+ Log.d(TAG, "onLoadKeyboard: " + stateToString(autoCapsFlags, recapitalizeMode));
}
// Reset alphabet shift state.
mAlphabetShiftState.setShiftLocked(false);
@@ -130,7 +133,7 @@ public final class KeyboardState {
mPrevSymbolsKeyboardWasShifted = false;
mShiftKeyState.onRelease();
mSymbolKeyState.onRelease();
- onRestoreKeyboardState(currentAutoCapsState, currentRecapitalizeState);
+ onRestoreKeyboardState(autoCapsFlags, recapitalizeMode);
}
private static final int UNSHIFT = 0;
@@ -156,14 +159,14 @@ public final class KeyboardState {
}
}
- private void onRestoreKeyboardState(final int currentAutoCapsState,
- final int currentRecapitalizeState) {
+ private void onRestoreKeyboardState(final int autoCapsFlags, final int recapitalizeMode) {
final SavedKeyboardState state = mSavedKeyboardState;
if (DEBUG_EVENT) {
- Log.d(TAG, "onRestoreKeyboardState: saved=" + state + " " + this);
+ Log.d(TAG, "onRestoreKeyboardState: saved=" + state
+ + " " + stateToString(autoCapsFlags, recapitalizeMode));
}
if (!state.mIsValid || state.mIsAlphabetMode) {
- setAlphabetKeyboard(currentAutoCapsState, currentRecapitalizeState);
+ setAlphabetKeyboard(autoCapsFlags, recapitalizeMode);
} else if (state.mIsEmojiMode) {
setEmojiKeyboard();
} else {
@@ -188,7 +191,7 @@ public final class KeyboardState {
}
private void setShifted(final int shiftMode) {
- if (DEBUG_ACTION) {
+ if (DEBUG_INTERNAL_ACTION) {
Log.d(TAG, "setShifted: shiftMode=" + shiftModeToString(shiftMode) + " " + this);
}
if (!mIsAlphabetMode) return;
@@ -227,7 +230,7 @@ public final class KeyboardState {
}
private void setShiftLocked(final boolean shiftLocked) {
- if (DEBUG_ACTION) {
+ if (DEBUG_INTERNAL_ACTION) {
Log.d(TAG, "setShiftLocked: shiftLocked=" + shiftLocked + " " + this);
}
if (!mIsAlphabetMode) return;
@@ -241,10 +244,10 @@ public final class KeyboardState {
mAlphabetShiftState.setShiftLocked(shiftLocked);
}
- private void toggleAlphabetAndSymbols(final int currentAutoCapsState,
- final int currentRecapitalizeState) {
- if (DEBUG_ACTION) {
- Log.d(TAG, "toggleAlphabetAndSymbols: " + this);
+ private void toggleAlphabetAndSymbols(final int autoCapsFlags, final int recapitalizeMode) {
+ if (DEBUG_INTERNAL_ACTION) {
+ Log.d(TAG, "toggleAlphabetAndSymbols: "
+ + stateToString(autoCapsFlags, recapitalizeMode));
}
if (mIsAlphabetMode) {
mPrevMainKeyboardWasShiftLocked = mAlphabetShiftState.isShiftLocked();
@@ -256,7 +259,7 @@ public final class KeyboardState {
mPrevSymbolsKeyboardWasShifted = false;
} else {
mPrevSymbolsKeyboardWasShifted = mIsSymbolShifted;
- setAlphabetKeyboard(currentAutoCapsState, currentRecapitalizeState);
+ setAlphabetKeyboard(autoCapsFlags, recapitalizeMode);
if (mPrevMainKeyboardWasShiftLocked) {
setShiftLocked(true);
}
@@ -266,15 +269,15 @@ public final class KeyboardState {
// TODO: Remove this method. Come up with a more comprehensive way to reset the keyboard layout
// when a keyboard layout set doesn't get reloaded in LatinIME.onStartInputViewInternal().
- private void resetKeyboardStateToAlphabet(final int currentAutoCapsState,
- final int currentRecapitalizeState) {
- if (DEBUG_ACTION) {
- Log.d(TAG, "resetKeyboardStateToAlphabet: " + this);
+ private void resetKeyboardStateToAlphabet(final int autoCapsFlags, final int recapitalizeMode) {
+ if (DEBUG_INTERNAL_ACTION) {
+ Log.d(TAG, "resetKeyboardStateToAlphabet: "
+ + stateToString(autoCapsFlags, recapitalizeMode));
}
if (mIsAlphabetMode) return;
mPrevSymbolsKeyboardWasShifted = mIsSymbolShifted;
- setAlphabetKeyboard(currentAutoCapsState, currentRecapitalizeState);
+ setAlphabetKeyboard(autoCapsFlags, recapitalizeMode);
if (mPrevMainKeyboardWasShiftLocked) {
setShiftLocked(true);
}
@@ -289,10 +292,9 @@ public final class KeyboardState {
}
}
- private void setAlphabetKeyboard(final int currentAutoCapsState,
- final int currentRecapitalizeState) {
- if (DEBUG_ACTION) {
- Log.d(TAG, "setAlphabetKeyboard");
+ private void setAlphabetKeyboard(final int autoCapsFlags, final int recapitalizeMode) {
+ if (DEBUG_INTERNAL_ACTION) {
+ Log.d(TAG, "setAlphabetKeyboard: " + stateToString(autoCapsFlags, recapitalizeMode));
}
mSwitchActions.setAlphabetKeyboard();
@@ -301,11 +303,11 @@ public final class KeyboardState {
mIsSymbolShifted = false;
mRecapitalizeMode = RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE;
mSwitchState = SWITCH_STATE_ALPHA;
- mSwitchActions.requestUpdatingShiftState(currentAutoCapsState, currentRecapitalizeState);
+ mSwitchActions.requestUpdatingShiftState(autoCapsFlags, recapitalizeMode);
}
private void setSymbolsKeyboard() {
- if (DEBUG_ACTION) {
+ if (DEBUG_INTERNAL_ACTION) {
Log.d(TAG, "setSymbolsKeyboard");
}
mSwitchActions.setSymbolsKeyboard();
@@ -318,7 +320,7 @@ public final class KeyboardState {
}
private void setSymbolsShiftedKeyboard() {
- if (DEBUG_ACTION) {
+ if (DEBUG_INTERNAL_ACTION) {
Log.d(TAG, "setSymbolsShiftedKeyboard");
}
mSwitchActions.setSymbolsShiftedKeyboard();
@@ -331,7 +333,7 @@ public final class KeyboardState {
}
private void setEmojiKeyboard() {
- if (DEBUG_ACTION) {
+ if (DEBUG_INTERNAL_ACTION) {
Log.d(TAG, "setEmojiKeyboard");
}
mIsAlphabetMode = false;
@@ -343,11 +345,12 @@ public final class KeyboardState {
mSwitchActions.setEmojiKeyboard();
}
- public void onPressKey(final int code, final boolean isSinglePointer,
- final int currentAutoCapsState, final int currentRecapitalizeState) {
+ public void onPressKey(final int code, final boolean isSinglePointer, final int autoCapsFlags,
+ final int recapitalizeMode) {
if (DEBUG_EVENT) {
- Log.d(TAG, "onPressKey: code=" + Constants.printableCode(code) + " single="
- + isSinglePointer + " autoCaps=" + currentAutoCapsState + " " + this);
+ Log.d(TAG, "onPressKey: code=" + Constants.printableCode(code)
+ + " single=" + isSinglePointer
+ + " " + stateToString(autoCapsFlags, recapitalizeMode));
}
if (code != Constants.CODE_SHIFT) {
// Because the double tap shift key timer is to detect two consecutive shift key press,
@@ -359,7 +362,7 @@ public final class KeyboardState {
} else if (code == Constants.CODE_CAPSLOCK) {
// Nothing to do here. See {@link #onReleaseKey(int,boolean)}.
} else if (code == Constants.CODE_SWITCH_ALPHA_SYMBOL) {
- onPressSymbol(currentAutoCapsState, currentRecapitalizeState);
+ onPressSymbol(autoCapsFlags, recapitalizeMode);
} else {
mShiftKeyState.onOtherKeyPressed();
mSymbolKeyState.onOtherKeyPressed();
@@ -372,7 +375,7 @@ public final class KeyboardState {
// off because, for example, we may be in the #1 state within the manual temporary
// shifted mode.
if (!isSinglePointer && mIsAlphabetMode
- && currentAutoCapsState != TextUtils.CAP_MODE_CHARACTERS) {
+ && autoCapsFlags != TextUtils.CAP_MODE_CHARACTERS) {
final boolean needsToResetAutoCaps = mAlphabetShiftState.isAutomaticShifted()
|| (mAlphabetShiftState.isManualShifted() && mShiftKeyState.isReleasing());
if (needsToResetAutoCaps) {
@@ -382,34 +385,35 @@ public final class KeyboardState {
}
}
- public void onReleaseKey(final int code, final boolean withSliding,
- final int currentAutoCapsState, final int currentRecapitalizeState) {
+ public void onReleaseKey(final int code, final boolean withSliding, final int autoCapsFlags,
+ final int recapitalizeMode) {
if (DEBUG_EVENT) {
Log.d(TAG, "onReleaseKey: code=" + Constants.printableCode(code)
- + " sliding=" + withSliding + " " + this);
+ + " sliding=" + withSliding
+ + " " + stateToString(autoCapsFlags, recapitalizeMode));
}
if (code == Constants.CODE_SHIFT) {
- onReleaseShift(withSliding, currentAutoCapsState, currentRecapitalizeState);
+ onReleaseShift(withSliding, autoCapsFlags, recapitalizeMode);
} else if (code == Constants.CODE_CAPSLOCK) {
setShiftLocked(!mAlphabetShiftState.isShiftLocked());
} else if (code == Constants.CODE_SWITCH_ALPHA_SYMBOL) {
- onReleaseSymbol(withSliding, currentAutoCapsState, currentRecapitalizeState);
+ onReleaseSymbol(withSliding, autoCapsFlags, recapitalizeMode);
}
}
- private void onPressSymbol(final int currentAutoCapsState,
- final int currentRecapitalizeState) {
- toggleAlphabetAndSymbols(currentAutoCapsState, currentRecapitalizeState);
+ private void onPressSymbol(final int autoCapsFlags,
+ final int recapitalizeMode) {
+ toggleAlphabetAndSymbols(autoCapsFlags, recapitalizeMode);
mSymbolKeyState.onPress();
mSwitchState = SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL;
}
- private void onReleaseSymbol(final boolean withSliding, final int currentAutoCapsState,
- final int currentRecapitalizeState) {
+ private void onReleaseSymbol(final boolean withSliding, final int autoCapsFlags,
+ final int recapitalizeMode) {
if (mSymbolKeyState.isChording()) {
// Switch back to the previous keyboard mode if the user chords the mode change key and
// another key, then releases the mode change key.
- toggleAlphabetAndSymbols(currentAutoCapsState, currentRecapitalizeState);
+ toggleAlphabetAndSymbols(autoCapsFlags, recapitalizeMode);
} else if (!withSliding) {
// If the mode change key is being released without sliding, we should forget the
// previous symbols keyboard shift state and simply switch back to symbols layout
@@ -419,23 +423,23 @@ public final class KeyboardState {
mSymbolKeyState.onRelease();
}
- public void onUpdateShiftState(final int autoCaps, final int recapitalizeMode) {
+ public void onUpdateShiftState(final int autoCapsFlags, final int recapitalizeMode) {
if (DEBUG_EVENT) {
- Log.d(TAG, "onUpdateShiftState: autoCaps=" + autoCaps + ", recapitalizeMode="
- + recapitalizeMode + " " + this);
+ Log.d(TAG, "onUpdateShiftState: " + stateToString(autoCapsFlags, recapitalizeMode));
}
mRecapitalizeMode = recapitalizeMode;
- updateAlphabetShiftState(autoCaps, recapitalizeMode);
+ updateAlphabetShiftState(autoCapsFlags, recapitalizeMode);
}
// TODO: Remove this method. Come up with a more comprehensive way to reset the keyboard layout
// when a keyboard layout set doesn't get reloaded in LatinIME.onStartInputViewInternal().
- public void onResetKeyboardStateToAlphabet(final int currentAutoCapsState,
- final int currentRecapitalizeState) {
+ public void onResetKeyboardStateToAlphabet(final int autoCapsFlags,
+ final int recapitalizeMode) {
if (DEBUG_EVENT) {
- Log.d(TAG, "onResetKeyboardStateToAlphabet: " + this);
+ Log.d(TAG, "onResetKeyboardStateToAlphabet: "
+ + stateToString(autoCapsFlags, recapitalizeMode));
}
- resetKeyboardStateToAlphabet(currentAutoCapsState, currentRecapitalizeState);
+ resetKeyboardStateToAlphabet(autoCapsFlags, recapitalizeMode);
}
private void updateShiftStateForRecapitalize(final int recapitalizeMode) {
@@ -453,7 +457,7 @@ public final class KeyboardState {
}
}
- private void updateAlphabetShiftState(final int autoCaps, final int recapitalizeMode) {
+ private void updateAlphabetShiftState(final int autoCapsFlags, final int recapitalizeMode) {
if (!mIsAlphabetMode) return;
if (RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE != recapitalizeMode) {
// We are recapitalizing. Match the keyboard to the current recapitalize state.
@@ -466,7 +470,7 @@ public final class KeyboardState {
return;
}
if (!mAlphabetShiftState.isShiftLocked() && !mShiftKeyState.isIgnoring()) {
- if (mShiftKeyState.isReleasing() && autoCaps != Constants.TextUtils.CAP_MODE_OFF) {
+ if (mShiftKeyState.isReleasing() && autoCapsFlags != Constants.TextUtils.CAP_MODE_OFF) {
// Only when shift key is releasing, automatic temporary upper case will be set.
setShifted(AUTOMATIC_SHIFT);
} else {
@@ -526,8 +530,8 @@ public final class KeyboardState {
}
}
- private void onReleaseShift(final boolean withSliding, final int currentAutoCapsState,
- final int currentRecapitalizeState) {
+ private void onReleaseShift(final boolean withSliding, final int autoCapsFlags,
+ final int recapitalizeMode) {
if (RecapitalizeStatus.NOT_A_RECAPITALIZE_MODE != mRecapitalizeMode) {
// We are recapitalizing. We should match the keyboard state to the recapitalize
// state in priority.
@@ -550,8 +554,7 @@ public final class KeyboardState {
// After chording input, automatic shift state may have been changed depending on
// what characters were input.
mShiftKeyState.onRelease();
- mSwitchActions.requestUpdatingShiftState(currentAutoCapsState,
- currentRecapitalizeState);
+ mSwitchActions.requestUpdatingShiftState(autoCapsFlags, recapitalizeMode);
return;
} else if (mAlphabetShiftState.isShiftLockShifted() && withSliding) {
// In shift locked state, shift has been pressed and slid out to other key.
@@ -588,21 +591,20 @@ public final class KeyboardState {
mShiftKeyState.onRelease();
}
- public void onFinishSlidingInput(final int currentAutoCapsState,
- final int currentRecapitalizeState) {
+ public void onFinishSlidingInput(final int autoCapsFlags, final int recapitalizeMode) {
if (DEBUG_EVENT) {
- Log.d(TAG, "onFinishSlidingInput: " + this);
+ Log.d(TAG, "onFinishSlidingInput: " + stateToString(autoCapsFlags, recapitalizeMode));
}
// Switch back to the previous keyboard mode if the user cancels sliding input.
switch (mSwitchState) {
case SWITCH_STATE_MOMENTARY_ALPHA_AND_SYMBOL:
- toggleAlphabetAndSymbols(currentAutoCapsState, currentRecapitalizeState);
+ toggleAlphabetAndSymbols(autoCapsFlags, recapitalizeMode);
break;
case SWITCH_STATE_MOMENTARY_SYMBOL_AND_MORE:
toggleShiftInSymbols();
break;
case SWITCH_STATE_MOMENTARY_ALPHA_SHIFT:
- setAlphabetKeyboard(currentAutoCapsState, currentRecapitalizeState);
+ setAlphabetKeyboard(autoCapsFlags, recapitalizeMode);
break;
}
}
@@ -611,12 +613,11 @@ public final class KeyboardState {
return c == Constants.CODE_SPACE || c == Constants.CODE_ENTER;
}
- public void onEvent(final Event event, final int currentAutoCapsState,
- final int currentRecapitalizeState) {
+ public void onEvent(final Event event, final int autoCapsFlags, final int recapitalizeMode) {
final int code = event.isFunctionalKeyEvent() ? event.mKeyCode : event.mCodePoint;
if (DEBUG_EVENT) {
Log.d(TAG, "onEvent: code=" + Constants.printableCode(code)
- + " autoCaps=" + currentAutoCapsState + " " + this);
+ + " " + stateToString(autoCapsFlags, recapitalizeMode));
}
switch (mSwitchState) {
@@ -652,7 +653,7 @@ public final class KeyboardState {
// Switch back to alpha keyboard mode if user types one or more non-space/enter
// characters followed by a space/enter.
if (isSpaceOrEnter(code)) {
- toggleAlphabetAndSymbols(currentAutoCapsState, currentRecapitalizeState);
+ toggleAlphabetAndSymbols(autoCapsFlags, recapitalizeMode);
mPrevSymbolsKeyboardWasShifted = false;
}
break;
@@ -660,11 +661,11 @@ public final class KeyboardState {
// If the code is a letter, update keyboard shift state.
if (Constants.isLetterCode(code)) {
- updateAlphabetShiftState(currentAutoCapsState, currentRecapitalizeState);
+ updateAlphabetShiftState(autoCapsFlags, recapitalizeMode);
} else if (code == Constants.CODE_EMOJI) {
setEmojiKeyboard();
} else if (code == Constants.CODE_ALPHA_FROM_EMOJI) {
- setAlphabetKeyboard(currentAutoCapsState, currentRecapitalizeState);
+ setAlphabetKeyboard(autoCapsFlags, recapitalizeMode);
}
}
@@ -697,4 +698,9 @@ public final class KeyboardState {
+ " symbol=" + mSymbolKeyState
+ " switch=" + switchStateToString(mSwitchState) + "]";
}
+
+ private String stateToString(final int autoCapsFlags, final int recapitalizeMode) {
+ return this + " autoCapsFlags=" + CapsModeUtils.flagsToString(autoCapsFlags)
+ + " recapitalizeMode=" + RecapitalizeStatus.modeToString(recapitalizeMode);
+ }
}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/LanguageOnSpacebarHelper.java b/java/src/com/android/inputmethod/keyboard/internal/LanguageOnSpacebarHelper.java
index 21eaed950..8ed80107a 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/LanguageOnSpacebarHelper.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/LanguageOnSpacebarHelper.java
@@ -25,6 +25,8 @@ import java.util.Collections;
import java.util.List;
import java.util.Locale;
+import javax.annotation.Nonnull;
+
/**
* This class determines that the language name on the spacebar should be displayed in what format.
*/
@@ -37,7 +39,7 @@ public final class LanguageOnSpacebarHelper {
private List<InputMethodSubtype> mEnabledSubtypes = Collections.emptyList();
private boolean mIsSystemLanguageSameAsInputLanguage;
- public int getLanguageOnSpacebarFormatType(final RichInputMethodSubtype subtype) {
+ public int getLanguageOnSpacebarFormatType(@Nonnull final RichInputMethodSubtype subtype) {
if (subtype.isNoLanguage()) {
return FORMAT_TYPE_FULL_LOCALE;
}
@@ -50,7 +52,7 @@ public final class LanguageOnSpacebarHelper {
return FORMAT_TYPE_MULTIPLE;
}
final String keyboardLanguage = locales[0].getLanguage();
- final String keyboardLayout = SubtypeLocaleUtils.getKeyboardLayoutSetName(subtype);
+ final String keyboardLayout = subtype.getKeyboardLayoutSetName();
int sameLanguageAndLayoutCount = 0;
for (final InputMethodSubtype ims : mEnabledSubtypes) {
final String language = SubtypeLocaleUtils.getSubtypeLocale(ims).getLanguage();
@@ -65,11 +67,30 @@ public final class LanguageOnSpacebarHelper {
: FORMAT_TYPE_LANGUAGE_ONLY;
}
- public void updateEnabledSubtypes(final List<InputMethodSubtype> enabledSubtypes) {
+ public void onUpdateEnabledSubtypes(@Nonnull final List<InputMethodSubtype> enabledSubtypes) {
mEnabledSubtypes = enabledSubtypes;
}
- public void updateIsSystemLanguageSameAsInputLanguage(final boolean isSame) {
- mIsSystemLanguageSameAsInputLanguage = isSame;
+ public void onSubtypeChanged(@Nonnull final RichInputMethodSubtype subtype,
+ final boolean implicitlyEnabledSubtype, @Nonnull final Locale systemLocale) {
+ final Locale[] newLocales = subtype.getLocales();
+ if (newLocales.length > 1) {
+ // In multi-locales mode, the system language is never the same as the input language
+ // because there is no single input language.
+ mIsSystemLanguageSameAsInputLanguage = false;
+ return;
+ }
+ final Locale newLocale = newLocales[0];
+ if (systemLocale.equals(newLocale)) {
+ mIsSystemLanguageSameAsInputLanguage = true;
+ return;
+ }
+ if (!systemLocale.getLanguage().equals(newLocale.getLanguage())) {
+ mIsSystemLanguageSameAsInputLanguage = false;
+ return;
+ }
+ // If the subtype is enabled explicitly, the language name should be displayed even when
+ // the keyboard language and the system language are equal.
+ mIsSystemLanguageSameAsInputLanguage = implicitlyEnabledSubtype;
}
}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java b/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java
index a0bb406aa..b1a3887d8 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/MoreKeySpec.java
@@ -21,10 +21,10 @@ import android.util.SparseIntArray;
import com.android.inputmethod.compat.CharacterCompat;
import com.android.inputmethod.keyboard.Key;
+import com.android.inputmethod.latin.common.CollectionUtils;
import com.android.inputmethod.latin.common.Constants;
import com.android.inputmethod.latin.common.StringUtils;
import com.android.inputmethod.latin.define.DebugFlags;
-import com.android.inputmethod.latin.utils.CollectionUtils;
import java.util.ArrayList;
import java.util.Arrays;
diff --git a/java/src/com/android/inputmethod/keyboard/internal/NonDistinctMultitouchHelper.java b/java/src/com/android/inputmethod/keyboard/internal/NonDistinctMultitouchHelper.java
index 3a9aa81a3..8a375c620 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/NonDistinctMultitouchHelper.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/NonDistinctMultitouchHelper.java
@@ -22,7 +22,7 @@ import android.view.MotionEvent;
import com.android.inputmethod.keyboard.Key;
import com.android.inputmethod.keyboard.KeyDetector;
import com.android.inputmethod.keyboard.PointerTracker;
-import com.android.inputmethod.latin.utils.CoordinateUtils;
+import com.android.inputmethod.latin.common.CoordinateUtils;
public final class NonDistinctMultitouchHelper {
private static final String TAG = NonDistinctMultitouchHelper.class.getSimpleName();
diff --git a/java/src/com/android/inputmethod/keyboard/internal/SlidingKeyInputDrawingPreview.java b/java/src/com/android/inputmethod/keyboard/internal/SlidingKeyInputDrawingPreview.java
index ef4c74d61..73a6f9516 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/SlidingKeyInputDrawingPreview.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/SlidingKeyInputDrawingPreview.java
@@ -23,7 +23,7 @@ import android.graphics.Path;
import com.android.inputmethod.keyboard.PointerTracker;
import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.utils.CoordinateUtils;
+import com.android.inputmethod.latin.common.CoordinateUtils;
/**
* Draw rubber band preview graphics during sliding key input.
diff --git a/java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java b/java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java
index 456522535..8068427bc 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/TimerHandler.java
@@ -22,8 +22,6 @@ import android.view.ViewConfiguration;
import com.android.inputmethod.keyboard.Key;
import com.android.inputmethod.keyboard.PointerTracker;
-import com.android.inputmethod.keyboard.PointerTracker.DrawingProxy;
-import com.android.inputmethod.keyboard.PointerTracker.TimerProxy;
import com.android.inputmethod.latin.common.Constants;
import com.android.inputmethod.latin.utils.LeakGuardHandlerWrapper;
@@ -86,7 +84,7 @@ public final class TimerHandler extends LeakGuardHandlerWrapper<DrawingProxy>
}
@Override
- public void startKeyRepeatTimerOf(final PointerTracker tracker, final int repeatCount,
+ public void startKeyRepeatTimerOf(@Nonnull final PointerTracker tracker, final int repeatCount,
final int delay) {
final Key key = tracker.getKey();
if (key == null || delay == 0) {
@@ -110,7 +108,7 @@ public final class TimerHandler extends LeakGuardHandlerWrapper<DrawingProxy>
}
@Override
- public void startLongPressTimerOf(final PointerTracker tracker, final int delay) {
+ public void startLongPressTimerOf(@Nonnull final PointerTracker tracker, final int delay) {
final Key key = tracker.getKey();
if (key == null) {
return;
@@ -123,13 +121,13 @@ public final class TimerHandler extends LeakGuardHandlerWrapper<DrawingProxy>
}
@Override
- public void cancelLongPressTimerOf(final PointerTracker tracker) {
+ public void cancelLongPressTimersOf(@Nonnull final PointerTracker tracker) {
removeMessages(MSG_LONGPRESS_KEY, tracker);
removeMessages(MSG_LONGPRESS_SHIFT_KEY, tracker);
}
@Override
- public void cancelLongPressShiftKeyTimers() {
+ public void cancelLongPressShiftKeyTimer() {
removeMessages(MSG_LONGPRESS_SHIFT_KEY);
}
@@ -139,7 +137,7 @@ public final class TimerHandler extends LeakGuardHandlerWrapper<DrawingProxy>
}
@Override
- public void startTypingStateTimer(final Key typedKey) {
+ public void startTypingStateTimer(@Nonnull final Key typedKey) {
if (typedKey.isModifier() || typedKey.altCodeWhileTyping()) {
return;
}
@@ -190,9 +188,9 @@ public final class TimerHandler extends LeakGuardHandlerWrapper<DrawingProxy>
}
@Override
- public void cancelKeyTimersOf(final PointerTracker tracker) {
+ public void cancelKeyTimersOf(@Nonnull final PointerTracker tracker) {
cancelKeyRepeatTimerOf(tracker);
- cancelLongPressTimerOf(tracker);
+ cancelLongPressTimersOf(tracker);
}
public void cancelAllKeyTimers() {
@@ -201,7 +199,7 @@ public final class TimerHandler extends LeakGuardHandlerWrapper<DrawingProxy>
}
@Override
- public void startUpdateBatchInputTimer(final PointerTracker tracker) {
+ public void startUpdateBatchInputTimer(@Nonnull final PointerTracker tracker) {
if (mGestureRecognitionUpdateTime <= 0) {
return;
}
@@ -211,7 +209,7 @@ public final class TimerHandler extends LeakGuardHandlerWrapper<DrawingProxy>
}
@Override
- public void cancelUpdateBatchInputTimer(final PointerTracker tracker) {
+ public void cancelUpdateBatchInputTimer(@Nonnull final PointerTracker tracker) {
removeMessages(MSG_UPDATE_BATCH_INPUT, tracker);
}
diff --git a/java/src/com/android/inputmethod/keyboard/internal/TimerProxy.java b/java/src/com/android/inputmethod/keyboard/internal/TimerProxy.java
new file mode 100644
index 000000000..0ce3de8d9
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/internal/TimerProxy.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.internal;
+
+import com.android.inputmethod.keyboard.Key;
+import com.android.inputmethod.keyboard.PointerTracker;
+
+import javax.annotation.Nonnull;
+
+public interface TimerProxy {
+ /**
+ * Start a timer to detect if a user is typing keys.
+ * @param typedKey the key that is typed.
+ */
+ public void startTypingStateTimer(@Nonnull Key typedKey);
+
+ /**
+ * Check if a user is key typing.
+ * @return true if a user is in typing.
+ */
+ public boolean isTypingState();
+
+ /**
+ * Start a timer to simulate repeated key presses while a user keep pressing a key.
+ * @param tracker the {@link PointerTracker} that points the key to be repeated.
+ * @param repeatCount the number of times that the key is repeating. Starting from 1.
+ * @param delay the interval delay to the next key repeat, in millisecond.
+ */
+ public void startKeyRepeatTimerOf(@Nonnull PointerTracker tracker, int repeatCount, int delay);
+
+ /**
+ * Start a timer to detect a long pressed key.
+ * If a key pointed by <code>tracker</code> is a shift key, start another timer to detect
+ * long pressed shift key.
+ * @param tracker the {@link PointerTracker} that starts long pressing.
+ * @param delay the delay to fire the long press timer, in millisecond.
+ */
+ public void startLongPressTimerOf(@Nonnull PointerTracker tracker, int delay);
+
+ /**
+ * Cancel timers for detecting a long pressed key and a long press shift key.
+ * @param tracker cancel long press timers of this {@link PointerTracker}.
+ */
+ public void cancelLongPressTimersOf(@Nonnull PointerTracker tracker);
+
+ /**
+ * Cancel a timer for detecting a long pressed shift key.
+ */
+ public void cancelLongPressShiftKeyTimer();
+
+ /**
+ * Cancel timers for detecting repeated key press, long pressed key, and long pressed shift key.
+ * @param tracker the {@link PointerTracker} that starts timers to be canceled.
+ */
+ public void cancelKeyTimersOf(@Nonnull PointerTracker tracker);
+
+ /**
+ * Start a timer to detect double tapped shift key.
+ */
+ public void startDoubleTapShiftKeyTimer();
+
+ /**
+ * Cancel a timer of detecting double tapped shift key.
+ */
+ public void cancelDoubleTapShiftKeyTimer();
+
+ /**
+ * Check if a timer of detecting double tapped shift key is running.
+ * @return true if detecting double tapped shift key is on going.
+ */
+ public boolean isInDoubleTapShiftKeyTimeout();
+
+ /**
+ * Start a timer to fire updating batch input while <code>tracker</code> is on hold.
+ * @param tracker the {@link PointerTracker} that stops moving.
+ */
+ public void startUpdateBatchInputTimer(@Nonnull PointerTracker tracker);
+
+ /**
+ * Cancel a timer of firing updating batch input.
+ * @param tracker the {@link PointerTracker} that resumes moving or ends gesture input.
+ */
+ public void cancelUpdateBatchInputTimer(@Nonnull PointerTracker tracker);
+
+ /**
+ * Cancel all timers of firing updating batch input.
+ */
+ public void cancelAllUpdateBatchInputTimers();
+
+ public static class Adapter implements TimerProxy {
+ @Override
+ public void startTypingStateTimer(@Nonnull Key typedKey) {}
+ @Override
+ public boolean isTypingState() { return false; }
+ @Override
+ public void startKeyRepeatTimerOf(@Nonnull PointerTracker tracker, int repeatCount,
+ int delay) {}
+ @Override
+ public void startLongPressTimerOf(@Nonnull PointerTracker tracker, int delay) {}
+ @Override
+ public void cancelLongPressTimersOf(@Nonnull PointerTracker tracker) {}
+ @Override
+ public void cancelLongPressShiftKeyTimer() {}
+ @Override
+ public void cancelKeyTimersOf(@Nonnull PointerTracker tracker) {}
+ @Override
+ public void startDoubleTapShiftKeyTimer() {}
+ @Override
+ public void cancelDoubleTapShiftKeyTimer() {}
+ @Override
+ public boolean isInDoubleTapShiftKeyTimeout() { return false; }
+ @Override
+ public void startUpdateBatchInputTimer(@Nonnull PointerTracker tracker) {}
+ @Override
+ public void cancelUpdateBatchInputTimer(@Nonnull PointerTracker tracker) {}
+ @Override
+ public void cancelAllUpdateBatchInputTimers() {}
+ }
+}
diff --git a/java/src/com/android/inputmethod/latin/AssetFileAddress.java b/java/src/com/android/inputmethod/latin/AssetFileAddress.java
index fd6c24dfe..f8d02d6ea 100644
--- a/java/src/com/android/inputmethod/latin/AssetFileAddress.java
+++ b/java/src/com/android/inputmethod/latin/AssetFileAddress.java
@@ -16,7 +16,7 @@
package com.android.inputmethod.latin;
-import com.android.inputmethod.latin.utils.FileUtils;
+import com.android.inputmethod.latin.common.FileUtils;
import java.io.File;
@@ -62,4 +62,9 @@ public final class AssetFileAddress {
public void deleteUnderlyingFile() {
FileUtils.deleteRecursively(new File(mFilename));
}
+
+ @Override
+ public String toString() {
+ return String.format("%s (offset=%d, length=%d)", mFilename, mOffset, mLength);
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index 8e9b5c6f6..46cd3b8b2 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -21,9 +21,10 @@ import android.util.Log;
import android.util.SparseArray;
import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.keyboard.ProximityInfo;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+import com.android.inputmethod.latin.common.ComposedData;
import com.android.inputmethod.latin.common.Constants;
+import com.android.inputmethod.latin.common.FileUtils;
import com.android.inputmethod.latin.common.InputPointers;
import com.android.inputmethod.latin.common.StringUtils;
import com.android.inputmethod.latin.makedict.DictionaryHeader;
@@ -33,7 +34,6 @@ import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
import com.android.inputmethod.latin.makedict.WordProperty;
import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion;
import com.android.inputmethod.latin.utils.BinaryDictionaryUtils;
-import com.android.inputmethod.latin.utils.FileUtils;
import com.android.inputmethod.latin.utils.JniUtils;
import com.android.inputmethod.latin.utils.WordInputEventForPersonalization;
@@ -262,8 +262,8 @@ public final class BinaryDictionary extends Dictionary {
}
@Override
- public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
- final NgramContext ngramContext, final ProximityInfo proximityInfo,
+ public ArrayList<SuggestedWordInfo> getSuggestions(final ComposedData composedData,
+ final NgramContext ngramContext, final long proximityInfoHandle,
final SettingsValuesForSuggestion settingsValuesForSuggestion,
final int sessionId, final float weightForLocale,
final float[] inOutWeightOfLangModelVsSpatialModel) {
@@ -274,12 +274,13 @@ public final class BinaryDictionary extends Dictionary {
Arrays.fill(session.mInputCodePoints, Constants.NOT_A_CODE);
ngramContext.outputToArray(session.mPrevWordCodePointArrays,
session.mIsBeginningOfSentenceArray);
- final InputPointers inputPointers = composer.getInputPointers();
- final boolean isGesture = composer.isBatchMode();
+ final InputPointers inputPointers = composedData.mInputPointers;
+ final boolean isGesture = composedData.mIsBatchMode;
final int inputSize;
if (!isGesture) {
- inputSize = composer.copyCodePointsExceptTrailingSingleQuotesAndReturnCodePointCount(
- session.mInputCodePoints);
+ inputSize =
+ composedData.copyCodePointsExceptTrailingSingleQuotesAndReturnCodePointCount(
+ session.mInputCodePoints);
if (inputSize < 0) {
return null;
}
@@ -303,7 +304,7 @@ public final class BinaryDictionary extends Dictionary {
Dictionary.NOT_A_WEIGHT_OF_LANG_MODEL_VS_SPATIAL_MODEL;
}
// TOOD: Pass multiple previous words information for n-gram.
- getSuggestionsNative(mNativeDict, proximityInfo.getNativeProximityInfo(),
+ getSuggestionsNative(mNativeDict, proximityInfoHandle,
getTraverseSession(sessionId).getSession(), inputPointers.getXCoordinates(),
inputPointers.getYCoordinates(), inputPointers.getTimes(),
inputPointers.getPointerIds(), session.mInputCodePoints, inputSize,
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
index 1570bdae0..5afb62b69 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionaryGetter.java
@@ -21,11 +21,11 @@ import android.content.SharedPreferences;
import android.content.res.AssetFileDescriptor;
import android.util.Log;
+import com.android.inputmethod.latin.common.LocaleUtils;
import com.android.inputmethod.latin.makedict.DictionaryHeader;
import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
import com.android.inputmethod.latin.utils.BinaryDictionaryUtils;
import com.android.inputmethod.latin.utils.DictionaryInfoUtils;
-import com.android.inputmethod.latin.utils.LocaleUtils;
import java.io.File;
import java.io.IOException;
@@ -284,7 +284,8 @@ final public class BinaryDictionaryGetter {
final AssetFileAddress afa = AssetFileAddress.makeFromFileName(f.getPath());
if (null != afa) fileList.add(afa);
} else {
- Log.e(TAG, "Found a cached dictionary file but cannot read or use it");
+ Log.e(TAG, "Found a cached dictionary file for " + locale.toString()
+ + " but cannot read or use it");
}
}
diff --git a/java/src/com/android/inputmethod/latin/DicTraverseSession.java b/java/src/com/android/inputmethod/latin/DicTraverseSession.java
index 95390aa9f..aefefd305 100644
--- a/java/src/com/android/inputmethod/latin/DicTraverseSession.java
+++ b/java/src/com/android/inputmethod/latin/DicTraverseSession.java
@@ -17,7 +17,8 @@
package com.android.inputmethod.latin;
import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.settings.NativeSuggestOptions;
+import com.android.inputmethod.latin.common.NativeSuggestOptions;
+import com.android.inputmethod.latin.settings.AdditionalFeaturesSettingUtils;
import com.android.inputmethod.latin.utils.JniUtils;
import java.util.Locale;
@@ -43,7 +44,8 @@ public final class DicTraverseSession {
public final int[] mOutputAutoCommitFirstWordConfidence = new int[1];
public final float[] mInputOutputWeightOfLangModelVsSpatialModel = new float[1];
- public final NativeSuggestOptions mNativeSuggestOptions = new NativeSuggestOptions();
+ public final NativeSuggestOptions mNativeSuggestOptions = new NativeSuggestOptions(
+ AdditionalFeaturesSettingUtils.ADDITIONAL_FEATURES_SETTINGS_SIZE);
private static native long setDicTraverseSessionNative(String locale, long dictSize);
private static native void initDicTraverseSessionNative(long nativeDicTraverseSession,
diff --git a/java/src/com/android/inputmethod/latin/Dictionary.java b/java/src/com/android/inputmethod/latin/Dictionary.java
index 28a62b283..7d7ed77e7 100644
--- a/java/src/com/android/inputmethod/latin/Dictionary.java
+++ b/java/src/com/android/inputmethod/latin/Dictionary.java
@@ -17,8 +17,8 @@
package com.android.inputmethod.latin;
import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.keyboard.ProximityInfo;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+import com.android.inputmethod.latin.common.ComposedData;
import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion;
import java.util.ArrayList;
@@ -87,9 +87,9 @@ public abstract class Dictionary {
/**
* Searches for suggestions for a given context.
- * @param composer the key sequence to match with coordinate info, as a WordComposer
+ * @param composedData the key sequence to match with coordinate info
* @param ngramContext the context for n-gram.
- * @param proximityInfo the object for key proximity. May be ignored by some implementations.
+ * @param proximityInfoHandle the handle for key proximity. Is ignored by some implementations.
* @param settingsValuesForSuggestion the settings values used for the suggestion.
* @param sessionId the session id.
* @param weightForLocale the weight given to this locale, to multiply the output scores for
@@ -99,8 +99,8 @@ public abstract class Dictionary {
* a float array that has only one element. This can be updated when a different value is used.
* @return the list of suggestions (possibly null if none)
*/
- abstract public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
- final NgramContext ngramContext, final ProximityInfo proximityInfo,
+ abstract public ArrayList<SuggestedWordInfo> getSuggestions(final ComposedData composedData,
+ final NgramContext ngramContext, final long proximityInfoHandle,
final SettingsValuesForSuggestion settingsValuesForSuggestion,
final int sessionId, final float weightForLocale,
final float[] inOutWeightOfLangModelVsSpatialModel);
@@ -203,8 +203,8 @@ public abstract class Dictionary {
}
@Override
- public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
- final NgramContext ngramContext, final ProximityInfo proximityInfo,
+ public ArrayList<SuggestedWordInfo> getSuggestions(final ComposedData composedData,
+ final NgramContext ngramContext, final long proximityInfoHandle,
final SettingsValuesForSuggestion settingsValuesForSuggestion,
final int sessionId, final float weightForLocale,
final float[] inOutWeightOfLangModelVsSpatialModel) {
diff --git a/java/src/com/android/inputmethod/latin/DictionaryCollection.java b/java/src/com/android/inputmethod/latin/DictionaryCollection.java
index a6d7205e2..96575f629 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryCollection.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryCollection.java
@@ -18,8 +18,8 @@ package com.android.inputmethod.latin;
import android.util.Log;
-import com.android.inputmethod.keyboard.ProximityInfo;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+import com.android.inputmethod.latin.common.ComposedData;
import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion;
import java.util.ArrayList;
@@ -59,8 +59,8 @@ public final class DictionaryCollection extends Dictionary {
}
@Override
- public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
- final NgramContext ngramContext, final ProximityInfo proximityInfo,
+ public ArrayList<SuggestedWordInfo> getSuggestions(final ComposedData composedData,
+ final NgramContext ngramContext, final long proximityInfoHandle,
final SettingsValuesForSuggestion settingsValuesForSuggestion,
final int sessionId, final float weightForLocale,
final float[] inOutWeightOfLangModelVsSpatialModel) {
@@ -68,15 +68,15 @@ public final class DictionaryCollection extends Dictionary {
if (dictionaries.isEmpty()) return null;
// To avoid creating unnecessary objects, we get the list out of the first
// dictionary and add the rest to it if not null, hence the get(0)
- ArrayList<SuggestedWordInfo> suggestions = dictionaries.get(0).getSuggestions(composer,
- ngramContext, proximityInfo, settingsValuesForSuggestion, sessionId,
+ ArrayList<SuggestedWordInfo> suggestions = dictionaries.get(0).getSuggestions(composedData,
+ ngramContext, proximityInfoHandle, settingsValuesForSuggestion, sessionId,
weightForLocale, inOutWeightOfLangModelVsSpatialModel);
if (null == suggestions) suggestions = new ArrayList<>();
final int length = dictionaries.size();
for (int i = 1; i < length; ++ i) {
- final ArrayList<SuggestedWordInfo> sugg = dictionaries.get(i).getSuggestions(composer,
- ngramContext, proximityInfo, settingsValuesForSuggestion, sessionId,
- weightForLocale, inOutWeightOfLangModelVsSpatialModel);
+ final ArrayList<SuggestedWordInfo> sugg = dictionaries.get(i).getSuggestions(
+ composedData, ngramContext, proximityInfoHandle, settingsValuesForSuggestion,
+ sessionId, weightForLocale, inOutWeightOfLangModelVsSpatialModel);
if (null != sugg) suggestions.addAll(sugg);
}
return suggestions;
diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
index 4a22cde7b..d23639a0d 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
@@ -23,7 +23,6 @@ import android.util.Pair;
import android.view.inputmethod.InputMethodSubtype;
import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.keyboard.ProximityInfo;
import com.android.inputmethod.latin.ExpandableBinaryDictionary.UpdateEntriesForInputEventsCallback;
import com.android.inputmethod.latin.NgramContext.WordInfo;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
@@ -683,7 +682,7 @@ public class DictionaryFacilitator {
// TODO: Revise the way to fusion suggestion results.
public SuggestionResults getSuggestionResults(final WordComposer composer,
- final NgramContext ngramContext, final ProximityInfo proximityInfo,
+ final NgramContext ngramContext, final long proximityInfoHandle,
final SettingsValuesForSuggestion settingsValuesForSuggestion, final int sessionId) {
final DictionaryGroup[] dictionaryGroups = mDictionaryGroups;
final SuggestionResults suggestionResults = new SuggestionResults(
@@ -698,8 +697,8 @@ public class DictionaryFacilitator {
? dictionaryGroup.mWeightForGesturingInLocale
: dictionaryGroup.mWeightForTypingInLocale;
final ArrayList<SuggestedWordInfo> dictionarySuggestions =
- dictionary.getSuggestions(composer, ngramContext, proximityInfo,
- settingsValuesForSuggestion, sessionId,
+ dictionary.getSuggestions(composer.getComposedDataSnapshot(), ngramContext,
+ proximityInfoHandle, settingsValuesForSuggestion, sessionId,
weightForLocale, weightOfLangModelVsSpatialModel);
if (null == dictionarySuggestions) continue;
suggestionResults.addAll(dictionarySuggestions);
diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
index 702d1536a..d9d22e0fc 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
@@ -20,9 +20,10 @@ import android.content.Context;
import android.util.Log;
import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.keyboard.ProximityInfo;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+import com.android.inputmethod.latin.common.ComposedData;
import com.android.inputmethod.latin.common.Constants;
+import com.android.inputmethod.latin.common.FileUtils;
import com.android.inputmethod.latin.makedict.DictionaryHeader;
import com.android.inputmethod.latin.makedict.FormatSpec;
import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
@@ -32,7 +33,6 @@ import com.android.inputmethod.latin.utils.AsyncResultHolder;
import com.android.inputmethod.latin.utils.CombinedFormatUtils;
import com.android.inputmethod.latin.utils.DistracterFilter;
import com.android.inputmethod.latin.utils.ExecutorUtils;
-import com.android.inputmethod.latin.utils.FileUtils;
import com.android.inputmethod.latin.utils.WordInputEventForPersonalization;
import java.io.File;
@@ -120,7 +120,8 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
private static boolean needsToMigrateDictionary(final int formatVersion) {
// When we bump up the dictionary format version, the old version should be added to here
// for supporting migration. Note that native code has to support reading such formats.
- return formatVersion == FormatSpec.VERSION4_ONLY_FOR_TESTING;
+ return formatVersion == FormatSpec.VERSION4_ONLY_FOR_TESTING
+ || formatVersion == FormatSpec.VERSION402;
}
public boolean isValidDictionaryLocked() {
@@ -480,8 +481,8 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
}
@Override
- public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
- final NgramContext ngramContext, final ProximityInfo proximityInfo,
+ public ArrayList<SuggestedWordInfo> getSuggestions(final ComposedData composedData,
+ final NgramContext ngramContext, final long proximityInfoHandle,
final SettingsValuesForSuggestion settingsValuesForSuggestion, final int sessionId,
final float weightForLocale, final float[] inOutWeightOfLangModelVsSpatialModel) {
reloadDictionaryIfRequired();
@@ -494,9 +495,9 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
return null;
}
final ArrayList<SuggestedWordInfo> suggestions =
- mBinaryDictionary.getSuggestions(composer, ngramContext, proximityInfo,
- settingsValuesForSuggestion, sessionId, weightForLocale,
- inOutWeightOfLangModelVsSpatialModel);
+ mBinaryDictionary.getSuggestions(composedData, ngramContext,
+ proximityInfoHandle, settingsValuesForSuggestion, sessionId,
+ weightForLocale, inOutWeightOfLangModelVsSpatialModel);
if (mBinaryDictionary.isCorrupted()) {
Log.i(TAG, "Dictionary (" + mDictName +") is corrupted. "
+ "Remove and regenerate it.");
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 3fa127005..719656b07 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.TextDecoratorUi;
import com.android.inputmethod.latin.Suggest.OnGetSuggestedWordsCallback;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.common.Constants;
+import com.android.inputmethod.latin.common.CoordinateUtils;
import com.android.inputmethod.latin.common.InputPointers;
import com.android.inputmethod.latin.define.DebugFlags;
import com.android.inputmethod.latin.define.ProductionFlags;
@@ -93,7 +94,6 @@ import com.android.inputmethod.latin.suggestions.SuggestionStripViewAccessor;
import com.android.inputmethod.latin.touchinputconsumer.GestureConsumer;
import com.android.inputmethod.latin.utils.ApplicationUtils;
import com.android.inputmethod.latin.utils.CapsModeUtils;
-import com.android.inputmethod.latin.utils.CoordinateUtils;
import com.android.inputmethod.latin.utils.CursorAnchorInfoUtils;
import com.android.inputmethod.latin.utils.DialogUtils;
import com.android.inputmethod.latin.utils.ImportantNoticeUtils;
@@ -603,7 +603,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// Has to be package-visible for unit tests
@UsedForTesting
void loadSettings() {
- final Locale[] locales = mSubtypeSwitcher.getCurrentSubtypeLocales();
+ final Locale[] locales = mRichImm.getCurrentSubtypeLocales();
final EditorInfo editorInfo = getCurrentInputEditorInfo();
final InputAttributes inputAttributes = new InputAttributes(
editorInfo, isFullscreenMode(), getPackageName());
@@ -626,7 +626,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
private void refreshPersonalizationDictionarySession(
final SettingsValues currentSettingsValues) {
mDictionaryFacilitator.setIsMonolingualUser(
- mSubtypeSwitcher.isSystemLocaleSameAsLocaleOfAllEnabledSubtypesOfEnabledImes());
+ mRichImm.isSystemLocaleSameAsLocaleOfAllEnabledSubtypesOfEnabledImes());
mPersonalizationDictionaryUpdater.onLoadSettings(
currentSettingsValues.mUsePersonalizedDicts);
mContextualDictionaryUpdater.onLoadSettings(currentSettingsValues.mUsePersonalizedDicts);
@@ -657,7 +657,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
void resetDictionaryFacilitatorIfNecessary() {
- final Locale[] subtypeSwitcherLocales = mSubtypeSwitcher.getCurrentSubtypeLocales();
+ final Locale[] subtypeSwitcherLocales = mRichImm.getCurrentSubtypeLocales();
if (mDictionaryFacilitator.isForLocales(subtypeSwitcherLocales)) {
return;
}
@@ -863,7 +863,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// also wouldn't be consuming gesture data.
mGestureConsumer = GestureConsumer.NULL_GESTURE_CONSUMER;
mRichImm.clearSubtypeCaches();
- mSubtypeSwitcher.refreshSubtypeInfo();
+ mSubtypeSwitcher.onSubtypeChanged(mRichImm.getCurrentRawSubtype());
final KeyboardSwitcher switcher = mKeyboardSwitcher;
switcher.updateKeyboardTheme();
final MainKeyboardView mainKeyboardView = switcher.getMainKeyboardView();
@@ -909,7 +909,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// Update to a gesture consumer with the current editor and IME state.
mGestureConsumer = GestureConsumer.newInstance(editorInfo,
mInputLogic.getPrivateCommandPerformer(),
- Arrays.asList(mSubtypeSwitcher.getCurrentSubtypeLocales()),
+ Arrays.asList(mRichImm.getCurrentSubtypeLocales()),
switcher.getKeyboard());
// Forward this event to the accessibility utilities, if enabled.
@@ -947,7 +947,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// span, so we should reset our state unconditionally, even if restarting is true.
// We also tell the input logic about the combining rules for the current subtype, so
// it can adjust its combiners if needed.
- mInputLogic.startInput(mSubtypeSwitcher.getCombiningRulesExtraValueOfCurrentSubtype(),
+ mInputLogic.startInput(mRichImm.getCombiningRulesExtraValueOfCurrentSubtype(),
currentSettingsValues);
resetDictionaryFacilitatorIfNecessary();
@@ -1077,11 +1077,12 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
+ ", cs=" + composingSpanStart + ", ce=" + composingSpanEnd);
}
- // This call happens when we have a hardware keyboard as well as when we don't. While we
- // don't support hardware keyboards yet we should avoid doing the processing associated
- // with cursor movement when we have a hardware keyboard since we are not in charge.
+ // This call happens whether our view is displayed or not, but if it's not then we should
+ // not attempt recorrection. This is true even with a hardware keyboard connected: if the
+ // view is not displayed we have no means of showing suggestions anyway, and if it is then
+ // we want to show suggestions anyway.
final SettingsValues settingsValues = mSettings.getCurrent();
- if ((!settingsValues.mHasHardwareKeyboard || ProductionFlags.IS_HARDWARE_KEYBOARD_SUPPORTED)
+ if (isInputViewShown()
&& mInputLogic.onUpdateSelection(oldSelStart, oldSelEnd, newSelStart, newSelEnd,
settingsValues)) {
mKeyboardSwitcher.requestUpdatingShiftState(getCurrentAutoCapsState(),
@@ -1194,7 +1195,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
if (hasHardwareKeyboard && visibleKeyboardView.getVisibility() == View.GONE) {
// If there is a hardware keyboard and a visible software keyboard view has been hidden,
// no visual element will be shown on the screen.
- outInsets.touchableInsets = inputHeight;
+ outInsets.contentTopInsets = inputHeight;
outInsets.visibleTopInsets = inputHeight;
mInsetsUpdater.setInsets(outInsets);
return;
@@ -1204,7 +1205,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
? mSuggestionStripView.getHeight() : 0;
final int visibleTopY = inputHeight - visibleKeyboardView.getHeight() - suggestionsHeight;
mSuggestionStripView.setMoreSuggestionsHeight(visibleTopY);
- // Need to set touchable region only if a keyboard view is being shown.
+ // Need to set expanded touchable region only if a keyboard view is being shown.
if (visibleKeyboardView.isShown()) {
final int touchLeft = 0;
final int touchTop = mKeyboardSwitcher.isShowingMoreKeysPanel() ? 0 : visibleTopY;
@@ -1423,7 +1424,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// completely replace #onCodeInput.
public void onEvent(@Nonnull final Event event) {
if (Constants.CODE_SHORTCUT == event.mKeyCode) {
- mSubtypeSwitcher.switchToShortcutIME(this);
+ mRichImm.switchToShortcutIME(this);
}
final InputTransaction completeInputTransaction =
mInputLogic.onCodeInput(mSettings.getCurrent(), event,
@@ -1467,7 +1468,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
public void onStartBatchInput() {
mInputLogic.onStartBatchInput(mSettings.getCurrent(), mKeyboardSwitcher, mHandler);
mGestureConsumer.onGestureStarted(
- Arrays.asList(mSubtypeSwitcher.getCurrentSubtypeLocales()),
+ Arrays.asList(mRichImm.getCurrentSubtypeLocales()),
mKeyboardSwitcher.getKeyboard());
}
@@ -1489,11 +1490,11 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
}
/**
- * To be called after the InputLogic has gotten a chance to act on the on-device decoding
- * for the full gesture, possibly updating the TextView to reflect the first decoding.
+ * To be called after the InputLogic has gotten a chance to act on the suggested words by the
+ * IME for the full gesture, possibly updating the TextView to reflect the first suggestion.
* <p>
* This method must be run on the UI Thread.
- * @param suggestedWords On-device decoding for the full gesture.
+ * @param suggestedWords suggested words by the IME for the full gesture.
*/
public void onTailBatchInputResultShown(final SuggestedWords suggestedWords) {
mGestureConsumer.onImeSuggestionsProcessed(suggestedWords,
@@ -1589,7 +1590,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
// We should clear the contextual strip if there is no suggestion from dictionaries.
|| noSuggestionsFromDictionaries) {
mSuggestionStripView.setSuggestions(suggestedWords,
- mSubtypeSwitcher.getCurrentSubtype().isRtlSubtype());
+ mRichImm.getCurrentSubtype().isRtlSubtype());
}
}
@@ -1810,7 +1811,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen
public void onReceive(final Context context, final Intent intent) {
final String action = intent.getAction();
if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
- mSubtypeSwitcher.onNetworkStateChanged(intent);
+ mRichImm.onNetworkStateChanged(intent);
} else if (action.equals(AudioManager.RINGER_MODE_CHANGED_ACTION)) {
AudioAndHapticFeedbackManager.getInstance().onRingerModeChanged();
}
diff --git a/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java
index bc8bd831c..7b1a53a6e 100644
--- a/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ReadOnlyBinaryDictionary.java
@@ -16,8 +16,8 @@
package com.android.inputmethod.latin;
-import com.android.inputmethod.keyboard.ProximityInfo;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+import com.android.inputmethod.latin.common.ComposedData;
import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion;
import java.util.ArrayList;
@@ -50,16 +50,16 @@ public final class ReadOnlyBinaryDictionary extends Dictionary {
}
@Override
- public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
- final NgramContext ngramContext, final ProximityInfo proximityInfo,
+ public ArrayList<SuggestedWordInfo> getSuggestions(final ComposedData composedData,
+ final NgramContext ngramContext, final long proximityInfoHandle,
final SettingsValuesForSuggestion settingsValuesForSuggestion,
final int sessionId, final float weightForLocale,
final float[] inOutWeightOfLangModelVsSpatialModel) {
if (mLock.readLock().tryLock()) {
try {
- return mBinaryDictionary.getSuggestions(composer, ngramContext, proximityInfo,
- settingsValuesForSuggestion, sessionId, weightForLocale,
- inOutWeightOfLangModelVsSpatialModel);
+ return mBinaryDictionary.getSuggestions(composedData, ngramContext,
+ proximityInfoHandle, settingsValuesForSuggestion, sessionId,
+ weightForLocale, inOutWeightOfLangModelVsSpatialModel);
} finally {
mLock.readLock().unlock();
}
diff --git a/java/src/com/android/inputmethod/latin/RichInputMethodManager.java b/java/src/com/android/inputmethod/latin/RichInputMethodManager.java
index 8d8e7ac38..a1ac55a20 100644
--- a/java/src/com/android/inputmethod/latin/RichInputMethodManager.java
+++ b/java/src/com/android/inputmethod/latin/RichInputMethodManager.java
@@ -17,9 +17,15 @@
package com.android.inputmethod.latin;
import static com.android.inputmethod.latin.common.Constants.Subtype.KEYBOARD_MODE;
+import static com.android.inputmethod.latin.common.Constants.Subtype.ExtraValue.REQ_NETWORK_CONNECTIVITY;
import android.content.Context;
+import android.content.Intent;
import android.content.SharedPreferences;
+import android.inputmethodservice.InputMethodService;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.os.AsyncTask;
import android.os.Build;
import android.os.IBinder;
import android.preference.PreferenceManager;
@@ -28,7 +34,9 @@ import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
+import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
+import com.android.inputmethod.keyboard.KeyboardSwitcher;
import com.android.inputmethod.latin.settings.AdditionalFeaturesSettingUtils;
import com.android.inputmethod.latin.settings.Settings;
import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils;
@@ -36,7 +44,11 @@ import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
import javax.annotation.Nonnull;
@@ -46,6 +58,7 @@ import javax.annotation.Nonnull;
// non final for easy mocking.
public class RichInputMethodManager {
private static final String TAG = RichInputMethodManager.class.getSimpleName();
+ private static final boolean DEBUG = false;
private RichInputMethodManager() {
// This utility class is not publicly instantiable.
@@ -56,6 +69,10 @@ public class RichInputMethodManager {
private Context mContext;
private InputMethodManagerCompatWrapper mImmWrapper;
private InputMethodInfoCache mInputMethodInfoCache;
+ private RichInputMethodSubtype mCurrentRichInputMethodSubtype;
+ private InputMethodInfo mShortcutInputMethodInfo;
+ private InputMethodSubtype mShortcutSubtype;
+ private boolean mIsNetworkConnected;
final HashMap<InputMethodInfo, List<InputMethodSubtype>>
mSubtypeListCacheWithImplicitlySelectedSubtypes = new HashMap<>();
final HashMap<InputMethodInfo, List<InputMethodSubtype>>
@@ -95,6 +112,11 @@ public class RichInputMethodManager {
SubtypeLocaleUtils.init(context);
final InputMethodSubtype[] additionalSubtypes = getAdditionalSubtypes(context);
setAdditionalInputMethodSubtypes(additionalSubtypes);
+
+ final ConnectivityManager connectivityManager =
+ (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ final NetworkInfo info = connectivityManager.getActiveNetworkInfo();
+ mIsNetworkConnected = (info != null && info.isConnected());
}
public InputMethodSubtype[] getAdditionalSubtypes(final Context context) {
@@ -304,10 +326,47 @@ public class RichInputMethodManager {
}
@Nonnull
+ public RichInputMethodSubtype onSubtypeChanged(@Nonnull final InputMethodSubtype newSubtype) {
+ final RichInputMethodSubtype richSubtype = createCurrentRichInputMethodSubtype(newSubtype);
+ if (DEBUG) {
+ Log.w(TAG, "onSubtypeChanged: " + richSubtype.getNameForLogging());
+ }
+ mCurrentRichInputMethodSubtype = richSubtype;
+ return richSubtype;
+ }
+
+ private static RichInputMethodSubtype sForcedSubtypeForTesting = null;
+
+ @UsedForTesting
+ static void forceSubtype(final InputMethodSubtype subtype) {
+ sForcedSubtypeForTesting = new RichInputMethodSubtype(subtype);
+ }
+
+ public Locale[] getCurrentSubtypeLocales() {
+ if (null != sForcedSubtypeForTesting) {
+ return sForcedSubtypeForTesting.getLocales();
+ }
+ return getCurrentSubtype().getLocales();
+ }
+
+ public RichInputMethodSubtype getCurrentSubtype() {
+ if (null != sForcedSubtypeForTesting) {
+ return sForcedSubtypeForTesting;
+ }
+ return mCurrentRichInputMethodSubtype;
+ }
+
+
+ public String getCombiningRulesExtraValueOfCurrentSubtype() {
+ return SubtypeLocaleUtils.getCombiningRulesExtraValue(getCurrentSubtype().getRawSubtype());
+ }
+
+ @Nonnull
public InputMethodSubtype getCurrentRawSubtype() {
return mImmWrapper.mImm.getCurrentInputMethodSubtype();
}
+ @Nonnull
public RichInputMethodSubtype createCurrentRichInputMethodSubtype(
@Nonnull final InputMethodSubtype rawSubtype) {
return AdditionalFeaturesSettingUtils.createRichInputMethodSubtype(this, rawSubtype,
@@ -432,4 +491,121 @@ public class RichInputMethodManager {
}
return mImmWrapper.shouldOfferSwitchingToNextInputMethod(binder);
}
+
+ public boolean isSystemLocaleSameAsLocaleOfAllEnabledSubtypesOfEnabledImes() {
+ final Locale systemLocale = mContext.getResources().getConfiguration().locale;
+ final Set<InputMethodSubtype> enabledSubtypesOfEnabledImes = new HashSet<>();
+ final InputMethodManager inputMethodManager = getInputMethodManager();
+ final List<InputMethodInfo> enabledInputMethodInfoList =
+ inputMethodManager.getEnabledInputMethodList();
+ for (final InputMethodInfo info : enabledInputMethodInfoList) {
+ final List<InputMethodSubtype> enabledSubtypes =
+ inputMethodManager.getEnabledInputMethodSubtypeList(
+ info, true /* allowsImplicitlySelectedSubtypes */);
+ if (enabledSubtypes.isEmpty()) {
+ // An IME with no subtypes is found.
+ return false;
+ }
+ enabledSubtypesOfEnabledImes.addAll(enabledSubtypes);
+ }
+ for (final InputMethodSubtype subtype : enabledSubtypesOfEnabledImes) {
+ if (!subtype.isAuxiliary() && !subtype.getLocale().isEmpty()
+ && !systemLocale.equals(SubtypeLocaleUtils.getSubtypeLocale(subtype))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // TODO: Make this private
+ void updateShortcutIME() {
+ if (DEBUG) {
+ Log.d(TAG, "Update shortcut IME from : "
+ + (mShortcutInputMethodInfo == null
+ ? "<null>" : mShortcutInputMethodInfo.getId()) + ", "
+ + (mShortcutSubtype == null ? "<null>" : (
+ mShortcutSubtype.getLocale() + ", " + mShortcutSubtype.getMode())));
+ }
+ // TODO: Update an icon for shortcut IME
+ final Map<InputMethodInfo, List<InputMethodSubtype>> shortcuts =
+ getInputMethodManager().getShortcutInputMethodsAndSubtypes();
+ mShortcutInputMethodInfo = null;
+ mShortcutSubtype = null;
+ for (final InputMethodInfo imi : shortcuts.keySet()) {
+ final List<InputMethodSubtype> subtypes = shortcuts.get(imi);
+ // TODO: Returns the first found IMI for now. Should handle all shortcuts as
+ // appropriate.
+ mShortcutInputMethodInfo = imi;
+ // TODO: Pick up the first found subtype for now. Should handle all subtypes
+ // as appropriate.
+ mShortcutSubtype = subtypes.size() > 0 ? subtypes.get(0) : null;
+ break;
+ }
+ if (DEBUG) {
+ Log.d(TAG, "Update shortcut IME to : "
+ + (mShortcutInputMethodInfo == null
+ ? "<null>" : mShortcutInputMethodInfo.getId()) + ", "
+ + (mShortcutSubtype == null ? "<null>" : (
+ mShortcutSubtype.getLocale() + ", " + mShortcutSubtype.getMode())));
+ }
+ }
+
+ public void switchToShortcutIME(final InputMethodService context) {
+ if (mShortcutInputMethodInfo == null) {
+ return;
+ }
+
+ final String imiId = mShortcutInputMethodInfo.getId();
+ switchToTargetIME(imiId, mShortcutSubtype, context);
+ }
+
+ private void switchToTargetIME(final String imiId, final InputMethodSubtype subtype,
+ final InputMethodService context) {
+ final IBinder token = context.getWindow().getWindow().getAttributes().token;
+ if (token == null) {
+ return;
+ }
+ final InputMethodManager imm = getInputMethodManager();
+ new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ imm.setInputMethodAndSubtype(token, imiId, subtype);
+ return null;
+ }
+ }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ }
+
+ public boolean isShortcutImeEnabled() {
+ updateShortcutIME();
+ if (mShortcutInputMethodInfo == null) {
+ return false;
+ }
+ if (mShortcutSubtype == null) {
+ return true;
+ }
+ return checkIfSubtypeBelongsToImeAndEnabled(
+ mShortcutInputMethodInfo, mShortcutSubtype);
+ }
+
+ public boolean isShortcutImeReady() {
+ updateShortcutIME();
+ if (mShortcutInputMethodInfo == null) {
+ return false;
+ }
+ if (mShortcutSubtype == null) {
+ return true;
+ }
+ if (mShortcutSubtype.containsExtraValueKey(REQ_NETWORK_CONNECTIVITY)) {
+ return mIsNetworkConnected;
+ }
+ return true;
+ }
+
+ public void onNetworkStateChanged(final Intent intent) {
+ final boolean noConnection = intent.getBooleanExtra(
+ ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
+ mIsNetworkConnected = !noConnection;
+
+ KeyboardSwitcher.getInstance().onNetworkStateChanged();
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/RichInputMethodSubtype.java b/java/src/com/android/inputmethod/latin/RichInputMethodSubtype.java
index 8d055531c..ea8d4a210 100644
--- a/java/src/com/android/inputmethod/latin/RichInputMethodSubtype.java
+++ b/java/src/com/android/inputmethod/latin/RichInputMethodSubtype.java
@@ -16,33 +16,45 @@
package com.android.inputmethod.latin;
+import static com.android.inputmethod.latin.common.Constants.Subtype.KEYBOARD_MODE;
+
+import android.util.Log;
import android.view.inputmethod.InputMethodSubtype;
-import com.android.inputmethod.latin.utils.LocaleUtils;
+import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils;
+import com.android.inputmethod.latin.common.Constants;
+import com.android.inputmethod.latin.common.LocaleUtils;
import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
import java.util.Arrays;
import java.util.Locale;
+import javax.annotation.Nonnull;
+
/**
* Enrichment class for InputMethodSubtype to enable concurrent multi-lingual input.
*
* Right now, this returns the extra value of its primary subtype.
*/
public final class RichInputMethodSubtype {
+ private static final String TAG = RichInputMethodSubtype.class.getSimpleName();
+
+ @Nonnull
private final InputMethodSubtype mSubtype;
+ @Nonnull
private final Locale[] mLocales;
- public RichInputMethodSubtype(final InputMethodSubtype subtype, final Locale... locales) {
+ public RichInputMethodSubtype(@Nonnull final InputMethodSubtype subtype,
+ @Nonnull final Locale... locales) {
mSubtype = subtype;
- mLocales = new Locale[locales.length+1];
+ mLocales = new Locale[locales.length + 1];
mLocales[0] = LocaleUtils.constructLocaleFromString(mSubtype.getLocale());
System.arraycopy(locales, 0, mLocales, 1, locales.length);
}
// Extra values are determined by the primary subtype. This is probably right, but
// we may have to revisit this later.
- public String getExtraValueOf(final String key) {
+ public String getExtraValueOf(@Nonnull final String key) {
return mSubtype.getExtraValueOf(key);
}
@@ -80,6 +92,7 @@ public final class RichInputMethodSubtype {
// en_US azerty T English English (US)
// zz azerty T AZERTY AZERTY
// Get the RichInputMethodSubtype's full display name in its locale.
+ @Nonnull
public String getFullDisplayName() {
if (isNoLanguage()) {
return SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(mSubtype);
@@ -88,6 +101,7 @@ public final class RichInputMethodSubtype {
}
// Get the RichInputMethodSubtype's middle display name in its locale.
+ @Nonnull
public String getMiddleDisplayName() {
if (isNoLanguage()) {
return SubtypeLocaleUtils.getKeyboardLayoutSetDisplayName(mSubtype);
@@ -96,7 +110,7 @@ public final class RichInputMethodSubtype {
}
@Override
- public boolean equals(Object o) {
+ public boolean equals(final Object o) {
if (!(o instanceof RichInputMethodSubtype)) {
return false;
}
@@ -114,15 +128,96 @@ public final class RichInputMethodSubtype {
return "Multi-lingual subtype: " + mSubtype.toString() + ", " + Arrays.toString(mLocales);
}
+ @Nonnull
public Locale[] getLocales() {
return mLocales;
}
public boolean isRtlSubtype() {
// The subtype is considered RTL if the language of the main subtype is RTL.
- return SubtypeLocaleUtils.isRtlLanguage(mLocales[0]);
+ return LocaleUtils.isRtlLanguage(mLocales[0]);
}
// TODO: remove this method
+ @Nonnull
public InputMethodSubtype getRawSubtype() { return mSubtype; }
+
+ @Nonnull
+ public String getKeyboardLayoutSetName() {
+ return SubtypeLocaleUtils.getKeyboardLayoutSetName(mSubtype);
+ }
+
+ // Dummy no language QWERTY subtype. See {@link R.xml.method}.
+ private static final int SUBTYPE_ID_OF_DUMMY_NO_LANGUAGE_SUBTYPE = 0xdde0bfd3;
+ private static final String EXTRA_VALUE_OF_DUMMY_NO_LANGUAGE_SUBTYPE =
+ "KeyboardLayoutSet=" + SubtypeLocaleUtils.QWERTY
+ + "," + Constants.Subtype.ExtraValue.ASCII_CAPABLE
+ + "," + Constants.Subtype.ExtraValue.ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE
+ + "," + Constants.Subtype.ExtraValue.EMOJI_CAPABLE;
+ @Nonnull
+ private static final RichInputMethodSubtype DUMMY_NO_LANGUAGE_SUBTYPE =
+ new RichInputMethodSubtype(InputMethodSubtypeCompatUtils.newInputMethodSubtype(
+ R.string.subtype_no_language_qwerty, R.drawable.ic_ime_switcher_dark,
+ SubtypeLocaleUtils.NO_LANGUAGE, KEYBOARD_MODE,
+ EXTRA_VALUE_OF_DUMMY_NO_LANGUAGE_SUBTYPE,
+ false /* isAuxiliary */, false /* overridesImplicitlyEnabledSubtype */,
+ SUBTYPE_ID_OF_DUMMY_NO_LANGUAGE_SUBTYPE));
+ // Caveat: We probably should remove this when we add an Emoji subtype in {@link R.xml.method}.
+ // Dummy Emoji subtype. See {@link R.xml.method}.
+ private static final int SUBTYPE_ID_OF_DUMMY_EMOJI_SUBTYPE = 0xd78b2ed0;
+ private static final String EXTRA_VALUE_OF_DUMMY_EMOJI_SUBTYPE =
+ "KeyboardLayoutSet=" + SubtypeLocaleUtils.EMOJI
+ + "," + Constants.Subtype.ExtraValue.EMOJI_CAPABLE;
+ @Nonnull
+ private static final RichInputMethodSubtype DUMMY_EMOJI_SUBTYPE = new RichInputMethodSubtype(
+ InputMethodSubtypeCompatUtils.newInputMethodSubtype(
+ R.string.subtype_emoji, R.drawable.ic_ime_switcher_dark,
+ SubtypeLocaleUtils.NO_LANGUAGE, KEYBOARD_MODE,
+ EXTRA_VALUE_OF_DUMMY_EMOJI_SUBTYPE,
+ false /* isAuxiliary */, false /* overridesImplicitlyEnabledSubtype */,
+ SUBTYPE_ID_OF_DUMMY_EMOJI_SUBTYPE));
+ private static RichInputMethodSubtype sNoLanguageSubtype;
+ private static RichInputMethodSubtype sEmojiSubtype;
+
+ @Nonnull
+ public static RichInputMethodSubtype getNoLanguageSubtype() {
+ RichInputMethodSubtype noLanguageSubtype = sNoLanguageSubtype;
+ if (noLanguageSubtype == null) {
+ final InputMethodSubtype rawNoLanguageSubtype = RichInputMethodManager.getInstance()
+ .findSubtypeByLocaleAndKeyboardLayoutSet(
+ SubtypeLocaleUtils.NO_LANGUAGE, SubtypeLocaleUtils.QWERTY);
+ if (rawNoLanguageSubtype != null) {
+ noLanguageSubtype = new RichInputMethodSubtype(rawNoLanguageSubtype);
+ }
+ }
+ if (noLanguageSubtype != null) {
+ sNoLanguageSubtype = noLanguageSubtype;
+ return noLanguageSubtype;
+ }
+ Log.w(TAG, "Can't find any language with QWERTY subtype");
+ Log.w(TAG, "No input method subtype found; returning dummy subtype: "
+ + DUMMY_NO_LANGUAGE_SUBTYPE);
+ return DUMMY_NO_LANGUAGE_SUBTYPE;
+ }
+
+ @Nonnull
+ public static RichInputMethodSubtype getEmojiSubtype() {
+ RichInputMethodSubtype emojiSubtype = sEmojiSubtype;
+ if (emojiSubtype == null) {
+ final InputMethodSubtype rawEmojiSubtype = RichInputMethodManager.getInstance()
+ .findSubtypeByLocaleAndKeyboardLayoutSet(
+ SubtypeLocaleUtils.NO_LANGUAGE, SubtypeLocaleUtils.EMOJI);
+ if (rawEmojiSubtype != null) {
+ emojiSubtype = new RichInputMethodSubtype(rawEmojiSubtype);
+ }
+ }
+ if (emojiSubtype != null) {
+ sEmojiSubtype = emojiSubtype;
+ return emojiSubtype;
+ }
+ Log.w(TAG, "Can't find emoji subtype");
+ Log.w(TAG, "No input method subtype found; returning dummy subtype: "
+ + DUMMY_EMOJI_SUBTYPE);
+ return DUMMY_EMOJI_SUBTYPE;
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
index 98bce95bd..23e348bff 100644
--- a/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
+++ b/java/src/com/android/inputmethod/latin/SubtypeSwitcher.java
@@ -16,40 +16,18 @@
package com.android.inputmethod.latin;
-import static com.android.inputmethod.latin.common.Constants.Subtype.ExtraValue.REQ_NETWORK_CONNECTIVITY;
import android.content.Context;
-import android.content.Intent;
import android.content.res.Resources;
-import android.inputmethodservice.InputMethodService;
-import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
-import android.os.AsyncTask;
-import android.os.IBinder;
-import android.util.Log;
-import android.view.inputmethod.InputMethodInfo;
-import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
-import com.android.inputmethod.annotations.UsedForTesting;
-import com.android.inputmethod.compat.InputMethodSubtypeCompatUtils;
-import com.android.inputmethod.keyboard.KeyboardSwitcher;
import com.android.inputmethod.keyboard.internal.LanguageOnSpacebarHelper;
-import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.define.DebugFlags;
import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
-import java.util.HashSet;
import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
import javax.annotation.Nonnull;
public final class SubtypeSwitcher {
- private static boolean DBG = DebugFlags.DEBUG_ENABLED;
- private static final String TAG = SubtypeSwitcher.class.getSimpleName();
-
private static final SubtypeSwitcher sInstance = new SubtypeSwitcher();
private /* final */ RichInputMethodManager mRichImm;
@@ -57,41 +35,6 @@ public final class SubtypeSwitcher {
private final LanguageOnSpacebarHelper mLanguageOnSpacebarHelper =
new LanguageOnSpacebarHelper();
- private InputMethodInfo mShortcutInputMethodInfo;
- private InputMethodSubtype mShortcutSubtype;
- private RichInputMethodSubtype mCurrentRichInputMethodSubtype;
- private RichInputMethodSubtype mNoLanguageSubtype;
- private RichInputMethodSubtype mEmojiSubtype;
- private boolean mIsNetworkConnected;
-
- private static final String KEYBOARD_MODE = "keyboard";
- // Dummy no language QWERTY subtype. See {@link R.xml.method}.
- private static final int SUBTYPE_ID_OF_DUMMY_NO_LANGUAGE_SUBTYPE = 0xdde0bfd3;
- private static final String EXTRA_VALUE_OF_DUMMY_NO_LANGUAGE_SUBTYPE =
- "KeyboardLayoutSet=" + SubtypeLocaleUtils.QWERTY
- + "," + Constants.Subtype.ExtraValue.ASCII_CAPABLE
- + "," + Constants.Subtype.ExtraValue.ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE
- + "," + Constants.Subtype.ExtraValue.EMOJI_CAPABLE;
- private static final RichInputMethodSubtype DUMMY_NO_LANGUAGE_SUBTYPE =
- new RichInputMethodSubtype(InputMethodSubtypeCompatUtils.newInputMethodSubtype(
- R.string.subtype_no_language_qwerty, R.drawable.ic_ime_switcher_dark,
- SubtypeLocaleUtils.NO_LANGUAGE, KEYBOARD_MODE,
- EXTRA_VALUE_OF_DUMMY_NO_LANGUAGE_SUBTYPE,
- false /* isAuxiliary */, false /* overridesImplicitlyEnabledSubtype */,
- SUBTYPE_ID_OF_DUMMY_NO_LANGUAGE_SUBTYPE));
- // Caveat: We probably should remove this when we add an Emoji subtype in {@link R.xml.method}.
- // Dummy Emoji subtype. See {@link R.xml.method}.
- private static final int SUBTYPE_ID_OF_DUMMY_EMOJI_SUBTYPE = 0xd78b2ed0;
- private static final String EXTRA_VALUE_OF_DUMMY_EMOJI_SUBTYPE =
- "KeyboardLayoutSet=" + SubtypeLocaleUtils.EMOJI
- + "," + Constants.Subtype.ExtraValue.EMOJI_CAPABLE;
- private static final RichInputMethodSubtype DUMMY_EMOJI_SUBTYPE = new RichInputMethodSubtype(
- InputMethodSubtypeCompatUtils.newInputMethodSubtype(
- R.string.subtype_emoji, R.drawable.ic_ime_switcher_dark,
- SubtypeLocaleUtils.NO_LANGUAGE, KEYBOARD_MODE,
- EXTRA_VALUE_OF_DUMMY_EMOJI_SUBTYPE,
- false /* isAuxiliary */, false /* overridesImplicitlyEnabledSubtype */,
- SUBTYPE_ID_OF_DUMMY_EMOJI_SUBTYPE));
public static SubtypeSwitcher getInstance() {
return sInstance;
@@ -113,18 +56,9 @@ public final class SubtypeSwitcher {
}
mResources = context.getResources();
mRichImm = RichInputMethodManager.getInstance();
- ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(
- Context.CONNECTIVITY_SERVICE);
-
- final NetworkInfo info = connectivityManager.getActiveNetworkInfo();
- mIsNetworkConnected = (info != null && info.isConnected());
-
- refreshSubtypeInfo();
- updateParametersOnStartInputView();
- }
- public void refreshSubtypeInfo() {
onSubtypeChanged(mRichImm.getCurrentRawSubtype());
+ updateParametersOnStartInputView();
}
/**
@@ -134,219 +68,21 @@ public final class SubtypeSwitcher {
public void updateParametersOnStartInputView() {
final List<InputMethodSubtype> enabledSubtypesOfThisIme =
mRichImm.getMyEnabledInputMethodSubtypeList(true);
- mLanguageOnSpacebarHelper.updateEnabledSubtypes(enabledSubtypesOfThisIme);
- updateShortcutIME();
- }
-
- private void updateShortcutIME() {
- if (DBG) {
- Log.d(TAG, "Update shortcut IME from : "
- + (mShortcutInputMethodInfo == null
- ? "<null>" : mShortcutInputMethodInfo.getId()) + ", "
- + (mShortcutSubtype == null ? "<null>" : (
- mShortcutSubtype.getLocale() + ", " + mShortcutSubtype.getMode())));
- }
- // TODO: Update an icon for shortcut IME
- final Map<InputMethodInfo, List<InputMethodSubtype>> shortcuts =
- mRichImm.getInputMethodManager().getShortcutInputMethodsAndSubtypes();
- mShortcutInputMethodInfo = null;
- mShortcutSubtype = null;
- for (final InputMethodInfo imi : shortcuts.keySet()) {
- final List<InputMethodSubtype> subtypes = shortcuts.get(imi);
- // TODO: Returns the first found IMI for now. Should handle all shortcuts as
- // appropriate.
- mShortcutInputMethodInfo = imi;
- // TODO: Pick up the first found subtype for now. Should handle all subtypes
- // as appropriate.
- mShortcutSubtype = subtypes.size() > 0 ? subtypes.get(0) : null;
- break;
- }
- if (DBG) {
- Log.d(TAG, "Update shortcut IME to : "
- + (mShortcutInputMethodInfo == null
- ? "<null>" : mShortcutInputMethodInfo.getId()) + ", "
- + (mShortcutSubtype == null ? "<null>" : (
- mShortcutSubtype.getLocale() + ", " + mShortcutSubtype.getMode())));
- }
+ mLanguageOnSpacebarHelper.onUpdateEnabledSubtypes(enabledSubtypesOfThisIme);
+ mRichImm.updateShortcutIME();
}
// Update the current subtype. LatinIME.onCurrentInputMethodSubtypeChanged calls this function.
public void onSubtypeChanged(@Nonnull final InputMethodSubtype newSubtype) {
- final RichInputMethodSubtype richSubtype =
- mRichImm.createCurrentRichInputMethodSubtype(newSubtype);
- if (DBG) {
- Log.w(TAG, "onSubtypeChanged: " + richSubtype.getNameForLogging());
- }
- mCurrentRichInputMethodSubtype = richSubtype;
- final Locale[] newLocales = richSubtype.getLocales();
- if (newLocales.length > 1) {
- // In multi-locales mode, the system language is never the same as the input language
- // because there is no single input language.
- mLanguageOnSpacebarHelper.updateIsSystemLanguageSameAsInputLanguage(false);
- } else {
- final Locale newLocale = newLocales[0];
- final Locale systemLocale = mResources.getConfiguration().locale;
- final boolean sameLocale = systemLocale.equals(newLocale);
- final boolean sameLanguage = systemLocale.getLanguage().equals(newLocale.getLanguage());
- final boolean implicitlyEnabled = mRichImm
- .checkIfSubtypeBelongsToThisImeAndImplicitlyEnabled(newSubtype);
- mLanguageOnSpacebarHelper.updateIsSystemLanguageSameAsInputLanguage(
- sameLocale || (sameLanguage && implicitlyEnabled));
- }
- updateShortcutIME();
- }
-
- ////////////////////////////
- // Shortcut IME functions //
- ////////////////////////////
-
- public void switchToShortcutIME(final InputMethodService context) {
- if (mShortcutInputMethodInfo == null) {
- return;
- }
-
- final String imiId = mShortcutInputMethodInfo.getId();
- switchToTargetIME(imiId, mShortcutSubtype, context);
- }
-
- private void switchToTargetIME(final String imiId, final InputMethodSubtype subtype,
- final InputMethodService context) {
- final IBinder token = context.getWindow().getWindow().getAttributes().token;
- if (token == null) {
- return;
- }
- final InputMethodManager imm = mRichImm.getInputMethodManager();
- new AsyncTask<Void, Void, Void>() {
- @Override
- protected Void doInBackground(Void... params) {
- imm.setInputMethodAndSubtype(token, imiId, subtype);
- return null;
- }
- }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- }
-
- public boolean isShortcutImeEnabled() {
- updateShortcutIME();
- if (mShortcutInputMethodInfo == null) {
- return false;
- }
- if (mShortcutSubtype == null) {
- return true;
- }
- return mRichImm.checkIfSubtypeBelongsToImeAndEnabled(
- mShortcutInputMethodInfo, mShortcutSubtype);
- }
-
- public boolean isShortcutImeReady() {
- updateShortcutIME();
- if (mShortcutInputMethodInfo == null) {
- return false;
- }
- if (mShortcutSubtype == null) {
- return true;
- }
- if (mShortcutSubtype.containsExtraValueKey(REQ_NETWORK_CONNECTIVITY)) {
- return mIsNetworkConnected;
- }
- return true;
- }
-
- public void onNetworkStateChanged(final Intent intent) {
- final boolean noConnection = intent.getBooleanExtra(
- ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
- mIsNetworkConnected = !noConnection;
-
- KeyboardSwitcher.getInstance().onNetworkStateChanged();
+ final RichInputMethodSubtype richSubtype = mRichImm.onSubtypeChanged(newSubtype);
+ final boolean implicitlyEnabledSubtype = mRichImm
+ .checkIfSubtypeBelongsToThisImeAndImplicitlyEnabled(newSubtype);
+ mLanguageOnSpacebarHelper.onSubtypeChanged(
+ richSubtype, implicitlyEnabledSubtype, mResources.getConfiguration().locale);
+ mRichImm.updateShortcutIME();
}
- //////////////////////////////////
- // Subtype Switching functions //
- //////////////////////////////////
-
public int getLanguageOnSpacebarFormatType(final RichInputMethodSubtype subtype) {
return mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(subtype);
}
-
- public boolean isSystemLocaleSameAsLocaleOfAllEnabledSubtypesOfEnabledImes() {
- final Locale systemLocale = mResources.getConfiguration().locale;
- final Set<InputMethodSubtype> enabledSubtypesOfEnabledImes = new HashSet<>();
- final InputMethodManager inputMethodManager = mRichImm.getInputMethodManager();
- final List<InputMethodInfo> enabledInputMethodInfoList =
- inputMethodManager.getEnabledInputMethodList();
- for (final InputMethodInfo info : enabledInputMethodInfoList) {
- final List<InputMethodSubtype> enabledSubtypes =
- inputMethodManager.getEnabledInputMethodSubtypeList(
- info, true /* allowsImplicitlySelectedSubtypes */);
- if (enabledSubtypes.isEmpty()) {
- // An IME with no subtypes is found.
- return false;
- }
- enabledSubtypesOfEnabledImes.addAll(enabledSubtypes);
- }
- for (final InputMethodSubtype subtype : enabledSubtypesOfEnabledImes) {
- if (!subtype.isAuxiliary() && !subtype.getLocale().isEmpty()
- && !systemLocale.equals(SubtypeLocaleUtils.getSubtypeLocale(subtype))) {
- return false;
- }
- }
- return true;
- }
-
- private static RichInputMethodSubtype sForcedSubtypeForTesting = null;
-
- @UsedForTesting
- static void forceSubtype(final InputMethodSubtype subtype) {
- sForcedSubtypeForTesting = new RichInputMethodSubtype(subtype);
- }
-
- public Locale[] getCurrentSubtypeLocales() {
- if (null != sForcedSubtypeForTesting) {
- return sForcedSubtypeForTesting.getLocales();
- }
- return getCurrentSubtype().getLocales();
- }
-
- public RichInputMethodSubtype getCurrentSubtype() {
- if (null != sForcedSubtypeForTesting) {
- return sForcedSubtypeForTesting;
- }
- return mCurrentRichInputMethodSubtype;
- }
-
- public RichInputMethodSubtype getNoLanguageSubtype() {
- if (mNoLanguageSubtype == null) {
- mNoLanguageSubtype = new RichInputMethodSubtype(
- mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
- SubtypeLocaleUtils.NO_LANGUAGE, SubtypeLocaleUtils.QWERTY));
- }
- if (mNoLanguageSubtype != null) {
- return mNoLanguageSubtype;
- }
- Log.w(TAG, "Can't find any language with QWERTY subtype");
- Log.w(TAG, "No input method subtype found; returning dummy subtype: "
- + DUMMY_NO_LANGUAGE_SUBTYPE);
- return DUMMY_NO_LANGUAGE_SUBTYPE;
- }
-
- public RichInputMethodSubtype getEmojiSubtype() {
- if (mEmojiSubtype == null) {
- final InputMethodSubtype rawEmojiSubtype =
- mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
- SubtypeLocaleUtils.NO_LANGUAGE, SubtypeLocaleUtils.EMOJI);
- if (null != rawEmojiSubtype) {
- mEmojiSubtype = new RichInputMethodSubtype(rawEmojiSubtype);
- }
- }
- if (mEmojiSubtype != null) {
- return mEmojiSubtype;
- }
- Log.w(TAG, "Can't find emoji subtype");
- Log.w(TAG, "No input method subtype found; returning dummy subtype: "
- + DUMMY_EMOJI_SUBTYPE);
- return DUMMY_EMOJI_SUBTYPE;
- }
-
- public String getCombiningRulesExtraValueOfCurrentSubtype() {
- return SubtypeLocaleUtils.getCombiningRulesExtraValue(getCurrentSubtype().getRawSubtype());
- }
}
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java
index 430f765ea..ee8d3f8cb 100644
--- a/java/src/com/android/inputmethod/latin/Suggest.java
+++ b/java/src/com/android/inputmethod/latin/Suggest.java
@@ -140,8 +140,8 @@ public final class Suggest {
: typedWord;
final SuggestionResults suggestionResults = mDictionaryFacilitator.getSuggestionResults(
- wordComposer, ngramContext, proximityInfo, settingsValuesForSuggestion,
- SESSION_ID_TYPING);
+ wordComposer, ngramContext, proximityInfo.getNativeProximityInfo(),
+ settingsValuesForSuggestion, SESSION_ID_TYPING);
final ArrayList<SuggestedWordInfo> suggestionsContainer =
getTransformedSuggestedWordInfoList(wordComposer, suggestionResults,
trailingSingleQuotesCount,
@@ -230,7 +230,7 @@ public final class Suggest {
inputStyle = inputStyleIfNotPrediction;
}
callback.onGetSuggestedWords(new SuggestedWords(suggestionsList,
- suggestionResults.mRawSuggestions,
+ suggestionResults.mRawSuggestions, typedWord,
// TODO: this first argument is lying. If this is a whitelisted word which is an
// actual word, it says typedWordValid = false, which looks wrong. We should either
// rename the attribute or change the value.
@@ -247,8 +247,8 @@ public final class Suggest {
final int inputStyle, final int sequenceNumber,
final OnGetSuggestedWordsCallback callback) {
final SuggestionResults suggestionResults = mDictionaryFacilitator.getSuggestionResults(
- wordComposer, ngramContext, proximityInfo, settingsValuesForSuggestion,
- SESSION_ID_GESTURE);
+ wordComposer, ngramContext, proximityInfo.getNativeProximityInfo(),
+ settingsValuesForSuggestion, SESSION_ID_GESTURE);
// For transforming words that don't come from a dictionary, because it's our best bet
final Locale defaultLocale = mDictionaryFacilitator.getMostProbableLocale();
final ArrayList<SuggestedWordInfo> suggestionsContainer =
@@ -286,8 +286,12 @@ public final class Suggest {
// (typedWordValid=true), not as an "auto correct word" (willAutoCorrect=false).
// Note that because this method is never used to get predictions, there is no need to
// modify inputType such in getSuggestedWordsForNonBatchInput.
+ final String pseudoTypedWord = suggestionsContainer.isEmpty() ? null
+ : suggestionsContainer.get(0).mWord;
+
callback.onGetSuggestedWords(new SuggestedWords(suggestionsContainer,
suggestionResults.mRawSuggestions,
+ pseudoTypedWord,
true /* typedWordValid */,
false /* willAutoCorrect */,
false /* isObsoleteSuggestions */,
diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java
index c51e20f21..bddeac495 100644
--- a/java/src/com/android/inputmethod/latin/SuggestedWords.java
+++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java
@@ -73,21 +73,11 @@ public class SuggestedWords {
final boolean willAutoCorrect,
final boolean isObsoleteSuggestions,
final int inputStyle) {
- this(suggestedWordInfoList, rawSuggestions, typedWordValid, willAutoCorrect,
- isObsoleteSuggestions, inputStyle, NOT_A_SEQUENCE_NUMBER);
- }
-
- public SuggestedWords(final ArrayList<SuggestedWordInfo> suggestedWordInfoList,
- final ArrayList<SuggestedWordInfo> rawSuggestions,
- final boolean typedWordValid,
- final boolean willAutoCorrect,
- final boolean isObsoleteSuggestions,
- final int inputStyle,
- final int sequenceNumber) {
this(suggestedWordInfoList, rawSuggestions,
(suggestedWordInfoList.isEmpty() || isPrediction(inputStyle)) ? null
: suggestedWordInfoList.get(INDEX_OF_TYPED_WORD).mWord,
- typedWordValid, willAutoCorrect, isObsoleteSuggestions, inputStyle, sequenceNumber);
+ typedWordValid, willAutoCorrect,
+ isObsoleteSuggestions, inputStyle, NOT_A_SEQUENCE_NUMBER);
}
public SuggestedWords(final ArrayList<SuggestedWordInfo> suggestedWordInfoList,
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
index 0b77f2ce3..78860d87d 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -19,11 +19,12 @@ package com.android.inputmethod.latin;
import com.android.inputmethod.event.CombinerChain;
import com.android.inputmethod.event.Event;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+import com.android.inputmethod.latin.common.ComposedData;
import com.android.inputmethod.latin.common.Constants;
+import com.android.inputmethod.latin.common.CoordinateUtils;
import com.android.inputmethod.latin.common.InputPointers;
import com.android.inputmethod.latin.common.StringUtils;
import com.android.inputmethod.latin.define.DebugFlags;
-import com.android.inputmethod.latin.utils.CoordinateUtils;
import java.util.ArrayList;
import java.util.Collections;
@@ -90,6 +91,10 @@ public final class WordComposer {
refreshTypedWordCache();
}
+ public ComposedData getComposedDataSnapshot() {
+ return new ComposedData(getInputPointers(), isBatchMode(), mTypedWordCache.toString());
+ }
+
/**
* Restart the combiners, possibly with a new spec.
* @param combiningSpec The spec string for combining. This is found in the extra value.
@@ -134,38 +139,6 @@ public final class WordComposer {
return mCodePointSize;
}
- /**
- * Copy the code points in the typed word to a destination array of ints.
- *
- * If the array is too small to hold the code points in the typed word, nothing is copied and
- * -1 is returned.
- *
- * @param destination the array of ints.
- * @return the number of copied code points.
- */
- public int copyCodePointsExceptTrailingSingleQuotesAndReturnCodePointCount(
- final int[] destination) {
- // This method can be called on a separate thread and mTypedWordCache can change while we
- // are executing this method.
- final String typedWord = mTypedWordCache.toString();
- // lastIndex is exclusive
- final int lastIndex = typedWord.length()
- - StringUtils.getTrailingSingleQuotesCount(typedWord);
- if (lastIndex <= 0) {
- // The string is empty or contains only single quotes.
- return 0;
- }
-
- // The following function counts the number of code points in the text range which begins
- // at index 0 and extends to the character at lastIndex.
- final int codePointSize = Character.codePointCount(typedWord, 0, lastIndex);
- if (codePointSize > destination.length) {
- return -1;
- }
- return StringUtils.copyCodePointsAndReturnCodePointCount(destination, typedWord, 0,
- lastIndex, true /* downCase */);
- }
-
public boolean isSingleLetter() {
return size() == 1;
}
diff --git a/java/src/com/android/inputmethod/latin/debug/ExternalDictionaryGetterForDebug.java b/java/src/com/android/inputmethod/latin/debug/ExternalDictionaryGetterForDebug.java
index d4be0e397..78bfd2b52 100644
--- a/java/src/com/android/inputmethod/latin/debug/ExternalDictionaryGetterForDebug.java
+++ b/java/src/com/android/inputmethod/latin/debug/ExternalDictionaryGetterForDebug.java
@@ -26,10 +26,10 @@ import android.os.Environment;
import com.android.inputmethod.latin.BinaryDictionaryFileDumper;
import com.android.inputmethod.latin.BinaryDictionaryGetter;
import com.android.inputmethod.latin.R;
+import com.android.inputmethod.latin.common.LocaleUtils;
import com.android.inputmethod.latin.makedict.DictionaryHeader;
import com.android.inputmethod.latin.utils.DialogUtils;
import com.android.inputmethod.latin.utils.DictionaryInfoUtils;
-import com.android.inputmethod.latin.utils.LocaleUtils;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
diff --git a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
index 78d79ae50..eba9654a5 100644
--- a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
+++ b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
@@ -171,14 +171,18 @@ public final class FormatSpec {
// ExpandableDictionary.matchesExpectedBinaryDictFormatVersionForThisType().
public static final int VERSION2 = 2;
public static final int VERSION201 = 201;
+ public static final int VERSION202 = 202;
public static final int MINIMUM_SUPPORTED_VERSION_OF_CODE_POINT_TABLE = VERSION201;
// Dictionary version used for testing.
public static final int VERSION4_ONLY_FOR_TESTING = 399;
- public static final int VERSION401 = 401;
- public static final int VERSION4 = 402;
- public static final int VERSION4_DEV = 403;
- static final int MINIMUM_SUPPORTED_VERSION = VERSION2;
- static final int MAXIMUM_SUPPORTED_VERSION = VERSION4_DEV;
+ public static final int VERSION402 = 402;
+ public static final int VERSION403 = 403;
+ public static final int VERSION4 = VERSION403;
+ public static final int VERSION4_DEV = VERSION403;
+ public static final int MINIMUM_SUPPORTED_STATIC_VERSION = VERSION202;
+ public static final int MAXIMUM_SUPPORTED_STATIC_VERSION = VERSION202;
+ static final int MINIMUM_SUPPORTED_DYNAMIC_VERSION = VERSION4;
+ static final int MAXIMUM_SUPPORTED_DYNAMIC_VERSION = VERSION4_DEV;
// TODO: Make this value adaptative to content data, store it in the header, and
// use it in the reading code.
diff --git a/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java b/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java
index 331f85e0e..8c5eb0aa7 100644
--- a/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java
+++ b/java/src/com/android/inputmethod/latin/personalization/PersonalizationHelper.java
@@ -19,7 +19,7 @@ package com.android.inputmethod.latin.personalization;
import android.content.Context;
import android.util.Log;
-import com.android.inputmethod.latin.utils.FileUtils;
+import com.android.inputmethod.latin.common.FileUtils;
import java.io.File;
import java.io.FilenameFilter;
@@ -28,32 +28,45 @@ import java.util.Locale;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+/**
+ * Helps handle and manage personalized dictionaries such as {@link UserHistoryDictionary} and
+ * {@link PersonalizationDictionary}.
+ */
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 = new ConcurrentHashMap<>();
private static final ConcurrentHashMap<String, SoftReference<PersonalizationDictionary>>
sLangPersonalizationDictCache = new ConcurrentHashMap<>();
+ @Nonnull
public static UserHistoryDictionary getUserHistoryDictionary(
- final Context context, final Locale locale) {
- final String localeStr = locale.toString();
+ final Context context, final Locale locale, @Nullable final String accountName) {
+ String lookupStr = locale.toString();
+ if (accountName != null) {
+ lookupStr += "." + accountName;
+ }
synchronized (sLangUserHistoryDictCache) {
- if (sLangUserHistoryDictCache.containsKey(localeStr)) {
+ if (sLangUserHistoryDictCache.containsKey(lookupStr)) {
final SoftReference<UserHistoryDictionary> ref =
- sLangUserHistoryDictCache.get(localeStr);
+ sLangUserHistoryDictCache.get(lookupStr);
final UserHistoryDictionary dict = ref == null ? null : ref.get();
if (dict != null) {
if (DEBUG) {
- Log.w(TAG, "Use cached UserHistoryDictionary for " + locale);
+ Log.d(TAG, "Use cached UserHistoryDictionary for " + locale +
+ " & account" + accountName);
}
dict.reloadDictionaryIfRequired();
return dict;
}
}
final UserHistoryDictionary dict = new UserHistoryDictionary(context, locale);
- sLangUserHistoryDictCache.put(localeStr, new SoftReference<>(dict));
+ sLangUserHistoryDictCache.put(lookupStr, new SoftReference<>(dict));
return dict;
}
}
diff --git a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java
index 58782c646..946835cbc 100644
--- a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java
@@ -17,30 +17,73 @@
package com.android.inputmethod.latin.personalization;
import android.content.Context;
+import android.content.SharedPreferences;
+import android.preference.PreferenceManager;
import com.android.inputmethod.annotations.ExternallyReferenced;
+import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.latin.Dictionary;
import com.android.inputmethod.latin.ExpandableBinaryDictionary;
import com.android.inputmethod.latin.NgramContext;
import com.android.inputmethod.latin.common.Constants;
+import com.android.inputmethod.latin.define.ProductionFlags;
+import com.android.inputmethod.latin.settings.LocalSettingsConstants;
import com.android.inputmethod.latin.utils.DistracterFilter;
import java.io.File;
import java.util.Locale;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
/**
* Locally gathers stats about the words user types and various other signals like auto-correction
* cancellation or manual picks. This allows the keyboard to adapt to the typist over time.
*/
public class UserHistoryDictionary extends DecayingExpandableBinaryDictionaryBase {
- /* package */ static final String NAME = UserHistoryDictionary.class.getSimpleName();
+ static final String NAME = UserHistoryDictionary.class.getSimpleName();
// TODO: Make this constructor private
- /* package */ UserHistoryDictionary(final Context context, final Locale locale) {
- super(context, getDictName(NAME, locale, null /* dictFile */), locale,
- Dictionary.TYPE_USER_HISTORY, null /* dictFile */);
+ UserHistoryDictionary(final Context context, final Locale locale) {
+ super(context,
+ getUserHistoryDictName(
+ NAME,
+ locale,
+ null /* dictFile */,
+ context),
+ locale,
+ Dictionary.TYPE_USER_HISTORY,
+ null /* dictFile */);
+ }
+
+ /**
+ * @returns the name of the {@link UserHistoryDictionary}.
+ */
+ @UsedForTesting
+ static String getUserHistoryDictName(final String name, final Locale locale,
+ @Nullable final File dictFile, final Context context) {
+ if (!ProductionFlags.ENABLE_PER_ACCOUNT_USER_HISTORY_DICTIONARY) {
+ return getDictName(name, locale, dictFile);
+ }
+ return getUserHistoryDictNamePerAccount(name, locale, dictFile, context);
+ }
+
+ /**
+ * Uses the currently signed in account to determine the dictionary name.
+ */
+ private static String getUserHistoryDictNamePerAccount(final String name, final Locale locale,
+ @Nullable final File dictFile, final Context context) {
+ if (dictFile != null) {
+ return dictFile.getName();
+ }
+ final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ final String account = prefs.getString(LocalSettingsConstants.PREF_ACCOUNT_NAME,
+ null /* default */);
+ String dictName = name + "." + locale.toString();
+ if (account != null) {
+ dictName += "." + account;
+ }
+ return dictName;
}
// Note: This method is called by {@link DictionaryFacilitator} using Java reflection.
@@ -48,7 +91,14 @@ public class UserHistoryDictionary extends DecayingExpandableBinaryDictionaryBas
@ExternallyReferenced
public static UserHistoryDictionary getDictionary(final Context context, final Locale locale,
final File dictFile, final String dictNamePrefix) {
- return PersonalizationHelper.getUserHistoryDictionary(context, locale);
+ final String account;
+ if (ProductionFlags.ENABLE_PER_ACCOUNT_USER_HISTORY_DICTIONARY) {
+ account = PreferenceManager.getDefaultSharedPreferences(context)
+ .getString(LocalSettingsConstants.PREF_ACCOUNT_NAME, null /* default */);
+ } else {
+ account = null;
+ }
+ return PersonalizationHelper.getUserHistoryDictionary(context, locale, account);
}
/**
diff --git a/java/src/com/android/inputmethod/latin/settings/CustomInputStylePreference.java b/java/src/com/android/inputmethod/latin/settings/CustomInputStylePreference.java
index 01398f467..b749aa51a 100644
--- a/java/src/com/android/inputmethod/latin/settings/CustomInputStylePreference.java
+++ b/java/src/com/android/inputmethod/latin/settings/CustomInputStylePreference.java
@@ -346,8 +346,10 @@ final class CustomInputStylePreference extends DialogPreference
super(context, android.R.layout.simple_spinner_item);
setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
+ final String[] predefinedKeyboardLayoutSet = context.getResources().getStringArray(
+ R.array.predefined_layouts);
// TODO: Should filter out already existing combinations of locale and layout.
- for (final String layout : SubtypeLocaleUtils.getPredefinedKeyboardLayoutSet()) {
+ for (final String layout : predefinedKeyboardLayoutSet) {
// This is a dummy subtype with NO_LANGUAGE, only for display.
final InputMethodSubtype subtype =
AdditionalSubtypeUtils.createDummyAdditionalSubtype(
diff --git a/java/src/com/android/inputmethod/latin/settings/PreferencesSettingsFragment.java b/java/src/com/android/inputmethod/latin/settings/PreferencesSettingsFragment.java
index 49db2bdc0..c0ceb8857 100644
--- a/java/src/com/android/inputmethod/latin/settings/PreferencesSettingsFragment.java
+++ b/java/src/com/android/inputmethod/latin/settings/PreferencesSettingsFragment.java
@@ -24,7 +24,7 @@ import android.preference.Preference;
import com.android.inputmethod.latin.AudioAndHapticFeedbackManager;
import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.SubtypeSwitcher;
+import com.android.inputmethod.latin.RichInputMethodManager;
/**
* "Preferences" settings sub screen.
@@ -49,7 +49,7 @@ public final class PreferencesSettingsFragment extends SubScreenFragment {
// When we are called from the Settings application but we are not already running, some
// singleton and utility classes may not have been initialized. We have to call
// initialization method of these classes here. See {@link LatinIME#onCreate()}.
- SubtypeSwitcher.init(context);
+ RichInputMethodManager.init(context);
final boolean showVoiceKeyOption = res.getBoolean(
R.bool.config_enable_show_voice_key_option);
@@ -71,7 +71,7 @@ public final class PreferencesSettingsFragment extends SubScreenFragment {
super.onResume();
final Preference voiceInputKeyOption = findPreference(Settings.PREF_VOICE_INPUT_KEY);
if (voiceInputKeyOption != null) {
- final boolean isShortcutImeEnabled = SubtypeSwitcher.getInstance()
+ final boolean isShortcutImeEnabled = RichInputMethodManager.getInstance()
.isShortcutImeEnabled();
voiceInputKeyOption.setEnabled(isShortcutImeEnabled);
voiceInputKeyOption.setSummary(
diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
index 509b41fd3..26415e7d4 100644
--- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
@@ -28,7 +28,6 @@ import com.android.inputmethod.compat.AppWorkaroundsUtils;
import com.android.inputmethod.latin.InputAttributes;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.RichInputMethodManager;
-import com.android.inputmethod.latin.SubtypeSwitcher;
import com.android.inputmethod.latin.utils.AsyncResultHolder;
import com.android.inputmethod.latin.utils.ResourceUtils;
import com.android.inputmethod.latin.utils.TargetPackageInfoGetterTask;
@@ -140,7 +139,7 @@ public class SettingsValues {
DebugSettings.PREF_SLIDING_KEY_INPUT_PREVIEW, true);
mShowsVoiceInputKey = needsToShowVoiceInputKey(prefs, res)
&& mInputAttributes.mShouldShowVoiceInputKey
- && SubtypeSwitcher.getInstance().isShortcutImeEnabled();
+ && RichInputMethodManager.getInstance().isShortcutImeEnabled();
final String autoCorrectionThresholdRawValue = prefs.getString(
Settings.PREF_AUTO_CORRECTION_THRESHOLD,
res.getString(R.string.auto_correction_threshold_mode_index_modest));
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
index 315e3696b..bcf7bbfdc 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java
@@ -168,7 +168,8 @@ public final class AndroidSpellCheckerService extends SpellCheckerService
DictionaryFacilitator dictionaryFacilitatorForLocale =
mDictionaryFacilitatorCache.get(locale);
return dictionaryFacilitatorForLocale.getSuggestionResults(composer, ngramContext,
- proximityInfo, mSettingsValuesForSuggestion, sessionId);
+ proximityInfo.getNativeProximityInfo(), mSettingsValuesForSuggestion,
+ sessionId);
} finally {
if (sessionId != null) {
mSessionIdPool.add(sessionId);
diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
index 3ad8fb910..c90e8a3cf 100644
--- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
+++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidWordLevelSpellCheckerSession.java
@@ -34,10 +34,10 @@ import com.android.inputmethod.latin.NgramContext;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.WordComposer;
import com.android.inputmethod.latin.common.Constants;
+import com.android.inputmethod.latin.common.CoordinateUtils;
+import com.android.inputmethod.latin.common.LocaleUtils;
import com.android.inputmethod.latin.common.StringUtils;
import com.android.inputmethod.latin.utils.BinaryDictionaryUtils;
-import com.android.inputmethod.latin.utils.CoordinateUtils;
-import com.android.inputmethod.latin.utils.LocaleUtils;
import com.android.inputmethod.latin.utils.ScriptUtils;
import com.android.inputmethod.latin.utils.SuggestionResults;
diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
index 27a0f62ff..7991a2473 100644
--- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
+++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java
@@ -50,10 +50,10 @@ import com.android.inputmethod.latin.PunctuationSuggestions;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.SuggestedWords;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
+import com.android.inputmethod.latin.common.LocaleUtils;
import com.android.inputmethod.latin.settings.Settings;
import com.android.inputmethod.latin.settings.SettingsValues;
import com.android.inputmethod.latin.utils.ResourceUtils;
-import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
import com.android.inputmethod.latin.utils.ViewLayoutUtils;
import java.util.ArrayList;
@@ -570,8 +570,7 @@ final class SuggestionStripLayoutHelper {
final boolean isRtlLanguage = (ViewCompat.getLayoutDirection(addToDictionaryStrip)
== ViewCompat.LAYOUT_DIRECTION_RTL);
final String arrow = isRtlLanguage ? RIGHTWARDS_ARROW : LEFTWARDS_ARROW;
- final boolean isRtlSystem = SubtypeLocaleUtils.isRtlLanguage(
- res.getConfiguration().locale);
+ final boolean isRtlSystem = LocaleUtils.isRtlLanguage(res.getConfiguration().locale);
final CharSequence hint = res.getText(R.string.hint_add_to_dictionary);
hintText = (isRtlLanguage == isRtlSystem) ? (arrow + hint) : (hint + arrow);
hintWidth = width - wordWidth;
diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java
index eda81940f..22fc35a42 100644
--- a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java
+++ b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryAddWordContents.java
@@ -28,7 +28,7 @@ import android.widget.EditText;
import com.android.inputmethod.compat.UserDictionaryCompatUtils;
import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.utils.LocaleUtils;
+import com.android.inputmethod.latin.common.LocaleUtils;
import java.util.ArrayList;
import java.util.Locale;
diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryList.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryList.java
index 90e4faafd..b9ed35375 100644
--- a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryList.java
+++ b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionaryList.java
@@ -31,7 +31,7 @@ import android.view.inputmethod.InputMethodManager;
import android.view.inputmethod.InputMethodSubtype;
import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.utils.LocaleUtils;
+import com.android.inputmethod.latin.common.LocaleUtils;
import java.util.List;
import java.util.Locale;
diff --git a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettingsUtils.java b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettingsUtils.java
index e58727ec4..c0a946e42 100644
--- a/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettingsUtils.java
+++ b/java/src/com/android/inputmethod/latin/userdictionary/UserDictionarySettingsUtils.java
@@ -17,7 +17,7 @@
package com.android.inputmethod.latin.userdictionary;
import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.utils.LocaleUtils;
+import com.android.inputmethod.latin.common.LocaleUtils;
import android.content.Context;
import android.text.TextUtils;
diff --git a/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java b/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java
index 0db63fd9f..0dbc7c858 100644
--- a/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/CapsModeUtils.java
@@ -24,6 +24,7 @@ import com.android.inputmethod.latin.common.Constants;
import com.android.inputmethod.latin.common.StringUtils;
import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
+import java.util.ArrayList;
import java.util.Locale;
public final class CapsModeUtils {
@@ -326,4 +327,31 @@ public final class CapsModeUtils {
// Here we arrived at the start of the line. This should behave exactly like whitespace.
return (START == state || LETTER == state) ? noCaps : caps;
}
+
+ /**
+ * Convert capitalize mode flags into human readable text.
+ *
+ * @param capsFlags The modes flags to be converted. It may be any combination of
+ * {@link TextUtils#CAP_MODE_CHARACTERS}, {@link TextUtils#CAP_MODE_WORDS}, and
+ * {@link TextUtils#CAP_MODE_SENTENCES}.
+ * @return the text that describe the <code>capsMode</code>.
+ */
+ public static String flagsToString(final int capsFlags) {
+ final int capsFlagsMask = TextUtils.CAP_MODE_CHARACTERS | TextUtils.CAP_MODE_WORDS
+ | TextUtils.CAP_MODE_SENTENCES;
+ if ((capsFlags & ~capsFlagsMask) != 0) {
+ return "unknown<0x" + Integer.toHexString(capsFlags) + ">";
+ }
+ final ArrayList<String> builder = new ArrayList<>();
+ if ((capsFlags & android.text.TextUtils.CAP_MODE_CHARACTERS) != 0) {
+ builder.add("characters");
+ }
+ if ((capsFlags & android.text.TextUtils.CAP_MODE_WORDS) != 0) {
+ builder.add("words");
+ }
+ if ((capsFlags & android.text.TextUtils.CAP_MODE_SENTENCES) != 0) {
+ builder.add("sentences");
+ }
+ return builder.isEmpty() ? "none" : TextUtils.join("|", builder);
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/utils/CombinedFormatUtils.java b/java/src/com/android/inputmethod/latin/utils/CombinedFormatUtils.java
index 4e0f5f583..8699f2ce7 100644
--- a/java/src/com/android/inputmethod/latin/utils/CombinedFormatUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/CombinedFormatUtils.java
@@ -36,7 +36,8 @@ public class CombinedFormatUtils {
public static final String WORD_TAG = "word";
public static final String BEGINNING_OF_SENTENCE_TAG = "beginning_of_sentence";
public static final String NOT_A_WORD_TAG = "not_a_word";
- public static final String BLACKLISTED_TAG = "blacklisted";
+ public static final String POSSIBLY_OFFENSIVE_TAG = "possibly_offensive";
+ public static final String TRUE_VALUE = "true";
public static String formatAttributeMap(final HashMap<String, String> attributeMap) {
final StringBuilder builder = new StringBuilder();
@@ -61,13 +62,13 @@ public class CombinedFormatUtils {
builder.append(",");
builder.append(formatProbabilityInfo(wordProperty.mProbabilityInfo));
if (wordProperty.mIsBeginningOfSentence) {
- builder.append("," + BEGINNING_OF_SENTENCE_TAG + "=true");
+ builder.append("," + BEGINNING_OF_SENTENCE_TAG + "=" + TRUE_VALUE);
}
if (wordProperty.mIsNotAWord) {
- builder.append("," + NOT_A_WORD_TAG + "=true");
+ builder.append("," + NOT_A_WORD_TAG + "=" + TRUE_VALUE);
}
if (wordProperty.mIsPossiblyOffensive) {
- builder.append("," + BLACKLISTED_TAG + "=true");
+ builder.append("," + POSSIBLY_OFFENSIVE_TAG + "=" + TRUE_VALUE);
}
builder.append("\n");
if (wordProperty.mHasShortcuts) {
@@ -111,4 +112,8 @@ public class CombinedFormatUtils {
}
return builder.toString();
}
+
+ public static boolean isLiteralTrue(final String value) {
+ return TRUE_VALUE.equalsIgnoreCase(value);
+ }
}
diff --git a/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java b/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java
index 24025b272..81c3e3c61 100644
--- a/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/DictionaryInfoUtils.java
@@ -28,6 +28,7 @@ import com.android.inputmethod.latin.AssetFileAddress;
import com.android.inputmethod.latin.BinaryDictionaryGetter;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.common.Constants;
+import com.android.inputmethod.latin.common.LocaleUtils;
import com.android.inputmethod.latin.makedict.DictionaryHeader;
import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
diff --git a/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatchesAndSuggestions.java b/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatchesAndSuggestions.java
index 56c8249bd..9c6a94810 100644
--- a/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatchesAndSuggestions.java
+++ b/java/src/com/android/inputmethod/latin/utils/DistracterFilterCheckingExactMatchesAndSuggestions.java
@@ -250,8 +250,9 @@ public class DistracterFilterCheckingExactMatchesAndSuggestions implements Distr
composer.setComposingWord(codePoints, coordinates);
final SuggestionResults suggestionResults;
synchronized (mLock) {
- suggestionResults = dictionaryFacilitator.getSuggestionResults(
- composer, NgramContext.EMPTY_PREV_WORDS_INFO, keyboard.getProximityInfo(),
+ suggestionResults = dictionaryFacilitator.getSuggestionResults(composer,
+ NgramContext.EMPTY_PREV_WORDS_INFO,
+ keyboard.getProximityInfo().getNativeProximityInfo(),
settingsValuesForSuggestion, 0 /* sessionId */);
}
if (suggestionResults.isEmpty()) {
diff --git a/java/src/com/android/inputmethod/latin/utils/LocaleUtils.java b/java/src/com/android/inputmethod/latin/utils/LocaleUtils.java
deleted file mode 100644
index c519a0de6..000000000
--- a/java/src/com/android/inputmethod/latin/utils/LocaleUtils.java
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * Copyright (C) 2011 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.utils;
-
-import android.text.TextUtils;
-
-import java.util.HashMap;
-import java.util.Locale;
-
-/**
- * A class to help with handling Locales in string form.
- *
- * This file has the same meaning and features (and shares all of its code) with
- * the one in the dictionary pack. They need to be kept synchronized; for any
- * update/bugfix to this file, consider also updating/fixing the version in the
- * dictionary pack.
- */
-public final class LocaleUtils {
- private LocaleUtils() {
- // Intentional empty constructor for utility class.
- }
-
- // Locale match level constants.
- // A higher level of match is guaranteed to have a higher numerical value.
- // Some room is left within constants to add match cases that may arise necessary
- // in the future, for example differentiating between the case where the countries
- // are both present and different, and the case where one of the locales does not
- // specify the countries. This difference is not needed now.
-
- // Nothing matches.
- public static final int LOCALE_NO_MATCH = 0;
- // The languages matches, but the country are different. Or, the reference locale requires a
- // country and the tested locale does not have one.
- public static final int LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER = 3;
- // The languages and country match, but the variants are different. Or, the reference locale
- // requires a variant and the tested locale does not have one.
- public static final int LOCALE_LANGUAGE_AND_COUNTRY_MATCH_VARIANT_DIFFER = 6;
- // The required locale is null or empty so it will accept anything, and the tested locale
- // is non-null and non-empty.
- public static final int LOCALE_ANY_MATCH = 10;
- // The language matches, and the tested locale specifies a country but the reference locale
- // does not require one.
- public static final int LOCALE_LANGUAGE_MATCH = 15;
- // The language and the country match, and the tested locale specifies a variant but the
- // reference locale does not require one.
- public static final int LOCALE_LANGUAGE_AND_COUNTRY_MATCH = 20;
- // The compared locales are fully identical. This is the best match level.
- public static final int LOCALE_FULL_MATCH = 30;
-
- // The level at which a match is "normally" considered a locale match with standard algorithms.
- // Don't use this directly, use #isMatch to test.
- private static final int LOCALE_MATCH = LOCALE_ANY_MATCH;
-
- // Make this match the maximum match level. If this evolves to have more than 2 digits
- // when written in base 10, also adjust the getMatchLevelSortedString method.
- private static final int MATCH_LEVEL_MAX = 30;
-
- /**
- * Return how well a tested locale matches a reference locale.
- *
- * This will check the tested locale against the reference locale and return a measure of how
- * a well it matches the reference. The general idea is that the tested locale has to match
- * every specified part of the required locale. A full match occur when they are equal, a
- * partial match when the tested locale agrees with the reference locale but is more specific,
- * and a difference when the tested locale does not comply with all requirements from the
- * reference locale.
- * In more detail, if the reference locale specifies at least a language and the testedLocale
- * does not specify one, or specifies a different one, LOCALE_NO_MATCH is returned. If the
- * reference locale is empty or null, it will match anything - in the form of LOCALE_FULL_MATCH
- * if the tested locale is empty or null, and LOCALE_ANY_MATCH otherwise. If the reference and
- * tested locale agree on the language, but not on the country,
- * LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER is returned if the reference locale specifies a country,
- * and LOCALE_LANGUAGE_MATCH otherwise.
- * If they agree on both the language and the country, but not on the variant,
- * LOCALE_LANGUAGE_AND_COUNTRY_MATCH_VARIANT_DIFFER is returned if the reference locale
- * specifies a variant, and LOCALE_LANGUAGE_AND_COUNTRY_MATCH otherwise. If everything matches,
- * LOCALE_FULL_MATCH is returned.
- * Examples:
- * en <=> en_US => LOCALE_LANGUAGE_MATCH
- * en_US <=> en => LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER
- * en_US_POSIX <=> en_US_Android => LOCALE_LANGUAGE_AND_COUNTRY_MATCH_VARIANT_DIFFER
- * en_US <=> en_US_Android => LOCALE_LANGUAGE_AND_COUNTRY_MATCH
- * sp_US <=> en_US => LOCALE_NO_MATCH
- * de <=> de => LOCALE_FULL_MATCH
- * en_US <=> en_US => LOCALE_FULL_MATCH
- * "" <=> en_US => LOCALE_ANY_MATCH
- *
- * @param referenceLocale the reference locale to test against.
- * @param testedLocale the locale to test.
- * @return a constant that measures how well the tested locale matches the reference locale.
- */
- public static int getMatchLevel(String referenceLocale, String testedLocale) {
- if (TextUtils.isEmpty(referenceLocale)) {
- return TextUtils.isEmpty(testedLocale) ? LOCALE_FULL_MATCH : LOCALE_ANY_MATCH;
- }
- if (null == testedLocale) return LOCALE_NO_MATCH;
- String[] referenceParams = referenceLocale.split("_", 3);
- String[] testedParams = testedLocale.split("_", 3);
- // By spec of String#split, [0] cannot be null and length cannot be 0.
- if (!referenceParams[0].equals(testedParams[0])) return LOCALE_NO_MATCH;
- switch (referenceParams.length) {
- case 1:
- return 1 == testedParams.length ? LOCALE_FULL_MATCH : LOCALE_LANGUAGE_MATCH;
- case 2:
- if (1 == testedParams.length) return LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER;
- if (!referenceParams[1].equals(testedParams[1]))
- return LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER;
- if (3 == testedParams.length) return LOCALE_LANGUAGE_AND_COUNTRY_MATCH;
- return LOCALE_FULL_MATCH;
- case 3:
- if (1 == testedParams.length) return LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER;
- if (!referenceParams[1].equals(testedParams[1]))
- return LOCALE_LANGUAGE_MATCH_COUNTRY_DIFFER;
- if (2 == testedParams.length) return LOCALE_LANGUAGE_AND_COUNTRY_MATCH_VARIANT_DIFFER;
- if (!referenceParams[2].equals(testedParams[2]))
- return LOCALE_LANGUAGE_AND_COUNTRY_MATCH_VARIANT_DIFFER;
- return LOCALE_FULL_MATCH;
- }
- // It should be impossible to come here
- return LOCALE_NO_MATCH;
- }
-
- /**
- * Return a string that represents this match level, with better matches first.
- *
- * The strings are sorted in lexicographic order: a better match will always be less than
- * a worse match when compared together.
- */
- public static String getMatchLevelSortedString(int matchLevel) {
- // This works because the match levels are 0~99 (actually 0~30)
- // Ideally this should use a number of digits equals to the 1og10 of the greater matchLevel
- return String.format(Locale.ROOT, "%02d", MATCH_LEVEL_MAX - matchLevel);
- }
-
- /**
- * Find out whether a match level should be considered a match.
- *
- * This method takes a match level as returned by the #getMatchLevel method, and returns whether
- * it should be considered a match in the usual sense with standard Locale functions.
- *
- * @param level the match level, as returned by getMatchLevel.
- * @return whether this is a match or not.
- */
- public static boolean isMatch(int level) {
- return LOCALE_MATCH <= level;
- }
-
- private static final HashMap<String, Locale> sLocaleCache = new HashMap<>();
-
- /**
- * Creates a locale from a string specification.
- */
- public static Locale constructLocaleFromString(final String localeStr) {
- if (localeStr == null) {
- return null;
- }
- synchronized (sLocaleCache) {
- Locale retval = sLocaleCache.get(localeStr);
- if (retval != null) {
- return retval;
- }
- String[] localeParams = localeStr.split("_", 3);
- if (localeParams.length == 1) {
- retval = new Locale(localeParams[0]);
- } else if (localeParams.length == 2) {
- retval = new Locale(localeParams[0], localeParams[1]);
- } else if (localeParams.length == 3) {
- retval = new Locale(localeParams[0], localeParams[1], localeParams[2]);
- }
- if (retval != null) {
- sLocaleCache.put(localeStr, retval);
- }
- return retval;
- }
- }
-}
diff --git a/java/src/com/android/inputmethod/latin/utils/RecapitalizeStatus.java b/java/src/com/android/inputmethod/latin/utils/RecapitalizeStatus.java
index 21daddce7..a381649a4 100644
--- a/java/src/com/android/inputmethod/latin/utils/RecapitalizeStatus.java
+++ b/java/src/com/android/inputmethod/latin/utils/RecapitalizeStatus.java
@@ -51,6 +51,17 @@ public class RecapitalizeStatus {
}
}
+ public static String modeToString(final int recapitalizeMode) {
+ switch (recapitalizeMode) {
+ case NOT_A_RECAPITALIZE_MODE: return "undefined";
+ case CAPS_MODE_ORIGINAL_MIXED_CASE: return "mixedCase";
+ case CAPS_MODE_ALL_LOWER: return "allLower";
+ case CAPS_MODE_FIRST_WORD_UPPER: return "firstWordUpper";
+ case CAPS_MODE_ALL_UPPER: return "allUpper";
+ default: return "unknown<" + recapitalizeMode + ">";
+ }
+ }
+
/**
* We store the location of the cursor and the string that was there before the recapitalize
* action was done, and the location of the cursor and the string that was there after.
diff --git a/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java b/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java
index 55c1dc9e5..013f024c0 100644
--- a/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtils.java
@@ -27,13 +27,14 @@ import android.util.Log;
import android.view.inputmethod.InputMethodSubtype;
import com.android.inputmethod.latin.R;
-import com.android.inputmethod.latin.RichInputMethodSubtype;
+import com.android.inputmethod.latin.common.LocaleUtils;
import com.android.inputmethod.latin.common.StringUtils;
-import java.util.Arrays;
import java.util.HashMap;
import java.util.Locale;
+import javax.annotation.Nonnull;
+
/**
* A helper class to deal with subtype locales.
*/
@@ -53,7 +54,6 @@ public final class SubtypeLocaleUtils {
private static volatile boolean sInitialized = false;
private static final Object sInitializeLock = new Object();
private static Resources sResources;
- private static String[] sPredefinedKeyboardLayoutSet;
// Keyboard layout to its display name map.
private static final HashMap<String, String> sKeyboardLayoutToDisplayNameMap = new HashMap<>();
// Keyboard layout to subtype name resource id map.
@@ -100,7 +100,6 @@ public final class SubtypeLocaleUtils {
sResources = res;
final String[] predefinedLayoutSet = res.getStringArray(R.array.predefined_layouts);
- sPredefinedKeyboardLayoutSet = predefinedLayoutSet;
final String[] layoutDisplayNames = res.getStringArray(
R.array.predefined_layout_display_names);
for (int i = 0; i < predefinedLayoutSet.length; i++) {
@@ -149,10 +148,6 @@ public final class SubtypeLocaleUtils {
}
}
- public static String[] getPredefinedKeyboardLayoutSet() {
- return sPredefinedKeyboardLayoutSet;
- }
-
public static boolean isExceptionalLocale(final String localeString) {
return sExceptionalLocaleToNameIdsMap.containsKey(localeString);
}
@@ -173,7 +168,8 @@ public final class SubtypeLocaleUtils {
return nameId == null ? UNKNOWN_KEYBOARD_LAYOUT : nameId;
}
- public static Locale getDisplayLocaleOfSubtypeLocale(final String localeString) {
+ @Nonnull
+ public static Locale getDisplayLocaleOfSubtypeLocale(@Nonnull final String localeString) {
if (NO_LANGUAGE.equals(localeString)) {
return sResources.getConfiguration().locale;
}
@@ -183,17 +179,20 @@ public final class SubtypeLocaleUtils {
return LocaleUtils.constructLocaleFromString(localeString);
}
- public static String getSubtypeLocaleDisplayNameInSystemLocale(final String localeString) {
+ public static String getSubtypeLocaleDisplayNameInSystemLocale(
+ @Nonnull final String localeString) {
final Locale displayLocale = sResources.getConfiguration().locale;
return getSubtypeLocaleDisplayNameInternal(localeString, displayLocale);
}
- public static String getSubtypeLocaleDisplayName(final String localeString) {
+ @Nonnull
+ public static String getSubtypeLocaleDisplayName(@Nonnull final String localeString) {
final Locale displayLocale = getDisplayLocaleOfSubtypeLocale(localeString);
return getSubtypeLocaleDisplayNameInternal(localeString, displayLocale);
}
- public static String getSubtypeLanguageDisplayName(final String localeString) {
+ @Nonnull
+ public static String getSubtypeLanguageDisplayName(@Nonnull final String localeString) {
final Locale displayLocale = getDisplayLocaleOfSubtypeLocale(localeString);
final String languageString;
if (sExceptionalLocaleDisplayedInRootLocale.containsKey(localeString)) {
@@ -205,8 +204,9 @@ public final class SubtypeLocaleUtils {
return getSubtypeLocaleDisplayNameInternal(languageString, displayLocale);
}
- private static String getSubtypeLocaleDisplayNameInternal(final String localeString,
- final Locale displayLocale) {
+ @Nonnull
+ private static String getSubtypeLocaleDisplayNameInternal(@Nonnull final String localeString,
+ @Nonnull final Locale displayLocale) {
if (NO_LANGUAGE.equals(localeString)) {
// No language subtype should be displayed in system locale.
return sResources.getString(R.string.subtype_no_language);
@@ -255,8 +255,9 @@ public final class SubtypeLocaleUtils {
// en_US azerty T English (US) (AZERTY) exception
// zz azerty T Alphabet (AZERTY) in system locale
- private static String getReplacementString(final InputMethodSubtype subtype,
- final Locale displayLocale) {
+ @Nonnull
+ private static String getReplacementString(@Nonnull final InputMethodSubtype subtype,
+ @Nonnull final Locale displayLocale) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN
&& subtype.containsExtraValueKey(UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME)) {
return subtype.getExtraValueOf(UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME);
@@ -264,20 +265,24 @@ public final class SubtypeLocaleUtils {
return getSubtypeLocaleDisplayNameInternal(subtype.getLocale(), displayLocale);
}
- public static String getSubtypeDisplayNameInSystemLocale(final InputMethodSubtype subtype) {
+ @Nonnull
+ public static String getSubtypeDisplayNameInSystemLocale(
+ @Nonnull final InputMethodSubtype subtype) {
final Locale displayLocale = sResources.getConfiguration().locale;
return getSubtypeDisplayNameInternal(subtype, displayLocale);
}
- public static String getSubtypeNameForLogging(final InputMethodSubtype subtype) {
+ @Nonnull
+ public static String getSubtypeNameForLogging(@Nonnull final InputMethodSubtype subtype) {
if (subtype == null) {
return "<null subtype>";
}
return getSubtypeLocale(subtype) + "/" + getKeyboardLayoutSetName(subtype);
}
- private static String getSubtypeDisplayNameInternal(final InputMethodSubtype subtype,
- final Locale displayLocale) {
+ @Nonnull
+ private static String getSubtypeDisplayNameInternal(@Nonnull final InputMethodSubtype subtype,
+ @Nonnull final Locale displayLocale) {
final String replacementString = getReplacementString(subtype, displayLocale);
// TODO: rework this for multi-lingual subtypes
final int nameResId = subtype.getNameResId();
@@ -302,24 +307,25 @@ public final class SubtypeLocaleUtils {
getSubtypeName.runInLocale(sResources, displayLocale), displayLocale);
}
- public static Locale getSubtypeLocale(final InputMethodSubtype subtype) {
+ @Nonnull
+ public static Locale getSubtypeLocale(@Nonnull final InputMethodSubtype subtype) {
final String localeString = subtype.getLocale();
return LocaleUtils.constructLocaleFromString(localeString);
}
- public static String getKeyboardLayoutSetDisplayName(final InputMethodSubtype subtype) {
+ @Nonnull
+ public static String getKeyboardLayoutSetDisplayName(
+ @Nonnull final InputMethodSubtype subtype) {
final String layoutName = getKeyboardLayoutSetName(subtype);
return getKeyboardLayoutSetDisplayName(layoutName);
}
- public static String getKeyboardLayoutSetDisplayName(final String layoutName) {
+ @Nonnull
+ public static String getKeyboardLayoutSetDisplayName(@Nonnull final String layoutName) {
return sKeyboardLayoutToDisplayNameMap.get(layoutName);
}
- public static String getKeyboardLayoutSetName(final RichInputMethodSubtype subtype) {
- return getKeyboardLayoutSetName(subtype.getRawSubtype());
- }
-
+ @Nonnull
public static String getKeyboardLayoutSetName(final InputMethodSubtype subtype) {
String keyboardLayoutSet = subtype.getExtraValueOf(KEYBOARD_LAYOUT_SET);
if (keyboardLayoutSet == null) {
@@ -339,22 +345,6 @@ public final class SubtypeLocaleUtils {
return keyboardLayoutSet;
}
- // TODO: Get this information from the framework instead of maintaining here by ourselves.
- // Sorted list of known Right-To-Left language codes.
- private static final String[] SORTED_RTL_LANGUAGES = {
- "ar", // Arabic
- "fa", // Persian
- "iw", // Hebrew
- };
- static {
- Arrays.sort(SORTED_RTL_LANGUAGES);
- }
-
- public static boolean isRtlLanguage(final Locale locale) {
- final String language = locale.getLanguage();
- return Arrays.binarySearch(SORTED_RTL_LANGUAGES, language) >= 0;
- }
-
public static String getCombiningRulesExtraValue(final InputMethodSubtype subtype) {
return subtype.getExtraValueOf(COMBINING_RULES);
}
diff --git a/native/dicttoolkit/Android.mk b/native/dicttoolkit/Android.mk
new file mode 100644
index 000000000..118682dfc
--- /dev/null
+++ b/native/dicttoolkit/Android.mk
@@ -0,0 +1,67 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+ifeq (,$(TARGET_BUILD_APPS))
+
+# Only build if it's explicitly requested, or running mm/mmm.
+ifneq ($(ONE_SHOT_MAKEFILE)$(filter $(MAKECMDGOALS),dicttoolkit),)
+
+# HACK: Temporarily disable host tool build on Mac until the build system is ready for C++11.
+LATINIME_HOST_OSNAME := $(shell uname -s)
+ifneq ($(LATINIME_HOST_OSNAME), Darwin) # TODO: Remove this
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LATIN_IME_CORE_PATH := $(LOCAL_PATH)/../jni
+
+LATIN_IME_DICT_TOOLKIT_SRC_DIR := src
+LATIN_IME_CORE_SRC_DIR := ../jni/src
+
+LOCAL_CFLAGS += -Werror -Wall -Wextra -Weffc++ -Wformat=2 -Wcast-qual -Wcast-align \
+ -Wwrite-strings -Wfloat-equal -Wpointer-arith -Winit-self -Wredundant-decls \
+ -Woverloaded-virtual -Wsign-promo -Wno-system-headers
+
+# To suppress compiler warnings for unused variables/functions used for debug features etc.
+LOCAL_CFLAGS += -Wno-unused-parameter -Wno-unused-function
+LOCAL_CFLAGS += -std=c++11 -Wno-unused-parameter -Wno-unused-function
+
+include $(LOCAL_PATH)/NativeFileList.mk
+include $(LATIN_IME_CORE_PATH)/NativeFileList.mk
+
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/$(LATIN_IME_DICT_TOOLKIT_SRC_DIR) \
+ $(LATIN_IME_CORE_PATH)/$(LATIN_IME_CORE_SRC_DIR)
+
+LOCAL_SRC_FILES := $(LATIN_IME_DICT_TOOLKIT_MAIN_SRC_FILES) \
+ $(addprefix $(LATIN_IME_DICT_TOOLKIT_SRC_DIR)/, $(LATIN_IME_DICT_TOOLKIT_SRC_FILES)) \
+ $(addprefix $(LATIN_IME_CORE_SRC_DIR)/, $(LATIN_IME_CORE_SRC_FILES))
+
+LOCAL_MODULE := dicttoolkit
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_CLANG := true
+LOCAL_CXX_STL := libc++
+
+include $(BUILD_HOST_EXECUTABLE)
+#################### Clean up the tmp vars
+include $(LOCAL_PATH)/CleanupNativeFileList.mk
+#################### Unit test
+include $(LOCAL_PATH)/UnitTests.mk
+
+endif # Darwin - TODO: Remove this
+
+endif
+
+endif # TARGET_BUILD_APPS
diff --git a/native/dicttoolkit/CleanupNativeFileList.mk b/native/dicttoolkit/CleanupNativeFileList.mk
new file mode 100644
index 000000000..b804b41ed
--- /dev/null
+++ b/native/dicttoolkit/CleanupNativeFileList.mk
@@ -0,0 +1,17 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LATIN_IME_DICT_TOOLKIT_MAIN_SRC_FILES :=
+LATIN_IME_DICT_TOOLKIT_SRC_FILES :=
+LATIN_IME_DICT_TOOLKIT_TEST_FILES :=
diff --git a/native/dicttoolkit/NativeFileList.mk b/native/dicttoolkit/NativeFileList.mk
new file mode 100644
index 000000000..d2c8c3a2c
--- /dev/null
+++ b/native/dicttoolkit/NativeFileList.mk
@@ -0,0 +1,43 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LATIN_IME_DICT_TOOLKIT_MAIN_SRC_FILES := \
+ dict_toolkit_main.cpp
+
+LATIN_IME_DICT_TOOLKIT_SRC_FILES := \
+ $(addprefix command_executors/, \
+ diff_executor.cpp \
+ header_executor.cpp \
+ help_executor.cpp \
+ info_executor.cpp \
+ makedict_executor.cpp) \
+ $(addprefix offdevice_intermediate_dict/, \
+ offdevice_intermediate_dict.cpp) \
+ $(addprefix utils/, \
+ arguments_parser.cpp \
+ command_utils.cpp \
+ utf8_utils.cpp)
+
+LATIN_IME_DICT_TOOLKIT_TEST_FILES := \
+ $(addprefix command_executors/, \
+ diff_executor_test.cpp \
+ header_executor_test.cpp \
+ info_executor_test.cpp \
+ makedict_executor_test.cpp) \
+ dict_toolkit_defines_test.cpp \
+ $(addprefix offdevice_intermediate_dict/, \
+ offdevice_intermediate_dict_test.cpp) \
+ $(addprefix utils/, \
+ command_utils_test.cpp \
+ utf8_utils_test.cpp)
diff --git a/native/dicttoolkit/UnitTests.mk b/native/dicttoolkit/UnitTests.mk
new file mode 100644
index 000000000..96e28730e
--- /dev/null
+++ b/native/dicttoolkit/UnitTests.mk
@@ -0,0 +1,69 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+ifeq (,$(TARGET_BUILD_APPS))
+
+LOCAL_PATH := $(call my-dir)
+
+######################################
+include $(CLEAR_VARS)
+
+LATIN_IME_CORE_PATH := $(LOCAL_PATH)/../jni
+
+LATIN_IME_DICT_TOOLKIT_SRC_DIR := src
+LATIN_IME_CORE_SRC_DIR := ../jni/src
+LATIN_DICT_TOOLKIT_TEST_SRC_DIR := tests
+
+include $(LOCAL_PATH)/NativeFileList.mk
+include $(LATIN_IME_CORE_PATH)/NativeFileList.mk
+
+# TODO: Remove -std=c++11 once it is set by default on host build.
+LATIN_IME_SRC_DIR := src
+LOCAL_ADDRESS_SANITIZER := true
+LOCAL_CFLAGS += -std=c++11 -Wno-unused-parameter -Wno-unused-function
+LOCAL_CLANG := true
+LOCAL_CXX_STL := libc++
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/$(LATIN_IME_DICT_TOOLKIT_SRC_DIR) \
+ $(LATIN_IME_CORE_PATH)/$(LATIN_IME_CORE_SRC_DIR)
+LOCAL_MODULE := liblatinime_dicttoolkit_host_static_for_unittests
+LOCAL_MODULE_TAGS := optional
+LOCAL_SRC_FILES := \
+ $(addprefix $(LATIN_IME_DICT_TOOLKIT_SRC_DIR)/, $(LATIN_IME_DICT_TOOLKIT_SRC_FILES)) \
+ $(addprefix $(LATIN_IME_CORE_SRC_DIR)/, $(LATIN_IME_CORE_SRC_FILES))
+include $(BUILD_HOST_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+
+# TODO: Remove -std=c++11 once it is set by default on host build.
+LOCAL_ADDRESS_SANITIZER := true
+LOCAL_CFLAGS += -std=c++11 -Wno-unused-parameter -Wno-unused-function
+LOCAL_CLANG := true
+LOCAL_CXX_STL := libc++
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/$(LATIN_IME_DICT_TOOLKIT_SRC_DIR) \
+ $(LATIN_IME_CORE_PATH)/$(LATIN_IME_CORE_SRC_DIR)
+LOCAL_MODULE := dicttoolkit_unittests
+LOCAL_MODULE_TAGS := tests
+LOCAL_SRC_FILES := \
+ $(addprefix $(LATIN_DICT_TOOLKIT_TEST_SRC_DIR)/, $(LATIN_IME_DICT_TOOLKIT_TEST_FILES))
+LOCAL_STATIC_LIBRARIES += liblatinime_dicttoolkit_host_static_for_unittests
+include $(BUILD_HOST_NATIVE_TEST)
+
+include $(LOCAL_PATH)/CleanupNativeFileList.mk
+
+#################### Clean up the tmp vars
+LATINIME_HOST_OSNAME :=
+LATIN_IME_SRC_DIR :=
+LATIN_IME_TEST_SRC_DIR :=
+
+endif # TARGET_BUILD_APPS
diff --git a/native/dicttoolkit/dict_toolkit_main.cpp b/native/dicttoolkit/dict_toolkit_main.cpp
new file mode 100644
index 000000000..53cc5e915
--- /dev/null
+++ b/native/dicttoolkit/dict_toolkit_main.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cstdio>
+
+#include "dict_toolkit_defines.h"
+#include "utils/command_utils.h"
+
+void usage(int argc, char **argv) {
+ fprintf(stderr, "Usage: %s <command> [arguments]\n", argc > 0 ? argv[0] : "dicttoolkit");
+}
+
+int main(int argc, char **argv) {
+ if (argc < MIN_ARG_COUNT) {
+ usage(argc, argv);
+ return 1;
+ }
+ using namespace latinime::dicttoolkit;
+ const CommandType commandType = CommandUtils::getCommandType(argv[1]);
+ if (commandType == CommandType::Unknown) {
+ CommandUtils::printCommandUnknownMessage(argv[0], argv[1]);
+ return 1;
+ }
+ const auto executor = CommandUtils::getCommandExecutor(commandType);
+ return executor(argc - 1, argv + 1);
+}
diff --git a/native/dicttoolkit/run_tests.sh b/native/dicttoolkit/run_tests.sh
new file mode 100755
index 000000000..44c99c144
--- /dev/null
+++ b/native/dicttoolkit/run_tests.sh
@@ -0,0 +1,34 @@
+#!/bin/bash
+# Copyright 2014, The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# check script arguments
+if [[ $(type -t mmm) != function ]]; then
+if [[ ${BASH_SOURCE[0]} != $0 ]]; then return; else exit 1; fi
+fi
+
+# Host build is never supported in unbundled (NDK/tapas) build
+if [[ -n $TARGET_BUILD_APPS ]]; then
+ echo "Host build is never supported in tapas build." 1>&2
+ echo "Use lunch command instead." 1>&2
+ if [[ ${BASH_SOURCE[0]} != $0 ]]; then return; else exit 1; fi
+fi
+
+test_name=dicttoolkit_unittests
+
+pushd $PWD > /dev/null
+cd $(gettop)
+(mmm -j16 packages/inputmethods/LatinIME/native/dicttoolkit) || (make -j16 $test_name)
+$ANDROID_HOST_OUT/bin/$test_name
+popd > /dev/null
diff --git a/native/dicttoolkit/src/command_executors/diff_executor.cpp b/native/dicttoolkit/src/command_executors/diff_executor.cpp
new file mode 100644
index 000000000..bf6830686
--- /dev/null
+++ b/native/dicttoolkit/src/command_executors/diff_executor.cpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "command_executors/diff_executor.h"
+
+#include <cstdio>
+
+namespace latinime {
+namespace dicttoolkit {
+
+const char *const DiffExecutor::COMMAND_NAME = "diff";
+
+/* static */ int DiffExecutor::run(const int argc, char **argv) {
+ fprintf(stderr, "Command '%s' has not been implemented yet.\n", COMMAND_NAME);
+ return 0;
+}
+
+/* static */ void DiffExecutor::printUsage() {
+ printf("*** %s\n", COMMAND_NAME);
+ getArgumentsParser().printUsage(COMMAND_NAME, "Shows differences between two dictionaries.");
+}
+
+/* static */ const ArgumentsParser DiffExecutor::getArgumentsParser() {
+ std::unordered_map<std::string, OptionSpec> optionSpecs;
+ optionSpecs["p"] = OptionSpec::switchOption("(plumbing) produce output suitable for a script");
+
+ const std::vector<ArgumentSpec> argumentSpecs = {
+ ArgumentSpec::singleArgument("dict1", "dictionary file"),
+ ArgumentSpec::singleArgument("dict2", "dictionary file")
+ };
+
+ return ArgumentsParser(std::move(optionSpecs), std::move(argumentSpecs));
+}
+
+} // namespace dicttoolkit
+} // namespace latinime
diff --git a/native/dicttoolkit/src/command_executors/diff_executor.h b/native/dicttoolkit/src/command_executors/diff_executor.h
new file mode 100644
index 000000000..f92ae49d5
--- /dev/null
+++ b/native/dicttoolkit/src/command_executors/diff_executor.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LATINIME_DICT_TOOLKIT_DIFF_EXECUTOR_H
+#define LATINIME_DICT_TOOLKIT_DIFF_EXECUTOR_H
+
+#include "dict_toolkit_defines.h"
+#include "utils/arguments_parser.h"
+
+namespace latinime {
+namespace dicttoolkit {
+
+class DiffExecutor final {
+ public:
+ static const char *const COMMAND_NAME;
+
+ static int run(const int argc, char **argv);
+ static void printUsage();
+ static const ArgumentsParser getArgumentsParser();
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(DiffExecutor);
+};
+
+} // namespace dicttoolkit
+} // namepsace latinime
+#endif // LATINIME_DICT_TOOLKIT_DIFF_EXECUTOR_H
diff --git a/native/dicttoolkit/src/command_executors/header_executor.cpp b/native/dicttoolkit/src/command_executors/header_executor.cpp
new file mode 100644
index 000000000..b3d273b4e
--- /dev/null
+++ b/native/dicttoolkit/src/command_executors/header_executor.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "command_executors/header_executor.h"
+
+#include <cstdio>
+
+namespace latinime {
+namespace dicttoolkit {
+
+const char *const HeaderExecutor::COMMAND_NAME = "header";
+
+/* static */ int HeaderExecutor::run(const int argc, char **argv) {
+ fprintf(stderr, "Command '%s' has not been implemented yet.\n", COMMAND_NAME);
+ return 0;
+}
+
+/* static */ void HeaderExecutor::printUsage() {
+ printf("*** %s\n", COMMAND_NAME);
+ getArgumentsParser().printUsage(COMMAND_NAME,
+ "Prints the header contents of a dictionary file.");
+}
+
+/* static */ const ArgumentsParser HeaderExecutor::getArgumentsParser() {
+ std::unordered_map<std::string, OptionSpec> optionSpecs;
+ optionSpecs["p"] = OptionSpec::switchOption("(plumbing) produce output suitable for a script");
+
+ const std::vector<ArgumentSpec> argumentSpecs = {
+ ArgumentSpec::singleArgument("dict", "prints the header contents of a dictionary file")
+ };
+
+ return ArgumentsParser(std::move(optionSpecs), std::move(argumentSpecs));
+}
+} // namespace dicttoolkit
+} // namespace latinime
diff --git a/native/dicttoolkit/src/command_executors/header_executor.h b/native/dicttoolkit/src/command_executors/header_executor.h
new file mode 100644
index 000000000..44cc9cfc4
--- /dev/null
+++ b/native/dicttoolkit/src/command_executors/header_executor.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LATINIME_DICT_TOOLKIT_HEADER_EXECUTOR_H
+#define LATINIME_DICT_TOOLKIT_HEADER_EXECUTOR_H
+
+#include "dict_toolkit_defines.h"
+#include "utils/arguments_parser.h"
+
+namespace latinime {
+namespace dicttoolkit {
+
+class HeaderExecutor final {
+ public:
+ static const char *const COMMAND_NAME;
+
+ static int run(const int argc, char **argv);
+ static void printUsage();
+ static const ArgumentsParser getArgumentsParser();
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(HeaderExecutor);
+};
+
+} // namespace dicttoolkit
+} // namepsace latinime
+#endif // LATINIME_DICT_TOOLKIT_HEADER_EXECUTOR_H
diff --git a/native/dicttoolkit/src/command_executors/help_executor.cpp b/native/dicttoolkit/src/command_executors/help_executor.cpp
new file mode 100644
index 000000000..bd29a5b16
--- /dev/null
+++ b/native/dicttoolkit/src/command_executors/help_executor.cpp
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "command_executors/help_executor.h"
+
+#include <cstdio>
+#include <functional>
+#include <vector>
+
+#include "command_executors/diff_executor.h"
+#include "command_executors/header_executor.h"
+#include "command_executors/info_executor.h"
+#include "command_executors/makedict_executor.h"
+#include "utils/command_utils.h"
+
+namespace latinime {
+namespace dicttoolkit {
+
+const char *const HelpExecutor::COMMAND_NAME = "help";
+
+/* static */ int HelpExecutor::run(const int argc, char **argv) {
+ printf("Available commands:\n\n");
+ const std::vector<std::function<void(void)>> printUsageMethods = {DiffExecutor::printUsage,
+ HeaderExecutor::printUsage, InfoExecutor::printUsage, MakedictExecutor::printUsage,
+ printUsage};
+ for (const auto &printUsageMethod : printUsageMethods) {
+ printUsageMethod();
+ }
+ return 0;
+}
+
+/* static */ void HelpExecutor::printUsage() {
+ printf("*** %s\n", COMMAND_NAME);
+ printf("Usage: %s\n", COMMAND_NAME);
+ printf("Show this help list.\n\n");
+}
+
+} // namespace dicttoolkit
+} // namespace latinime
diff --git a/native/dicttoolkit/src/command_executors/help_executor.h b/native/dicttoolkit/src/command_executors/help_executor.h
new file mode 100644
index 000000000..280610eb9
--- /dev/null
+++ b/native/dicttoolkit/src/command_executors/help_executor.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LATINIME_DICT_TOOLKIT_HELP_EXECUTOR_H
+#define LATINIME_DICT_TOOLKIT_HELP_EXECUTOR_H
+
+#include "dict_toolkit_defines.h"
+
+namespace latinime {
+namespace dicttoolkit {
+
+class HelpExecutor final {
+ public:
+ static const char *const COMMAND_NAME;
+
+ static int run(const int argc, char **argv);
+ static void printUsage();
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(HelpExecutor);
+};
+
+} // namespace dicttoolkit
+} // namepsace latinime
+#endif // LATINIME_DICT_TOOLKIT_HELP_EXECUTOR_H
diff --git a/native/dicttoolkit/src/command_executors/info_executor.cpp b/native/dicttoolkit/src/command_executors/info_executor.cpp
new file mode 100644
index 000000000..351da4aff
--- /dev/null
+++ b/native/dicttoolkit/src/command_executors/info_executor.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "command_executors/info_executor.h"
+
+#include <cstdio>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+namespace latinime {
+namespace dicttoolkit {
+
+const char *const InfoExecutor::COMMAND_NAME = "info";
+
+/* static */ int InfoExecutor::run(const int argc, char **argv) {
+ fprintf(stderr, "Command '%s' has not been implemented yet.\n", COMMAND_NAME);
+ return 0;
+}
+
+/* static */ void InfoExecutor::printUsage() {
+ printf("*** %s\n", COMMAND_NAME);
+ getArgumentsParser().printUsage(COMMAND_NAME,
+ "Prints various information about a dictionary file.");
+}
+
+/* static */const ArgumentsParser InfoExecutor::getArgumentsParser() {
+ std::unordered_map<std::string, OptionSpec> optionSpecs;
+ optionSpecs["p"] = OptionSpec::switchOption("(plumbing) produce output suitable for a script");
+
+ const std::vector<ArgumentSpec> argumentSpecs = {
+ ArgumentSpec::singleArgument("dict", "dictionary file name"),
+ ArgumentSpec::variableLengthArguments("word", 0 /* minCount */,
+ ArgumentSpec::UNLIMITED_COUNT, "word to show information")
+ };
+
+ return ArgumentsParser(std::move(optionSpecs), std::move(argumentSpecs));
+}
+
+} // namespace dicttoolkit
+} // namespace latinime
diff --git a/native/dicttoolkit/src/command_executors/info_executor.h b/native/dicttoolkit/src/command_executors/info_executor.h
new file mode 100644
index 000000000..d4106d59f
--- /dev/null
+++ b/native/dicttoolkit/src/command_executors/info_executor.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LATINIME_DICT_TOOLKIT_INFO_EXECUTOR_H
+#define LATINIME_DICT_TOOLKIT_INFO_EXECUTOR_H
+
+#include "dict_toolkit_defines.h"
+#include "utils/arguments_parser.h"
+
+namespace latinime {
+namespace dicttoolkit {
+
+class InfoExecutor final {
+ public:
+ static const char *const COMMAND_NAME;
+
+ static int run(const int argc, char **argv);
+ static void printUsage();
+ static const ArgumentsParser getArgumentsParser();
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(InfoExecutor);
+};
+
+} // namepsace dicttoolkit
+} // namespace latinime
+#endif // LATINIME_DICT_TOOLKIT_INFO_EXECUTOR_H
diff --git a/native/dicttoolkit/src/command_executors/makedict_executor.cpp b/native/dicttoolkit/src/command_executors/makedict_executor.cpp
new file mode 100644
index 000000000..8a84e8069
--- /dev/null
+++ b/native/dicttoolkit/src/command_executors/makedict_executor.cpp
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "command_executors/makedict_executor.h"
+
+#include <cstdio>
+
+namespace latinime {
+namespace dicttoolkit {
+
+const char *const MakedictExecutor::COMMAND_NAME = "makedict";
+
+/* static */ int MakedictExecutor::run(const int argc, char **argv) {
+ fprintf(stderr, "Command '%s' has not been implemented yet.\n", COMMAND_NAME);
+ return 0;
+}
+
+/* static */ void MakedictExecutor::printUsage() {
+ printf("*** %s\n", COMMAND_NAME);
+ getArgumentsParser().printUsage(COMMAND_NAME,
+ "Converts a source dictionary file to one or several outputs.\n"
+ "Source can be a binary dictionary file or a combined format file.\n"
+ "Binary version 2 (Jelly Bean), 4, and combined format outputs are supported.");
+}
+
+/* static */const ArgumentsParser MakedictExecutor::getArgumentsParser() {
+ std::unordered_map<std::string, OptionSpec> optionSpecs;
+ optionSpecs["o"] = OptionSpec::keyValueOption("format", "2",
+ "output format version: 2/4/combined");
+ optionSpecs["t"] = OptionSpec::keyValueOption("mode", "off",
+ "code point table switch: on/off/auto");
+
+ const std::vector<ArgumentSpec> argumentSpecs = {
+ ArgumentSpec::singleArgument("src_dict", "source dictionary file"),
+ ArgumentSpec::singleArgument("dest_dict", "output dictionary file")
+ };
+
+ return ArgumentsParser(std::move(optionSpecs), std::move(argumentSpecs));
+}
+
+} // namespace dicttoolkit
+} // namespace latinime
diff --git a/native/dicttoolkit/src/command_executors/makedict_executor.h b/native/dicttoolkit/src/command_executors/makedict_executor.h
new file mode 100644
index 000000000..c3de977a3
--- /dev/null
+++ b/native/dicttoolkit/src/command_executors/makedict_executor.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LATINIME_DICT_TOOLKIT_MAKEDICT_EXECUTOR_H
+#define LATINIME_DICT_TOOLKIT_MAKEDICT_EXECUTOR_H
+
+#include "dict_toolkit_defines.h"
+#include "utils/arguments_parser.h"
+
+namespace latinime {
+namespace dicttoolkit {
+
+class MakedictExecutor final {
+ public:
+ static const char *const COMMAND_NAME;
+
+ static int run(const int argc, char **argv);
+ static void printUsage();
+ static const ArgumentsParser getArgumentsParser();
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(MakedictExecutor);
+};
+
+} // namespace dicttoolkit
+} // namepsace latinime
+#endif // LATINIME_DICT_TOOLKIT_MAKEDICT_EXECUTOR_H
diff --git a/native/dicttoolkit/src/dict_toolkit_defines.h b/native/dicttoolkit/src/dict_toolkit_defines.h
new file mode 100644
index 000000000..dbaae0ca0
--- /dev/null
+++ b/native/dicttoolkit/src/dict_toolkit_defines.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LATINIME_DICT_TOOLKIT_DEFINES_H
+#define LATINIME_DICT_TOOLKIT_DEFINES_H
+
+#include "defines.h"
+
+#define MIN_ARG_COUNT 2
+
+#endif // LATINIME_DICT_TOOLKIT_DEFINES_H
diff --git a/native/dicttoolkit/src/offdevice_intermediate_dict/offdevice_intermediate_dict.cpp b/native/dicttoolkit/src/offdevice_intermediate_dict/offdevice_intermediate_dict.cpp
new file mode 100644
index 000000000..af28131cf
--- /dev/null
+++ b/native/dicttoolkit/src/offdevice_intermediate_dict/offdevice_intermediate_dict.cpp
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "offdevice_intermediate_dict/offdevice_intermediate_dict.h"
+
+#include "offdevice_intermediate_dict/offdevice_intermediate_dict_pt_node.h"
+
+namespace latinime {
+namespace dicttoolkit {
+
+bool OffdeviceIntermediateDict::addWord(const WordProperty &wordProperty) {
+ const CodePointArrayView codePoints = wordProperty.getCodePoints();
+ if (codePoints.empty() || codePoints.size() > MAX_WORD_LENGTH) {
+ return false;
+ }
+ return addWordInner(codePoints, wordProperty, mRootPtNodeArray);
+}
+
+bool OffdeviceIntermediateDict::addWordInner(const CodePointArrayView codePoints,
+ const WordProperty &wordProperty, OffdeviceIntermediateDictPtNodeArray &ptNodeArray) {
+ auto ptNodeList = ptNodeArray.getMutablePtNodeList();
+ auto ptNodeIt = ptNodeList->begin();
+ for (; ptNodeIt != ptNodeList->end(); ++ptNodeIt) {
+ const auto &ptNode = *ptNodeIt;
+ const CodePointArrayView ptNodeCodePoints = ptNode->getPtNodeCodePoints();
+ if (codePoints[0] < ptNodeCodePoints[0]) {
+ continue;
+ }
+ if (codePoints[0] > ptNodeCodePoints[0]) {
+ break;
+ }
+ size_t i = 1;
+ for (; i < codePoints.size(); ++i) {
+ if (i >= ptNodeCodePoints.size()) {
+ // Add new child.
+ return addWordInner(codePoints.skip(i), wordProperty,
+ ptNode->getChildrenPtNodeArray());
+ }
+ if (codePoints[i] != ptNodeCodePoints[i]) {
+ break;
+ }
+ }
+ if (codePoints.size() == i && codePoints.size() == ptNodeCodePoints.size()) {
+ // All code points matched.
+ if (ptNode->getWordProperty()) {
+ // Adding the same word multiple times is not supported.
+ return false;
+ }
+ ptNodeList->insert(ptNodeIt,
+ std::make_shared<OffdeviceIntermediateDictPtNode>(wordProperty, *ptNode));
+ ptNodeList->erase(ptNodeIt);
+ return true;
+ }
+ // The (i+1)-th elements are different.
+ // Create and Add new parent ptNode for the common part.
+ auto newPtNode = codePoints.size() == i
+ ? std::make_shared<OffdeviceIntermediateDictPtNode>(codePoints, wordProperty)
+ : std::make_shared<OffdeviceIntermediateDictPtNode>(codePoints.limit(i));
+ ptNodeList->insert(ptNodeIt, newPtNode);
+ OffdeviceIntermediateDictPtNodeArray &childrenPtNodeArray =
+ newPtNode->getChildrenPtNodeArray();
+ // Add new child for the existing ptNode.
+ childrenPtNodeArray.getMutablePtNodeList()->push_back(
+ std::make_shared<OffdeviceIntermediateDictPtNode>(
+ ptNodeCodePoints.skip(i), *ptNode));
+ ptNodeList->erase(ptNodeIt);
+ if (codePoints.size() != i) {
+ // Add a child for the new word.
+ return addWordInner(codePoints.skip(i), wordProperty, childrenPtNodeArray);
+ }
+ return true;
+ }
+ ptNodeList->insert(ptNodeIt,
+ std::make_shared<OffdeviceIntermediateDictPtNode>(codePoints, wordProperty));
+ return true;
+}
+
+const WordProperty *OffdeviceIntermediateDict::getWordProperty(
+ const CodePointArrayView codePoints) const {
+ const OffdeviceIntermediateDictPtNodeArray *ptNodeArray = &mRootPtNodeArray;
+ for (size_t i = 0; i < codePoints.size();) {
+ bool foundNext = false;
+ for (const auto ptNode : ptNodeArray->getPtNodeList()) {
+ const CodePointArrayView ptNodeCodePoints = ptNode->getPtNodeCodePoints();
+ if (codePoints[i] < ptNodeCodePoints[0]) {
+ continue;
+ }
+ if (codePoints[i] > ptNodeCodePoints[0]
+ || codePoints.size() < ptNodeCodePoints.size()) {
+ return nullptr;
+ }
+ for (size_t j = 1; j < ptNodeCodePoints.size(); ++j) {
+ if (codePoints[i + j] != ptNodeCodePoints[j]) {
+ return nullptr;
+ }
+ }
+ i += ptNodeCodePoints.size();
+ if (i == codePoints.size()) {
+ return ptNode->getWordProperty();
+ }
+ ptNodeArray = &ptNode->getChildrenPtNodeArray();
+ foundNext = true;
+ break;
+ }
+ if (!foundNext) {
+ break;
+ }
+ }
+ return nullptr;
+}
+
+} // namespace dicttoolkit
+} // namespace latinime
diff --git a/native/dicttoolkit/src/offdevice_intermediate_dict/offdevice_intermediate_dict.h b/native/dicttoolkit/src/offdevice_intermediate_dict/offdevice_intermediate_dict.h
new file mode 100644
index 000000000..13d26ba91
--- /dev/null
+++ b/native/dicttoolkit/src/offdevice_intermediate_dict/offdevice_intermediate_dict.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LATINIME_DICT_TOOLKIT_OFFDEVICE_INTERMEDIATE_DICT_H
+#define LATINIME_DICT_TOOLKIT_OFFDEVICE_INTERMEDIATE_DICT_H
+
+#include "dict_toolkit_defines.h"
+#include "offdevice_intermediate_dict/offdevice_intermediate_dict_header.h"
+#include "offdevice_intermediate_dict/offdevice_intermediate_dict_pt_node_array.h"
+#include "suggest/core/dictionary/property/word_property.h"
+#include "utils/int_array_view.h"
+
+namespace latinime {
+namespace dicttoolkit {
+
+/**
+ * On memory patricia trie to represent a dictionary.
+ */
+class OffdeviceIntermediateDict final {
+ public:
+ OffdeviceIntermediateDict(const OffdeviceIntermediateDictHeader &header)
+ : mHeader(header), mRootPtNodeArray() {}
+
+ bool addWord(const WordProperty &wordProperty);
+ // The returned value will be invalid after modifying the dictionary. e.g. calling addWord().
+ const WordProperty *getWordProperty(const CodePointArrayView codePoints) const;
+ const OffdeviceIntermediateDictHeader &getHeader() const { return mHeader; }
+
+ private:
+ DISALLOW_ASSIGNMENT_OPERATOR(OffdeviceIntermediateDict);
+
+ const OffdeviceIntermediateDictHeader mHeader;
+ OffdeviceIntermediateDictPtNodeArray mRootPtNodeArray;
+
+ bool addWordInner(const CodePointArrayView codePoints, const WordProperty &wordProperty,
+ OffdeviceIntermediateDictPtNodeArray &ptNodeArray);
+};
+
+} // namespace dicttoolkit
+} // namespace latinime
+#endif // LATINIME_DICT_TOOLKIT_OFFDEVICE_INTERMEDIATE_DICT_H
diff --git a/native/dicttoolkit/src/offdevice_intermediate_dict/offdevice_intermediate_dict_header.h b/native/dicttoolkit/src/offdevice_intermediate_dict/offdevice_intermediate_dict_header.h
new file mode 100644
index 000000000..440627a79
--- /dev/null
+++ b/native/dicttoolkit/src/offdevice_intermediate_dict/offdevice_intermediate_dict_header.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LATINIME_DICT_TOOLKIT_OFFDEVICE_INTERMEDIATE_DICT_HEADER_H
+#define LATINIME_DICT_TOOLKIT_OFFDEVICE_INTERMEDIATE_DICT_HEADER_H
+
+#include <map>
+#include <vector>
+
+#include "dict_toolkit_defines.h"
+
+namespace latinime {
+namespace dicttoolkit {
+
+class OffdeviceIntermediateDictHeader final {
+ public:
+ using AttributeMap = std::map<std::vector<int>, std::vector<int>>;
+
+ OffdeviceIntermediateDictHeader(const AttributeMap &attributesMap)
+ : mAttributeMap(attributesMap) {}
+
+ private:
+ DISALLOW_DEFAULT_CONSTRUCTOR(OffdeviceIntermediateDictHeader);
+ DISALLOW_ASSIGNMENT_OPERATOR(OffdeviceIntermediateDictHeader);
+
+ const AttributeMap mAttributeMap;
+};
+
+} // namespace dicttoolkit
+} // namespace latinime
+#endif // LATINIME_DICT_TOOLKIT_OFFDEVICE_INTERMEDIATE_DICT_HEADER_H
diff --git a/native/dicttoolkit/src/offdevice_intermediate_dict/offdevice_intermediate_dict_pt_node.h b/native/dicttoolkit/src/offdevice_intermediate_dict/offdevice_intermediate_dict_pt_node.h
new file mode 100644
index 000000000..721ccd778
--- /dev/null
+++ b/native/dicttoolkit/src/offdevice_intermediate_dict/offdevice_intermediate_dict_pt_node.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LATINIME_DICT_TOOLKIT_OFFDEVICE_INTERMEDIATE_DICT_PT_NODE_H
+#define LATINIME_DICT_TOOLKIT_OFFDEVICE_INTERMEDIATE_DICT_PT_NODE_H
+
+#include <memory>
+
+#include "dict_toolkit_defines.h"
+#include "offdevice_intermediate_dict/offdevice_intermediate_dict_pt_node_array.h"
+#include "suggest/core/dictionary/property/word_property.h"
+#include "utils/int_array_view.h"
+
+namespace latinime {
+namespace dicttoolkit {
+
+class OffdeviceIntermediateDictPtNode final {
+ public:
+ // Non-terminal
+ OffdeviceIntermediateDictPtNode(const CodePointArrayView ptNodeCodePoints)
+ : mPtNodeCodePoints(ptNodeCodePoints.toVector()), mChildrenPtNodeArray(),
+ mWortProperty(nullptr) {}
+
+ // Terminal
+ OffdeviceIntermediateDictPtNode(const CodePointArrayView ptNodeCodePoints,
+ const WordProperty &wordProperty)
+ : mPtNodeCodePoints(ptNodeCodePoints.toVector()), mChildrenPtNodeArray(),
+ mWortProperty(new WordProperty(wordProperty)) {}
+
+ // Replacing PtNodeCodePoints.
+ OffdeviceIntermediateDictPtNode(const CodePointArrayView ptNodeCodePoints,
+ const OffdeviceIntermediateDictPtNode &ptNode)
+ : mPtNodeCodePoints(ptNodeCodePoints.toVector()),
+ mChildrenPtNodeArray(ptNode.mChildrenPtNodeArray),
+ mWortProperty(new WordProperty(*ptNode.mWortProperty)) {}
+
+ // Replacing WordProperty.
+ OffdeviceIntermediateDictPtNode(const WordProperty &wordProperty,
+ const OffdeviceIntermediateDictPtNode &ptNode)
+ : mPtNodeCodePoints(ptNode.mPtNodeCodePoints),
+ mChildrenPtNodeArray(ptNode.mChildrenPtNodeArray),
+ mWortProperty(new WordProperty(wordProperty)) {}
+
+ const WordProperty *getWordProperty() const {
+ return mWortProperty.get();
+ }
+
+ const CodePointArrayView getPtNodeCodePoints() const {
+ return CodePointArrayView(mPtNodeCodePoints);
+ }
+
+ OffdeviceIntermediateDictPtNodeArray &getChildrenPtNodeArray() {
+ return mChildrenPtNodeArray;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(OffdeviceIntermediateDictPtNode);
+
+ const std::vector<int> mPtNodeCodePoints;
+ OffdeviceIntermediateDictPtNodeArray mChildrenPtNodeArray;
+ const std::unique_ptr<WordProperty> mWortProperty;
+};
+
+} // namespace dicttoolkit
+} // namespace latinime
+#endif // LATINIME_DICT_TOOLKIT_OFFDEVICE_INTERMEDIATE_DICT_PT_NODE_H
diff --git a/native/dicttoolkit/src/offdevice_intermediate_dict/offdevice_intermediate_dict_pt_node_array.h b/native/dicttoolkit/src/offdevice_intermediate_dict/offdevice_intermediate_dict_pt_node_array.h
new file mode 100644
index 000000000..f87456ce0
--- /dev/null
+++ b/native/dicttoolkit/src/offdevice_intermediate_dict/offdevice_intermediate_dict_pt_node_array.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LATINIME_DICT_TOOLKIT_OFFDEVICE_INTERMEDIATE_DICT_PT_NODE_ARRAY_H
+#define LATINIME_DICT_TOOLKIT_OFFDEVICE_INTERMEDIATE_DICT_PT_NODE_ARRAY_H
+
+#include <list>
+#include <memory>
+
+#include "dict_toolkit_defines.h"
+
+namespace latinime {
+namespace dicttoolkit {
+
+class OffdeviceIntermediateDictPtNode;
+
+class OffdeviceIntermediateDictPtNodeArray final {
+ public:
+ const std::list<std::shared_ptr<OffdeviceIntermediateDictPtNode>> &getPtNodeList() const {
+ return mPtNodes;
+ }
+
+ std::list<std::shared_ptr<OffdeviceIntermediateDictPtNode>> *getMutablePtNodeList() {
+ return &mPtNodes;
+ }
+
+ private:
+ DISALLOW_ASSIGNMENT_OPERATOR(OffdeviceIntermediateDictPtNodeArray);
+
+ std::list<std::shared_ptr<OffdeviceIntermediateDictPtNode>> mPtNodes;
+};
+
+} // namespace dicttoolkit
+} // namespace latinime
+#endif // LATINIME_DICT_TOOLKIT_OFFDEVICE_INTERMEDIATE_DICT_PT_NODE_ARRAY_H
diff --git a/native/dicttoolkit/src/utils/arguments_and_options.h b/native/dicttoolkit/src/utils/arguments_and_options.h
new file mode 100644
index 000000000..d8f5985e5
--- /dev/null
+++ b/native/dicttoolkit/src/utils/arguments_and_options.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LATINIME_DICT_TOOLKIT_ARGUMENTS_AND_OPTIONS_H
+#define LATINIME_DICT_TOOLKIT_ARGUMENTS_AND_OPTIONS_H
+
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "dict_toolkit_defines.h"
+
+namespace latinime {
+namespace dicttoolkit {
+
+class ArgumentsAndOptions {
+ public:
+ ArgumentsAndOptions() : mIsValid(false), mOptions(), mArguments() {}
+
+ ArgumentsAndOptions(std::unordered_map<std::string, std::string> &&options,
+ std::unordered_map<std::string, std::vector<std::string>> &&arguments)
+ : mIsValid(true), mOptions(std::move(options)), mArguments(std::move(arguments)) {}
+
+ bool isValid() const {
+ return mIsValid;
+ }
+
+ bool hasOption(const std::string &optionName) const {
+ return mOptions.find(optionName) != mOptions.end();
+ }
+
+ private:
+ DISALLOW_ASSIGNMENT_OPERATOR(ArgumentsAndOptions);
+
+ const bool mIsValid;
+ const std::unordered_map<std::string, std::string> mOptions;
+ const std::unordered_map<std::string, std::vector<std::string>> mArguments;
+};
+} // namespace dicttoolkit
+} // namespace latinime
+#endif // LATINIME_DICT_TOOLKIT_ARGUMENTS_AND_OPTIONS_H
diff --git a/native/dicttoolkit/src/utils/arguments_parser.cpp b/native/dicttoolkit/src/utils/arguments_parser.cpp
new file mode 100644
index 000000000..039dae35b
--- /dev/null
+++ b/native/dicttoolkit/src/utils/arguments_parser.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utils/arguments_parser.h"
+
+namespace latinime {
+namespace dicttoolkit {
+
+const int ArgumentSpec::UNLIMITED_COUNT = -1;
+
+bool ArgumentsParser::validateSpecs() const {
+ for (size_t i = 0; i < mArgumentSpecs.size() ; ++i) {
+ if (mArgumentSpecs[i].getMinCount() != mArgumentSpecs[i].getMaxCount()
+ && i != mArgumentSpecs.size() - 1) {
+ AKLOGE("Variable length argument must be at the end.");
+ return false;
+ }
+ }
+ return true;
+}
+
+void ArgumentsParser::printUsage(const std::string &commandName,
+ const std::string &description) const {
+ printf("Usage: %s", commandName.c_str());
+ for (const auto &option : mOptionSpecs) {
+ const std::string &optionName = option.first;
+ const OptionSpec &spec = option.second;
+ printf(" [-%s", optionName.c_str());
+ if (spec.takeValue()) {
+ printf(" <%s>", spec.getValueName().c_str());
+ }
+ printf("]");
+ }
+ for (const auto &argSpec : mArgumentSpecs) {
+ if (argSpec.getMinCount() == 0 && argSpec.getMaxCount() == 1) {
+ printf(" [<%s>]", argSpec.getName().c_str());
+ } else if (argSpec.getMinCount() == 1 && argSpec.getMaxCount() == 1) {
+ printf(" <%s>", argSpec.getName().c_str());
+ } else if (argSpec.getMinCount() == 0) {
+ printf(" [<%s>...]", argSpec.getName().c_str());
+ } else if (argSpec.getMinCount() == 1) {
+ printf(" <%s>...", argSpec.getName().c_str());
+ }
+ }
+ printf("\n%s\n\n", description.c_str());
+ for (const auto &option : mOptionSpecs) {
+ const std::string &optionName = option.first;
+ const OptionSpec &spec = option.second;
+ printf(" -%s", optionName.c_str());
+ if (spec.takeValue()) {
+ printf(" <%s>", spec.getValueName().c_str());
+ }
+ printf("\t\t\t%s", spec.getDescription().c_str());
+ if (spec.takeValue() && !spec.getDefaultValue().empty()) {
+ printf("\tdefault: %s", spec.getDefaultValue().c_str());
+ }
+ printf("\n");
+ }
+ for (const auto &argSpec : mArgumentSpecs) {
+ printf(" <%s>\t\t\t%s\n", argSpec.getName().c_str(), argSpec.getDescription().c_str());
+ }
+ printf("\n\n");
+}
+
+const ArgumentsAndOptions ArgumentsParser::parseArguments(const int argc, char **argv) const {
+ // TODO: Implement
+ return ArgumentsAndOptions();
+}
+
+} // namespace dicttoolkit
+} // namespace latinime
diff --git a/native/dicttoolkit/src/utils/arguments_parser.h b/native/dicttoolkit/src/utils/arguments_parser.h
new file mode 100644
index 000000000..be2dd8749
--- /dev/null
+++ b/native/dicttoolkit/src/utils/arguments_parser.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LATINIME_DICT_TOOLKIT_ARGUMENTS_PARSER_H
+#define LATINIME_DICT_TOOLKIT_ARGUMENTS_PARSER_H
+
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "dict_toolkit_defines.h"
+#include "utils/arguments_and_options.h"
+
+namespace latinime {
+namespace dicttoolkit {
+
+class OptionSpec {
+ public:
+ // Default constructor and assignment operator is enabled to be used with std::unordered_map.
+ OptionSpec() = default;
+ OptionSpec &operator=(const OptionSpec &) = default;
+
+ static OptionSpec keyValueOption(const std::string &valueName, const std::string &defaultValue,
+ const std::string &description) {
+ return OptionSpec(true /* takeValue */, valueName, defaultValue, description);
+ }
+
+ static OptionSpec switchOption(const std::string &description) {
+ return OptionSpec(false /* takeValue */, "" /* valueName */, "" /* defaultValue */,
+ description);
+ }
+
+ bool takeValue() const { return mTakeValue; }
+ const std::string &getValueName() const { return mValueName; }
+ const std::string &getDefaultValue() const { return mDefaultValue; }
+ const std::string &getDescription() const { return mDescription; }
+
+ private:
+ OptionSpec(const bool takeValue, const std::string &valueName, const std::string &defaultValue,
+ const std::string &description)
+ : mTakeValue(takeValue), mValueName(valueName), mDefaultValue(defaultValue),
+ mDescription(description) {}
+
+ // Whether the option have to be used with a value or just a switch.
+ // e.g. 'f' in "command -f /path/to/file" is mTakeValue == true.
+ // 'f' in "command -f -t" is mTakeValue == false.
+ bool mTakeValue;
+ // Name of the value used to show usage.
+ std::string mValueName;
+ std::string mDefaultValue;
+ std::string mDescription;
+};
+
+class ArgumentSpec {
+ public:
+ static const int UNLIMITED_COUNT;
+
+ static ArgumentSpec singleArgument(const std::string &name, const std::string &description) {
+ return ArgumentSpec(name, 1 /* minCount */, 1 /* maxCount */, description);
+ }
+
+ static ArgumentSpec variableLengthArguments(const std::string &name, const int minCount,
+ const int maxCount, const std::string &description) {
+ return ArgumentSpec(name, minCount, maxCount, description);
+ }
+
+ const std::string &getName() const { return mName; }
+ int getMinCount() const { return mMinCount; }
+ int getMaxCount() const { return mMaxCount; }
+ const std::string &getDescription() const { return mDescription; }
+
+ private:
+ DISALLOW_DEFAULT_CONSTRUCTOR(ArgumentSpec);
+
+ ArgumentSpec(const std::string &name, const int minCount, const int maxCount,
+ const std::string &description)
+ : mName(name), mMinCount(minCount), mMaxCount(maxCount), mDescription(description) {}
+
+ const std::string mName;
+ const int mMinCount;
+ const int mMaxCount;
+ const std::string mDescription;
+};
+
+class ArgumentsParser {
+ public:
+ ArgumentsParser(std::unordered_map<std::string, OptionSpec> &&optionSpecs,
+ std::vector<ArgumentSpec> &&argumentSpecs)
+ : mOptionSpecs(std::move(optionSpecs)), mArgumentSpecs(std::move(argumentSpecs)) {}
+
+ const ArgumentsAndOptions parseArguments(const int argc, char **argv) const;
+ bool validateSpecs() const;
+ void printUsage(const std::string &commandName, const std::string &description) const;
+
+ private:
+ DISALLOW_DEFAULT_CONSTRUCTOR(ArgumentsParser);
+ DISALLOW_ASSIGNMENT_OPERATOR(ArgumentsParser);
+
+ const std::unordered_map<std::string, OptionSpec> mOptionSpecs;
+ const std::vector<ArgumentSpec> mArgumentSpecs;
+};
+
+} // namespace dicttoolkit
+} // namespace latinime
+#endif // LATINIME_DICT_TOOLKIT_ARGUMENTS_PARSER_H
diff --git a/native/dicttoolkit/src/utils/command_utils.cpp b/native/dicttoolkit/src/utils/command_utils.cpp
new file mode 100644
index 000000000..34196425e
--- /dev/null
+++ b/native/dicttoolkit/src/utils/command_utils.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utils/command_utils.h"
+
+#include <cstdio>
+
+#include "command_executors/diff_executor.h"
+#include "command_executors/header_executor.h"
+#include "command_executors/help_executor.h"
+#include "command_executors/info_executor.h"
+#include "command_executors/makedict_executor.h"
+
+namespace latinime {
+namespace dicttoolkit {
+
+/* static */ CommandType CommandUtils::getCommandType(const std::string &commandName) {
+ if (commandName == InfoExecutor::COMMAND_NAME) {
+ return CommandType::Info;
+ } else if (commandName == DiffExecutor::COMMAND_NAME) {
+ return CommandType::Diff;
+ } else if (commandName == MakedictExecutor::COMMAND_NAME) {
+ return CommandType::Makedict;
+ } else if (commandName == HeaderExecutor::COMMAND_NAME) {
+ return CommandType::Header;
+ } else if (commandName == HelpExecutor::COMMAND_NAME) {
+ return CommandType::Help;
+ } else {
+ return CommandType::Unknown;
+ }
+}
+
+/* static */ void CommandUtils::printCommandUnknownMessage(const std::string &programName,
+ const std::string &commandName) {
+ fprintf(stderr, "Command '%s' is unknown. Try '%s %s' for more information.\n",
+ commandName.c_str(), programName.c_str(), HelpExecutor::COMMAND_NAME);
+}
+
+/* static */ std::function<int(int, char **)> CommandUtils::getCommandExecutor(
+ const CommandType commandType) {
+ switch (commandType) {
+ case CommandType::Info:
+ return InfoExecutor::run;
+ case CommandType::Diff:
+ return DiffExecutor::run;
+ case CommandType::Makedict:
+ return MakedictExecutor::run;
+ case CommandType::Header:
+ return HeaderExecutor::run;
+ case CommandType::Help:
+ return HelpExecutor::run;
+ default:
+ return [] (int, char **) -> int {
+ printf("Command executor not found.");
+ return 1;
+ };
+ }
+}
+
+} // namespace dicttoolkit
+} // namespace latinime
diff --git a/native/dicttoolkit/src/utils/command_utils.h b/native/dicttoolkit/src/utils/command_utils.h
new file mode 100644
index 000000000..4a181f194
--- /dev/null
+++ b/native/dicttoolkit/src/utils/command_utils.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LATINIME_DICT_TOOLKIT_COMMAND_UTILS_H
+#define LATINIME_DICT_TOOLKIT_COMMAND_UTILS_H
+
+#include <functional>
+#include <memory>
+#include <string>
+
+#include "dict_toolkit_defines.h"
+
+namespace latinime {
+namespace dicttoolkit {
+
+enum class CommandType : int {
+ Info,
+ Diff,
+ Makedict,
+ Header,
+ Help,
+ Unknown
+};
+
+class CommandUtils {
+public:
+ static CommandType getCommandType(const std::string &commandName);
+ static void printCommandUnknownMessage(const std::string &programName,
+ const std::string &commandName);
+ static std::function<int(int, char **)> getCommandExecutor(const CommandType commandType);
+
+private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(CommandUtils);
+};
+} // namespace dicttoolkit
+} // namespace latinime
+#endif // LATINIME_DICT_TOOLKIT_COMMAND_UTILS_H
diff --git a/native/dicttoolkit/src/utils/utf8_utils.cpp b/native/dicttoolkit/src/utils/utf8_utils.cpp
new file mode 100644
index 000000000..0f349f512
--- /dev/null
+++ b/native/dicttoolkit/src/utils/utf8_utils.cpp
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utils/utf8_utils.h"
+
+#include "utils/char_utils.h"
+
+namespace latinime {
+namespace dicttoolkit {
+
+const size_t Utf8Utils::MAX_SEQUENCE_SIZE_FOR_A_CODE_POINT = 4;
+const uint8_t Utf8Utils::FIRST_BYTE_MARKER_MASKS[] = {0, 0x80, 0xE0, 0xF0, 0xF8};
+const uint8_t Utf8Utils::FIRST_BYTE_MARKERS[] = {0, 0x00, 0xC0, 0xE0, 0xF0};
+const uint8_t Utf8Utils::FIRST_BYTE_CODE_POINT_BITS_MASKS[] = {0, 0x7F, 0x1F, 0x0F, 0x03};
+const int Utf8Utils::MAX_ENCODED_CODE_POINT_VALUES[] = {-1, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF};
+
+const uint8_t Utf8Utils::TRAILING_BYTE_CODE_POINT_BITS_MASK = 0x3F;
+const uint8_t Utf8Utils::TRAILING_BYTE_MARKER = 0x80;
+const size_t Utf8Utils::CODE_POINT_BIT_COUNT_IN_TRAILING_BYTE = 6;
+
+/* static */ std::vector<int> Utf8Utils::getCodePoints(const std::string &utf8Str) {
+ std::vector<int> codePoints;
+ int remainingByteCountForCurrentCodePoint = 0;
+ int currentCodePointSequenceSize = 0;
+ int codePoint = 0;
+ for (const char c : utf8Str) {
+ if (remainingByteCountForCurrentCodePoint == 0) {
+ currentCodePointSequenceSize = getSequenceSizeByCheckingFirstByte(c);
+ if (currentCodePointSequenceSize <= 0) {
+ AKLOGE("%x is an invalid utf8 first byte value.", c);
+ return std::vector<int>();
+ }
+ remainingByteCountForCurrentCodePoint = currentCodePointSequenceSize;
+ codePoint = maskFirstByte(c, remainingByteCountForCurrentCodePoint);
+ } else {
+ codePoint <<= CODE_POINT_BIT_COUNT_IN_TRAILING_BYTE;
+ codePoint += maskTrailingByte(c);
+ }
+ remainingByteCountForCurrentCodePoint--;
+ if (remainingByteCountForCurrentCodePoint == 0) {
+ if (codePoint <= MAX_ENCODED_CODE_POINT_VALUES[currentCodePointSequenceSize - 1]) {
+ AKLOGE("%d bytes encode for codePoint(%x) is a redundant UTF-8 sequence.",
+ currentCodePointSequenceSize, codePoint);
+ return std::vector<int>();
+ }
+ codePoints.push_back(codePoint);
+ }
+ }
+ return codePoints;
+}
+
+/* static */ int Utf8Utils::getSequenceSizeByCheckingFirstByte(const uint8_t firstByte) {
+ for (size_t i = 1; i <= MAX_SEQUENCE_SIZE_FOR_A_CODE_POINT; ++i) {
+ if ((firstByte & FIRST_BYTE_MARKER_MASKS[i]) == FIRST_BYTE_MARKERS[i]) {
+ return i;
+ }
+ }
+ // Not a valid utf8 char first byte.
+ return -1;
+}
+
+/* static */ AK_FORCE_INLINE int Utf8Utils::maskFirstByte(const uint8_t firstByte,
+ const int sequenceSize) {
+ return firstByte & FIRST_BYTE_CODE_POINT_BITS_MASKS[sequenceSize];
+}
+
+/* static */ AK_FORCE_INLINE int Utf8Utils::maskTrailingByte(const uint8_t secondOrLaterByte) {
+ return secondOrLaterByte & TRAILING_BYTE_CODE_POINT_BITS_MASK;
+}
+
+/* static */ std::string Utf8Utils::getUtf8String(const CodePointArrayView codePoints) {
+ std::string utf8String;
+ for (const int codePoint : codePoints) {
+ const int sequenceSize = getSequenceSizeToEncodeCodePoint(codePoint);
+ if (sequenceSize <= 0) {
+ AKLOGE("Cannot encode code point (%d).", codePoint);
+ return std::string();
+ }
+ const int trailingByteCount = sequenceSize - 1;
+ // Output first byte.
+ const int value = codePoint >> (trailingByteCount * CODE_POINT_BIT_COUNT_IN_TRAILING_BYTE);
+ utf8String.push_back(static_cast<char>(value | FIRST_BYTE_MARKERS[sequenceSize]));
+ // Output second and later bytes.
+ for (int i = 1; i < sequenceSize; ++i) {
+ const int shiftAmount = (trailingByteCount - i) * CODE_POINT_BIT_COUNT_IN_TRAILING_BYTE;
+ const int value = (codePoint >> shiftAmount) & TRAILING_BYTE_CODE_POINT_BITS_MASK;
+ utf8String.push_back(static_cast<char>(value | TRAILING_BYTE_MARKER));
+ }
+ }
+ return utf8String;
+}
+
+/* static */ int Utf8Utils::getSequenceSizeToEncodeCodePoint(const int codePoint) {
+ if (codePoint < 0) {
+ return -1;
+ }
+ for (size_t i = 1; i <= MAX_SEQUENCE_SIZE_FOR_A_CODE_POINT; ++i) {
+ if (codePoint <= MAX_ENCODED_CODE_POINT_VALUES[i]) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+} // namespace dicttoolkit
+} // namespace latinime
diff --git a/native/dicttoolkit/src/utils/utf8_utils.h b/native/dicttoolkit/src/utils/utf8_utils.h
new file mode 100644
index 000000000..35818e56c
--- /dev/null
+++ b/native/dicttoolkit/src/utils/utf8_utils.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LATINIME_DICT_TOOLKIT_UTF8_UTILS_H
+#define LATINIME_DICT_TOOLKIT_UTF8_UTILS_H
+
+#include <cstdint>
+#include <string>
+#include <vector>
+
+#include "dict_toolkit_defines.h"
+#include "utils/int_array_view.h"
+
+namespace latinime {
+namespace dicttoolkit {
+
+class Utf8Utils {
+public:
+ static std::vector<int> getCodePoints(const std::string &utf8Str);
+ static std::string getUtf8String(const CodePointArrayView codePoints);
+
+private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Utf8Utils);
+
+ // Values indexed by sequence size.
+ static const size_t MAX_SEQUENCE_SIZE_FOR_A_CODE_POINT;
+ static const uint8_t FIRST_BYTE_MARKER_MASKS[];
+ static const uint8_t FIRST_BYTE_MARKERS[];
+ static const uint8_t FIRST_BYTE_CODE_POINT_BITS_MASKS[];
+ static const int MAX_ENCODED_CODE_POINT_VALUES[];
+
+ static const uint8_t TRAILING_BYTE_CODE_POINT_BITS_MASK;
+ static const uint8_t TRAILING_BYTE_MARKER;
+ static const size_t CODE_POINT_BIT_COUNT_IN_TRAILING_BYTE;
+
+ static int getSequenceSizeByCheckingFirstByte(const uint8_t firstByte);
+ static int maskFirstByte(const uint8_t firstByte, const int encodeSize);
+ static int maskTrailingByte(const uint8_t secondOrLaterByte);
+ static int getSequenceSizeToEncodeCodePoint(const int codePoint);
+};
+} // namespace dicttoolkit
+} // namespace latinime
+#endif // LATINIME_DICT_TOOLKIT_UTF8_UTILS_H
diff --git a/native/dicttoolkit/tests/command_executors/diff_executor_test.cpp b/native/dicttoolkit/tests/command_executors/diff_executor_test.cpp
new file mode 100644
index 000000000..444141427
--- /dev/null
+++ b/native/dicttoolkit/tests/command_executors/diff_executor_test.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "command_executors/diff_executor.h"
+
+#include <gtest/gtest.h>
+
+namespace latinime {
+namespace dicttoolkit {
+namespace {
+
+TEST(DiffExecutorTests, TestArguemntSpecs) {
+ EXPECT_TRUE(DiffExecutor::getArgumentsParser().validateSpecs());
+}
+
+} // namespace
+} // namespace dicttoolkit
+} // namespace latinime
diff --git a/native/dicttoolkit/tests/command_executors/header_executor_test.cpp b/native/dicttoolkit/tests/command_executors/header_executor_test.cpp
new file mode 100644
index 000000000..a94150b01
--- /dev/null
+++ b/native/dicttoolkit/tests/command_executors/header_executor_test.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "command_executors/header_executor.h"
+
+#include <gtest/gtest.h>
+
+namespace latinime {
+namespace dicttoolkit {
+namespace {
+
+TEST(HeaderExecutorTests, TestArguemntSpecs) {
+ EXPECT_TRUE(HeaderExecutor::getArgumentsParser().validateSpecs());
+}
+
+} // namespace
+} // namespace dicttoolkit
+} // namespace latinime
diff --git a/native/dicttoolkit/tests/command_executors/info_executor_test.cpp b/native/dicttoolkit/tests/command_executors/info_executor_test.cpp
new file mode 100644
index 000000000..debe8c601
--- /dev/null
+++ b/native/dicttoolkit/tests/command_executors/info_executor_test.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "command_executors/info_executor.h"
+
+#include <gtest/gtest.h>
+
+namespace latinime {
+namespace dicttoolkit {
+namespace {
+
+TEST(InfoExecutorTests, TestArguemntSpecs) {
+ EXPECT_TRUE(InfoExecutor::getArgumentsParser().validateSpecs());
+}
+
+} // namespace
+} // namespace dicttoolkit
+} // namespace latinime
diff --git a/native/dicttoolkit/tests/command_executors/makedict_executor_test.cpp b/native/dicttoolkit/tests/command_executors/makedict_executor_test.cpp
new file mode 100644
index 000000000..44eb3dc1b
--- /dev/null
+++ b/native/dicttoolkit/tests/command_executors/makedict_executor_test.cpp
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "command_executors/makedict_executor.h"
+
+#include <gtest/gtest.h>
+
+namespace latinime {
+namespace dicttoolkit {
+namespace {
+
+TEST(MakedictExecutorTests, TestArguemntSpecs) {
+ EXPECT_TRUE(MakedictExecutor::getArgumentsParser().validateSpecs());
+}
+
+} // namespace
+} // namespace dicttoolkit
+} // namespace latinime
diff --git a/native/dicttoolkit/tests/dict_toolkit_defines_test.cpp b/native/dicttoolkit/tests/dict_toolkit_defines_test.cpp
new file mode 100644
index 000000000..3445bd0c5
--- /dev/null
+++ b/native/dicttoolkit/tests/dict_toolkit_defines_test.cpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "dict_toolkit_defines.h"
+
+#include <gtest/gtest.h>
+
+namespace latinime {
+namespace dicttoolkit {
+namespace {
+
+// Initial trivial test case.
+TEST(DictToolkitDefinesTest, TestKeycodeSpace) {
+ EXPECT_EQ(' ', KEYCODE_SPACE);
+}
+
+} // namespace
+} // namespace dicttoolkit
+} // namespace latinime
diff --git a/native/dicttoolkit/tests/offdevice_intermediate_dict/offdevice_intermediate_dict_test.cpp b/native/dicttoolkit/tests/offdevice_intermediate_dict/offdevice_intermediate_dict_test.cpp
new file mode 100644
index 000000000..f2e24ab5f
--- /dev/null
+++ b/native/dicttoolkit/tests/offdevice_intermediate_dict/offdevice_intermediate_dict_test.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "offdevice_intermediate_dict/offdevice_intermediate_dict.h"
+
+#include <gtest/gtest.h>
+
+#include <vector>
+
+#include "suggest/core/dictionary/property/word_property.h"
+#include "utils/int_array_view.h"
+
+namespace latinime {
+namespace dicttoolkit {
+namespace {
+
+const std::vector<int> getCodePointVector(const char *str) {
+ std::vector<int> codePoints;
+ while (*str) {
+ codePoints.push_back(*str);
+ ++str;
+ }
+ return codePoints;
+}
+
+const WordProperty getDummpWordProperty(const std::vector<int> &&codePoints) {
+ return WordProperty(std::move(codePoints), UnigramProperty(), std::vector<NgramProperty>());
+}
+
+TEST(OffdeviceIntermediateDictTest, TestAddWordProperties) {
+ OffdeviceIntermediateDict dict = OffdeviceIntermediateDict(
+ OffdeviceIntermediateDictHeader(OffdeviceIntermediateDictHeader::AttributeMap()));
+ EXPECT_EQ(nullptr, dict.getWordProperty(CodePointArrayView()));
+
+ const WordProperty wordProperty0 = getDummpWordProperty(getCodePointVector("abcd"));
+ EXPECT_TRUE(dict.addWord(wordProperty0));
+ EXPECT_NE(nullptr, dict.getWordProperty(wordProperty0.getCodePoints()));
+
+ const WordProperty wordProperty1 = getDummpWordProperty(getCodePointVector("efgh"));
+ EXPECT_TRUE(dict.addWord(wordProperty1));
+ EXPECT_NE(nullptr, dict.getWordProperty(wordProperty1.getCodePoints()));
+
+ const WordProperty wordProperty2 = getDummpWordProperty(getCodePointVector("ab"));
+ EXPECT_TRUE(dict.addWord(wordProperty2));
+ EXPECT_NE(nullptr, dict.getWordProperty(wordProperty2.getCodePoints()));
+
+ const WordProperty wordProperty3 = getDummpWordProperty(getCodePointVector("abcdefg"));
+ EXPECT_TRUE(dict.addWord(wordProperty3));
+ EXPECT_NE(nullptr, dict.getWordProperty(wordProperty3.getCodePoints()));
+
+ const WordProperty wordProperty4 = getDummpWordProperty(getCodePointVector("efef"));
+ EXPECT_TRUE(dict.addWord(wordProperty4));
+ EXPECT_NE(nullptr, dict.getWordProperty(wordProperty4.getCodePoints()));
+
+ const WordProperty wordProperty5 = getDummpWordProperty(getCodePointVector("ef"));
+ EXPECT_TRUE(dict.addWord(wordProperty5));
+ EXPECT_NE(nullptr, dict.getWordProperty(wordProperty5.getCodePoints()));
+
+ const WordProperty wordProperty6 = getDummpWordProperty(getCodePointVector("abcd"));
+ EXPECT_FALSE(dict.addWord(wordProperty6)) << "Adding the same word multiple times should fail.";
+
+ EXPECT_NE(nullptr, dict.getWordProperty(wordProperty0.getCodePoints()));
+ EXPECT_NE(nullptr, dict.getWordProperty(wordProperty1.getCodePoints()));
+ EXPECT_NE(nullptr, dict.getWordProperty(wordProperty2.getCodePoints()));
+ EXPECT_NE(nullptr, dict.getWordProperty(wordProperty3.getCodePoints()));
+ EXPECT_NE(nullptr, dict.getWordProperty(wordProperty4.getCodePoints()));
+ EXPECT_NE(nullptr, dict.getWordProperty(wordProperty5.getCodePoints()));
+}
+
+} // namespace
+} // namespace dicttoolkit
+} // namespace latinime
diff --git a/native/dicttoolkit/tests/utils/command_utils_test.cpp b/native/dicttoolkit/tests/utils/command_utils_test.cpp
new file mode 100644
index 000000000..9d79c9dd9
--- /dev/null
+++ b/native/dicttoolkit/tests/utils/command_utils_test.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utils/command_utils.h"
+
+#include <gtest/gtest.h>
+
+namespace latinime {
+namespace dicttoolkit {
+namespace {
+
+TEST(CommandUtilsTests, TestGetCommandType) {
+ EXPECT_EQ(CommandUtils::getCommandType(""), CommandType::Unknown);
+ EXPECT_EQ(CommandUtils::getCommandType("abc"), CommandType::Unknown);
+ EXPECT_EQ(CommandUtils::getCommandType("info"), CommandType::Info);
+ EXPECT_EQ(CommandUtils::getCommandType("diff"), CommandType::Diff);
+ EXPECT_EQ(CommandUtils::getCommandType("makedict"), CommandType::Makedict);
+ EXPECT_EQ(CommandUtils::getCommandType("header"), CommandType::Header);
+ EXPECT_EQ(CommandUtils::getCommandType("help"), CommandType::Help);
+}
+
+} // namespace
+} // namespace dicttoolkit
+} // namespace latinime
diff --git a/native/dicttoolkit/tests/utils/utf8_utils_test.cpp b/native/dicttoolkit/tests/utils/utf8_utils_test.cpp
new file mode 100644
index 000000000..9c59a8b05
--- /dev/null
+++ b/native/dicttoolkit/tests/utils/utf8_utils_test.cpp
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "utils/utf8_utils.h"
+
+#include <gtest/gtest.h>
+
+#include <vector>
+
+#include "utils/int_array_view.h"
+
+namespace latinime {
+namespace dicttoolkit {
+namespace {
+
+TEST(Utf8UtilsTests, TestGetCodePoints) {
+ {
+ const std::vector<int> codePoints = Utf8Utils::getCodePoints("");
+ EXPECT_EQ(0u, codePoints.size());
+ }
+ {
+ const std::vector<int> codePoints = Utf8Utils::getCodePoints("test");
+ EXPECT_EQ(4u, codePoints.size());
+ EXPECT_EQ('t', codePoints[0]);
+ EXPECT_EQ('e', codePoints[1]);
+ EXPECT_EQ('s', codePoints[2]);
+ EXPECT_EQ('t', codePoints[3]);
+ }
+ {
+ const std::vector<int> codePoints = Utf8Utils::getCodePoints(u8"\u3042a\u03C2\u0410");
+ EXPECT_EQ(4u, codePoints.size());
+ EXPECT_EQ(0x3042, codePoints[0]); // HIRAGANA LETTER A
+ EXPECT_EQ('a', codePoints[1]);
+ EXPECT_EQ(0x03C2, codePoints[2]); // CYRILLIC CAPITAL LETTER A
+ EXPECT_EQ(0x0410, codePoints[3]); // GREEK SMALL LETTER FINAL SIGMA
+ }
+ {
+ const std::vector<int> codePoints = Utf8Utils::getCodePoints(u8"\U0001F36A?\U0001F752");
+ EXPECT_EQ(3u, codePoints.size());
+ EXPECT_EQ(0x1F36A, codePoints[0]); // COOKIE
+ EXPECT_EQ('?', codePoints[1]);
+ EXPECT_EQ(0x1F752, codePoints[2]); // ALCHEMICAL SYMBOL FOR STARRED TRIDENT
+ }
+
+ // Redundant UTF-8 sequences must be rejected.
+ EXPECT_TRUE(Utf8Utils::getCodePoints("\xC0\xAF").empty());
+ EXPECT_TRUE(Utf8Utils::getCodePoints("\xE0\x80\xAF").empty());
+ EXPECT_TRUE(Utf8Utils::getCodePoints("\xF0\x80\x80\xAF").empty());
+}
+
+TEST(Utf8UtilsTests, TestGetUtf8String) {
+ {
+ const std::vector<int> codePoints = {'t', 'e', 's', 't'};
+ EXPECT_EQ("test", Utf8Utils::getUtf8String(CodePointArrayView(codePoints)));
+ }
+ {
+ const std::vector<int> codePoints = {
+ 0x00E0 /* LATIN SMALL LETTER A WITH GRAVE */,
+ 0x03C2 /* GREEK SMALL LETTER FINAL SIGMA */,
+ 0x0430 /* CYRILLIC SMALL LETTER A */,
+ 0x3042 /* HIRAGANA LETTER A */,
+ 0x1F36A /* COOKIE */,
+ 0x1F752 /* ALCHEMICAL SYMBOL FOR STARRED TRIDENT */
+ };
+ EXPECT_EQ(u8"\u00E0\u03C2\u0430\u3042\U0001F36A\U0001F752",
+ Utf8Utils::getUtf8String(CodePointArrayView(codePoints)));
+ }
+}
+
+} // namespace
+} // namespace dicttoolkit
+} // namespace latinime
diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
index 118f600bb..9c065e0d1 100644
--- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
+++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
@@ -35,6 +35,7 @@
#include "utils/int_array_view.h"
#include "utils/jni_data_utils.h"
#include "utils/log_utils.h"
+#include "utils/profiler.h"
#include "utils/time_keeper.h"
namespace latinime {
@@ -43,8 +44,8 @@ class ProximityInfo;
static jlong latinime_BinaryDictionary_open(JNIEnv *env, jclass clazz, jstring sourceDir,
jlong dictOffset, jlong dictSize, jboolean isUpdatable) {
- PROF_OPEN;
- PROF_START(66);
+ PROF_INIT;
+ PROF_TIMER_START(66);
const jsize sourceDirUtf8Length = env->GetStringUTFLength(sourceDir);
if (sourceDirUtf8Length <= 0) {
AKLOGE("DICT: Can't get sourceDir string");
@@ -63,8 +64,7 @@ static jlong latinime_BinaryDictionary_open(JNIEnv *env, jclass clazz, jstring s
Dictionary *const dictionary =
new Dictionary(env, std::move(dictionaryStructureWithBufferPolicy));
- PROF_END(66);
- PROF_CLOSE;
+ PROF_TIMER_END(66);
return reinterpret_cast<jlong>(dictionary);
}
@@ -586,7 +586,7 @@ static bool latinime_BinaryDictionary_migrateNative(JNIEnv *env, jclass clazz, j
}
if (!dictionaryStructureWithBufferPolicy->addUnigramEntry(
CodePointArrayView(wordCodePoints, wordCodePointCount),
- wordProperty.getUnigramProperty())) {
+ &wordProperty.getUnigramProperty())) {
LogUtils::logToJava(env, "Cannot add unigram to the new dict.");
return false;
}
@@ -605,7 +605,7 @@ static bool latinime_BinaryDictionary_migrateNative(JNIEnv *env, jclass clazz, j
return false;
}
}
- for (const NgramProperty &ngramProperty : *wordProperty.getNgramProperties()) {
+ for (const NgramProperty &ngramProperty : wordProperty.getNgramProperties()) {
if (!dictionaryStructureWithBufferPolicy->addNgramEntry(&ngramProperty)) {
LogUtils::logToJava(env, "Cannot add ngram to the new dict.");
return false;
diff --git a/native/jni/src/defines.h b/native/jni/src/defines.h
index 885118524..0e67b4d5a 100644
--- a/native/jni/src/defines.h
+++ b/native/jni/src/defines.h
@@ -23,10 +23,10 @@
#define AK_FORCE_INLINE inline
#endif // __GNUC__
-#if defined(FLAG_DO_PROFILE) || defined(FLAG_DBG)
+#if defined(FLAG_DBG)
#undef AK_FORCE_INLINE
#define AK_FORCE_INLINE inline
-#endif // defined(FLAG_DO_PROFILE) || defined(FLAG_DBG)
+#endif // defined(FLAG_DBG)
// Must be equal to Constants.Dictionary.MAX_WORD_LENGTH in Java
#define MAX_WORD_LENGTH 48
@@ -172,69 +172,6 @@ static inline void showStackTrace() {
#define INTS_TO_CHARS(input, length, output)
#endif // defined(FLAG_DO_PROFILE) || defined(FLAG_DBG)
-#ifdef FLAG_DO_PROFILE
-// Profiler
-#include <time.h>
-
-#define PROF_BUF_SIZE 100
-static float profile_buf[PROF_BUF_SIZE];
-static float profile_old[PROF_BUF_SIZE];
-static unsigned int profile_counter[PROF_BUF_SIZE];
-
-#define PROF_RESET prof_reset()
-#define PROF_COUNT(prof_buf_id) ++profile_counter[prof_buf_id]
-#define PROF_OPEN do { PROF_RESET; PROF_START(PROF_BUF_SIZE - 1); } while (0)
-#define PROF_START(prof_buf_id) do { \
- PROF_COUNT(prof_buf_id); profile_old[prof_buf_id] = (clock()); } while (0)
-#define PROF_CLOSE do { PROF_END(PROF_BUF_SIZE - 1); PROF_OUTALL; } while (0)
-#define PROF_END(prof_buf_id) profile_buf[prof_buf_id] += ((clock()) - profile_old[prof_buf_id])
-#define PROF_CLOCKOUT(prof_buf_id) \
- AKLOGI("%s : clock is %f", __FUNCTION__, (clock() - profile_old[prof_buf_id]))
-#define PROF_OUTALL do { AKLOGI("--- %s ---", __FUNCTION__); prof_out(); } while (0)
-
-static inline void prof_reset(void) {
- for (int i = 0; i < PROF_BUF_SIZE; ++i) {
- profile_buf[i] = 0;
- profile_old[i] = 0;
- profile_counter[i] = 0;
- }
-}
-
-static inline void prof_out(void) {
- if (profile_counter[PROF_BUF_SIZE - 1] != 1) {
- AKLOGI("Error: You must call PROF_OPEN before PROF_CLOSE.");
- }
- AKLOGI("Total time is %6.3f ms.",
- profile_buf[PROF_BUF_SIZE - 1] * 1000.0f / static_cast<float>(CLOCKS_PER_SEC));
- float all = 0.0f;
- for (int i = 0; i < PROF_BUF_SIZE - 1; ++i) {
- all += profile_buf[i];
- }
- if (all < 1.0f) all = 1.0f;
- for (int i = 0; i < PROF_BUF_SIZE - 1; ++i) {
- if (profile_buf[i] > 0.0f) {
- AKLOGI("(%d): Used %4.2f%%, %8.4f ms. Called %d times.",
- i, (profile_buf[i] * 100.0f / all),
- profile_buf[i] * 1000.0f / static_cast<float>(CLOCKS_PER_SEC),
- profile_counter[i]);
- }
- }
-}
-
-#else // FLAG_DO_PROFILE
-#define PROF_BUF_SIZE 0
-#define PROF_RESET
-#define PROF_COUNT(prof_buf_id)
-#define PROF_OPEN
-#define PROF_START(prof_buf_id)
-#define PROF_CLOSE
-#define PROF_END(prof_buf_id)
-#define PROF_CLOCK_OUT(prof_buf_id)
-#define PROF_CLOCKOUT(prof_buf_id)
-#define PROF_OUTALL
-
-#endif // FLAG_DO_PROFILE
-
#ifdef FLAG_DBG
#define DEBUG_DICT true
#define DEBUG_DICT_FULL false
diff --git a/native/jni/src/suggest/core/dictionary/error_type_utils.cpp b/native/jni/src/suggest/core/dictionary/error_type_utils.cpp
index 1e2494e92..8f07ce275 100644
--- a/native/jni/src/suggest/core/dictionary/error_type_utils.cpp
+++ b/native/jni/src/suggest/core/dictionary/error_type_utils.cpp
@@ -31,6 +31,7 @@ const ErrorTypeUtils::ErrorType ErrorTypeUtils::NEW_WORD = 0x100;
const ErrorTypeUtils::ErrorType ErrorTypeUtils::ERRORS_TREATED_AS_AN_EXACT_MATCH =
NOT_AN_ERROR | MATCH_WITH_WRONG_CASE | MATCH_WITH_MISSING_ACCENT | MATCH_WITH_DIGRAPH;
+const ErrorTypeUtils::ErrorType ErrorTypeUtils::ERRORS_TREATED_AS_A_PERFECT_MATCH = NOT_AN_ERROR;
const ErrorTypeUtils::ErrorType
ErrorTypeUtils::ERRORS_TREATED_AS_AN_EXACT_MATCH_WITH_INTENTIONAL_OMISSION =
diff --git a/native/jni/src/suggest/core/dictionary/error_type_utils.h b/native/jni/src/suggest/core/dictionary/error_type_utils.h
index fd1d5fcff..e92c509fa 100644
--- a/native/jni/src/suggest/core/dictionary/error_type_utils.h
+++ b/native/jni/src/suggest/core/dictionary/error_type_utils.h
@@ -52,6 +52,10 @@ class ErrorTypeUtils {
return (containedErrorTypes & ~ERRORS_TREATED_AS_AN_EXACT_MATCH) == 0;
}
+ static bool isPerfectMatch(const ErrorType containedErrorTypes) {
+ return (containedErrorTypes & ~ERRORS_TREATED_AS_A_PERFECT_MATCH) == 0;
+ }
+
static bool isExactMatchWithIntentionalOmission(const ErrorType containedErrorTypes) {
return (containedErrorTypes
& ~ERRORS_TREATED_AS_AN_EXACT_MATCH_WITH_INTENTIONAL_OMISSION) == 0;
@@ -73,6 +77,7 @@ class ErrorTypeUtils {
DISALLOW_IMPLICIT_CONSTRUCTORS(ErrorTypeUtils);
static const ErrorType ERRORS_TREATED_AS_AN_EXACT_MATCH;
+ static const ErrorType ERRORS_TREATED_AS_A_PERFECT_MATCH;
static const ErrorType ERRORS_TREATED_AS_AN_EXACT_MATCH_WITH_INTENTIONAL_OMISSION;
};
} // namespace latinime
diff --git a/native/jni/src/suggest/core/dictionary/property/historical_info.h b/native/jni/src/suggest/core/dictionary/property/historical_info.h
index f9bd6fd8c..e5ce1ea25 100644
--- a/native/jni/src/suggest/core/dictionary/property/historical_info.h
+++ b/native/jni/src/suggest/core/dictionary/property/historical_info.h
@@ -38,6 +38,7 @@ class HistoricalInfo {
return mTimestamp;
}
+ // TODO: Remove
int getLevel() const {
return mLevel;
}
diff --git a/native/jni/src/suggest/core/dictionary/property/word_property.h b/native/jni/src/suggest/core/dictionary/property/word_property.h
index b5314faaa..d4db3f09f 100644
--- a/native/jni/src/suggest/core/dictionary/property/word_property.h
+++ b/native/jni/src/suggest/core/dictionary/property/word_property.h
@@ -23,6 +23,7 @@
#include "jni.h"
#include "suggest/core/dictionary/property/ngram_property.h"
#include "suggest/core/dictionary/property/unigram_property.h"
+#include "utils/int_array_view.h"
namespace latinime {
@@ -33,10 +34,10 @@ class WordProperty {
WordProperty()
: mCodePoints(), mUnigramProperty(), mNgrams() {}
- WordProperty(const std::vector<int> &&codePoints, const UnigramProperty *const unigramProperty,
- const std::vector<NgramProperty> *const ngrams)
- : mCodePoints(std::move(codePoints)), mUnigramProperty(*unigramProperty),
- mNgrams(*ngrams) {}
+ WordProperty(const std::vector<int> &&codePoints, const UnigramProperty &unigramProperty,
+ const std::vector<NgramProperty> &ngrams)
+ : mCodePoints(std::move(codePoints)), mUnigramProperty(unigramProperty),
+ mNgrams(ngrams) {}
void outputProperties(JNIEnv *const env, jintArray outCodePoints, jbooleanArray outFlags,
jintArray outProbabilityInfo, jobject outNgramPrevWordsArray,
@@ -44,12 +45,16 @@ class WordProperty {
jobject outNgramProbabilities, jobject outShortcutTargets,
jobject outShortcutProbabilities) const;
- const UnigramProperty *getUnigramProperty() const {
- return &mUnigramProperty;
+ const CodePointArrayView getCodePoints() const {
+ return CodePointArrayView(mCodePoints);
}
- const std::vector<NgramProperty> *getNgramProperties() const {
- return &mNgrams;
+ const UnigramProperty &getUnigramProperty() const {
+ return mUnigramProperty;
+ }
+
+ const std::vector<NgramProperty> &getNgramProperties() const {
+ return mNgrams;
}
private:
diff --git a/native/jni/src/suggest/core/policy/scoring.h b/native/jni/src/suggest/core/policy/scoring.h
index ce3684a1c..b9dda83ad 100644
--- a/native/jni/src/suggest/core/policy/scoring.h
+++ b/native/jni/src/suggest/core/policy/scoring.h
@@ -30,7 +30,7 @@ class Scoring {
public:
virtual int calculateFinalScore(const float compoundDistance, const int inputSize,
const ErrorTypeUtils::ErrorType containedErrorTypes, const bool forceCommit,
- const bool boostExactMatches) const = 0;
+ const bool boostExactMatches, const bool hasProbabilityZero) const = 0;
virtual void getMostProbableString(const DicTraverseSession *const traverseSession,
const float weightOfLangModelVsSpatialModel,
SuggestionResults *const outSuggestionResults) const = 0;
diff --git a/native/jni/src/suggest/core/result/suggestions_output_utils.cpp b/native/jni/src/suggest/core/result/suggestions_output_utils.cpp
index 3283f6deb..74db95953 100644
--- a/native/jni/src/suggest/core/result/suggestions_output_utils.cpp
+++ b/native/jni/src/suggest/core/result/suggestions_output_utils.cpp
@@ -76,6 +76,52 @@ const int SuggestionsOutputUtils::MIN_LEN_FOR_MULTI_WORD_AUTOCORRECT = 16;
weightOfLangModelVsSpatialModelToOutputSuggestions, outSuggestionResults);
}
+/* static */ bool SuggestionsOutputUtils::shouldBlockWord(
+ const SuggestOptions *const suggestOptions, const DicNode *const terminalDicNode,
+ const WordAttributes wordAttributes, const bool isLastWord) {
+ const bool currentWordExactMatch =
+ ErrorTypeUtils::isExactMatch(terminalDicNode->getContainedErrorTypes());
+ // When we have to block offensive words, non-exact matched offensive words should not be
+ // output.
+ const bool shouldBlockOffensiveWords = suggestOptions->blockOffensiveWords();
+
+ const bool isBlockedOffensiveWord = shouldBlockOffensiveWords &&
+ wordAttributes.isPossiblyOffensive();
+
+ // This function is called in two situations:
+ //
+ // 1) At the end of a search, in which case terminalDicNode will point to the last DicNode
+ // of the search, and isLastWord will be true.
+ // "fuck"
+ // |
+ // \ terminalDicNode (isLastWord=true, currentWordExactMatch=true)
+ // In this case, if the current word is an exact match, we will always let the word
+ // through, even if the user is blocking offensive words (it's exactly what they typed!)
+ //
+ // 2) In the middle of the search, when we hit a terminal node, to decide whether or not
+ // to start a new search at root, to try to match the rest of the input. In this case,
+ // terminalDicNode will point to the terminal node we just hit, and isLastWord will be
+ // false.
+ // "fuckvthis"
+ // |
+ // \ terminalDicNode (isLastWord=false, currentWordExactMatch=true)
+ //
+ // In this case, we should NOT allow the match through (correcting "fuckthis" to "fuck this"
+ // when offensive words are blocked would be a bad idea).
+ //
+ // In the case of a multi-word correction where the offensive word is typed last (eg.
+ // for the input "allfuck"), this function will be called with isLastWord==true, but
+ // currentWordExactMatch==false. So we are OK in this case as well.
+ // "allfuck"
+ // |
+ // \ terminalDicNode (isLastWord=true, currentWordExactMatch=false)
+ if (isLastWord && currentWordExactMatch) {
+ return false;
+ } else {
+ return isBlockedOffensiveWord;
+ }
+}
+
/* static */ void SuggestionsOutputUtils::outputSuggestionsOfDicNode(
const Scoring *const scoringPolicy, DicTraverseSession *traverseSession,
const DicNode *const terminalDicNode, const float weightOfLangModelVsSpatialModel,
@@ -98,24 +144,16 @@ const int SuggestionsOutputUtils::MIN_LEN_FOR_MULTI_WORD_AUTOCORRECT = 16;
const bool isExactMatchWithIntentionalOmission =
ErrorTypeUtils::isExactMatchWithIntentionalOmission(
terminalDicNode->getContainedErrorTypes());
- const bool isFirstCharUppercase = terminalDicNode->isFirstCharUppercase();
- // Heuristic: We exclude probability=0 first-char-uppercase words from exact match.
- // (e.g. "AMD" and "and")
- const bool isSafeExactMatch = isExactMatch
- && !(wordAttributes.isPossiblyOffensive() && isFirstCharUppercase);
const int outputTypeFlags =
(wordAttributes.isPossiblyOffensive() ? Dictionary::KIND_FLAG_POSSIBLY_OFFENSIVE : 0)
- | ((isSafeExactMatch && boostExactMatches) ? Dictionary::KIND_FLAG_EXACT_MATCH : 0)
+ | ((isExactMatch && boostExactMatches) ? Dictionary::KIND_FLAG_EXACT_MATCH : 0)
| (isExactMatchWithIntentionalOmission ?
Dictionary::KIND_FLAG_EXACT_MATCH_WITH_INTENTIONAL_OMISSION : 0);
-
// Entries that are blacklisted or do not represent a word should not be output.
const bool isValidWord = !(wordAttributes.isBlacklisted() || wordAttributes.isNotAWord());
- // When we have to block offensive words, non-exact matched offensive words should not be
- // output.
- const bool blockOffensiveWords = traverseSession->getSuggestOptions()->blockOffensiveWords();
- const bool isBlockedOffensiveWord = blockOffensiveWords && wordAttributes.isPossiblyOffensive()
- && !isSafeExactMatch;
+
+ const bool shouldBlockThisWord = shouldBlockWord(traverseSession->getSuggestOptions(),
+ terminalDicNode, wordAttributes, true /* isLastWord */);
// Increase output score of top typing suggestion to ensure autocorrection.
// TODO: Better integration with java side autocorrection logic.
@@ -123,11 +161,11 @@ const int SuggestionsOutputUtils::MIN_LEN_FOR_MULTI_WORD_AUTOCORRECT = 16;
compoundDistance, traverseSession->getInputSize(),
terminalDicNode->getContainedErrorTypes(),
(forceCommitMultiWords && terminalDicNode->hasMultipleWords()),
- boostExactMatches);
+ boostExactMatches, wordAttributes.getProbability() == 0);
// Don't output invalid or blocked offensive words. However, we still need to submit their
// shortcuts if any.
- if (isValidWord && !isBlockedOffensiveWord) {
+ if (isValidWord && !shouldBlockThisWord) {
int codePoints[MAX_WORD_LENGTH];
terminalDicNode->outputResult(codePoints);
const int indexToPartialCommit = outputSecondWordFirstLetterInputIndex ?
diff --git a/native/jni/src/suggest/core/result/suggestions_output_utils.h b/native/jni/src/suggest/core/result/suggestions_output_utils.h
index bf8497828..eca1f78b2 100644
--- a/native/jni/src/suggest/core/result/suggestions_output_utils.h
+++ b/native/jni/src/suggest/core/result/suggestions_output_utils.h
@@ -18,6 +18,7 @@
#define LATINIME_SUGGESTIONS_OUTPUT_UTILS
#include "defines.h"
+#include "suggest/core/dictionary/word_attributes.h"
namespace latinime {
@@ -25,11 +26,19 @@ class BinaryDictionaryShortcutIterator;
class DicNode;
class DicTraverseSession;
class Scoring;
+class SuggestOptions;
class SuggestionResults;
class SuggestionsOutputUtils {
public:
/**
+ * Returns true if we should block the incoming word, in the context of the user's
+ * preferences to include or not include possibly offensive words
+ */
+ static bool shouldBlockWord(const SuggestOptions *const suggestOptions,
+ const DicNode *const terminalDicNode, const WordAttributes wordAttributes,
+ const bool isLastWord);
+ /**
* Outputs the final list of suggestions (i.e., terminal nodes).
*/
static void outputSuggestions(const Scoring *const scoringPolicy,
diff --git a/native/jni/src/suggest/core/suggest.cpp b/native/jni/src/suggest/core/suggest.cpp
index 68a36454e..e5e9b46bf 100644
--- a/native/jni/src/suggest/core/suggest.cpp
+++ b/native/jni/src/suggest/core/suggest.cpp
@@ -29,6 +29,7 @@
#include "suggest/core/result/suggestions_output_utils.h"
#include "suggest/core/session/dic_traverse_session.h"
#include "suggest/core/suggest_options.h"
+#include "utils/profiler.h"
namespace latinime {
@@ -48,8 +49,8 @@ void Suggest::getSuggestions(ProximityInfo *pInfo, void *traverseSession,
int *inputXs, int *inputYs, int *times, int *pointerIds, int *inputCodePoints,
int inputSize, const float weightOfLangModelVsSpatialModel,
SuggestionResults *const outSuggestionResults) const {
- PROF_OPEN;
- PROF_START(0);
+ PROF_INIT;
+ PROF_TIMER_START(0);
const float maxSpatialDistance = TRAVERSAL->getMaxSpatialDistance();
DicTraverseSession *tSession = static_cast<DicTraverseSession *>(traverseSession);
tSession->setupForGetSuggestions(pInfo, inputCodePoints, inputSize, inputXs, inputYs, times,
@@ -57,8 +58,8 @@ void Suggest::getSuggestions(ProximityInfo *pInfo, void *traverseSession,
// TODO: Add the way to evaluate cache
initializeSearch(tSession);
- PROF_END(0);
- PROF_START(1);
+ PROF_TIMER_END(0);
+ PROF_TIMER_START(1);
// keep expanding search dicNodes until all have terminated.
while (tSession->getDicTraverseCache()->activeSize() > 0) {
@@ -66,12 +67,11 @@ void Suggest::getSuggestions(ProximityInfo *pInfo, void *traverseSession,
tSession->getDicTraverseCache()->advanceActiveDicNodes();
tSession->getDicTraverseCache()->advanceInputIndex(inputSize);
}
- PROF_END(1);
- PROF_START(2);
+ PROF_TIMER_END(1);
+ PROF_TIMER_START(2);
SuggestionsOutputUtils::outputSuggestions(
SCORING, tSession, weightOfLangModelVsSpatialModel, outSuggestionResults);
- PROF_END(2);
- PROF_CLOSE;
+ PROF_TIMER_END(2);
}
/**
@@ -416,6 +416,11 @@ void Suggest::createNextWordDicNode(DicTraverseSession *traverseSession, DicNode
traverseSession->getDictionaryStructurePolicy()->getWordAttributesInContext(
dicNode->getPrevWordIds(), dicNode->getWordId(),
traverseSession->getMultiBigramMap());
+ if (SuggestionsOutputUtils::shouldBlockWord(traverseSession->getSuggestOptions(),
+ dicNode, wordAttributes, false /* isLastWord */)) {
+ return;
+ }
+
if (!TRAVERSAL->isGoodToTraverseNextWord(dicNode, wordAttributes.getProbability())) {
return;
}
diff --git a/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h
index 44c2f443f..7a5acd7d5 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h
@@ -134,15 +134,17 @@ class HeaderPolicy : public DictionaryHeaderStructurePolicy {
// same so we use them for both here.
switch (mDictFormatVersion) {
case FormatUtils::VERSION_2:
- return FormatUtils::VERSION_2;
case FormatUtils::VERSION_201:
- return FormatUtils::VERSION_201;
+ AKLOGE("Dictionary versions 2 and 201 are incompatible with this version");
+ return FormatUtils::UNKNOWN_VERSION;
+ case FormatUtils::VERSION_202:
+ return FormatUtils::VERSION_202;
case FormatUtils::VERSION_4_ONLY_FOR_TESTING:
return FormatUtils::VERSION_4_ONLY_FOR_TESTING;
- case FormatUtils::VERSION_4:
- return FormatUtils::VERSION_4;
- case FormatUtils::VERSION_4_DEV:
- return FormatUtils::VERSION_4_DEV;
+ case FormatUtils::VERSION_402:
+ return FormatUtils::VERSION_402;
+ case FormatUtils::VERSION_403:
+ return FormatUtils::VERSION_403;
default:
return FormatUtils::UNKNOWN_VERSION;
}
@@ -245,7 +247,7 @@ class HeaderPolicy : public DictionaryHeaderStructurePolicy {
}
bool supportsBeginningOfSentence() const {
- return mDictFormatVersion >= FormatUtils::VERSION_4;
+ return mDictFormatVersion >= FormatUtils::VERSION_402;
}
const int *getCodePointTable() const {
diff --git a/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.cpp
index 41a8b13b8..19ed0d468 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.cpp
@@ -111,11 +111,12 @@ typedef DictionaryHeaderStructurePolicy::AttributeMap AttributeMap;
switch (version) {
case FormatUtils::VERSION_2:
case FormatUtils::VERSION_201:
- // Version 2 or 201 dictionary writing is not supported.
+ case FormatUtils::VERSION_202:
+ // None of the static dictionaries (v2x) support writing
return false;
case FormatUtils::VERSION_4_ONLY_FOR_TESTING:
- case FormatUtils::VERSION_4:
- case FormatUtils::VERSION_4_DEV:
+ case FormatUtils::VERSION_402:
+ case FormatUtils::VERSION_403:
return buffer->writeUintAndAdvancePosition(version /* data */,
HEADER_DICTIONARY_VERSION_SIZE, writingPos);
default:
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/bigram_dict_content.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/bigram_dict_content.cpp
index 9e1adff70..15ac88319 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/bigram_dict_content.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/bigram_dict_content.cpp
@@ -65,6 +65,8 @@ const BigramEntry BigramDictContent::getBigramEntryAndAdvancePosition(
(encodedTargetTerminalId == Ver4DictConstants::INVALID_BIGRAM_TARGET_TERMINAL_ID) ?
Ver4DictConstants::NOT_A_TERMINAL_ID : encodedTargetTerminalId;
if (mHasHistoricalInfo) {
+ // Hack for better migration.
+ count += level;
const HistoricalInfo historicalInfo(timestamp, level, count);
return BigramEntry(hasNext, probability, &historicalInfo, targetTerminalId);
} else {
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/probability_dict_content.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/probability_dict_content.cpp
index ef6166ffd..61ef4aa42 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/probability_dict_content.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/content/probability_dict_content.cpp
@@ -50,7 +50,8 @@ const ProbabilityEntry ProbabilityDictContent::getProbabilityEntry(const int ter
Ver4DictConstants::WORD_LEVEL_FIELD_SIZE, &entryPos);
const int count = buffer->readUintAndAdvancePosition(
Ver4DictConstants::WORD_COUNT_FIELD_SIZE, &entryPos);
- const HistoricalInfo historicalInfo(timestamp, level, count);
+ // Hack for better migration.
+ const HistoricalInfo historicalInfo(timestamp, level, count + level);
return ProbabilityEntry(flags, probability, &historicalInfo);
} else {
return ProbabilityEntry(flags, probability);
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.cpp
index 08e39ce43..ca7d93b0e 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/backward/v402/ver4_patricia_trie_policy.cpp
@@ -140,7 +140,7 @@ const WordAttributes Ver4PatriciaTriePolicy::getWordAttributesInContext(
const WordAttributes Ver4PatriciaTriePolicy::getWordAttributes(const int probability,
const PtNodeParams &ptNodeParams) const {
- return WordAttributes(probability, ptNodeParams.isBlacklisted(), ptNodeParams.isNotAWord(),
+ return WordAttributes(probability, false /* isBlacklisted */, ptNodeParams.isNotAWord(),
ptNodeParams.getProbability() == 0);
}
@@ -164,7 +164,7 @@ int Ver4PatriciaTriePolicy::getProbabilityOfWord(const WordIdArrayView prevWordI
}
const int ptNodePos = getTerminalPtNodePosFromWordId(wordId);
const PtNodeParams ptNodeParams(mNodeReader.fetchPtNodeParamsInBufferFromPtNodePos(ptNodePos));
- if (ptNodeParams.isDeleted() || ptNodeParams.isBlacklisted() || ptNodeParams.isNotAWord()) {
+ if (ptNodeParams.isDeleted() || ptNodeParams.isNotAWord()) {
return NOT_A_PROBABILITY;
}
if (prevWordIds.empty()) {
@@ -614,7 +614,7 @@ const WordProperty Ver4PatriciaTriePolicy::getWordProperty(
const UnigramProperty unigramProperty(ptNodeParams.representsBeginningOfSentence(),
ptNodeParams.isNotAWord(), ptNodeParams.isPossiblyOffensive(),
ptNodeParams.getProbability(), *historicalInfo, std::move(shortcuts));
- return WordProperty(wordCodePoints.toVector(), &unigramProperty, &ngrams);
+ return WordProperty(wordCodePoints.toVector(), unigramProperty, ngrams);
}
int Ver4PatriciaTriePolicy::getNextWordAndNextToken(const int token, int *const outCodePoints,
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.cpp
index 372c9e36f..9a9a21b6b 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.cpp
@@ -58,7 +58,7 @@ namespace latinime {
const DictionaryHeaderStructurePolicy::AttributeMap *const attributeMap) {
FormatUtils::FORMAT_VERSION dictFormatVersion = FormatUtils::getFormatVersion(formatVersion);
switch (dictFormatVersion) {
- case FormatUtils::VERSION_4: {
+ case FormatUtils::VERSION_402: {
return newPolicyForOnMemoryV4Dict<backward::v402::Ver4DictConstants,
backward::v402::Ver4DictBuffers,
backward::v402::Ver4DictBuffers::Ver4DictBuffersPtr,
@@ -66,7 +66,7 @@ namespace latinime {
dictFormatVersion, locale, attributeMap);
}
case FormatUtils::VERSION_4_ONLY_FOR_TESTING:
- case FormatUtils::VERSION_4_DEV: {
+ case FormatUtils::VERSION_403: {
return newPolicyForOnMemoryV4Dict<Ver4DictConstants, Ver4DictBuffers,
Ver4DictBuffers::Ver4DictBuffersPtr, Ver4PatriciaTriePolicy>(
dictFormatVersion, locale, attributeMap);
@@ -115,9 +115,10 @@ template<class DictConstants, class DictBuffers, class DictBuffersPtr, class Str
switch (formatVersion) {
case FormatUtils::VERSION_2:
case FormatUtils::VERSION_201:
- AKLOGE("Given path is a directory but the format is version 2 or 201. path: %s", path);
+ case FormatUtils::VERSION_202:
+ AKLOGE("Given path is a directory but the format is version 2xx. path: %s", path);
break;
- case FormatUtils::VERSION_4: {
+ case FormatUtils::VERSION_402: {
return newPolicyForV4Dict<backward::v402::Ver4DictConstants,
backward::v402::Ver4DictBuffers,
backward::v402::Ver4DictBuffers::Ver4DictBuffersPtr,
@@ -125,7 +126,7 @@ template<class DictConstants, class DictBuffers, class DictBuffersPtr, class Str
headerFilePath, formatVersion, std::move(mmappedBuffer));
}
case FormatUtils::VERSION_4_ONLY_FOR_TESTING:
- case FormatUtils::VERSION_4_DEV: {
+ case FormatUtils::VERSION_403: {
return newPolicyForV4Dict<Ver4DictConstants, Ver4DictBuffers,
Ver4DictBuffers::Ver4DictBuffersPtr, Ver4PatriciaTriePolicy>(
headerFilePath, formatVersion, std::move(mmappedBuffer));
@@ -177,11 +178,14 @@ template<class DictConstants, class DictBuffers, class DictBuffersPtr, class Str
switch (FormatUtils::detectFormatVersion(mmappedBuffer->getReadOnlyByteArrayView())) {
case FormatUtils::VERSION_2:
case FormatUtils::VERSION_201:
+ AKLOGE("Dictionary versions 2 and 201 are incompatible with this version");
+ break;
+ case FormatUtils::VERSION_202:
return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(
new PatriciaTriePolicy(std::move(mmappedBuffer)));
case FormatUtils::VERSION_4_ONLY_FOR_TESTING:
- case FormatUtils::VERSION_4:
- case FormatUtils::VERSION_4_DEV:
+ case FormatUtils::VERSION_402:
+ case FormatUtils::VERSION_403:
AKLOGE("Given path is a file but the format is version 4. path: %s", path);
break;
default:
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_params.h b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_params.h
index 585e87a24..e52706e07 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_params.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_params.h
@@ -144,17 +144,6 @@ class PtNodeParams {
return PatriciaTrieReadingUtils::isTerminal(mFlags);
}
- AK_FORCE_INLINE bool isBlacklisted() const {
- // Note: this method will be removed in the next change.
- // It is used in getProbabilityOfWord and getWordAttributes for both v402 and v403.
- // * getProbabilityOfWord will be changed to no longer return NOT_A_PROBABILITY
- // when isBlacklisted (i.e. to only check if isNotAWord or isDeleted)
- // * getWordAttributes will be changed to always return blacklisted=false and
- // isPossiblyOffensive according to the function below (instead of the current
- // behaviour of checking if the probability is zero)
- return PatriciaTrieReadingUtils::isPossiblyOffensive(mFlags);
- }
-
AK_FORCE_INLINE bool isPossiblyOffensive() const {
return PatriciaTrieReadingUtils::isPossiblyOffensive(mFlags);
}
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.cpp
index 66fd18a52..1a51acad5 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.cpp
@@ -14,7 +14,6 @@
* limitations under the License.
*/
-
#include "suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h"
#include "defines.h"
@@ -317,8 +316,8 @@ const WordAttributes PatriciaTriePolicy::getWordAttributesInContext(
const WordAttributes PatriciaTriePolicy::getWordAttributes(const int probability,
const PtNodeParams &ptNodeParams) const {
- return WordAttributes(probability, ptNodeParams.isBlacklisted(), ptNodeParams.isNotAWord(),
- ptNodeParams.getProbability() == 0);
+ return WordAttributes(probability, false /* isBlacklisted */, ptNodeParams.isNotAWord(),
+ ptNodeParams.isPossiblyOffensive());
}
int PatriciaTriePolicy::getProbability(const int unigramProbability,
@@ -345,10 +344,9 @@ int PatriciaTriePolicy::getProbabilityOfWord(const WordIdArrayView prevWordIds,
const int ptNodePos = getTerminalPtNodePosFromWordId(wordId);
const PtNodeParams ptNodeParams =
mPtNodeReader.fetchPtNodeParamsInBufferFromPtNodePos(ptNodePos);
- if (ptNodeParams.isNotAWord() || ptNodeParams.isBlacklisted()) {
- // If this is not a word, or if it's a blacklisted entry, it should behave as
- // having no probability outside of the suggestion process (where it should be used
- // for shortcuts).
+ if (ptNodeParams.isNotAWord()) {
+ // If this is not a word, it should behave as having no probability outside of the
+ // suggestion process (where it should be used for shortcuts).
return NOT_A_PROBABILITY;
}
if (!prevWordIds.empty()) {
@@ -480,7 +478,7 @@ const WordProperty PatriciaTriePolicy::getWordProperty(
const UnigramProperty unigramProperty(ptNodeParams.representsBeginningOfSentence(),
ptNodeParams.isNotAWord(), ptNodeParams.isPossiblyOffensive(),
ptNodeParams.getProbability(), HistoricalInfo(), std::move(shortcuts));
- return WordProperty(wordCodePoints.toVector(), &unigramProperty, &ngrams);
+ return WordProperty(wordCodePoints.toVector(), unigramProperty, ngrams);
}
int PatriciaTriePolicy::getNextWordAndNextToken(const int token, int *const outCodePoints,
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h
index f4d340f86..9c4ab18e4 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/probability_entry.h
@@ -105,7 +105,7 @@ class ProbabilityEntry {
encodedEntry = (encodedEntry << (Ver4DictConstants::WORD_LEVEL_FIELD_SIZE * CHAR_BIT))
| static_cast<uint8_t>(mHistoricalInfo.getLevel());
encodedEntry = (encodedEntry << (Ver4DictConstants::WORD_COUNT_FIELD_SIZE * CHAR_BIT))
- | static_cast<uint8_t>(mHistoricalInfo.getCount());
+ | static_cast<uint16_t>(mHistoricalInfo.getCount());
} else {
encodedEntry = (encodedEntry << (Ver4DictConstants::PROBABILITY_SIZE * CHAR_BIT))
| static_cast<uint8_t>(mProbability);
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp
index eb6080a24..bd89b8da7 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.cpp
@@ -49,8 +49,8 @@ const int Ver4DictConstants::TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE = 3;
const int Ver4DictConstants::NOT_A_TERMINAL_ADDRESS = 0;
const int Ver4DictConstants::TERMINAL_ID_FIELD_SIZE = 4;
const int Ver4DictConstants::TIME_STAMP_FIELD_SIZE = 4;
-const int Ver4DictConstants::WORD_LEVEL_FIELD_SIZE = 1;
-const int Ver4DictConstants::WORD_COUNT_FIELD_SIZE = 1;
+const int Ver4DictConstants::WORD_LEVEL_FIELD_SIZE = 0;
+const int Ver4DictConstants::WORD_COUNT_FIELD_SIZE = 2;
const uint8_t Ver4DictConstants::FLAG_REPRESENTS_BEGINNING_OF_SENTENCE = 0x1;
const uint8_t Ver4DictConstants::FLAG_NOT_A_VALID_ENTRY = 0x2;
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h
index 600b5ffe4..13d7a5714 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_dict_constants.h
@@ -47,6 +47,7 @@ class Ver4DictConstants {
static const int NOT_A_TERMINAL_ADDRESS;
static const int TERMINAL_ID_FIELD_SIZE;
static const int TIME_STAMP_FIELD_SIZE;
+ // TODO: Remove
static const int WORD_LEVEL_FIELD_SIZE;
static const int WORD_COUNT_FIELD_SIZE;
// Flags in probability entry.
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp
index 96d789f58..7449cd02b 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp
@@ -146,8 +146,16 @@ void Ver4PatriciaTriePolicy::iterateNgramEntries(const WordIdArrayView prevWordI
if (!probabilityEntry.isValid()) {
continue;
}
- const int probability = probabilityEntry.hasHistoricalInfo() ?
- 0 : probabilityEntry.getProbability();
+ int probability = NOT_A_PROBABILITY;
+ if (probabilityEntry.hasHistoricalInfo()) {
+ // TODO: Quit checking count here.
+ // If count <= 1, the word can be an invaild word. The actual probability should
+ // be checked using getWordAttributesInContext() in onVisitEntry().
+ probability = probabilityEntry.getHistoricalInfo()->getCount() <= 1 ?
+ NOT_A_PROBABILITY : 0;
+ } else {
+ probability = probabilityEntry.getProbability();
+ }
listener->onVisitEntry(probability, entry.getWordId());
}
}
@@ -552,7 +560,7 @@ const WordProperty Ver4PatriciaTriePolicy::getWordProperty(
wordAttributes.isNotAWord(), wordAttributes.isBlacklisted(),
wordAttributes.isPossiblyOffensive(), wordAttributes.getProbability(),
*historicalInfo, std::move(shortcuts));
- return WordProperty(wordCodePoints.toVector(), &unigramProperty, &ngrams);
+ return WordProperty(wordCodePoints.toVector(), unigramProperty, ngrams);
}
int Ver4PatriciaTriePolicy::getNextWordAndNextToken(const int token, int *const outCodePoints,
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.cpp
index 9d8e86675..edcb43678 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.cpp
@@ -44,13 +44,13 @@ const int DictFileWritingUtils::SIZE_OF_BUFFER_SIZE_FIELD = 4;
TimeKeeper::setCurrentTime();
const FormatUtils::FORMAT_VERSION formatVersion = FormatUtils::getFormatVersion(dictVersion);
switch (formatVersion) {
- case FormatUtils::VERSION_4:
+ case FormatUtils::VERSION_402:
return createEmptyV4DictFile<backward::v402::Ver4DictConstants,
backward::v402::Ver4DictBuffers,
backward::v402::Ver4DictBuffers::Ver4DictBuffersPtr>(
filePath, localeAsCodePointVector, attributeMap, formatVersion);
case FormatUtils::VERSION_4_ONLY_FOR_TESTING:
- case FormatUtils::VERSION_4_DEV:
+ case FormatUtils::VERSION_403:
return createEmptyV4DictFile<Ver4DictConstants, Ver4DictBuffers,
Ver4DictBuffers::Ver4DictBuffersPtr>(
filePath, localeAsCodePointVector, attributeMap, formatVersion);
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.cpp
index 0cffe569d..e225c235e 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.cpp
@@ -28,15 +28,17 @@ const size_t FormatUtils::DICTIONARY_MINIMUM_SIZE = 12;
/* static */ FormatUtils::FORMAT_VERSION FormatUtils::getFormatVersion(const int formatVersion) {
switch (formatVersion) {
case VERSION_2:
- return VERSION_2;
case VERSION_201:
- return VERSION_201;
+ AKLOGE("Dictionary versions 2 and 201 are incompatible with this version");
+ return UNKNOWN_VERSION;
+ case VERSION_202:
+ return VERSION_202;
case VERSION_4_ONLY_FOR_TESTING:
return VERSION_4_ONLY_FOR_TESTING;
- case VERSION_4:
- return VERSION_4;
- case VERSION_4_DEV:
- return VERSION_4_DEV;
+ case VERSION_402:
+ return VERSION_402;
+ case VERSION_403:
+ return VERSION_403;
default:
return UNKNOWN_VERSION;
}
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.h b/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.h
index 96310086b..1616efcce 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.h
@@ -31,11 +31,15 @@ class FormatUtils {
public:
enum FORMAT_VERSION {
// These MUST have the same values as the relevant constants in FormatSpec.java.
+ // TODO: Remove VERSION_2 and VERSION_201 when we:
+ // * Confirm that old versions of LatinIME download old-format dictionaries
+ // * We no longer need the corresponding constants on the Java side for dicttool
VERSION_2 = 2,
VERSION_201 = 201,
+ VERSION_202 = 202,
VERSION_4_ONLY_FOR_TESTING = 399,
- VERSION_4 = 402,
- VERSION_4_DEV = 403,
+ VERSION_402 = 402,
+ VERSION_403 = 403,
UNKNOWN_VERSION = -1
};
diff --git a/native/jni/src/suggest/policyimpl/typing/scoring_params.cpp b/native/jni/src/suggest/policyimpl/typing/scoring_params.cpp
index a6f9a8b23..856808a74 100644
--- a/native/jni/src/suggest/policyimpl/typing/scoring_params.cpp
+++ b/native/jni/src/suggest/policyimpl/typing/scoring_params.cpp
@@ -24,6 +24,7 @@ const int ScoringParams::THRESHOLD_NEXT_WORD_PROBABILITY_FOR_CAPPED = 120;
const float ScoringParams::AUTOCORRECT_OUTPUT_THRESHOLD = 1.0f;
const float ScoringParams::EXACT_MATCH_PROMOTION = 1.1f;
+const float ScoringParams::PERFECT_MATCH_PROMOTION = 1.1f;
const float ScoringParams::CASE_ERROR_PENALTY_FOR_EXACT_MATCH = 0.01f;
const float ScoringParams::ACCENT_ERROR_PENALTY_FOR_EXACT_MATCH = 0.02f;
const float ScoringParams::DIGRAPH_PENALTY_FOR_EXACT_MATCH = 0.03f;
diff --git a/native/jni/src/suggest/policyimpl/typing/scoring_params.h b/native/jni/src/suggest/policyimpl/typing/scoring_params.h
index b8f889559..6f327a370 100644
--- a/native/jni/src/suggest/policyimpl/typing/scoring_params.h
+++ b/native/jni/src/suggest/policyimpl/typing/scoring_params.h
@@ -34,6 +34,7 @@ class ScoringParams {
static const int THRESHOLD_SHORT_WORD_LENGTH;
static const float EXACT_MATCH_PROMOTION;
+ static const float PERFECT_MATCH_PROMOTION;
static const float CASE_ERROR_PENALTY_FOR_EXACT_MATCH;
static const float ACCENT_ERROR_PENALTY_FOR_EXACT_MATCH;
static const float DIGRAPH_PENALTY_FOR_EXACT_MATCH;
diff --git a/native/jni/src/suggest/policyimpl/typing/typing_scoring.h b/native/jni/src/suggest/policyimpl/typing/typing_scoring.h
index 0240bcf54..6acd767ea 100644
--- a/native/jni/src/suggest/policyimpl/typing/typing_scoring.h
+++ b/native/jni/src/suggest/policyimpl/typing/typing_scoring.h
@@ -44,23 +44,50 @@ class TypingScoring : public Scoring {
AK_FORCE_INLINE int calculateFinalScore(const float compoundDistance, const int inputSize,
const ErrorTypeUtils::ErrorType containedErrorTypes, const bool forceCommit,
- const bool boostExactMatches) const {
+ const bool boostExactMatches, const bool hasProbabilityZero) const {
const float maxDistance = ScoringParams::DISTANCE_WEIGHT_LANGUAGE
+ static_cast<float>(inputSize) * ScoringParams::TYPING_MAX_OUTPUT_SCORE_PER_INPUT;
float score = ScoringParams::TYPING_BASE_OUTPUT_SCORE - compoundDistance / maxDistance;
if (forceCommit) {
score += ScoringParams::AUTOCORRECT_OUTPUT_THRESHOLD;
}
- if (boostExactMatches && ErrorTypeUtils::isExactMatch(containedErrorTypes)) {
- score += ScoringParams::EXACT_MATCH_PROMOTION;
- if ((ErrorTypeUtils::MATCH_WITH_WRONG_CASE & containedErrorTypes) != 0) {
- score -= ScoringParams::CASE_ERROR_PENALTY_FOR_EXACT_MATCH;
+ if (hasProbabilityZero) {
+ // Previously, when both legitimate 0-frequency words (such as distracters) and
+ // offensive words were encoded in the same way, distracters would never show up
+ // when the user blocked offensive words (the default setting, as well as the
+ // setting for regression tests).
+ //
+ // When b/11031090 was fixed and a separate encoding was used for offensive words,
+ // 0-frequency words would no longer be blocked when they were an "exact match"
+ // (where case mismatches and accent mismatches would be considered an "exact
+ // match"). The exact match boosting functionality meant that, for example, when
+ // the user typed "mt" they would be suggested the word "Mt", although they most
+ // probably meant to type "my".
+ //
+ // For this reason, we introduced this change, which does the following:
+ // * Defines the "perfect match" as a really exact match, with no room for case or
+ // accent mismatches
+ // * When the target word has probability zero (as "Mt" does, because it is a
+ // distracter), ONLY boost its score if it is a perfect match.
+ //
+ // By doing this, when the user types "mt", the word "Mt" will NOT be boosted, and
+ // they will get "my". However, if the user makes an explicit effort to type "Mt",
+ // we do boost the word "Mt" so that the user's input is not autocorrected to "My".
+ if (boostExactMatches && ErrorTypeUtils::isPerfectMatch(containedErrorTypes)) {
+ score += ScoringParams::PERFECT_MATCH_PROMOTION;
}
- if ((ErrorTypeUtils::MATCH_WITH_MISSING_ACCENT & containedErrorTypes) != 0) {
- score -= ScoringParams::ACCENT_ERROR_PENALTY_FOR_EXACT_MATCH;
- }
- if ((ErrorTypeUtils::MATCH_WITH_DIGRAPH & containedErrorTypes) != 0) {
- score -= ScoringParams::DIGRAPH_PENALTY_FOR_EXACT_MATCH;
+ } else {
+ if (boostExactMatches && ErrorTypeUtils::isExactMatch(containedErrorTypes)) {
+ score += ScoringParams::EXACT_MATCH_PROMOTION;
+ if ((ErrorTypeUtils::MATCH_WITH_WRONG_CASE & containedErrorTypes) != 0) {
+ score -= ScoringParams::CASE_ERROR_PENALTY_FOR_EXACT_MATCH;
+ }
+ if ((ErrorTypeUtils::MATCH_WITH_MISSING_ACCENT & containedErrorTypes) != 0) {
+ score -= ScoringParams::ACCENT_ERROR_PENALTY_FOR_EXACT_MATCH;
+ }
+ if ((ErrorTypeUtils::MATCH_WITH_DIGRAPH & containedErrorTypes) != 0) {
+ score -= ScoringParams::DIGRAPH_PENALTY_FOR_EXACT_MATCH;
+ }
}
}
return static_cast<int>(score * SUGGEST_INTERFACE_OUTPUT_SCALE);
diff --git a/native/jni/src/utils/int_array_view.h b/native/jni/src/utils/int_array_view.h
index 408373176..e0f671056 100644
--- a/native/jni/src/utils/int_array_view.h
+++ b/native/jni/src/utils/int_array_view.h
@@ -133,6 +133,29 @@ class IntArrayView {
return std::vector<int>(begin(), end());
}
+ std::vector<IntArrayView> split(const int separator, const int limit = S_INT_MAX) const {
+ if (limit <= 0) {
+ return std::vector<IntArrayView>();
+ }
+ std::vector<IntArrayView> result;
+ if (limit == 1) {
+ result.emplace_back(mPtr, mSize);
+ return result;
+ }
+ size_t startIndex = 0;
+ for (size_t i = 0; i < mSize; ++i) {
+ if (mPtr[i] == separator) {
+ result.emplace_back(mPtr + startIndex, i - startIndex);
+ startIndex = i + 1;
+ if (result.size() >= static_cast<size_t>(limit - 1)) {
+ break;
+ }
+ }
+ }
+ result.emplace_back(mPtr + startIndex, mSize - startIndex);
+ return result;
+ }
+
private:
DISALLOW_ASSIGNMENT_OPERATOR(IntArrayView);
diff --git a/native/jni/src/utils/profiler.h b/native/jni/src/utils/profiler.h
new file mode 100644
index 000000000..5f107fed3
--- /dev/null
+++ b/native/jni/src/utils/profiler.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2014, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LATINIME_PROFILER_H
+#define LATINIME_PROFILER_H
+
+#ifdef FLAG_DO_PROFILE
+
+#include "defines.h"
+
+#include <ctime>
+#include <unordered_map>
+
+namespace latinime {
+
+class Profiler final {
+ public:
+ Profiler(const clockid_t clockId)
+ : mClockId(clockId), mStartTime(getTimeInMicroSec()), mStartTimes(), mTimes(),
+ mCounters() {}
+
+ ~Profiler() {
+ const float totalTime =
+ static_cast<float>(getTimeInMicroSec() - mStartTime) / 1000.f;
+ AKLOGI("Total time is %6.3f ms.", totalTime);
+ for (const auto &time : mTimes) {
+ AKLOGI("(%d): Used %4.2f%%, %8.4f ms. Called %d times.", time.first,
+ time.second / totalTime * 100.0f, time.second, mCounters[time.first]);
+ }
+ }
+
+ void startTimer(const int id) {
+ mStartTimes[id] = getTimeInMicroSec();
+ }
+
+ void endTimer(const int id) {
+ mTimes[id] += static_cast<float>(getTimeInMicroSec() - mStartTimes[id]) / 1000.0f;
+ mCounters[id]++;
+ }
+
+ operator bool() const { return false; }
+
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Profiler);
+
+ const clockid_t mClockId;
+ int64_t mStartTime;
+ std::unordered_map<int, int64_t> mStartTimes;
+ std::unordered_map<int, float> mTimes;
+ std::unordered_map<int, int> mCounters;
+
+ int64_t getTimeInMicroSec() {
+ timespec time;
+ clock_gettime(mClockId, &time);
+ return static_cast<int64_t>(time.tv_sec) * 1000000
+ + static_cast<int64_t>(time.tv_nsec) / 1000;
+ }
+};
+} // namespace latinime
+
+#define PROF_INIT Profiler __LATINIME__PROFILER__(CLOCK_THREAD_CPUTIME_ID)
+#define PROF_TIMER_START(timer_id) __LATINIME__PROFILER__.startTimer(timer_id)
+#define PROF_TIMER_END(timer_id) __LATINIME__PROFILER__.endTimer(timer_id)
+
+#else // FLAG_DO_PROFILE
+
+#define PROF_INIT
+#define PROF_TIMER_START(timer_id)
+#define PROF_TIMER_END(timer_id)
+
+#endif // FLAG_DO_PROFILE
+
+#endif /* LATINIME_PROFILER_H */
diff --git a/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_test.cpp b/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_test.cpp
index 86040f12c..313a9af10 100644
--- a/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_test.cpp
+++ b/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/language_model_dict_content_test.cpp
@@ -52,16 +52,14 @@ TEST(LanguageModelDictContentTest, TestUnigramProbabilityWithHistoricalInfo) {
const int flag = 0xF0;
const int timestamp = 0x3FFFFFFF;
- const int level = 3;
const int count = 10;
const int wordId = 100;
- const HistoricalInfo historicalInfo(timestamp, level, count);
+ const HistoricalInfo historicalInfo(timestamp, 0 /* level */, count);
const ProbabilityEntry probabilityEntry(flag, &historicalInfo);
languageModelDictContent.setProbabilityEntry(wordId, &probabilityEntry);
const ProbabilityEntry entry = languageModelDictContent.getProbabilityEntry(wordId);
EXPECT_EQ(flag, entry.getFlags());
EXPECT_EQ(timestamp, entry.getHistoricalInfo()->getTimestamp());
- EXPECT_EQ(level, entry.getHistoricalInfo()->getLevel());
EXPECT_EQ(count, entry.getHistoricalInfo()->getCount());
// Remove
diff --git a/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/probability_entry_test.cpp b/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/probability_entry_test.cpp
index 260b347ce..eb78034ba 100644
--- a/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/probability_entry_test.cpp
+++ b/native/jni/tests/suggest/policyimpl/dictionary/structure/v4/content/probability_entry_test.cpp
@@ -39,20 +39,18 @@ TEST(ProbabilityEntryTest, TestEncodeDecode) {
TEST(ProbabilityEntryTest, TestEncodeDecodeWithHistoricalInfo) {
const int flag = 0xF0;
const int timestamp = 0x3FFFFFFF;
- const int level = 3;
- const int count = 10;
+ const int count = 0xABCD;
- const HistoricalInfo historicalInfo(timestamp, level, count);
+ const HistoricalInfo historicalInfo(timestamp, 0 /* level */, count);
const ProbabilityEntry entry(flag, &historicalInfo);
const uint64_t encodedEntry = entry.encode(true /* hasHistoricalInfo */);
- EXPECT_EQ(0xF03FFFFFFF030Aull, encodedEntry);
+ EXPECT_EQ(0xF03FFFFFFFABCDull, encodedEntry);
const ProbabilityEntry decodedEntry =
ProbabilityEntry::decode(encodedEntry, true /* hasHistoricalInfo */);
EXPECT_EQ(flag, decodedEntry.getFlags());
EXPECT_EQ(timestamp, decodedEntry.getHistoricalInfo()->getTimestamp());
- EXPECT_EQ(level, decodedEntry.getHistoricalInfo()->getLevel());
EXPECT_EQ(count, decodedEntry.getHistoricalInfo()->getCount());
}
diff --git a/native/jni/tests/suggest/policyimpl/dictionary/utils/format_utils_test.cpp b/native/jni/tests/suggest/policyimpl/dictionary/utils/format_utils_test.cpp
index 15f560cd1..494200568 100644
--- a/native/jni/tests/suggest/policyimpl/dictionary/utils/format_utils_test.cpp
+++ b/native/jni/tests/suggest/policyimpl/dictionary/utils/format_utils_test.cpp
@@ -62,14 +62,14 @@ TEST(FormatUtilsTest, TestDetectFormatVersion) {
}
{
const std::vector<uint8_t> buffer =
- getBuffer(FormatUtils::MAGIC_NUMBER, FormatUtils::VERSION_4, 0, 0);
- EXPECT_EQ(FormatUtils::VERSION_4, FormatUtils::detectFormatVersion(
+ getBuffer(FormatUtils::MAGIC_NUMBER, FormatUtils::VERSION_402, 0, 0);
+ EXPECT_EQ(FormatUtils::VERSION_402, FormatUtils::detectFormatVersion(
ReadOnlyByteArrayView(buffer.data(), buffer.size())));
}
{
const std::vector<uint8_t> buffer =
- getBuffer(FormatUtils::MAGIC_NUMBER, FormatUtils::VERSION_4_DEV, 0, 0);
- EXPECT_EQ(FormatUtils::VERSION_4_DEV, FormatUtils::detectFormatVersion(
+ getBuffer(FormatUtils::MAGIC_NUMBER, FormatUtils::VERSION_403, 0, 0);
+ EXPECT_EQ(FormatUtils::VERSION_403, FormatUtils::detectFormatVersion(
ReadOnlyByteArrayView(buffer.data(), buffer.size())));
}
diff --git a/native/jni/tests/utils/int_array_view_test.cpp b/native/jni/tests/utils/int_array_view_test.cpp
index 4757a416b..2fce633f5 100644
--- a/native/jni/tests/utils/int_array_view_test.cpp
+++ b/native/jni/tests/utils/int_array_view_test.cpp
@@ -151,5 +151,52 @@ TEST(IntArrayViewTest, TestToVector) {
EXPECT_EQ(std::vector<int>(), CodePointArrayView().toVector());
}
+TEST(IntArrayViewTest, TestSplit) {
+ EXPECT_TRUE(IntArrayView().split(0, 0).empty());
+ {
+ const auto intArrayViews = IntArrayView().split(0, 1);
+ EXPECT_EQ(1u, intArrayViews.size());
+ EXPECT_TRUE(intArrayViews[0].empty());
+ }
+ {
+ const auto intArrayViews = IntArrayView().split(0, 100);
+ EXPECT_EQ(1u, intArrayViews.size());
+ EXPECT_TRUE(intArrayViews[0].empty());
+ }
+
+ const std::vector<int> intVector = {1, 2, 3, 3, 2, 3};
+ const IntArrayView intArrayView(intVector);
+ {
+ const auto intArrayViews = intArrayView.split(2);
+ EXPECT_EQ(3u, intArrayViews.size());
+ EXPECT_EQ(std::vector<int>({1}), intArrayViews[0].toVector());
+ EXPECT_EQ(std::vector<int>({3, 3}), intArrayViews[1].toVector());
+ EXPECT_EQ(std::vector<int>({3}), intArrayViews[2].toVector());
+ }
+ {
+ const auto intArrayViews = intArrayView.split(2, 2);
+ EXPECT_EQ(2u, intArrayViews.size());
+ EXPECT_EQ(std::vector<int>({1}), intArrayViews[0].toVector());
+ EXPECT_EQ(std::vector<int>({3, 3, 2, 3}), intArrayViews[1].toVector());
+ }
+ {
+ const auto intArrayViews = intArrayView.split(2, 1);
+ EXPECT_EQ(1u, intArrayViews.size());
+ EXPECT_EQ(intVector, intArrayViews[0].toVector());
+ }
+ {
+ const auto intArrayViews = intArrayView.split(2, 0);
+ EXPECT_EQ(0u, intArrayViews.size());
+ }
+ {
+ const auto intArrayViews = intArrayView.split(3);
+ EXPECT_EQ(4u, intArrayViews.size());
+ EXPECT_EQ(std::vector<int>({1, 2}), intArrayViews[0].toVector());
+ EXPECT_EQ(std::vector<int>(), intArrayViews[1].toVector());
+ EXPECT_EQ(std::vector<int>({2}), intArrayViews[2].toVector());
+ EXPECT_EQ(std::vector<int>(), intArrayViews[3].toVector());
+ }
+}
+
} // namespace
} // namespace latinime
diff --git a/tests/Android.mk b/tests/Android.mk
index 7cdabf249..0b5449143 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-LOCAL_PATH:= $(call my-dir)
+LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# We only want this apk build for tests.
diff --git a/tests/src/com/android/inputmethod/keyboard/action/ActionTestsBase.java b/tests/src/com/android/inputmethod/keyboard/action/ActionTestsBase.java
index 94caf51ed..1ea68e471 100644
--- a/tests/src/com/android/inputmethod/keyboard/action/ActionTestsBase.java
+++ b/tests/src/com/android/inputmethod/keyboard/action/ActionTestsBase.java
@@ -30,7 +30,7 @@ import com.android.inputmethod.keyboard.KeyboardLayoutSetTestsBase;
import com.android.inputmethod.keyboard.internal.KeyboardIconsSet;
import com.android.inputmethod.keyboard.layout.expected.ExpectedKeyVisual;
import com.android.inputmethod.latin.common.Constants;
-import com.android.inputmethod.latin.utils.LocaleUtils;
+import com.android.inputmethod.latin.common.LocaleUtils;
import com.android.inputmethod.latin.utils.RunInLocale;
import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
diff --git a/tests/src/com/android/inputmethod/keyboard/internal/LanguageOnSpacebarHelperTests.java b/tests/src/com/android/inputmethod/keyboard/internal/LanguageOnSpacebarHelperTests.java
index e6198015a..6bb255b01 100644
--- a/tests/src/com/android/inputmethod/keyboard/internal/LanguageOnSpacebarHelperTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/internal/LanguageOnSpacebarHelperTests.java
@@ -30,10 +30,11 @@ import com.android.inputmethod.latin.RichInputMethodSubtype;
import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils;
import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
-import java.util.Arrays;
-import java.util.List;
+import java.util.ArrayList;
import java.util.Locale;
+import javax.annotation.Nonnull;
+
@SmallTest
public class LanguageOnSpacebarHelperTests extends AndroidTestCase {
private final LanguageOnSpacebarHelper mLanguageOnSpacebarHelper =
@@ -48,6 +49,7 @@ public class LanguageOnSpacebarHelperTests extends AndroidTestCase {
RichInputMethodSubtype FR_CH_SWISS;
RichInputMethodSubtype FR_CH_QWERTY;
RichInputMethodSubtype FR_CH_QWERTZ;
+ RichInputMethodSubtype IW_HEBREW;
RichInputMethodSubtype ZZ_QWERTY;
@Override
@@ -56,116 +58,160 @@ public class LanguageOnSpacebarHelperTests extends AndroidTestCase {
final Context context = getContext();
RichInputMethodManager.init(context);
mRichImm = RichInputMethodManager.getInstance();
- SubtypeLocaleUtils.init(context);
-
- EN_US_QWERTY = new RichInputMethodSubtype(mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
- Locale.US.toString(), "qwerty"));
- EN_GB_QWERTY = new RichInputMethodSubtype(mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
- Locale.UK.toString(), "qwerty"));
- FR_AZERTY = new RichInputMethodSubtype(mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
- Locale.FRENCH.toString(), "azerty"));
- FR_CA_QWERTY = new RichInputMethodSubtype(mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
- Locale.CANADA_FRENCH.toString(), "qwerty"));
- FR_CH_SWISS = new RichInputMethodSubtype(mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
- "fr_CH", "swiss"));
+
+ EN_US_QWERTY = findSubtypeOf(Locale.US.toString(), "qwerty");
+ EN_GB_QWERTY = findSubtypeOf(Locale.UK.toString(), "qwerty");
+ FR_AZERTY = findSubtypeOf(Locale.FRENCH.toString(), "azerty");
+ FR_CA_QWERTY = findSubtypeOf(Locale.CANADA_FRENCH.toString(), "qwerty");
+ FR_CH_SWISS = findSubtypeOf("fr_CH", "swiss");
FR_CH_QWERTZ = new RichInputMethodSubtype(
AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype("fr_CH", "qwertz"));
FR_CH_QWERTY = new RichInputMethodSubtype(
AdditionalSubtypeUtils.createAsciiEmojiCapableAdditionalSubtype("fr_CH", "qwerty"));
- ZZ_QWERTY = new RichInputMethodSubtype(mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
- SubtypeLocaleUtils.NO_LANGUAGE, "qwerty"));
+ IW_HEBREW = findSubtypeOf("iw", "hebrew");
+ ZZ_QWERTY = findSubtypeOf(SubtypeLocaleUtils.NO_LANGUAGE, "qwerty");
+ }
+
+ @Nonnull
+ private RichInputMethodSubtype findSubtypeOf(final String localeString,
+ final String keyboardLayoutSetName) {
+ final InputMethodSubtype subtype = mRichImm.findSubtypeByLocaleAndKeyboardLayoutSet(
+ localeString, keyboardLayoutSetName);
+ if (subtype == null) {
+ throw new RuntimeException("Can't find subtype of " + localeString + " with "
+ + keyboardLayoutSetName);
+ }
+ return new RichInputMethodSubtype(subtype);
+ }
+
+ private void enableSubtypes(final RichInputMethodSubtype ... subtypes) {
+ final ArrayList<InputMethodSubtype> enabledSubtypes = new ArrayList<>();
+ for (final RichInputMethodSubtype subtype : subtypes) {
+ enabledSubtypes.add(subtype.getRawSubtype());
+ }
+ mLanguageOnSpacebarHelper.onUpdateEnabledSubtypes(enabledSubtypes);
+ }
+
+ private void assertFormatType(final RichInputMethodSubtype subtype,
+ final boolean implicitlyEnabledSubtype, final Locale systemLocale,
+ final int expectedFormat) {
+ mLanguageOnSpacebarHelper.onSubtypeChanged(subtype, implicitlyEnabledSubtype, systemLocale);
+ assertEquals(subtype.getLocales()[0] + " implicitly=" + implicitlyEnabledSubtype
+ + " in " + systemLocale, expectedFormat,
+ mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(subtype));
+ }
+
+ public void testOneSubtypeImplicitlyEnabled() {
+ enableSubtypes(EN_US_QWERTY);
+ assertFormatType(EN_US_QWERTY, true, Locale.US, FORMAT_TYPE_NONE);
+
+ enableSubtypes(EN_GB_QWERTY);
+ assertFormatType(EN_GB_QWERTY, true, Locale.UK, FORMAT_TYPE_NONE);
+
+ enableSubtypes(FR_AZERTY);
+ assertFormatType(FR_AZERTY, true, Locale.FRANCE, FORMAT_TYPE_NONE);
+
+ enableSubtypes(FR_CA_QWERTY);
+ assertFormatType(FR_CA_QWERTY, true, Locale.CANADA_FRENCH, FORMAT_TYPE_NONE);
+ }
+
+ public void testOneSubtypeExplicitlyEnabled() {
+ enableSubtypes(EN_US_QWERTY);
+ assertFormatType(EN_US_QWERTY, false, Locale.UK, FORMAT_TYPE_LANGUAGE_ONLY);
+ assertFormatType(EN_US_QWERTY, false, Locale.FRANCE, FORMAT_TYPE_LANGUAGE_ONLY);
+
+ enableSubtypes(EN_GB_QWERTY);
+ assertFormatType(EN_GB_QWERTY, false, Locale.US, FORMAT_TYPE_LANGUAGE_ONLY);
+ assertFormatType(EN_GB_QWERTY, false, Locale.FRANCE, FORMAT_TYPE_LANGUAGE_ONLY);
+
+ enableSubtypes(FR_AZERTY);
+ assertFormatType(FR_AZERTY, false, Locale.US, FORMAT_TYPE_LANGUAGE_ONLY);
+ assertFormatType(FR_AZERTY, false, Locale.CANADA_FRENCH, FORMAT_TYPE_LANGUAGE_ONLY);
+
+ enableSubtypes(FR_CA_QWERTY);
+ assertFormatType(FR_CA_QWERTY, false, Locale.US, FORMAT_TYPE_LANGUAGE_ONLY);
+ assertFormatType(FR_CA_QWERTY, false, Locale.FRANCE, FORMAT_TYPE_LANGUAGE_ONLY);
}
- private static List<InputMethodSubtype> asList(final InputMethodSubtype ... subtypes) {
- return Arrays.asList(subtypes);
+ public void testOneSubtypeImplicitlyEnabledWithNoLanguageSubtype() {
+ final Locale Locale_IW = new Locale("iw");
+ enableSubtypes(IW_HEBREW, ZZ_QWERTY);
+ // TODO: Should this be FORMAT_TYPE_NONE?
+ assertFormatType(IW_HEBREW, true, Locale_IW, FORMAT_TYPE_LANGUAGE_ONLY);
+ // TODO: Should this be FORMAT_TYPE_NONE?
+ assertFormatType(ZZ_QWERTY, true, Locale_IW, FORMAT_TYPE_FULL_LOCALE);
}
- public void testOneSubtype() {
- mLanguageOnSpacebarHelper.updateEnabledSubtypes(asList(EN_US_QWERTY.getRawSubtype()));
- mLanguageOnSpacebarHelper.updateIsSystemLanguageSameAsInputLanguage(true /* isSame */);
- assertEquals("one same English (US)", FORMAT_TYPE_NONE,
- mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(EN_US_QWERTY));
- assertEquals("one same NoLanguage", FORMAT_TYPE_FULL_LOCALE,
- mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(ZZ_QWERTY));
-
- mLanguageOnSpacebarHelper.updateEnabledSubtypes(asList(FR_AZERTY.getRawSubtype()));
- mLanguageOnSpacebarHelper.updateIsSystemLanguageSameAsInputLanguage(false /* isSame */);
- assertEquals("one diff English (US)", FORMAT_TYPE_LANGUAGE_ONLY,
- mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(EN_US_QWERTY));
- assertEquals("one diff NoLanguage", FORMAT_TYPE_FULL_LOCALE,
- mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(ZZ_QWERTY));
+ public void testTwoSubtypesExplicitlyEnabled() {
+ enableSubtypes(EN_US_QWERTY, FR_AZERTY);
+ assertFormatType(EN_US_QWERTY, false, Locale.US, FORMAT_TYPE_LANGUAGE_ONLY);
+ assertFormatType(FR_AZERTY, false, Locale.US, FORMAT_TYPE_LANGUAGE_ONLY);
+ assertFormatType(EN_US_QWERTY, false, Locale.FRANCE, FORMAT_TYPE_LANGUAGE_ONLY);
+ assertFormatType(FR_AZERTY, false, Locale.FRANCE, FORMAT_TYPE_LANGUAGE_ONLY);
+ assertFormatType(EN_US_QWERTY, false, Locale.JAPAN, FORMAT_TYPE_LANGUAGE_ONLY);
+ assertFormatType(FR_AZERTY, false, Locale.JAPAN, FORMAT_TYPE_LANGUAGE_ONLY);
+
+ enableSubtypes(EN_US_QWERTY, ZZ_QWERTY);
+ assertFormatType(EN_US_QWERTY, false, Locale.US, FORMAT_TYPE_LANGUAGE_ONLY);
+ assertFormatType(ZZ_QWERTY, false, Locale.US, FORMAT_TYPE_FULL_LOCALE);
+ assertFormatType(EN_US_QWERTY, false, Locale.FRANCE, FORMAT_TYPE_LANGUAGE_ONLY);
+ assertFormatType(ZZ_QWERTY, false, Locale.FRANCE, FORMAT_TYPE_FULL_LOCALE);
+
}
- public void testTwoSubtypes() {
- mLanguageOnSpacebarHelper.updateEnabledSubtypes(asList(EN_US_QWERTY.getRawSubtype(),
- FR_AZERTY.getRawSubtype()));
- mLanguageOnSpacebarHelper.updateIsSystemLanguageSameAsInputLanguage(true /* isSame */);
- assertEquals("two same English (US)", FORMAT_TYPE_LANGUAGE_ONLY,
- mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(EN_US_QWERTY));
- assertEquals("two same French)", FORMAT_TYPE_LANGUAGE_ONLY,
- mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(FR_AZERTY));
- assertEquals("two same NoLanguage", FORMAT_TYPE_FULL_LOCALE,
- mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(ZZ_QWERTY));
-
- mLanguageOnSpacebarHelper.updateIsSystemLanguageSameAsInputLanguage(false /* isSame */);
- assertEquals("two diff English (US)", FORMAT_TYPE_LANGUAGE_ONLY,
- mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(EN_US_QWERTY));
- assertEquals("two diff French", FORMAT_TYPE_LANGUAGE_ONLY,
- mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(FR_AZERTY));
- assertEquals("two diff NoLanguage", FORMAT_TYPE_FULL_LOCALE,
- mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(ZZ_QWERTY));
+ public void testMultiSubtypeWithSameLanuageAndSameLayout() {
+ // Explicitly enable en_US, en_GB, fr_FR, and no language keyboards.
+ enableSubtypes(EN_US_QWERTY, EN_GB_QWERTY, FR_CA_QWERTY, ZZ_QWERTY);
+
+ assertFormatType(EN_US_QWERTY, false, Locale.US, FORMAT_TYPE_FULL_LOCALE);
+ assertFormatType(EN_GB_QWERTY, false, Locale.US, FORMAT_TYPE_FULL_LOCALE);
+ assertFormatType(FR_CA_QWERTY, false, Locale.US, FORMAT_TYPE_LANGUAGE_ONLY);
+ assertFormatType(ZZ_QWERTY, false, Locale.US, FORMAT_TYPE_FULL_LOCALE);
+
+ assertFormatType(EN_US_QWERTY, false, Locale.JAPAN, FORMAT_TYPE_FULL_LOCALE);
+ assertFormatType(EN_GB_QWERTY, false, Locale.JAPAN, FORMAT_TYPE_FULL_LOCALE);
+ assertFormatType(FR_CA_QWERTY, false, Locale.JAPAN, FORMAT_TYPE_LANGUAGE_ONLY);
+ assertFormatType(ZZ_QWERTY, false, Locale.JAPAN, FORMAT_TYPE_FULL_LOCALE);
}
- public void testSameLanuageSubtypes() {
- mLanguageOnSpacebarHelper.updateEnabledSubtypes(
- asList(EN_US_QWERTY.getRawSubtype(), EN_GB_QWERTY.getRawSubtype(),
- FR_AZERTY.getRawSubtype(), ZZ_QWERTY.getRawSubtype()));
-
- mLanguageOnSpacebarHelper.updateIsSystemLanguageSameAsInputLanguage(true /* isSame */);
- assertEquals("two same English (US)", FORMAT_TYPE_FULL_LOCALE,
- mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(EN_US_QWERTY));
- assertEquals("two same English (UK)", FORMAT_TYPE_FULL_LOCALE,
- mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(EN_GB_QWERTY));
- assertEquals("two same NoLanguage", FORMAT_TYPE_FULL_LOCALE,
- mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(ZZ_QWERTY));
-
- mLanguageOnSpacebarHelper.updateIsSystemLanguageSameAsInputLanguage(false /* isSame */);
- assertEquals("two diff English (US)", FORMAT_TYPE_FULL_LOCALE,
- mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(EN_US_QWERTY));
- assertEquals("two diff English (UK)", FORMAT_TYPE_FULL_LOCALE,
- mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(EN_GB_QWERTY));
- assertEquals("two diff NoLanguage", FORMAT_TYPE_FULL_LOCALE,
- mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(ZZ_QWERTY));
+ public void testMultiSubtypesWithSameLanguageButHaveDifferentLayout() {
+ enableSubtypes(FR_AZERTY, FR_CA_QWERTY, FR_CH_SWISS, FR_CH_QWERTZ);
+
+ assertFormatType(FR_AZERTY, false, Locale.FRANCE, FORMAT_TYPE_LANGUAGE_ONLY);
+ assertFormatType(FR_CA_QWERTY, false, Locale.FRANCE, FORMAT_TYPE_LANGUAGE_ONLY);
+ assertFormatType(FR_CH_SWISS, false, Locale.FRANCE, FORMAT_TYPE_LANGUAGE_ONLY);
+ assertFormatType(FR_CH_QWERTZ, false, Locale.FRANCE, FORMAT_TYPE_LANGUAGE_ONLY);
+
+ assertFormatType(FR_AZERTY, false, Locale.CANADA_FRENCH, FORMAT_TYPE_LANGUAGE_ONLY);
+ assertFormatType(FR_CA_QWERTY, false, Locale.CANADA_FRENCH, FORMAT_TYPE_LANGUAGE_ONLY);
+ assertFormatType(FR_CH_SWISS, false, Locale.CANADA_FRENCH, FORMAT_TYPE_LANGUAGE_ONLY);
+ assertFormatType(FR_CH_QWERTZ, false, Locale.CANADA_FRENCH, FORMAT_TYPE_LANGUAGE_ONLY);
+
+ assertFormatType(FR_AZERTY, false, Locale.JAPAN, FORMAT_TYPE_LANGUAGE_ONLY);
+ assertFormatType(FR_CA_QWERTY, false, Locale.JAPAN, FORMAT_TYPE_LANGUAGE_ONLY);
+ assertFormatType(FR_CH_SWISS, false, Locale.JAPAN, FORMAT_TYPE_LANGUAGE_ONLY);
+ assertFormatType(FR_CH_QWERTZ, false, Locale.JAPAN, FORMAT_TYPE_LANGUAGE_ONLY);
}
- public void testMultiSameLanuageSubtypes() {
- mLanguageOnSpacebarHelper.updateEnabledSubtypes(
- asList(FR_AZERTY.getRawSubtype(), FR_CA_QWERTY.getRawSubtype(),
- FR_CH_SWISS.getRawSubtype(), FR_CH_QWERTY.getRawSubtype(),
- FR_CH_QWERTZ.getRawSubtype()));
-
- mLanguageOnSpacebarHelper.updateIsSystemLanguageSameAsInputLanguage(true /* isSame */);
- assertEquals("multi same French", FORMAT_TYPE_LANGUAGE_ONLY,
- mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(FR_AZERTY));
- assertEquals("multi same French (CA)", FORMAT_TYPE_FULL_LOCALE,
- mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(FR_CA_QWERTY));
- assertEquals("multi same French (CH)", FORMAT_TYPE_LANGUAGE_ONLY,
- mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(FR_CH_SWISS));
- assertEquals("multi same French (CH) (QWERTY)", FORMAT_TYPE_FULL_LOCALE,
- mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(FR_CH_QWERTY));
- assertEquals("multi same French (CH) (QWERTZ)", FORMAT_TYPE_LANGUAGE_ONLY,
- mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(FR_CH_QWERTZ));
-
- mLanguageOnSpacebarHelper.updateIsSystemLanguageSameAsInputLanguage(false /* isSame */);
- assertEquals("multi diff French", FORMAT_TYPE_LANGUAGE_ONLY,
- mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(FR_AZERTY));
- assertEquals("multi diff French (CA)", FORMAT_TYPE_FULL_LOCALE,
- mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(FR_CA_QWERTY));
- assertEquals("multi diff French (CH)", FORMAT_TYPE_LANGUAGE_ONLY,
- mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(FR_CH_SWISS));
- assertEquals("multi diff French (CH) (QWERTY)", FORMAT_TYPE_FULL_LOCALE,
- mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(FR_CH_QWERTY));
- assertEquals("multi diff French (CH) (QWERTZ)", FORMAT_TYPE_LANGUAGE_ONLY,
- mLanguageOnSpacebarHelper.getLanguageOnSpacebarFormatType(FR_CH_QWERTZ));
+ public void testMultiSubtypesWithSameLanguageAndMayHaveSameLayout() {
+ enableSubtypes(FR_AZERTY, FR_CA_QWERTY, FR_CH_SWISS, FR_CH_QWERTY, FR_CH_QWERTZ);
+
+ assertFormatType(FR_AZERTY, false, Locale.FRANCE, FORMAT_TYPE_LANGUAGE_ONLY);
+ assertFormatType(FR_CA_QWERTY, false, Locale.FRANCE, FORMAT_TYPE_FULL_LOCALE);
+ assertFormatType(FR_CH_SWISS, false, Locale.FRANCE, FORMAT_TYPE_LANGUAGE_ONLY);
+ assertFormatType(FR_CH_QWERTY, false, Locale.FRANCE, FORMAT_TYPE_FULL_LOCALE);
+ assertFormatType(FR_CH_QWERTZ, false, Locale.FRANCE, FORMAT_TYPE_LANGUAGE_ONLY);
+
+ assertFormatType(FR_AZERTY, false, Locale.CANADA_FRENCH, FORMAT_TYPE_LANGUAGE_ONLY);
+ assertFormatType(FR_CA_QWERTY, false, Locale.CANADA_FRENCH, FORMAT_TYPE_FULL_LOCALE);
+ assertFormatType(FR_CH_SWISS, false, Locale.CANADA_FRENCH, FORMAT_TYPE_LANGUAGE_ONLY);
+ assertFormatType(FR_CH_QWERTY, false, Locale.CANADA_FRENCH, FORMAT_TYPE_FULL_LOCALE);
+ assertFormatType(FR_CH_QWERTZ, false, Locale.CANADA_FRENCH, FORMAT_TYPE_LANGUAGE_ONLY);
+
+ assertFormatType(FR_AZERTY, false, Locale.JAPAN, FORMAT_TYPE_LANGUAGE_ONLY);
+ assertFormatType(FR_CA_QWERTY, false, Locale.JAPAN, FORMAT_TYPE_FULL_LOCALE);
+ assertFormatType(FR_CH_SWISS, false, Locale.JAPAN, FORMAT_TYPE_LANGUAGE_ONLY);
+ assertFormatType(FR_CH_QWERTY, false, Locale.JAPAN, FORMAT_TYPE_FULL_LOCALE);
+ assertFormatType(FR_CH_QWERTZ, false, Locale.JAPAN, FORMAT_TYPE_LANGUAGE_ONLY);
}
}
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBengaliBD.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBengaliBD.java
index 62625890e..2d38c874d 100644
--- a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBengaliBD.java
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsBengaliBD.java
@@ -46,6 +46,11 @@ public final class TestsBengaliBD extends LayoutTestsBase {
}
@Override
+ public ExpectedKey[] getSpaceKeys(final boolean isPhone) {
+ return joinKeys(LANGUAGE_SWITCH_KEY, SPACE_KEY, key(ZWNJ_KEY, ZWJ_KEY));
+ }
+
+ @Override
public ExpectedKey getCurrencyKey() { return CURRENCY_RUPEE; }
// U+09F3: "৳" BENGALI RUPEE SIGN
diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
index b9764488e..cff489dd5 100644
--- a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
+++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
@@ -22,6 +22,8 @@ import android.util.Pair;
import com.android.inputmethod.latin.NgramContext.WordInfo;
import com.android.inputmethod.latin.common.CodePointUtils;
+import com.android.inputmethod.latin.common.FileUtils;
+import com.android.inputmethod.latin.common.LocaleUtils;
import com.android.inputmethod.latin.makedict.BinaryDictIOUtils;
import com.android.inputmethod.latin.makedict.DictDecoder;
import com.android.inputmethod.latin.makedict.DictionaryHeader;
@@ -30,8 +32,6 @@ import com.android.inputmethod.latin.makedict.FusionDictionary;
import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
import com.android.inputmethod.latin.utils.BinaryDictionaryUtils;
-import com.android.inputmethod.latin.utils.FileUtils;
-import com.android.inputmethod.latin.utils.LocaleUtils;
import com.android.inputmethod.latin.utils.WordInputEventForPersonalization;
import java.io.File;
@@ -49,7 +49,7 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase {
private static final String TEST_LOCALE = "test";
private static final int DUMMY_PROBABILITY = 0;
private static final int[] DICT_FORMAT_VERSIONS =
- new int[] { FormatSpec.VERSION4, FormatSpec.VERSION4_DEV };
+ new int[] { FormatSpec.VERSION402, FormatSpec.VERSION403, FormatSpec.VERSION4_DEV };
private static final String DICTIONARY_ID = "TestDecayingBinaryDictionary";
private int mCurrentTime = 0;
@@ -73,11 +73,11 @@ public class BinaryDictionaryDecayingTests extends AndroidTestCase {
}
private static boolean supportsCountBasedNgram(final int formatVersion) {
- return formatVersion >= FormatSpec.VERSION4_DEV;
+ return formatVersion >= FormatSpec.VERSION403;
}
private static boolean supportsNgram(final int formatVersion) {
- return formatVersion >= FormatSpec.VERSION4_DEV;
+ return formatVersion >= FormatSpec.VERSION403;
}
private void onInputWord(final BinaryDictionary binaryDictionary, final String word,
diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java
index a1ae93c2f..60d2de18c 100644
--- a/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java
+++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java
@@ -24,12 +24,12 @@ import android.util.Pair;
import com.android.inputmethod.latin.NgramContext.WordInfo;
import com.android.inputmethod.latin.common.CodePointUtils;
import com.android.inputmethod.latin.common.Constants;
+import com.android.inputmethod.latin.common.FileUtils;
import com.android.inputmethod.latin.makedict.DictionaryHeader;
import com.android.inputmethod.latin.makedict.FormatSpec;
import com.android.inputmethod.latin.makedict.WeightedString;
import com.android.inputmethod.latin.makedict.WordProperty;
import com.android.inputmethod.latin.utils.BinaryDictionaryUtils;
-import com.android.inputmethod.latin.utils.FileUtils;
import java.io.File;
import java.io.IOException;
@@ -45,19 +45,11 @@ public class BinaryDictionaryTests extends AndroidTestCase {
private static final String TEST_DICT_FILE_EXTENSION = ".testDict";
private static final String TEST_LOCALE = "test";
private static final int[] DICT_FORMAT_VERSIONS =
- new int[] { FormatSpec.VERSION4, FormatSpec.VERSION4_DEV };
+ new int[] { FormatSpec.VERSION402, FormatSpec.VERSION403, FormatSpec.VERSION4_DEV };
private static final String DICTIONARY_ID = "TestBinaryDictionary";
- private static boolean canCheckBigramProbability(final int formatVersion) {
- return formatVersion > FormatSpec.VERSION401;
- }
-
- private static boolean supportsBeginningOfSentence(final int formatVersion) {
- return formatVersion > FormatSpec.VERSION401;
- }
-
private static boolean supportsNgram(final int formatVersion) {
- return formatVersion >= FormatSpec.VERSION4_DEV;
+ return formatVersion >= FormatSpec.VERSION403;
}
private HashSet<File> mDictFilesToBeDeleted = new HashSet<>();
@@ -84,19 +76,13 @@ public class BinaryDictionaryTests extends AndroidTestCase {
private File createEmptyDictionaryWithAttributesAndGetFile(final int formatVersion,
final HashMap<String, String> attributeMap) {
- if (formatVersion == FormatSpec.VERSION4
- || formatVersion == FormatSpec.VERSION4_ONLY_FOR_TESTING
- || formatVersion == FormatSpec.VERSION4_DEV) {
- try {
- final File dictFile = createEmptyVer4DictionaryAndGetFile(formatVersion,
- attributeMap);
- mDictFilesToBeDeleted.add(dictFile);
- return dictFile;
- } catch (final IOException e) {
- fail(e.toString());
- }
- } else {
- fail("Dictionary format version " + formatVersion + " is not supported.");
+ try {
+ final File dictFile = createEmptyVer4DictionaryAndGetFile(formatVersion,
+ attributeMap);
+ mDictFilesToBeDeleted.add(dictFile);
+ return dictFile;
+ } catch (final IOException e) {
+ fail(e.toString());
}
return null;
}
@@ -350,18 +336,14 @@ public class BinaryDictionaryTests extends AndroidTestCase {
assertTrue(isValidBigram(binaryDictionary, "aaa", "bcc"));
assertTrue(isValidBigram(binaryDictionary, "abb", "aaa"));
assertTrue(isValidBigram(binaryDictionary, "abb", "bcc"));
- if (canCheckBigramProbability(formatVersion)) {
- assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "aaa", "abb"));
- assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "aaa", "bcc"));
- assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "abb", "aaa"));
- assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "abb", "bcc"));
- }
+ assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "aaa", "abb"));
+ assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "aaa", "bcc"));
+ assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "abb", "aaa"));
+ assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "abb", "bcc"));
addBigramWords(binaryDictionary, "aaa", "abb", updatedBigramProbability);
- if (canCheckBigramProbability(formatVersion)) {
- assertEquals(updatedBigramProbability,
- getBigramProbability(binaryDictionary, "aaa", "abb"));
- }
+ assertEquals(updatedBigramProbability,
+ getBigramProbability(binaryDictionary, "aaa", "abb"));
assertFalse(isValidBigram(binaryDictionary, "bcc", "aaa"));
assertFalse(isValidBigram(binaryDictionary, "bcc", "bbc"));
@@ -381,17 +363,12 @@ public class BinaryDictionaryTests extends AndroidTestCase {
addUnigramWord(binaryDictionary, "abc", unigramProbability);
addUnigramWord(binaryDictionary, "f", unigramProbability);
- if (canCheckBigramProbability(formatVersion)) {
- assertEquals(bigramProbability,
- getBigramProbability(binaryDictionary, "abcde", "fghij"));
- }
+ assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "abcde", "fghij"));
assertEquals(Dictionary.NOT_A_PROBABILITY,
getBigramProbability(binaryDictionary, "abcde", "fgh"));
addBigramWords(binaryDictionary, "abcde", "fghij", updatedBigramProbability);
- if (canCheckBigramProbability(formatVersion)) {
- assertEquals(updatedBigramProbability,
- getBigramProbability(binaryDictionary, "abcde", "fghij"));
- }
+ assertEquals(updatedBigramProbability,
+ getBigramProbability(binaryDictionary, "abcde", "fghij"));
}
public void testRandomlyAddBigramWords() {
@@ -441,10 +418,8 @@ public class BinaryDictionaryTests extends AndroidTestCase {
final int bigramProbability = bigramProbabilities.get(bigram);
assertEquals(bigramProbability != Dictionary.NOT_A_PROBABILITY,
isValidBigram(binaryDictionary, bigram.first, bigram.second));
- if (canCheckBigramProbability(formatVersion)) {
- assertEquals(bigramProbability,
- getBigramProbability(binaryDictionary, bigram.first, bigram.second));
- }
+ assertEquals(bigramProbability,
+ getBigramProbability(binaryDictionary, bigram.first, bigram.second));
}
}
@@ -594,12 +569,10 @@ public class BinaryDictionaryTests extends AndroidTestCase {
assertEquals(unigramProbability, binaryDictionary.getFrequency("aaa"));
assertEquals(unigramProbability, binaryDictionary.getFrequency("abb"));
assertEquals(unigramProbability, binaryDictionary.getFrequency("bcc"));
- if (canCheckBigramProbability(formatVersion)) {
- assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "aaa", "abb"));
- assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "aaa", "bcc"));
- assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "abb", "aaa"));
- assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "abb", "bcc"));
- }
+ assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "aaa", "abb"));
+ assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "aaa", "bcc"));
+ assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "abb", "aaa"));
+ assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "abb", "bcc"));
assertFalse(isValidBigram(binaryDictionary, "bcc", "aaa"));
assertFalse(isValidBigram(binaryDictionary, "bcc", "bbc"));
assertFalse(isValidBigram(binaryDictionary, "aaa", "aaa"));
@@ -661,10 +634,8 @@ public class BinaryDictionaryTests extends AndroidTestCase {
final int bigramProbability = bigramProbabilities.get(bigram);
assertEquals(bigramProbability != Dictionary.NOT_A_PROBABILITY,
isValidBigram(binaryDictionary, bigram.first, bigram.second));
- if (canCheckBigramProbability(formatVersion)) {
- assertEquals(bigramProbability,
- getBigramProbability(binaryDictionary, bigram.first, bigram.second));
- }
+ assertEquals(bigramProbability,
+ getBigramProbability(binaryDictionary, bigram.first, bigram.second));
}
}
@@ -768,10 +739,8 @@ public class BinaryDictionaryTests extends AndroidTestCase {
probability = Dictionary.NOT_A_PROBABILITY;
}
- if (canCheckBigramProbability(formatVersion)) {
- assertEquals(probability,
- getBigramProbability(binaryDictionary, bigram.first, bigram.second));
- }
+ assertEquals(probability,
+ getBigramProbability(binaryDictionary, bigram.first, bigram.second));
assertEquals(probability != Dictionary.NOT_A_PROBABILITY,
isValidBigram(binaryDictionary, bigram.first, bigram.second));
}
@@ -971,10 +940,8 @@ public class BinaryDictionaryTests extends AndroidTestCase {
for (final WeightedString bigramTarget : wordProperty.getBigrams()) {
final String word1 = bigramTarget.mWord;
assertTrue(bigramWord1s.contains(word1));
- if (canCheckBigramProbability(formatVersion)) {
- final int bigramProbability = bigramProbabilities.get(new Pair<>(word0, word1));
- assertEquals(bigramProbability, bigramTarget.getProbability());
- }
+ final int bigramProbability = bigramProbabilities.get(new Pair<>(word0, word1));
+ assertEquals(bigramProbability, bigramTarget.getProbability());
}
}
}
@@ -1057,10 +1024,8 @@ public class BinaryDictionaryTests extends AndroidTestCase {
final String word1 = bigramTarget.mWord;
assertTrue(bigramWord1s.contains(word1));
final Pair<String, String> bigram = new Pair<>(word0, word1);
- if (canCheckBigramProbability(formatVersion)) {
- final int bigramProbability = bigramProbabilitiesToCheckLater.get(bigram);
- assertEquals(bigramProbability, bigramTarget.getProbability());
- }
+ final int bigramProbability = bigramProbabilitiesToCheckLater.get(bigram);
+ assertEquals(bigramProbability, bigramTarget.getProbability());
bigramSet.remove(bigram);
}
}
@@ -1198,7 +1163,7 @@ public class BinaryDictionaryTests extends AndroidTestCase {
public void testPossiblyOffensiveAttributeMaintained() {
final BinaryDictionary binaryDictionary =
- getEmptyBinaryDictionary(FormatSpec.VERSION4_DEV);
+ getEmptyBinaryDictionary(FormatSpec.VERSION403);
binaryDictionary.addUnigramEntry("ddd", 100, null, Dictionary.NOT_A_PROBABILITY,
false, true, true, 0);
WordProperty wordProperty = binaryDictionary.getWordProperty("ddd", false);
@@ -1236,11 +1201,9 @@ public class BinaryDictionaryTests extends AndroidTestCase {
assertEquals(toFormatVersion, binaryDictionary.getFormatVersion());
assertEquals(unigramProbability, binaryDictionary.getFrequency("aaa"));
assertEquals(unigramProbability, binaryDictionary.getFrequency("bbb"));
- if (canCheckBigramProbability(toFormatVersion)) {
- assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "aaa", "bbb"));
- assertEquals(bigramProbability, binaryDictionary.getNgramProbability(
- NgramContext.BEGINNING_OF_SENTENCE, "aaa"));
- }
+ assertEquals(bigramProbability, getBigramProbability(binaryDictionary, "aaa", "bbb"));
+ assertEquals(bigramProbability, binaryDictionary.getNgramProbability(
+ NgramContext.BEGINNING_OF_SENTENCE, "aaa"));
assertTrue(isValidBigram(binaryDictionary, "aaa", "bbb"));
WordProperty wordProperty = binaryDictionary.getWordProperty("ccc",
false /* isBeginningOfSentence */);
@@ -1311,10 +1274,8 @@ public class BinaryDictionaryTests extends AndroidTestCase {
binaryDictionary.getPropertyForGettingStats(BinaryDictionary.UNIGRAM_COUNT_QUERY)));
for (final Pair<String, String> bigram : bigrams) {
- if (canCheckBigramProbability(toFormatVersion)) {
- assertEquals((int)bigramProbabilities.get(bigram),
- getBigramProbability(binaryDictionary, bigram.first, bigram.second));
- }
+ assertEquals((int)bigramProbabilities.get(bigram),
+ getBigramProbability(binaryDictionary, bigram.first, bigram.second));
assertTrue(isValidBigram(binaryDictionary, bigram.first, bigram.second));
}
assertEquals(bigramProbabilities.size(), Integer.parseInt(
@@ -1323,9 +1284,7 @@ public class BinaryDictionaryTests extends AndroidTestCase {
public void testBeginningOfSentence() {
for (final int formatVersion : DICT_FORMAT_VERSIONS) {
- if (supportsBeginningOfSentence(formatVersion)) {
- testBeginningOfSentence(formatVersion);
- }
+ testBeginningOfSentence(formatVersion);
}
}
diff --git a/tests/src/com/android/inputmethod/latin/InputTestsBase.java b/tests/src/com/android/inputmethod/latin/InputTestsBase.java
index 926a2d3e1..00e0b52cb 100644
--- a/tests/src/com/android/inputmethod/latin/InputTestsBase.java
+++ b/tests/src/com/android/inputmethod/latin/InputTestsBase.java
@@ -44,10 +44,10 @@ import com.android.inputmethod.latin.Dictionary.PhonyDictionary;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.common.Constants;
import com.android.inputmethod.latin.common.InputPointers;
+import com.android.inputmethod.latin.common.LocaleUtils;
import com.android.inputmethod.latin.common.StringUtils;
import com.android.inputmethod.latin.settings.DebugSettings;
import com.android.inputmethod.latin.settings.Settings;
-import com.android.inputmethod.latin.utils.LocaleUtils;
import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
import java.util.Locale;
@@ -387,7 +387,7 @@ public class InputTestsBase extends ServiceTestCase<LatinIMEForTests> {
false /* isAuxiliary */,
false /* overridesImplicitlyEnabledSubtype */,
0 /* id */);
- SubtypeSwitcher.forceSubtype(subtype);
+ RichInputMethodManager.forceSubtype(subtype);
mLatinIME.onCurrentInputMethodSubtypeChanged(subtype);
runMessages();
mKeyboard = mLatinIME.mKeyboardSwitcher.getKeyboard();
diff --git a/tests/src/com/android/inputmethod/latin/utils/SpacebarLanguageUtilsTests.java b/tests/src/com/android/inputmethod/latin/RichInputMethodSubtypeTests.java
index 83afd782d..aed7d6ad6 100644
--- a/tests/src/com/android/inputmethod/latin/utils/SpacebarLanguageUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/RichInputMethodSubtypeTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.inputmethod.latin.utils;
+package com.android.inputmethod.latin;
import android.content.Context;
import android.content.res.Resources;
@@ -26,12 +26,15 @@ import android.view.inputmethod.InputMethodSubtype;
import com.android.inputmethod.latin.R;
import com.android.inputmethod.latin.RichInputMethodManager;
import com.android.inputmethod.latin.RichInputMethodSubtype;
+import com.android.inputmethod.latin.utils.AdditionalSubtypeUtils;
+import com.android.inputmethod.latin.utils.RunInLocale;
+import com.android.inputmethod.latin.utils.SubtypeLocaleUtils;
import java.util.ArrayList;
import java.util.Locale;
@SmallTest
-public class SpacebarLanguageUtilsTests extends AndroidTestCase {
+public class RichInputMethodSubtypeTests extends AndroidTestCase {
// All input method subtypes of LatinIME.
private final ArrayList<RichInputMethodSubtype> mSubtypesList = new ArrayList<>();
diff --git a/tests/src/com/android/inputmethod/latin/WordComposerTests.java b/tests/src/com/android/inputmethod/latin/WordComposerTests.java
index 20256e670..8ae475f7e 100644
--- a/tests/src/com/android/inputmethod/latin/WordComposerTests.java
+++ b/tests/src/com/android/inputmethod/latin/WordComposerTests.java
@@ -20,8 +20,8 @@ import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import com.android.inputmethod.latin.common.Constants;
+import com.android.inputmethod.latin.common.CoordinateUtils;
import com.android.inputmethod.latin.common.StringUtils;
-import com.android.inputmethod.latin.utils.CoordinateUtils;
/**
* Unit tests for WordComposer.
diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java
index a35fa13ce..d833b9736 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderEncoderTests.java
@@ -67,6 +67,8 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase {
private static final SparseArray<List<Integer>> sChainBigrams = new SparseArray<>();
private static final HashMap<String, List<String>> sShortcuts = new HashMap<>();
+ final Random mRandom;
+
public BinaryDictDecoderEncoderTests() {
this(System.currentTimeMillis(), DEFAULT_MAX_UNIGRAMS);
}
@@ -75,10 +77,10 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase {
super();
BinaryDictionaryUtils.setCurrentTimeForTest(0);
Log.e(TAG, "Testing dictionary: seed is " + seed);
- final Random random = new Random(seed);
+ mRandom = new Random(seed);
sWords.clear();
sWordsWithVariousCodePoints.clear();
- generateWords(maxUnigrams, random);
+ generateWords(maxUnigrams, mRandom);
for (int i = 0; i < sWords.size(); ++i) {
sChainBigrams.put(i, new ArrayList<Integer>());
@@ -96,10 +98,10 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase {
sShortcuts.clear();
for (int i = 0; i < NUM_OF_NODES_HAVING_SHORTCUTS; ++i) {
- final int from = Math.abs(random.nextInt()) % sWords.size();
+ final int from = Math.abs(mRandom.nextInt()) % sWords.size();
sShortcuts.put(sWords.get(from), new ArrayList<String>());
for (int j = 0; j < NUM_OF_SHORTCUTS; ++j) {
- final int to = Math.abs(random.nextInt()) % sWords.size();
+ final int to = Math.abs(mRandom.nextInt()) % sWords.size();
sShortcuts.get(sWords.get(from)).add(sWords.get(to));
}
}
@@ -314,14 +316,14 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase {
final String dictVersion = Long.toString(System.currentTimeMillis());
final String codePointTableAttribute = DictionaryHeader.CODE_POINT_TABLE_KEY;
final File file = BinaryDictUtils.getDictFile(dictName, dictVersion,
- BinaryDictUtils.VERSION201_OPTIONS, getContext().getCacheDir());
+ BinaryDictUtils.STATIC_OPTIONS, getContext().getCacheDir());
// Write a test dictionary
final DictEncoder dictEncoder = new Ver2DictEncoder(file,
Ver2DictEncoder.CODE_POINT_TABLE_ON);
final FormatSpec.FormatOptions formatOptions =
new FormatSpec.FormatOptions(
- FormatSpec.MINIMUM_SUPPORTED_VERSION_OF_CODE_POINT_TABLE);
+ FormatSpec.MINIMUM_SUPPORTED_STATIC_VERSION);
final FusionDictionary sourcedict = new FusionDictionary(new PtNodeArray(),
BinaryDictUtils.makeDictionaryOptions(dictName, dictVersion, formatOptions));
addUnigrams(words.size(), sourcedict, words, null /* shortcutMap */);
@@ -359,11 +361,11 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase {
final List<String> results = new ArrayList<>();
runReadAndWriteTests(results, BinaryDictUtils.USE_BYTE_BUFFER,
- BinaryDictUtils.VERSION2_OPTIONS);
+ BinaryDictUtils.STATIC_OPTIONS);
runReadAndWriteTests(results, BinaryDictUtils.USE_BYTE_BUFFER,
- BinaryDictUtils.VERSION4_OPTIONS_WITHOUT_TIMESTAMP);
+ BinaryDictUtils.DYNAMIC_OPTIONS_WITHOUT_TIMESTAMP);
runReadAndWriteTests(results, BinaryDictUtils.USE_BYTE_BUFFER,
- BinaryDictUtils.VERSION4_OPTIONS_WITH_TIMESTAMP);
+ BinaryDictUtils.DYNAMIC_OPTIONS_WITH_TIMESTAMP);
for (final String result : results) {
Log.d(TAG, result);
}
@@ -373,11 +375,11 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase {
final List<String> results = new ArrayList<>();
runReadAndWriteTests(results, BinaryDictUtils.USE_BYTE_ARRAY,
- BinaryDictUtils.VERSION2_OPTIONS);
+ BinaryDictUtils.STATIC_OPTIONS);
runReadAndWriteTests(results, BinaryDictUtils.USE_BYTE_ARRAY,
- BinaryDictUtils.VERSION4_OPTIONS_WITHOUT_TIMESTAMP);
+ BinaryDictUtils.DYNAMIC_OPTIONS_WITHOUT_TIMESTAMP);
runReadAndWriteTests(results, BinaryDictUtils.USE_BYTE_ARRAY,
- BinaryDictUtils.VERSION4_OPTIONS_WITH_TIMESTAMP);
+ BinaryDictUtils.DYNAMIC_OPTIONS_WITH_TIMESTAMP);
for (final String result : results) {
Log.d(TAG, result);
@@ -501,7 +503,7 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase {
final ArrayList<String> results = new ArrayList<>();
runReadUnigramsAndBigramsTests(results, BinaryDictUtils.USE_BYTE_BUFFER,
- BinaryDictUtils.VERSION2_OPTIONS);
+ BinaryDictUtils.STATIC_OPTIONS);
for (final String result : results) {
Log.d(TAG, result);
@@ -512,7 +514,7 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase {
final ArrayList<String> results = new ArrayList<>();
runReadUnigramsAndBigramsTests(results, BinaryDictUtils.USE_BYTE_ARRAY,
- BinaryDictUtils.VERSION2_OPTIONS);
+ BinaryDictUtils.STATIC_OPTIONS);
for (final String result : results) {
Log.d(TAG, result);
@@ -604,11 +606,10 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase {
+ " : " + outputOptions(bufferType, formatOptions));
// Test a word that isn't contained within the dictionary.
- final Random random = new Random((int)System.currentTimeMillis());
final int[] codePointSet = CodePointUtils.generateCodePointSet(DEFAULT_CODE_POINT_SET_SIZE,
- random);
+ mRandom);
for (int i = 0; i < 1000; ++i) {
- final String word = CodePointUtils.generateWord(random, codePointSet);
+ final String word = CodePointUtils.generateWord(mRandom, codePointSet);
if (sWords.indexOf(word) != -1) continue;
checkGetTerminalPosition(dictDecoder, word, false);
}
@@ -623,9 +624,9 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase {
final ArrayList<String> results = new ArrayList<>();
runGetTerminalPositionTests(BinaryDictUtils.USE_BYTE_ARRAY,
- BinaryDictUtils.VERSION2_OPTIONS);
+ BinaryDictUtils.STATIC_OPTIONS);
runGetTerminalPositionTests(BinaryDictUtils.USE_BYTE_BUFFER,
- BinaryDictUtils.VERSION2_OPTIONS);
+ BinaryDictUtils.STATIC_OPTIONS);
for (final String result : results) {
Log.d(TAG, result);
@@ -633,7 +634,7 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase {
}
public void testVer2DictGetWordProperty() {
- final FormatOptions formatOptions = BinaryDictUtils.VERSION2_OPTIONS;
+ final FormatOptions formatOptions = BinaryDictUtils.STATIC_OPTIONS;
final ArrayList<String> words = sWords;
final HashMap<String, List<String>> shortcuts = sShortcuts;
final String dictName = "testGetWordProperty";
@@ -669,7 +670,7 @@ public class BinaryDictDecoderEncoderTests extends AndroidTestCase {
}
public void testVer2DictIteration() {
- final FormatOptions formatOptions = BinaryDictUtils.VERSION2_OPTIONS;
+ final FormatOptions formatOptions = BinaryDictUtils.STATIC_OPTIONS;
final ArrayList<String> words = sWords;
final HashMap<String, List<String>> shortcuts = sShortcuts;
final SparseArray<List<Integer>> bigrams = sEmptyBigrams;
diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java
index 120b96bc6..be75565bb 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java
@@ -17,11 +17,16 @@
package com.android.inputmethod.latin.makedict;
import com.android.inputmethod.annotations.UsedForTesting;
+import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
+
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.HashMap;
+import java.util.LinkedList;
+
+import javax.annotation.Nonnull;
/**
* Decodes binary files for a FusionDictionary.
@@ -361,6 +366,43 @@ public final class BinaryDictDecoderUtils {
}
/**
+ * Helper method that brutally decodes a header from a byte array.
+ *
+ * @param headerBuffer a buffer containing the bytes of the header.
+ * @return a hashmap of the attributes stored in the header
+ */
+ @Nonnull
+ public static HashMap<String, String> decodeHeaderAttributes(@Nonnull final byte[] headerBuffer)
+ throws UnsupportedFormatException {
+ final StringBuilder sb = new StringBuilder();
+ final LinkedList<String> keyValues = new LinkedList<>();
+ int index = 0;
+ while (index < headerBuffer.length) {
+ if (headerBuffer[index] == FormatSpec.PTNODE_CHARACTERS_TERMINATOR) {
+ keyValues.add(sb.toString());
+ sb.setLength(0);
+ } else if (CharEncoding.fitsOnOneByte(headerBuffer[index] & 0xFF,
+ null /* codePointTable */)) {
+ sb.appendCodePoint(headerBuffer[index] & 0xFF);
+ } else {
+ sb.appendCodePoint(((headerBuffer[index] & 0xFF) << 16)
+ + ((headerBuffer[index + 1] & 0xFF) << 8)
+ + (headerBuffer[index + 2] & 0xFF));
+ index += 2;
+ }
+ index += 1;
+ }
+ if ((keyValues.size() & 1) != 0) {
+ throw new UnsupportedFormatException("Odd number of attributes");
+ }
+ final HashMap<String, String> attributes = new HashMap<>();
+ for (int i = 0; i < keyValues.size(); i += 2) {
+ attributes.put(keyValues.get(i), keyValues.get(i + 1));
+ }
+ return attributes;
+ }
+
+ /**
* Helper method to pass a file name instead of a File object to isBinaryDictionary.
*/
public static boolean isBinaryDictionary(final String filename) {
diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java
index 60e38250f..ce905c499 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java
@@ -819,12 +819,18 @@ public class BinaryDictEncoderUtils {
final ArrayList<Entry<Integer, Integer>> codePointOccurrenceArray)
throws IOException, UnsupportedFormatException {
final int version = formatOptions.mVersion;
- if (version < FormatSpec.MINIMUM_SUPPORTED_VERSION
- || version > FormatSpec.MAXIMUM_SUPPORTED_VERSION) {
+ if ((version >= FormatSpec.MINIMUM_SUPPORTED_STATIC_VERSION &&
+ version <= FormatSpec.MAXIMUM_SUPPORTED_STATIC_VERSION) || (
+ version >= FormatSpec.MINIMUM_SUPPORTED_DYNAMIC_VERSION &&
+ version <= FormatSpec.MAXIMUM_SUPPORTED_DYNAMIC_VERSION)) {
+ // Dictionary is valid
+ } else {
throw new UnsupportedFormatException("Requested file format version " + version
- + ", but this implementation only supports versions "
- + FormatSpec.MINIMUM_SUPPORTED_VERSION + " through "
- + FormatSpec.MAXIMUM_SUPPORTED_VERSION);
+ + ", but this implementation only supports static versions "
+ + FormatSpec.MINIMUM_SUPPORTED_STATIC_VERSION + " through "
+ + FormatSpec.MAXIMUM_SUPPORTED_STATIC_VERSION + " and dynamic versions "
+ + FormatSpec.MINIMUM_SUPPORTED_DYNAMIC_VERSION + " through "
+ + FormatSpec.MAXIMUM_SUPPORTED_DYNAMIC_VERSION);
}
ByteArrayOutputStream headerBuffer = new ByteArrayOutputStream(256);
diff --git a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictUtils.java b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictUtils.java
index 8eabf749d..9c1e4cf84 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/BinaryDictUtils.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/BinaryDictUtils.java
@@ -28,13 +28,11 @@ public class BinaryDictUtils {
public static final String TEST_DICT_FILE_EXTENSION = ".testDict";
- public static final FormatSpec.FormatOptions VERSION2_OPTIONS =
- new FormatSpec.FormatOptions(FormatSpec.VERSION2);
- public static final FormatSpec.FormatOptions VERSION201_OPTIONS =
- new FormatSpec.FormatOptions(FormatSpec.VERSION201);
- public static final FormatSpec.FormatOptions VERSION4_OPTIONS_WITHOUT_TIMESTAMP =
+ public static final FormatSpec.FormatOptions STATIC_OPTIONS =
+ new FormatSpec.FormatOptions(FormatSpec.VERSION202);
+ public static final FormatSpec.FormatOptions DYNAMIC_OPTIONS_WITHOUT_TIMESTAMP =
new FormatSpec.FormatOptions(FormatSpec.VERSION4, false /* hasTimestamp */);
- public static final FormatSpec.FormatOptions VERSION4_OPTIONS_WITH_TIMESTAMP =
+ public static final FormatSpec.FormatOptions DYNAMIC_OPTIONS_WITH_TIMESTAMP =
new FormatSpec.FormatOptions(FormatSpec.VERSION4, true /* hasTimestamp */);
public static DictionaryOptions makeDictionaryOptions(final String id, final String version,
@@ -55,7 +53,8 @@ public class BinaryDictUtils {
public static File getDictFile(final String name, final String version,
final FormatOptions formatOptions, final File directory) {
if (formatOptions.mVersion == FormatSpec.VERSION2
- || formatOptions.mVersion == FormatSpec.VERSION201) {
+ || formatOptions.mVersion == FormatSpec.VERSION201
+ || formatOptions.mVersion == FormatSpec.VERSION202) {
return new File(directory, name + "." + version + TEST_DICT_FILE_EXTENSION);
} else if (formatOptions.mVersion == FormatSpec.VERSION4) {
return new File(directory, name + "." + version);
@@ -71,7 +70,7 @@ public class BinaryDictUtils {
file.mkdir();
}
return new Ver4DictEncoder(file);
- } else if (formatOptions.mVersion == FormatSpec.VERSION2) {
+ } else if (formatOptions.mVersion == FormatSpec.VERSION202) {
return new Ver2DictEncoder(file, Ver2DictEncoder.CODE_POINT_TABLE_OFF);
} else {
throw new RuntimeException("The format option has a wrong version : "
diff --git a/tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java b/tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java
index 457e7af8e..5c261a94d 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java
@@ -178,7 +178,8 @@ public class Ver2DictDecoder extends AbstractDictDecoder {
throw new IOException("Cannot read the dictionary header.");
}
if (header.mFormatOptions.mVersion != FormatSpec.VERSION2 &&
- header.mFormatOptions.mVersion != FormatSpec.VERSION201) {
+ header.mFormatOptions.mVersion != FormatSpec.VERSION201 &&
+ header.mFormatOptions.mVersion != FormatSpec.VERSION202) {
throw new UnsupportedFormatException("File header has a wrong version : "
+ header.mFormatOptions.mVersion);
}
diff --git a/tests/src/com/android/inputmethod/latin/makedict/Ver2DictEncoder.java b/tests/src/com/android/inputmethod/latin/makedict/Ver2DictEncoder.java
index 2c2152be7..b52b8c485 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/Ver2DictEncoder.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/Ver2DictEncoder.java
@@ -124,7 +124,8 @@ public class Ver2DictEncoder implements DictEncoder {
@Override
public void writeDictionary(final FusionDictionary dict, final FormatOptions formatOptions)
throws IOException, UnsupportedFormatException {
- if (formatOptions.mVersion > FormatSpec.VERSION201) {
+ // We no longer support anything but the latest version of v2.
+ if (formatOptions.mVersion != FormatSpec.VERSION202) {
throw new UnsupportedFormatException(
"The given format options has wrong version number : "
+ formatOptions.mVersion);
diff --git a/tests/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java b/tests/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java
index 7e54ce986..63ea89c1d 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java
@@ -18,7 +18,7 @@ package com.android.inputmethod.latin.makedict;
import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.latin.BinaryDictionary;
-import com.android.inputmethod.latin.utils.FileUtils;
+import com.android.inputmethod.latin.common.FileUtils;
import java.io.File;
import java.io.FileNotFoundException;
diff --git a/tests/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java b/tests/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java
index 155421922..1e4bd768c 100644
--- a/tests/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java
+++ b/tests/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java
@@ -20,10 +20,10 @@ import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.latin.BinaryDictionary;
import com.android.inputmethod.latin.Dictionary;
import com.android.inputmethod.latin.NgramContext;
+import com.android.inputmethod.latin.common.LocaleUtils;
import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode;
import com.android.inputmethod.latin.utils.BinaryDictionaryUtils;
-import com.android.inputmethod.latin.utils.LocaleUtils;
import java.io.File;
import java.io.IOException;
diff --git a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java
index 778f6e800..a84df28c9 100644
--- a/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java
+++ b/tests/src/com/android/inputmethod/latin/personalization/UserHistoryDictionaryTests.java
@@ -16,6 +16,8 @@
package com.android.inputmethod.latin.personalization;
+import android.content.SharedPreferences;
+import android.preference.PreferenceManager;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.LargeTest;
import android.util.Log;
@@ -23,9 +25,10 @@ import android.util.Log;
import com.android.inputmethod.latin.ExpandableBinaryDictionary;
import com.android.inputmethod.latin.NgramContext;
import com.android.inputmethod.latin.NgramContext.WordInfo;
+import com.android.inputmethod.latin.common.FileUtils;
+import com.android.inputmethod.latin.settings.LocalSettingsConstants;
import com.android.inputmethod.latin.utils.BinaryDictionaryUtils;
import com.android.inputmethod.latin.utils.DistracterFilter;
-import com.android.inputmethod.latin.utils.FileUtils;
import java.io.File;
import java.io.FilenameFilter;
@@ -36,6 +39,8 @@ import java.util.Locale;
import java.util.Random;
import java.util.concurrent.TimeUnit;
+import javax.annotation.Nullable;
+
/**
* Unit tests for UserHistoryDictionary
*/
@@ -44,6 +49,7 @@ public class UserHistoryDictionaryTests extends AndroidTestCase {
private static final String TAG = UserHistoryDictionaryTests.class.getSimpleName();
private static final int WAIT_FOR_WRITING_FILE_IN_MILLISECONDS = 3000;
private static final String TEST_LOCALE_PREFIX = "test_";
+ private static final String TEST_ACCOUNT = "account@example.com";
private static final String[] CHARACTERS = {
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
@@ -52,15 +58,18 @@ public class UserHistoryDictionaryTests extends AndroidTestCase {
private int mCurrentTime = 0;
+ private SharedPreferences mPrefs;
+ private String mLastKnownAccount = null;
+
private void removeAllTestDictFiles() {
final Locale dummyLocale = new Locale(TEST_LOCALE_PREFIX);
- final String dictName = ExpandableBinaryDictionary.getDictName(
- UserHistoryDictionary.NAME, dummyLocale, null /* dictFile */);
+ final String dictName = UserHistoryDictionary.getUserHistoryDictName(
+ UserHistoryDictionary.NAME, dummyLocale, null /* dictFile */, getContext());
final File dictFile = ExpandableBinaryDictionary.getDictFile(
mContext, dictName, null /* dictFile */);
final FilenameFilter filenameFilter = new FilenameFilter() {
@Override
- public boolean accept(File dir, String filename) {
+ public boolean accept(final File dir, final String filename) {
return filename.startsWith(UserHistoryDictionary.NAME + "." + TEST_LOCALE_PREFIX);
}
};
@@ -99,6 +108,12 @@ public class UserHistoryDictionaryTests extends AndroidTestCase {
@Override
protected void setUp() throws Exception {
super.setUp();
+
+ mPrefs = PreferenceManager.getDefaultSharedPreferences(getContext());
+ // Keep track of the current account so that we restore it when the test finishes.
+ mLastKnownAccount = mPrefs.getString(LocalSettingsConstants.PREF_ACCOUNT_NAME, null);
+ updateAccountName(TEST_ACCOUNT);
+
resetCurrentTimeForTestMode();
removeAllTestDictFiles();
}
@@ -107,6 +122,10 @@ public class UserHistoryDictionaryTests extends AndroidTestCase {
protected void tearDown() throws Exception {
removeAllTestDictFiles();
stopTestModeInNativeCode();
+
+ // Restore the account that was present before running the test.
+ updateAccountName(mLastKnownAccount);
+
super.tearDown();
}
@@ -115,6 +134,14 @@ public class UserHistoryDictionaryTests extends AndroidTestCase {
setCurrentTimeForTestMode(mCurrentTime);
}
+ private void updateAccountName(@Nullable final String accountName) {
+ if (accountName == null) {
+ mPrefs.edit().remove(LocalSettingsConstants.PREF_ACCOUNT_NAME).apply();
+ } else {
+ mPrefs.edit().putString(LocalSettingsConstants.PREF_ACCOUNT_NAME, accountName).apply();
+ }
+ }
+
private void forcePassingShortTime() {
// 3 days.
final int timeToElapse = (int)TimeUnit.DAYS.toSeconds(3);
@@ -142,7 +169,7 @@ public class UserHistoryDictionaryTests extends AndroidTestCase {
*/
private static String generateWord(final int value) {
final int lengthOfChars = CHARACTERS.length;
- StringBuilder builder = new StringBuilder();
+ final StringBuilder builder = new StringBuilder();
long lvalue = Math.abs((long)value);
while (lvalue > 0) {
builder.append(CHARACTERS[(int)(lvalue % lengthOfChars)]);
@@ -162,7 +189,7 @@ public class UserHistoryDictionaryTests extends AndroidTestCase {
private static void addToDict(final UserHistoryDictionary dict, final List<String> words,
final int timestamp) {
NgramContext ngramContext = NgramContext.EMPTY_PREV_WORDS_INFO;
- for (String word : words) {
+ for (final String word : words) {
UserHistoryDictionary.addToDictionary(dict, ngramContext, word, true, timestamp,
DistracterFilter.EMPTY_DISTRACTER_FILTER);
ngramContext = ngramContext.getNextNgramContext(new WordInfo(word));
@@ -204,12 +231,12 @@ public class UserHistoryDictionaryTests extends AndroidTestCase {
Log.d(TAG, "This test can be used for profiling.");
Log.d(TAG, "Usage: please set UserHistoryDictionary.PROFILE_SAVE_RESTORE to true.");
final Locale dummyLocale = getDummyLocale("random_words");
- final String dictName = ExpandableBinaryDictionary.getDictName(
- UserHistoryDictionary.NAME, dummyLocale, null /* dictFile */);
+ final String dictName = UserHistoryDictionary.getUserHistoryDictName(
+ UserHistoryDictionary.NAME, dummyLocale, null /* dictFile */, getContext());
final File dictFile = ExpandableBinaryDictionary.getDictFile(
mContext, dictName, null /* dictFile */);
final UserHistoryDictionary dict = PersonalizationHelper.getUserHistoryDictionary(
- getContext(), dummyLocale);
+ getContext(), dummyLocale, TEST_ACCOUNT);
final int numberOfWords = 1000;
final Random random = new Random(123456);
@@ -232,12 +259,12 @@ public class UserHistoryDictionaryTests extends AndroidTestCase {
// Create filename suffixes for this test.
for (int i = 0; i < numberOfLanguages; i++) {
final Locale dummyLocale = getDummyLocale("switching_languages" + i);
- final String dictName = ExpandableBinaryDictionary.getDictName(
- UserHistoryDictionary.NAME, dummyLocale, null /* dictFile */);
+ final String dictName = UserHistoryDictionary.getUserHistoryDictName(
+ UserHistoryDictionary.NAME, dummyLocale, null /* dictFile */, getContext());
dictFiles[i] = ExpandableBinaryDictionary.getDictFile(
mContext, dictName, null /* dictFile */);
dicts[i] = PersonalizationHelper.getUserHistoryDictionary(getContext(),
- dummyLocale);
+ dummyLocale, TEST_ACCOUNT);
clearHistory(dicts[i]);
}
@@ -262,14 +289,14 @@ public class UserHistoryDictionaryTests extends AndroidTestCase {
public void testAddManyWords() {
final Locale dummyLocale = getDummyLocale("many_random_words");
- final String dictName = ExpandableBinaryDictionary.getDictName(
- UserHistoryDictionary.NAME, dummyLocale, null /* dictFile */);
+ final String dictName = UserHistoryDictionary.getUserHistoryDictName(
+ UserHistoryDictionary.NAME, dummyLocale, null /* dictFile */, getContext());
final File dictFile = ExpandableBinaryDictionary.getDictFile(
mContext, dictName, null /* dictFile */);
final int numberOfWords = 10000;
final Random random = new Random(123456);
final UserHistoryDictionary dict = PersonalizationHelper.getUserHistoryDictionary(
- getContext(), dummyLocale);
+ getContext(), dummyLocale, TEST_ACCOUNT);
clearHistory(dict);
try {
addAndWriteRandomWords(dict, numberOfWords, random, true /* checksContents */);
@@ -281,7 +308,7 @@ public class UserHistoryDictionaryTests extends AndroidTestCase {
public void testDecaying() {
final Locale dummyLocale = getDummyLocale("decaying");
final UserHistoryDictionary dict = PersonalizationHelper.getUserHistoryDictionary(
- getContext(), dummyLocale);
+ getContext(), dummyLocale, TEST_ACCOUNT);
final int numberOfWords = 5000;
final Random random = new Random(123456);
resetCurrentTimeForTestMode();
@@ -309,4 +336,9 @@ public class UserHistoryDictionaryTests extends AndroidTestCase {
assertFalse(dict.isInDictionary(word));
}
}
-}
+
+ public void testRandomWords_NullAccount() {
+ updateAccountName(null);
+ testRandomWords();
+ }
+} \ No newline at end of file
diff --git a/tests/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtilsTests.java
index 131865ab2..f50b8e0b8 100644
--- a/tests/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/utils/BinaryDictionaryUtilsTests.java
@@ -20,6 +20,8 @@ import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.LargeTest;
import com.android.inputmethod.latin.BinaryDictionary;
+import com.android.inputmethod.latin.common.FileUtils;
+import com.android.inputmethod.latin.common.LocaleUtils;
import com.android.inputmethod.latin.makedict.DictionaryHeader;
import com.android.inputmethod.latin.makedict.FormatSpec;
diff --git a/tests/src/com/android/inputmethod/latin/utils/CapsModeUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/CapsModeUtilsTests.java
index 4646a823d..9680d85b3 100644
--- a/tests/src/com/android/inputmethod/latin/utils/CapsModeUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/utils/CapsModeUtilsTests.java
@@ -21,8 +21,8 @@ import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
import android.text.TextUtils;
+import com.android.inputmethod.latin.common.LocaleUtils;
import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
-import com.android.inputmethod.latin.utils.LocaleUtils;
import java.util.Locale;
diff --git a/tests/src/com/android/inputmethod/latin/utils/CollectionUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/CollectionUtilsTests.java
index a5979c3df..47fd5feaa 100644
--- a/tests/src/com/android/inputmethod/latin/utils/CollectionUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/utils/CollectionUtilsTests.java
@@ -19,6 +19,8 @@ package com.android.inputmethod.latin.utils;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
+import com.android.inputmethod.latin.common.CollectionUtils;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -29,14 +31,45 @@ import java.util.Collections;
@SmallTest
public class CollectionUtilsTests extends AndroidTestCase {
/**
+ * Tests that {@link CollectionUtils#arrayAsList(Object[],int,int)} fails as expected
+ * with some invalid inputs.
+ */
+ public void testArrayAsListFailure() {
+ final String[] array = { "0", "1" };
+ // Negative start
+ try {
+ CollectionUtils.arrayAsList(array, -1, 1);
+ fail("Failed to catch start < 0");
+ } catch (final IllegalArgumentException e) {
+ assertEquals("Invalid start: -1 end: 1 with array.length: 2", e.getMessage());
+ }
+ // start > end
+ try {
+ CollectionUtils.arrayAsList(array, 1, -1);
+ fail("Failed to catch start > end");
+ } catch (final IllegalArgumentException e) {
+ assertEquals("Invalid start: 1 end: -1 with array.length: 2", e.getMessage());
+ }
+ // end > array.length
+ try {
+ CollectionUtils.arrayAsList(array, 1, 3);
+ fail("Failed to catch end > array.length");
+ } catch (final IllegalArgumentException e) {
+ assertEquals("Invalid start: 1 end: 3 with array.length: 2", e.getMessage());
+ }
+ }
+
+ /**
* Tests that {@link CollectionUtils#arrayAsList(Object[],int,int)} gives the expected
* results for a few valid inputs.
*/
public void testArrayAsList() {
- final String[] array = { "0", "1", "2", "3", "4" };
final ArrayList<String> empty = new ArrayList<>();
+ assertEquals(empty, CollectionUtils.arrayAsList(new String[] { }, 0, 0));
+ final String[] array = { "0", "1", "2", "3", "4" };
assertEquals(empty, CollectionUtils.arrayAsList(array, 0, 0));
assertEquals(empty, CollectionUtils.arrayAsList(array, 1, 1));
+ assertEquals(empty, CollectionUtils.arrayAsList(array, array.length, array.length));
final ArrayList<String> expected123 = new ArrayList<>(Arrays.asList("1", "2", "3"));
assertEquals(expected123, CollectionUtils.arrayAsList(array, 1, 4));
}
diff --git a/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java
index 54f478f5a..03dcdfc78 100644
--- a/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/utils/SubtypeLocaleUtilsTests.java
@@ -434,8 +434,8 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase {
// locale layout | display name
// ------ -------------- - ----------------------
// sr south_slavic F Српски
- // sr_ZZ serbian_qwertz F српски (латиница)
- // sr_ZZ qwerty T српски (QWERTY)
+ // sr_ZZ serbian_qwertz F Српски (латиница)
+ // sr_ZZ qwerty T Српски (QWERTY)
public void testSerbianLatinSubtypesInSerbianSystemLocale() {
final RunInLocale<Void> tests = new RunInLocale<Void>() {
@@ -445,12 +445,10 @@ public class SubtypeLocaleUtilsTests extends AndroidTestCase {
SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(SR));
// These are preliminary subtypes and may not exist.
if (SR_LATN != null) {
- // TODO: Uncommented because of the current translation of these strings
- // in Seriban are described in Latin script.
-// assertEquals("sr_ZZ", "српски (латиница)",
-// SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(SR_LATN));
-// assertEquals("sr_ZZ", "српски (QWERTY)",
-// SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(SR_LATN_QWERTY));
+ assertEquals("sr_ZZ", "Српски (латиница)",
+ SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(SR_LATN));
+ assertEquals("sr_ZZ", "Српски (QWERTY)",
+ SubtypeLocaleUtils.getSubtypeDisplayNameInSystemLocale(SR_LATN_QWERTY));
}
return null;
}
diff --git a/tools/dicttool/Android.mk b/tools/dicttool/Android.mk
index 81c0706c1..1a9f029ae 100644
--- a/tools/dicttool/Android.mk
+++ b/tools/dicttool/Android.mk
@@ -42,23 +42,15 @@ LATINIME_TESTS_SRC_DIR := $(LATINIME_LOCAL_DIR)/tests/src/com/android/inputmetho
# a significant part of the dependencies are mocked in the compat/ directory, with empty or
# nearly-empty implementations, for parts that we don't use in Dicttool.
LATINIME_SRC_FILES_FOR_DICTTOOL := \
- event/Combiner.java \
- event/Event.java \
latin/BinaryDictionary.java \
latin/DicTraverseSession.java \
latin/Dictionary.java \
- latin/LastComposedWord.java \
latin/NgramContext.java \
latin/SuggestedWords.java \
- latin/WordComposer.java \
- latin/settings/NativeSuggestOptions.java \
latin/settings/SettingsValuesForSuggestion.java \
latin/utils/BinaryDictionaryUtils.java \
latin/utils/CombinedFormatUtils.java \
- latin/utils/CoordinateUtils.java \
- latin/utils/FileUtils.java \
- latin/utils/JniUtils.java \
- latin/utils/LocaleUtils.java
+ latin/utils/JniUtils.java
LATINIME_OVERRIDABLE_SRC_FILES_FOR_DICTTOOL := \
latin/define/DebugFlags.java
diff --git a/tools/dicttool/compat/com/android/inputmethod/event/CombinerChain.java b/tools/dicttool/compat/com/android/inputmethod/event/CombinerChain.java
deleted file mode 100644
index c4457a1b7..000000000
--- a/tools/dicttool/compat/com/android/inputmethod/event/CombinerChain.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.inputmethod.event;
-
-import java.util.ArrayList;
-
-/**
- * Compatibility class that stands in for the combiner chain in LatinIME.
- *
- * This is not used by dicttool, it's just needed by the dependency chain.
- */
-// TODO: there should not be a dependency to this in dicttool, so there
-// should be a sensible way to separate them cleanly.
-public class CombinerChain {
- private StringBuilder mComposingWord;
- public CombinerChain(final String initialText, final Combiner... combinerList) {
- mComposingWord = new StringBuilder(initialText);
- }
-
- public Event processEvent(final ArrayList<Event> previousEvents, final Event newEvent) {
- return newEvent;
- }
-
- public void applyProcessedEvent(final Event event) {
- mComposingWord.append(event.getTextToCommit());
- }
-
- public CharSequence getComposingWordWithCombiningFeedback() {
- return mComposingWord;
- }
-
- public void reset() {
- mComposingWord.setLength(0);
- }
-
- public static Combiner[] createCombiners(final String spec) {
- // Dicttool never uses a combiner at all, so we just return a zero-sized array.
- return new Combiner[0];
- }
-}
diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtils.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtils.java
index 1c5dfa9fb..84c3956f7 100644
--- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtils.java
+++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtils.java
@@ -19,6 +19,10 @@ package com.android.inputmethod.latin.dicttool;
import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils;
import com.android.inputmethod.latin.makedict.BinaryDictIOUtils;
import com.android.inputmethod.latin.makedict.DictDecoder;
+import com.android.inputmethod.latin.makedict.DictionaryHeader;
+import com.android.inputmethod.latin.makedict.FormatSpec;
+import com.android.inputmethod.latin.makedict.FormatSpec.DictionaryOptions;
+import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
import com.android.inputmethod.latin.makedict.FusionDictionary;
import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
@@ -27,12 +31,18 @@ import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
+import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
/**
* Class grouping utilities for offline dictionary making.
@@ -44,26 +54,27 @@ public final class BinaryDictOffdeviceUtils {
// Prefix and suffix are arbitrary, the values do not really matter
private final static String PREFIX = "dicttool";
private final static String SUFFIX = ".tmp";
-
private final static int COPY_BUFFER_SIZE = 8192;
- public static class DecoderChainSpec {
+ public static class DecoderChainSpec<T> {
public final static int COMPRESSION = 1;
public final static int ENCRYPTION = 2;
- private final static int MAX_DECODE_DEPTH = 4;
- final int[] mDecoderSpec;
- File mFile;
+ private final static int[][] VALID_DECODER_CHAINS = {
+ { }, { COMPRESSION }, { ENCRYPTION, COMPRESSION }
+ };
+
+ private final int mDecoderSpecIndex;
+ public T mResult;
public DecoderChainSpec() {
- mDecoderSpec = new int[0];
- mFile = null;
+ mDecoderSpecIndex = 0;
+ mResult = null;
}
- public DecoderChainSpec(final DecoderChainSpec src, final int newStep) {
- mDecoderSpec = Arrays.copyOf(src.mDecoderSpec, src.mDecoderSpec.length + 1);
- mDecoderSpec[src.mDecoderSpec.length] = newStep;
- mFile = src.mFile;
+ private DecoderChainSpec(final DecoderChainSpec<T> src) {
+ mDecoderSpecIndex = src.mDecoderSpecIndex + 1;
+ mResult = src.mResult;
}
private String getStepDescription(final int step) {
@@ -79,110 +90,177 @@ public final class BinaryDictOffdeviceUtils {
public String describeChain() {
final StringBuilder s = new StringBuilder("raw");
- for (final int step : mDecoderSpec) {
+ for (final int step : VALID_DECODER_CHAINS[mDecoderSpecIndex]) {
s.append(" > ");
s.append(getStepDescription(step));
}
return s.toString();
}
- }
- public static void copy(final InputStream input, final OutputStream output) throws IOException {
- final byte[] buffer = new byte[COPY_BUFFER_SIZE];
- for (int readBytes = input.read(buffer); readBytes >= 0; readBytes = input.read(buffer)) {
- output.write(buffer, 0, readBytes);
+ /**
+ * Returns the next sequential spec. If exhausted, return null.
+ */
+ public DecoderChainSpec next() {
+ if (mDecoderSpecIndex + 1 >= VALID_DECODER_CHAINS.length) {
+ return null;
+ }
+ return new DecoderChainSpec(this);
+ }
+
+ public InputStream getStream(final File src) throws FileNotFoundException, IOException {
+ InputStream input = new BufferedInputStream(new FileInputStream(src));
+ for (final int step : VALID_DECODER_CHAINS[mDecoderSpecIndex]) {
+ switch (step) {
+ case COMPRESSION:
+ input = Compress.getUncompressedStream(input);
+ break;
+ case ENCRYPTION:
+ input = Crypt.getDecryptedStream(input);
+ break;
+ }
+ }
+ return input;
}
}
- /**
- * Returns a decrypted/uncompressed dictionary.
- *
- * This will decrypt/uncompress any number of times as necessary until it finds the
- * dictionary signature, and copy the decoded file to a temporary place.
- * If this is not a dictionary, the method returns null.
- */
- public static DecoderChainSpec getRawDictionaryOrNull(final File src) {
- return getRawDictionaryOrNullInternal(new DecoderChainSpec(), src, 0);
+ public interface InputProcessor<T> {
+ @Nonnull
+ public T process(@Nonnull final InputStream input)
+ throws IOException, UnsupportedFormatException;
}
- private static DecoderChainSpec getRawDictionaryOrNullInternal(
- final DecoderChainSpec spec, final File src, final int depth) {
- // Unfortunately the decoding scheme we use can consider any data to be encrypted
- // and will produce some output, meaning it's not possible to reliably detect encrypted
- // data. Thus, some non-dictionary files (especially small) ones may successfully decrypt
- // over and over, ending in a stack overflow. Hence we limit the depth at which we try
- // decoding the file.
- if (depth > DecoderChainSpec.MAX_DECODE_DEPTH) {
- return null;
+ public static class CopyProcessor implements InputProcessor<File> {
+ @Override @Nonnull
+ public File process(@Nonnull final InputStream input) throws IOException,
+ UnsupportedFormatException {
+ final File dst = File.createTempFile(PREFIX, SUFFIX);
+ dst.deleteOnExit();
+ try (final OutputStream output = new BufferedOutputStream(new FileOutputStream(dst))) {
+ copy(input, output);
+ output.flush();
+ output.close();
+ if (BinaryDictDecoderUtils.isBinaryDictionary(dst)
+ || CombinedInputOutput.isCombinedDictionary(dst.getAbsolutePath())) {
+ return dst;
+ }
+ }
+ throw new UnsupportedFormatException("Input stream not at the expected format");
}
- if (BinaryDictDecoderUtils.isBinaryDictionary(src)
- || CombinedInputOutput.isCombinedDictionary(src.getAbsolutePath())) {
- spec.mFile = src;
- return spec;
+ }
+
+ public static class HeaderReaderProcessor implements InputProcessor<DictionaryHeader> {
+ // Arbitrarily limit the header length to 32k. Sounds like it would never be larger
+ // than this. Revisit this if needed later.
+ private final int MAX_HEADER_LENGTH = 32 * 1024;
+ @Override @Nonnull
+ public DictionaryHeader process(final InputStream input) throws IOException,
+ UnsupportedFormatException {
+ // Do everything as curtly and ad-hoc as possible for performance.
+ final byte[] tmpBuffer = new byte[12];
+ if (tmpBuffer.length != input.read(tmpBuffer)) {
+ throw new UnsupportedFormatException("File too short, not a dictionary");
+ }
+ // Ad-hoc check for the magic number. See FormatSpec.java as well as
+ // byte_array_utils.h and BinaryDictEncoderUtils#writeDictionaryHeader().
+ final int MAGIC_NUMBER_START_OFFSET = 0;
+ final int VERSION_START_OFFSET = 4;
+ final int HEADER_SIZE_OFFSET = 8;
+ final int magicNumber = ((tmpBuffer[MAGIC_NUMBER_START_OFFSET] & 0xFF) << 24)
+ + ((tmpBuffer[MAGIC_NUMBER_START_OFFSET + 1] & 0xFF) << 16)
+ + ((tmpBuffer[MAGIC_NUMBER_START_OFFSET + 2] & 0xFF) << 8)
+ + (tmpBuffer[MAGIC_NUMBER_START_OFFSET + 3] & 0xFF);
+ if (magicNumber != FormatSpec.MAGIC_NUMBER) {
+ throw new UnsupportedFormatException("Wrong magic number");
+ }
+ final int version = ((tmpBuffer[VERSION_START_OFFSET] & 0xFF) << 8)
+ + (tmpBuffer[VERSION_START_OFFSET + 1] & 0xFF);
+ if (version != FormatSpec.VERSION2 && version != FormatSpec.VERSION201
+ && version != FormatSpec.VERSION202) {
+ throw new UnsupportedFormatException("Only versions 2, 201, 202 are supported");
+ }
+ final int totalHeaderSize = ((tmpBuffer[HEADER_SIZE_OFFSET] & 0xFF) << 24)
+ + ((tmpBuffer[HEADER_SIZE_OFFSET + 1] & 0xFF) << 16)
+ + ((tmpBuffer[HEADER_SIZE_OFFSET + 2] & 0xFF) << 8)
+ + (tmpBuffer[HEADER_SIZE_OFFSET + 3] & 0xFF);
+ if (totalHeaderSize > MAX_HEADER_LENGTH) {
+ throw new UnsupportedFormatException("Header too large");
+ }
+ final byte[] headerBuffer = new byte[totalHeaderSize - tmpBuffer.length];
+ readStreamExhaustively(input, headerBuffer);
+ final HashMap<String, String> attributes =
+ BinaryDictDecoderUtils.decodeHeaderAttributes(headerBuffer);
+ return new DictionaryHeader(totalHeaderSize, new DictionaryOptions(attributes),
+ new FormatOptions(version, false /* hasTimestamp */));
}
- // It's not a raw dictionary - try to see if it's compressed.
- final File uncompressedFile = tryGetUncompressedFile(src);
- if (null != uncompressedFile) {
- final DecoderChainSpec newSpec =
- getRawDictionaryOrNullInternal(spec, uncompressedFile, depth + 1);
- if (null == newSpec) return null;
- return new DecoderChainSpec(newSpec, DecoderChainSpec.COMPRESSION);
+ }
+
+ private static void readStreamExhaustively(final InputStream inputStream,
+ final byte[] outBuffer) throws IOException, UnsupportedFormatException {
+ int readBytes = 0;
+ int readBytesLastCycle = -1;
+ while (readBytes != outBuffer.length) {
+ readBytesLastCycle = inputStream.read(outBuffer, readBytes,
+ outBuffer.length - readBytes);
+ if (readBytesLastCycle == -1)
+ throw new UnsupportedFormatException("File shorter than specified in the header"
+ + " (expected " + outBuffer.length + ", read " + readBytes + ")");
+ readBytes += readBytesLastCycle;
}
- // It's not a compressed either - try to see if it's crypted.
- final File decryptedFile = tryGetDecryptedFile(src);
- if (null != decryptedFile) {
- final DecoderChainSpec newSpec =
- getRawDictionaryOrNullInternal(spec, decryptedFile, depth + 1);
- if (null == newSpec) return null;
- return new DecoderChainSpec(newSpec, DecoderChainSpec.ENCRYPTION);
+ }
+
+ public static void copy(final InputStream input, final OutputStream output) throws IOException {
+ final byte[] buffer = new byte[COPY_BUFFER_SIZE];
+ for (int readBytes = input.read(buffer); readBytes >= 0; readBytes = input.read(buffer)) {
+ output.write(buffer, 0, readBytes);
}
- return null;
}
- /* Try to uncompress the file passed as an argument.
+ /**
+ * Process a dictionary, decrypting/uncompressing it on the fly as necessary.
*
- * If the file can be uncompressed, the uncompressed version is returned. Otherwise, null
- * is returned.
+ * This will execute the given processor repeatedly with the possible alternatives
+ * for dictionary format until the processor does not throw an exception.
+ * If the processor succeeds for none of the possible formats, the method returns null.
*/
- private static File tryGetUncompressedFile(final File src) {
- try {
- final File dst = File.createTempFile(PREFIX, SUFFIX);
- dst.deleteOnExit();
- try (
- final InputStream input = Compress.getUncompressedStream(
- new BufferedInputStream(new FileInputStream(src)));
- final OutputStream output = new BufferedOutputStream(new FileOutputStream(dst))
- ) {
- copy(input, output);
- return dst;
+ @Nullable
+ public static <T> DecoderChainSpec<T> decodeDictionaryForProcess(@Nonnull final File src,
+ @Nonnull final InputProcessor<T> processor) {
+ @Nonnull DecoderChainSpec spec = new DecoderChainSpec();
+ while (null != spec) {
+ try {
+ final InputStream input = spec.getStream(src);
+ spec.mResult = processor.process(input);
+ try {
+ input.close();
+ } catch (IOException e) {
+ // CipherInputStream doesn't like being closed without having read the
+ // entire stream, for some reason. But we don't want to because it's a waste
+ // of resources. We really, really don't care about this.
+ // However on close() CipherInputStream does throw this exception, wrapped
+ // in an IOException so we need to catch it.
+ if (!(e.getCause() instanceof javax.crypto.BadPaddingException)) {
+ throw e;
+ }
+ }
+ return spec;
+ } catch (IOException | UnsupportedFormatException | ArrayIndexOutOfBoundsException e) {
+ // If the format is not the right one for this file, the processor will throw one
+ // of these exceptions. In our case, that means we should try the next spec,
+ // since it may still be at another format we haven't tried yet.
+ // TODO: stop using exceptions for this non-exceptional case.
}
- } catch (final IOException e) {
- // Could not uncompress the file: presumably the file is simply not a compressed file
- return null;
+ spec = spec.next();
}
+ return null;
}
- /* Try to decrypt the file passed as an argument.
- *
- * If the file can be decrypted, the decrypted version is returned. Otherwise, null
- * is returned.
+ /**
+ * Get a decoder chain spec with a raw dictionary file. This makes a new file on the
+ * disk ready for any treatment the client wants.
*/
- private static File tryGetDecryptedFile(final File src) {
- try {
- final File dst = File.createTempFile(PREFIX, SUFFIX);
- dst.deleteOnExit();
- try (
- final InputStream input = Crypt.getDecryptedStream(
- new BufferedInputStream(new FileInputStream(src)));
- final OutputStream output = new BufferedOutputStream(new FileOutputStream(dst))
- ) {
- copy(input, output);
- return dst;
- }
- } catch (final IOException e) {
- // Could not decrypt the file: presumably the file is simply not a crypted file
- return null;
- }
+ @Nullable
+ public static DecoderChainSpec<File> getRawDictionaryOrNull(@Nonnull final File src) {
+ return decodeDictionaryForProcess(src, new CopyProcessor());
}
static FusionDictionary getDictionary(final String filename, final boolean report) {
@@ -192,28 +270,28 @@ public final class BinaryDictOffdeviceUtils {
System.out.println("Size : " + file.length() + " bytes");
}
try {
- final DecoderChainSpec decodedSpec = getRawDictionaryOrNull(file);
+ final DecoderChainSpec<File> decodedSpec = getRawDictionaryOrNull(file);
if (null == decodedSpec) {
throw new RuntimeException("Does not seem to be a dictionary file " + filename);
}
- if (CombinedInputOutput.isCombinedDictionary(decodedSpec.mFile.getAbsolutePath())) {
+ if (CombinedInputOutput.isCombinedDictionary(decodedSpec.mResult.getAbsolutePath())) {
if (report) {
System.out.println("Format : Combined format");
System.out.println("Packaging : " + decodedSpec.describeChain());
- System.out.println("Uncompressed size : " + decodedSpec.mFile.length());
+ System.out.println("Uncompressed size : " + decodedSpec.mResult.length());
}
try (final BufferedReader reader = new BufferedReader(
- new InputStreamReader(new FileInputStream(decodedSpec.mFile), "UTF-8"))) {
+ new InputStreamReader(new FileInputStream(decodedSpec.mResult), "UTF-8"))) {
return CombinedInputOutput.readDictionaryCombined(reader);
}
}
final DictDecoder dictDecoder = BinaryDictIOUtils.getDictDecoder(
- decodedSpec.mFile, 0, decodedSpec.mFile.length(),
+ decodedSpec.mResult, 0, decodedSpec.mResult.length(),
DictDecoder.USE_BYTEARRAY);
if (report) {
System.out.println("Format : Binary dictionary format");
System.out.println("Packaging : " + decodedSpec.describeChain());
- System.out.println("Uncompressed size : " + decodedSpec.mFile.length());
+ System.out.println("Uncompressed size : " + decodedSpec.mResult.length());
}
return dictDecoder.readDictionaryBinary(false /* deleteDictIfBroken */);
} catch (final IOException | UnsupportedFormatException e) {
diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/CombinedInputOutput.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/CombinedInputOutput.java
index 48d2e5922..955c5728c 100644
--- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/CombinedInputOutput.java
+++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/CombinedInputOutput.java
@@ -98,6 +98,7 @@ public class CombinedInputOutput {
String word = null;
ProbabilityInfo probabilityInfo = new ProbabilityInfo(0);
boolean isNotAWord = false;
+ boolean isPossiblyOffensive = false;
ArrayList<WeightedString> bigrams = new ArrayList<>();
ArrayList<WeightedString> shortcuts = new ArrayList<>();
while (null != (line = reader.readLine())) {
@@ -106,7 +107,7 @@ public class CombinedInputOutput {
if (args[0].matches(CombinedFormatUtils.WORD_TAG + "=.*")) {
if (null != word) {
dict.add(word, probabilityInfo, shortcuts.isEmpty() ? null : shortcuts,
- isNotAWord, false /* isPossiblyOffensive */);
+ isNotAWord, isPossiblyOffensive);
for (WeightedString s : bigrams) {
dict.setBigram(word, s.mWord, s.mProbabilityInfo);
}
@@ -114,27 +115,37 @@ public class CombinedInputOutput {
if (!shortcuts.isEmpty()) shortcuts = new ArrayList<>();
if (!bigrams.isEmpty()) bigrams = new ArrayList<>();
isNotAWord = false;
+ isPossiblyOffensive = false;
for (String param : args) {
final String params[] = param.split("=", 2);
if (2 != params.length) throw new RuntimeException("Wrong format : " + line);
- if (CombinedFormatUtils.WORD_TAG.equals(params[0])) {
- word = params[1];
- } else if (CombinedFormatUtils.PROBABILITY_TAG.equals(params[0])) {
- probabilityInfo = new ProbabilityInfo(Integer.parseInt(params[1]),
- probabilityInfo.mTimestamp, probabilityInfo.mLevel,
- probabilityInfo.mCount);
- } else if (CombinedFormatUtils.HISTORICAL_INFO_TAG.equals(params[0])) {
- final String[] historicalInfoParams =
- params[1].split(CombinedFormatUtils.HISTORICAL_INFO_SEPARATOR);
- if (historicalInfoParams.length != HISTORICAL_INFO_ELEMENT_COUNT) {
- throw new RuntimeException("Wrong format (historical info) : " + line);
- }
- probabilityInfo = new ProbabilityInfo(probabilityInfo.mProbability,
- Integer.parseInt(historicalInfoParams[0]),
- Integer.parseInt(historicalInfoParams[1]),
- Integer.parseInt(historicalInfoParams[2]));
- } else if (CombinedFormatUtils.NOT_A_WORD_TAG.equals(params[0])) {
- isNotAWord = "true".equals(params[1]);
+ switch (params[0]) {
+ case CombinedFormatUtils.WORD_TAG:
+ word = params[1];
+ break;
+ case CombinedFormatUtils.PROBABILITY_TAG:
+ probabilityInfo = new ProbabilityInfo(Integer.parseInt(params[1]),
+ probabilityInfo.mTimestamp, probabilityInfo.mLevel,
+ probabilityInfo.mCount);
+ break;
+ case CombinedFormatUtils.HISTORICAL_INFO_TAG:
+ final String[] historicalInfoParams = params[1].split(
+ CombinedFormatUtils.HISTORICAL_INFO_SEPARATOR);
+ if (historicalInfoParams.length != HISTORICAL_INFO_ELEMENT_COUNT) {
+ throw new RuntimeException("Wrong format (historical info) : "
+ + line);
+ }
+ probabilityInfo = new ProbabilityInfo(probabilityInfo.mProbability,
+ Integer.parseInt(historicalInfoParams[0]),
+ Integer.parseInt(historicalInfoParams[1]),
+ Integer.parseInt(historicalInfoParams[2]));
+ break;
+ case CombinedFormatUtils.NOT_A_WORD_TAG:
+ isNotAWord = CombinedFormatUtils.isLiteralTrue(params[1]);
+ break;
+ case CombinedFormatUtils.POSSIBLY_OFFENSIVE_TAG:
+ isPossiblyOffensive = CombinedFormatUtils.isLiteralTrue(params[1]);
+ break;
}
}
} else if (args[0].matches(CombinedFormatUtils.SHORTCUT_TAG + "=.*")) {
@@ -190,7 +201,7 @@ public class CombinedInputOutput {
}
if (null != word) {
dict.add(word, probabilityInfo, shortcuts.isEmpty() ? null : shortcuts, isNotAWord,
- false /* isPossiblyOffensive */);
+ isPossiblyOffensive);
for (WeightedString s : bigrams) {
dict.setBigram(word, s.mWord, s.mProbabilityInfo);
}
diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/CommandList.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/CommandList.java
index 0d93c7fa9..8fdf7633f 100644
--- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/CommandList.java
+++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/CommandList.java
@@ -18,7 +18,9 @@ package com.android.inputmethod.latin.dicttool;
public class CommandList {
public static void populate() {
+ // TODO: Move some commands to native code.
Dicttool.addCommand("info", Info.class);
+ Dicttool.addCommand("header", Header.class);
Dicttool.addCommand("diff", Diff.class);
Dicttool.addCommand("compress", Compress.Compressor.class);
Dicttool.addCommand("uncompress", Compress.Uncompressor.class);
diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/DictionaryMaker.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/DictionaryMaker.java
index 8f9e4a3a6..6187853c8 100644
--- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/DictionaryMaker.java
+++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/DictionaryMaker.java
@@ -120,7 +120,7 @@ public class DictionaryMaker {
String inputCombined = null;
String outputBinary = null;
String outputCombined = null;
- int outputBinaryFormatVersion = FormatSpec.VERSION201; // the default version is 201.
+ int outputBinaryFormatVersion = FormatSpec.VERSION202; // the default version is 202.
// Don't use code point table by default.
int codePointTableMode = Ver2DictEncoder.CODE_POINT_TABLE_OFF;
diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Header.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Header.java
new file mode 100644
index 000000000..ba96c0aeb
--- /dev/null
+++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Header.java
@@ -0,0 +1,70 @@
+/**
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.inputmethod.latin.dicttool;
+
+import com.android.inputmethod.latin.BinaryDictionary;
+import com.android.inputmethod.latin.dicttool.BinaryDictOffdeviceUtils.DecoderChainSpec;
+import com.android.inputmethod.latin.makedict.DictionaryHeader;
+import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Locale;
+
+public class Header extends Dicttool.Command {
+ public static final String COMMAND = "header";
+
+ public Header() {
+ }
+
+ @Override
+ public String getHelp() {
+ return COMMAND + " <filename>: prints the header contents of a dictionary file";
+ }
+
+ @Override
+ public void run() throws UnsupportedFormatException {
+ final boolean plumbing;
+ if (mArgs.length > 0 && "-p".equals(mArgs[0])) {
+ plumbing = true;
+ mArgs = Arrays.copyOfRange(mArgs, 1, mArgs.length);
+ } else {
+ plumbing = false;
+ }
+ if (mArgs.length < 1) {
+ throw new RuntimeException("Not enough arguments for command " + COMMAND);
+ }
+ final String filename = mArgs[0];
+ final File dictFile = new File(filename);
+ final DecoderChainSpec<DictionaryHeader> spec =
+ BinaryDictOffdeviceUtils.decodeDictionaryForProcess(dictFile,
+ new BinaryDictOffdeviceUtils.HeaderReaderProcessor());
+ if (null == spec) {
+ throw new UnsupportedFormatException(filename
+ + " doesn't seem to be a valid version 2 dictionary file");
+ }
+
+ final DictionaryHeader header = spec.mResult;
+ System.out.println("Dictionary : " + dictFile.getAbsolutePath());
+ System.out.println("Size : " + dictFile.length() + " bytes");
+ System.out.println("Format : Binary dictionary format");
+ System.out.println("Format version : " + header.mFormatOptions.mVersion);
+ System.out.println("Packaging : " + spec.describeChain());
+ System.out.println("Header attributes :");
+ System.out.print(header.mDictionaryOptions.toString(2 /* indentCount */, plumbing));
+ }
+}
diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Package.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Package.java
index 47ea70629..3efa10a80 100644
--- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Package.java
+++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Package.java
@@ -16,6 +16,8 @@
package com.android.inputmethod.latin.dicttool;
+import com.android.inputmethod.latin.makedict.DictionaryHeader;
+
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
@@ -77,16 +79,16 @@ public class Package {
if (mArgs.length != 2) {
throw new RuntimeException("Too many/too few arguments for command " + COMMAND);
}
- final BinaryDictOffdeviceUtils.DecoderChainSpec decodedSpec =
- BinaryDictOffdeviceUtils.getRawDictionaryOrNull(new File(mArgs[0]));
+ final BinaryDictOffdeviceUtils.DecoderChainSpec<DictionaryHeader> decodedSpec =
+ BinaryDictOffdeviceUtils.decodeDictionaryForProcess(new File(mArgs[0]),
+ new BinaryDictOffdeviceUtils.HeaderReaderProcessor());
if (null == decodedSpec) {
System.out.println(mArgs[0] + " does not seem to be a dictionary");
return;
}
System.out.println("Packaging : " + decodedSpec.describeChain());
- System.out.println("Uncompressed size : " + decodedSpec.mFile.length());
try (
- final InputStream input = getFileInputStream(decodedSpec.mFile);
+ final InputStream input = decodedSpec.getStream(new File(mArgs[0]));
final OutputStream output = new BufferedOutputStream(
getFileOutputStreamOrStdOut(mArgs[1]))
) {
diff --git a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Test.java b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Test.java
index b6383d788..e2dd5199b 100644
--- a/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Test.java
+++ b/tools/dicttool/src/com/android/inputmethod/latin/dicttool/Test.java
@@ -16,10 +16,10 @@
package com.android.inputmethod.latin.dicttool;
+import com.android.inputmethod.latin.common.FileUtils;
import com.android.inputmethod.latin.makedict.BinaryDictDecoderEncoderTests;
import com.android.inputmethod.latin.makedict.BinaryDictEncoderFlattenTreeTests;
import com.android.inputmethod.latin.makedict.FusionDictionaryTest;
-import com.android.inputmethod.latin.utils.FileUtils;
import java.io.File;
import java.io.IOException;
diff --git a/tools/dicttool/tests/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtilsTests.java b/tools/dicttool/tests/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtilsTests.java
index 6cdbff7e5..ea9d4cc19 100644
--- a/tools/dicttool/tests/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtilsTests.java
+++ b/tools/dicttool/tests/com/android/inputmethod/latin/dicttool/BinaryDictOffdeviceUtilsTests.java
@@ -16,10 +16,17 @@
package com.android.inputmethod.latin.dicttool;
+import com.android.inputmethod.latin.common.CodePointUtils;
+import com.android.inputmethod.latin.dicttool.BinaryDictOffdeviceUtils;
+import com.android.inputmethod.latin.dicttool.Compress;
+import com.android.inputmethod.latin.dicttool.Crypt;
+import com.android.inputmethod.latin.dicttool.BinaryDictOffdeviceUtils.DecoderChainSpec;
import com.android.inputmethod.latin.makedict.BinaryDictIOUtils;
+import com.android.inputmethod.latin.makedict.BinaryDictUtils;
import com.android.inputmethod.latin.makedict.DictDecoder;
import com.android.inputmethod.latin.makedict.DictEncoder;
import com.android.inputmethod.latin.makedict.DictionaryHeader;
+import com.android.inputmethod.latin.makedict.FormatSpec;
import com.android.inputmethod.latin.makedict.FormatSpec.DictionaryOptions;
import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions;
import com.android.inputmethod.latin.makedict.FusionDictionary;
@@ -35,13 +42,37 @@ import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Random;
+import java.util.Set;
/**
* Unit tests for BinaryDictOffdeviceUtils
*/
public class BinaryDictOffdeviceUtilsTests extends TestCase {
private static final int TEST_FREQ = 37; // Some arbitrary value unlikely to happen by chance
+ private static final int CODE_POINT_SET_SIZE = 300;
+ final Random mRandom;
+ private static final ArrayList<String> sWords = new ArrayList<>();
+
+ public BinaryDictOffdeviceUtilsTests(final long seed, final int maxUnigrams) {
+ super();
+ mRandom = new Random(seed);
+ sWords.clear();
+ generateWords(maxUnigrams, mRandom);
+ }
+
+ private static void generateWords(final int maxUnigrams, final Random random) {
+ final int[] codePointSet = CodePointUtils.generateCodePointSet(
+ CODE_POINT_SET_SIZE, random);
+ final Set<String> wordSet = new HashSet<>();
+ while (wordSet.size() < maxUnigrams) {
+ wordSet.add(CodePointUtils.generateWord(random, codePointSet));
+ }
+ sWords.addAll(wordSet);
+ }
public void testGetRawDictWorks() throws IOException, UnsupportedFormatException {
final String VERSION = "1";
@@ -68,23 +99,17 @@ public class BinaryDictOffdeviceUtilsTests extends TestCase {
final File dst = File.createTempFile("testGetRawDict", ".tmp");
dst.deleteOnExit();
try (final OutputStream out = Compress.getCompressedStream(
- Compress.getCompressedStream(
- Compress.getCompressedStream(
- new BufferedOutputStream(new FileOutputStream(dst)))))) {
+ new BufferedOutputStream(new FileOutputStream(dst)))) {
final DictEncoder dictEncoder = new Ver2DictEncoder(out);
- dictEncoder.writeDictionary(dict, new FormatOptions(2, false));
+ dictEncoder.writeDictionary(dict, new FormatOptions(FormatSpec.VERSION202, false));
}
// Test for an actually compressed dictionary and its contents
- final BinaryDictOffdeviceUtils.DecoderChainSpec decodeSpec =
+ final BinaryDictOffdeviceUtils.DecoderChainSpec<File> decodeSpec =
BinaryDictOffdeviceUtils.getRawDictionaryOrNull(dst);
- for (final int step : decodeSpec.mDecoderSpec) {
- assertEquals("Wrong decode spec",
- BinaryDictOffdeviceUtils.DecoderChainSpec.COMPRESSION, step);
- }
- assertEquals("Wrong decode spec", 3, decodeSpec.mDecoderSpec.length);
- final DictDecoder dictDecoder = BinaryDictIOUtils.getDictDecoder(decodeSpec.mFile, 0,
- decodeSpec.mFile.length());
+ assertEquals("Wrong decode spec", "raw > compression", decodeSpec.describeChain());
+ final DictDecoder dictDecoder = BinaryDictIOUtils.getDictDecoder(decodeSpec.mResult, 0,
+ decodeSpec.mResult.length());
final FusionDictionary resultDict =
dictDecoder.readDictionaryBinary(false /* deleteDictIfBroken */);
assertEquals("Wrong version attribute", VERSION, resultDict.mOptions.mAttributes.get(
@@ -125,4 +150,64 @@ public class BinaryDictOffdeviceUtilsTests extends TestCase {
assertNull("Wrongly identified data file",
BinaryDictOffdeviceUtils.getRawDictionaryOrNull(gzDst));
}
+
+ public void runTestHeaderReaderProcessorWithOneSpec(final boolean compress, final boolean crypt)
+ throws IOException, UnsupportedFormatException {
+ final String dictName = "testHeaderReaderProcessor";
+ final String dictVersion = Long.toString(System.currentTimeMillis());
+ final FormatOptions formatOptions = BinaryDictUtils.STATIC_OPTIONS;
+ final int MAX_NUMBER_OF_OPTIONS_TO_ADD = 5;
+ final HashMap<String, String> options = new HashMap<>();
+ // Required attributes
+ options.put("dictionary", "main:en_US");
+ options.put("locale", "en_US");
+ options.put("version", Integer.toString(mRandom.nextInt()));
+ // Add some random options for test
+ final int numberOfOptionsToAdd = mRandom.nextInt() % (MAX_NUMBER_OF_OPTIONS_TO_ADD + 1);
+ for (int i = 0; i < numberOfOptionsToAdd; ++i) {
+ options.put(sWords.get(2 * i), sWords.get(2 * 1 + 1));
+ }
+ final FusionDictionary dict = new FusionDictionary(new PtNodeArray(),
+ new DictionaryOptions(options));
+
+ for (int i = 0; i < sWords.size(); ++i) {
+ final String word = sWords.get(i);
+ dict.add(word, new ProbabilityInfo(TEST_FREQ), null /* shortcuts */,
+ false /* isNotAWord */, false /* isPossiblyOffensive */);
+ }
+
+ File file = File.createTempFile(dictName, ".tmp");
+ final DictEncoder dictEncoder = BinaryDictUtils.getDictEncoder(file, formatOptions);
+ dictEncoder.writeDictionary(dict, formatOptions);
+
+ if (compress) {
+ final File rawFile = file;
+ file = File.createTempFile(dictName + ".compress", ".tmp");
+ final Compress.Compressor compressCommand = new Compress.Compressor();
+ compressCommand.setArgs(new String[] { rawFile.getPath(), file.getPath() });
+ compressCommand.run();
+ }
+ if (crypt) {
+ final File rawFile = file;
+ file = File.createTempFile(dictName + ".crypt", ".tmp");
+ final Crypt.Encrypter cryptCommand = new Crypt.Encrypter();
+ cryptCommand.setArgs(new String[] { rawFile.getPath(), file.getPath() });
+ cryptCommand.run();
+ }
+
+ final DecoderChainSpec<DictionaryHeader> spec =
+ BinaryDictOffdeviceUtils.decodeDictionaryForProcess(file,
+ new BinaryDictOffdeviceUtils.HeaderReaderProcessor());
+ assertNotNull("Can't decode a dictionary we just wrote : " + file, spec);
+ final DictionaryHeader header = spec.mResult;
+ assertEquals("raw" + (crypt ? " > encryption" : "") + (compress ? " > compression" : ""),
+ spec.describeChain());
+ assertEquals(header.mDictionaryOptions.mAttributes, options);
+ }
+
+ public void testHeaderReaderProcessor() throws IOException, UnsupportedFormatException {
+ runTestHeaderReaderProcessorWithOneSpec(false /* compress */, false /* crypt */);
+ runTestHeaderReaderProcessorWithOneSpec(true /* compress */, false /* crypt */);
+ runTestHeaderReaderProcessorWithOneSpec(true /* compress */, true /* crypt */);
+ }
}